diff -Nru mstflint-4.17.0+1/aclocal.m4 mstflint-4.21.0+1/aclocal.m4 --- mstflint-4.17.0+1/aclocal.m4 2021-06-29 08:51:03.000000000 +0000 +++ mstflint-4.21.0+1/aclocal.m4 2022-07-31 09:34:40.000000000 +0000 @@ -1,6 +1,6 @@ -# generated automatically by aclocal 1.16.3 -*- Autoconf -*- +# generated automatically by aclocal 1.16.1 -*- Autoconf -*- -# Copyright (C) 1996-2020 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -2881,6 +2881,9 @@ # before this can be enabled. hardcode_into_libs=yes + # Add ABI-specific directories to the system library path. + sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib" + # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command @@ -2889,7 +2892,7 @@ # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + sys_lib_dlsearch_path_spec="$sys_lib_dlsearch_path_spec $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on @@ -9049,9 +9052,9 @@ m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) -dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- -dnl serial 11 (pkg-config-0.29.1) -dnl +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# serial 11 (pkg-config-0.29.1) + dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson dnl @@ -9325,7 +9328,75 @@ AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR -# Copyright (C) 2002-2020 Free Software Foundation, Inc. +dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES, +dnl [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND], +dnl [DESCRIPTION], [DEFAULT]) +dnl ------------------------------------------ +dnl +dnl Prepare a "--with-" configure option using the lowercase +dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and +dnl PKG_CHECK_MODULES in a single macro. +AC_DEFUN([PKG_WITH_MODULES], +[ +m4_pushdef([with_arg], m4_tolower([$1])) + +m4_pushdef([description], + [m4_default([$5], [build with ]with_arg[ support])]) + +m4_pushdef([def_arg], [m4_default([$6], [auto])]) +m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes]) +m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no]) + +m4_case(def_arg, + [yes],[m4_pushdef([with_without], [--without-]with_arg)], + [m4_pushdef([with_without],[--with-]with_arg)]) + +AC_ARG_WITH(with_arg, + AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),, + [AS_TR_SH([with_]with_arg)=def_arg]) + +AS_CASE([$AS_TR_SH([with_]with_arg)], + [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)], + [auto],[PKG_CHECK_MODULES([$1],[$2], + [m4_n([def_action_if_found]) $3], + [m4_n([def_action_if_not_found]) $4])]) + +m4_popdef([with_arg]) +m4_popdef([description]) +m4_popdef([def_arg]) + +])dnl PKG_WITH_MODULES + +dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES, +dnl [DESCRIPTION], [DEFAULT]) +dnl ----------------------------------------------- +dnl +dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES +dnl check._[VARIABLE-PREFIX] is exported as make variable. +AC_DEFUN([PKG_HAVE_WITH_MODULES], +[ +PKG_WITH_MODULES([$1],[$2],,,[$3],[$4]) + +AM_CONDITIONAL([HAVE_][$1], + [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"]) +])dnl PKG_HAVE_WITH_MODULES + +dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES, +dnl [DESCRIPTION], [DEFAULT]) +dnl ------------------------------------------------------ +dnl +dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after +dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make +dnl and preprocessor variable. +AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES], +[ +PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4]) + +AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"], + [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])]) +])dnl PKG_HAVE_DEFINE_WITH_MODULES + +# Copyright (C) 2002-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -9340,7 +9411,7 @@ [am__api_version='1.16' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.16.3], [], +m4_if([$1], [1.16.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) @@ -9356,12 +9427,12 @@ # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], -[AM_AUTOMAKE_VERSION([1.16.3])dnl +[AM_AUTOMAKE_VERSION([1.16.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) -# Copyright (C) 2011-2020 Free Software Foundation, Inc. +# Copyright (C) 2011-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -9423,7 +9494,7 @@ # AM_AUX_DIR_EXPAND -*- Autoconf -*- -# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -9475,7 +9546,7 @@ # AM_CONDITIONAL -*- Autoconf -*- -# Copyright (C) 1997-2020 Free Software Foundation, Inc. +# Copyright (C) 1997-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -9506,7 +9577,7 @@ Usually this means the macro was only invoked conditionally.]]) fi])]) -# Copyright (C) 1999-2020 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -9697,7 +9768,7 @@ # Generate code to set up dependency tracking. -*- Autoconf -*- -# Copyright (C) 1999-2020 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -9736,9 +9807,7 @@ done if test $am_rc -ne 0; then AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments - for automatic dependency tracking. If GNU make was not used, consider - re-running the configure script with MAKE="gmake" (or whatever is - necessary). You can also try re-running configure with the + for automatic dependency tracking. Try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking).]) fi @@ -9765,7 +9834,7 @@ # Do all the work for Automake. -*- Autoconf -*- -# Copyright (C) 1996-2020 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -9962,7 +10031,7 @@ done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) -# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -9983,7 +10052,7 @@ fi AC_SUBST([install_sh])]) -# Copyright (C) 2003-2020 Free Software Foundation, Inc. +# Copyright (C) 2003-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -10004,7 +10073,7 @@ # Check to see how 'make' treats includes. -*- Autoconf -*- -# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -10047,7 +10116,7 @@ # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- -# Copyright (C) 1997-2020 Free Software Foundation, Inc. +# Copyright (C) 1997-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -10068,7 +10137,12 @@ [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then - MISSING="\${SHELL} '$am_aux_dir/missing'" + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then @@ -10081,7 +10155,7 @@ # Helper functions for option handling. -*- Autoconf -*- -# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -10110,7 +10184,7 @@ AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) -# Copyright (C) 1999-2020 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -10157,7 +10231,7 @@ # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) -# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -10176,7 +10250,7 @@ # Check to make sure that the build environment is sane. -*- Autoconf -*- -# Copyright (C) 1996-2020 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -10257,7 +10331,7 @@ rm -f conftest.file ]) -# Copyright (C) 2009-2020 Free Software Foundation, Inc. +# Copyright (C) 2009-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -10317,7 +10391,7 @@ _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) -# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -10345,7 +10419,7 @@ INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) -# Copyright (C) 2006-2020 Free Software Foundation, Inc. +# Copyright (C) 2006-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -10364,7 +10438,7 @@ # Check how to create a tarball. -*- Autoconf -*- -# Copyright (C) 2004-2020 Free Software Foundation, Inc. +# Copyright (C) 2004-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, diff -Nru mstflint-4.17.0+1/adb_parser/adb_common_functions.h mstflint-4.21.0+1/adb_parser/adb_common_functions.h --- mstflint-4.17.0+1/adb_parser/adb_common_functions.h 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_common_functions.h 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + * + * Version: $Id$ + */ + +indentString(int) diff -Nru mstflint-4.17.0+1/adb_parser/adb_config.cpp mstflint-4.21.0+1/adb_parser/adb_config.cpp --- mstflint-4.17.0+1/adb_parser/adb_config.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_config.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + * + * Version: $Id$ + */ +/*************************** AdbConfig ***************************/ +/** + * Function: AdbField::print + **/ + +#include "adb_config.h" +#include + +void AdbConfig::print(int indent) +{ + cout << indentString(indent) << "Attributes:" << endl; + AttrsMap::iterator iter; + for (iter = attrs.begin(); iter != attrs.end(); iter++) + cout << indentString(indent + 1) << iter->first << " - " << iter->second << endl; + + cout << indentString(indent) << "Enums:" << endl; + for (iter = enums.begin(); iter != enums.end(); iter++) + cout << indentString(indent + 1) << iter->first << " - " << iter->second << endl; +} + +/** + * Function: AdbConfig::toXml + **/ +string AdbConfig::toXml() +{ + string xml = "first + "=\"" + encodeXml(it->second) + "\""; + } + + if (!enums.empty()) + { + xml += " >\n"; + + for (AttrsMap::iterator it = enums.begin(); it != enums.end(); it++) + { + xml += "\tfirst) + "\" value=\"" + encodeXml(it->second) + "\" />\n"; + } + xml += ""; + } + else + { + xml += " />"; + } + + return xml; +} diff -Nru mstflint-4.17.0+1/adb_parser/adb_config.h mstflint-4.21.0+1/adb_parser/adb_config.h --- mstflint-4.17.0+1/adb_parser/adb_config.h 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_config.h 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + * + * Version: $Id$ + */ +/*************************** AdbConfig ***************************/ + +#ifndef ADB_CONFIG_H +#define ADB_CONFIG_H + +#include "adb_xmlCreator.h" +#include +#include + +using namespace xmlCreator; + +using namespace std; + +typedef map AttrsMap; + +class AdbConfig +{ +public: + // FOR DEBUG + void print(int indent = 0); + + string toXml(); + +public: + // Members + AttrsMap attrs; + AttrsMap enums; +}; + +#endif diff -Nru mstflint-4.17.0+1/adb_parser/adb_db.cpp mstflint-4.21.0+1/adb_parser/adb_db.cpp --- mstflint-4.17.0+1/adb_parser/adb_db.cpp 2021-06-29 08:49:47.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_db.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -1,25 +1,26 @@ -/* - * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. - * +/* + * + * Copyright (c) 2010-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: - * + * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: - * + * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. - * + * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * 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 @@ -28,12 +29,6 @@ * 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. - - * - * End of legal section ...................................................... - * - * adb_db.c - "C" wrapper module for adb_parser classes. - * This module provides an alternative "C" way to work with AdbInstance objects. * * Version: $Id$ * @@ -45,22 +40,22 @@ #include #include #include "adb_db.h" -#include "adb_parser.h" -#define CHECK_FIELD(field, node_w) \ - if (((AdbInstance*)field)->isReserved()) { \ - continue; \ - } \ - offset = db_field_offset(field); \ - if (offset - node_w->node->offset < from || \ - offset - node_w->node->offset > to) { \ - continue; \ +#define CHECK_FIELD(field, node_w) \ + if (((AdbInstance*)field)->isReserved()) \ + { \ + continue; \ + } \ + offset = db_field_offset(field); \ + if (offset - node_w->node->offset < from || offset - node_w->node->offset > to) \ + { \ + continue; \ } struct node_wrapper { - AdbInstance *node; - vector *fields; + AdbInstance* node; + vector* fields; }; static char err[1024] = {0}; @@ -71,15 +66,15 @@ adb_db_t* db_create() { return new Adb(); - } /** * db_load */ -int db_load(adb_db_t *db, const char *adb_file_path, int add_reserved) +int db_load(adb_db_t* db, const char* adb_file_path, int add_reserved) { - if (!((Adb*)db)->load(adb_file_path, add_reserved, NULL, false)) { + if (!((Adb*)db)->load(adb_file_path, add_reserved, NULL, false)) + { sprintf(err, "Failed to load adabe project: %s", ((Adb*)db)->getLastError().c_str()); return 1; } @@ -90,9 +85,10 @@ /** * db_load_from_str */ -int db_load_from_str(adb_db_t *db, const char *adb_data, int add_reserved) +int db_load_from_str(adb_db_t* db, const char* adb_data, int add_reserved) { - if (!((Adb*)db)->loadFromString(adb_data, add_reserved, NULL, false)) { + if (!((Adb*)db)->loadFromString(adb_data, add_reserved, NULL, false)) + { sprintf(err, "Failed to load adabe project: %s", ((Adb*)db)->getLastError().c_str()); return 1; } @@ -103,9 +99,10 @@ /** * db_destroy */ -void db_destroy(adb_db_t *db) +void db_destroy(adb_db_t* db) { - if (db) { + if (db) + { delete (Adb*)db; } } @@ -118,34 +115,39 @@ return err; } - -adb_limits_map_t* db_create_limits_map(adb_db_t *db) +adb_limits_map_t* db_create_limits_map(adb_db_t* db) { - map *limits = new map(); + map* limits = new map(); // Load defines - if (db) { - for (size_t i = 0; i < ((Adb*)db)->configs.size(); i++) { + if (db) + { + for (size_t i = 0; i < ((Adb*)db)->configs.size(); i++) + { AttrsMap::iterator attrs_map = ((Adb*)db)->configs[i]->attrs.find("define"); - if (attrs_map != ((Adb*)db)->configs[i]->attrs.end()) { + if (attrs_map != ((Adb*)db)->configs[i]->attrs.end()) + { vector defVal; boost::algorithm::split(defVal, attrs_map->second, boost::is_any_of(string("="))); - if (defVal.size() == 1) { + if (defVal.size() == 1) + { ((map*)limits)->insert(pair(defVal[0], "0")); - } else { + } + else + { ((map*)limits)->insert(pair(defVal[0], defVal[1])); } } } } return (adb_limits_map_t*)limits; - } -void db_destroy_limits_map(adb_limits_map_t *limits) +void db_destroy_limits_map(adb_limits_map_t* limits) { - if (limits) { + if (limits) + { ((map*)limits)->clear(); delete (map*)limits; } @@ -154,16 +156,18 @@ /** * db_get_node */ -adb_node_t* db_get_node(adb_db_t *db, const char *node_name) +adb_node_t* db_get_node(adb_db_t* db, const char* node_name) { - Adb *adb = (Adb*)db; - AdbInstance *node = adb->createLayout(node_name, false, NULL); - if (!node) { + Adb* adb = (Adb*)db; + AdbInstance* node = adb->createLayout(node_name, false, NULL); + if (!node) + { sprintf(err, "Failed to create node %s: %s", node_name, adb->getLastError().c_str()); return NULL; } - struct node_wrapper *node_w = (struct node_wrapper*)malloc(sizeof(struct node_wrapper)); - if (!node_w) { + struct node_wrapper* node_w = (struct node_wrapper*)malloc(sizeof(struct node_wrapper)); + if (!node_w) + { delete node; sprintf(err, "Failed to allocate memory for node"); return NULL; @@ -178,14 +182,17 @@ /** * db_node_destroy */ -void db_node_destroy(adb_node_t *node) +void db_node_destroy(adb_node_t* node) { - struct node_wrapper *node_w = (struct node_wrapper*)node; - if (node_w) { - if (node_w->node) { + struct node_wrapper* node_w = (struct node_wrapper*)node; + if (node_w) + { + if (node_w->node) + { delete node_w->node; } - if (node_w->fields) { + if (node_w->fields) + { delete node_w->fields; } free(node_w); @@ -195,65 +202,73 @@ /** * db_node_name */ -void db_node_name(adb_node_t *node, char name[]) +void db_node_name(adb_node_t* node, char name[]) { - struct node_wrapper *node_w = (struct node_wrapper*)node; + struct node_wrapper* node_w = (struct node_wrapper*)node; strcpy(name, node_w->node->name.c_str()); } /** * db_node_size */ -int db_node_size(adb_node_t *node) +int db_node_size(adb_node_t* node) { - struct node_wrapper *node_w = (struct node_wrapper*)node; + struct node_wrapper* node_w = (struct node_wrapper*)node; return node_w->node->size; } /** * db_node_num_of_fields */ -int db_node_num_of_fields(adb_node_t *node) +int db_node_num_of_fields(adb_node_t* node) { - struct node_wrapper *node_w = (struct node_wrapper*)node; + struct node_wrapper* node_w = (struct node_wrapper*)node; return (int)node_w->fields->size(); } /** * db_node_get_field */ -adb_field_t* db_node_get_field(adb_node_t *node, int field_idx) +adb_field_t* db_node_get_field(adb_node_t* node, int field_idx) { - if (field_idx >= db_node_num_of_fields(node)) { + if (field_idx >= db_node_num_of_fields(node)) + { sprintf(err, "index out of range"); return NULL; } - struct node_wrapper *node_w = (struct node_wrapper*)node; + struct node_wrapper* node_w = (struct node_wrapper*)node; return node_w->fields->at(field_idx); } /** * db_node_get_field_by_path */ -adb_field_t* db_node_get_field_by_path(adb_node_t *node, const char *path, int is_case_sensitive) +adb_field_t* db_node_get_field_by_path(adb_node_t* node, const char* path, int is_case_sensitive) { - struct node_wrapper *node_w = (struct node_wrapper*)node; + struct node_wrapper* node_w = (struct node_wrapper*)node; string fullPath(path); - if (node_w->node->isConditionalNode()) { + if (node_w->node->isConditionalNode()) + { fullPath += ".val"; } - for (size_t i = 0; i < node_w->fields->size(); i++) { - if (is_case_sensitive) { - if (node_w->fields->at(i)->fullName(1) == fullPath) { + for (size_t i = 0; i < node_w->fields->size(); i++) + { + if (is_case_sensitive) + { + if (node_w->fields->at(i)->fullName(1) == fullPath) + { return node_w->fields->at(i); } - } else { + } + else + { string field_name_lower = boost::algorithm::to_lower_copy(node_w->fields->at(i)->fullName(1)); string path_lower = boost::algorithm::to_lower_copy(fullPath); - if (path_lower == field_name_lower) { + if (path_lower == field_name_lower) + { return node_w->fields->at(i); } } @@ -267,9 +282,9 @@ * db_node_dump * Returns: false on error */ -bool db_node_dump(adb_node_t *node, u_int8_t buf[], dump_format_t format, FILE *stream) +bool db_node_dump(adb_node_t* node, u_int8_t buf[], dump_format_t format, FILE* stream) { - struct node_wrapper *node_w = (struct node_wrapper*)node; + struct node_wrapper* node_w = (struct node_wrapper*)node; return db_node_range_dump(node, 0, node_w->node->size - 1, buf, format, stream); } @@ -277,13 +292,13 @@ * db_node_dump_with_limits * Returns: false on error */ -bool db_node_conditional_dump(adb_node_t *node, +bool db_node_conditional_dump(adb_node_t* node, u_int8_t buf[], dump_format_t format, - FILE *stream, - adb_limits_map_t *values_map) + FILE* stream, + adb_limits_map_t* values_map) { - struct node_wrapper *node_w = (struct node_wrapper*)node; + struct node_wrapper* node_w = (struct node_wrapper*)node; return db_node_conditional_range_dump(node, 0, node_w->node->size - 1, buf, format, stream, values_map); } @@ -291,8 +306,12 @@ * db_node_range_dump * Returns: false on error */ -bool db_node_range_dump(adb_node_t *node, u_int32_t from, u_int32_t to, - u_int8_t buf[], dump_format_t format, FILE *stream) +bool db_node_range_dump(adb_node_t* node, + u_int32_t from, + u_int32_t to, + u_int8_t buf[], + dump_format_t format, + FILE* stream) { return db_node_conditional_range_dump(node, from, to, buf, format, stream, NULL); } @@ -301,36 +320,41 @@ * db_node_range_dump_with_limits * Returns: false on error */ -bool db_node_conditional_range_dump(adb_node_t *node, +bool db_node_conditional_range_dump(adb_node_t* node, u_int32_t from, u_int32_t to, u_int8_t buf[], dump_format_t format, - FILE *stream, - adb_limits_map_t *values_map) + FILE* stream, + adb_limits_map_t* values_map) { int i; - adb_field_t *field; + adb_field_t* field; char enum_str[256]; char name[256]; int is_enum; u_int32_t value; u_int32_t offset; u_int32_t size; - struct node_wrapper *node_w = (struct node_wrapper*)node; - if (values_map) { + struct node_wrapper* node_w = (struct node_wrapper*)node; + if (values_map) + { // Fill values map for conditions - for (i = 0; i < db_node_num_of_fields(node); i++) { + for (i = 0; i < db_node_num_of_fields(node); i++) + { field = db_node_get_field(node, i); - if (!field) { + if (!field) + { return false; } CHECK_FIELD(field, node_w); db_field_full_name(field, 0, name); value = db_field_value(field, (u_int8_t*)buf); - if (node_w->node->isConditionalNode()) { - char *p = strstr(name, ".val"); - if (p != NULL) { + if (node_w->node->isConditionalNode()) + { + char* p = strstr(name, ".val"); + if (p != NULL) + { *p = '\0'; } } @@ -338,52 +362,63 @@ } } - for (i = 0; i < db_node_num_of_fields(node); i++) { + for (i = 0; i < db_node_num_of_fields(node); i++) + { field = db_node_get_field(node, i); - if (!field) { + if (!field) + { return false; } CHECK_FIELD(field, node_w); db_field_full_name(field, 0, name); value = db_field_value(field, (u_int8_t*)buf); - if (node_w->node->isConditionalNode()) { - char *p = strstr(name, ".val"); - if (p != NULL) { + if (node_w->node->isConditionalNode()) + { + char* p = strstr(name, ".val"); + if (p != NULL) + { *p = '\0'; } } /* * Just if the node is Conditional */ - if (node_w->node->isConditionalNode() && values_map != NULL) { - try { - AdbInstance *parentField = node_w->node->subItems[i]; - if (!parentField->isConditionValid(((map*)values_map))) { + if (node_w->node->isConditionalNode() && values_map != NULL) + { + try + { + AdbInstance* parentField = node_w->node->subItems[i]; + if (!parentField->isConditionValid(((map*)values_map))) + { ((map*)values_map)->erase(name); continue; } - } catch (...) { + } + catch (...) + { continue; } } is_enum = db_field_enum(field, value, enum_str); size = db_field_size(field); - if (stream != NULL) { - switch (format) { - case DB_FORMAT_STANDARD: - fprintf(stream, "%-40s : 0x%x %s\n", name, value, is_enum ? enum_str : ""); - break; - - case DB_FORMAT_STANDARD_NO_ENUM: - fprintf(stream, "%-40s : 0x%x\n", name, value); - break; - - case DB_FORMAT_FULL_DETAILS: - fprintf(stream, "%-40s : 0x%x %s - address: 0x%x.%d:%d\n", - name, value, is_enum ? enum_str : "", offset / 32 * 4, offset % 32, size); - break; + if (stream != NULL) + { + switch (format) + { + case DB_FORMAT_STANDARD: + fprintf(stream, "%-40s : 0x%x %s\n", name, value, is_enum ? enum_str : ""); + break; + + case DB_FORMAT_STANDARD_NO_ENUM: + fprintf(stream, "%-40s : 0x%x\n", name, value); + break; + + case DB_FORMAT_FULL_DETAILS: + fprintf(stream, "%-40s : 0x%x %s - address: 0x%x.%d:%d\n", name, value, is_enum ? enum_str : "", + offset / 32 * 4, offset % 32, size); + break; } } } @@ -393,7 +428,7 @@ /** * db_field_name */ -void db_field_name(adb_field_t *field, char name[]) +void db_field_name(adb_field_t* field, char name[]) { strcpy(name, ((AdbInstance*)field)->name.c_str()); } @@ -401,7 +436,7 @@ /** * db_field_full_name */ -void db_field_full_name(adb_field_t *field, int skip_level, char name[]) +void db_field_full_name(adb_field_t* field, int skip_level, char name[]) { strcpy(name, ((AdbInstance*)field)->fullName(skip_level + 1).c_str()); } @@ -409,7 +444,7 @@ /** * db_field_offset */ -int db_field_offset(adb_field_t *field) +int db_field_offset(adb_field_t* field) { return ((AdbInstance*)field)->offset; } @@ -417,7 +452,7 @@ /** * db_field_size */ -int db_field_size(adb_field_t *field) +int db_field_size(adb_field_t* field) { return ((AdbInstance*)field)->size; } @@ -425,19 +460,19 @@ /** * db_field_value */ -u_int64_t db_field_value(adb_field_t *field, u_int8_t buf[]) +u_int64_t db_field_value(adb_field_t* field, u_int8_t buf[]) { -// cout << ((AdbInstance*)field)->offset / 8 << endl; -// for (unsigned i = 0; i < ((AdbInstance*)field)->offset / 8; i++) { -// printf ("%02x ", buf[i]); -// } + // cout << ((AdbInstance*)field)->offset / 8 << endl; + // for (unsigned i = 0; i < ((AdbInstance*)field)->offset / 8; i++) { + // printf ("%02x ", buf[i]); + // } return ((AdbInstance*)field)->popBuf(buf); } /** * db_field_set_value */ -void db_field_set_value(adb_field_t *field, u_int8_t buf[], u_int64_t value) +void db_field_set_value(adb_field_t* field, u_int8_t buf[], u_int64_t value) { ((AdbInstance*)field)->pushBuf(buf, value); } @@ -445,13 +480,16 @@ /** * db_field_enum */ -int db_field_enum(adb_field_t *field, u_int64_t value, char enum_str[]) +int db_field_enum(adb_field_t* field, u_int64_t value, char enum_str[]) { string s; - if (((AdbInstance*)field)->intToEnum(value, s)) { + if (((AdbInstance*)field)->intToEnum(value, s)) + { strcpy(enum_str, s.c_str()); return 1; - } else { + } + else + { strcpy(enum_str, ""); return 0; } @@ -460,12 +498,13 @@ /** * db_print_nodes */ -void db_print_nodes(adb_db_t *db) +void db_print_nodes(adb_db_t* db) { int i = 0; printf("DB nodes list:\n"); - for (NodesMap::iterator iter = ((Adb*)db)->nodesMap.begin(); iter != ((Adb*)db)->nodesMap.end(); iter++) { + for (NodesMap::iterator iter = ((Adb*)db)->nodesMap.begin(); iter != ((Adb*)db)->nodesMap.end(); iter++) + { i++; printf("%-5d) %s\n", i, iter->first.c_str()); } diff -Nru mstflint-4.17.0+1/adb_parser/adb_db.h mstflint-4.21.0+1/adb_parser/adb_db.h --- mstflint-4.17.0+1/adb_parser/adb_db.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_db.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,25 +1,27 @@ -/* - * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. - * +/* + * + * Copyright (C) February 2010, Mellanox Technologies Ltd. ALL RIGHTS RESERVED. + * Copyright (c) 2010-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: - * + * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: - * + * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. - * + * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * 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 @@ -28,12 +30,6 @@ * 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. - - * - * End of legal section ...................................................... - * - * adb_db.h - "C" wrapper module for adb_parser classes. - * This module provides an alternative "C" way to work with AdbInstance objects. * * Version: $Id$ * @@ -44,60 +40,74 @@ #include #include +#include "adb_parser.h" #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif -typedef void adb_db_t; -typedef void adb_node_t; -typedef void adb_field_t; -typedef void adb_limits_map_t; - -typedef enum -{ - DB_FORMAT_STANDARD, - DB_FORMAT_STANDARD_NO_ENUM, - DB_FORMAT_FULL_DETAILS -} dump_format_t; - -// TODO - this API isn't thread-safe because of the error reporting mechanism -// DB Functions -adb_db_t* db_create(); -int db_load(adb_db_t *db, const char *adb_file_path, int add_reserved); -int db_load_from_str(adb_db_t *db, const char *adb_data, int add_reserved); -void db_destroy(adb_db_t *db); -const char* db_get_last_err(); - -adb_limits_map_t* db_create_limits_map(adb_db_t *db); -void db_destroy_limits_map(adb_limits_map_t *limits); - -// Node Functions - *** node contains flat fields (full path leaves) *** -adb_node_t* db_get_node(adb_db_t *db, const char *node_name); -void db_node_destroy(adb_node_t *node); -void db_node_name(adb_node_t *node, char name[]); -int db_node_size(adb_node_t *node); -int db_node_num_of_fields(adb_node_t *node); -adb_field_t* db_node_get_field(adb_node_t *node, int field_idx); -adb_field_t* db_node_get_field_by_path(adb_node_t *node, const char *path, int is_case_sensitive); -bool db_node_dump(adb_node_t *node, u_int8_t buf[], dump_format_t format, FILE *stream); -bool db_node_conditional_dump(adb_node_t *node, u_int8_t buf[], dump_format_t format, - FILE *stream, adb_limits_map_t *values_map); -bool db_node_range_dump(adb_node_t *node, u_int32_t from, u_int32_t to, - u_int8_t buf[], dump_format_t format, FILE *stream); -bool db_node_conditional_range_dump(adb_node_t *node, u_int32_t from, u_int32_t to, - u_int8_t buf[], dump_format_t format, FILE *stream, adb_limits_map_t *values_map); - -// Field Functions -void db_field_name(adb_field_t *field, char name[]); -void db_field_full_name(adb_field_t *field, int skip_level, char name[]); -int db_field_offset(adb_field_t *field); -int db_field_size(adb_field_t *field); -u_int64_t db_field_value(adb_field_t *field, u_int8_t buf[]); -void db_field_set_value(adb_field_t *field, u_int8_t buf[], u_int64_t value); -int db_field_enum(adb_field_t *field, u_int64_t value, char enum_str[]); + typedef void adb_db_t; + typedef void adb_node_t; + typedef void adb_field_t; + typedef void adb_limits_map_t; + + typedef enum + { + DB_FORMAT_STANDARD, + DB_FORMAT_STANDARD_NO_ENUM, + DB_FORMAT_FULL_DETAILS + } dump_format_t; + + // TODO - this API isn't thread-safe because of the error reporting mechanism + // DB Functions + adb_db_t* db_create(); + int db_load(adb_db_t* db, const char* adb_file_path, int add_reserved); + int db_load_from_str(adb_db_t* db, const char* adb_data, int add_reserved); + void db_destroy(adb_db_t* db); + const char* db_get_last_err(); + + adb_limits_map_t* db_create_limits_map(adb_db_t* db); + void db_destroy_limits_map(adb_limits_map_t* limits); + + // Node Functions - *** node contains flat fields (full path leaves) *** + adb_node_t* db_get_node(adb_db_t* db, const char* node_name); + void db_node_destroy(adb_node_t* node); + void db_node_name(adb_node_t* node, char name[]); + int db_node_size(adb_node_t* node); + int db_node_num_of_fields(adb_node_t* node); + adb_field_t* db_node_get_field(adb_node_t* node, int field_idx); + adb_field_t* db_node_get_field_by_path(adb_node_t* node, const char* path, int is_case_sensitive); + bool db_node_dump(adb_node_t* node, u_int8_t buf[], dump_format_t format, FILE* stream); + bool db_node_conditional_dump(adb_node_t* node, + u_int8_t buf[], + dump_format_t format, + FILE* stream, + adb_limits_map_t* values_map); + bool db_node_range_dump(adb_node_t* node, + u_int32_t from, + u_int32_t to, + u_int8_t buf[], + dump_format_t format, + FILE* stream); + bool db_node_conditional_range_dump(adb_node_t* node, + u_int32_t from, + u_int32_t to, + u_int8_t buf[], + dump_format_t format, + FILE* stream, + adb_limits_map_t* values_map); + + // Field Functions + void db_field_name(adb_field_t* field, char name[]); + void db_field_full_name(adb_field_t* field, int skip_level, char name[]); + int db_field_offset(adb_field_t* field); + int db_field_size(adb_field_t* field); + u_int64_t db_field_value(adb_field_t* field, u_int8_t buf[]); + void db_field_set_value(adb_field_t* field, u_int8_t buf[], u_int64_t value); + int db_field_enum(adb_field_t* field, u_int64_t value, char enum_str[]); -// For debug only -void db_print_nodes(adb_db_t *db); + // For debug only + void db_print_nodes(adb_db_t* db); #ifdef __cplusplus } diff -Nru mstflint-4.17.0+1/adb_parser/adb_exceptionHolder.cpp mstflint-4.21.0+1/adb_parser/adb_exceptionHolder.cpp --- mstflint-4.17.0+1/adb_parser/adb_exceptionHolder.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_exceptionHolder.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,109 @@ +/* + * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + * + */ +/*************************** AdbException Implementation ***************************/ + +#include "adb_exceptionHolder.h" + +ExceptionsMap ExceptionHolder::adbExceptionMap; +const string ExceptionHolder::FATAL_EXCEPTION = "FATAL"; +const string ExceptionHolder::ERROR_EXCEPTION = "ERROR"; +const string ExceptionHolder::WARN_EXCEPTION = "WARNING"; +int ExceptionHolder::exceptionCounter = 0; + +AdbException::AdbException() {} + +/** + * Function: AdbException::AdbException + **/ +AdbException::AdbException(const char* fmt, ...) +{ + char tmp[1024]; + va_list args; + va_start(args, fmt); + vsprintf(tmp, fmt, args); + va_end(args); + _msg = tmp; +} + +/** + * Function: AdbException::AdbException + **/ +AdbException::AdbException(string msg) : _msg(msg) {} + +/** + * Function: AdbException::~AdbException + **/ +AdbException::~AdbException() throw() {} + +/** + * Function: AdbException::what + **/ +const char* AdbException::what() const throw() +{ + return _msg.c_str(); +} + +/** + * Function: AdbException::what_s + **/ +string AdbException::what_s() const +{ + return _msg; +} + +int ExceptionHolder::getNumberOfExceptions() +{ + return ExceptionHolder::exceptionCounter; +} + +/** + * * Function: ExceptionHolder::getAdbExceptionsMap + * * This function return the adb exception map + * **/ + +ExceptionsMap ExceptionHolder::getAdbExceptionsMap() +{ + return ExceptionHolder::adbExceptionMap; +} + +/** + * * Function: ExceptionHolder::insertNewException + * * This function take the excpetion type [FATAL:0, ERROR:1, WARNING:2] and the exception string + * * Then it insert it to the adb exception map + * **/ +void ExceptionHolder::insertNewException(const string exceptionType, string exceptionTxt) +{ + ExceptionHolder::adbExceptionMap[exceptionType].push_back(exceptionTxt); + ExceptionHolder::exceptionCounter += 1; +} diff -Nru mstflint-4.17.0+1/adb_parser/adb_exceptionHolder.h mstflint-4.21.0+1/adb_parser/adb_exceptionHolder.h --- mstflint-4.17.0+1/adb_parser/adb_exceptionHolder.h 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_exceptionHolder.h 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,112 @@ +/* + * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + * + */ +/*************************** ExceptionHolder ***************************/ + +#ifndef ADB_EXCEPTIONHOLDER_H +#define ADB_EXCEPTIONHOLDER_H + +#include +#include +#include +#include +#include +#include + +using namespace std; + +typedef vector StringVector; +typedef map ExceptionsMap; + +class ExceptionHolder +{ +public: + // METHODS + static void insertNewException(const string exceptionType, string exceptionTxt); + static ExceptionsMap getAdbExceptionsMap(); + static int getNumberOfExceptions(); + +public: + // VARIABLES + static ExceptionsMap adbExceptionMap; + static const string FATAL_EXCEPTION; + static const string ERROR_EXCEPTION; + static const string WARN_EXCEPTION; + static int exceptionCounter; +}; + +/*************************** AdbException ***************************/ +class AdbException : public std::exception +{ +public: + // Methods + AdbException(); + AdbException(const char* msg, ...) __attribute__((format(__printf__, 2, 3))); + AdbException(string msg); + virtual ~AdbException() throw(); + virtual const char* what() const throw(); + virtual string what_s() const; + +private: + string _msg; +}; + +/** + * Function: ExceptionHolder::getNumberOfExceptions + * This function return the number of exceptions found + **/ + +/*int ExceptionHolder::getNumberOfExceptions() { + return ExceptionHolder::exceptionCounter; +}*/ + +/** + * Function: ExceptionHolder::getAdbExceptionsMap + * This function return the adb exception map + **/ + +/*ExceptionsMap ExceptionHolder::getAdbExceptionsMap() { + return ExceptionHolder::adbExceptionMap; +}*/ + +/** + * Function: ExceptionHolder::insertNewException + * This function take the excpetion type [FATAL:0, ERROR:1, WARNING:2] and the exception string + * Then it insert it to the adb exception map + **/ +/*void ExceptionHolder::insertNewException(const string exceptionType, string exceptionTxt) { + ExceptionHolder::adbExceptionMap[exceptionType].push_back(exceptionTxt); + ExceptionHolder::exceptionCounter += 1; +}*/ + +#endif diff -Nru mstflint-4.17.0+1/adb_parser/adb_expr.cpp mstflint-4.21.0+1/adb_parser/adb_expr.cpp --- mstflint-4.17.0+1/adb_parser/adb_expr.cpp 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_expr.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ - -/* +/* * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. - * + * Copyright (c) 2013-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: - * + * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: - * + * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. - * + * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * 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 @@ -29,34 +29,26 @@ * 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. - * */ #include #include "adb_expr.h" -#include "adb_parser.h" /** * Function: AdbExpr::AdbExpr **/ -AdbExpr::AdbExpr() : _varsMap(0) -{ - -} +AdbExpr::AdbExpr() : _varsMap(0) {} /** * Function: AdbExpr::~AdbExpr **/ -AdbExpr::~AdbExpr() -{ - -} +AdbExpr::~AdbExpr() {} /** * Function: AdbExpr::setVars **/ -void AdbExpr::setVars(map *varsMap) +void AdbExpr::setVars(map* varsMap) { _varsMap = varsMap; } @@ -68,35 +60,44 @@ { switch (status) { - case Expr::ERR_RPAR_EXP: return "Right parentheses expected"; + case Expr::ERR_RPAR_EXP: + return "Right parentheses expected"; - case Expr::ERR_VALUE_EXP: return "Value expected"; + case Expr::ERR_VALUE_EXP: + return "Value expected"; - case Expr::ERR_BIN_EXP: return "Binary operation expected "; + case Expr::ERR_BIN_EXP: + return "Binary operation expected "; - case Expr::ERR_DIV_ZERO: return "Divide zero attempt"; + case Expr::ERR_DIV_ZERO: + return "Divide zero attempt"; - case Expr::ERR_BAD_NUMBER: return "Bad constant syntax"; + case Expr::ERR_BAD_NUMBER: + return "Bad constant syntax"; - case Expr::ERR_BAD_NAME: return "Variable Name not resolved"; + case Expr::ERR_BAD_NAME: + return "Variable Name not resolved"; - default: return "Unknown error"; + default: + return "Unknown error"; } } /** * Function: AdbExpr::ResolveName **/ -int AdbExpr::ResolveName(char *name, u_int64_t *val) +int AdbExpr::ResolveName(char* name, u_int64_t* val) { map::iterator it = _varsMap->find(name); - if (it == _varsMap->end()) { + if (it == _varsMap->end()) + { return ERR_BAD_NAME; } - char *end; + char* end; *val = strtoul(it->second.c_str(), &end, 0); - if (*end != '\0') { + if (*end != '\0') + { return ERR_BAD_NUMBER; } diff -Nru mstflint-4.17.0+1/adb_parser/adb_expr.h mstflint-4.21.0+1/adb_parser/adb_expr.h --- mstflint-4.17.0+1/adb_parser/adb_expr.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_expr.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ - -/* +/* * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. - * + * Copyright (c) 2013-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: - * + * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: - * + * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. - * + * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * 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 @@ -29,7 +29,6 @@ * 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. - * */ @@ -39,6 +38,8 @@ #include #include #include "expr.h" +#include "adb_exceptionHolder.h" + using namespace std; class AdbExpr : public Expr @@ -48,12 +49,12 @@ ~AdbExpr(); static const char* statusStr(int status); - int ResolveName(char *name, u_int64_t *val); + int ResolveName(char* name, u_int64_t* val); void Error(const std::string& message); - void setVars(map *varsMap); + void setVars(map* varsMap); private: - map *_varsMap; + map* _varsMap; }; #endif // ADB_EXPR_H diff -Nru mstflint-4.17.0+1/adb_parser/adb_field.cpp mstflint-4.21.0+1/adb_parser/adb_field.cpp --- mstflint-4.17.0+1/adb_parser/adb_field.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_field.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,174 @@ +/* + * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + * + */ +/*************************** AdbField ***************************/ +/** + * Function: AdbField::AdbField + **/ + +#include "adb_field.h" +#include +#include +#include +#include +#include + +AdbField::AdbField() : + size(0), + offset(0xffffffff), + definedAsArr(false), + lowBound(0), + highBound(0), + unlimitedArr(false), + isReserved(false), + userData(0) +{ +} + +/** + * Function: AdbField::~AdbField + **/ +AdbField::~AdbField() {} + +/** + * Function: AdbField::isLeaf + **/ +bool AdbField::isLeaf() +{ + return subNode.empty(); +} + +/** + * Function: AdbField::isStruct + **/ +bool AdbField::isStruct() +{ + return !isLeaf(); +} + +/** + * Function: AdbField::operator< + **/ +bool AdbField::operator<(AdbField& other) +{ + return offset < other.offset; +} + +/** + * Function: AdbField::isArray + **/ +bool AdbField::isArray() +{ + if (unlimitedArr) + { + return true; + } + else + { + return definedAsArr ? true : lowBound != highBound; + } +} + +/** + * Function: AdbField::arrayLen + **/ +u_int32_t AdbField::arrayLen() +{ + if (unlimitedArr) + { + return 1; + } + else + { + return (highBound - lowBound + 1); + } +} + +/** + * Function: AdbField::isUnlimitedArr + **/ +bool AdbField::isUnlimitedArr() +{ + return unlimitedArr; +} + +/** + * Function: AdbField::isArray + **/ +u_int32_t AdbField::eSize() +{ + if (unlimitedArr) + { + return size; + } + else + { + return size / arrayLen(); + } +} + +/** + * Function: AdbField::print + **/ +void AdbField::print(int indent) +{ + cout << indentString(indent); + cout << "- FIELD - Name: " << name << " offset: 0x" << hex << offset / 32 * 4 << "." << dec << offset % 32 + << " size: 0x" << hex << size / 32 * 4 << "." << dec << size % 32 << " low_bound: " << lowBound + << " high_bound: " << highBound << " sub_node: " << subNode << " isReserved: " << isReserved << endl; +} + +/** + * Function: AdbField::toXml + **/ +string AdbField::toXml(const string& addPrefix) +{ + string xml = "first == "name" || it->first == "subnode" || it->first == "descr") + { + continue; + } + + xml += " " + it->first + "=\"" + encodeXml(it->second) + "\""; + } + + xml += " />"; + return xml; +} diff -Nru mstflint-4.17.0+1/adb_parser/adb_field.h mstflint-4.21.0+1/adb_parser/adb_field.h --- mstflint-4.17.0+1/adb_parser/adb_field.h 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_field.h 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,87 @@ +/* + * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + * + */ +/*************************** AdbField ***************************/ + +#ifndef ADB_FIELD_H +#define ADB_FIELD_H + +#include "adb_xmlCreator.h" +#include +#include + +using namespace xmlCreator; +using namespace std; + +typedef map AttrsMap; + +class AdbField +{ +public: + // Methods + AdbField(); + ~AdbField(); + bool isLeaf(); + bool isStruct(); + bool isArray(); + u_int32_t arrayLen(); + bool isUnlimitedArr(); + u_int32_t eSize(); + string toXml(const string& addPrefix); + + // Operator overloading - useful for sorting + bool operator<(AdbField& other); + + // FOR DEBUG + void print(int indent = 0); + +public: + // Members + string name; + u_int32_t size; // in bits + u_int32_t offset; // in bits (relative to the node start addr) + string desc; + bool definedAsArr; + u_int32_t lowBound; + u_int32_t highBound; + bool unlimitedArr; + string subNode; + AttrsMap attrs; + bool isReserved; + string condition; // field's visibility dynamic condition + + // FOR USER USAGE + void* userData; +}; + +#endif diff -Nru mstflint-4.17.0+1/adb_parser/adb_instance.cpp mstflint-4.21.0+1/adb_parser/adb_instance.cpp --- mstflint-4.17.0+1/adb_parser/adb_instance.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_instance.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,715 @@ +/* + * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + * + */ +/*************************** AdbInstance ***************************/ +/** + * Function: AdbInstance::AdbInstance + **/ + +#include "adb_instance.h" +#include "adb_node.h" +#include "adb_field.h" +#include "adb_expr.h" +#include "adb_exceptionHolder.h" +#include "buf_ops.h" +#include +#include +#include +#include + +string addPathSuffixForArraySupport(string path) +{ + if (path[path.length() - 1] == ']') + { // Won't add suffix if leaf is in Array + return ""; + } + size_t pos = 0; + string suffix = ""; + while ((pos = path.find("[")) != std::string::npos) + { // Add array index number in path to suffix. + size_t end_pos = path.find("]"); + size_t len = end_pos - pos - 1; + suffix = suffix + "_" + path.substr(pos + 1, len); + path.erase(0, end_pos + 1); + } + return suffix; +} + +AdbInstance::AdbInstance() : + fieldDesc(NULL), + nodeDesc(NULL), + parent(NULL), + offset(0xffffffff), + size(0), + maxLeafSize(0), + arrIdx(0), + isNameBeenExtended(false), + unionSelector(NULL), + isDiff(false), + userData(NULL) + +{ +} + +/** + * Function: AdbInstance::AdbInstance + **/ +AdbInstance::~AdbInstance() +{ + for (size_t i = 0; i < subItems.size(); i++) + delete subItems[i]; +} + +/** + * Function: AdbInstance::isLeaf + **/ +bool AdbInstance::isLeaf() +{ + return nodeDesc == NULL; +} + +/** + * Function: isUnionisUnion + **/ +bool AdbInstance::isUnion() +{ + return isNode() && nodeDesc->isUnion; +} + +/** + * Function: AdbInstance::isStruct + **/ +bool AdbInstance::isStruct() +{ + return isNode() && !isUnion(); +} + +/** + * Function: AdbInstance::isNode + **/ +bool AdbInstance::isNode() +{ + return !isLeaf(); +} + +/** + * Function: AdbInstance::isNode + **/ +bool AdbInstance::isPartOfArray() +{ + return fieldDesc->isArray(); +} + +/** + * Function: AdbInstance::fullName + **/ +string AdbInstance::fullName(int skipLevel) +{ + list fnList; + AdbInstance* p = parent; + + fnList.push_front(name); + while (p != NULL) + { + fnList.push_front(p->name); + p = p->parent; + } + + if ((int)fnList.size() > skipLevel) + { + while (skipLevel--) + { + fnList.pop_front(); + } + + return boost::algorithm::join(fnList, "."); + } + else + { + return fnList.back(); + } +} + +/** + * Function: AdbInstance::fullName + **/ +bool AdbInstance::isReserved() +{ + return fieldDesc->isReserved; +} + +/** + * Function: AdbInstance::fullName + **/ +u_int32_t AdbInstance::dwordAddr() +{ + return (offset >> 5) << 2; +} + +/** + * Function: AdbInstance::fullName + **/ +u_int32_t AdbInstance::startBit() +{ + return offset % 32; +} + +/** + * Function: AdbInstance::operator< + **/ +bool AdbInstance::operator<(const AdbInstance& other) +{ + return offset < other.offset; +} + +/** + * Function: AdbInstance::getChildByPath + **/ +AdbInstance* AdbInstance::getChildByPath(const string& path, bool isCaseSensitive) +{ + string effPath = isCaseSensitive ? path : boost::algorithm::to_lower_copy(path); + if (effPath[0] == '.') + { + effPath.erase(0, 1); + } + + size_t idx = effPath.find("."); + string childName = idx == string::npos ? effPath : effPath.substr(0, idx); + string grandChildPath = idx == string::npos ? string() : effPath.substr(idx + 1); + + if (path.empty()) + { + return this; + } + + // Search for childName + AdbInstance* child = NULL; + for (size_t i = 0; i < subItems.size(); i++) + { + string subName = isCaseSensitive ? subItems[i]->name : boost::algorithm::to_lower_copy(subItems[i]->name); + if (subName == childName) + { + child = subItems[i]; + break; + } + } + + if (!child) + { + return NULL; + } + + // if grandChildPath isn't empty this means we need to continue and find the desired field in grand child node + return grandChildPath.empty() ? child : child->getChildByPath(grandChildPath, isCaseSensitive); +} + +/** + * Function: AdbInstance::findChild + **/ +vector AdbInstance::findChild(const string& childName, bool isCaseSensitive, bool by_inst_name) +{ + string effName = isCaseSensitive ? childName : boost::algorithm::to_lower_copy(childName); + vector childList; + + if (by_inst_name || isLeaf()) + { + if (name == childName) + { + childList.push_back(this); + } + } + else + { + if (isNode() && nodeDesc->name == childName) + { + childList.push_back(this); + } + } + + // do that recursively for all child items + for (size_t i = 0; i < subItems.size(); i++) + { + vector l = subItems[i]->findChild(effName, true); + childList.insert(childList.end(), l.begin(), l.end()); + } + + return childList; +} + +/** + * Function: AdbInstance::getAttr + **/ +string AdbInstance::getInstanceAttr(const string& attrName) const +{ + AttrsMap::const_iterator it = instAttrsMap.find(attrName); + if (it == instAttrsMap.end()) + { + if (fieldDesc) + { + it = fieldDesc->attrs.find(attrName); + if (it == fieldDesc->attrs.end()) + { + return string(); + } + } + else + { + return string(); + } + } + return it->second; +} + +/** + * Function: AdbInstance::getInstanceAttrIterator + **/ +AttrsMap::iterator AdbInstance::getInstanceAttrIterator(const string& attrName, bool& found) +{ + AttrsMap::iterator it = instAttrsMap.find(attrName); + found = false; + if (it != instAttrsMap.end()) + { + found = true; + } + else if (fieldDesc) + { + it = fieldDesc->attrs.find(attrName); + if (it != fieldDesc->attrs.end()) + { + found = true; + } + } + return it; +} + +/** + * Function: AdbInstance::setInstanceAttr + **/ +void AdbInstance::setInstanceAttr(const string& attrName, const string& attrValue) +{ + instAttrsMap[attrName] = attrValue; +} + +/* + * Function: AdbInstance::copyAllInstanceAttr - copies all attrs from attributes structure + **/ +void AdbInstance::copyAllInstanceAttr(AttrsMap& attsToCopy) +{ + instAttrsMap = attsToCopy; +} + +/** + * Function: AdbInstance::getFullInstanceAttrsMapCopy - returns the instance attr map by val + **/ +AttrsMap AdbInstance::getFullInstanceAttrsMapCopy() +{ + AttrsMap tmpCopy; + // copy first the fields attr map + for (AttrsMap::iterator it = fieldDesc->attrs.begin(); it != fieldDesc->attrs.end(); it++) + { + tmpCopy[it->first] = it->second; + } + // copy the overriden variables - in instAttrsMap + for (AttrsMap::iterator it = instAttrsMap.begin(); it != instAttrsMap.end(); it++) + { + tmpCopy[it->first] = it->second; + } + return tmpCopy; +} + +/** + * Function: AdbInstance::setVarsMap - set an item in vars map + **/ +void AdbInstance::setVarsMap(const string& attrName, const string& attrValue) +{ + varsMap[attrName] = attrValue; +} + +/** + * Function: AdbInstance::setVarsMap - set an item in vars map + **/ +void AdbInstance::setVarsMap(const AttrsMap& AttrsMap) +{ + varsMap = AttrsMap; +} + +/** + * Function: AdbInstance::setVarsMap - returns a copy of vars map + **/ +AttrsMap AdbInstance::getVarsMap() +{ + return varsMap; +} + +/** + * Function: AdbInstance::isEnumExists + **/ +bool AdbInstance::isEnumExists() +{ + return !getInstanceAttr("enum").empty(); +} + +/** + * Function: AdbInstance::enumToInt + **/ +bool AdbInstance::enumToInt(const string& name, u_int64_t& val) +{ + vector enumValues; + string enums = getInstanceAttr("enum"); + boost::algorithm::split(enumValues, enums, boost::is_any_of(string(","))); + + for (size_t i = 0; i < enumValues.size(); i++) + { + vector pair; + string trimedEnumValues = enumValues[i]; + boost::algorithm::trim(trimedEnumValues); + boost::algorithm::split(pair, trimedEnumValues, boost::is_any_of(string("="))); + + if (pair.size() != 2) + { + continue; + } + + char* end; + if (pair[0] == name) + { + val = strtoul(pair[1].c_str(), &end, 0); + if (*end != '\0') + { + return false; + } + return true; + } + } + + return false; +} + +/** + * Function: AdbInstance::intToEnum + **/ +bool AdbInstance::intToEnum(u_int64_t val, string& valName) +{ + vector enumValues; + string enums = getInstanceAttr("enum"); + boost::algorithm::split(enumValues, enums, boost::is_any_of(string(","))); + + for (size_t i = 0; i < enumValues.size(); i++) + { + vector pair; + string trimedEnumValues = enumValues[i]; + boost::algorithm::trim(trimedEnumValues); + boost::algorithm::split(pair, trimedEnumValues, boost::is_any_of(string("="))); + + if (pair.size() != 2) + { + continue; + } + + char* end; + u_int64_t tmp = strtoul(pair[1].c_str(), &end, 0); + if (*end != '\0') + { + continue; + } + + if (tmp == val) + { + valName = pair[0]; + return true; + } + } + + return false; +} + +/** + * Function: AdbInstance::getEnumMap + **/ +map AdbInstance::getEnumMap() +{ + map enumMap; + vector enumValues; + string enums = getInstanceAttr("enum"); + boost::algorithm::split(enumValues, enums, boost::is_any_of(string(","))); + + for (size_t i = 0; i < enumValues.size(); i++) + { + vector pair; + string trimedEnumValues = enumValues[i]; + boost::algorithm::trim(trimedEnumValues); + boost::algorithm::split(pair, trimedEnumValues, boost::is_any_of(string("="))); + if (pair.size() != 2) + { + throw AdbException("Can't parse enum: " + enumValues[i]); + // continue; + } + + char* end; + u_int64_t intVal = strtoul(pair[1].c_str(), &end, 0); + if (*end != '\0') + { + throw AdbException(string("Can't evaluate enum (") + pair[0].c_str() + "): " + pair[1].c_str()); + // continue; + } + + enumMap[pair[0]] = intVal; + } + + return enumMap; +} + +/** + * Function: AdbInstance::getEnumValues + **/ +vector AdbInstance::getEnumValues() +{ + vector values; + vector enumValues; + string enums = getInstanceAttr("enum"); + boost::algorithm::split(enumValues, enums, boost::is_any_of(string(","))); + + for (size_t i = 0; i < enumValues.size(); i++) + { + vector pair; + string trimedEnumValues = enumValues[i]; + boost::algorithm::trim(trimedEnumValues); + boost::algorithm::split(pair, trimedEnumValues, boost::is_any_of(string("="))); + if (pair.size() != 2) + { + continue; + } + + char* end; + u_int64_t intVal = strtoul(pair[1].c_str(), &end, 0); + if (*end != '\0') + { + continue; + } + + values.push_back(intVal); + } + + return values; +} + +/** + * Function: AdbInstance::getUnionSelectedNodeName + **/ +AdbInstance* AdbInstance::getUnionSelectedNodeName(const u_int64_t& selectorVal) +{ + if (!isUnion()) + { + throw AdbException("This is not union node (%s), can't get selected node name", fullName().c_str()); + } + + if (!unionSelector) + { + throw AdbException("Can't find selector for union: " + name); + } + + map selectorValMap = unionSelector->getEnumMap(); + for (map::iterator it = selectorValMap.begin(); it != selectorValMap.end(); it++) + { + if (it->second == selectorVal) + { + const string& selectorEnum = it->first; + // search for the sub instance with the "selected_by" attribute == selectorEnum + for (size_t i = 0; i < subItems.size(); i++) + { + if (subItems[i]->getInstanceAttr("selected_by") == selectorEnum) + { + return subItems[i]; + } + } + throw AdbException("Found selector value (" + selectorEnum + ") is defined for selector field (" + + unionSelector->name + ") but no appropriate subfield of this union was found"); + } + } + + throw AdbException("Union selector field (" + unionSelector->name + ") doesn't define selector value (" + + boost::lexical_cast(selectorVal)); +} + +/** + * Function: AdbInstance::getUnionSelectedNodeName + **/ +AdbInstance* AdbInstance::getUnionSelectedNodeName(const string& selectorEnum) +{ + if (!isUnion()) + { + throw AdbException("This is not union node (%s), can't get selected node name", fullName().c_str()); + } + + if (!unionSelector) + { + throw AdbException("Can't find selector for union: " + name); + } + + for (size_t i = 0; i < subItems.size(); i++) + { + if (subItems[i]->getInstanceAttr("selected_by") == selectorEnum) + { + return subItems[i]; + } + } + + throw AdbException("Union selector field (" + unionSelector->name + ") doesn't define a selector value (" + + selectorEnum + ")"); +} + +/** + * Function: AdbInstance::isConditionalNode + * Throws exception: AdbException + **/ +bool AdbInstance::isConditionalNode() +{ + if (isNode()) + { + return (getInstanceAttr("is_conditional") == "1"); + } + return false; +} + +/** + * Function: AdbInstance::isConditionValid + * Throws exception: AdbException + **/ +bool AdbInstance::isConditionValid(map* valuesMap) +{ + u_int64_t res; + AdbExpr expressionChecker; + int status = -1; + char* condExp; + char* exp; + + if (fieldDesc->condition.empty()) + { + return true; + } + + condExp = new char[fieldDesc->condition.size() + 1]; + exp = condExp; + if (!exp) + { + throw AdbException("Memory allocation error"); + } + strcpy(exp, fieldDesc->condition.c_str()); + + expressionChecker.setVars(valuesMap); + try + { + status = expressionChecker.expr(&exp, &res); + } + catch (AdbException& e) + { + delete[] condExp; + throw AdbException(string("AdbException: ") + e.what_s()); + } + delete[] condExp; + + if (status < 0) + { + throw AdbException(string("Error evaluating expression \"") + fieldDesc->condition.c_str() + + "\" : " + AdbExpr::statusStr(status)); + } + + return (status >= 0 && res != 0); +} + +/** + * Function: AdbInstance::getLeafFields + **/ +vector AdbInstance::getLeafFields(bool extendenName) +{ + vector fields; + + for (size_t i = 0; i < subItems.size(); i++) + { + if (subItems[i]->isNode()) + { + vector subFields = subItems[i]->getLeafFields(extendenName); + fields.insert(fields.end(), subFields.begin(), subFields.end()); + } + else + { + if (extendenName && !subItems[i]->isNameBeenExtended) + { + if (subItems[i]->parent->fieldDesc->subNode == "uint64") + { + subItems[i]->name = subItems[i]->parent->name + "_" + subItems[i]->name; + } + else + { + subItems[i]->name = subItems[i]->name + addPathSuffixForArraySupport(subItems[i]->fullName()); + } + subItems[i]->isNameBeenExtended = true; + } + fields.push_back(subItems[i]); + } + } + + return fields; +} + +/** + * Function: AdbInstance::pushBuf + **/ +void AdbInstance::pushBuf(u_int8_t* buf, u_int64_t value) +{ + push_to_buf(buf, offset, size, value); +} + +/** + * Function: AdbInstance::popBuf + **/ +u_int64_t AdbInstance::popBuf(u_int8_t* buf) +{ + return pop_from_buf(buf, offset, size); +} + +/** + * Function: AdbInstance::print + **/ +void AdbInstance::print(int indent) +{ + string indentStr = indentString(indent); + printf("%sfullName: %s, offset: 0x%x.%d, size: 0x%x.%d, isNode:%d, isUnion:%d\n", indentStr.c_str(), + fullName().c_str(), (offset >> 5) << 2, offset % 32, (size >> 5) << 2, size % 32, isNode(), isUnion()); + + if (isNode()) + { + for (size_t i = 0; i < subItems.size(); i++) + subItems[i]->print(indent + 1); + } +} diff -Nru mstflint-4.17.0+1/adb_parser/adb_instance.h mstflint-4.21.0+1/adb_parser/adb_instance.h --- mstflint-4.17.0+1/adb_parser/adb_instance.h 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_instance.h 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,116 @@ +/* + * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + * + */ +/*************************** AdbInstance ***************************/ + +#ifndef ADB_INSTANCE_H +#define ADB_INSTANCE_H + +#include "adb_xmlCreator.h" +#include +#include +#include + +using namespace std; +using namespace xmlCreator; + +typedef map AttrsMap; +class AdbField; +class AdbNode; + +class AdbInstance +{ +public: + // Methods + AdbInstance(); + ~AdbInstance(); + bool isLeaf(); + bool isUnion(); + bool isStruct(); + bool isNode(); + bool isPartOfArray(); + string fullName(int skipLevel = 0); + bool isReserved(); + u_int32_t dwordAddr(); + u_int32_t startBit(); + bool isEnumExists(); + bool enumToInt(const string& name, u_int64_t& val); // false means no enum value found + bool intToEnum(u_int64_t val, string& valName); // false means no enum name found + map getEnumMap(); + vector getEnumValues(); + AdbInstance* getUnionSelectedNodeName(const u_int64_t& selectorVal); + AdbInstance* getUnionSelectedNodeName(const string& selectorEnum); + bool operator<(const AdbInstance& other); + bool isConditionalNode(); + bool isConditionValid(map* valuesMap); + // DB like access methods + AdbInstance* getChildByPath(const string& path, bool isCaseSensitive = true); + vector findChild(const string& name, bool isCaseSensitive = true, bool by_inst_name = false); + string getInstanceAttr(const string& attrName) const; + AttrsMap::iterator getInstanceAttrIterator(const string& attrName, bool& isEnd); + void setInstanceAttr(const string& attrName, const string& attrValue); + void copyAllInstanceAttr(AttrsMap& attsToCopy); + AttrsMap getFullInstanceAttrsMapCopy(); + void setVarsMap(const string& attrName, const string& attrValue); + void setVarsMap(const AttrsMap& AttrsMap); + AttrsMap getVarsMap(); + vector getLeafFields(bool extendedName); // Get all leaf fields + void pushBuf(u_int8_t* buf, u_int64_t value); + u_int64_t popBuf(u_int8_t* buf); + int instAttrsMapLen() { return instAttrsMap.size(); } + // FOR DEBUG + void print(int indent = 0); + +public: + // Members + AdbField* fieldDesc; + AdbNode* nodeDesc; + AdbInstance* parent; + string name; // instance name + vector subItems; + u_int32_t offset; // Global offset in bits (Relative to 0) + u_int32_t size; // in bits + u_int32_t maxLeafSize; // in bits for DS alignment check + u_int32_t arrIdx; + bool isNameBeenExtended; + AdbInstance* unionSelector; // For union instances only + bool isDiff; + // FOR USER USAGE + void* userData; + +private: + AttrsMap instAttrsMap; // Attributes after evaluations and array expanding + AttrsMap varsMap; // all variables relevant to this item after evaluation +}; + +#endif diff -Nru mstflint-4.17.0+1/adb_parser/adb_logfile.cpp mstflint-4.21.0+1/adb_parser/adb_logfile.cpp --- mstflint-4.17.0+1/adb_parser/adb_logfile.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_logfile.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,79 @@ +/* + * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + * + */ + +#include "adb_logfile.h" + +#define WRITE "w" + +LogFile::LogFile() : _logFile(NULL) {} + +void LogFile::init(string logFileName, bool allowMultipleExceptions) +{ + if (logFileName.compare("") != 0) + { + _logFile = fopen(logFileName.c_str(), WRITE); + if (!_logFile) + { + string _lastError = "Can't open file (" + logFileName + ") for writing: " + strerror(errno); + if (allowMultipleExceptions) + { + ExceptionHolder::insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); + } + else + { + throw AdbException(_lastError); + } + } + } + else + { + _logFile = NULL; + } +} + +LogFile::~LogFile() +{ + if (_logFile) + { + fclose(_logFile); + } +} + +void LogFile::appendLogFile(string str) +{ + if (_logFile) + { + fprintf(_logFile, "%s", str.c_str()); + } +} diff -Nru mstflint-4.17.0+1/adb_parser/adb_logfile.h mstflint-4.21.0+1/adb_parser/adb_logfile.h --- mstflint-4.17.0+1/adb_parser/adb_logfile.h 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_logfile.h 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,60 @@ +/* + * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + * + */ +/*************************** LogFile ***************************/ + +#ifndef ADB_LOGFILE_H +#define ADB_LOGFILE_H + +#include +#include +#include +#include +#include "adb_exceptionHolder.h" +#include + +using namespace std; + +class LogFile +{ +public: + LogFile(); + void init(string logFileName, bool allowMultipleExceptions); + ~LogFile(); + void appendLogFile(string adbFileName); + +private: + FILE* _logFile; +}; + +#endif diff -Nru mstflint-4.17.0+1/adb_parser/adb_node.cpp mstflint-4.21.0+1/adb_parser/adb_node.cpp --- mstflint-4.17.0+1/adb_parser/adb_node.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_node.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,122 @@ +/* + * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + * + */ +/*************************** AdbNode ***************************/ + +/** + * * Function: AdbNode::AdbNode + * **/ + +#include "adb_node.h" +#include "adb_field.h" +#include + +#if __cplusplus >= 201402L +#include +#else +#include +using namespace boost; +#endif + +#include +#include +#include +#include + +AdbNode::AdbNode() : size(0), _maxLeafSize(0), isUnion(false), inLayout(false), lineNumber(-1), userData(0) {} + +/** + * * Function: AdbNode::~AdbNode + * **/ +AdbNode::~AdbNode() +{ + for (size_t i = 0; i < fields.size(); i++) + { + delete fields[i]; + } + fields.clear(); + for (size_t i = 0; i < condFields.size(); i++) + { + delete condFields[i]; + } + condFields.clear(); +} + +/** + * * Function: AdbNode::toXml + * **/ +string AdbNode::toXml(const string& addPrefix) +{ + string xml = "first == "name" || it->first == "descr") + { + continue; + } + + xml += " " + it->first + "=\"" + encodeXml(it->second) + "\""; + } + xml += " >\n"; + + FieldsList allFields = fields; + allFields.insert(allFields.end(), condFields.begin(), condFields.end()); + stable_sort(allFields.begin(), allFields.end(), compareFieldsPtr); + + for (size_t i = 0; i < allFields.size(); i++) + { + if (allFields[i]->isReserved) + { + continue; + } + + xml += "\t" + allFields[i]->toXml(addPrefix) + "\n"; + } + + xml += ""; + return xml; +} + +/** + * * Function: AdbNode::print + * **/ +void AdbNode::print(int indent) +{ + cout << indentString(indent); + cout << "+ Node Name: " << name << " size: 0x" << hex << size / 32 * 4 << "." << dec << size % 32 + << " isUnion: " << isUnion << " Description: " << desc << endl; + + cout << indentString(indent) << "Fields:" << endl; + for (size_t i = 0; i < fields.size(); i++) + fields[i]->print(indent + 1); +} diff -Nru mstflint-4.17.0+1/adb_parser/adb_node.h mstflint-4.21.0+1/adb_parser/adb_node.h --- mstflint-4.17.0+1/adb_parser/adb_node.h 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_node.h 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,82 @@ +/* + * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + * + */ +/*************************** AdbNode ***************************/ + +#ifndef ADB_NODE_H +#define ADB_NODE_H + +#include "adb_xmlCreator.h" +#include +#include +#include + +using namespace std; +using namespace xmlCreator; + +class AdbField; + +typedef map AttrsMap; +typedef vector FieldsList; +class AdbNode +{ +public: + // Methods + AdbNode(); + ~AdbNode(); + string toXml(const string& addPrefix); + + // FOR DEBUG + void print(int indent = 0); + +public: + // Members + string name; + u_int32_t size; // in bits + u_int32_t _maxLeafSize; // in bits + bool isUnion; + string desc; + FieldsList fields; + FieldsList condFields; // Field that weren't instantiated due to not satisfied condition + AttrsMap attrs; + bool inLayout; + + // defined in + string fileName; + int lineNumber; + + // FOR USER USAGE + void* userData; +}; + +#endif diff -Nru mstflint-4.17.0+1/adb_parser/adb_parser.cpp mstflint-4.21.0+1/adb_parser/adb_parser.cpp --- mstflint-4.17.0+1/adb_parser/adb_parser.cpp 2021-06-29 08:49:47.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_parser.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -1,12 +1,13 @@ -/* +/* * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. - * + * Copyright (c) 2012-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: - * + * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: @@ -19,7 +20,7 @@ * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * 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 @@ -28,24 +29,22 @@ * 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. - * */ +/*************************** Adb ***************************/ +/** + * Function: Adb::Adb + **/ -#include -#include -#include -#include -#include -#include +#include "adb_parser.h" +#include "adb_instance.h" +#include "buf_ops.h" #include #include #include #include +#include #include -#include -#include "adb_parser.h" -#include "buf_ops.h" #if __cplusplus >= 201402L #include @@ -54,217 +53,33 @@ using namespace boost; #endif -#ifdef __WIN__ -#include -#define OS_PATH_SEP "\\" -#else -#define OS_PATH_SEP "/" -#endif - -#ifdef __linux__ -#define LC_ALL_HINT " using `export LC_ALL=C`" -#else -#define LC_ALL_HINT "" -#endif - -#define PROGRESS_NODE_CNT 100 // each 100 parsed node call progress callback - -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#endif -#ifndef MAX -#define MAX(a, b) ((a) > (b) ? (a) : (b)) -#endif - -#define CHECK_RUNTIME_ERROR(e) ((strstr(e.what(), "locale::facet::_S_create_c_locale") != NULL) ? \ - string("Encoding error, please set locale encoding to C") + LC_ALL_HINT + "." : \ - string("runtime_error: ") + e.what()) - -#define WRITE "w" - -string encodeXml(const string& data) { - std::string buffer; - buffer.reserve(data.size()); - for (size_t pos = 0; pos != data.size(); ++pos) { - switch (data[pos]) { - case '&': - buffer.append("&"); - break; - - case '\"': - buffer.append("""); - break; - - case '\'': - buffer.append("'"); - break; - - case '<': - buffer.append("<"); - break; - - case '>': - buffer.append(">"); - break; - - default: - buffer.append(1, data.at(pos)); - break; - } - } - - return buffer; -} - -string addPathSuffixForArraySupport(string path) { - if (path[path.length()-1] == ']') { //Won't add suffix if leaf is in Array - return ""; - } - size_t pos = 0; - string suffix = ""; - while ((pos = path.find("[")) != std::string::npos) {//Add array index number in path to suffix. - size_t end_pos = path.find("]"); - size_t len = end_pos - pos - 1; - suffix = suffix + "_" + path.substr(pos+1, len); - path.erase(0, end_pos+1); - } - return suffix; -} - -string indentString(int i) { - string s; - while (i--) { - s += "\t"; - } - return s; -} - -int dword(int bits) { - if (bits < 0) { - bits -= 31; - } - - return (bits / 32) * 4; -} - -int startBit(int bits) { - return bits % 32; -} - -string formatAddr(u_int32_t offs, u_int32_t size) { - char str[64]; - sprintf(str, "0x%x.%u:%u", dword(offs), startBit(offs), size); - return str; -} - -/** - * Function: AdbParser::compareFieldsPtr - **/ -template -bool compareFieldsPtr(T *f1, T *f2) { - return (*f1) < (*f2); -} - -/*************************** ExceptionHolder ***************************/ -class ExceptionHolder { -public: - // METHODS - static void insertNewException(const string exceptionType, string exceptionTxt); - static ExceptionsMap getAdbExceptionsMap(); - static int getNumberOfExceptions(); -public: - // VARIABLES - static ExceptionsMap adbExceptionMap; - static const string FATAL_EXCEPTION; - static const string ERROR_EXCEPTION; - static const string WARN_EXCEPTION; - static int exceptionCounter; -}; -ExceptionsMap ExceptionHolder::adbExceptionMap; -const string ExceptionHolder::FATAL_EXCEPTION = "FATAL"; -const string ExceptionHolder::ERROR_EXCEPTION = "ERROR"; -const string ExceptionHolder::WARN_EXCEPTION = "WARNING"; -int ExceptionHolder::exceptionCounter = 0; - -/** - * Function: ExceptionHolder::getNumberOfExceptions - * This function return the number of exceptions found - **/ - -int ExceptionHolder::getNumberOfExceptions() { - return ExceptionHolder::exceptionCounter; -} - -/** - * Function: ExceptionHolder::getAdbExceptionsMap - * This function return the adb exception map - **/ - -ExceptionsMap ExceptionHolder::getAdbExceptionsMap() { - return ExceptionHolder::adbExceptionMap; -} - -/** - * Function: ExceptionHolder::insertNewException - * This function take the excpetion type [FATAL:0, ERROR:1, WARNING:2] and the exception string - * Then it insert it to the adb exception map - **/ -void ExceptionHolder::insertNewException(const string exceptionType, string exceptionTxt) { - ExceptionHolder::adbExceptionMap[exceptionType].push_back(exceptionTxt); - ExceptionHolder::exceptionCounter += 1; -} - -LogFile::LogFile() : _logFile(NULL) { -} - -void LogFile::init(string logFileName, bool allowMultipleExceptions) { - if (logFileName.compare("") != 0) { - _logFile = fopen(logFileName.c_str(), WRITE); - if (!_logFile) { - string _lastError = "Can't open file (" + logFileName + ") for writing: " - + strerror(errno); - if (allowMultipleExceptions) { - ExceptionHolder::insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); - } else { - throw AdbException(_lastError); - } - } - } else { - _logFile = NULL; - } -} - -LogFile::~LogFile() { - if (_logFile) { - fclose (_logFile); - } -} - -void LogFile::appendLogFile(string str) { - if (_logFile) { - fprintf(_logFile, "%s", str.c_str()); - } -} +using namespace xmlCreator; -/*************************** AdbParser ***************************/ -class AdbParser { +class AdbParser +{ public: // METHODS - AdbParser(string fileName, Adb *adbCtxt, bool addReserved = false, - AdbProgress *progressObj = NULL, bool strict = true, - string includePath = "", bool enforceExtraChecks = false); + AdbParser(string fileName, + Adb* adbCtxt, + bool addReserved = false, + AdbProgress* progressObj = NULL, + bool strict = true, + string includePath = "", + bool enforceExtraChecks = false, + bool checkDsAlign = false, + bool enforceGuiChecks = false); ~AdbParser(); bool load(); - bool loadFromString(const char *adbString); + bool loadFromString(const char* adbString); string getError(); static string descXmlToNative(const string& desc); static string descNativeToXml(const string& desc); - static void includeAllFilesInDir(AdbParser *adbParser, string dirPath, - int lineNumber = -1); + static void includeAllFilesInDir(AdbParser* adbParser, string dirPath, int lineNumber = -1); static bool checkSpecialChars(string tagName); static bool checkBigger32(string num); static bool checkHEXFormat(string addr); + static bool checkAttrExist(const XML_Char** atts, const XML_Char* name); static void setAllowMultipleExceptionsTrue(); public: @@ -273,48 +88,59 @@ private: // METHODS string findFile(string fileName); - static void addIncludePaths(Adb *adbCtxt, string includePaths); - static void includeFile(AdbParser *adbParser, string fileName, int lineNumber = -1); - static void startElement(void *adbParser, const XML_Char *name, const XML_Char **atts); - - static void startNodesDefElement(const XML_Char **atts, AdbParser *adbParser); - static void startEnumElement(const XML_Char **atts, AdbParser *adbParser, const int lineNumber); - static void startConfigElement(const XML_Char **atts, AdbParser *adbParser, const int lineNumber); - static void startInfoElement(const XML_Char **atts, AdbParser *adbParser); - static void startIncludeElement(const XML_Char **atts, AdbParser *adbParser, const int lineNumber); - static void startInstOpAttrReplaceElement(const XML_Char **atts, AdbParser *adbParser, const int lineNumber); - static void startNodeElement(const XML_Char **atts, AdbParser *adbParser, const int lineNumber); - static void startFieldElement(const XML_Char **atts, AdbParser *adbParser, const int lineNumber); - - static void endElement(void *adbParser, const XML_Char *name); - static void addReserved(vector &reserveds, u_int32_t offset, u_int32_t size); - static int attrCount(const XML_Char **atts); - static string attrValue(const XML_Char **atts, const XML_Char *attrName); - static string attrName(const XML_Char **atts, int i); - static string attrValue(const XML_Char **atts, int i); + static void addIncludePaths(Adb* adbCtxt, string includePaths); + static void includeFile(AdbParser* adbParser, string fileName, int lineNumber = -1); + static void startElement(void* adbParser, const XML_Char* name, const XML_Char** atts); + + static void startNodesDefElement(const XML_Char** atts, AdbParser* adbParser); + static void startEnumElement(const XML_Char** atts, AdbParser* adbParser, const int lineNumber); + static void startConfigElement(const XML_Char** atts, AdbParser* adbParser, const int lineNumber); + static void startInfoElement(const XML_Char** atts, AdbParser* adbParser); + static void startIncludeElement(const XML_Char** atts, AdbParser* adbParser, const int lineNumber); + static void startInstOpAttrReplaceElement(const XML_Char** atts, AdbParser* adbParser, const int lineNumber); + static void startNodeElement(const XML_Char** atts, AdbParser* adbParser, const int lineNumber); + static void startFieldElement(const XML_Char** atts, AdbParser* adbParser, const int lineNumber); + + static void endElement(void* adbParser, const XML_Char* name); + static void addReserved(vector& reserveds, u_int32_t offset, u_int32_t size); + static int attrCount(const XML_Char** atts); + static string attrValue(const XML_Char** atts, const XML_Char* attrName); + static string attrName(const XML_Char** atts, int i); + static string attrValue(const XML_Char** atts, int i); + static bool is_inst_ifdef_exist_and_correct_project(const XML_Char** atts, AdbParser* adbParser); static u_int32_t addr2int(string& s); static u_int32_t dword(u_int32_t offset); static u_int32_t startBit(u_int32_t offset); - static bool raiseException(bool allowMultipleExceptions, string exceptionTxt, - string addedMsg, const string expType); + static bool raiseException(bool allowMultipleExceptions, string exceptionTxt, string addedMsg, const string expType); private: // MEMBERS - Adb *_adbCtxt; + Adb* _adbCtxt; XML_Parser _xmlParser; string _fileName; string _lastError; bool _addReserved; int _progressCnt; - AdbProgress *_progressObj; + AdbProgress* _progressObj; bool _strict; + bool _checkDsAlign; + bool skipNode; string _includePath; string _currentTagValue; - AdbNode *_currentNode; - AdbField *_currentField; - AdbConfig *_currentConfig; + AdbNode* _currentNode; + AdbField* _currentField; + AdbConfig* _currentConfig; bool _instanceOps; bool _enforceExtraChecks; + bool _enforceGuiChecks; + string _nname_pattern; + string _fname_pattern; + string _enum_pattern; + std::map defines_map; + std::set field_attr_names_set; + std::set field_spec_attr; + std::vector field_mand_attr; + std::map attr_pattern; static const string TAG_NODES_DEFINITION; static const string TAG_RCS_HEADERS; @@ -330,7 +156,9 @@ static const string TAG_ATTR_BIG_ENDIAN; static const string TAG_ATTR_SINGLE_ENTRY_ARR; static const string TAG_ATTR_INCLUDE_PATH; + static const string TAG_ATTR_DEFINE_PATTERN; }; + bool AdbParser::allowMultipleExceptions = false; const string AdbParser::TAG_NODES_DEFINITION = "NodesDefinition"; const string AdbParser::TAG_RCS_HEADERS = "RCSheaders"; @@ -346,3248 +174,3226 @@ const string AdbParser::TAG_ATTR_BIG_ENDIAN = "big_endian_arr"; const string AdbParser::TAG_ATTR_SINGLE_ENTRY_ARR = "single_entry_arr"; const string AdbParser::TAG_ATTR_INCLUDE_PATH = "include_path"; - -/*************************** AdbNode ***************************/ +const string AdbParser::TAG_ATTR_DEFINE_PATTERN = "([A-Za-z_]\\w*)=(\\w+)"; /** - * Function: AdbNode::AdbNode + * Function: AdbParser::compareFieldsPtr **/ -AdbNode::AdbNode() : - size(0), isUnion(false), lineNumber(-1), userData(0) { - -} +/*************************** AdbParser Implementation ***************************/ /** - * Function: AdbNode::~AdbNode + * Function: AdbParser::AdbParser **/ -AdbNode::~AdbNode() { - for (size_t i = 0; i < fields.size(); i++) { - delete fields[i]; - } - fields.clear(); - for (size_t i = 0; i < condFields.size(); i++) { - delete condFields[i]; - } - condFields.clear(); -} +AdbParser::AdbParser(string fileName, + Adb* adbCtxt, + bool addReserved, + AdbProgress* progressObj, + bool strict, + string includePath, + bool enforceExtraChecks, + bool _checkDsAlign, + bool _enforceGuiChecks) : + _adbCtxt(adbCtxt), + _fileName(fileName), + _addReserved(addReserved), + _progressCnt(0), + _progressObj(progressObj), + _strict(strict), + _checkDsAlign(_checkDsAlign), + skipNode(false), + _includePath(includePath), + _currentNode(0), + _currentField(0), + _currentConfig(0), + _enforceGuiChecks(_enforceGuiChecks), + _nname_pattern(".*"), + _fname_pattern(".*") +{ + _enforceExtraChecks = enforceExtraChecks; -/** - * Function: AdbNode::toXml - **/ -string AdbNode::toXml(const string& addPrefix) { - string xml = "first == "name" || it->first == "descr") { - continue; - } + // add default patterns + _nname_pattern = ".*"; + _fname_pattern = ".*"; + _enum_pattern = "(\\s*\\w+\\s*=\\s*(0x)?[0-9a-fA-f]+\\s*(,)?)+"; + + // add special attr names + field_spec_attr.insert("name"); + field_spec_attr.insert("offset"); + field_spec_attr.insert("size"); + field_spec_attr.insert("descr"); + field_spec_attr.insert("low_bound"); + field_spec_attr.insert("high_bound"); - xml += " " + it->first + "=\"" + encodeXml(it->second) + "\""; + if (includePath != "") + { + addIncludePaths(adbCtxt, includePath); } - xml += " >\n"; - - FieldsList allFields = fields; - allFields.insert(allFields.end(), condFields.begin(), condFields.end()); - stable_sort(allFields.begin(), allFields.end(), compareFieldsPtr ); - - for (size_t i = 0; i < allFields.size(); i++) { - if (allFields[i]->isReserved) { - continue; - } - - xml += "\t" + allFields[i]->toXml(addPrefix) + "\n"; + _xmlParser = XML_ParserCreate(0); + XML_SetUserData(_xmlParser, this); + XML_SetElementHandler(_xmlParser, startElement, endElement); + if (adbCtxt->includePaths.size() == 0) + { + // first add the opened project path + adbCtxt->includePaths.push_back( + _fileName.find(OS_PATH_SEP) == string::npos ? "." : _fileName.substr(0, _fileName.rfind(OS_PATH_SEP))); + vector path; + boost::algorithm::split(path, fileName, boost::is_any_of(string(OS_PATH_SEP))); + IncludeFileInfo info = {fileName, "ROOT", 0}; + _adbCtxt->includedFiles[path[path.size() - 1]] = info; } - - xml += ""; - return xml; + _instanceOps = false; } -/** - * Function: AdbNode::print - **/ -void AdbNode::print(int indent) { - cout << indentString(indent); - cout << "+ Node Name: " << name << " size: 0x" << hex << size / 32 * 4 - << "." << dec << size % 32 << " isUnion: " << isUnion - << " Description: " << desc << endl; +void AdbParser::addIncludePaths(Adb* adbCtxt, string includePaths) +{ + vector paths; + boost::algorithm::split(paths, includePaths, boost::is_any_of(string(";"))); + adbCtxt->includePaths.insert(adbCtxt->includePaths.end(), paths.begin(), paths.end()); - cout << indentString(indent) << "Fields:" << endl; - for (size_t i = 0; i < fields.size(); i++) - fields[i]->print(indent + 1); -} + StringVector relatives; + string projPath = boost::filesystem::path(adbCtxt->mainFileName).parent_path().string(); -/*************************** AdbField ***************************/ -/** - * Function: AdbField::AdbField - **/ -AdbField::AdbField() : - size(0), offset(0xffffffff), definedAsArr(false), lowBound(0), - highBound(0), unlimitedArr(false), isReserved(false), userData(0) { -} + for (StringVector::iterator it = adbCtxt->includePaths.begin(); it != adbCtxt->includePaths.end(); it++) + { + if (projPath != "" && projPath != *it && boost::filesystem::path(*it).is_relative()) + { + relatives.push_back(projPath + OS_PATH_SEP + *it); + } + } -/** - * Function: AdbField::~AdbField - **/ -AdbField::~AdbField() { + adbCtxt->includePaths.insert(adbCtxt->includePaths.end(), relatives.begin(), relatives.end()); } /** - * Function: AdbField::isLeaf + * Function: AdbParser::~AdbParser **/ -bool AdbField::isLeaf() { - return subNode.empty(); +AdbParser::~AdbParser() +{ + XML_ParserFree(_xmlParser); } /** - * Function: AdbField::isStruct + * Function: AdbParser::setAllowMultipleExceptionsTrue + * This function is a static function to change the value of set all exceptions to true **/ -bool AdbField::isStruct() { - return !isLeaf(); +void AdbParser::setAllowMultipleExceptionsTrue() +{ + AdbParser::allowMultipleExceptions = true; } /** - * Function: AdbField::operator< + * Function: AdbParser::load **/ -bool AdbField::operator<(AdbField& other) { - return offset < other.offset; -} +bool AdbParser::load() +{ + FILE* file; + char* data = NULL; + file = fopen(_fileName.c_str(), "r"); + _adbCtxt->_logFile->appendLogFile("Opening " + _fileName + "\n"); -/** - * Function: AdbField::isArray - **/ -bool AdbField::isArray() { - if (unlimitedArr) { - return true; - } else { - return definedAsArr ? true : lowBound != highBound; + if (!file) + { + _lastError = "Can't open file (" + _fileName + ") for reading: " + strerror(errno); + if (allowMultipleExceptions) + { + ExceptionHolder::insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); + } + return false; } -} -/** - * Function: AdbField::arrayLen - **/ -u_int32_t AdbField::arrayLen() { - if (unlimitedArr) { - return 1; - } else { - return (highBound - lowBound + 1); + if (fseek(file, 0L, SEEK_END) < 0) + { // go to the end of the file + _lastError = "fseek() failed for file (" + _fileName + "): " + strerror(errno); + fclose(file); + if (allowMultipleExceptions) + { + ExceptionHolder::insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); + } + return false; } -} - -/** - * Function: AdbField::isUnlimitedArr - **/ -bool AdbField::isUnlimitedArr() { - return unlimitedArr; -} -/** - * Function: AdbField::isArray - **/ -u_int32_t AdbField::eSize() { - if (unlimitedArr) { - return size; - } else { - return size / arrayLen(); + long fSize = ftell(file); + if (fSize < 0) + { + _lastError = "ftell() failed for file (" + _fileName + "): " + strerror(errno); + fclose(file); + if (allowMultipleExceptions) + { + ExceptionHolder::insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); + } + return false; } -} - -/** - * Function: AdbField::print - **/ -void AdbField::print(int indent) { - cout << indentString(indent); - cout << "- FIELD - Name: " << name << " offset: 0x" << hex << offset / 32 - * 4 << "." << dec << offset % 32 << " size: 0x" << hex << size / 32 - * 4 << "." << dec << size % 32 << " low_bound: " << lowBound - << " high_bound: " << highBound << " sub_node: " << subNode - << " isReserved: " << isReserved << endl; -} -/** - * Function: AdbField::toXml - **/ -string AdbField::toXml(const string& addPrefix) { - string xml = "first == "name" || it->first == "subnode" || it->first - == "descr") { - continue; + if (fseek(file, 0L, SEEK_SET) < 0) + { // go back to the start of the file + _lastError = "Failed to read file (" + _fileName + "): " + strerror(errno); + fclose(file); + free(data); + data = NULL; + if (allowMultipleExceptions) + { + ExceptionHolder::insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); } - - xml += " " + it->first + "=\"" + encodeXml(it->second) + "\""; + return false; } - xml += " />"; - return xml; -} - -/*************************** AdbConfig ***************************/ -/** - * Function: AdbField::print - **/ -void AdbConfig::print(int indent) { - cout << indentString(indent) << "Attributes:" << endl; - AttrsMap::iterator iter; - for (iter = attrs.begin(); iter != attrs.end(); iter++) - cout << indentString(indent + 1) << iter->first << " - " - << iter->second << endl; - - cout << indentString(indent) << "Enums:" << endl; - for (iter = enums.begin(); iter != enums.end(); iter++) - cout << indentString(indent + 1) << iter->first << " - " - << iter->second << endl; -} - -/** - * Function: AdbConfig::toXml - **/ -string AdbConfig::toXml() { - string xml = "first + "=\"" + encodeXml(it->second) + "\""; + size_t result = fread(data, fSize, 1, file); + if (result != 1) + { + _lastError = "Failed to read file (" + _fileName + "): " + strerror(errno); + fclose(file); + free(data); + data = NULL; + if (allowMultipleExceptions) + { + ExceptionHolder::insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); + } + return false; } - if (!enums.empty()) { - xml += " >\n"; + data[fSize] = 0; + fclose(file); - for (AttrsMap::iterator it = enums.begin(); it != enums.end(); it++) { - xml += "\tfirst) + "\" value=\"" - + encodeXml(it->second) + "\" />\n"; + bool xmlStatus = true; + try + { + if (!XML_Parse(_xmlParser, data, strlen(data), 0)) + { + enum XML_Error errNo = XML_GetErrorCode(_xmlParser); + throw AdbException(string("XML parsing issues: ") + XML_ErrorString(errNo)); } - xml += ""; - } else { - xml += " />"; } - - return xml; -} - -/*************************** AdbInstance ***************************/ -/** - * Function: AdbInstance::AdbInstance - **/ -AdbInstance::AdbInstance() : - fieldDesc(NULL), nodeDesc(NULL), parent(NULL), offset(0xffffffff), size(0), - arrIdx(0), isNameBeenExtended(false), unionSelector(NULL), isDiff(false), userData(NULL) - -{ - -} - -/** - * Function: AdbInstance::AdbInstance - **/ -AdbInstance::~AdbInstance() { - for (size_t i = 0; i < subItems.size(); i++) - delete subItems[i]; -} - -/** - * Function: AdbInstance::isLeaf - **/ -bool AdbInstance::isLeaf() { - return nodeDesc == NULL; -} - -/** - * Function: isUnionisUnion - **/ -bool AdbInstance::isUnion() { - return isNode() && nodeDesc->isUnion; -} - -/** - * Function: AdbInstance::isStruct - **/ -bool AdbInstance::isStruct() { - return isNode() && !isUnion(); -} - -/** - * Function: AdbInstance::isNode - **/ -bool AdbInstance::isNode() { - return !isLeaf(); -} - -/** - * Function: AdbInstance::isNode - **/ -bool AdbInstance::isPartOfArray() { - return fieldDesc->isArray(); -} - -/** - * Function: AdbInstance::fullName - **/ -string AdbInstance::fullName(int skipLevel) { - list < string > fnList; - AdbInstance *p = parent; - - fnList.push_front(name); - while (p != NULL) { - fnList.push_front(p->name); - p = p->parent; - } - - if ((int) fnList.size() > skipLevel) { - while (skipLevel--) { - fnList.pop_front(); + catch (AdbException& exp) + { + if (exp.what_s().find("in file:") == string::npos) + { + int line = XML_GetCurrentLineNumber(_xmlParser); + int column = XML_GetCurrentColumnNumber(_xmlParser); + _lastError = exp.what_s() + " in file: " + _fileName + " at line: " + boost::lexical_cast(line) + + " column: " + boost::lexical_cast(column); + } + else + { + _lastError = exp.what_s(); + } + if (allowMultipleExceptions) + { + xmlStatus = false; + ExceptionHolder::insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); + } + else + { + free(data); + return false; } - - return boost::algorithm::join(fnList, "."); - } else { - return fnList.back(); - } -} - -/** - * Function: AdbInstance::fullName - **/ -bool AdbInstance::isReserved() { - return fieldDesc->isReserved; -} - -/** - * Function: AdbInstance::fullName - **/ -u_int32_t AdbInstance::dwordAddr() { - return (offset >> 5) << 2; -} - -/** - * Function: AdbInstance::fullName - **/ -u_int32_t AdbInstance::startBit() { - return offset % 32; -} - -/** - * Function: AdbInstance::operator< - **/ -bool AdbInstance::operator<(const AdbInstance& other) { - return offset < other.offset; -} - -/** - * Function: AdbInstance::getChildByPath - **/ -AdbInstance* AdbInstance::getChildByPath(const string &path, - bool isCaseSensitive) { - string effPath = isCaseSensitive ? path : boost::algorithm::to_lower_copy( - path); - if (effPath[0] == '.') { - effPath.erase(0, 1); - } - - size_t idx = effPath.find("."); - string childName = idx == string::npos ? effPath : effPath.substr(0, idx); - string grandChildPath = idx == string::npos ? string() : effPath.substr( - idx + 1); - - if (path.empty()) { - return this; } - - // Search for childName - AdbInstance *child = NULL; - for (size_t i = 0; i < subItems.size(); i++) { - string subName = isCaseSensitive ? subItems[i]->name - : boost::algorithm::to_lower_copy(subItems[i]->name); - if (subName == childName) { - child = subItems[i]; - break; + catch (std::runtime_error& e) + { + _lastError = CHECK_RUNTIME_ERROR(e); + if (allowMultipleExceptions) + { + xmlStatus = false; + ExceptionHolder::insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); + } + else + { + free(data); + return false; } } - - if (!child) { - return NULL; + catch (...) + { + int line = XML_GetCurrentLineNumber(_xmlParser); + int column = XML_GetCurrentColumnNumber(_xmlParser); + _lastError = string("An exception raised during the loading of the file: ") + _fileName + + " at line: " + boost::lexical_cast(line) + + " column: " + boost::lexical_cast(column); + if (allowMultipleExceptions) + { + xmlStatus = false; + ExceptionHolder::insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); + } + else + { + free(data); + return false; + } } - // if grandChildPath isn't empty this means we need to continue and find the desired field in grand child node - return grandChildPath.empty() ? child : child->getChildByPath( - grandChildPath, isCaseSensitive); + free(data); + return xmlStatus; } /** - * Function: AdbInstance::findChild + * Function: AdbParser::loadFromString **/ -vector AdbInstance::findChild(const string& childName, - bool isCaseSensitive, bool by_inst_name) { - string effName = isCaseSensitive ? childName - : boost::algorithm::to_lower_copy(childName); - vector childList; - - if (by_inst_name || isLeaf()) { - if (name == childName) { - childList.push_back(this); - } - } else { - if (isNode() && nodeDesc->name == childName) { - childList.push_back(this); +bool AdbParser::loadFromString(const char* adbString) +{ + _fileName = "\"STRING\""; + try + { + if (!XML_Parse(_xmlParser, adbString, strlen(adbString), 0)) + { + enum XML_Error errNo = XML_GetErrorCode(_xmlParser); + throw AdbException(string("XML parsing issues: ") + XML_ErrorString(errNo)); } } + catch (AdbException& exp) + { + if (exp.what_s().find("in file:") == string::npos) + { + int line = XML_GetCurrentLineNumber(_xmlParser); + int column = XML_GetCurrentColumnNumber(_xmlParser); + _lastError = exp.what_s() + " in file: " + _fileName + " at line: " + boost::lexical_cast(line) + + " column: " + boost::lexical_cast(column); + } + else + { + _lastError = exp.what_s(); + } - // do that recursively for all child items - for (size_t i = 0; i < subItems.size(); i++) { - vector l = subItems[i]->findChild(effName, true); - childList.insert(childList.end(), l.begin(), l.end()); + _lastError += string("\nNOTE: this project is configured to work with: \"") + + (_adbCtxt->bigEndianArr ? "Big" : "Little") + " Endian Arrays\""; + return false; + } + catch (std::runtime_error& e) + { + _lastError = CHECK_RUNTIME_ERROR(e); + return false; + } + catch (...) + { + int line = XML_GetCurrentLineNumber(_xmlParser); + int column = XML_GetCurrentColumnNumber(_xmlParser); + _lastError = string("An exception raised during the loading of the file: ") + _fileName + + " at line: " + boost::lexical_cast(line) + + " column: " + boost::lexical_cast(column); + return false; } - return childList; + return true; } /** - * Function: AdbInstance::getAttr + * Function: AdbParser::getError **/ -string AdbInstance::getInstanceAttr(const string &attrName) const +string AdbParser::getError() { - AttrsMap::const_iterator it = instAttrsMap.find(attrName); - if (it == instAttrsMap.end()) { - if (fieldDesc) { - it = fieldDesc->attrs.find(attrName); - if (it == fieldDesc->attrs.end()) { - return string(); - } - } - else{ - return string(); - } - } - return it->second; + return _lastError; } - /** - * Function: AdbInstance::getInstanceAttrIterator + * Function: AdbParser::attrsCount **/ -AttrsMap::iterator AdbInstance::getInstanceAttrIterator(const string &attrName, bool &found) +int AdbParser::attrCount(const XML_Char** atts) { - AttrsMap::iterator it = instAttrsMap.find(attrName); - found = false; - if (it != instAttrsMap.end()) { - found = true; - } - else if (fieldDesc){ - it = fieldDesc->attrs.find(attrName); - if (it != fieldDesc->attrs.end()) { - found =true; - } + int i = 0; + while (atts[i]) + { + i++; } - return it; + return i / 2; } - /** - * Function: AdbInstance::setInstanceAttr + * Function: AdbParser::attrValue **/ -void AdbInstance::setInstanceAttr(const string &attrName, const string &attrValue) +string AdbParser::attrValue(const XML_Char** atts, const XML_Char* attrName) { - instAttrsMap[attrName] = attrValue; -} + int i = 0; + while (atts[i]) + { + if (!strcmp(atts[i], attrName)) + { + return string(atts[i + 1]); + } + i += 2; + } -/* - * Function: AdbInstance::copyAllInstanceAttr - copies all attrs from attributes structure - **/ -void AdbInstance::copyAllInstanceAttr(AttrsMap &attsToCopy) -{ - instAttrsMap = attsToCopy; + return string(); } /** - * Function: AdbInstance::getFullInstanceAttrsMapCopy - returns the instance attr map by val - **/ -AttrsMap AdbInstance::getFullInstanceAttrsMapCopy() + * Function: AdbParser::checkAttrExist + */ +bool AdbParser::checkAttrExist(const XML_Char** atts, const XML_Char* attrName) { - AttrsMap tmpCopy; - // copy first the fields attr map - for (AttrsMap::iterator it = fieldDesc->attrs.begin(); it != fieldDesc->attrs.end(); it++){ - tmpCopy[it->first] = it->second; - } - // copy the overriden variables - in instAttrsMap - for (AttrsMap::iterator it = instAttrsMap.begin(); it != instAttrsMap.end(); it++){ - tmpCopy[it->first] = it->second; + int i = 0; + while (atts[i]) + { + if (!strcmp(atts[i], attrName)) + { + return true; + } + i += 2; } - return tmpCopy; + return false; } /** - * Function: AdbInstance::setVarsMap - set an item in vars map + * Function: AdbParser::attrName **/ -void AdbInstance::setVarsMap(const string &attrName, const string &attrValue) +string AdbParser::attrName(const XML_Char** atts, int i) { - varsMap[attrName] = attrValue; + return string(atts[i * 2]); } /** - * Function: AdbInstance::setVarsMap - set an item in vars map + * Function: AdbParser::attrName **/ -void AdbInstance::setVarsMap(const AttrsMap &AttrsMap) +string AdbParser::attrValue(const XML_Char** atts, int i) { - varsMap = AttrsMap; + return string(atts[i * 2 + 1]); } /** - * Function: AdbInstance::setVarsMap - returns a copy of vars map + * Function: AdbParser::findFile **/ -AttrsMap AdbInstance::getVarsMap() +string AdbParser::findFile(string fileName) { - return varsMap; -} - -/** - * Function: AdbInstance::isEnumExists - **/ -bool AdbInstance::isEnumExists() { - return !getInstanceAttr("enum").empty(); -} - -/** - * Function: AdbInstance::enumToInt - **/ -bool AdbInstance::enumToInt(const string &name, u_int64_t &val) { - vector < string > enumValues; - string enums = getInstanceAttr("enum"); - boost::algorithm::split(enumValues, enums, boost::is_any_of(string(","))); + FILE* f; - for (size_t i = 0; i < enumValues.size(); i++) { - vector < string > pair; - string trimedEnumValues = enumValues[i]; - boost::algorithm::trim(trimedEnumValues); - boost::algorithm::split(pair, trimedEnumValues, - boost::is_any_of(string("="))); - - if (pair.size() != 2) { - continue; + for (size_t i = 0; i < _adbCtxt->includePaths.size(); i++) + { + string filePath = _adbCtxt->includePaths[i] + OS_PATH_SEP + fileName; + f = fopen(filePath.c_str(), "r"); + if (f) + { + fclose(f); + return filePath; } - - char *end; - if (pair[0] == name) { - val = strtoul(pair[1].c_str(), &end, 0); - if (*end != '\0') { - return false; + else if (fileName.find(_adbCtxt->includePaths[i]) != string::npos) + { // includePath is part of fileName + f = fopen(fileName.c_str(), "r"); + if (f) + { + fclose(f); + return fileName; } - return true; } } - - return false; + return string(); } /** - * Function: AdbInstance::intToEnum + * Function: AdbParser::addr2int **/ -bool AdbInstance::intToEnum(u_int64_t val, string &valName) { - vector < string > enumValues; - string enums = getInstanceAttr("enum"); - boost::algorithm::split(enumValues, enums, boost::is_any_of(string(","))); - - for (size_t i = 0; i < enumValues.size(); i++) { - vector < string > pair; - string trimedEnumValues = enumValues[i]; - boost::algorithm::trim(trimedEnumValues); - boost::algorithm::split(pair, trimedEnumValues, - boost::is_any_of(string("="))); - - if (pair.size() != 2) { - continue; - } - - char *end; - u_int64_t tmp = strtoul(pair[1].c_str(), &end, 0); - if (*end != '\0') { - continue; - } +u_int32_t AdbParser::addr2int(string& s) +{ + try + { + u_int32_t res; + boost::algorithm::to_lower(s); + char* end; + vector words; + boost::algorithm::split(words, s, boost::is_any_of(string("."))); - if (tmp == val) { - valName = pair[0]; - return true; + // fix for cases like: 0x.16 + if (words.size() && !words[0].compare("0x")) + { + words[0] = "0"; } - } - return false; -} + switch (words.size()) + { + case 1: + res = strtoul(words[0].c_str(), &end, 0); + if (*end != '\0') + { + throw AdbException(); + } -/** - * Function: AdbInstance::getEnumMap - **/ -map AdbInstance::getEnumMap() { - map < string, u_int64_t > enumMap; - vector < string > enumValues; - string enums = getInstanceAttr("enum"); - boost::algorithm::split(enumValues, enums, boost::is_any_of(string(","))); + res *= 8; + break; - for (size_t i = 0; i < enumValues.size(); i++) { - vector < string > pair; - string trimedEnumValues = enumValues[i]; - boost::algorithm::trim(trimedEnumValues); - boost::algorithm::split(pair, trimedEnumValues, - boost::is_any_of(string("="))); - if (pair.size() != 2) { - throw AdbException("Can't parse enum: " + enumValues[i]); - //continue; - } + case 2: + if (words[0].empty()) + { + // .DDD form + res = strtoul(words[1].c_str(), &end, 0); + if (*end != '\0') + { + throw AdbException(); + } + } + else + { + // 0xHHHHH.DDD or DDDDD.DDD form + res = strtoul(words[0].c_str(), &end, 0); + if (*end != '\0') + { + throw AdbException(); + } + res *= 8; + res += strtoul(words[1].c_str(), &end, 0); + if (*end != '\0') + { + throw AdbException(); + } + } + break; - char *end; - u_int64_t intVal = strtoul(pair[1].c_str(), &end, 0); - if (*end != '\0') { - throw AdbException( - string("Can't evaluate enum (") + pair[0].c_str() + "): " - + pair[1].c_str()); - //continue; + default: + throw AdbException("Invalid size: " + s); } - enumMap[pair[0]] = intVal; + return res; + } + catch (AdbException& exp) + { + throw AdbException("Failed to retrieve integer from string: " + exp.what_s()); } - - return enumMap; } /** - * Function: AdbInstance::getEnumValues + * Function: AdbParser::dword **/ -vector AdbInstance::getEnumValues() { - vector < u_int64_t > values; - vector < string > enumValues; - string enums = getInstanceAttr("enum"); - boost::algorithm::split(enumValues, enums, boost::is_any_of(string(","))); - - for (size_t i = 0; i < enumValues.size(); i++) { - vector < string > pair; - string trimedEnumValues = enumValues[i]; - boost::algorithm::trim(trimedEnumValues); - boost::algorithm::split(pair, trimedEnumValues, - boost::is_any_of(string("="))); - if (pair.size() != 2) { - continue; - } - - char *end; - u_int64_t intVal = strtoul(pair[1].c_str(), &end, 0); - if (*end != '\0') { - continue; - } - - values.push_back(intVal); - } - - return values; +u_int32_t AdbParser::dword(u_int32_t offset) +{ + return (offset >> 5) << 2; } /** - * Function: AdbInstance::getUnionSelectedNodeName + * Function: AdbParser::startBit **/ -AdbInstance* AdbInstance::getUnionSelectedNodeName(const u_int64_t& selectorVal) { - if (!isUnion()) { - throw AdbException( - "This is not union node (%s), can't get selected node name", fullName().c_str()); - } - - if (!unionSelector) { - throw AdbException("Can't find selector for union: " + name); - } - - map < string, u_int64_t > selectorValMap = unionSelector->getEnumMap(); - for (map::iterator it = selectorValMap.begin(); - it != selectorValMap.end(); it++) { - if (it->second == selectorVal) { - const string& selectorEnum = it->first; - // search for the sub instance with the "selected_by" attribute == selectorEnum - for (size_t i = 0; i < subItems.size(); i++) { - if (subItems[i]->getInstanceAttr("selected_by") == selectorEnum) { - return subItems[i]; - } - - } - throw AdbException("Found selector value (" + selectorEnum - + ") is defined for selector field (" - + unionSelector->name - + ") but no appropriate subfield of this union was found"); - } - } - - throw AdbException( - "Union selector field (" + unionSelector->name - + ") doesn't define selector value (" - + boost::lexical_cast(selectorVal)); +u_int32_t AdbParser::startBit(u_int32_t offset) +{ + return offset % 32; } /** - * Function: AdbInstance::getUnionSelectedNodeName + * Function: AdbParser::descXmlToNative **/ -AdbInstance* AdbInstance::getUnionSelectedNodeName(const string& selectorEnum) { - if (!isUnion()) { - throw AdbException( - "This is not union node (%s), can't get selected node name", fullName().c_str()); - } - - if (!unionSelector) { - throw AdbException("Can't find selector for union: " + name); - } +string AdbParser::descXmlToNative(const string& desc) +{ + return boost::replace_all_copy(desc, "\\;", "\n"); +} - for (size_t i = 0; i < subItems.size(); i++) { - if (subItems[i]->getInstanceAttr("selected_by") == selectorEnum) { - return subItems[i]; +bool AdbParser::is_inst_ifdef_exist_and_correct_project(const XML_Char** atts, AdbParser* adbParser) +{ + bool cond = true; + string definedProject = attrValue(atts, "inst_ifdef"); + if (!definedProject.empty()) + { + bool found = false; + for (size_t i = 0; i < adbParser->_adbCtxt->configs.size(); i++) + { + AttrsMap::iterator it_def = adbParser->_adbCtxt->configs[i]->attrs.find("define"); + if (it_def != adbParser->_adbCtxt->configs[i]->attrs.end()) + { + vector defVal; + boost::algorithm::split(defVal, it_def->second, boost::is_any_of(string("="))); + + if (defVal[0] == definedProject) + { + found = true; + break; + } + } } + cond = found; } - - throw AdbException("Union selector field (" + unionSelector->name - + ") doesn't define a selector value (" + selectorEnum + ")"); + return cond; } /** - * Function: AdbInstance::isConditionalNode - * Throws exception: AdbException + * Function: AdbParser::descNativeToXml **/ -bool AdbInstance::isConditionalNode() { - if (isNode()) { - return (getInstanceAttr("is_conditional") == "1"); - } - return false; -} /** - * Function: AdbInstance::isConditionValid - * Throws exception: AdbException + * Function: AdbParser::includeFile **/ -bool AdbInstance::isConditionValid(map *valuesMap) { - u_int64_t res; - AdbExpr expressionChecker; - int status = -1; - char *condExp; - char *exp; +void AdbParser::includeFile(AdbParser* adbParser, string fileName, int lineNumber) +{ + // add this file to the included files map + string filePath; + FILE* probeFile = NULL; - if (fieldDesc->condition.empty()) { - return true; + if (!boost::filesystem::path(fileName).is_relative()) + { + probeFile = fopen(fileName.c_str(), "r"); } - condExp = new char[fieldDesc->condition.size() + 1]; - exp = condExp; - if (!exp) { - throw AdbException("Memory allocation error"); - } - strcpy(exp, fieldDesc->condition.c_str()); - - expressionChecker.setVars(valuesMap); - try { - status = expressionChecker.expr(&exp, &res); - } catch (AdbException &e) { - delete[] condExp; - throw AdbException(string("AdbException: ") + e.what_s()); + if (probeFile) + { + fclose(probeFile); + filePath = fileName; } - delete[] condExp; - - if (status < 0) { - throw AdbException( - string("Error evaluating expression \"") - + fieldDesc->condition.c_str() + "\" : " - + AdbExpr::statusStr(status)); + else + { + filePath = adbParser->findFile(fileName); + } + if (filePath.empty()) + { + throw AdbException(string() + "Can't find the file: " + fileName); } - return (status >= 0 && res != 0); -} + // Update filename to be only base name with extension to prevent duplications + boost::filesystem::path boostPath(filePath); + fileName = boostPath.filename().string(); -/** - * Function: AdbInstance::getLeafFields - **/ -vector AdbInstance::getLeafFields(bool extendenName) { - vector fields; + if (!adbParser->_adbCtxt->includedFiles.count(fileName)) + { + IncludeFileInfo info = {filePath, adbParser->_fileName, lineNumber}; + adbParser->_adbCtxt->includedFiles[fileName] = info; - for (size_t i = 0; i < subItems.size(); i++) { - if (subItems[i]->isNode()) { - vector subFields = subItems[i]->getLeafFields(extendenName); - fields.insert(fields.end(), subFields.begin(), subFields.end()); - } else { - if (extendenName && !subItems[i]->isNameBeenExtended) { - subItems[i]->name = subItems[i]->name + addPathSuffixForArraySupport(subItems[i]->fullName()); - subItems[i]->isNameBeenExtended = true; - } - fields.push_back(subItems[i]); + // parse the included file + AdbParser p(filePath, adbParser->_adbCtxt, adbParser->_addReserved, adbParser->_progressObj, adbParser->_strict, + "", adbParser->_enforceExtraChecks); + if (!p.load()) + { + throw AdbException(p.getError()); } } - - return fields; -} - -/** - * Function: AdbInstance::pushBuf - **/ -void AdbInstance::pushBuf(u_int8_t *buf, u_int64_t value) { - push_to_buf(buf, offset, size, value); -} - -/** - * Function: AdbInstance::popBuf - **/ -u_int64_t AdbInstance::popBuf(u_int8_t *buf) { - return pop_from_buf(buf, offset, size); } /** - * Function: AdbInstance::print + * Function: AdbParser::includeAllFilesInDir **/ -void AdbInstance::print(int indent) { - string indentStr = indentString(indent); - printf( - "%sfullName: %s, offset: 0x%x.%d, size: 0x%x.%d, isNode:%d, isUnion:%d\n", - indentStr.c_str(), fullName().c_str(), (offset >> 5) << 2, - offset % 32, (size >> 5) << 2, size % 32, isNode(), isUnion()); +void AdbParser::includeAllFilesInDir(AdbParser* adbParser, string dirPath, int lineNumber) +{ + vector paths; + boost::filesystem::path mainFile(adbParser->_fileName); + boost::algorithm::split(paths, dirPath, boost::is_any_of(string(";"))); + for (StringVector::iterator pathIt = paths.begin(); pathIt != paths.end(); pathIt++) + { + // first look in the main file's path + boost::filesystem::path fsPath(mainFile.parent_path().string() + "/" + *pathIt); + if (!boost::filesystem::exists(fsPath)) + { + fsPath = boost::filesystem::path(*pathIt); + } - if (isNode()) { - for (size_t i = 0; i < subItems.size(); i++) - subItems[i]->print(indent + 1); + if (boost::filesystem::exists(fsPath) && boost::filesystem::is_directory(fsPath)) + { + addIncludePaths(adbParser->_adbCtxt, *pathIt); + boost::filesystem::directory_iterator filesIter(fsPath), dirEnd; + for (; filesIter != dirEnd; ++filesIter) + { + if (boost::filesystem::is_regular_file(filesIter->status()) && filesIter->path().extension() == ".adb") + { + try + { + includeFile(adbParser, filesIter->path().string(), lineNumber); + } + catch (AdbException& e) + { + throw(e); + } + } + } + } } } -/*************************** Adb ***************************/ -/** - * Function: Adb::Adb - **/ -Adb::Adb() : - bigEndianArr(false), singleEntryArrSupp(false){ -} - -/** - * Function: Adb::~Adb - **/ -Adb::~Adb() { - // Free configs - for (size_t i = 0; i < configs.size(); i++) - delete configs[i]; - - // Free nodes - NodesMap::iterator iter; - for (iter = nodesMap.begin(); iter != nodesMap.end(); iter++) - delete iter->second; -} - /** - * Function: Adb::raiseException - **/ -void Adb::raiseException(bool allowMultipleExceptions, string exceptionTxt, const string expType) + * Function AdbParser::checkSpecialChars + */ +bool AdbParser::checkSpecialChars(string tagName) { - if (allowMultipleExceptions) { - ExceptionHolder::insertNewException(expType, exceptionTxt); - } else { - throw AdbException(exceptionTxt); - } - return; -} - -/** - * Function: Adb::getAdbExceptionsMap - * This function return the adb exception map - **/ -void Adb::fetchAdbExceptionsMap(ExceptionsMap otherMap) { - vector < string > fatals = otherMap[ExceptionHolder::FATAL_EXCEPTION]; - for (vector < string >::iterator it = fatals.begin(); it != fatals.end(); ++it) { - insertNewException(ExceptionHolder::FATAL_EXCEPTION, *it); + smatch match; + regex allowedCharsExpr("[^\\w\\[\\]]"); // only alphanumeric and square brackets are allowed + if (regex_search(tagName, match, allowedCharsExpr)) + { + return false; } - vector < string > errors = otherMap[ExceptionHolder::ERROR_EXCEPTION]; - for (vector < string >::iterator it = errors.begin(); it != errors.end(); ++it) { - insertNewException(ExceptionHolder::ERROR_EXCEPTION, *it); + regex checkArrayExpr("[\\[\\]]"); // this check if containing the array brackets + if (regex_search(tagName, match, checkArrayExpr)) + { + regex correctNameForArrayExpr("[_A-Za-z][\\w]*\\[[\\d]+\\]$"); + if (!regex_search(tagName, match, correctNameForArrayExpr)) + { // check the name if correct the square brackets (array) + return false; + } } - vector < string > warnings = otherMap[ExceptionHolder::WARN_EXCEPTION]; - for (vector < string >::iterator it = warnings.begin(); it != warnings.end(); ++it) { - insertNewException(ExceptionHolder::WARN_EXCEPTION, *it); + else + { + regex correctNameNoneArrayExpr("[_A-Za-z][\\w]*$"); + if (!regex_search(tagName, match, correctNameNoneArrayExpr)) + { // check the name if correct without the square brackets (not array) + return false; + } } + return true; } - -/** - * Function: Adb::insertNewException - * This function take the excpetion type [FATAL:0, ERROR:1, WARNING:2] and the exception string - * Then it insert it to the adb exception map - **/ -void Adb::insertNewException(const string exceptionType, string exceptionTxt) { - adbExceptionMap[exceptionType].push_back(exceptionTxt); -} - -/** - * Function: Adb::printAdbExceptionMap - * This function will pring the content of the Adb Exception Map - **/ -string Adb::printAdbExceptionMap() { - string errorStr = ""; - vector < string > fatals = adbExceptionMap[ExceptionHolder::FATAL_EXCEPTION]; - for (vector < string >::iterator it = fatals.begin(); it != fatals.end(); ++it) { - errorStr += "-" + ExceptionHolder::FATAL_EXCEPTION + "- " + *it + ";"; - } - vector < string > errors = adbExceptionMap[ExceptionHolder::ERROR_EXCEPTION]; - for (vector < string >::iterator it = errors.begin(); it != errors.end(); ++it) { - errorStr += "-" + ExceptionHolder::ERROR_EXCEPTION + "- " + *it + ";"; - } - vector < string > warnings = adbExceptionMap[ExceptionHolder::WARN_EXCEPTION]; - for (vector < string >::iterator it = warnings.begin(); it != warnings.end(); ++it) { - errorStr += "-" + ExceptionHolder::WARN_EXCEPTION + "- " + "- " + *it + ";"; +bool AdbParser::checkHEXFormat(string addr) +{ + smatch match; + regex correctHEXExpr("(0x)?[0-9A-Fa-f]+?(.0)?$"); + if (!regex_search(addr, match, correctHEXExpr)) + { + return false; } - return errorStr; + return true; } -/** - * Function: Adb::load - **/ -bool Adb::load(string fname, bool addReserved, AdbProgress *progressObj, - bool strict, string includePath, string includeDir, - bool enforceExtraChecks, bool allowMultipleExceptions, string logFileStr) { - try { - bool status = true; - mainFileName = fname; - if (allowMultipleExceptions) { - AdbParser::setAllowMultipleExceptionsTrue(); +bool AdbParser::checkBigger32(string num) +{ + std::istringstream iss(num); + std::string token; + std::getline(iss, token, '.'); + if (std::getline(iss, token, '.')) + { // the second part of the size after the . max to be 32 0x0.0 + if (addr2int(token) / 8 >= 32) + { + return true; } - _logFile.init(logFileStr, allowMultipleExceptions); + } + return false; +} - AdbParser p(fname, this, addReserved, progressObj, strict, includePath, - enforceExtraChecks); - if (!p.load()) { - _lastError = p.getError(); - status = false; - } - if (status && includeDir != "") { - AdbParser::includeAllFilesInDir(&p, includeDir); - } - if (status && !nodesMap.size()) { - _lastError = "Empty project, no nodes were found"; - if (allowMultipleExceptions) { - insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); +void AdbParser::startNodesDefElement(const XML_Char** atts, AdbParser* adbParser) +{ + if (adbParser->_adbCtxt->version == "") + { + if ((attrCount(atts) == 1) && (attrName(atts, 0) == "version")) + { + string adbVersion = attrValue(atts, 0); + if (adbVersion != "1" && adbVersion != "1.0" && adbVersion != "2") + { + throw AdbException("Requested Adb Version (%s) is not supported. Supporting only version 1 or 2", + adbVersion.c_str()); + } + if (adbVersion == "1.0") + { + adbParser->_adbCtxt->version = "1"; + } + else + { + adbParser->_adbCtxt->version = adbVersion; } - status = false; - } - if (status) { - bool checkSizeConsistency = strict ? checkInstSizeConsistency( - allowMultipleExceptions) : true; - status = status && checkSizeConsistency; } - if (allowMultipleExceptions && ExceptionHolder::getNumberOfExceptions() - > 0) { - fetchAdbExceptionsMap(ExceptionHolder::getAdbExceptionsMap()); - status = false; + else if (attrCount(atts) == 0) + { + adbParser->_adbCtxt->version = "1"; } - return status; - } catch (AdbException &e) { - _lastError = e.what_s(); - if (allowMultipleExceptions) { - insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); + else + { + throw AdbException("\"NodesDefinition\" tag can only have \"version\" attribute and only once"); } - return false; } } -/** - * Function: Adb::loadFromString - **/ -bool Adb::loadFromString(const char *adbContents, bool addReserved, - AdbProgress *progressObj, bool strict, bool enforceExtraChecks) { - try { - AdbParser p(string(), this, addReserved, progressObj, strict, "", - enforceExtraChecks); - mainFileName = OS_PATH_SEP; - if (!p.loadFromString(adbContents)) { - _lastError = p.getError(); - return false; - } +void AdbParser::startEnumElement(const XML_Char** atts, AdbParser* adbParser, const int lineNumber) +{ + bool expFound = false; + if (!adbParser->_currentConfig || !adbParser->_currentConfig->attrs.count("type") || + TAG_ATTR_ENUM.compare(adbParser->_currentConfig->attrs["type"])) + { + expFound = + raiseException(allowMultipleExceptions, + "\"enum\" tag must be inside relevant \"config\" tag", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); + } - if (!nodesMap.size()) { - _lastError = "Empty project, no nodes were found"; - return false; + string tagName = attrValue(atts, "name"); + string value = attrValue(atts, "value"); + if (adbParser->_enforceExtraChecks) + { + if (!AdbParser::checkSpecialChars(tagName)) + { + expFound = raiseException( + allowMultipleExceptions, + "Invalid character in enum name, in enum: \"" + tagName + "\"", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::WARN_EXCEPTION); } - - return strict ? checkInstSizeConsistency() : true; - } catch (AdbException &e) { - _lastError = e.what_s(); - return false; + } + if (tagName.empty() || value.empty()) + { + expFound = + raiseException(allowMultipleExceptions, + "Both \"name\" and \"value\" attributes must be specified", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); + } + if (!expFound) + { + adbParser->_currentConfig->enums.insert(pair(tagName, value)); } } -/** - * Function: Adb::toXml - **/ -string Adb::toXml(vector nodeNames, bool addRootNode, string rootName, - string addPrefix) { - try { - vector < string > nodeDeps; - for (vector::iterator it = nodeNames.begin(); it - != nodeNames.end(); it++) { - vector < string > tmp = getNodeDeps(*it); - nodeDeps.insert(nodeDeps.end(), tmp.begin(), tmp.end()); - } - stable_sort(nodeDeps.begin(), nodeDeps.end()); - nodeDeps.erase(unique(nodeDeps.begin(), nodeDeps.end()), nodeDeps.end()); +void AdbParser::startConfigElement(const XML_Char** atts, AdbParser* adbParser, const int lineNumber) +{ + bool expFound = false; + if (adbParser->_currentConfig) + { + expFound = + raiseException(allowMultipleExceptions, + "config tag can't appear within other config", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::FATAL_EXCEPTION); + } - string xml; - if (this->version == "2") { - xml = "\n"; - } else { - xml = "\n"; - } - for (ConfigList::iterator it = configs.begin(); it != configs.end(); it++) { - xml += (*it)->toXml() + "\n"; - } + adbParser->_currentConfig = new AdbConfig; + for (int i = 0; i < attrCount(atts); i++) + { + // First add this config to context + string aName = attrName(atts, i); + string aValue = attrValue(atts, i); + adbParser->_currentConfig->attrs.insert(pair(aName, aValue)); - // Add source info - xml += "\n"; - - if (nodeNames.empty()) { - for (NodesMap::iterator it = nodesMap.begin(); it != nodesMap.end(); it++) { - AdbNode *node = it->second; - xml += node->toXml(addPrefix); + // checks that was imported from the gui-parser + if (adbParser->_enforceGuiChecks) + { + // check if the attribute is mandatory + if (!aName.compare("field_mand")) + { + aValue = regex_replace(aValue, regex("\\s+"), ""); + boost::split(adbParser->field_mand_attr, aValue, boost::is_any_of(",")); + } + + // check the define statement is according to format + else if (!aName.compare("define")) + { + smatch result; + regex pattern(TAG_ATTR_DEFINE_PATTERN); + if (!regex_match(aValue, result, pattern)) + { + expFound = raiseException( + allowMultipleExceptions, + string("Bad define format: \"") + aName + "\" attribute value: \"" + aValue + "\"", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); + } + else + { + // check for define duplication + string define_key = result[1], define_value = result[2]; + + if (adbParser->defines_map.find(define_key) != adbParser->defines_map.end()) + { + expFound = raiseException(allowMultipleExceptions, + string("Multiple definition of preprocessor variable: \"") + + define_key + "\" attribute name: \"" + aName + "\"", + ", in file: \"" + adbParser->_fileName + + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); + } + else + { + adbParser->defines_map.insert(pair(define_key, define_value)); + } + } } - } else { - u_int32_t maxSize = 0; - for (size_t i = 0; i < nodeDeps.size(); i++) { - NodesMap::iterator it = nodesMap.find(nodeDeps[i]); - if (it == nodesMap.end()) { - _lastError = "Can't find node definition for: " - + nodeDeps[i]; - return ""; + + else if (!expFound && !aName.compare("field_attr")) + { + // check that the field_attr is unique + if (adbParser->field_attr_names_set.find(aValue) != adbParser->field_attr_names_set.end()) + { + expFound = raiseException( + allowMultipleExceptions, + string("Redefinition of field_attr: \"") + aName + "\" attribute name: \"" + aValue + "\"", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); + } + else + { + adbParser->field_attr_names_set.insert(aValue); + } + + string attr_type = attrValue(atts, "type"); + + // check that attr 'type' was provided and not empty + if (attr_type.empty()) + { + expFound = raiseException( + allowMultipleExceptions, + string("field_attr type must be specified: \"") + aName + "\" attribute value: \"" + aValue + "\"", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); + } + else + { + // check that type 'multival' has a 'fw_label' value + if (!attr_type.compare("multival") && aValue.compare("fw_label")) + { + expFound = + raiseException(allowMultipleExceptions, + string("Type \"multival\" is supported only for \"fw_label\" not for: \"") + + aName + "\" attribute value: \"" + aValue + "\"", + ", in file: \"" + adbParser->_fileName + + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); + } } - AdbNode *node = it->second; - xml += node->toXml(addPrefix) + "\n\n"; + string attr_used_for = attrValue(atts, "used_for"); - maxSize = TOOLS_MAX(node->size, maxSize); + // check that attr 'used_for' was not provided empty + if (checkAttrExist(atts, "used_for") && attr_used_for.empty()) + { + expFound = raiseException( + allowMultipleExceptions, + string("used_for is invalid: \"") + aName + "\" attribute value: \"" + aValue + "\"", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); + } + + // check if 'pattern' was provided + if (checkAttrExist(atts, "pattern")) + { + // check if the given pattern is a valid reg expression. + try + { + string pattern_value = attrValue(atts, "pattern"); + regex r(pattern_value); + adbParser->attr_pattern.insert(pair(aValue, pattern_value)); + } + catch (...) + { + expFound = raiseException(allowMultipleExceptions, + string("Bad attribute pattern: \"") + aValue + "\" name Pattern: \"" + + attrValue(atts, "pattern") + "\"", + ", in file: \"" + adbParser->_fileName + + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); + } + } } - if (addRootNode) { - stringstream buf; - buf << "> 5) << 2) << "." << dec - << (maxSize % 32) << "\" descr=\"\" >\n"; - buf << "\t> 5) << 2) << "." - << dec << (maxSize % 32) << "\" subnode=\"" << addPrefix - + rootName << "\" descr=\"\" />\n"; - buf << "\n\n"; + // check if nname_pattern was provided + else if (!expFound && checkAttrExist(atts, "nname_pattern")) + { + // check if the given pattern is a valid reg expression. + try + { + string nname_pattern = attrValue(atts, "nname_pattern"); + regex r(nname_pattern); + adbParser->_nname_pattern = nname_pattern; + } + catch (...) + { + expFound = raiseException( + allowMultipleExceptions, + string("Bad node name pattern: \"") + aValue + "\" pattern: \"" + + attrValue(atts, "nname_pattern") + "\"", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); + } + } - buf << "> 5) << 2) << "." << dec - << (maxSize % 32) - << "\" attr_is_union=\"1\" descr=\"\" >\n"; - for (size_t i = 0; i < nodeNames.size(); i++) { - AdbNode *node = nodesMap[nodeNames[i]]; - buf << "\tname - << "\" offset=\"0x0.0\" size=\"0x" << hex - << ((node->size >> 5) << 2) << "." << dec - << (node->size % 32) << "\" subnode=\"" + addPrefix - + node->name + "\" descr=\"\" />\n"; + // check if fname_pattern was provided and the name of the field is compatible with that pattern + else if (!expFound && checkAttrExist(atts, "fname_pattern")) + { + // check if the given pattern is a valid reg expression. + try + { + string fname_pattern = attrValue(atts, "fname_pattern"); + regex r(fname_pattern); + adbParser->_fname_pattern = fname_pattern; + } + catch (...) + { + expFound = raiseException( + allowMultipleExceptions, + string("Bad field name pattern: \"") + aValue + "\" pattern: \"" + + attrValue(atts, "fname_pattern") + "\"", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); } - buf << "\n"; - xml += buf.str(); } } - xml += "\n"; - return xml; - } catch (AdbException& exp) { - _lastError = exp.what_s(); - return ""; - } -} - -/** - * Function: Adb::addMissingNodes - **/ + // Parse attrs that affect the parser itself + if (!TAG_ATTR_BIG_ENDIAN.compare(aName)) + { + try + { + int val = boost::lexical_cast(aValue); + adbParser->_adbCtxt->bigEndianArr = val != 0; + } + catch (std::exception&) + { + expFound = raiseException( + allowMultipleExceptions, + string("Filed to parse the \"") + aName + "\" attribute value: \"" + aValue + "\"", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); + } + } -AdbInstance* Adb::addMissingNodes(int depth, bool allowMultipleExceptions) { - try { - NodesMap::iterator it; - for (it = nodesMap.begin(); it != nodesMap.end(); it++) { - AdbNode *nodeDesc = it->second; - for (size_t i = 0; (depth == -1 || depth > 0) && i - < nodeDesc->fields.size(); i++) { - AdbField *fieldDesc = nodeDesc->fields[i]; - for (u_int32_t i = 0; i < fieldDesc->arrayLen(); i++) { - if (fieldDesc->isStruct()) { - NodesMap::iterator it2 = nodesMap.find(fieldDesc->subNode); - if (it2 == nodesMap.end()) { - // If in ignore missing nodes mode, it should create temporary node with placeholder - AdbNode *tmpNode = new AdbNode; - tmpNode->name = fieldDesc->subNode; - tmpNode->size = fieldDesc->eSize(); - tmpNode->desc = fieldDesc->desc + " ***MISSING NODE***"; - tmpNode->isUnion = false; - tmpNode->fileName = "tempForMissingNodes.adb"; - tmpNode->lineNumber = 0; - AdbField *tmpField = new AdbField; - tmpField->name = "placeholder"; - tmpField->desc - = "This field is part of auto generated node for missing node."; - tmpField->size = tmpNode->size; - tmpField->offset = 0; - tmpNode->fields.push_back(tmpField); - nodesMap.insert( - pair (tmpNode->name, tmpNode)); - } - } - } + if (!TAG_ATTR_SINGLE_ENTRY_ARR.compare(aName)) + { + try + { + int val = boost::lexical_cast(aValue); + adbParser->_adbCtxt->singleEntryArrSupp = val != 0; + } + catch (std::exception&) + { + expFound = raiseException( + allowMultipleExceptions, + string("Filed to parse the \"") + aName + "\" attribute value: \"" + aValue + "\"", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); } } - } catch (AdbException &exp) { - _lastError = exp.what_s(); - if (allowMultipleExceptions) { - insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); + + if (!expFound && !TAG_ATTR_INCLUDE_PATH.compare(aName)) + { + vector paths; + boost::algorithm::split(paths, aValue, boost::is_any_of(string(";"))); + adbParser->_adbCtxt->includePaths.insert(adbParser->_adbCtxt->includePaths.end(), paths.begin(), + paths.end()); + + StringVector relatives; + string projPath = boost::filesystem::path(adbParser->_adbCtxt->mainFileName).parent_path().string(); + + for (StringVector::iterator it = adbParser->_adbCtxt->includePaths.begin(); + it != adbParser->_adbCtxt->includePaths.end(); + it++) + { + if (boost::filesystem::path(*it).is_relative()) + { + relatives.push_back(projPath + OS_PATH_SEP + *it); + } + } + + adbParser->_adbCtxt->includePaths.insert(adbParser->_adbCtxt->includePaths.end(), relatives.begin(), + relatives.end()); } - return NULL; - } catch (...) { - _lastError = "Unknown error occurred"; - return NULL; } - return NULL; } -/** - * Function: Adb::cleaninstAttrs - **/ -void Adb::cleanInstAttrs() +void AdbParser::startInfoElement(const XML_Char** atts, AdbParser* adbParser) { - for (InstanceAttrs::iterator it = instAttrs.begin(); it!= instAttrs.end(); it++){ - it->second.clear(); - } - instAttrs.clear(); + string docName = attrValue(atts, "source_doc_name"); + string docVer = attrValue(atts, "source_doc_version"); + adbParser->_adbCtxt->srcDocName = docName; + adbParser->_adbCtxt->srcDocVer = docVer; } -/** - * Function: Adb::createLayout - **/ -AdbInstance* Adb::createLayout(string rootNodeName, bool isExprEval, - AdbProgress *progressObj, int depth, bool ignoreMissingNodes, - bool allowMultipleExceptions) { - try { - //find root in nodes map - NodesMap::iterator it; - it = nodesMap.find(rootNodeName); - if (it == nodesMap.end()) { - raiseException(allowMultipleExceptions, - "Can't find definition for node \"" + rootNodeName + "\"", - ExceptionHolder::FATAL_EXCEPTION); - } +void AdbParser::startIncludeElement(const XML_Char** atts, AdbParser* adbParser, const int lineNumber) +{ + bool cond = is_inst_ifdef_exist_and_correct_project(atts, adbParser); - AdbNode *nodeDesc = it->second; - AdbInstance *rootItem = new AdbInstance(); - rootItem->fieldDesc = NULL; - rootItem->nodeDesc = nodeDesc; - rootItem->parent = NULL; - rootItem->name = nodeDesc->name; - rootItem->offset = 0; - rootItem->size = nodeDesc->size; - rootItem->copyAllInstanceAttr(nodeDesc->attrs); + if (cond) + { + string includeAttr = attrName(atts, 0); + boost::algorithm::trim(includeAttr); - map < string, string > emptyVars; - _unionSelectorEvalDeffered.clear(); - if (ignoreMissingNodes) { - addMissingNodes(depth, allowMultipleExceptions); + bool expFound = false; + if (includeAttr == "file") + { + string fname = attrValue(atts, "file"); + boost::algorithm::trim(fname); + if (fname.empty()) + { + expFound = raiseException( + allowMultipleExceptions, + string() + "File attribute isn't given within " + TAG_INCLUDE + " tag", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::FATAL_EXCEPTION); + } + if (!expFound) + includeFile(adbParser, fname, lineNumber); } + else if (includeAttr == "dir") + { + string includeAllDirPath = attrValue(atts, "dir"); + boost::algorithm::trim(includeAllDirPath); + if (includeAllDirPath.empty()) + { + expFound = raiseException( + allowMultipleExceptions, + string() + "Directory to include isn't given within " + TAG_INCLUDE + " tag", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::FATAL_EXCEPTION); + } - for (size_t i = 0; (depth == -1 || depth > 0) && i - < nodeDesc->fields.size(); i++) { - vector subItems = createInstance(nodeDesc->fields[i], - rootItem, emptyVars, isExprEval, progressObj, - depth == -1 ? -1 : depth - 1, ignoreMissingNodes, - allowMultipleExceptions); - rootItem->subItems.insert(rootItem->subItems.end(), - subItems.begin(), subItems.end()); + if (!expFound) + includeAllFilesInDir(adbParser, includeAllDirPath, lineNumber); + } + else + { + expFound = raiseException( + allowMultipleExceptions, + string() + "Include is called without file or dir attribute.", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); } + } +} - // Now set the instance attributes (override field attrs), only if this is root node instantiation - if (rootNodeName == rootNode && depth == -1) { - for (InstanceAttrs::iterator it = instAttrs.begin(); it!= instAttrs.end(); it++) { - size_t idx = it->first.find("."); - string path = idx == string::npos ? string() - : it->first.substr(idx + 1); +void AdbParser::startInstOpAttrReplaceElement(const XML_Char** atts, AdbParser* adbParser, const int lineNumber) +{ + bool expFound = false; + if (!adbParser->_instanceOps) + { + expFound = + raiseException(allowMultipleExceptions, + "Operation attr_replace must be defined within element only.", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::FATAL_EXCEPTION); + } - AdbInstance *inst = rootItem->getChildByPath(path); - if (!inst) { - raiseException(allowMultipleExceptions, - "Can't find instance path (" + it->first + ") defined in section", - ExceptionHolder::ERROR_EXCEPTION); - } + string path = attrValue(atts, "path"); + if (path.empty()) + { + expFound = + raiseException(allowMultipleExceptions, + "path attribute is missing in attr_replace operation", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); + } - for (AttrsMap::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++) { - if (allowMultipleExceptions && !inst) { - break; - } - inst->setInstanceAttr(it2->first, it2->second); - } + if (!expFound) + { + adbParser->_adbCtxt->instAttrs[path] = AttrsMap(); + for (int i = 0; i < attrCount(atts); i++) + { + string attrN = attrName(atts, i); + if (attrN == "path") + { + continue; } - } - /* Evaluate unions selector fields*/ - for (list::iterator it = _unionSelectorEvalDeffered.begin(); - it != _unionSelectorEvalDeffered.end(); it++) { - vector < string > path; - AdbInstance *inst = *it; - AdbInstance *curInst = inst; - const string splitVal = inst->getInstanceAttr("union_selector"); - boost::algorithm::split(path, splitVal, boost::is_any_of(string("."))); - for (size_t i = 0; i < path.size(); i++) { - if (path[i] == "#(parent)" || path[i] == "$(parent)") { - curInst = curInst->parent; - } else { - size_t j; - bool inPath = false; - for (j = 0; j < curInst->subItems.size(); j++) { - if (curInst->subItems[j]->name == path[i]) { - curInst = curInst->subItems[j]; - inPath = true; - break; - } - } + string attrV = attrValue(atts, i); + adbParser->_adbCtxt->instAttrs[path][attrN] = attrV; + } + } +} - if (j == curInst->subItems.size() && !inPath) { - if (rootNodeName == rootNode) { // give this warning only if this root instantiation - raiseException(allowMultipleExceptions, - "Failed to find union selector for union (" + inst->fullName() + ") Can't find field (" + path[i] + ") under (" + curInst->fullName() + ")", - ExceptionHolder::ERROR_EXCEPTION); - } - } - } - } +void AdbParser::startNodeElement(const XML_Char** atts, AdbParser* adbParser, const int lineNumber) +{ + if (adbParser->_currentNode || adbParser->skipNode) + { + raiseException(allowMultipleExceptions, + "Nested nodes are not allowed", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::FATAL_EXCEPTION); + } + bool cond = is_inst_ifdef_exist_and_correct_project(atts, adbParser); - inst->unionSelector = curInst; - for (size_t i = 0; i < inst->subItems.size(); i++) { - //printf("Field %s, isResered=%d\n", inst->subItems[i]->fullName().c_str(), inst->subItems[i]->isReserved()); - if (inst->subItems[i]->isReserved()) { - continue; - } + if (cond) + { + string nodeName = attrValue(atts, "name"); + boost::algorithm::trim(nodeName); + string size = attrValue(atts, "size"); - // make sure all union subnodes define "selected_by" attribute - bool found = false; - AttrsMap::iterator selectorValIt = inst->subItems[i]->getInstanceAttrIterator("selected_by", found); - if (!found) { - raiseException(allowMultipleExceptions, - "In union (" + inst->fullName() + ") the union subnode (" + inst->subItems[i]->name + ") doesn't define selection value", - ExceptionHolder::ERROR_EXCEPTION); - } + if (adbParser->_enforceGuiChecks) + { + // check the node name is compatible with the nname_pattern + if (!regex_match(nodeName, regex(adbParser->_nname_pattern))) + { + raiseException(allowMultipleExceptions, + "Illegal node name: \"" + nodeName + "\" doesn't match the given node name pattern: \"", + adbParser->_nname_pattern + "\", in file: \"" + adbParser->_fileName + + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::WARN_EXCEPTION); + } + } - // make sure that all union subnodes selector values are defined in the selector field enum - if (selectorValIt->second == "") { - continue; - } + if (adbParser->_enforceExtraChecks) + { + if (!AdbParser::checkSpecialChars(nodeName)) + { + raiseException( + allowMultipleExceptions, + "Invalid character in node name, in Node: \"" + nodeName + "\"", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::WARN_EXCEPTION); + } + if (AdbParser::checkBigger32(size)) + { // the second part of the size after the . max to be 32 0x0.0 + raiseException( + allowMultipleExceptions, + "Invalid size format, valid format 0x0.0 not allowed to be more than 0x0.31", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::WARN_EXCEPTION); + } + if (addr2int(size) == 0) + { + raiseException( + allowMultipleExceptions, + "Node Size is not allowed to be 0, in Node: \"" + nodeName + "\"", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); + } + } + string desc = descXmlToNative(attrValue(atts, "descr")); - map::iterator it; - map < string, u_int64_t > selectorValMap - = inst->unionSelector->getEnumMap(); - for (it = selectorValMap.begin(); it != selectorValMap.end(); it++) { - if (it->first == selectorValIt->second) { - break; - } - } + // Check for mandatory attrs + if (nodeName.empty()) + { + raiseException( + allowMultipleExceptions, + "Missing node name", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::FATAL_EXCEPTION); + } + if (size.empty()) + { + raiseException( + allowMultipleExceptions, + "Missing node size", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::FATAL_EXCEPTION); + } + // Check for duplications + if (adbParser->_adbCtxt->nodesMap.count(nodeName)) + { + raiseException( + allowMultipleExceptions, + "node \"" + nodeName + "\" is already defined in file: \"" + + adbParser->_adbCtxt->nodesMap[nodeName]->fileName + + "\" line: " + boost::lexical_cast(adbParser->_adbCtxt->nodesMap[nodeName]->lineNumber), + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::FATAL_EXCEPTION); + } + + adbParser->_currentNode = new AdbNode; + adbParser->_currentNode->name = nodeName; + adbParser->_currentNode->size = addr2int(size); + adbParser->_currentNode->desc = desc; + string unionAttrVal = attrValue(atts, "attr_is_union"); + adbParser->_currentNode->isUnion = !unionAttrVal.empty() && boost::lexical_cast(unionAttrVal) != 0; + adbParser->_currentNode->fileName = adbParser->_fileName; + adbParser->_currentNode->lineNumber = lineNumber; - //if not found in map throw exeption - if (it == selectorValMap.end()) { - string exceptionTxt = "In union (" + inst->fullName() - + ") the union subnode (" + inst->subItems[i]->name - + ") uses a selector value (" - + selectorValIt->second - + ") which isn't defined in the selector field (" - + inst->unionSelector->fullName() + ")"; - raiseException(allowMultipleExceptions, - exceptionTxt, - ExceptionHolder::ERROR_EXCEPTION); - } - } + if (adbParser->_strict && adbParser->_currentNode->isUnion && adbParser->_currentNode->size % 32) + { + // throw AdbException("union must be dword aligned"); } - return rootItem; - } catch (AdbException &exp) { - _lastError = exp.what_s(); - if (allowMultipleExceptions) { - insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); + // Add all node attributes + for (int i = 0; i < attrCount(atts); i++) + { + if (attrValue(atts, i) == "") + { + continue; + } + adbParser->_currentNode->attrs[attrName(atts, i)] = attrValue(atts, i); } - return NULL; - } catch (...) { - _lastError = "Unknown error occurred"; - return NULL; + } + else + { + adbParser->skipNode = true; } } -/** - * Function: Adb::getNodeDeps - **/ -vector Adb::getNodeDeps(string nodeName) { - NodesMap::iterator it = nodesMap.find(nodeName); - if (it == nodesMap.end()) { - throw AdbException("Can't find node definition for: " + nodeName); +void AdbParser::startFieldElement(const XML_Char** atts, AdbParser* adbParser, const int lineNumber) +{ + bool expFound = false; + if (!adbParser->_currentNode && !adbParser->skipNode) + { + expFound = + raiseException(allowMultipleExceptions, + "Field definition outside of node", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::FATAL_EXCEPTION); } - AdbNode *node = it->second; - vector < string > deps(1, node->name); - - for (size_t i = 0; i < node->fields.size(); i++) { - if (node->fields[i]->isStruct()) { - deps.push_back(node->fields[i]->subNode); - vector < string > subDeps = getNodeDeps(node->fields[i]->subNode); - deps.insert(deps.end(), subDeps.begin(), subDeps.end()); + if (!adbParser->skipNode) + { + if (adbParser->_currentField) + { + expFound = raiseException( + allowMultipleExceptions, + "Nested fields are not allowed", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::FATAL_EXCEPTION); } - } - stable_sort(deps.begin(), deps.end()); - deps.erase(unique(deps.begin(), deps.end()), deps.end()); - return deps; -} + string fieldName = attrValue(atts, "name"); + boost::algorithm::trim(fieldName); + string offset = attrValue(atts, "offset"); + string size = attrValue(atts, "size"); -/** - * Function: Adb::createInstance - **/ -vector Adb::createInstance(AdbField *field, - AdbInstance *parent, map vars, bool isExprEval, - AdbProgress *progressObj, int depth, bool ignoreMissingNodes, - bool allowMultipleExceptions) { - static const regex EXP_PATTERN( - "\\s*([a-zA-Z0-9_]+)=((\\$\\(.*?\\)|\\S+|$)*)\\s*"); - if (progressObj) { - progressObj->progress(); - } + if (adbParser->_enforceExtraChecks) + { + if (addr2int(size) % 32 == 0 && !AdbParser::checkHEXFormat(size)) + { + expFound = raiseException( + allowMultipleExceptions, + "Invalid size format", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::WARN_EXCEPTION); + } + if (!AdbParser::checkHEXFormat(offset)) + { + expFound = raiseException( + allowMultipleExceptions, + "Invalid offset format", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::WARN_EXCEPTION); + } + if (!AdbParser::checkSpecialChars(fieldName)) + { + expFound = raiseException( + allowMultipleExceptions, + "Invalid character in field name, in Field: \"" + fieldName + "\"", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::WARN_EXCEPTION); + } + if (!expFound && adbParser->_currentNode->isUnion && addr2int(offset) != 0) + { + expFound = raiseException( + allowMultipleExceptions, + "Offset must be 0x0.0 in Union fields", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::WARN_EXCEPTION); + } + if (AdbParser::checkBigger32(size)) + { // the second part of the size after the . max to be 32 0x0.0 + expFound = raiseException( + allowMultipleExceptions, + "Invalid size format, valid format 0x0.0 not allowed to be more than 0x0.31", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::WARN_EXCEPTION); + } + if (AdbParser::checkBigger32(offset)) + { // the second part of the size after the . max to be 32 0x0.0 + expFound = raiseException( + allowMultipleExceptions, + "Invalid offset format, valid format 0x0.0 not allowed to be more than 0x0.31", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::WARN_EXCEPTION); + } + if (addr2int(size) == 0) + { + expFound = raiseException( + allowMultipleExceptions, + "Field Size is not allowed to be 0, in Field: \"" + fieldName + "\"", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::WARN_EXCEPTION); + } + } - // if array create instance for each item. else - create 1 - vector instList; - for (u_int32_t i = 0; i < field->arrayLen(); i++) { - AdbInstance *inst = new AdbInstance; - inst->fieldDesc = field; + string desc = descXmlToNative(attrValue(atts, "descr")); + string lowBound = attrValue(atts, "low_bound"); + string highBound = attrValue(atts, "high_bound"); - // if field is struct - find field->subNode in nodes map and addto instance not desc - if (field->isStruct()) { - NodesMap::iterator it = nodesMap.find(field->subNode); - if (it == nodesMap.end()) { - delete inst; - raiseException(allowMultipleExceptions, - "Can't find the definition for subnode: " + field->subNode + " of field: " + field->name, - ExceptionHolder::ERROR_EXCEPTION); - } else { - inst->nodeDesc = it->second; + if (lowBound != "" && highBound != "") + { + int low = atoi(lowBound.c_str()); + int high = 0; + if (highBound != "VARIABLE") + { + high = atoi(highBound.c_str()); + } + int isize = addr2int(size); + int entrySize = isize / (high - low + 1); + if (entrySize % 8 != 0 && entrySize > 8) + { + expFound = raiseException( + allowMultipleExceptions, + "Invalid size of array entries", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); + } + if (entrySize < 8 && entrySize != 4 && entrySize != 2 && entrySize != 1 && + ((isize > 32 && highBound != "VARIABLE") || highBound == "VARIABLE")) + { + expFound = raiseException( + allowMultipleExceptions, + "Array with entry size 3, 5, 6 or 7 bits and array size is larger than 32bit", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); } } + else if (adbParser->_enforceGuiChecks && !(lowBound == "" && highBound == "")) + { + expFound = raiseException( + allowMultipleExceptions, + "Field: \"" + adbParser->_currentField->name + "\": both low_bound or high_bound must be specified", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::WARN_EXCEPTION); + } - inst->name = field->name; - inst->arrIdx = 0; - inst->size = field->eSize(); - inst->parent = parent; + string subNode = attrValue(atts, "subnode"); - if (field->offset == 0xffffffff) { - if (parent->subItems.size() > 0) { - field->offset = parent->subItems.back()->offset; - } else { - field->offset = parent->offset; + // Check for mandatory attrs + if (fieldName.empty()) + { + expFound = raiseException( + allowMultipleExceptions, + "Missing field name", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::FATAL_EXCEPTION); + } + + if (size.empty()) + { + expFound = raiseException( + allowMultipleExceptions, + "Missing field size", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::FATAL_EXCEPTION); + } + + // Create new fields + adbParser->_currentField = new AdbField; + + // Fill the new fields + adbParser->_currentField->name = fieldName; + adbParser->_currentField->desc = desc; + adbParser->_currentField->size = addr2int(size); + if (adbParser->_adbCtxt->singleEntryArrSupp) + { + int low = -1; + int high = -1; + if (!lowBound.empty()) + { + low = atoi(lowBound.c_str()); + } + if (!highBound.empty()) + { + high = atoi(highBound.c_str()); } + adbParser->_currentField->definedAsArr = (low == high) && (low >= 0); } - inst->offset = calcArrOffset(field, parent, i); - if (field->isArray()) { - inst->name += "[" + boost::lexical_cast(i + field->lowBound) + "]"; - inst->arrIdx = i; + else + { + adbParser->_currentField->definedAsArr = false; } - //printf("field: %s, offset: %s (%d)\n", inst->name.c_str(),formatAddr(inst->offset, inst->size).c_str(), inst->offset); - if (isExprEval) { - try { - // First add the special variables - char tmpStr[32]; - vars["NAME"] = inst->name; - vars["ARR_IDX"] = boost::lexical_cast(inst->arrIdx); - sprintf(tmpStr, "[%d:%d]", inst->offset % 32 + inst->size - 1, - inst->offset % 32); - vars["BN"] = tmpStr; - vars["parent"] = "#(parent)"; // special internal name + adbParser->_currentField->lowBound = lowBound.empty() ? 0 : boost::lexical_cast(lowBound); + if (highBound == "VARIABLE") + { + adbParser->_currentField->highBound = 0; + adbParser->_currentField->unlimitedArr = true; + } + else + { + adbParser->_currentField->highBound = highBound.empty() ? 0 : boost::lexical_cast(highBound); + } + adbParser->_currentField->subNode = subNode; - // Get variables attribute value - AttrsMap::iterator it = field->attrs.find("variables"); - string varStr = (it == field->attrs.end() ? string() : it->second); - boost::algorithm::trim(varStr); + // Check array element size + if (adbParser->_currentField->isArray() && !adbParser->_currentField->isUnlimitedArr() && + adbParser->_currentField->size % (adbParser->_currentField->arrayLen())) + { + char exceptionTxt[1000]; + sprintf(exceptionTxt, "In field \"%s\" invalid array element size\"%0.2f\" in file: \"%s\" line: %d", + fieldName.c_str(), + ((double)adbParser->_currentField->size / (adbParser->_currentField->arrayLen())), + adbParser->_fileName.c_str(), lineNumber); - // build var_name->value map from varAttrStr - AttrsMap curVars; - if (!varStr.empty()) { - match_results < string::const_iterator > what; - string::const_iterator start = varStr.begin(); - string::const_iterator end = varStr.end(); + expFound = raiseException(allowMultipleExceptions, exceptionTxt, "", ExceptionHolder::FATAL_EXCEPTION); + } + if (offset.empty()) + { + if (adbParser->_currentNode->fields.empty()) + { + adbParser->_currentField->offset = 0; + } + else + { + AdbField* lastField = adbParser->_currentNode->fields.back(); + adbParser->_currentField->offset = lastField->offset + lastField->size; + } + } + else + { + adbParser->_currentField->offset = addr2int(offset); + } - while (regex_search(start, end, what, EXP_PATTERN)) { - string var(what[1].first, what[1].second); - string exp(what[2].first, what[2].second); -#if 0 - printf("%s) %s <=> %s\n", inst->fullName().c_str(), var.c_str(), exp.c_str()); - printf("Variable Attr: %s\n", inst->field->attrs["variables"].c_str()); -#endif - start = what[0].second; - curVars[var] = exp; - } + // Very tricky but works well for big endian arrays support - on endElement we will fix the address again + if (!expFound && adbParser->_adbCtxt->bigEndianArr) + { + u_int32_t offset = adbParser->_currentField->offset; + u_int32_t size = adbParser->_currentField->eSize(); - for (AttrsMap::iterator it = curVars.begin(); it - != curVars.end(); it++) { - vars[it->first] = it->second; - } + adbParser->_currentField->offset = + ((offset >> 5) << 5) + ((MIN(32, adbParser->_currentNode->size) - ((offset + size) % 32)) % 32); + } - // evaluate the variables themselves - for (AttrsMap::iterator it = curVars.begin(); it - != curVars.end(); it++) { - vars[it->first] = evalExpr(it->second, &vars); - } - } + // For DS alignment check + u_int32_t esize = adbParser->_currentField->eSize(); + if ((adbParser->_currentField->isLeaf() || adbParser->_currentField->subNode == "uint64") && + (esize == 16 || esize == 32 || esize == 64) && esize > adbParser->_currentNode->_maxLeafSize) + { + adbParser->_currentNode->_maxLeafSize = esize; + } -#if 0 - printf("Variables of: %s\n", inst->fullName().c_str()); - for (AttrsMap::iterator it = vars.begin(); it != vars.end(); it++) { - printf("\tVars: %s - %s\n", it->first.c_str(), it->second.c_str()); - } -#endif + if (!expFound && adbParser->_strict && adbParser->_currentNode->isUnion && adbParser->_currentField->isLeaf()) + { + expFound = raiseException( + allowMultipleExceptions, + "Fields are not allowed in unions (only subnodes)", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::FATAL_EXCEPTION); + } - // evaluate other attrs - for (AttrsMap::iterator it = field->attrs.begin(); it != field->attrs.end(); it++) { - if (it->first == "variables") { - continue; - } + if (!expFound && adbParser->_strict && adbParser->_currentNode->isUnion && adbParser->_currentField->size % 32) + { + expFound = raiseException( + allowMultipleExceptions, + "Union is allowed to contains only dword aligned subnodes", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::FATAL_EXCEPTION); + } - inst->setInstanceAttr(it->first, evalExpr(it->second, &vars)); - } - inst->setVarsMap(vars); - } catch (AdbException &exp) { - string exceptionTxt = - "Failed to evaluate expression for field (" - + inst->fullName() + "): " + exp.what_s(); - if (allowMultipleExceptions) { - insertNewException(ExceptionHolder::ERROR_EXCEPTION, exceptionTxt); - } else { - throw AdbException(exceptionTxt); - } - } - } - // else { - // inst->copyAllInstanceAttr(field->attrs); - // } + if (!expFound && adbParser->_currentNode->isUnion && + adbParser->_currentField->size > adbParser->_currentNode->size) + { + expFound = raiseException( + allowMultipleExceptions, + "Field size is greater than the parent node size", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::FATAL_EXCEPTION); + } - /* if union and uses selecotr field add it to _unionSelectorEvalDeffered in order to defferred evaluation */ - bool found = false; - inst->getInstanceAttrIterator("union_selector", found); - if (inst->isUnion() && found) { - _unionSelectorEvalDeffered.push_back(inst); + if (!expFound && adbParser->_strict && !adbParser->_currentField->isArray() && + adbParser->_currentField->isLeaf() && adbParser->_currentField->size > 32) + { + expFound = raiseException( + allowMultipleExceptions, + "Leaf fields can't be > 32 bits", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::FATAL_EXCEPTION); } - checkInstanceOffsetValidity(inst, parent, allowMultipleExceptions); + if (!expFound && adbParser->_checkDsAlign) + { + u_int32_t esize = adbParser->_currentField->eSize(); + if ((adbParser->_currentField->isLeaf() || adbParser->_currentField->subNode == "uint64") && + (esize == 16 || esize == 32 || esize == 64) && adbParser->_currentField->offset % esize != 0) + { + expFound = raiseException( + allowMultipleExceptions, + "Field: " + adbParser->_currentField->name + " in Node: " + adbParser->_currentNode->name + + " offset(" + boost::lexical_cast(adbParser->_currentField->offset) + + ") is not aligned to size(" + boost::lexical_cast(esize) + ")", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); + } + } - if (field->isStruct() && !inst->nodeDesc->fields.empty() && (depth - == -1 || depth > 0)) { - // validation 2 - if (inst->size != inst->nodeDesc->size) { - inst->nodeDesc->size = inst->size; - /*throw AdbException("Node +(" + inst->name + ") size (" + boost::lexical_cast(inst->size)) + - " isn't the same as its instance (" + inst->nodeDesc->name + - ") (" + boost::lexical_cast(inst->nodeDesc->size) + ")";*/ + if (adbParser->_enforceGuiChecks) + { + // check if all mandatory attributes were supplied + for (unsigned int i = 0; i < adbParser->field_mand_attr.size(); i++) + { + if (!checkAttrExist(atts, adbParser->field_mand_attr[i].c_str())) + { + expFound = raiseException( + allowMultipleExceptions, + "Atrribute: \"" + adbParser->field_mand_attr[i] + "\" for field must be specified.", + " in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); + } } - vector::iterator it; - for (it = inst->nodeDesc->fields.begin(); it - != inst->nodeDesc->fields.end(); it++) { - map < string, string > varsCopy = vars; - vector subItems = createInstance(*it, inst, vars, - isExprEval, progressObj, depth == -1 ? -1 : depth - 1, - ignoreMissingNodes, allowMultipleExceptions); - inst->subItems.insert(inst->subItems.end(), subItems.begin(), - subItems.end()); - } - - if (!inst->isUnion()) { - stable_sort(inst->subItems.begin(), inst->subItems.end(), - compareFieldsPtr ); - - for (size_t j = 0; j < inst->subItems.size() - 1; j++) { - //printf("field: %s, offset: %s\n", inst->subItems[j+1]->name.c_str(),formatAddr(inst->subItems[j+1]->offset, inst->subItems[j+1]->size).c_str()); - //printf("field: %s, offset: %s\n", inst->subItems[j]->name.c_str(),formatAddr(inst->subItems[j]->offset, inst->subItems[j]->size).c_str()); - if (inst->subItems[j + 1]->offset - < inst->subItems[j]->offset + inst->subItems[j]->size) { - string exceptionTxt = "Field (" - + inst->subItems[j + 1]->name + ") (" - + formatAddr(inst->subItems[j + 1]->offset, - inst->subItems[j + 1]->size).c_str() - + ") overlaps with (" + inst->subItems[j]->name - + ") (" + formatAddr(inst->subItems[j]->offset, - inst->subItems[j]->size).c_str() + ")"; - raiseException(allowMultipleExceptions, - exceptionTxt, - ExceptionHolder::ERROR_EXCEPTION); + // check if the field name is compatible with the fname pattern + if (!regex_match(adbParser->_currentField->name, regex(adbParser->_fname_pattern))) + { + expFound = raiseException( + allowMultipleExceptions, + "Illegal field name: \"" + adbParser->_currentField->name + + "\" doesn't match the given field name pattern", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::WARN_EXCEPTION); + } + } + + // Add all field attributes + if (!expFound) + { + for (int i = 0; i < attrCount(atts); i++) + { + if (adbParser->_enforceGuiChecks) + { + string aName = attrName(atts, i); + string aValue = attrValue(atts, i); + + // check if the field contains illegal attribute + if (!aName.compare("inst_if") && !aName.compare("inst_ifdef") && !aName.compare("subnode") && + adbParser->field_attr_names_set.find(aName) != adbParser->field_attr_names_set.end() && + adbParser->field_spec_attr.find(aName) != adbParser->field_spec_attr.end()) + { + expFound = + raiseException(allowMultipleExceptions, + "Unknown attribute: " + aName + " at field: " + adbParser->_currentField->name, + ", in file: \"" + adbParser->_fileName + + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); + } + + if (!aName.compare("enum")) + { + // check the enum attribute is compatible with the enum pattern + if (!regex_match(aValue, regex(adbParser->_enum_pattern))) + { + expFound = raiseException( + allowMultipleExceptions, + "Illegal value: \"" + aValue + "\" for attribute: \"" + aName + "\" ", + "doesn't match the given attribute pattern, in file: \"" + adbParser->_fileName + + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::WARN_EXCEPTION); + } + } + else + { + // check the attribute name is compatible with the attribute pattern + if (adbParser->attr_pattern.find(aName) != adbParser->attr_pattern.end() && + !regex_match(aValue, regex(adbParser->attr_pattern[aName]))) + { + expFound = raiseException( + allowMultipleExceptions, + "Illegal value: \"" + aValue + "\" for attribute: \"" + aName + "\" ", + "doesn't match the given attribute pattern, in file: \"" + adbParser->_fileName + + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::WARN_EXCEPTION); + } } } + adbParser->_currentField->attrs[attrName(atts, i)] = attrValue(atts, i); } } - - instList.push_back(inst); } - - stable_sort(instList.begin(), instList.end(), compareFieldsPtr ); - return instList; } /** - * Function: Adb::checkInstanceOffsetValidity + * Function: AdbParser::startElement **/ -void Adb::checkInstanceOffsetValidity(AdbInstance *inst, AdbInstance *parent, bool allowMultipleExceptions) +void AdbParser::startElement(void* _adbParser, const XML_Char* name, const XML_Char** atts) { - if (inst->offset + inst->size > parent->offset + parent->size) { - string exceptionTxt = "Field (" + inst->name + ") " + formatAddr( - inst->offset, inst->size) + " crosses its parent node (" - + parent->name + ") " + formatAddr(parent->offset, - parent->size) + " boundaries"; - if (allowMultipleExceptions) { - insertNewException(ExceptionHolder::ERROR_EXCEPTION, exceptionTxt); - } else { + AdbParser* adbParser = static_cast(_adbParser); + + int lineNumber = XML_GetCurrentLineNumber(adbParser->_xmlParser); + adbParser->_currentTagValue = ""; + + if (TAG_NODES_DEFINITION == name) + { + startNodesDefElement(atts, adbParser); + } + else if (TAG_ENUM == name) + { + startEnumElement(atts, adbParser, lineNumber); + } + else if (TAG_CONFIG == name) + { + startConfigElement(atts, adbParser, lineNumber); + } + else if (TAG_INFO == name) + { + startInfoElement(atts, adbParser); + } + else if (TAG_INCLUDE == name) + { + startIncludeElement(atts, adbParser, lineNumber); + } + else if (TAG_INSTANCE_OPS == name) + { + adbParser->_instanceOps = true; + } + else if (TAG_INSTANCE_OP_ATTR_REPLACE == name) + { + startInstOpAttrReplaceElement(atts, adbParser, lineNumber); + } + else if (TAG_NODE == name) + { + startNodeElement(atts, adbParser, lineNumber); + } + else if (TAG_FIELD == name) + { + startFieldElement(atts, adbParser, lineNumber); + } + else if (TAG_RCS_HEADERS == name) + { + // DO NOTHING + } + else + { + string exceptionTxt = "Unsupported tag: " + string(name); + if (allowMultipleExceptions) + { + exceptionTxt = exceptionTxt + ", in file: \"" + adbParser->_fileName + + "\" line: " + boost::lexical_cast(lineNumber); + ExceptionHolder::insertNewException(ExceptionHolder::FATAL_EXCEPTION, exceptionTxt); + return; + } + else + { throw AdbException(exceptionTxt); } } } /** - * Function: Adb::calcArrOffset + * Function: AdbParser::addReserved **/ -u_int32_t Adb::calcArrOffset(AdbField *fieldDesc, AdbInstance *parent, - u_int32_t arrIdx) { - int offs; +void AdbParser::addReserved(vector& reserveds, u_int32_t offset, u_int32_t size) +{ + u_int32_t numOfDwords = (dword(offset + size - 1) - dword(offset)) / 4 + 1; - if (fieldDesc->eSize() >= 32) { - // Make sure this is dword aligned - if (fieldDesc->eSize() % 32 || parent->offset % 32 || fieldDesc->offset - % 32) { - throw AdbException("Field " + fieldDesc->name + " isn't dword aligned"); - } - offs = parent->offset + fieldDesc->offset + fieldDesc->eSize() * arrIdx; - } else { - if (bigEndianArr) { - offs = (int) parent->offset + (int) fieldDesc->offset - - (int) fieldDesc->eSize() * arrIdx; + // printf("==> %s\n", formatAddr(offset, size).c_str()); + // printf("numOfDwords = %d\n", numOfDwords); - int dwordDelta = abs((dword(parent->offset + fieldDesc->offset) - dword(offs)))/ 4; + if (numOfDwords == 1 || ((offset % 32) == 0 && ((offset + size) % 32) == 0)) + { + AdbField* f1 = new AdbField; + f1->name = "reserved"; + f1->offset = offset; + f1->isReserved = true; + f1->size = size; - if (dwordDelta) { - offs += 32 * 2 * dwordDelta; + reserveds.push_back(f1); + // printf("case1: reserved0: %s\n", formatAddr(f1->offset, f1->size).c_str()); + } + else if (numOfDwords == 2) + { + AdbField* f1 = new AdbField; + f1->name = "reserved"; + f1->offset = offset; + f1->isReserved = true; + f1->size = 32 - startBit(offset); + + AdbField* f2 = new AdbField; + f2->name = "reserved"; + f2->offset = dword(offset + 32) * 8; + f2->isReserved = true; + f2->size = size - f1->size; + reserveds.push_back(f1); + reserveds.push_back(f2); + /*printf("case2: reserved0: %s, reserved1: %s\n", + formatAddr(f1->offset, f1->size).c_str(), + formatAddr(f2->offset, f2->size).c_str());*/ + } + else + { + AdbField* f1 = new AdbField; + f1->name = "reserved"; + f1->offset = offset; + f1->isReserved = true; + f1->size = 32 - startBit(offset); + + AdbField* f2 = new AdbField; + f2->name = "reserved"; + f2->offset = dword(offset + 32) * 8; + f2->isReserved = true; + f2->size = (numOfDwords - 2) * 32; + + if (!((offset + size) % 32)) + { + f2->size = (numOfDwords - 1) * 32; + reserveds.push_back(f1); + reserveds.push_back(f2); + /*printf("case3.1: reserved0: %s, reserved1: %s\n", + formatAddr(f1->offset, f1->size).c_str(), + formatAddr(f2->offset, f2->size).c_str());*/ + return; + } + else + { + if (!(f1->size % 32)) + { + f1->size = (numOfDwords - 1) * 32; + f2->size = size - f1->size; + f2->offset = f1->offset + f1->size; + reserveds.push_back(f1); + reserveds.push_back(f2); + /*printf("case3.2: reserved0: %s, reserved1: %s\n", + formatAddr(f1->offset, f1->size).c_str(), + formatAddr(f2->offset, f2->size).c_str());*/ + return; + } + else + { + f2->size = (numOfDwords - 2) * 32; + AdbField* f3 = new AdbField; + f3->name = "reserved"; + f3->offset = f2->offset + f2->size; + f3->isReserved = true; + f3->size = size - f1->size - f2->size; + reserveds.push_back(f1); + reserveds.push_back(f2); + reserveds.push_back(f3); + /*printf("case3.3: reserved0: %s, reserved1: %s, reserved2: %s\n", + formatAddr(f1->offset, f1->size).c_str(), + formatAddr(f2->offset, f2->size).c_str(), + formatAddr(f3->offset, f3->size).c_str());*/ + return; } - } else { - offs = parent->offset + fieldDesc->offset + fieldDesc->eSize() - * arrIdx; } } - - return offs; } /** - * Function: Adb::evalExpr + * Function: AdbParser::endElement **/ -string Adb::evalExpr(string expr, AttrsMap *vars) { - if (expr.find('$') == string::npos) { - return expr; +void AdbParser::endElement(void* _adbParser, const XML_Char* name) +{ + AdbParser* adbParser = static_cast(_adbParser); + int lineNumber = XML_GetCurrentLineNumber(adbParser->_xmlParser); + /* + * Config + * ---- + */ + if (TAG_CONFIG == name) + { + adbParser->_adbCtxt->configs.push_back(adbParser->_currentConfig); + adbParser->_currentConfig = NULL; } + /* + * Instance Operations + * ---- + */ + else if (TAG_INSTANCE_OPS == name) + { + adbParser->_instanceOps = false; + } + /* + * Node + * ---- + */ + else if (TAG_NODE == name) + { + // Sort the fields by offset + if (adbParser->skipNode) + { + adbParser->skipNode = false; + } + else + { + if (!adbParser->_currentNode->isUnion) + { + stable_sort(adbParser->_currentNode->fields.begin(), + adbParser->_currentNode->fields.end(), + compareFieldsPtr); + } + + // Check overlapping + vector reserveds; + AdbField prevFieldDummy; + prevFieldDummy.offset = 0; + prevFieldDummy.size = 0; + AdbField* prevField = &prevFieldDummy; + if (!adbParser->_currentNode->isUnion) + { + for (size_t i = 0; i < adbParser->_currentNode->fields.size(); i++) + { + AdbField* field = adbParser->_currentNode->fields[i]; + long int delta = (long)field->offset - (long)(prevField->offset + prevField->size); + + if (delta < 0) + { // Overlapping + string exceptionTxt = "Field: " + field->name + " " + formatAddr(field->offset, field->size) + + " overlaps with Field: " + prevField->name + " " + + formatAddr(prevField->offset, prevField->size); + if (allowMultipleExceptions) + { + exceptionTxt = exceptionTxt + ", in file: \"" + adbParser->_fileName + + "\" line: " + boost::lexical_cast(lineNumber); + ExceptionHolder::insertNewException(ExceptionHolder::ERROR_EXCEPTION, exceptionTxt); + } + else + { + throw AdbException(exceptionTxt); + } + } + if (delta > 0 && adbParser->_addReserved) + { // Need reserved + addReserved(reserveds, prevField->offset + prevField->size, delta); + } - smatch what, what2; - regex singleExpr("^([^\\$]*)(\\$\\(([^)]+)\\))(.*)$"); - while (regex_search(expr, what, singleExpr)) { - string vname = what[3].str(); - string vvalue; - regex singleVar("^[a-zA-Z_][a-zA-Z0-9_]*$"); - - //Need to change to array-like initialization when we'll move to c++11 - vector specialVars; - specialVars.push_back("NAME"); - specialVars.push_back("ARR_IDX"); - specialVars.push_back("BN"); - specialVars.push_back("parent"); - - if (regex_search(vname, what2, singleVar)) { - if (find(specialVars.begin(), specialVars.end(), vname) == specialVars.end()) { - return expr; - } else { - AttrsMap::iterator it = vars->find(vname); - if (it == vars->end()) { - throw AdbException("Can't find the variable: " + vname); + prevField = field; } - vvalue = it->second; - } - } else { - string vnameCopy = vname; - regex singleVarInExpr("[a-zA-Z_][a-zA-Z0-9_]*"); - smatch matches; - while (regex_search(vnameCopy, matches, singleVarInExpr)) { - if (find(specialVars.begin(), specialVars.end(), matches[0]) == specialVars.end()) { - return expr; - } - vnameCopy = matches.suffix(); } - char exp[vname.size() + 1]; - char *expPtr = exp; - strcpy(exp, vname.c_str()); - u_int64_t res; - _adbExpr.setVars(vars); - int status = _adbExpr.expr(&expPtr, &res); - string statusStr; - - //printf("-D- Eval expr \"%s\" = %ul.\n", vname.ascii(), (u_int32_t)res); - - if (status < 0) { - switch (status) { - case Expr::ERR_RPAR_EXP: - statusStr = "Right parentheses expected"; - break; - - case Expr::ERR_VALUE_EXP: - statusStr = "Value expected"; - break; - - case Expr::ERR_BIN_EXP: - statusStr = "Binary operation expected "; - break; - - case Expr::ERR_DIV_ZERO: - statusStr = "Divide zero attempt"; - break; - - case Expr::ERR_BAD_NUMBER: - statusStr = "Bad constant syntax"; - break; - - case Expr::ERR_BAD_NAME: - statusStr = "Variable Name not resolved"; - break; + // Add reserved filler at end of union/node + // special case if node is empty, we need to add reserved anyway + if (adbParser->_addReserved /*|| adbParser->_currentNode->fields.empty()*/) + { + if (adbParser->_currentNode->isUnion) + { + addReserved(reserveds, 0, adbParser->_currentNode->size); + } + else + { + long delta = (long)adbParser->_currentNode->size - (long)(prevField->offset + prevField->size); + /*if (adbParser->_currentNode->name == "miss_machine_slice_info") + { + printf("Adding filler\n"); + printf("node offs: %s, prevField: %s\n", formatAddr(0, adbParser->_currentNode->size).c_str(), + formatAddr(prevField->offset, prevField->size).c_str()); + printf("delta: %ld\n", delta); + }*/ + + if (delta > 0) + { + addReserved(reserveds, prevField->offset + prevField->size, delta); + } + } - default: - statusStr = "Unknown error"; + adbParser->_currentNode->fields.insert(adbParser->_currentNode->fields.end(), reserveds.begin(), + reserveds.end()); + } + if (adbParser->_checkDsAlign && adbParser->_currentNode->_maxLeafSize != 0 && + adbParser->_currentNode->_maxLeafSize != 0 && + adbParser->_currentNode->size % adbParser->_currentNode->_maxLeafSize != 0) + { + raiseException( + allowMultipleExceptions, + "Node: " + adbParser->_currentNode->name + " size(" + + boost::lexical_cast(adbParser->_currentNode->size) + ") is not aligned with largest leaf(" + + boost::lexical_cast(adbParser->_currentNode->_maxLeafSize) + ")", + ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), + ExceptionHolder::ERROR_EXCEPTION); + } + + // Re-fix fields offset + if (adbParser->_adbCtxt->bigEndianArr) + { + for (size_t i = 0; i < adbParser->_currentNode->fields.size(); i++) + { + u_int32_t offset = adbParser->_currentNode->fields[i]->offset; + u_int32_t size = adbParser->_currentNode->fields[i]->eSize(); + + adbParser->_currentNode->fields[i]->offset = + ((offset >> 5) << 5) + ((MIN(32, adbParser->_currentNode->size) - ((offset + size) % 32)) % 32); } - - throw AdbException( - "Error evaluating expression " + expr + " : " - + statusStr); } - vvalue = boost::lexical_cast(res); + // Resort the fields by offset (since we changed offset to workaround big endian issues and added reserved) + if (!adbParser->_currentNode->isUnion) + { + stable_sort(adbParser->_currentNode->fields.begin(), + adbParser->_currentNode->fields.end(), + compareFieldsPtr); + } + + // Add this node to AdbCtxt + adbParser->_adbCtxt->nodesMap.insert( + pair(adbParser->_currentNode->name, adbParser->_currentNode)); + adbParser->_currentNode = 0; + + // Call progress_func callback + if (adbParser->_progressObj && !(adbParser->_progressCnt++ % PROGRESS_NODE_CNT)) + { + adbParser->_progressObj->progress(); // TODO - call with real parameters + } } - - /*printf("what[0] - %s, what[1] - %s, what[2] - %s, what[3] - %s, what[4] - %s\n", - what[0].str().c_str(), what[1].str().c_str(), - what[2].str().c_str(), what[3].str().c_str(), - what[4].str().c_str());*/ - - expr = what[1].str() + vvalue + what[4].str(); } + /* + * Field + * ---- + */ + else if (TAG_FIELD == name) + { + // Add this field to current node + if (!adbParser->skipNode) + { + if (adbParser->_currentNode->name == "root") + { + adbParser->_adbCtxt->rootNode = adbParser->_currentField->subNode; + } - return expr; -} + // Check conditions + bool cond = true; + AttrsMap::iterator it; + it = adbParser->_currentField->attrs.find("inst_ifdef"); + if (it != adbParser->_currentField->attrs.end()) + { + bool found = false; + for (size_t i = 0; i < adbParser->_adbCtxt->configs.size(); i++) + { + AttrsMap::iterator it_def = adbParser->_adbCtxt->configs[i]->attrs.find("define"); + if (it_def != adbParser->_adbCtxt->configs[i]->attrs.end()) + { + vector defVal; + boost::algorithm::split(defVal, it_def->second, boost::is_any_of(string("="))); + + if (defVal[0] == it->second) + { + found = true; + break; + } + } + } + cond = found; + } -/** - * Function: Adb::checkInstSizeConsistency - **/ -bool Adb::checkInstSizeConsistency(bool allowMultipleExceptions) { - bool status = true; - for (NodesMap::iterator it = nodesMap.begin(); it != nodesMap.end(); it++) { - for (size_t i = 0; i < it->second->fields.size(); i++) { - if (it->second->fields[i]->isStruct()) { - NodesMap::iterator iter = nodesMap.find( - it->second->fields[i]->subNode); - if (iter == nodesMap.end()) { - continue; - /* - _lastError = "Can't find definition of subnode \"" + it->second->fields[i]->subNode + - "\" instantiated from node \"" + it->first + "\""; - return false; - */ + // + it = adbParser->_currentField->attrs.find("inst_if"); + if (cond && it != adbParser->_currentField->attrs.end()) + { + // Prepare vars + map vars; + for (size_t i = 0; i < adbParser->_adbCtxt->configs.size(); i++) + { + AttrsMap::iterator it_def = adbParser->_adbCtxt->configs[i]->attrs.find("define"); + if (it_def != adbParser->_adbCtxt->configs[i]->attrs.end()) + { + vector defVal; + boost::algorithm::split(defVal, it_def->second, boost::is_any_of(string("="))); + + if (defVal.size() == 1) + { + vars[defVal[0]] = "0"; + } + else + { + vars[defVal[0]] = defVal[1]; + } + } } - AdbNode *node = nodesMap[it->second->fields[i]->subNode]; - if (node->size != it->second->fields[i]->size - / it->second->fields[i]->arrayLen()) { - char tmp[256]; - sprintf( - tmp, - "Node (%s) size 0x%x.%d is not consistent with the instance (%s->%s) size 0x%x.%d", - node->name.c_str(), (node->size >> 5) << 2, - node->size % 32, it->second->name.c_str(), - it->second->fields[i]->name.c_str(), - (it->second->fields[i]->size >> 5) << 2, - it->second->fields[i]->size % 32); - _lastError = tmp; - if (allowMultipleExceptions) { - status = false; - insertNewException(ExceptionHolder::ERROR_EXCEPTION, tmp); - } else { - return false; + + AdbExpr adbExpr; + char* expOrg = new char[it->second.size() + 1]; + char* exp = expOrg; + if (!exp) + { + throw AdbException("Memory allocation error"); + } + strcpy(exp, it->second.c_str()); + + u_int64_t res; + adbExpr.setVars(&vars); + int status = adbExpr.expr(&exp, &res); + delete[] expOrg; + string statusStr; + + if (status < 0) + { + string exceptionTxt = string("Error evaluating expression \"") + it->second.c_str() + + "\" : " + AdbExpr::statusStr(status); + if (allowMultipleExceptions) + { + exceptionTxt = exceptionTxt + ", in file: \"" + adbParser->_fileName + + "\" line: " + boost::lexical_cast(lineNumber); + ExceptionHolder::insertNewException(ExceptionHolder::ERROR_EXCEPTION, exceptionTxt); + } + else + { + throw AdbException(exceptionTxt); + } + } + cond = !!res; + } + + if (cond) + { + // Name should be unique + if (adbParser->_strict) + { + for (size_t i = 0; i < adbParser->_currentNode->fields.size(); i++) + { + if (!adbParser->_currentNode->fields[i]->name.compare(adbParser->_currentField->name)) + { + string exceptionTxt = + "The field \"" + adbParser->_currentField->name + "\" isn't unique in node"; + if (allowMultipleExceptions) + { + exceptionTxt = exceptionTxt + ", in file: \"" + adbParser->_fileName + + "\" line: " + boost::lexical_cast(lineNumber); + ExceptionHolder::insertNewException(ExceptionHolder::ERROR_EXCEPTION, exceptionTxt); + } + else + { + throw AdbException(exceptionTxt); + } + } } } + it = adbParser->_currentField->attrs.find("condition"); + if (it != adbParser->_currentField->attrs.end()) + { + adbParser->_currentField->condition = string(it->second); + } + + adbParser->_currentNode->fields.push_back(adbParser->_currentField); } + else + { + adbParser->_currentNode->condFields.push_back(adbParser->_currentField); + } + adbParser->_currentField = 0; } } - - return status; } /** - * Function: Adb::getLastError + * Function: AdbParser::raiseException **/ -string Adb::getLastError() { - return _lastError; +bool AdbParser::raiseException(bool allowMultipleExceptions, string exceptionTxt, string addedMsg, const string expType) +{ + if (allowMultipleExceptions) + { + ExceptionHolder::insertNewException(expType, exceptionTxt + addedMsg); + return false; + } + else + { + throw AdbException(exceptionTxt); + } } -/** - * Function: Adb::print - **/ -void Adb::print(int indent) { - cout << indentString(indent) << "Include paths: " << endl; - for (size_t i = 0; i < includePaths.size(); i++) - cout << indentString(indent + 1) << includePaths[i] << endl; +#if 0 +void progress(int i, int t) +{ + printf("."); + fflush(stdout); +} - cout << indentString(indent) << "Is Big Endian Arrays: " << bigEndianArr - << endl; - cout << "-------------------------------------" << endl; - cout << indentString(indent) << "Configs: " << endl; - for (size_t i = 0; i < configs.size(); i++) - configs[i]->print(indent + 1); +int main() +{ +#define ADB_FILE "path_to.adb" + Adb adb; + if (!adb.load(ADB_FILE, true /*, progress*/)) { + printf("Failed : %s\n", adb.getLastError().c_str()); + return 1; + } - cout << "-------------------------------------" << endl; - cout << indentString(indent) << "Nodes" << endl; - NodesMap::iterator iter; - for (iter = nodesMap.begin(); iter != nodesMap.end(); iter++) - iter->second->print(indent + 1); -} + try + { + //adb.print(0); + cout << "Root node: " << adb.rootNode << endl; + AdbInstance *root = adb.createLayout(adb.rootNode, false, NULL); + AdbInstance *child = root->getChildByPath("packetA.x"); + if (!child) { + printf("can't find packetA.x\n"); + return 1; + } -/*************************** AdbException Implementation ***************************/ + printf("Child name: %s, addr: %s\n", child->name.c_str(), formatAddr(child->offset, child->size).c_str()); -AdbException::AdbException() { -} + string n1, n2; + u_int64_t v1, v2; + child->intToEnum(0, n1); + child->intToEnum(1, n2); + child->enumToInt("SET", v1); + child->enumToInt("GET", v2); -/** - * Function: AdbException::AdbException - **/ -AdbException::AdbException(const char *fmt, ...) { - char tmp[1024]; - va_list args; - va_start(args, fmt); - vsprintf(tmp, fmt, args); - va_end(args); - _msg = tmp; -} + printf("enumName(0) = %s, enumName(1) = %s, value(SET) = %d, value(GET) = %d", n1.c_str(), n2.c_str(), v1, v2); + //root->print(); + } + catch (AdbException &exp) + { + printf("-E- %s\n", exp.what()); + return 1; + } -/** - * Function: AdbException::AdbException - **/ -AdbException::AdbException(string msg) : - _msg(msg) { + cout << endl; + //adb.print(0); + return 0; } +#endif -/** - * Function: AdbException::~AdbException - **/ -AdbException::~AdbException() throw () { +Adb::Adb() : bigEndianArr(false), singleEntryArrSupp(false), _checkDsAlign(false), _enforceGuiChecks(false) +{ + _logFile = new LogFile; } /** - * Function: AdbException::what + * Function: Adb::~Adb **/ -const char* AdbException::what() const throw () { - return _msg.c_str(); +Adb::~Adb() +{ + // Free configs + for (size_t i = 0; i < configs.size(); i++) + delete configs[i]; + + // Free nodes + NodesMap::iterator iter; + for (iter = nodesMap.begin(); iter != nodesMap.end(); iter++) + delete iter->second; + + delete _logFile; } /** - * Function: AdbException::what_s + * Function: Adb::raiseException **/ -string AdbException::what_s() const { - return _msg; +void Adb::raiseException(bool allowMultipleExceptions, string exceptionTxt, const string expType) +{ + if (allowMultipleExceptions) + { + ExceptionHolder::insertNewException(expType, exceptionTxt); + } + else + { + throw AdbException(exceptionTxt); + } + return; } -/*************************** AdbParser Implementation ***************************/ /** - * Function: AdbParser::AdbParser + * Function: Adb::getAdbExceptionsMap + * This function return the adb exception map **/ -AdbParser::AdbParser(string fileName, Adb *adbCtxt, bool addReserved, - AdbProgress *progressObj, bool strict, string includePath, - bool enforceExtraChecks) : - _adbCtxt(adbCtxt), _fileName(fileName), _addReserved(addReserved), - _progressCnt(0), _progressObj(progressObj), _strict(strict), - _includePath(includePath), _currentNode(0), _currentField(0), - _currentConfig(0) { - _enforceExtraChecks = enforceExtraChecks; - if (includePath != "") { - addIncludePaths(adbCtxt, includePath); +void Adb::fetchAdbExceptionsMap(ExceptionsMap otherMap) +{ + vector fatals = otherMap[ExceptionHolder::FATAL_EXCEPTION]; + for (vector::iterator it = fatals.begin(); it != fatals.end(); ++it) + { + insertNewException(ExceptionHolder::FATAL_EXCEPTION, *it); } - _xmlParser = XML_ParserCreate(0); - XML_SetUserData(_xmlParser, this); - XML_SetElementHandler(_xmlParser, startElement, endElement); - if (adbCtxt->includePaths.size() == 0) { - // first add the opened project path - adbCtxt->includePaths.push_back( - _fileName.find(OS_PATH_SEP) == string::npos ? "." - : _fileName.substr(0, _fileName.rfind(OS_PATH_SEP))); - vector < string > path; - boost::algorithm::split(path, fileName, - boost::is_any_of(string(OS_PATH_SEP))); - IncludeFileInfo info = { fileName, "ROOT", 0 }; - _adbCtxt->includedFiles[path[path.size() - 1]] = info; + vector errors = otherMap[ExceptionHolder::ERROR_EXCEPTION]; + for (vector::iterator it = errors.begin(); it != errors.end(); ++it) + { + insertNewException(ExceptionHolder::ERROR_EXCEPTION, *it); + } + vector warnings = otherMap[ExceptionHolder::WARN_EXCEPTION]; + for (vector::iterator it = warnings.begin(); it != warnings.end(); ++it) + { + insertNewException(ExceptionHolder::WARN_EXCEPTION, *it); } - _instanceOps = false; -} - -void AdbParser::addIncludePaths(Adb *adbCtxt, string includePaths) { - vector < string > paths; - boost::algorithm::split(paths, includePaths, - boost::is_any_of(string(";"))); - adbCtxt->includePaths.insert(adbCtxt->includePaths.end(), - paths.begin(), paths.end()); - - StringVector relatives; - string - projPath = - boost::filesystem::path(adbCtxt->mainFileName).parent_path().string(); - - for (StringVector::iterator it = adbCtxt->includePaths.begin(); it - != adbCtxt->includePaths.end(); it++) { - if (projPath != "" && projPath != *it && boost::filesystem::path(*it).is_relative()) { - relatives.push_back(projPath + OS_PATH_SEP + *it); - } - } - - adbCtxt->includePaths.insert(adbCtxt->includePaths.end(), - relatives.begin(), relatives.end()); } /** - * Function: AdbParser::~AdbParser + * Function: Adb::insertNewException + * This function take the excpetion type [FATAL:0, ERROR:1, WARNING:2] and the exception string + * Then it insert it to the adb exception map **/ -AdbParser::~AdbParser() { - XML_ParserFree(_xmlParser); +void Adb::insertNewException(const string exceptionType, string exceptionTxt) +{ + adbExceptionMap[exceptionType].push_back(exceptionTxt); } /** - * Function: AdbParser::setAllowMultipleExceptionsTrue - * This function is a static function to change the value of set all exceptions to true + * Function: Adb::printAdbExceptionMap + * This function will pring the content of the Adb Exception Map **/ -void AdbParser::setAllowMultipleExceptionsTrue() { - AdbParser::allowMultipleExceptions = true; +string Adb::printAdbExceptionMap() +{ + string errorStr = ""; + vector fatals = adbExceptionMap[ExceptionHolder::FATAL_EXCEPTION]; + for (vector::iterator it = fatals.begin(); it != fatals.end(); ++it) + { + errorStr += "-" + ExceptionHolder::FATAL_EXCEPTION + "- " + *it + ";"; + } + vector errors = adbExceptionMap[ExceptionHolder::ERROR_EXCEPTION]; + for (vector::iterator it = errors.begin(); it != errors.end(); ++it) + { + errorStr += "-" + ExceptionHolder::ERROR_EXCEPTION + "- " + *it + ";"; + } + vector warnings = adbExceptionMap[ExceptionHolder::WARN_EXCEPTION]; + for (vector::iterator it = warnings.begin(); it != warnings.end(); ++it) + { + errorStr += "-" + ExceptionHolder::WARN_EXCEPTION + "- " + "- " + *it + ";"; + } + return errorStr; } /** - * Function: AdbParser::load + * Function: Adb::load **/ -bool AdbParser::load() { - FILE *file; - char *data = NULL; - file = fopen(_fileName.c_str(), "r"); - _adbCtxt->_logFile.appendLogFile("Opening " + _fileName + "\n"); - - if (!file) { - _lastError = "Can't open file (" + _fileName + ") for reading: " - + strerror(errno); - if (allowMultipleExceptions) { - ExceptionHolder::insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); +bool Adb::load(string fname, + bool addReserved, + AdbProgress* progressObj, + bool strict, + string includePath, + string includeDir, + bool enforceExtraChecks, + bool allowMultipleExceptions, + string logFileStr, + bool checkDsAlign, + bool enforceGuiChecks) +{ + try + { + bool status = true; + mainFileName = fname; + if (allowMultipleExceptions) + { + AdbParser::setAllowMultipleExceptionsTrue(); } - return false; - } - - if (fseek(file, 0L, SEEK_END) < 0) {// go to the end of the file - _lastError = "fseek() failed for file (" + _fileName + "): " - + strerror(errno); - fclose(file); - if (allowMultipleExceptions) { - ExceptionHolder::insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); + _logFile->init(logFileStr, allowMultipleExceptions); + AdbParser p(fname, this, addReserved, progressObj, strict, includePath, enforceExtraChecks, checkDsAlign, + enforceGuiChecks); + _checkDsAlign = checkDsAlign; + _enforceGuiChecks = enforceGuiChecks; + if (!p.load()) + { + _lastError = p.getError(); + status = false; } - return false; + if (status && includeDir != "") + { + AdbParser::includeAllFilesInDir(&p, includeDir); + } + if (status && !nodesMap.size()) + { + _lastError = "Empty project, no nodes were found"; + if (allowMultipleExceptions) + { + insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); + } + status = false; + } + if (status) + { + bool checkSizeConsistency = strict ? checkInstSizeConsistency(allowMultipleExceptions) : true; + status = status && checkSizeConsistency; + } + if (allowMultipleExceptions && ExceptionHolder::getNumberOfExceptions() > 0) + { + fetchAdbExceptionsMap(ExceptionHolder::getAdbExceptionsMap()); + status = false; + } + return status; } - - long fSize = ftell(file); - if (fSize < 0) { - _lastError = "ftell() failed for file (" + _fileName + "): " - + strerror(errno); - fclose(file); - if (allowMultipleExceptions) { - ExceptionHolder::insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); + catch (AdbException& e) + { + _lastError = e.what_s(); + if (allowMultipleExceptions) + { + insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); } return false; } +} - data = (char*) malloc(fSize + 1); - if (!data) { - fclose(file); - throw AdbException("Out of memory."); - } - - if (fseek(file, 0L, SEEK_SET) < 0) { //go back to the start of the file - _lastError = "Failed to read file (" + _fileName + "): " + strerror( - errno); - fclose(file); - free(data); - data = NULL; - if (allowMultipleExceptions) { - ExceptionHolder::insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); +/** + * Function: Adb::loadFromString + **/ +bool Adb::loadFromString(const char* adbContents, + bool addReserved, + AdbProgress* progressObj, + bool strict, + bool enforceExtraChecks) +{ + try + { + AdbParser p(string(), this, addReserved, progressObj, strict, "", enforceExtraChecks); + mainFileName = OS_PATH_SEP; + if (!p.loadFromString(adbContents)) + { + _lastError = p.getError(); + return false; } - return false; - } - size_t result = fread(data, fSize, 1, file); - if (result != 1) { - _lastError = "Failed to read file (" + _fileName + "): " + strerror( - errno); - fclose(file); - free(data); - data = NULL; - if (allowMultipleExceptions) { - ExceptionHolder::insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); + if (!nodesMap.size()) + { + _lastError = "Empty project, no nodes were found"; + return false; } + + return strict ? checkInstSizeConsistency() : true; + } + catch (AdbException& e) + { + _lastError = e.what_s(); return false; } +} - data[fSize] = 0; - fclose(file); - - bool xmlStatus = true; - try { - if (!XML_Parse(_xmlParser, data, strlen(data), 0)) { - enum XML_Error errNo = XML_GetErrorCode(_xmlParser); - throw AdbException( - string("XML parsing issues: ") + XML_ErrorString(errNo)); - } - } catch (AdbException& exp) { - if (exp.what_s().find("in file:") == string::npos) { - int line = XML_GetCurrentLineNumber(_xmlParser); - int column = XML_GetCurrentColumnNumber(_xmlParser); - _lastError = exp.what_s() + " in file: " + _fileName + " at line: " - + boost::lexical_cast(line) + " column: " - + boost::lexical_cast(column); - } else { - _lastError = exp.what_s(); +/** + * Function: Adb::toXml + **/ +string Adb::toXml(vector nodeNames, bool addRootNode, string rootName, string addPrefix) +{ + try + { + vector nodeDeps; + for (vector::iterator it = nodeNames.begin(); it != nodeNames.end(); it++) + { + vector tmp = getNodeDeps(*it); + nodeDeps.insert(nodeDeps.end(), tmp.begin(), tmp.end()); } - if (allowMultipleExceptions) { - xmlStatus = false; - ExceptionHolder::insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); - } else { - free(data); - return false; + stable_sort(nodeDeps.begin(), nodeDeps.end()); + nodeDeps.erase(unique(nodeDeps.begin(), nodeDeps.end()), nodeDeps.end()); + + string xml; + if (this->version == "2") + { + xml = "\n"; } - } catch (std::runtime_error& e) { - _lastError = CHECK_RUNTIME_ERROR(e); - if (allowMultipleExceptions) { - xmlStatus = false; - ExceptionHolder::insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); - } else { - free(data); - return false; + else + { + xml = "\n"; } - } catch (...) { - int line = XML_GetCurrentLineNumber(_xmlParser); - int column = XML_GetCurrentColumnNumber(_xmlParser); - _lastError = string( - "An exception raised during the loading of the file: ") - + _fileName + " at line: " + boost::lexical_cast(line) - + " column: " + boost::lexical_cast(column); - if (allowMultipleExceptions) { - xmlStatus = false; - ExceptionHolder::insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); - } else { - free(data); - return false; + for (ConfigList::iterator it = configs.begin(); it != configs.end(); it++) + { + xml += (*it)->toXml() + "\n"; } - } - free(data); - return xmlStatus; -} + // Add source info + xml += "\n"; -/** - * Function: AdbParser::loadFromString - **/ -bool AdbParser::loadFromString(const char *adbString) { - _fileName = "\"STRING\""; - try { - if (!XML_Parse(_xmlParser, adbString, strlen(adbString), 0)) { - enum XML_Error errNo = XML_GetErrorCode(_xmlParser); - throw AdbException( - string("XML parsing issues: ") + XML_ErrorString(errNo)); - } - } catch (AdbException& exp) { - if (exp.what_s().find("in file:") == string::npos) { - int line = XML_GetCurrentLineNumber(_xmlParser); - int column = XML_GetCurrentColumnNumber(_xmlParser); - _lastError = exp.what_s() + " in file: " + _fileName + " at line: " - + boost::lexical_cast(line) + " column: " - + boost::lexical_cast(column); - } else { - _lastError = exp.what_s(); + if (nodeNames.empty()) + { + for (NodesMap::iterator it = nodesMap.begin(); it != nodesMap.end(); it++) + { + AdbNode* node = it->second; + xml += node->toXml(addPrefix); + } } + else + { + u_int32_t maxSize = 0; + for (size_t i = 0; i < nodeDeps.size(); i++) + { + NodesMap::iterator it = nodesMap.find(nodeDeps[i]); + if (it == nodesMap.end()) + { + _lastError = "Can't find node definition for: " + nodeDeps[i]; + return ""; + } - _lastError += string( - "\nNOTE: this project is configured to work with: \"") - + (_adbCtxt->bigEndianArr ? "Big" : "Little") - + " Endian Arrays\""; - return false; - } catch (std::runtime_error& e) { - _lastError = CHECK_RUNTIME_ERROR(e); - return false; - } catch (...) { - int line = XML_GetCurrentLineNumber(_xmlParser); - int column = XML_GetCurrentColumnNumber(_xmlParser); - _lastError = string( - "An exception raised during the loading of the file: ") - + _fileName + " at line: " + boost::lexical_cast(line) - + " column: " + boost::lexical_cast(column); - return false; - } + AdbNode* node = it->second; + xml += node->toXml(addPrefix) + "\n\n"; - return true; -} + maxSize = TOOLS_MAX(node->size, maxSize); + } -/** - * Function: AdbParser::getError - **/ -string AdbParser::getError() { - return _lastError; -} + if (addRootNode) + { + stringstream buf; + buf << "> 5) << 2) << "." << dec << (maxSize % 32) + << "\" descr=\"\" >\n"; + buf << "\t> 5) << 2) << "." << dec << (maxSize % 32) << "\" subnode=\"" + << addPrefix + rootName << "\" descr=\"\" />\n"; + buf << "\n\n"; -/** - * Function: AdbParser::attrsCount - **/ -int AdbParser::attrCount(const XML_Char **atts) { - int i = 0; - while (atts[i]) { - i++; + buf << "> 5) << 2) << "." + << dec << (maxSize % 32) << "\" attr_is_union=\"1\" descr=\"\" >\n"; + for (size_t i = 0; i < nodeNames.size(); i++) + { + AdbNode* node = nodesMap[nodeNames[i]]; + buf << "\tname << "\" offset=\"0x0.0\" size=\"0x" << hex + << ((node->size >> 5) << 2) << "." << dec << (node->size % 32) + << "\" subnode=\"" + addPrefix + node->name + "\" descr=\"\" />\n"; + } + buf << "\n"; + xml += buf.str(); + } + } + + xml += "\n"; + return xml; + } + catch (AdbException& exp) + { + _lastError = exp.what_s(); + return ""; } - return i / 2; } /** - * Function: AdbParser::attrValue + * Function: Adb::addMissingNodes **/ -string AdbParser::attrValue(const XML_Char **atts, const XML_Char *attrName) { - int i = 0; - while (atts[i]) { - if (!strcmp(atts[i], attrName)) { - return string(atts[i + 1]); + +AdbInstance* Adb::addMissingNodes(int depth, bool allowMultipleExceptions) +{ + try + { + NodesMap::iterator it; + for (it = nodesMap.begin(); it != nodesMap.end(); it++) + { + AdbNode* nodeDesc = it->second; + for (size_t i = 0; (depth == -1 || depth > 0) && i < nodeDesc->fields.size(); i++) + { + AdbField* fieldDesc = nodeDesc->fields[i]; + for (u_int32_t i = 0; i < fieldDesc->arrayLen(); i++) + { + if (fieldDesc->isStruct()) + { + NodesMap::iterator it2 = nodesMap.find(fieldDesc->subNode); + if (it2 == nodesMap.end()) + { + // If in ignore missing nodes mode, it should create temporary node with placeholder + AdbNode* tmpNode = new AdbNode; + tmpNode->name = fieldDesc->subNode; + tmpNode->size = fieldDesc->eSize(); + tmpNode->desc = fieldDesc->desc + " ***MISSING NODE***"; + tmpNode->isUnion = false; + tmpNode->fileName = "tempForMissingNodes.adb"; + tmpNode->lineNumber = 0; + AdbField* tmpField = new AdbField; + tmpField->name = "placeholder"; + tmpField->desc = "This field is part of auto generated node for missing node."; + tmpField->size = tmpNode->size; + tmpField->offset = 0; + tmpNode->fields.push_back(tmpField); + nodesMap.insert(pair(tmpNode->name, tmpNode)); + } + } + } + } } - i += 2; } - - return string(); + catch (AdbException& exp) + { + _lastError = exp.what_s(); + if (allowMultipleExceptions) + { + insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); + } + return NULL; + } + catch (...) + { + _lastError = "Unknown error occurred"; + return NULL; + } + return NULL; } /** - * Function: AdbParser::attrName + * Function: Adb::cleaninstAttrs **/ -string AdbParser::attrName(const XML_Char **atts, int i) { - return string(atts[i * 2]); +void Adb::cleanInstAttrs() +{ + for (InstanceAttrs::iterator it = instAttrs.begin(); it != instAttrs.end(); it++) + { + it->second.clear(); + } + instAttrs.clear(); } /** - * Function: AdbParser::attrName + * Function: Adb::createLayout **/ -string AdbParser::attrValue(const XML_Char **atts, int i) { - return string(atts[i * 2 + 1]); -} +AdbInstance* Adb::createLayout(string rootNodeName, + bool isExprEval, + AdbProgress* progressObj, + int depth, + bool ignoreMissingNodes, + bool allowMultipleExceptions) +{ + try + { + // find root in nodes map + NodesMap::iterator it; + it = nodesMap.find(rootNodeName); + if (it == nodesMap.end()) + { + raiseException(allowMultipleExceptions, + "Can't find definition for node \"" + rootNodeName + "\"", + ExceptionHolder::FATAL_EXCEPTION); + } -/** - * Function: AdbParser::findFile - **/ -string AdbParser::findFile(string fileName) { - FILE *f; + AdbNode* nodeDesc = it->second; + nodeDesc->inLayout = true; + AdbInstance* rootItem = new AdbInstance(); + rootItem->fieldDesc = NULL; + rootItem->nodeDesc = nodeDesc; + rootItem->parent = NULL; + rootItem->name = nodeDesc->name; + rootItem->offset = 0; + rootItem->size = nodeDesc->size; + rootItem->copyAllInstanceAttr(nodeDesc->attrs); - for (size_t i = 0; i < _adbCtxt->includePaths.size(); i++) { - string filePath = _adbCtxt->includePaths[i] + OS_PATH_SEP + fileName; - f = fopen(filePath.c_str(), "r"); - if (f) { - fclose(f); - return filePath; - } else if (fileName.find(_adbCtxt->includePaths[i]) != string::npos) { // includePath is part of fileName - f = fopen(fileName.c_str(), "r"); - if (f) { - fclose(f); - return fileName; - } + map emptyVars; + _unionSelectorEvalDeffered.clear(); + if (ignoreMissingNodes) + { + addMissingNodes(depth, allowMultipleExceptions); } - } - return string(); -} - -/** - * Function: AdbParser::addr2int - **/ -u_int32_t AdbParser::addr2int(string& s) { - try { - u_int32_t res; - boost::algorithm::to_lower(s); - char *end; - vector < string > words; - boost::algorithm::split(words, s, boost::is_any_of(string("."))); - // fix for cases like: 0x.16 - if (words.size() && !words[0].compare("0x")) { - words[0] = "0"; + for (size_t i = 0; (depth == -1 || depth > 0) && i < nodeDesc->fields.size(); i++) + { + vector subItems = + createInstance(nodeDesc->fields[i], rootItem, emptyVars, isExprEval, progressObj, + depth == -1 ? -1 : depth - 1, ignoreMissingNodes, allowMultipleExceptions); + rootItem->subItems.insert(rootItem->subItems.end(), subItems.begin(), subItems.end()); } - switch (words.size()) { - case 1: - res = strtoul(words[0].c_str(), &end, 0); - if (*end != '\0') { - throw AdbException(); - } + // Now set the instance attributes (override field attrs), only if this is root node instantiation + if (rootNodeName == rootNode && depth == -1) + { + for (InstanceAttrs::iterator it = instAttrs.begin(); it != instAttrs.end(); it++) + { + size_t idx = it->first.find("."); + string path = idx == string::npos ? string() : it->first.substr(idx + 1); - res *= 8; - break; - - case 2: - if (words[0].empty()) { - // .DDD form - res = strtoul(words[1].c_str(), &end, 0); - if (*end != '\0') { - throw AdbException(); - } - } else { - // 0xHHHHH.DDD or DDDDD.DDD form - res = strtoul(words[0].c_str(), &end, 0); - if (*end != '\0') { - throw AdbException(); + AdbInstance* inst = rootItem->getChildByPath(path); + if (!inst) + { + raiseException(allowMultipleExceptions, + "Can't find instance path (" + it->first + ") defined in section", + ExceptionHolder::ERROR_EXCEPTION); } - res *= 8; - res += strtoul(words[1].c_str(), &end, 0); - if (*end != '\0') { - throw AdbException(); + + for (AttrsMap::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++) + { + if (allowMultipleExceptions && !inst) + { + break; + } + inst->setInstanceAttr(it2->first, it2->second); } } - break; - - default: - throw AdbException("Invalid size: " + s); } - return res; - } catch (AdbException& exp) { - throw AdbException( - "Failed to retrieve integer from string: " + exp.what_s()); - } -} - -/** - * Function: AdbParser::dword - **/ -u_int32_t AdbParser::dword(u_int32_t offset) { - return (offset >> 5) << 2; -} - -/** - * Function: AdbParser::startBit - **/ -u_int32_t AdbParser::startBit(u_int32_t offset) { - return offset % 32; -} - -/** - * Function: AdbParser::descXmlToNative - **/ -string AdbParser::descXmlToNative(const string& desc) { - return boost::replace_all_copy(desc, "\\;", "\n"); -} - -/** - * Function: AdbParser::descNativeToXml - **/ -string AdbParser::descNativeToXml(const string& desc) { - return boost::replace_all_copy(desc, "\n", "\\;"); -} - -/** - * Function: AdbParser::includeFile - **/ -void AdbParser::includeFile(AdbParser *adbParser, string fileName, - int lineNumber) { - // add this file to the included files map - string filePath; - FILE *probeFile = NULL; + /* Evaluate unions selector fields*/ + for (list::iterator it = _unionSelectorEvalDeffered.begin(); + it != _unionSelectorEvalDeffered.end(); + it++) + { + bool foundSelector = true; + vector path; + AdbInstance* inst = *it; + AdbInstance* curInst = inst; + const string splitVal = inst->getInstanceAttr("union_selector"); + boost::algorithm::split(path, splitVal, boost::is_any_of(string("."))); + for (size_t i = 0; i < path.size(); i++) + { + if (path[i] == "#(parent)" || path[i] == "$(parent)") + { + curInst = curInst->parent; + if (curInst == NULL || i == path.size() - 1) + { + foundSelector = false; + if (rootNodeName == rootNode) + { // give this warning only if this root instantiation + if (allowMultipleExceptions) + cout << "allow multiple"; + raiseException(allowMultipleExceptions, + "Invalid union selector (" + inst->fullName() + + "), must be a leaf field, cannot be a parent of root", + ExceptionHolder::ERROR_EXCEPTION); + } + break; + } + } + else + { + size_t j; + bool inPath = false; + for (j = 0; j < curInst->subItems.size(); j++) + { + if (curInst->subItems[j]->name == path[i]) + { + curInst = curInst->subItems[j]; + inPath = true; + break; + } + } - if (!boost::filesystem::path(fileName).is_relative()) { - probeFile = fopen(fileName.c_str(), "r"); - } + if (j == curInst->subItems.size() && !inPath) + { + foundSelector = false; + if (rootNodeName == rootNode) + { // give this warning only if this root instantiation + raiseException(allowMultipleExceptions, + "Failed to find union selector for union (" + inst->fullName() + + ") Can't find field (" + path[i] + ") under (" + curInst->fullName() + ")", + ExceptionHolder::ERROR_EXCEPTION); + } + break; + } + } + } - if (probeFile) { - fclose(probeFile); - filePath = fileName; - } else { - filePath = adbParser->findFile(fileName); - } - if (filePath.empty()) { - throw AdbException(string() + "Can't find the file: " + fileName); - } + if (foundSelector) + { + inst->unionSelector = curInst; + for (size_t i = 0; i < inst->subItems.size(); i++) + { + // printf("Field %s, isResered=%d\n", inst->subItems[i]->fullName().c_str(), + // inst->subItems[i]->isReserved()); + if (inst->subItems[i]->isReserved()) + { + continue; + } - // Update filename to be only base name with extension to prevent duplications - boost::filesystem::path boostPath(filePath); - fileName = boostPath.filename().string(); + // make sure all union subnodes define "selected_by" attribute + bool found = false; + AttrsMap::iterator selectorValIt = inst->subItems[i]->getInstanceAttrIterator("selected_by", found); + if (!found) + { + raiseException(allowMultipleExceptions, + "In union (" + inst->fullName() + ") the union subnode (" + + inst->subItems[i]->name + ") doesn't define selection value", + ExceptionHolder::ERROR_EXCEPTION); + } - if (!adbParser->_adbCtxt->includedFiles.count(fileName)) { - IncludeFileInfo info = { filePath, adbParser->_fileName, lineNumber }; - adbParser->_adbCtxt->includedFiles[fileName] = info; + // make sure that all union subnodes selector values are defined in the selector field enum + if (selectorValIt->second == "") + { + continue; + } - // parse the included file - AdbParser p(filePath, adbParser->_adbCtxt, adbParser->_addReserved, - adbParser->_progressObj, adbParser->_strict, - "", adbParser->_enforceExtraChecks); - if (!p.load()) { - throw AdbException(p.getError()); - } - } -} + if (!inst->unionSelector->isEnumExists()) + { + string exceptionTxt = "In union (" + inst->fullName() + ") the union selector (" + + inst->unionSelector->fullName() + ") is not an enum"; + raiseException(allowMultipleExceptions, exceptionTxt, ExceptionHolder::ERROR_EXCEPTION); + break; + } -/** - * Function: AdbParser::includeAllFilesInDir - **/ -void AdbParser::includeAllFilesInDir(AdbParser *adbParser, string dirPath, - int lineNumber) { - vector < string > paths; - boost::filesystem::path mainFile(adbParser->_fileName); - boost::algorithm::split(paths, dirPath, boost::is_any_of(string(";"))); - for (StringVector::iterator pathIt = paths.begin(); pathIt != paths.end(); pathIt++) { - //first look in the main file's path - boost::filesystem::path fsPath (mainFile.parent_path().string() + "/" + *pathIt); - if (!boost::filesystem::exists(fsPath)) { - fsPath = boost::filesystem::path(*pathIt); - } + map::iterator it; + map selectorValMap = inst->unionSelector->getEnumMap(); + for (it = selectorValMap.begin(); it != selectorValMap.end(); it++) + { + if (it->first == selectorValIt->second) + { + break; + } + } - if (boost::filesystem::exists(fsPath) - && boost::filesystem::is_directory(fsPath)) { - addIncludePaths(adbParser->_adbCtxt, *pathIt); - boost::filesystem::directory_iterator filesIter(fsPath), dirEnd; - for (; filesIter != dirEnd; ++filesIter) { - if (boost::filesystem::is_regular_file(filesIter->status()) - && filesIter->path().extension() == ".adb") { - try { - includeFile(adbParser, filesIter->path().string(), - lineNumber); - } catch (AdbException &e) { - throw(e); + // if not found in map throw exeption + if (it == selectorValMap.end()) + { + string exceptionTxt = "In union (" + inst->fullName() + ") the union subnode (" + + inst->subItems[i]->name + ") uses a selector value (" + + selectorValIt->second + ") which isn't defined in the selector field (" + + inst->unionSelector->fullName() + ")"; + raiseException(allowMultipleExceptions, exceptionTxt, ExceptionHolder::ERROR_EXCEPTION); } } } } - } -} - -/** - * Function AdbParser::checkSpecialChars - */ -bool AdbParser::checkSpecialChars(string tagName) { - smatch match; - regex allowedCharsExpr("[^\\w\\[\\]]"); // only alphanumeric and square brackets are allowed - if (regex_search(tagName, match, allowedCharsExpr)) { - return false; - } - regex checkArrayExpr("[\\[\\]]"); // this check if containing the array brackets - if (regex_search(tagName, match, checkArrayExpr)) { - regex correctNameForArrayExpr("[_A-Za-z][\\w]*\\[[\\d]+\\]$"); - if (!regex_search(tagName, match, correctNameForArrayExpr)) { // check the name if correct the square brackets (array) - return false; - } - } else { - regex correctNameNoneArrayExpr("[_A-Za-z][\\w]*$"); - if (!regex_search(tagName, match, correctNameNoneArrayExpr)) { // check the name if correct without the square brackets (not array) - return false; - } - } - return true; -} - -bool AdbParser::checkHEXFormat(string addr) { - smatch match; - regex correctHEXExpr("(0x)?[0-9A-Fa-f]+?(.0)?$"); - if (!regex_search(addr, match, correctHEXExpr)) { - return false; - } - return true; -} - -bool AdbParser::checkBigger32(string num) { - std::istringstream iss(num); - std::string token; - std::getline(iss, token, '.'); - if (std::getline(iss, token, '.')) { // the second part of the size after the . max to be 32 0x0.0 - if (addr2int(token) / 8 >= 32) { - return true; - } - } - return false; -} -void AdbParser::startNodesDefElement(const XML_Char **atts, AdbParser *adbParser) -{ - if (adbParser->_adbCtxt->version == "") { - if ((attrCount(atts) == 1) && (attrName(atts, 0) == "version")) { - string adbVersion = attrValue(atts, 0); - if (adbVersion != "1" && adbVersion != "1.0" && adbVersion != "2") { - throw AdbException("Requested Adb Version (%s) is not supported. Supporting only version 1 or 2", adbVersion.c_str()); - } - if (adbVersion == "1.0") { - adbParser->_adbCtxt->version = "1"; - } else { - adbParser->_adbCtxt->version = adbVersion; - } - } else if (attrCount(atts) == 0 ) { - adbParser->_adbCtxt->version = "1"; - } else { - throw AdbException("\"NodesDefinition\" tag can only have \"version\" attribute and only once"); + if (allowMultipleExceptions && ExceptionHolder::getNumberOfExceptions() > 0) + { + fetchAdbExceptionsMap(ExceptionHolder::getAdbExceptionsMap()); + rootItem = NULL; } + return rootItem; } -} - - -void AdbParser::startEnumElement(const XML_Char **atts, AdbParser *adbParser, const int lineNumber) -{ - bool expFound = false; - if (!adbParser->_currentConfig || - !adbParser->_currentConfig->attrs.count("type") || - TAG_ATTR_ENUM.compare(adbParser->_currentConfig->attrs["type"])) { - expFound = raiseException(allowMultipleExceptions, - "\"enum\" tag must be inside relevant \"config\" tag" , - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::ERROR_EXCEPTION); - } - - string tagName = attrValue(atts, "name"); - string value = attrValue(atts, "value"); - if (adbParser->_enforceExtraChecks) { - if (!AdbParser::checkSpecialChars(tagName)) { - expFound = raiseException(allowMultipleExceptions, - "Invalid character in enum name, in enum: \"" + tagName + "\"", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::WARN_EXCEPTION); + catch (AdbException& exp) + { + _lastError = exp.what_s(); + if (allowMultipleExceptions) + { + insertNewException(ExceptionHolder::FATAL_EXCEPTION, _lastError); } + return NULL; } - if (tagName.empty() || value.empty()) { - expFound = raiseException(allowMultipleExceptions, - "Both \"name\" and \"value\" attributes must be specified", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::ERROR_EXCEPTION); - } - if (!expFound) { - adbParser->_currentConfig->enums.insert( pair (tagName, value)); + catch (...) + { + _lastError = "Unknown error occurred"; + return NULL; } } -void AdbParser::startConfigElement(const XML_Char **atts, AdbParser *adbParser, const int lineNumber) +/** + * Function: Adb::getNodeDeps + **/ +vector Adb::getNodeDeps(string nodeName) { - bool expFound = false; - if (adbParser->_currentConfig) { - expFound = raiseException(allowMultipleExceptions, - "config tag can't appear within other config", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::FATAL_EXCEPTION); + NodesMap::iterator it = nodesMap.find(nodeName); + if (it == nodesMap.end()) + { + throw AdbException("Can't find node definition for: " + nodeName); } - adbParser->_currentConfig = new AdbConfig; - for (int i = 0; i < attrCount(atts); i++) { - // First add this config to context - string aName = attrName(atts, i); - string aValue = attrValue(atts, i); - adbParser->_currentConfig->attrs.insert( - pair (aName, aValue)); - - // Parse attrs that affect the parser itself - if (!TAG_ATTR_BIG_ENDIAN.compare(aName)) { - try { - int val = boost::lexical_cast(aValue); - adbParser->_adbCtxt->bigEndianArr = val != 0; - } catch (std::exception&) { - expFound = raiseException(allowMultipleExceptions, - string("Filed to parse the \"") + aName + "\" attribute value: \"" + aValue + "\"", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::ERROR_EXCEPTION); - } - } - - if (!TAG_ATTR_SINGLE_ENTRY_ARR.compare(aName)) { - try { - int val = boost::lexical_cast(aValue); - adbParser->_adbCtxt->singleEntryArrSupp = val != 0; - } catch (std::exception&) { - expFound = raiseException(allowMultipleExceptions, - string("Filed to parse the \"") + aName + "\" attribute value: \"" + aValue + "\"", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::ERROR_EXCEPTION); - } - } - - if (!expFound && !TAG_ATTR_INCLUDE_PATH.compare(aName)) { - vector < string > paths; - boost::algorithm::split(paths, aValue, boost::is_any_of(string(";"))); - adbParser->_adbCtxt->includePaths.insert(adbParser->_adbCtxt->includePaths.end(), - paths.begin(), paths.end()); - - StringVector relatives; - string - projPath = boost::filesystem::path( - adbParser->_adbCtxt->mainFileName).parent_path().string(); - - for (StringVector::iterator it = - adbParser->_adbCtxt->includePaths.begin(); it - != adbParser->_adbCtxt->includePaths.end(); it++) { - if (boost::filesystem::path(*it).is_relative()) { - relatives.push_back(projPath + OS_PATH_SEP + *it); - } - } + AdbNode* node = it->second; + vector deps(1, node->name); - adbParser->_adbCtxt->includePaths.insert( - adbParser->_adbCtxt->includePaths.end(), - relatives.begin(), relatives.end()); + for (size_t i = 0; i < node->fields.size(); i++) + { + if (node->fields[i]->isStruct()) + { + deps.push_back(node->fields[i]->subNode); + vector subDeps = getNodeDeps(node->fields[i]->subNode); + deps.insert(deps.end(), subDeps.begin(), subDeps.end()); } } -} -void AdbParser::startInfoElement(const XML_Char **atts, AdbParser *adbParser) -{ - string docName = attrValue(atts, "source_doc_name"); - string docVer = attrValue(atts, "source_doc_version"); - adbParser->_adbCtxt->srcDocName = docName; - adbParser->_adbCtxt->srcDocVer = docVer; -} - -void AdbParser::startIncludeElement(const XML_Char **atts, AdbParser *adbParser, const int lineNumber) -{ - string includeAttr = attrName(atts, 0); - boost::algorithm::trim(includeAttr); - - bool expFound = false; - if (includeAttr == "file") { - string fname = attrValue(atts, "file"); - boost::algorithm::trim(fname); - if (fname.empty()) { - expFound = raiseException(allowMultipleExceptions, - string() + "File attribute isn't given within " + TAG_INCLUDE + " tag", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::FATAL_EXCEPTION); - } - if (!expFound) - includeFile(adbParser, fname, lineNumber); - } - else if (includeAttr == "dir") { - string includeAllDirPath = attrValue(atts, "dir"); - boost::algorithm::trim(includeAllDirPath); - if (includeAllDirPath.empty()) { - expFound = raiseException(allowMultipleExceptions, - string() + "Directory to include isn't given within " + TAG_INCLUDE + " tag", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::FATAL_EXCEPTION); - } - - if (!expFound) - includeAllFilesInDir(adbParser, includeAllDirPath, lineNumber); - } else { - expFound = raiseException(allowMultipleExceptions, - string() + "Include is called without file or dir attribute.", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::ERROR_EXCEPTION); - } + stable_sort(deps.begin(), deps.end()); + deps.erase(unique(deps.begin(), deps.end()), deps.end()); + return deps; } -void AdbParser::startInstOpAttrReplaceElement(const XML_Char **atts, AdbParser *adbParser, const int lineNumber) +/** + * Function: Adb::createInstance + **/ +vector Adb::createInstance(AdbField* field, + AdbInstance* parent, + map vars, + bool isExprEval, + AdbProgress* progressObj, + int depth, + bool ignoreMissingNodes, + bool allowMultipleExceptions) { - bool expFound = false; - if (!adbParser->_instanceOps) { - expFound = raiseException(allowMultipleExceptions, - "Operation attr_replace must be defined within element only.", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::FATAL_EXCEPTION); + static const regex EXP_PATTERN("\\s*([a-zA-Z0-9_]+)=((\\$\\(.*?\\)|\\S+|$)*)\\s*"); + if (progressObj) + { + progressObj->progress(); } - string path = attrValue(atts, "path"); - if (path.empty()) { - expFound = raiseException(allowMultipleExceptions, - "path attribute is missing in attr_replace operation", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::ERROR_EXCEPTION); + // if array create instance for each item. else - create 1 + vector instList; + for (u_int32_t i = 0; i < field->arrayLen(); i++) + { + AdbInstance* inst = new AdbInstance; + inst->fieldDesc = field; - } + // if field is struct - find field->subNode in nodes map and addto instance not desc - if (!expFound) { - adbParser->_adbCtxt->instAttrs[path] = AttrsMap(); - for (int i = 0; i < attrCount(atts); i++) { - string attrN = attrName(atts, i); - if (attrN == "path") { - continue; + if (field->isStruct()) + { + NodesMap::iterator it = nodesMap.find(field->subNode); + if (it == nodesMap.end()) + { + delete inst; + raiseException(allowMultipleExceptions, + "Can't find the definition for subnode: " + field->subNode + " of field: " + field->name, + ExceptionHolder::ERROR_EXCEPTION); + return instList; + } + else + { + inst->nodeDesc = it->second; } - - string attrV = attrValue(atts, i); - adbParser->_adbCtxt->instAttrs[path][attrN] = attrV; - } - } -} - -void AdbParser::startNodeElement(const XML_Char **atts, AdbParser *adbParser, const int lineNumber) -{ - if (adbParser->_currentNode) { - raiseException(allowMultipleExceptions, - "Nested nodes are not allowed", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::FATAL_EXCEPTION); - } - - string nodeName = attrValue(atts, "name"); - boost::algorithm::trim(nodeName); - string size = attrValue(atts, "size"); - - if (adbParser->_enforceExtraChecks) { - if (!AdbParser::checkSpecialChars(nodeName)) { - raiseException(allowMultipleExceptions, - "Invalid character in node name, in Node: \"" + nodeName + "\"", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::WARN_EXCEPTION); - } - if (AdbParser::checkBigger32(size)) { // the second part of the size after the . max to be 32 0x0.0 - raiseException(allowMultipleExceptions, - "Invalid size format, valid format 0x0.0 not allowed to be more than 0x0.31", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::WARN_EXCEPTION); - } - if (addr2int(size) == 0) { - raiseException(allowMultipleExceptions, - "Node Size is not allowed to be 0, in Node: \"" + nodeName + "\"", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::ERROR_EXCEPTION); } - } - string desc = descXmlToNative(attrValue(atts, "descr")); - - // Check for mandatory attrs - if (nodeName.empty()) { - raiseException(allowMultipleExceptions, - "Missing node name", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::FATAL_EXCEPTION); - - } - if (size.empty()) { - raiseException(allowMultipleExceptions, - "Missing node size", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::FATAL_EXCEPTION); - } - - // Check for duplications - if (adbParser->_adbCtxt->nodesMap.count(nodeName)) { - raiseException(allowMultipleExceptions, - "node \"" + nodeName + "\" is already defined in file: \"" + adbParser->_adbCtxt->nodesMap[nodeName]->fileName + "\" line: " + boost::lexical_cast(adbParser->_adbCtxt->nodesMap[nodeName]->lineNumber), - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::FATAL_EXCEPTION); - } - - adbParser->_currentNode = new AdbNode; - adbParser->_currentNode->name = nodeName; - adbParser->_currentNode->size = addr2int(size); - adbParser->_currentNode->desc = desc; - string unionAttrVal = attrValue(atts, "attr_is_union"); - adbParser->_currentNode->isUnion = !unionAttrVal.empty() - && boost::lexical_cast(unionAttrVal) != 0; - adbParser->_currentNode->fileName = adbParser->_fileName; - adbParser->_currentNode->lineNumber = lineNumber; - - if (adbParser->_strict && adbParser->_currentNode->isUnion - && adbParser->_currentNode->size % 32) { - //throw AdbException("union must be dword aligned"); - } - // Add all node attributes - for (int i = 0; i < attrCount(atts); i++) { - if (attrValue(atts, i) == "") { - continue; + if (inst) + { + inst->name = field->name; + inst->arrIdx = 0; + inst->size = field->eSize(); + inst->parent = parent; } - adbParser->_currentNode->attrs[attrName(atts, i)] = attrValue(atts, i); - } -} -void AdbParser::startFieldElement(const XML_Char **atts, AdbParser *adbParser, const int lineNumber) -{ - bool expFound = false; - if (!adbParser->_currentNode) { - expFound = raiseException(allowMultipleExceptions, - "Field definition outside of node", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::FATAL_EXCEPTION); - } - - if (adbParser->_currentField) { - expFound = raiseException(allowMultipleExceptions, - "Nested fields are not allowed", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::FATAL_EXCEPTION); - } - - string fieldName = attrValue(atts, "name"); - boost::algorithm::trim(fieldName); - string offset = attrValue(atts, "offset"); - string size = attrValue(atts, "size"); - - if (adbParser->_enforceExtraChecks) { - if (addr2int(size) % 32 == 0 && !AdbParser::checkHEXFormat(size)) - { - expFound = raiseException(allowMultipleExceptions, - "Invalid size format", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::WARN_EXCEPTION); - } - if (!AdbParser::checkHEXFormat(offset)) - { - expFound = raiseException(allowMultipleExceptions, - "Invalid offset format", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::WARN_EXCEPTION); - } - if (!AdbParser::checkSpecialChars(fieldName)) - { - expFound = raiseException(allowMultipleExceptions, - "Invalid character in field name, in Field: \"" + fieldName + "\"", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::WARN_EXCEPTION); - } - if (!expFound && adbParser->_currentNode->isUnion && addr2int(offset) != 0) - { - expFound = raiseException(allowMultipleExceptions, - "Offset must be 0x0.0 in Union fields", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::WARN_EXCEPTION); - } - if (AdbParser::checkBigger32(size)) - { // the second part of the size after the . max to be 32 0x0.0 - expFound = raiseException(allowMultipleExceptions, - "Invalid size format, valid format 0x0.0 not allowed to be more than 0x0.31", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::WARN_EXCEPTION); - } - if (AdbParser::checkBigger32(offset)) - { // the second part of the size after the . max to be 32 0x0.0 - expFound = raiseException(allowMultipleExceptions, - "Invalid offset format, valid format 0x0.0 not allowed to be more than 0x0.31", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::WARN_EXCEPTION); - } - if (addr2int(size) == 0) - { - expFound = raiseException(allowMultipleExceptions, - "Field Size is not allowed to be 0, in Field: \"" + fieldName + "\"", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::WARN_EXCEPTION); - } - } - - string desc = descXmlToNative(attrValue(atts, "descr")); - string lowBound = attrValue(atts, "low_bound"); - string highBound = attrValue(atts, "high_bound"); - - if (lowBound != "" && highBound != "") { - int low = atoi(lowBound.c_str()); - int high = 0; - if (highBound != "VARIABLE") { - high = atoi(highBound.c_str()); - } - int isize = addr2int(size); - int entrySize = isize / (high - low + 1); - if (entrySize % 8 != 0 && entrySize > 8) - { - expFound = raiseException(allowMultipleExceptions, - "Invalid size of array entries", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::ERROR_EXCEPTION); - } - if (entrySize < 8 && entrySize != 4 && entrySize != 2 && entrySize - != 1 && ((isize > 32 && highBound != "VARIABLE") - || highBound == "VARIABLE")) - { - expFound = raiseException(allowMultipleExceptions, - "Array with entry size 3, 5, 6 or 7 bits and array size is larger than 32bit", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::ERROR_EXCEPTION); - } - } - - string subNode = attrValue(atts, "subnode"); - - // Check for mandatory attrs - if (fieldName.empty()){ - expFound = raiseException(allowMultipleExceptions, - "Missing field name", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::FATAL_EXCEPTION); - } - - if (size.empty()) { - expFound = raiseException(allowMultipleExceptions, - "Missing field size", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::FATAL_EXCEPTION); - } - - // Create new fields - adbParser->_currentField = new AdbField; - - // Fill the new fields - adbParser->_currentField->name = fieldName; - adbParser->_currentField->desc = desc; - adbParser->_currentField->size = addr2int(size); - if (adbParser->_adbCtxt->singleEntryArrSupp) { - int low = -1; - int high = -1; - if (!lowBound.empty()) { - low = atoi(lowBound.c_str()); - } - if (!highBound.empty()) { - high = atoi(highBound.c_str()); - } - adbParser->_currentField->definedAsArr = (low == high) - && (low >= 0); - } else { - adbParser->_currentField->definedAsArr = false; - } - - adbParser->_currentField->lowBound = lowBound.empty() ? 0 - : boost::lexical_cast(lowBound); - if (highBound == "VARIABLE") { - adbParser->_currentField->highBound = 0; - adbParser->_currentField->unlimitedArr = true; - } else { - adbParser->_currentField->highBound = highBound.empty() ? 0 - : boost::lexical_cast(highBound); - } - adbParser->_currentField->subNode = subNode; - - // Check array element size - if (adbParser->_currentField->isArray() - && !adbParser->_currentField->isUnlimitedArr() - && adbParser->_currentField->size - % (adbParser->_currentField->arrayLen())) { - char exceptionTxt[1000]; - sprintf(exceptionTxt, - "In field \"%s\" invalid array element size\"%0.2f\" in file: \"%s\" line: %d", - fieldName.c_str(), - ((double) adbParser->_currentField->size - / (adbParser->_currentField->arrayLen())), - adbParser->_fileName.c_str(), lineNumber); - - expFound = raiseException(allowMultipleExceptions, - exceptionTxt, - "", - ExceptionHolder::FATAL_EXCEPTION); - } - if (offset.empty()) { - if (adbParser->_currentNode->fields.empty()) { - adbParser->_currentField->offset = 0; - } else { - AdbField *lastField = adbParser->_currentNode->fields.back(); - adbParser->_currentField->offset = lastField->offset - + lastField->size; - } - } else { - adbParser->_currentField->offset = addr2int(offset); - } - - // Very tricky but works well for big endian arrays support - on endElement we will fix the address again - if (!expFound && adbParser->_adbCtxt->bigEndianArr) { - u_int32_t offset = adbParser->_currentField->offset; - u_int32_t size = adbParser->_currentField->eSize(); - - adbParser->_currentField->offset = ((offset >> 5) << 5) - + ((MIN(32, adbParser->_currentNode->size) - ((offset - + size) % 32)) % 32); - } - - if (!expFound && adbParser->_strict - && adbParser->_currentNode->isUnion - && adbParser->_currentField->isLeaf()) - { - expFound = raiseException(allowMultipleExceptions, - "Fields are not allowed in unions (only subnodes)", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::FATAL_EXCEPTION); - } - - if (!expFound && adbParser->_strict - && adbParser->_currentNode->isUnion - && adbParser->_currentField->size % 32) - { - expFound = raiseException(allowMultipleExceptions, - "Union is allowed to contains only dword aligned subnodes", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::FATAL_EXCEPTION); - } - - if (!expFound && adbParser->_currentNode->isUnion - && adbParser->_currentField->size - > adbParser->_currentNode->size) - { - expFound = raiseException(allowMultipleExceptions, - "Field size is greater than the parent node size", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::FATAL_EXCEPTION); - } - - if (!expFound && adbParser->_strict - && !adbParser->_currentField->isArray() - && adbParser->_currentField->isLeaf() - && adbParser->_currentField->size > 32) - { - expFound = raiseException(allowMultipleExceptions, - "Leaf fields can't be > 32 bits", - ", in file: \"" + adbParser->_fileName + "\" line: " + boost::lexical_cast(lineNumber), - ExceptionHolder::FATAL_EXCEPTION); - } - // Add all field attributes - if (!expFound) { - for (int i = 0; i < attrCount(atts); i++) { - adbParser->_currentField->attrs[attrName(atts, i)] = attrValue( - atts, i); + if (field->offset == 0xffffffff) + { + if (parent->subItems.size() > 0) + { + field->offset = parent->subItems.back()->offset; + } + else + { + field->offset = parent->offset; + } } - } -} - -/** - * Function: AdbParser::startElement - **/ -void AdbParser::startElement(void *_adbParser, const XML_Char *name, const XML_Char **atts) -{ - AdbParser *adbParser = static_cast (_adbParser); - - int lineNumber = XML_GetCurrentLineNumber(adbParser->_xmlParser); - adbParser->_currentTagValue = ""; - - if (TAG_NODES_DEFINITION == name) { - startNodesDefElement(atts, adbParser); - } - else if (TAG_ENUM == name) { - startEnumElement(atts, adbParser, lineNumber); - } - else if (TAG_CONFIG == name) { - startConfigElement(atts, adbParser, lineNumber); - } - else if (TAG_INFO == name) { - startInfoElement(atts, adbParser); - } - else if (TAG_INCLUDE == name) { - startIncludeElement(atts, adbParser, lineNumber); - } - else if (TAG_INSTANCE_OPS == name) { - adbParser->_instanceOps = true; - } - else if (TAG_INSTANCE_OP_ATTR_REPLACE == name) { - startInstOpAttrReplaceElement(atts, adbParser, lineNumber); - } - else if (TAG_NODE == name) { - startNodeElement(atts, adbParser, lineNumber); - } - else if (TAG_FIELD == name) { - startFieldElement(atts, adbParser, lineNumber); - } - else if (TAG_RCS_HEADERS == name) { - // DO NOTHING - } else { - string exceptionTxt = "Unsupported tag: " + string(name); - if (allowMultipleExceptions) + inst->offset = calcArrOffset(field, parent, i); + if (field->isArray()) { - exceptionTxt = exceptionTxt + ", in file: \"" + adbParser->_fileName + "\" line: " - + boost::lexical_cast(lineNumber); - ExceptionHolder::insertNewException(ExceptionHolder::FATAL_EXCEPTION, exceptionTxt); - return; - } else { - throw AdbException(exceptionTxt); + inst->name += "[" + boost::lexical_cast(i + field->lowBound) + "]"; + inst->arrIdx = i; } - } -} + // printf("field: %s, offset: %s (%d)\n", inst->name.c_str(),formatAddr(inst->offset, inst->size).c_str(), + // inst->offset); -/** - * Function: AdbParser::addReserved - **/ -void AdbParser::addReserved(vector &reserveds, u_int32_t offset, - u_int32_t size) { - u_int32_t numOfDwords = (dword(offset + size - 1) - dword(offset)) / 4 + 1; - - //printf("==> %s\n", formatAddr(offset, size).c_str()); - //printf("numOfDwords = %d\n", numOfDwords); + if (isExprEval) + { + try + { + // First add the special variables + char tmpStr[32]; + vars["NAME"] = inst->name; + vars["ARR_IDX"] = boost::lexical_cast(inst->arrIdx); + sprintf(tmpStr, "[%d:%d]", inst->offset % 32 + inst->size - 1, inst->offset % 32); + vars["BN"] = tmpStr; + vars["parent"] = "#(parent)"; // special internal name - if (numOfDwords == 1 || ((offset % 32) == 0 && ((offset + size) % 32) == 0)) { - AdbField *f1 = new AdbField; - f1->name = "reserved"; - f1->offset = offset; - f1->isReserved = true; - f1->size = size; + // Get variables attribute value + AttrsMap::iterator it = field->attrs.find("variables"); + string varStr = (it == field->attrs.end() ? string() : it->second); + boost::algorithm::trim(varStr); - reserveds.push_back(f1); - //printf("case1: reserved0: %s\n", formatAddr(f1->offset, f1->size).c_str()); - } else if (numOfDwords == 2) { - AdbField *f1 = new AdbField; - f1->name = "reserved"; - f1->offset = offset; - f1->isReserved = true; - f1->size = 32 - startBit(offset); + // build var_name->value map from varAttrStr + AttrsMap curVars; + if (!varStr.empty()) + { + match_results what; + string::const_iterator start = varStr.begin(); + string::const_iterator end = varStr.end(); - AdbField *f2 = new AdbField; - f2->name = "reserved"; - f2->offset = dword(offset + 32) * 8; - f2->isReserved = true; - f2->size = size - f1->size; - reserveds.push_back(f1); - reserveds.push_back(f2); - /*printf("case2: reserved0: %s, reserved1: %s\n", - formatAddr(f1->offset, f1->size).c_str(), - formatAddr(f2->offset, f2->size).c_str());*/ - } else { - AdbField *f1 = new AdbField; - f1->name = "reserved"; - f1->offset = offset; - f1->isReserved = true; - f1->size = 32 - startBit(offset); + while (regex_search(start, end, what, EXP_PATTERN)) + { + string var(what[1].first, what[1].second); + string exp(what[2].first, what[2].second); +#if 0 + printf("%s) %s <=> %s\n", inst->fullName().c_str(), var.c_str(), exp.c_str()); + printf("Variable Attr: %s\n", inst->field->attrs["variables"].c_str()); +#endif + start = what[0].second; + curVars[var] = exp; + } - AdbField *f2 = new AdbField; - f2->name = "reserved"; - f2->offset = dword(offset + 32) * 8; - f2->isReserved = true; - f2->size = (numOfDwords - 2) * 32; + for (AttrsMap::iterator it = curVars.begin(); it != curVars.end(); it++) + { + vars[it->first] = it->second; + } - if (!((offset + size) % 32)) { - f2->size = (numOfDwords - 1) * 32; - reserveds.push_back(f1); - reserveds.push_back(f2); - /*printf("case3.1: reserved0: %s, reserved1: %s\n", - formatAddr(f1->offset, f1->size).c_str(), - formatAddr(f2->offset, f2->size).c_str());*/ - return; - } else { - if (!(f1->size % 32)) { - f1->size = (numOfDwords - 1) * 32; - f2->size = size - f1->size; - f2->offset = f1->offset + f1->size; - reserveds.push_back(f1); - reserveds.push_back(f2); - /*printf("case3.2: reserved0: %s, reserved1: %s\n", - formatAddr(f1->offset, f1->size).c_str(), - formatAddr(f2->offset, f2->size).c_str());*/ - return; - } else { - f2->size = (numOfDwords - 2) * 32; - AdbField *f3 = new AdbField; - f3->name = "reserved"; - f3->offset = f2->offset + f2->size; - f3->isReserved = true; - f3->size = size - f1->size - f2->size; - reserveds.push_back(f1); - reserveds.push_back(f2); - reserveds.push_back(f3); - /*printf("case3.3: reserved0: %s, reserved1: %s, reserved2: %s\n", - formatAddr(f1->offset, f1->size).c_str(), - formatAddr(f2->offset, f2->size).c_str(), - formatAddr(f3->offset, f3->size).c_str());*/ - return; - } - } - } -} + // evaluate the variables themselves + for (AttrsMap::iterator it = curVars.begin(); it != curVars.end(); it++) + { + vars[it->first] = evalExpr(it->second, &vars); + } + } -/** - * Function: AdbParser::endElement - **/ -void AdbParser::endElement(void *_adbParser, const XML_Char *name) { - AdbParser *adbParser = static_cast (_adbParser); - int lineNumber = XML_GetCurrentLineNumber(adbParser->_xmlParser); - /* - * Config - * ---- - */ - if (TAG_CONFIG == name) { - adbParser->_adbCtxt->configs.push_back(adbParser->_currentConfig); - adbParser->_currentConfig = NULL; - } - /* - * Instance Operations - * ---- - */ - else if (TAG_INSTANCE_OPS == name) { - adbParser->_instanceOps = false; - } - /* - * Node - * ---- - */ - else if (TAG_NODE == name) { - // Sort the fields by offset - if (!adbParser->_currentNode->isUnion) { - stable_sort(adbParser->_currentNode->fields.begin(), - adbParser->_currentNode->fields.end(), - compareFieldsPtr ); - } - - // Check overlapping - vector reserveds; - AdbField prevFieldDummy; - prevFieldDummy.offset = 0; - prevFieldDummy.size = 0; - AdbField *prevField = &prevFieldDummy; - if (!adbParser->_currentNode->isUnion) { - for (size_t i = 0; i < adbParser->_currentNode->fields.size(); i++) { - AdbField *field = adbParser->_currentNode->fields[i]; - long int delta = (long) field->offset - - (long) (prevField->offset + prevField->size); - - if (delta < 0) { // Overlapping - string exceptionTxt = "Field: " + field->name + " " - + formatAddr(field->offset, field->size) - + " overlaps with Field: " + prevField->name + " " - + formatAddr(prevField->offset, prevField->size); - if (allowMultipleExceptions) { - exceptionTxt = exceptionTxt + ", in file: \"" - + adbParser->_fileName + "\" line: " - + boost::lexical_cast(lineNumber); - ExceptionHolder::insertNewException(ExceptionHolder::ERROR_EXCEPTION, exceptionTxt); - } else { - throw AdbException(exceptionTxt); +#if 0 + printf("Variables of: %s\n", inst->fullName().c_str()); + for (AttrsMap::iterator it = vars.begin(); it != vars.end(); it++) { + printf("\tVars: %s - %s\n", it->first.c_str(), it->second.c_str()); + } +#endif + + // evaluate other attrs + for (AttrsMap::iterator it = field->attrs.begin(); it != field->attrs.end(); it++) + { + if (it->first == "variables") + { + continue; } + + inst->setInstanceAttr(it->first, evalExpr(it->second, &vars)); } - if (delta > 0 && adbParser->_addReserved) { // Need reserved - addReserved(reserveds, prevField->offset + prevField->size, - delta); + inst->setVarsMap(vars); + } + catch (AdbException& exp) + { + string exceptionTxt = + "Failed to evaluate expression for field (" + inst->fullName() + "): " + exp.what_s(); + if (allowMultipleExceptions) + { + insertNewException(ExceptionHolder::ERROR_EXCEPTION, exceptionTxt); + } + else + { + throw AdbException(exceptionTxt); } - - prevField = field; } } + // else { + // inst->copyAllInstanceAttr(field->attrs); + // } + + /* if union and uses selecotr field add it to _unionSelectorEvalDeffered in order to defferred evaluation */ + bool found = false; + inst->getInstanceAttrIterator("union_selector", found); + if (inst->isUnion() && found) + { + _unionSelectorEvalDeffered.push_back(inst); + } - // Add reserved filler at end of union/node - // special case if node is empty, we need to add reserved anyway - if (adbParser->_addReserved /*|| adbParser->_currentNode->fields.empty()*/) { - if (adbParser->_currentNode->isUnion) { - addReserved(reserveds, 0, adbParser->_currentNode->size); - } else { - long delta = (long) adbParser->_currentNode->size - - (long) (prevField->offset + prevField->size); - /*if (adbParser->_currentNode->name == "miss_machine_slice_info") - { - printf("Adding filler\n"); - printf("node offs: %s, prevField: %s\n", formatAddr(0, adbParser->_currentNode->size).c_str(), - formatAddr(prevField->offset, prevField->size).c_str()); - printf("delta: %ld\n", delta); - }*/ + checkInstanceOffsetValidity(inst, parent, allowMultipleExceptions); - if (delta > 0) { - addReserved(reserveds, prevField->offset + prevField->size, - delta); - } + if (field->isStruct() && !inst->nodeDesc->fields.empty() && (depth == -1 || depth > 0)) + { + if (inst->nodeDesc->inLayout) + { + delete inst; + raiseException(false, + "Cyclic definition of nodes, node: " + field->name + " was already added to the layout", + ExceptionHolder::ERROR_EXCEPTION); } + inst->nodeDesc->inLayout = true; - adbParser->_currentNode->fields.insert( - adbParser->_currentNode->fields.end(), reserveds.begin(), - reserveds.end()); - } + // validation 2 + if (inst->size != inst->nodeDesc->size) + { + inst->nodeDesc->size = inst->size; + /*throw AdbException("Node +(" + inst->name + ") size (" + boost::lexical_cast(inst->size)) + + " isn't the same as its instance (" + inst->nodeDesc->name + + ") (" + boost::lexical_cast(inst->nodeDesc->size) + ")";*/ + } - // Re-fix fields offset - if (adbParser->_adbCtxt->bigEndianArr) { - for (size_t i = 0; i < adbParser->_currentNode->fields.size(); i++) { - u_int32_t offset = adbParser->_currentNode->fields[i]->offset; - u_int32_t size = adbParser->_currentNode->fields[i]->eSize(); + vector::iterator it; + for (it = inst->nodeDesc->fields.begin(); it != inst->nodeDesc->fields.end(); it++) + { + map varsCopy = vars; + vector subItems = + createInstance(*it, inst, vars, isExprEval, progressObj, depth == -1 ? -1 : depth - 1, + ignoreMissingNodes, allowMultipleExceptions); + if (subItems.size() > 0) + { + inst->subItems.insert(inst->subItems.end(), subItems.begin(), subItems.end()); + if (subItems[0]->maxLeafSize > inst->maxLeafSize) + { + inst->maxLeafSize = subItems[0]->maxLeafSize; + } + } + } + inst->nodeDesc->inLayout = false; - adbParser->_currentNode->fields[i]->offset = ((offset >> 5) - << 5) + ((MIN(32, adbParser->_currentNode->size) - - ((offset + size) % 32)) % 32); + if (_checkDsAlign && inst->maxLeafSize != 0 && inst->size % inst->maxLeafSize != 0) + { + raiseException(allowMultipleExceptions, + "Node: " + inst->nodeDesc->name + " size(" + boost::lexical_cast(inst->size) + + ") is not aligned with largest leaf(" + + boost::lexical_cast(inst->maxLeafSize) + ")", + ExceptionHolder::ERROR_EXCEPTION); + } + + if (!inst->isUnion() && inst->subItems.size() > 0) + { + stable_sort(inst->subItems.begin(), inst->subItems.end(), compareFieldsPtr); + + for (size_t j = 0; j < inst->subItems.size() - 1; j++) + { + // printf("field: %s, offset: %s\n", + // inst->subItems[j+1]->name.c_str(),formatAddr(inst->subItems[j+1]->offset, + // inst->subItems[j+1]->size).c_str()); printf("field: %s, offset: %s\n", + // inst->subItems[j]->name.c_str(),formatAddr(inst->subItems[j]->offset, + // inst->subItems[j]->size).c_str()); + if (inst->subItems[j + 1]->offset < inst->subItems[j]->offset + inst->subItems[j]->size) + { + string exceptionTxt = + "Field (" + inst->subItems[j + 1]->name + ") (" + + formatAddr(inst->subItems[j + 1]->offset, inst->subItems[j + 1]->size).c_str() + + ") overlaps with (" + inst->subItems[j]->name + ") (" + + formatAddr(inst->subItems[j]->offset, inst->subItems[j]->size).c_str() + ")"; + raiseException(allowMultipleExceptions, exceptionTxt, ExceptionHolder::ERROR_EXCEPTION); + } + } } } - // Resort the fields by offset (since we changed offset to workaround big endian issues and added reserved) - if (!adbParser->_currentNode->isUnion) { - stable_sort(adbParser->_currentNode->fields.begin(), - adbParser->_currentNode->fields.end(), - compareFieldsPtr ); + u_int32_t esize = inst->fieldDesc->size; + if ((field->isLeaf() || field->subNode == "uint64") && (esize == 16 || esize == 32 || esize == 64)) // A leaf + { + inst->maxLeafSize = esize; } + instList.push_back(inst); + } - // Add this node to AdbCtxt - adbParser->_adbCtxt->nodesMap.insert( - pair (adbParser->_currentNode->name, - adbParser->_currentNode)); - adbParser->_currentNode = 0; + stable_sort(instList.begin(), instList.end(), compareFieldsPtr); + return instList; +} - // Call progress_func callback - if (adbParser->_progressObj && !(adbParser->_progressCnt++ - % PROGRESS_NODE_CNT)) { - adbParser->_progressObj->progress(); // TODO - call with real parameters +/** + * Function: Adb::checkInstanceOffsetValidity + **/ +void Adb::checkInstanceOffsetValidity(AdbInstance* inst, AdbInstance* parent, bool allowMultipleExceptions) +{ + if (inst->offset + inst->size > parent->offset + parent->size) + { + string exceptionTxt = "Field (" + inst->name + ") " + formatAddr(inst->offset, inst->size) + + " crosses its parent node (" + parent->name + ") " + + formatAddr(parent->offset, parent->size) + " boundaries"; + if (allowMultipleExceptions) + { + insertNewException(ExceptionHolder::ERROR_EXCEPTION, exceptionTxt); + } + else + { + throw AdbException(exceptionTxt); } } - /* - * Field - * ---- - */ - else if (TAG_FIELD == name) { - // Add this field to current node - if (adbParser->_currentNode->name == "root") { - adbParser->_adbCtxt->rootNode = adbParser->_currentField->subNode; +} + +/** + * Function: Adb::calcArrOffset + **/ +u_int32_t Adb::calcArrOffset(AdbField* fieldDesc, AdbInstance* parent, u_int32_t arrIdx) +{ + int offs; + + if (fieldDesc->eSize() >= 32) + { + // Make sure this is dword aligned + if (fieldDesc->eSize() % 32 || parent->offset % 32 || fieldDesc->offset % 32) + { + throw AdbException("Field " + fieldDesc->name + " isn't dword aligned"); } + offs = parent->offset + fieldDesc->offset + fieldDesc->eSize() * arrIdx; + } + else + { + if (bigEndianArr) + { + offs = (int)parent->offset + (int)fieldDesc->offset - (int)fieldDesc->eSize() * arrIdx; - // Check conditions - bool cond = true; - AttrsMap::iterator it; - it = adbParser->_currentField->attrs.find("inst_ifdef"); - if (it != adbParser->_currentField->attrs.end()) { - bool found = false; - for (size_t i = 0; i < adbParser->_adbCtxt->configs.size(); i++) { - AttrsMap::iterator it_def = - adbParser->_adbCtxt->configs[i]->attrs.find("define"); - if (it_def != adbParser->_adbCtxt->configs[i]->attrs.end()) { - vector < string > defVal; - boost::algorithm::split(defVal, it_def->second, - boost::is_any_of(string("="))); + int dwordDelta = abs((dword(parent->offset + fieldDesc->offset) - dword(offs))) / 4; - if (defVal[0] == it->second) { - found = true; - break; - } - } + if (dwordDelta) + { + offs += 32 * 2 * dwordDelta; } - cond = found; } + else + { + offs = parent->offset + fieldDesc->offset + fieldDesc->eSize() * arrIdx; + } + } - // - it = adbParser->_currentField->attrs.find("inst_if"); - if (cond && it != adbParser->_currentField->attrs.end()) { - // Prepare vars - map < string, string > vars; - for (size_t i = 0; i < adbParser->_adbCtxt->configs.size(); i++) { - AttrsMap::iterator it_def = - adbParser->_adbCtxt->configs[i]->attrs.find("define"); - if (it_def != adbParser->_adbCtxt->configs[i]->attrs.end()) { - vector < string > defVal; - boost::algorithm::split(defVal, it_def->second, - boost::is_any_of(string("="))); + return offs; +} - if (defVal.size() == 1) { - vars[defVal[0]] = "0"; - } else { - vars[defVal[0]] = defVal[1]; - } +/** + * Function: Adb::evalExpr + **/ +string Adb::evalExpr(string expr, AttrsMap* vars) +{ + if (expr.find('$') == string::npos) + { + return expr; + } + + smatch what, what2; + regex singleExpr("^([^\\$]*)(\\$\\(([^)]+)\\))(.*)$"); + while (regex_search(expr, what, singleExpr)) + { + string vname = what[3].str(); + string vvalue; + regex singleVar("^[a-zA-Z_][a-zA-Z0-9_]*$"); + + // Need to change to array-like initialization when we'll move to c++11 + vector specialVars; + specialVars.push_back("NAME"); + specialVars.push_back("ARR_IDX"); + specialVars.push_back("BN"); + specialVars.push_back("parent"); + + if (regex_search(vname, what2, singleVar)) + { + if (find(specialVars.begin(), specialVars.end(), vname) == specialVars.end()) + { + return expr; + } + else + { + AttrsMap::iterator it = vars->find(vname); + if (it == vars->end()) + { + throw AdbException("Can't find the variable: " + vname); } + vvalue = it->second; } - - AdbExpr adbExpr; - char *expOrg = new char[it->second.size() + 1]; - char *exp = expOrg; - if (!exp) { - throw AdbException("Memory allocation error"); + } + else + { + string vnameCopy = vname; + regex singleVarInExpr("[a-zA-Z_][a-zA-Z0-9_]*"); + smatch matches; + while (regex_search(vnameCopy, matches, singleVarInExpr)) + { + if (find(specialVars.begin(), specialVars.end(), matches[0]) == specialVars.end()) + { + return expr; + } + vnameCopy = matches.suffix(); } - strcpy(exp, it->second.c_str()); + char exp[vname.size() + 1]; + char* expPtr = exp; + strcpy(exp, vname.c_str()); u_int64_t res; - adbExpr.setVars(&vars); - int status = adbExpr.expr(&exp, &res); - delete[] expOrg; + _adbExpr.setVars(vars); + int status = _adbExpr.expr(&expPtr, &res); string statusStr; - if (status < 0) { - string exceptionTxt = string("Error evaluating expression \"") - + it->second.c_str() + "\" : " + AdbExpr::statusStr( - status); - if (allowMultipleExceptions) { - exceptionTxt = exceptionTxt + ", in file: \"" - + adbParser->_fileName + "\" line: " - + boost::lexical_cast(lineNumber); - ExceptionHolder::insertNewException(ExceptionHolder::ERROR_EXCEPTION, exceptionTxt); - } else { - throw AdbException(exceptionTxt); - } - } - cond = !!res; - } + // printf("-D- Eval expr \"%s\" = %ul.\n", vname.ascii(), (u_int32_t)res); - if (cond) { - // Name should be unique - if (adbParser->_strict) { - for (size_t i = 0; i < adbParser->_currentNode->fields.size(); i++) { - if (!adbParser->_currentNode->fields[i]->name.compare( - adbParser->_currentField->name)) { - string exceptionTxt = "The field \"" - + adbParser->_currentField->name - + "\" isn't unique in node"; - if (allowMultipleExceptions) { - exceptionTxt = exceptionTxt + ", in file: \"" - + adbParser->_fileName + "\" line: " - + boost::lexical_cast(lineNumber); - ExceptionHolder::insertNewException(ExceptionHolder::ERROR_EXCEPTION, exceptionTxt); - } else { - throw AdbException(exceptionTxt); - } - } + if (status < 0) + { + switch (status) + { + case Expr::ERR_RPAR_EXP: + statusStr = "Right parentheses expected"; + break; + + case Expr::ERR_VALUE_EXP: + statusStr = "Value expected"; + break; + + case Expr::ERR_BIN_EXP: + statusStr = "Binary operation expected "; + break; + + case Expr::ERR_DIV_ZERO: + statusStr = "Divide zero attempt"; + break; + + case Expr::ERR_BAD_NUMBER: + statusStr = "Bad constant syntax"; + break; + + case Expr::ERR_BAD_NAME: + statusStr = "Variable Name not resolved"; + break; + + default: + statusStr = "Unknown error"; } - } - it = adbParser->_currentField->attrs.find("condition"); - if (it != adbParser->_currentField->attrs.end()) { - adbParser->_currentField->condition = string(it->second); + + throw AdbException("Error evaluating expression " + expr + " : " + statusStr); } - adbParser->_currentNode->fields.push_back(adbParser->_currentField); - } else { - adbParser->_currentNode->condFields.push_back( - adbParser->_currentField); + vvalue = boost::lexical_cast(res); } - adbParser->_currentField = 0; + + /*printf("what[0] - %s, what[1] - %s, what[2] - %s, what[3] - %s, what[4] - %s\n", + what[0].str().c_str(), what[1].str().c_str(), + what[2].str().c_str(), what[3].str().c_str(), + what[4].str().c_str());*/ + + expr = what[1].str() + vvalue + what[4].str(); } + + return expr; } /** - * Function: AdbParser::raiseException + * Function: Adb::checkInstSizeConsistency **/ -bool AdbParser::raiseException(bool allowMultipleExceptions, string exceptionTxt, - string addedMsg, const string expType) +bool Adb::checkInstSizeConsistency(bool allowMultipleExceptions) { - if (allowMultipleExceptions) { - ExceptionHolder::insertNewException(expType, exceptionTxt + addedMsg); - return false; - } else { - throw AdbException(exceptionTxt); + bool status = true; + for (NodesMap::iterator it = nodesMap.begin(); it != nodesMap.end(); it++) + { + for (size_t i = 0; i < it->second->fields.size(); i++) + { + if (it->second->fields[i]->isStruct()) + { + NodesMap::iterator iter = nodesMap.find(it->second->fields[i]->subNode); + if (iter == nodesMap.end()) + { + continue; + /* + _lastError = "Can't find definition of subnode \"" + it->second->fields[i]->subNode + + "\" instantiated from node \"" + it->first + "\""; + return false; + */ + } + AdbNode* node = nodesMap[it->second->fields[i]->subNode]; + if (node->size != it->second->fields[i]->size / it->second->fields[i]->arrayLen()) + { + char tmp[256]; + sprintf(tmp, "Node (%s) size 0x%x.%d is not consistent with the instance (%s->%s) size 0x%x.%d", + node->name.c_str(), (node->size >> 5) << 2, node->size % 32, it->second->name.c_str(), + it->second->fields[i]->name.c_str(), (it->second->fields[i]->size >> 5) << 2, + it->second->fields[i]->size % 32); + _lastError = tmp; + if (allowMultipleExceptions) + { + status = false; + insertNewException(ExceptionHolder::ERROR_EXCEPTION, tmp); + } + else + { + return false; + } + } + } + } } -} + return status; +} -#if 0 -void progress(int i, int t) +/** + * Function: Adb::getLastError + **/ +string Adb::getLastError() { - printf("."); - fflush(stdout); + return _lastError; } -int main() +/** + * Function: Adb::print + **/ +void Adb::print(int indent) { -#define ADB_FILE "path_to.adb" - Adb adb; - if (!adb.load(ADB_FILE, true /*, progress*/)) { - printf("Failed : %s\n", adb.getLastError().c_str()); - return 1; - } - - try - { - //adb.print(0); - cout << "Root node: " << adb.rootNode << endl; - AdbInstance *root = adb.createLayout(adb.rootNode, false, NULL); - AdbInstance *child = root->getChildByPath("packetA.x"); - if (!child) { - printf("can't find packetA.x\n"); - return 1; - } - - printf("Child name: %s, addr: %s\n", child->name.c_str(), formatAddr(child->offset, child->size).c_str()); - - string n1, n2; - u_int64_t v1, v2; - child->intToEnum(0, n1); - child->intToEnum(1, n2); - child->enumToInt("SET", v1); - child->enumToInt("GET", v2); + cout << indentString(indent) << "Include paths: " << endl; + for (size_t i = 0; i < includePaths.size(); i++) + cout << indentString(indent + 1) << includePaths[i] << endl; - printf("enumName(0) = %s, enumName(1) = %s, value(SET) = %d, value(GET) = %d", n1.c_str(), n2.c_str(), v1, v2); - //root->print(); - } - catch (AdbException &exp) - { - printf("-E- %s\n", exp.what()); - return 1; - } + cout << indentString(indent) << "Is Big Endian Arrays: " << bigEndianArr << endl; + cout << "-------------------------------------" << endl; + cout << indentString(indent) << "Configs: " << endl; + for (size_t i = 0; i < configs.size(); i++) + configs[i]->print(indent + 1); - cout << endl; - //adb.print(0); - return 0; + cout << "-------------------------------------" << endl; + cout << indentString(indent) << "Nodes" << endl; + NodesMap::iterator iter; + for (iter = nodesMap.begin(); iter != nodesMap.end(); iter++) + iter->second->print(indent + 1); } -#endif diff -Nru mstflint-4.17.0+1/adb_parser/adb_parser.h mstflint-4.21.0+1/adb_parser/adb_parser.h --- mstflint-4.17.0+1/adb_parser/adb_parser.h 2021-06-29 08:49:47.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_parser.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,25 +1,26 @@ -/* +/* * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. - * + * Copyright (c) 2012-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: - * + * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: - * + * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. - * + * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * 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 @@ -28,12 +29,21 @@ * 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. - * */ +/*************************** Adb ***************************/ +#ifndef ADB_ADB_H +#define ADB_ADB_H + +class LogFile; -#ifndef ADB_PARSER_H -#define ADB_PARSER_H +#include +#include +#include +#include +#include +#include "adb_expr.h" +#include "adb_xmlCreator.h" #include #include @@ -42,6 +52,47 @@ #include #include #include "adb_expr.h" +#include "adb_node.h" +#include "adb_field.h" +#include "adb_exceptionHolder.h" +#include "adb_logfile.h" +#include "adb_config.h" +#include "adb_progress.h" +#include "adb_instance.h" +#include +#include +#include +#include +#include +#include +#include "adb_xmlCreator.h" + +#ifdef __WIN__ +#include +#define OS_PATH_SEP "\\" +#else +#define OS_PATH_SEP "/" +#endif + +#ifdef __linux__ +#define LC_ALL_HINT " using `export LC_ALL=C`" +#else +#define LC_ALL_HINT "" +#endif + +#define PROGRESS_NODE_CNT 100 // each 100 parsed node call progress callback + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#define CHECK_RUNTIME_ERROR(e) \ + ((strstr(e.what(), "locale::facet::_S_create_c_locale") != NULL) ? \ + string("Encoding error, please set locale encoding to C") + LC_ALL_HINT + "." : \ + string("runtime_error: ") + e.what()) using namespace std; @@ -52,198 +103,16 @@ #define PRINTF_FORMAT printf #endif -/*************************** AdbException ***************************/ -class AdbException: public std::exception { -public: - // Methods - AdbException(); - AdbException(const char *msg, ...) __attribute__((format(__printf__, 2, 3))); - AdbException(string msg); - virtual ~AdbException() throw (); - virtual const char* what() const throw (); - virtual string what_s() const; - -private: - string _msg; -}; - -/*************************** LogFile ***************************/ -class LogFile { -public: - LogFile(); - void init(string logFileName, bool allowMultipleExceptions); - ~LogFile(); - void appendLogFile(string adbFileName); -private: - FILE *_logFile; -}; - -/*************************** AdbProgress ***************************/ -class AdbProgress { -public: - virtual ~AdbProgress() { - } - ; - virtual void progress(int prog = 0, int total = 0) { - (void) prog; - (void) total; - } - ; -}; - -/*************************** AdbNode ***************************/ +class AdbInstance; class AdbField; -typedef map AttrsMap; -typedef vector FieldsList; -class AdbNode { -public: - // Methods - AdbNode(); - ~AdbNode(); - string toXml(const string& addPrefix); - // FOR DEBUG - void print(int indent = 0); - -public: - // Members - string name; - u_int32_t size; // in bits - bool isUnion; - string desc; - FieldsList fields; - FieldsList condFields; // Field that weren't instantiated due to not satisfied condition - AttrsMap attrs; - - // defined in - string fileName; - int lineNumber; - - // FOR USER USAGE - void *userData; -}; - -/*************************** AdbField ***************************/ -class AdbField { -public: - // Methods - AdbField(); - ~AdbField(); - bool isLeaf(); - bool isStruct(); - bool isArray(); - u_int32_t arrayLen(); - bool isUnlimitedArr(); - u_int32_t eSize(); - string toXml(const string& addPrefix); - - // Operator overloading - useful for sorting - bool operator<(AdbField& other); - - // FOR DEBUG - void print(int indent = 0); - -public: - // Members - string name; - u_int32_t size; // in bits - u_int32_t offset; // in bits (relative to the node start addr) - string desc; - bool definedAsArr; - u_int32_t lowBound; - u_int32_t highBound; - bool unlimitedArr; - string subNode; - AttrsMap attrs; - bool isReserved; - string condition; // field's visibility dynamic condition - - // FOR USER USAGE - void *userData; -}; - -/*************************** AdbConfig ***************************/ -class AdbConfig { -public: - // FOR DEBUG - void print(int indent = 0); - - string toXml(); - -public: - // Members - AttrsMap attrs; - AttrsMap enums; -}; - -/*************************** AdbInstance ***************************/ -class AdbInstance { -public: - // Methods - AdbInstance(); - ~AdbInstance(); - bool isLeaf(); - bool isUnion(); - bool isStruct(); - bool isNode(); - bool isPartOfArray(); - string fullName(int skipLevel = 0); - bool isReserved(); - u_int32_t dwordAddr(); - u_int32_t startBit(); - bool isEnumExists(); - bool enumToInt(const string &name, u_int64_t &val); // false means no enum value found - bool intToEnum(u_int64_t val, string &valName); // false means no enum name found - map getEnumMap(); - vector getEnumValues(); - AdbInstance* getUnionSelectedNodeName(const u_int64_t& selectorVal); - AdbInstance* getUnionSelectedNodeName(const string& selectorEnum); - bool operator<(const AdbInstance& other); - bool isConditionalNode(); - bool isConditionValid(map *valuesMap); - // DB like access methods - AdbInstance - * getChildByPath(const string& path, bool isCaseSensitive = true); - vector findChild(const string& name, - bool isCaseSensitive = true, bool by_inst_name = false); - string getInstanceAttr(const string &attrName) const; - AttrsMap::iterator getInstanceAttrIterator(const string &attrName, bool &isEnd); - void setInstanceAttr(const string &attrName, const string &attrValue); - void copyAllInstanceAttr(AttrsMap &attsToCopy); - AttrsMap getFullInstanceAttrsMapCopy(); - void setVarsMap(const string &attrName, const string &attrValue); - void setVarsMap(const AttrsMap &AttrsMap); - AttrsMap getVarsMap(); - vector getLeafFields(bool extendedName); // Get all leaf fields - void pushBuf(u_int8_t *buf, u_int64_t value); - u_int64_t popBuf(u_int8_t *buf); - int instAttrsMapLen() {return instAttrsMap.size();} - // FOR DEBUG - void print(int indent = 0); - -public: - // Members - AdbField *fieldDesc; - AdbNode *nodeDesc; - AdbInstance *parent; - string name; // instance name - vector subItems; - u_int32_t offset; // Global offset in bits (Relative to 0) - u_int32_t size; // in bits - u_int32_t arrIdx; - bool isNameBeenExtended; - AdbInstance *unionSelector; // For union instances only - bool isDiff; - // FOR USER USAGE - void *userData; -private: - AttrsMap instAttrsMap; // Attributes after evaluations and array expanding - AttrsMap varsMap; // all variables relevant to this item after evaluation -}; +using namespace xmlCreator; +using namespace std; -/*************************** Adb ***************************/ +typedef map AttrsMap; typedef vector StringVector; -typedef struct { +typedef struct +{ string fullPath; string includedFromFile; int includedFromLine; @@ -253,7 +122,8 @@ typedef vector ConfigList; typedef map InstanceAttrs; typedef map ExceptionsMap; -class Adb { +class Adb +{ public: // Methods Adb(); @@ -263,22 +133,34 @@ // 1- dwrod aligned unions // 2- contains nodes only // 3- check node size vs instance size - bool loadFromString(const char *adbContents, bool addReserved = false, - AdbProgress *progressObj = NULL, bool strict = true, - bool enforceExtraChecks = false); - bool load(string fname, bool addReserved = false, - AdbProgress *progressObj = NULL, bool strict = true, - string includePath = "", string includeDir = "", - bool enforceExtraChecks = false, bool getAllExceptions = false, - string logFile = ""); - string toXml(vector nodeNames = vector (), - bool addRootNode = false, string rootName = "MainNode", - string addPrefix = ""); + bool loadFromString(const char* adbContents, + bool addReserved = false, + AdbProgress* progressObj = NULL, + bool strict = true, + bool enforceExtraChecks = false); + bool load(string fname, + bool addReserved = false, + AdbProgress* progressObj = NULL, + bool strict = true, + string includePath = "", + string includeDir = "", + bool enforceExtraChecks = false, + bool getAllExceptions = false, + string logFile = "", + bool checkDsAlign = false, + bool enforceGuiChecks = false); + string toXml(vector nodeNames = vector(), + bool addRootNode = false, + string rootName = "MainNode", + string addPrefix = ""); AdbInstance* addMissingNodes(int depth, bool allowMultipleExceptions); - AdbInstance* createLayout(string rootNodeName, bool isExprEval = false, - AdbProgress *progressObj = NULL, int depth = -1, /* -1 means instantiate full tree */ - bool ignoreMissingNodes = false, bool getAllExceptions = false); + AdbInstance* createLayout(string rootNodeName, + bool isExprEval = false, + AdbProgress* progressObj = NULL, + int depth = -1, /* -1 means instantiate full tree */ + bool ignoreMissingNodes = false, + bool getAllExceptions = false); vector getNodeDeps(string nodeName); string getLastError(); @@ -301,7 +183,7 @@ InstanceAttrs instAttrs; // Key is instance's Adabe full path, value is attribute map string srcDocName; string srcDocVer; - LogFile _logFile; + LogFile* _logFile; /* For internal use */ public: @@ -310,22 +192,29 @@ IncludeFileMap includedFiles; StringVector warnings; ExceptionsMap adbExceptionMap; + private: - vector createInstance(AdbField *fieldDesc, - AdbInstance *parent, map vars, bool isExprEval, - AdbProgress *progressObj, int depth, - bool ignoreMissingNodes = false, bool getAllExceptions = false); - u_int32_t calcArrOffset(AdbField *fieldDesc, AdbInstance *parent, - u_int32_t arrIdx); - string evalExpr(string expr, AttrsMap *vars); + vector createInstance(AdbField* fieldDesc, + AdbInstance* parent, + map vars, + bool isExprEval, + AdbProgress* progressObj, + int depth, + bool ignoreMissingNodes = false, + bool getAllExceptions = false); + u_int32_t calcArrOffset(AdbField* fieldDesc, AdbInstance* parent, u_int32_t arrIdx); + string evalExpr(string expr, AttrsMap* vars); bool checkInstSizeConsistency(bool getAllExceptions = false); void cleanInstAttrs(); private: string _lastError; AdbExpr _adbExpr; - std::list _unionSelectorEvalDeffered; - void checkInstanceOffsetValidity(AdbInstance *inst, AdbInstance *parent, bool allowMultipleExceptions); + bool _checkDsAlign; + bool _enforceGuiChecks; + list _unionSelectorEvalDeffered; + void checkInstanceOffsetValidity(AdbInstance* inst, AdbInstance* parent, bool allowMultipleExceptions); void throwExeption(bool allowMultipleExceptions, string exceptionTxt, string addedMsgMultiExp); }; -#endif // ADB_PARSER_H + +#endif diff -Nru mstflint-4.17.0+1/adb_parser/adb_progress.cpp mstflint-4.21.0+1/adb_parser/adb_progress.cpp --- mstflint-4.17.0+1/adb_parser/adb_progress.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_progress.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010-2021 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + * + * Version: $Id$ + */ diff -Nru mstflint-4.17.0+1/adb_parser/adb_progress.h mstflint-4.21.0+1/adb_parser/adb_progress.h --- mstflint-4.17.0+1/adb_parser/adb_progress.h 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_progress.h 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + * + * Version: $Id$ + */ +/*************************** AdbProgress ***************************/ +class AdbProgress +{ +public: + virtual ~AdbProgress(){}; + virtual void progress(int prog = 0, int total = 0) + { + (void)prog; + (void)total; + }; +}; diff -Nru mstflint-4.17.0+1/adb_parser/adb_xmlCreator.cpp mstflint-4.21.0+1/adb_parser/adb_xmlCreator.cpp --- mstflint-4.17.0+1/adb_parser/adb_xmlCreator.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_xmlCreator.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,119 @@ +/* + * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + * + */ + +#include "adb_xmlCreator.h" +#include +#include +#include +#include + +string xmlCreator::indentString(int i) +{ + string s; + while (i--) + { + s += "\t"; + } + return s; +} + +int xmlCreator::dword(int bits) +{ + if (bits < 0) + { + bits -= 31; + } + + return (bits / 32) * 4; +} + +int xmlCreator::startBit(int bits) +{ + return bits % 32; +} + +string xmlCreator::formatAddr(u_int32_t offs, u_int32_t size) +{ + char str[64]; + sprintf(str, "0x%x.%u:%u", dword(offs), startBit(offs), size); + return str; +} + +/*template +bool xmlCreator::compareFieldsPtr(AdbInstance *f1, AdbInstance *f2) +{ + return (*f1) < (*f2); +}*/ + +string xmlCreator::encodeXml(const string& data) +{ + std::string buffer; + buffer.reserve(data.size()); + for (size_t pos = 0; pos != data.size(); ++pos) + { + switch (data[pos]) + { + case '&': + buffer.append("&"); + break; + + case '\"': + buffer.append("""); + break; + + case '\'': + buffer.append("'"); + break; + + case '<': + buffer.append("<"); + break; + + case '>': + buffer.append(">"); + break; + + default: + buffer.append(1, data.at(pos)); + break; + } + } + + return buffer; +} + +string xmlCreator::descNativeToXml(const string& desc) +{ + return boost::replace_all_copy(desc, "\n", "\\;"); +} diff -Nru mstflint-4.17.0+1/adb_parser/adb_xmlCreator.h mstflint-4.21.0+1/adb_parser/adb_xmlCreator.h --- mstflint-4.17.0+1/adb_parser/adb_xmlCreator.h 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/adb_xmlCreator.h 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,62 @@ +/* + * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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 xmlCreator_H +#define xmlCreator_H + +#include +#include + +using namespace std; + +namespace xmlCreator +{ +template +bool compareFieldsPtr(T* f1, T* f2); +string indentString(int i); +int dword(int bits); +int startBit(int bits); +string formatAddr(u_int32_t offs, u_int32_t size); +string encodeXml(const string& data); +string descNativeToXml(const string& desc); + +} // namespace xmlCreator + +template +bool xmlCreator::compareFieldsPtr(T* f1, T* f2) +{ + return (*f1) < (*f2); +} + +#endif diff -Nru mstflint-4.17.0+1/adb_parser/buf_ops.cpp mstflint-4.21.0+1/adb_parser/buf_ops.cpp --- mstflint-4.17.0+1/adb_parser/buf_ops.cpp 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/buf_ops.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ - -/* +/* * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. - * + * Copyright (c) 2013-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: - * + * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: - * + * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. - * + * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * 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 @@ -29,90 +29,102 @@ * 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. - + * * */ #include "buf_ops.h" /************************************ -* Function: calc_array_field_address -************************************/ -static u_int32_t calc_array_field_address(u_int32_t start_bit_offset, u_int32_t arr_elemnt_size, - int arr_idx, u_int32_t parent_node_size, + * Function: calc_array_field_address + ************************************/ +static u_int32_t calc_array_field_address(u_int32_t start_bit_offset, + u_int32_t arr_elemnt_size, + int arr_idx, + u_int32_t parent_node_size, int is_big_endian_arr) { u_int32_t offs; - if (arr_elemnt_size > 32) { + if (arr_elemnt_size > 32) + { assert(!(arr_elemnt_size % 32)); start_bit_offset += arr_elemnt_size * arr_idx; return start_bit_offset; } - if (is_big_endian_arr) { + if (is_big_endian_arr) + { u_int32_t dword_delta; offs = start_bit_offset - arr_elemnt_size * arr_idx; dword_delta = (((start_bit_offset >> 5) << 2) - ((offs >> 5) << 2)) / 4; - if (dword_delta) { + if (dword_delta) + { offs += 64 * dword_delta; } - } else { + } + else + { offs = start_bit_offset + arr_elemnt_size * arr_idx; } - //printf("==> parent_node_size=%d, offs=%d, arr_elemnt_size=%d\n", parent_node_size, offs, arr_elemnt_size); + // printf("==> parent_node_size=%d, offs=%d, arr_elemnt_size=%d\n", parent_node_size, offs, arr_elemnt_size); return TOOLS_MIN(32, parent_node_size) - (offs % 32) - arr_elemnt_size + ((offs >> 5) << 5); } /************************************ -* Function: push_integer_to_buff -************************************/ -static void push_integer_to_buff(u_int8_t *buff, u_int32_t bit_offset, u_int32_t byte_size, u_int64_t field_value) + * Function: push_integer_to_buff + ************************************/ +static void push_integer_to_buff(u_int8_t* buff, u_int32_t bit_offset, u_int32_t byte_size, u_int64_t field_value) { field_value = CPU_TO_BE64(field_value); memcpy(buff + bit_offset / 8, (u_int8_t*)&field_value + (8 - byte_size), byte_size); } /************************************ -* Function: push_bits_to_buff -************************************/ -//the next function will push the field into the buffer by inserting it's MSB bits first -//and therefore by doing it we save the CPU_TO_BE operation -static void push_bits_to_buff(u_int8_t *buff, u_int32_t bit_offset, u_int32_t field_size, u_int32_t field_value) + * Function: push_bits_to_buff + ************************************/ +// the next function will push the field into the buffer by inserting it's MSB bits first +// and therefore by doing it we save the CPU_TO_BE operation +static void push_bits_to_buff(u_int8_t* buff, u_int32_t bit_offset, u_int32_t field_size, u_int32_t field_value) { - u_int32_t i = 0; - u_int32_t byte_n = bit_offset / 8; + u_int32_t i = 0; + u_int32_t byte_n = bit_offset / 8; u_int32_t byte_n_offset = bit_offset % 8; u_int32_t to_push; - //going over all bits in field - while (i < field_size) { + // going over all bits in field + while (i < field_size) + { to_push = TOOLS_MIN(8 - byte_n_offset, field_size - i); i += to_push; - BYTE_N(buff, byte_n) = INSERTF(BYTE_N(buff, byte_n), 8 - to_push - byte_n_offset, field_value, field_size - i, to_push); - byte_n_offset = 0; //(byte_n_offset + to_push) % 8; + BYTE_N(buff, byte_n) = + INSERTF(BYTE_N(buff, byte_n), 8 - to_push - byte_n_offset, field_value, field_size - i, to_push); + byte_n_offset = 0; //(byte_n_offset + to_push) % 8; byte_n++; } } /************************************ -* Function: push_to_buf -************************************/ -void push_to_buf(u_int8_t *buff, u_int32_t bit_offset, u_int32_t field_size, u_int64_t field_value) + * Function: push_to_buf + ************************************/ +void push_to_buf(u_int8_t* buff, u_int32_t bit_offset, u_int32_t field_size, u_int64_t field_value) { bit_offset = calc_array_field_address(bit_offset, field_size, 0, field_size + 32, 0); - if (field_size <= 32) { + if (field_size <= 32) + { push_bits_to_buff(buff, bit_offset, field_size, field_value); - } else { - push_integer_to_buff(buff, bit_offset, field_size / 8, field_value); + } + else + { + push_integer_to_buff(buff, bit_offset, field_size / 8, field_value); } } /************************************ -* Function: pop_integer_from_buff -************************************/ -static u_int64_t pop_integer_from_buff(const u_int8_t *buff, u_int32_t bit_offset, u_int32_t byte_size) + * Function: pop_integer_from_buff + ************************************/ +static u_int64_t pop_integer_from_buff(const u_int8_t* buff, u_int32_t bit_offset, u_int32_t byte_size) { u_int64_t val = 0; memcpy((u_int8_t*)&val + (8 - byte_size), buff + bit_offset / 8, byte_size); @@ -120,24 +132,25 @@ } /************************************ -* Function: pop_bits_from_buff -************************************/ -//the next function will pop the field into the buffer by removing it's MSB bits first -//and therefore by doing it we save the BE_TO_CPU operation -static u_int32_t pop_bits_from_buff(const u_int8_t *buff, u_int32_t bit_offset, u_int32_t field_size) + * Function: pop_bits_from_buff + ************************************/ +// the next function will pop the field into the buffer by removing it's MSB bits first +// and therefore by doing it we save the BE_TO_CPU operation +static u_int32_t pop_bits_from_buff(const u_int8_t* buff, u_int32_t bit_offset, u_int32_t field_size) { - u_int32_t i = 0; - u_int32_t byte_n = bit_offset / 8; + u_int32_t i = 0; + u_int32_t byte_n = bit_offset / 8; u_int32_t byte_n_offset = bit_offset % 8; - u_int32_t field_32 = 0; + u_int32_t field_32 = 0; u_int32_t to_pop; - //going over all bits in field - while (i < field_size) { - to_pop = TOOLS_MIN(8 - byte_n_offset, field_size - i); + // going over all bits in field + while (i < field_size) + { + to_pop = TOOLS_MIN(8 - byte_n_offset, field_size - i); i += to_pop; field_32 = INSERTF(field_32, field_size - i, BYTE_N(buff, byte_n), 8 - to_pop - byte_n_offset, to_pop); - byte_n_offset = 0; //(byte_n_offset + to_pop) % 8; + byte_n_offset = 0; //(byte_n_offset + to_pop) % 8; byte_n++; } @@ -145,40 +158,46 @@ } /************************************ -* Function: pop_from_buf -************************************/ -u_int64_t pop_from_buf(const u_int8_t *buff, u_int32_t bit_offset, u_int32_t field_size) + * Function: pop_from_buf + ************************************/ +u_int64_t pop_from_buf(const u_int8_t* buff, u_int32_t bit_offset, u_int32_t field_size) { bit_offset = calc_array_field_address(bit_offset, field_size, 0, field_size + 32, 0); - if (field_size <= 32) { + if (field_size <= 32) + { return pop_bits_from_buff(buff, bit_offset, field_size); - } else { + } + else + { return pop_integer_from_buff(buff, bit_offset, field_size / 8); } } /************************************ -* Function: add_indentation -************************************/ -static void add_indentation(FILE *file, int indent_level) + * Function: add_indentation + ************************************/ +static void add_indentation(FILE* file, int indent_level) { - while (indent_level) { + while (indent_level) + { fprintf(file, "\\t"); indent_level--; } } /************************************ -* Function: print_raw -************************************/ -void print_raw(FILE *file, void *buff, int buff_len) + * Function: print_raw + ************************************/ +void print_raw(FILE* file, void* buff, int buff_len) { - u_int8_t *data = (u_int8_t*)buff; + u_int8_t* data = (u_int8_t*)buff; int i; add_indentation(file, 0); - for (i = 0; i < buff_len; i++) { - if (!(i % 4)) { + for (i = 0; i < buff_len; i++) + { + if (!(i % 4)) + { fprintf(file, "\n0x%08x: ", i); } diff -Nru mstflint-4.17.0+1/adb_parser/buf_ops.h mstflint-4.21.0+1/adb_parser/buf_ops.h --- mstflint-4.17.0+1/adb_parser/buf_ops.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/buf_ops.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ - -/* +/* * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. - * + * Copyright (c) 2013-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: - * + * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: - * + * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. - * + * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * 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 @@ -29,7 +29,7 @@ * 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. - + * * */ @@ -47,7 +47,7 @@ /************************************/ /************************************/ /************************************/ -void print_raw(FILE *file, void *buff, int buff_len); -u_int64_t pop_from_buf(const u_int8_t *buff, u_int32_t bit_offset, u_int32_t field_size); -void push_to_buf(u_int8_t *buff, u_int32_t bit_offset, u_int32_t field_size, u_int64_t field_value); +void print_raw(FILE* file, void* buff, int buff_len); +u_int64_t pop_from_buf(const u_int8_t* buff, u_int32_t bit_offset, u_int32_t field_size); +void push_to_buf(u_int8_t* buff, u_int32_t bit_offset, u_int32_t field_size, u_int64_t field_value); #endif // BIT_OPS_H diff -Nru mstflint-4.17.0+1/adb_parser/expr.cpp mstflint-4.21.0+1/adb_parser/expr.cpp --- mstflint-4.17.0+1/adb_parser/expr.cpp 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/expr.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ /* - * * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. - * + * Copyright (c) 2013-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: - * + * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: - * + * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. - * + * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * 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 @@ -29,7 +29,8 @@ * 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. - + * + * */ #include @@ -37,9 +38,8 @@ #include #include "expr.h" - -char*Expr::str; -char*Expr::initial_arg; +char* Expr::str; +char* Expr::initial_arg; Expr::status Expr::state; /* @@ -50,51 +50,51 @@ /* * Unary prefixes which present radix */ -#define RADIX16 "0x" -#define RADIX2 "0b" +#define RADIX16 "0x" +#define RADIX2 "0b" /* * Unary operations */ -#define ULOGNOT 71 /* ! */ -#define UMINUS 72 /* - */ -#define UNOT 73 /* ~ */ -#define UPLUS 74 /* + */ -#define U2POW 75 /* : */ -#define U2POW_ALT 76 /* POW2 */ -#define ULOG2 77 /* ' */ -#define ULOG2_ALT 78 /* LOG2 */ -#define SWAP32 79 /* SWAP32 */ -#define SWAP16 80 /* SWAP16 */ +#define ULOGNOT 71 /* ! */ +#define UMINUS 72 /* - */ +#define UNOT 73 /* ~ */ +#define UPLUS 74 /* + */ +#define U2POW 75 /* : */ +#define U2POW_ALT 76 /* POW2 */ +#define ULOG2 77 /* ' */ +#define ULOG2_ALT 78 /* LOG2 */ +#define SWAP32 79 /* SWAP32 */ +#define SWAP16 80 /* SWAP16 */ /* * Binary operations */ -#define AND 1 /* && */ -#define BIT_AND 2 /* & */ -#define BIT_OR 3 /* | */ -#define BIT_XOR 4 /* ^ */ -#define DIVID 5 /* / */ -#define EQ 6 /* == */ -#define GREAT 7 /* > */ -#define GREAT_EQ 8 /* >= */ -#define LESS 9 /* < */ -#define LESS_EQ 10 /* <= */ -#define MINUS 11 /* - */ -#define MODUL 12 /* % */ -#define MULT 13 /* * */ -#define NOTEQ 14 /* != */ -#define OR 15 /* || */ -#define PLUS 16 /* + */ -#define SHIFT_L 17 /* << */ -#define SHIFT_R 18 /* >> */ -#define XOR 19 /* log.xor*/ +#define AND 1 /* && */ +#define BIT_AND 2 /* & */ +#define BIT_OR 3 /* | */ +#define BIT_XOR 4 /* ^ */ +#define DIVID 5 /* / */ +#define EQ 6 /* == */ +#define GREAT 7 /* > */ +#define GREAT_EQ 8 /* >= */ +#define LESS 9 /* < */ +#define LESS_EQ 10 /* <= */ +#define MINUS 11 /* - */ +#define MODUL 12 /* % */ +#define MULT 13 /* * */ +#define NOTEQ 14 /* != */ +#define OR 15 /* || */ +#define PLUS 16 /* + */ +#define SHIFT_L 17 /* << */ +#define SHIFT_R 18 /* >> */ +#define XOR 19 /* log.xor*/ /* * Other token types */ -#define VALUE 103 /* Name or constant */ -#define EXPR_OK 0 /* Expression getted successfully */ +#define VALUE 103 /* Name or constant */ +#define EXPR_OK 0 /* Expression getted successfully */ /* * Miscellaneous constant @@ -102,146 +102,112 @@ #ifdef IGNORE #undef IGNORE #endif -#define IGNORE " \t\n\r" /* Ignore in parsing */ -#define HIGH_PRI 1 -#define LOW_PRI 9 +#define IGNORE " \t\n\r" /* Ignore in parsing */ +#define HIGH_PRI 1 +#define LOW_PRI 9 /* * Unary operations. Priority not applicable, * all unary operations have same priority. */ -static Expr::table unar[] = { - { U2POW, 0, ":" }, - { U2POW_ALT, 0, "POW2" }, - { ULOG2, 0, "'" }, - { ULOG2_ALT, 0, "LOG2" }, - { ULOGNOT, 0, "!" }, - { UMINUS, 0, "-" }, - { UNOT, 0, "~" }, - { UPLUS, 0, "+" }, - { SWAP32, 0, "SWAP32" }, - { SWAP16, 0, "SWAP16" } -}; +static Expr::table unar[] = {{U2POW, 0, ":"}, {U2POW_ALT, 0, "POW2"}, {ULOG2, 0, "'"}, {ULOG2_ALT, 0, "LOG2"}, + {ULOGNOT, 0, "!"}, {UMINUS, 0, "-"}, {UNOT, 0, "~"}, {UPLUS, 0, "+"}, + {SWAP32, 0, "SWAP32"}, {SWAP16, 0, "SWAP16"}}; static int Lunar = numbel(unar); /* * Binary operations. */ static Expr::table binar[] = { - { MODUL, 1, "%" }, - { MULT, 1, "*" }, - { DIVID, 1, "/" }, - { PLUS, 2, "+" }, - { MINUS, 2, "-" }, - { SHIFT_L, 3, "<<" }, - { SHIFT_L, 3, "SHIFT_L" }, - { SHIFT_R, 3, ">>" }, - { SHIFT_R, 3, "SHIFT_R" }, - { GREAT, 4, ">" }, - { GREAT, 4, "GREAT" }, - { GREAT_EQ, 4, ">=" }, - { GREAT_EQ, 4, "GREAT_EQ" }, - { LESS, 4, "<" }, - { LESS, 4, "LESS" }, - { LESS_EQ, 4, "<=" }, - { LESS_EQ, 4, "LESS_EQ" }, - { EQ, 5, "==" }, - { EQ, 5, "EQ" }, - { NOTEQ, 5, "!=" }, - { NOTEQ, 5, "NOTEQ" }, - { BIT_AND, 6, "&" }, - { BIT_AND, 6, "BIT_AND" }, - { BIT_OR, 7, "|" }, - { BIT_OR, 7, "BIT_OR" }, - { BIT_XOR, 7, "^" }, - { BIT_XOR, 7, "BIT_XOR" }, - { AND, 8, "&&" }, - { AND, 8, "AND" }, - { OR, 9, "||" }, - { OR, 9, "OR" }, - { XOR, 9, "XOR" } -}; + {MODUL, 1, "%"}, {MULT, 1, "*"}, {DIVID, 1, "/"}, {PLUS, 2, "+"}, + {MINUS, 2, "-"}, {SHIFT_L, 3, "<<"}, {SHIFT_L, 3, "SHIFT_L"}, {SHIFT_R, 3, ">>"}, + {SHIFT_R, 3, "SHIFT_R"}, {GREAT, 4, ">"}, {GREAT, 4, "GREAT"}, {GREAT_EQ, 4, ">="}, + {GREAT_EQ, 4, "GREAT_EQ"}, {LESS, 4, "<"}, {LESS, 4, "LESS"}, {LESS_EQ, 4, "<="}, + {LESS_EQ, 4, "LESS_EQ"}, {EQ, 5, "=="}, {EQ, 5, "EQ"}, {NOTEQ, 5, "!="}, + {NOTEQ, 5, "NOTEQ"}, {BIT_AND, 6, "&"}, {BIT_AND, 6, "BIT_AND"}, {BIT_OR, 7, "|"}, + {BIT_OR, 7, "BIT_OR"}, {BIT_XOR, 7, "^"}, {BIT_XOR, 7, "BIT_XOR"}, {AND, 8, "&&"}, + {AND, 8, "AND"}, {OR, 9, "||"}, {OR, 9, "OR"}, {XOR, 9, "XOR"}}; static int Lbinar = numbel(binar); - /******************************************************** -* routine: expr -* -* description: -* Main routine for expression evaluation. Do some interface -* functions and call binary operation evaluation with lowest -* priority. -* -* arguments: -* pstr pointer to string which contains expression to -* evaluation. After successfully evaluation expr -* moves this pointer to end of expression (first -* unparsed character). -* result pointer to unsigned long to put result of expression. -* dr default radix for numbers. -* ErrorRep pointer to callbeck routine for error reporting. -* arguments - like printf. -* -* return code: -* >0 length of evaluated expression. -* <0 error code, one of following: -* ERR_RPAR_EXP - Right parentheses expected. -* ERR_VALUE_EXP - Value expected. -* ERR_BIN_EXP - Binary operation expected. It is a normal situation -* on experssion end. -* ERR_DIV_ZERO - Divide zero attempt. -* ERR_BAD_NUMBER - Bad constant syntax. -* ERR_BAD_NAME - Name not found in symbol table. -* -********************************************************/ -int Expr::expr(char **pstr, u_int64_t *result) + * routine: expr + * + * description: + * Main routine for expression evaluation. Do some interface + * functions and call binary operation evaluation with lowest + * priority. + * + * arguments: + * pstr pointer to string which contains expression to + * evaluation. After successfully evaluation expr + * moves this pointer to end of expression (first + * unparsed character). + * result pointer to unsigned long to put result of expression. + * dr default radix for numbers. + * ErrorRep pointer to callbeck routine for error reporting. + * arguments - like printf. + * + * return code: + * >0 length of evaluated expression. + * <0 error code, one of following: + * ERR_RPAR_EXP - Right parentheses expected. + * ERR_VALUE_EXP - Value expected. + * ERR_BIN_EXP - Binary operation expected. It is a normal situation + * on experssion end. + * ERR_DIV_ZERO - Divide zero attempt. + * ERR_BAD_NUMBER - Bad constant syntax. + * ERR_BAD_NAME - Name not found in symbol table. + * + ********************************************************/ +int Expr::expr(char** pstr, u_int64_t* result) { int rc = 0; int len = 0; - str = *pstr; + str = *pstr; initial_arg = *pstr; - state = was_bin; + state = was_bin; /* Operation(s) found, do really evaluation. */ rc = GetBinaryOp(result, LOW_PRI); - if (rc == EXPR_OK || - rc == ERR_BIN_EXP) { + if (rc == EXPR_OK || rc == ERR_BIN_EXP) + { len = (int)(str - *pstr); *pstr = str; return len; - } else { + } + else + { return rc; } } - /******************************************************** -* routine: GetBinaryOp -* -* description: -* Executes binary operations with current priority level. -* If priority great then constant HIGH_PRI, calls itself -* recursive with decreased priority to get operands for -* expression, otherwise calls GetUnaryOp to get operands. -* -* arguments: -* val Pointer to unsigned long where result of -* operation will be placed. -* priority Execute operation only with this priority -* -* return code: -* EXPR_OK Binary operation was calculated successfully -* ERR_DIV_ZERO Zero divide attempt -* otherwise Error code from GetUnaryOp (see) -* -* side effects: -* Attention, deep recursion... -* -********************************************************/ -int Expr::GetBinaryOp(u_int64_t *val, int priority) + * routine: GetBinaryOp + * + * description: + * Executes binary operations with current priority level. + * If priority great then constant HIGH_PRI, calls itself + * recursive with decreased priority to get operands for + * expression, otherwise calls GetUnaryOp to get operands. + * + * arguments: + * val Pointer to unsigned long where result of + * operation will be placed. + * priority Execute operation only with this priority + * + * return code: + * EXPR_OK Binary operation was calculated successfully + * ERR_DIV_ZERO Zero divide attempt + * otherwise Error code from GetUnaryOp (see) + * + * side effects: + * Attention, deep recursion... + * + ********************************************************/ +int Expr::GetBinaryOp(u_int64_t* val, int priority) { - char *str_old; + char* str_old; int rc = 0; int i = 0; u_int64_t left = 0; @@ -249,21 +215,24 @@ token curr; status stat_old; - rc = priority > HIGH_PRI ? GetBinaryOp(&left, priority - 1) : - GetUnaryOp(&left); - if (rc != EXPR_OK) { + rc = priority > HIGH_PRI ? GetBinaryOp(&left, priority - 1) : GetUnaryOp(&left); + if (rc != EXPR_OK) + { return rc; } - for (;;) { + for (;;) + { str_old = str; stat_old = state; GetToken(&curr); - for (i = 0; i < Lbinar; i++) { - if (binar[i].type == curr.type && binar[i].pri == priority) { - rc = priority > HIGH_PRI ? GetBinaryOp(&right, priority - 1) : - GetUnaryOp(&right); - if (rc != EXPR_OK) { + for (i = 0; i < Lbinar; i++) + { + if (binar[i].type == curr.type && binar[i].pri == priority) + { + rc = priority > HIGH_PRI ? GetBinaryOp(&right, priority - 1) : GetUnaryOp(&right); + if (rc != EXPR_OK) + { str = str_old; state = stat_old; return rc; @@ -271,94 +240,97 @@ switch (curr.type) { - case MODUL: - if (right == 0) { - ErrorReport("Zero modulo attempt.\n"); - return ERR_DIV_ZERO; - } - left = left % right; - break; - - case MULT: - left = left * right; - break; - - case DIVID: - if (right == 0) { - ErrorReport("Zero divide attempt.\n"); - return ERR_DIV_ZERO; - } - left = left / right; - break; - - case PLUS: - left = left + right; - break; - - case MINUS: - left = left - right; - break; - - case SHIFT_L: - left = (left << (int)right); - break; - - case SHIFT_R: - left = (left >> (int)right); - break; - - case GREAT: - left = (left > right); - break; - - case GREAT_EQ: - left = (left >= right); - break; - - case LESS: - left = (left < right); - break; - - case LESS_EQ: - left = (left <= right); - break; - - case EQ: - left = (left == right); - break; - - case NOTEQ: - left = (left != right); - break; - - case BIT_AND: - left = left & right; - break; - - case BIT_OR: - left = left | right; - break; - - case BIT_XOR: - left = left ^ right; - break; - - case AND: - left = (left && right); - break; - - case OR: - left = (left || right); - break; - - case XOR: - left = (left && !right) || (!left && right); - break; + case MODUL: + if (right == 0) + { + ErrorReport("Zero modulo attempt.\n"); + return ERR_DIV_ZERO; + } + left = left % right; + break; + + case MULT: + left = left * right; + break; + + case DIVID: + if (right == 0) + { + ErrorReport("Zero divide attempt.\n"); + return ERR_DIV_ZERO; + } + left = left / right; + break; + + case PLUS: + left = left + right; + break; + + case MINUS: + left = left - right; + break; + + case SHIFT_L: + left = (left << (int)right); + break; + + case SHIFT_R: + left = (left >> (int)right); + break; + + case GREAT: + left = (left > right); + break; + + case GREAT_EQ: + left = (left >= right); + break; + + case LESS: + left = (left < right); + break; + + case LESS_EQ: + left = (left <= right); + break; + + case EQ: + left = (left == right); + break; + + case NOTEQ: + left = (left != right); + break; + + case BIT_AND: + left = left & right; + break; + + case BIT_OR: + left = left | right; + break; + + case BIT_XOR: + left = left ^ right; + break; + + case AND: + left = (left && right); + break; + + case OR: + left = (left || right); + break; + + case XOR: + left = (left && !right) || (!left && right); + break; } break; } } - if (i >= Lbinar) { + if (i >= Lbinar) + { UngetToken(curr); *val = left; return EXPR_OK; @@ -366,23 +338,22 @@ } } - /******************************************************** -* routine: GetUnaryOp -* -* description: -* Executes unary operations. -* -* arguments: -* val Pointer to unsigned long where result of -* operation will be placed. -* -* return code: -* EXPR_OK Binary operation was calculated successfully -* otherwise Error code from GetToken (see) -* -********************************************************/ -int Expr::GetUnaryOp(u_int64_t *val) + * routine: GetUnaryOp + * + * description: + * Executes unary operations. + * + * arguments: + * val Pointer to unsigned long where result of + * operation will be placed. + * + * return code: + * EXPR_OK Binary operation was calculated successfully + * otherwise Error code from GetToken (see) + * + ********************************************************/ +int Expr::GetUnaryOp(u_int64_t* val) { int rc, unary_op; u_int64_t tmp = 0; @@ -393,112 +364,110 @@ GetToken(&curr); switch (unary_op = curr.type) { - case U2POW: - case U2POW_ALT: - case ULOG2: - case ULOG2_ALT: - case ULOGNOT: - case UMINUS: - case UNOT: - case UPLUS: - case SWAP32: - case SWAP16: - /* - * Was unary operation - retrieve more token. - */ - rc = GetUnaryOp(val); - if (rc != EXPR_OK) { - return rc; - } - - /* OK, execute required unary operation. */ - switch (unary_op) - { - case U2POW_ALT: case U2POW: - *val = (u_int64_t)1 << (int)(*val); - break; - - case ULOG2_ALT: + case U2POW_ALT: case ULOG2: - if (*val != 0) { - for (tmp = 1, tmp1 = 0; tmp < *val; tmp = tmp << 1) - ++tmp1; - *val = tmp1; + case ULOG2_ALT: + case ULOGNOT: + case UMINUS: + case UNOT: + case UPLUS: + case SWAP32: + case SWAP16: + /* + * Was unary operation - retrieve more token. + */ + rc = GetUnaryOp(val); + if (rc != EXPR_OK) + { + return rc; } - break; - case ULOGNOT: - *val = !(*val); - break; + /* OK, execute required unary operation. */ + switch (unary_op) + { + case U2POW_ALT: + case U2POW: + *val = (u_int64_t)1 << (int)(*val); + break; - case UMINUS: - *val = (u_int64_t)(-((int64_t)(*val))); - break; + case ULOG2_ALT: + case ULOG2: + if (*val != 0) + { + for (tmp = 1, tmp1 = 0; tmp < *val; tmp = tmp << 1) + ++tmp1; + *val = tmp1; + } + break; - case UNOT: - *val = ~(*val); - break; + case ULOGNOT: + *val = !(*val); + break; - case UPLUS: - break; + case UMINUS: + *val = (u_int64_t)(-((int64_t)(*val))); + break; - case SWAP32: - *val = ((tmpVal & 0x000000FFUL) << 24) | - ((tmpVal & 0x0000FF00UL) << 8) | - ((tmpVal & 0x00FF0000UL) >> 8) | - ((tmpVal & 0xFF000000UL) >> 24); - break; + case UNOT: + *val = ~(*val); + break; - case SWAP16: - *val = ((tmpVal & 0xFF000000U) >> 8) | - ((tmpVal & 0x00FF0000U) << 8) | - ((tmpVal & 0x0000FF00U) >> 8) | - ((tmpVal & 0x000000FFU) << 8); + case UPLUS: + break; + + case SWAP32: + *val = ((tmpVal & 0x000000FFUL) << 24) | ((tmpVal & 0x0000FF00UL) << 8) | + ((tmpVal & 0x00FF0000UL) >> 8) | ((tmpVal & 0xFF000000UL) >> 24); + break; + + case SWAP16: + *val = ((tmpVal & 0xFF000000U) >> 8) | ((tmpVal & 0x00FF0000U) << 8) | + ((tmpVal & 0x0000FF00U) >> 8) | ((tmpVal & 0x000000FFU) << 8); + break; + } break; - } - break; - case VALUE: - /* - * Unary operation is omitted. Assume no unary operations. - */ - *val = curr.value; - break; + case VALUE: + /* + * Unary operation is omitted. Assume no unary operations. + */ + *val = curr.value; + break; - default: - /* - * Here we expect only unary operation or value. - * Something other - error. Bring error code witch - * present by curr.type to caller. - */ - UngetToken(curr); - return curr.type; + default: + /* + * Here we expect only unary operation or value. + * Something other - error. Bring error code witch + * present by curr.type to caller. + */ + UngetToken(curr); + return curr.type; } return EXPR_OK; } /******************************************************** -* routine: GetToken -* -* description: -* Gets next token from static pointer str. -* -* arguments: -* pt pointer to token. GetToken fills in all -* field by this pointer. -* -* return code: -* None -* -* side effects: -* 1. May insrease pointer str (if token getted successfully). -* 2. May change static variable state (if token getted -* successfully and state was changed). -* -********************************************************/ -void Expr::GetToken(token *pt) + * routine: GetToken + * + * description: + * Gets next token from static pointer str. + * + * arguments: + * pt pointer to token. GetToken fills in all + * field by this pointer. + * + * return code: + * None + * + * side effects: + * 1. May insrease pointer str (if token getted successfully). + * 2. May change static variable state (if token getted + * successfully and state was changed). + * + ********************************************************/ +void Expr::GetToken(token* pt) { char *old_str, *p; int i, rc, oplen; @@ -506,227 +475,263 @@ /* * Skip all leading ignorable characters */ - do { + do + { old_str = str; for (p = (char*)IGNORE; *p; p++) - if (*str == *p) { + if (*str == *p) + { str++; } } while ((old_str != str) && *str); + pt->value = 0; pt->beg = str; pt->sta = state; switch (state) { - case was_bin: - /* - * Was binary operation (or begin of experssion). - * Unary operation expected. - * Try find unary operation with maximal length. - */ - oplen = 0; - for (i = 0; i < Lunar; i++) - if (!strncmp(unar[i].st, str, strlen(unar[i].st))) { - if ((int)strlen(unar[i].st) > oplen) { - pt->type = unar[i].type; - oplen = strlen(unar[i].st); - } - } - if (oplen) { - /* Unary operation found. */ - str += oplen; - state = was_bin; - return; - } - - /* - * Unary operation not found. - * Number or name expected. - */ - if (!strncmp(str, RADIX16, strlen(RADIX16)) - || !strncmp(str, RADIX2, strlen(RADIX2))) { + case was_bin: /* - * Number expected. + * Was binary operation (or begin of experssion). + * Unary operation expected. + * Try find unary operation with maximal length. */ - rc = GetNumb(&pt->value); - if (rc == EXPR_OK) { - pt->type = VALUE; - } else { - pt->type = rc; + oplen = 0; + for (i = 0; i < Lunar; i++) + if (!strncmp(unar[i].st, str, strlen(unar[i].st))) + { + if ((int)strlen(unar[i].st) > oplen) + { + pt->type = unar[i].type; + oplen = strlen(unar[i].st); + } + } + if (oplen) + { + /* Unary operation found. */ + str += oplen; + state = was_bin; return; } - } else if (valid_name(*str) || valid_digit(*str, def_radix)) { + /* - * Name or number expected. + * Unary operation not found. + * Number or name expected. */ - rc = GetName(&pt->value); - if (rc == EXPR_OK) { - pt->type = VALUE; - } else { - pt->type = rc; - return; + if (!strncmp(str, RADIX16, strlen(RADIX16)) || !strncmp(str, RADIX2, strlen(RADIX2))) + { + /* + * Number expected. + */ + rc = GetNumb(&pt->value); + if (rc == EXPR_OK) + { + pt->type = VALUE; + } + else + { + pt->type = rc; + return; + } } - } else if (*str == '(') { - /* - * Parentheses expression expected. - */ - old_str = str++; - rc = GetBinaryOp(&pt->value, LOW_PRI); - if (rc == EXPR_OK) { - pt->type = VALUE; - } else { - pt->type = rc; - return; + else if (valid_name(*str) || valid_digit(*str, def_radix)) + { + /* + * Name or number expected. + */ + rc = GetName(&pt->value); + if (rc == EXPR_OK) + { + pt->type = VALUE; + } + else + { + pt->type = rc; + return; + } } - if (*str == ')') { - str++; - } else { - /* error, right parentheses expected. */ - pt->type = ERR_RPAR_EXP; - if (*str) { - ErrorReport("Instead of\"" + std::string(str) + "\" right parentheses expected.\n"); - } else { - ErrorReport("Unexpected end in expression \"" + std::string(initial_arg) + "\" - right parentheses expected.\n"); + else if (*str == '(') + { + /* + * Parentheses expression expected. + */ + old_str = str++; + rc = GetBinaryOp(&pt->value, LOW_PRI); + if (rc == EXPR_OK) + { + pt->type = VALUE; + } + else + { + pt->type = rc; + return; + } + if (*str == ')') + { + str++; + } + else + { + /* error, right parentheses expected. */ + pt->type = ERR_RPAR_EXP; + if (*str) + { + ErrorReport("Instead of\"" + std::string(str) + "\" right parentheses expected.\n"); + } + else + { + ErrorReport("Unexpected end in expression \"" + std::string(initial_arg) + + "\" - right parentheses expected.\n"); + } + str = old_str; + return; } - str = old_str; - return; } - } else { - /* error, number or name or unary operation expected. */ - pt->type = ERR_VALUE_EXP; - if (*str) { - ErrorReport("Instead of \"" + std::string(str) + "\" value or unary operation expected.\n"); - } else { - ErrorReport("Unexpected end in expression \"" + std::string(initial_arg) + "\" - value or unary operation expected.\n"); + else + { + /* error, number or name or unary operation expected. */ + pt->type = ERR_VALUE_EXP; + if (*str) + { + ErrorReport("Instead of \"" + std::string(str) + "\" value or unary operation expected.\n"); + } + else + { + ErrorReport("Unexpected end in expression \"" + std::string(initial_arg) + + "\" - value or unary operation expected.\n"); + } + return; } + state = was_opr; return; - } - state = was_opr; - return; - case was_opr: - /* - * Was operand. Binary operation expected - * Try find binary operation with maximal length. - */ - oplen = 0; - for (i = 0; i < Lbinar; i++) - if (!strncmp(binar[i].st, str, strlen(binar[i].st))) { - if ((int)strlen(binar[i].st) > oplen) { - oplen = strlen(binar[i].st); - pt->type = binar[i].type; + case was_opr: + /* + * Was operand. Binary operation expected + * Try find binary operation with maximal length. + */ + oplen = 0; + for (i = 0; i < Lbinar; i++) + if (!strncmp(binar[i].st, str, strlen(binar[i].st))) + { + if ((int)strlen(binar[i].st) > oplen) + { + oplen = strlen(binar[i].st); + pt->type = binar[i].type; + } } + if (oplen) + { + /* Binary operation found. */ + str += oplen; + state = was_bin; + return; } - if (oplen) { - /* Binary operation found. */ - str += oplen; - state = was_bin; - return; - } - /* error, binary operation expected. */ - pt->type = ERR_BIN_EXP; - return; + /* error, binary operation expected. */ + pt->type = ERR_BIN_EXP; + return; } } /******************************************************** -* routine: UngetToken -* -* description: -* Ungets previously getted token. -* -* arguments: -* t token to unget -* -* return code: -* None -* -* side effects: -* 1. Restores str as was before token. -* 2. Restores state as was before token -* -********************************************************/ + * routine: UngetToken + * + * description: + * Ungets previously getted token. + * + * arguments: + * t token to unget + * + * return code: + * None + * + * side effects: + * 1. Restores str as was before token. + * 2. Restores state as was before token + * + ********************************************************/ void Expr::UngetToken(token t) { str = t.beg; state = t.sta; } - /******************************************************** -* routine: GetNumb -* -* description: -* Gets constant from str. First character of constant may -* be special value to set radix (% for 2, & for 10 and $ for 16). -* -* arguments: -* val pointer to unsigned long to put constatnt value. -* -* return code: -* EXPR_OK - Constant OK -* ERR_BAD_NUMBER - Bad constant syntax -* -* side effects: -* Moves pointer str to end of constant. -* -********************************************************/ -int Expr::GetNumb(u_int64_t *val) + * routine: GetNumb + * + * description: + * Gets constant from str. First character of constant may + * be special value to set radix (% for 2, & for 10 and $ for 16). + * + * arguments: + * val pointer to unsigned long to put constatnt value. + * + * return code: + * EXPR_OK - Constant OK + * ERR_BAD_NUMBER - Bad constant syntax + * + * side effects: + * Moves pointer str to end of constant. + * + ********************************************************/ +int Expr::GetNumb(u_int64_t* val) { int radix = def_radix; - if (!strncmp(str, RADIX16, strlen(RADIX16))) { + if (!strncmp(str, RADIX16, strlen(RADIX16))) + { radix = 16; str += strlen(RADIX16); - } else if (!strncmp(str, RADIX2, strlen(RADIX2))) { + } + else if (!strncmp(str, RADIX2, strlen(RADIX2))) + { radix = 2; str += strlen(RADIX2); } - if (!valid_digit(*str, radix)) { + if (!valid_digit(*str, radix)) + { ErrorReport("\"" + std::string(str) + "\" -- bad constant syntax.\n"); return ERR_BAD_NUMBER; } *val = 0L; for (; valid_digit(*str, radix); str++) - *val = *val * radix + - ( ('a' <= *str && *str <= 'f') ? *str - 'a' + 10 : - ( ('A' <= *str && *str <= 'F') ? *str - 'A' + 10 : - *str - '0')); + *val = + *val * radix + (('a' <= *str && *str <= 'f') ? *str - 'a' + 10 : + (('A' <= *str && *str <= 'F') ? *str - 'A' + 10 : *str - '0')); return EXPR_OK; } - /******************************************************** -* routine: GetName -* -* description: -* Gets name from str and tries to find it in symbol -* table. If name looks as constant in "def_radix", -* interprets it as constant rather than name. -* -* arguments: -* val - pointer to unsigned long to put value -* -* return code: -* EXPR_OK - Name successfully found in symbol table and -* substituted by its value. -* ERR_BAD_NAME - Name not found in symbol table. -* -* side effects: -* Moves pointer str to end of name. -* -********************************************************/ + * routine: GetName + * + * description: + * Gets name from str and tries to find it in symbol + * table. If name looks as constant in "def_radix", + * interprets it as constant rather than name. + * + * arguments: + * val - pointer to unsigned long to put value + * + * return code: + * EXPR_OK - Name successfully found in symbol table and + * substituted by its value. + * ERR_BAD_NAME - Name not found in symbol table. + * + * side effects: + * Moves pointer str to end of name. + * + ********************************************************/ #define MAXNAM 100 -int Expr::GetName(u_int64_t *val) +int Expr::GetName(u_int64_t* val) { static char name[MAXNAM]; - char *p, *old_str; + char *p, *old_str; - old_str = str; /* Save start name position. */ + old_str = str; /* Save start name position. */ /* Get name. */ for (p = name; valid_name(*str);) @@ -735,10 +740,12 @@ /* Try interpret as constant first. */ for (p = name; *p; p++) - if (!valid_digit(*p, def_radix)) { + if (!valid_digit(*p, def_radix)) + { break; } - if (!*p) { + if (!*p) + { /* * All characters of this name are valid digit in radix * "def_radix". So, interpret this name as constant. @@ -749,80 +756,80 @@ /* Retrieve name from symbol table. */ /* -------------------------------- */ - if (ResolveName(name, val) == 0) { + if (ResolveName(name, val) == 0) + { return EXPR_OK; - } else { - ErrorReport("Symbolic name \"" + std::string(name) + "\" not resolved.\n"); + } + else + { + ErrorReport("Symbolic name \"" + std::string(name) + "\" not resolved.\n"); return ERR_BAD_NAME; } } /******************************************************** -* routine: valid_name -* -* description: -* Function checks if character may be part of symbolic name. -* -* arguments: -* ch - character to check -* -* return code: -* 0 - character is not valid -* 1 - character is valid -* -********************************************************/ + * routine: valid_name + * + * description: + * Function checks if character may be part of symbolic name. + * + * arguments: + * ch - character to check + * + * return code: + * 0 - character is not valid + * 1 - character is valid + * + ********************************************************/ int Expr::valid_name(char ch) { return (ch == '_' || ch == '.') ? 1 : isalnum(ch); } /******************************************************** -* routine: valid_digit -* -* description: -* Check validity of digit ch (in ASCII, ex. '1') according to radix 'radix'. -* -* arguments: -* ch - character to check -* radix - radix -* -* return code: -* 0 - character is not valid -* 1 - character is valid -* -********************************************************/ + * routine: valid_digit + * + * description: + * Check validity of digit ch (in ASCII, ex. '1') according to radix 'radix'. + * + * arguments: + * ch - character to check + * radix - radix + * + * return code: + * 0 - character is not valid + * 1 - character is valid + * + ********************************************************/ int Expr::valid_digit(char ch, int radix) { switch (radix) { - case 2: - return (ch == '0' || ch == '1') ? 1 : 0; + case 2: + return (ch == '0' || ch == '1') ? 1 : 0; - case 10: - return isdigit(ch); + case 10: + return isdigit(ch); - case 16: - return isxdigit(ch); + case 16: + return isxdigit(ch); - default: - return 1; + default: + return 1; } } - /******************************************************** -* routine: ErrorReport -* -* description: -* Error reporting -* -* arguments: -* like printf -* -********************************************************/ + * routine: ErrorReport + * + * description: + * Error reporting + * + * arguments: + * like printf + * + ********************************************************/ void Expr::ErrorReport(const std::string& msg) { - Error(msg); } - diff -Nru mstflint-4.17.0+1/adb_parser/expr.h mstflint-4.21.0+1/adb_parser/expr.h --- mstflint-4.17.0+1/adb_parser/expr.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/expr.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ /* - * - * Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. - * + * + * Copyright (c) 2013-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: - * + * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: - * + * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. - * + * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * 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 @@ -29,7 +29,8 @@ * 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. - + * + * Version: $Id$ * * expr.h - expression evaluator definitions * @@ -101,67 +102,69 @@ #include - class Expr { public: // Error codes - enum { - ERR_RPAR_EXP = -1, // Right parentheses expected - ERR_VALUE_EXP = -2, // Value expected - ERR_BIN_EXP = -3, // Binary operation expected (normal at end) - ERR_DIV_ZERO = -4, // Divide zero attempt - ERR_BAD_NUMBER = -5, // Bad constant syntax - ERR_BAD_NAME = -6 // Name not resolved + enum + { + ERR_RPAR_EXP = -1, // Right parentheses expected + ERR_VALUE_EXP = -2, // Value expected + ERR_BIN_EXP = -3, // Binary operation expected (normal at end) + ERR_DIV_ZERO = -4, // Divide zero attempt + ERR_BAD_NUMBER = -5, // Bad constant syntax + ERR_BAD_NAME = -6 // Name not resolved }; - Expr() : def_radix(10) { } - virtual ~Expr() { } + Expr() : def_radix(10) {} + virtual ~Expr() {} - int expr(char **pstr, u_int64_t *result); + int expr(char** pstr, u_int64_t* result); /* Current state of parsing. */ - typedef enum { - was_bin, /* was binary operation */ - was_opr /* was operand */ + typedef enum + { + was_bin, /* was binary operation */ + was_opr /* was operand */ } status; /* Token as it read by parser. */ - typedef struct { - char *beg; /* pointer to token begining */ + typedef struct + { + char* beg; /* pointer to token begining */ status sta; /* state of parser before this token */ int type; /* type of token (any operation, VALUE or error code */ u_int64_t value; /* if type == VALUE value of token */ } token; /* Table of operations. */ - typedef struct { - int type; /* type of operation - internal number */ - int pri; /* priority of this operation */ - const char *st; /* string which cause this operation */ + typedef struct + { + int type; /* type of operation - internal number */ + int pri; /* priority of this operation */ + const char* st; /* string which cause this operation */ } table; private: - - static char *str; - static char *initial_arg; + static char* str; + static char* initial_arg; static status state; int def_radix; - int GetBinaryOp(u_int64_t *val, int priority); - int GetUnaryOp(u_int64_t *val); - void GetToken(token *pt); - void UngetToken(token t); - int GetNumb(u_int64_t *val); - int GetName(u_int64_t *val); - int valid_digit(char ch, int radix); - int valid_name(char ch); + int GetBinaryOp(u_int64_t* val, int priority); + int GetUnaryOp(u_int64_t* val); + void GetToken(token* pt); + void UngetToken(token t); + int GetNumb(u_int64_t* val); + int GetName(u_int64_t* val); + int valid_digit(char ch, int radix); + int valid_name(char ch); - void ErrorReport(const std::string& msg); + void ErrorReport(const std::string& msg); /* * Pure virtual methods. You must define them to use expr. */ - virtual int ResolveName(char *name, u_int64_t *val) = 0; + virtual int ResolveName(char* name, u_int64_t* val) = 0; virtual void Error(const std::string& msg) = 0; }; diff -Nru mstflint-4.17.0+1/adb_parser/Makefile.am mstflint-4.21.0+1/adb_parser/Makefile.am --- mstflint-4.17.0+1/adb_parser/Makefile.am 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/Makefile.am 2022-04-28 11:50:02.000000000 +0000 @@ -1,6 +1,6 @@ #-- # -# Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. +# Copyright (c) 2019-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU @@ -38,7 +38,9 @@ USER_DIR = .. INCLUDES = -I$(USER_DIR) -I$(USER_DIR)/common -AM_CXXFLAGS = -Wall -W -g -MP -MD -Werror -pipe $(COMPILER_FPIC) +AM_CXXFLAGS = -Wall -W -g -MP -MD -pipe $(COMPILER_FPIC) lib_LTLIBRARIES = libadb_parser.a -libadb_parser_a_SOURCES = adb_parser.h adb_parser.cpp buf_ops.h buf_ops.cpp expr.h expr.cpp adb_expr.h adb_expr.cpp adb_db.h adb_db.cpp + +libadb_parser_a_SOURCES = buf_ops.cpp expr.cpp adb_expr.cpp adb_parser.cpp adb_db.cpp adb_config.cpp adb_exceptionHolder.cpp adb_field.cpp adb_instance.cpp adb_logfile.cpp adb_node.cpp adb_xmlCreator.cpp + diff -Nru mstflint-4.17.0+1/adb_parser/Makefile.in mstflint-4.21.0+1/adb_parser/Makefile.in --- mstflint-4.17.0+1/adb_parser/Makefile.in 2021-06-29 08:51:07.000000000 +0000 +++ mstflint-4.21.0+1/adb_parser/Makefile.in 2022-07-31 09:34:41.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.3 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2020 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -16,7 +16,7 @@ #-- # -# Copyright (C) Jan 2019 Mellanox Technologies Ltd. All rights reserved. +# Copyright (c) 2019-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU @@ -166,8 +166,10 @@ am__installdirs = "$(DESTDIR)$(libdir)" LTLIBRARIES = $(lib_LTLIBRARIES) libadb_parser_a_LIBADD = -am_libadb_parser_a_OBJECTS = adb_parser.lo buf_ops.lo expr.lo \ - adb_expr.lo adb_db.lo +am_libadb_parser_a_OBJECTS = buf_ops.lo expr.lo adb_expr.lo \ + adb_parser.lo adb_db.lo adb_config.lo adb_exceptionHolder.lo \ + adb_field.lo adb_instance.lo adb_logfile.lo adb_node.lo \ + adb_xmlCreator.lo libadb_parser_a_OBJECTS = $(am_libadb_parser_a_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -188,8 +190,12 @@ DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/config/depcomp am__maybe_remake_depfiles = depfiles -am__depfiles_remade = ./$(DEPDIR)/adb_db.Plo ./$(DEPDIR)/adb_expr.Plo \ - ./$(DEPDIR)/adb_parser.Plo ./$(DEPDIR)/buf_ops.Plo \ +am__depfiles_remade = ./$(DEPDIR)/adb_config.Plo \ + ./$(DEPDIR)/adb_db.Plo ./$(DEPDIR)/adb_exceptionHolder.Plo \ + ./$(DEPDIR)/adb_expr.Plo ./$(DEPDIR)/adb_field.Plo \ + ./$(DEPDIR)/adb_instance.Plo ./$(DEPDIR)/adb_logfile.Plo \ + ./$(DEPDIR)/adb_node.Plo ./$(DEPDIR)/adb_parser.Plo \ + ./$(DEPDIR)/adb_xmlCreator.Plo ./$(DEPDIR)/buf_ops.Plo \ ./$(DEPDIR)/expr.Plo am__mv = mv -f CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ @@ -210,24 +216,6 @@ am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = -COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ - $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ - $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ - $(AM_CFLAGS) $(CFLAGS) -AM_V_CC = $(am__v_CC_@AM_V@) -am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) -am__v_CC_0 = @echo " CC " $@; -am__v_CC_1 = -CCLD = $(CC) -LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ - $(AM_LDFLAGS) $(LDFLAGS) -o $@ -AM_V_CCLD = $(am__v_CCLD_@AM_V@) -am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) -am__v_CCLD_0 = @echo " CCLD " $@; -am__v_CCLD_1 = SOURCES = $(libadb_parser_a_SOURCES) DIST_SOURCES = $(libadb_parser_a_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ @@ -450,9 +438,9 @@ DIST_SUBDIRS = . USER_DIR = .. INCLUDES = -I$(USER_DIR) -I$(USER_DIR)/common -AM_CXXFLAGS = -Wall -W -g -MP -MD -Werror -pipe $(COMPILER_FPIC) +AM_CXXFLAGS = -Wall -W -g -MP -MD -pipe $(COMPILER_FPIC) lib_LTLIBRARIES = libadb_parser.a -libadb_parser_a_SOURCES = adb_parser.h adb_parser.cpp buf_ops.h buf_ops.cpp expr.h expr.cpp adb_expr.h adb_expr.cpp adb_db.h adb_db.cpp +libadb_parser_a_SOURCES = buf_ops.cpp expr.cpp adb_expr.cpp adb_parser.cpp adb_db.cpp adb_config.cpp adb_exceptionHolder.cpp adb_field.cpp adb_instance.cpp adb_logfile.cpp adb_node.cpp adb_xmlCreator.cpp all: all-recursive .SUFFIXES: @@ -531,9 +519,16 @@ distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adb_config.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adb_db.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adb_exceptionHolder.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adb_expr.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adb_field.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adb_instance.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adb_logfile.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adb_node.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adb_parser.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adb_xmlCreator.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buf_ops.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/expr.Plo@am__quote@ # am--include-marker @@ -771,9 +766,16 @@ mostlyclean-am distclean: distclean-recursive - -rm -f ./$(DEPDIR)/adb_db.Plo + -rm -f ./$(DEPDIR)/adb_config.Plo + -rm -f ./$(DEPDIR)/adb_db.Plo + -rm -f ./$(DEPDIR)/adb_exceptionHolder.Plo -rm -f ./$(DEPDIR)/adb_expr.Plo + -rm -f ./$(DEPDIR)/adb_field.Plo + -rm -f ./$(DEPDIR)/adb_instance.Plo + -rm -f ./$(DEPDIR)/adb_logfile.Plo + -rm -f ./$(DEPDIR)/adb_node.Plo -rm -f ./$(DEPDIR)/adb_parser.Plo + -rm -f ./$(DEPDIR)/adb_xmlCreator.Plo -rm -f ./$(DEPDIR)/buf_ops.Plo -rm -f ./$(DEPDIR)/expr.Plo -rm -f Makefile @@ -821,9 +823,16 @@ installcheck-am: maintainer-clean: maintainer-clean-recursive - -rm -f ./$(DEPDIR)/adb_db.Plo + -rm -f ./$(DEPDIR)/adb_config.Plo + -rm -f ./$(DEPDIR)/adb_db.Plo + -rm -f ./$(DEPDIR)/adb_exceptionHolder.Plo -rm -f ./$(DEPDIR)/adb_expr.Plo + -rm -f ./$(DEPDIR)/adb_field.Plo + -rm -f ./$(DEPDIR)/adb_instance.Plo + -rm -f ./$(DEPDIR)/adb_logfile.Plo + -rm -f ./$(DEPDIR)/adb_node.Plo -rm -f ./$(DEPDIR)/adb_parser.Plo + -rm -f ./$(DEPDIR)/adb_xmlCreator.Plo -rm -f ./$(DEPDIR)/buf_ops.Plo -rm -f ./$(DEPDIR)/expr.Plo -rm -f Makefile diff -Nru mstflint-4.17.0+1/autom4te.cache/output.0 mstflint-4.21.0+1/autom4te.cache/output.0 --- mstflint-4.17.0+1/autom4te.cache/output.0 2021-06-29 08:51:01.000000000 +0000 +++ mstflint-4.21.0+1/autom4te.cache/output.0 2022-07-31 09:34:39.000000000 +0000 @@ -1,6 +1,6 @@ @%:@! /bin/sh @%:@ Guess values for system-dependent variables and create Makefiles. -@%:@ Generated by GNU Autoconf 2.69 for mstflint 4.17.0. +@%:@ Generated by GNU Autoconf 2.69 for mstflint 4.21.0. @%:@ @%:@ Report bugs to . @%:@ @@ -590,8 +590,8 @@ # Identity of this package. PACKAGE_NAME='mstflint' PACKAGE_TARNAME='mstflint' -PACKAGE_VERSION='4.17.0' -PACKAGE_STRING='mstflint 4.17.0' +PACKAGE_VERSION='4.21.0' +PACKAGE_STRING='mstflint 4.21.0' PACKAGE_BUGREPORT='eranj@mellanox.co.il' PACKAGE_URL='' @@ -1391,7 +1391,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures mstflint 4.17.0 to adapt to many kinds of systems. +\`configure' configures mstflint 4.21.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1461,7 +1461,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of mstflint 4.17.0:";; + short | recursive ) echo "Configuration of mstflint 4.21.0:";; esac cat <<\_ACEOF @@ -1596,7 +1596,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -mstflint configure 4.17.0 +mstflint configure 4.21.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2177,7 +2177,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by mstflint $as_me 4.17.0, which was +It was created by mstflint $as_me 4.21.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2535,7 +2535,7 @@ cat >>confdefs.h <<_ACEOF -@%:@define VERSION "4.17.0" +@%:@define VERSION "4.21.0" _ACEOF @@ -2747,7 +2747,12 @@ am_aux_dir=`cd "$ac_aux_dir" && pwd` if test x"${MISSING+set}" != xset; then - MISSING="\${SHELL} '$am_aux_dir/missing'" + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then @@ -3052,7 +3057,7 @@ # Define the identity of the package. PACKAGE='mstflint' - VERSION='4.17.0' + VERSION='4.21.0' cat >>confdefs.h <<_ACEOF @@ -11794,6 +11799,9 @@ # before this can be enabled. hardcode_into_libs=yes + # Add ABI-specific directories to the system library path. + sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib" + # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command @@ -11802,7 +11810,7 @@ # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + sys_lib_dlsearch_path_spec="$sys_lib_dlsearch_path_spec $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on @@ -15756,6 +15764,9 @@ # before this can be enabled. hardcode_into_libs=yes + # Add ABI-specific directories to the system library path. + sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib" + # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command @@ -15764,7 +15775,7 @@ # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + sys_lib_dlsearch_path_spec="$sys_lib_dlsearch_path_spec $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on @@ -16128,9 +16139,32 @@ ac_config_headers="$ac_config_headers config.h" -OS=$(uname -s) +# OPENSSL_VERSION_NUMBER < 0x100020bf +#0x100020bfL = OpenSSL 1.0.2k - 26 Jan 2017 +MIN_OPENSSL_VER="1.0.2k" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#if OPENSSL_VERSION_NUMBER < 0x100020bf + ERROR: OPENSSL_VERSION_NUMBER version must be >= 0x100020bf ("OpenSSL 1.0.2k - 26 Jan 2017") +#endif + +int +main () +{ -MIN_OPENSSL_VER="1.1.0" + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + OPENSSL_VERSION_VALID=yes +else + OPENSSL_VERSION_VALID=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +OS=$(uname -s) MTCR_CONF_DIR="" LDL="" default_en_inband="" @@ -17062,7 +17096,7 @@ JSON_SYSTEM_AVAILABLE="no" JSON_CFLAGS='-I$(top_srcdir)/ext_libs/json' - JSON_LIBS='$(top_builddir)/ext_libs/json/libjson.a' + JSON_LIBS='$(top_builddir)/ext_libs/json/libjson.la' elif test $pkg_failed = untried; then @@ -17072,7 +17106,7 @@ JSON_SYSTEM_AVAILABLE="no" JSON_CFLAGS='-I$(top_srcdir)/ext_libs/json' - JSON_LIBS='$(top_builddir)/ext_libs/json/libjson.a' + JSON_LIBS='$(top_builddir)/ext_libs/json/libjson.la' else @@ -17237,9 +17271,21 @@ fi + CFLAGS="$CFLAGS -DMST_UL" CXXFLAGS="$CXXFLAGS -DMST_UL" + +CCVER=`${CC} --version | grep ${CC} | grep -E -o "( )[4-9]\.[0-9]\.[0-9]" | head -1` +{ $as_echo "$as_me:${as_lineno-$LINENO}: Compiler: ${CC}; version: ${CCVER};" >&5 +$as_echo "$as_me: Compiler: ${CC}; version: ${CCVER};" >&6;} +if test ${CCVER} \> 5.0.0 ; then + CXXFLAGS="$CXXFLAGS -std=c++14" + CFLAGS="$CFLAGS -fgnu89-inline" +elif test ${CCVER} \> 4.7.2 ; then + CXXFLAGS="$CXXFLAGS -std=c++11" +fi + AM_CFLAGS="-Wall" AM_CXXFLAGS="-Wall" @@ -17557,9 +17603,10 @@ fi + if test "x$enable_openssl" = "xyes"; then : - if test x"`(openssl version|awk '{print $2}';echo $MIN_OPENSSL_VER)|sort -V|head -n1`" = x"$MIN_OPENSSL_VER"; then : + if test x"$OPENSSL_VERSION_VALID" = "xyes"; then : ac_config_files="$ac_config_files mlxsign_lib/Makefile" else @@ -18219,7 +18266,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by mstflint $as_me 4.17.0, which was +This file was extended by mstflint $as_me 4.21.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -18285,7 +18332,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -mstflint config.status 4.17.0 +mstflint config.status 4.21.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -19532,9 +19579,7 @@ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "Something went wrong bootstrapping makefile fragments - for automatic dependency tracking. If GNU make was not used, consider - re-running the configure script with MAKE=\"gmake\" (or whatever is - necessary). You can also try re-running configure with the + for automatic dependency tracking. Try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking). See \`config.log' for more details" "$LINENO" 5; } diff -Nru mstflint-4.17.0+1/autom4te.cache/output.1 mstflint-4.21.0+1/autom4te.cache/output.1 --- mstflint-4.17.0+1/autom4te.cache/output.1 2021-06-29 08:51:06.000000000 +0000 +++ mstflint-4.21.0+1/autom4te.cache/output.1 2022-07-31 09:34:41.000000000 +0000 @@ -1,6 +1,6 @@ @%:@! /bin/sh @%:@ Guess values for system-dependent variables and create Makefiles. -@%:@ Generated by GNU Autoconf 2.69 for mstflint 4.17.0. +@%:@ Generated by GNU Autoconf 2.69 for mstflint 4.21.0. @%:@ @%:@ Report bugs to . @%:@ @@ -590,8 +590,8 @@ # Identity of this package. PACKAGE_NAME='mstflint' PACKAGE_TARNAME='mstflint' -PACKAGE_VERSION='4.17.0' -PACKAGE_STRING='mstflint 4.17.0' +PACKAGE_VERSION='4.21.0' +PACKAGE_STRING='mstflint 4.21.0' PACKAGE_BUGREPORT='eranj@mellanox.co.il' PACKAGE_URL='' @@ -1391,7 +1391,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures mstflint 4.17.0 to adapt to many kinds of systems. +\`configure' configures mstflint 4.21.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1461,7 +1461,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of mstflint 4.17.0:";; + short | recursive ) echo "Configuration of mstflint 4.21.0:";; esac cat <<\_ACEOF @@ -1596,7 +1596,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -mstflint configure 4.17.0 +mstflint configure 4.21.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2177,7 +2177,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by mstflint $as_me 4.17.0, which was +It was created by mstflint $as_me 4.21.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2535,7 +2535,7 @@ cat >>confdefs.h <<_ACEOF -@%:@define VERSION "4.17.0" +@%:@define VERSION "4.21.0" _ACEOF @@ -2747,7 +2747,12 @@ am_aux_dir=`cd "$ac_aux_dir" && pwd` if test x"${MISSING+set}" != xset; then - MISSING="\${SHELL} '$am_aux_dir/missing'" + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then @@ -3052,7 +3057,7 @@ # Define the identity of the package. PACKAGE='mstflint' - VERSION='4.17.0' + VERSION='4.21.0' cat >>confdefs.h <<_ACEOF @@ -11790,6 +11795,9 @@ # before this can be enabled. hardcode_into_libs=yes + # Add ABI-specific directories to the system library path. + sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib" + # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command @@ -11798,7 +11806,7 @@ # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + sys_lib_dlsearch_path_spec="$sys_lib_dlsearch_path_spec $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on @@ -15752,6 +15760,9 @@ # before this can be enabled. hardcode_into_libs=yes + # Add ABI-specific directories to the system library path. + sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib" + # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command @@ -15760,7 +15771,7 @@ # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + sys_lib_dlsearch_path_spec="$sys_lib_dlsearch_path_spec $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on @@ -16124,9 +16135,32 @@ ac_config_headers="$ac_config_headers config.h" -OS=$(uname -s) +# OPENSSL_VERSION_NUMBER < 0x100020bf +#0x100020bfL = OpenSSL 1.0.2k - 26 Jan 2017 +MIN_OPENSSL_VER="1.0.2k" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#if OPENSSL_VERSION_NUMBER < 0x100020bf + ERROR: OPENSSL_VERSION_NUMBER version must be >= 0x100020bf ("OpenSSL 1.0.2k - 26 Jan 2017") +#endif + +int +main () +{ -MIN_OPENSSL_VER="1.1.0" + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + OPENSSL_VERSION_VALID=yes +else + OPENSSL_VERSION_VALID=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +OS=$(uname -s) MTCR_CONF_DIR="" LDL="" default_en_inband="" @@ -17058,7 +17092,7 @@ JSON_SYSTEM_AVAILABLE="no" JSON_CFLAGS='-I$(top_srcdir)/ext_libs/json' - JSON_LIBS='$(top_builddir)/ext_libs/json/libjson.a' + JSON_LIBS='$(top_builddir)/ext_libs/json/libjson.la' elif test $pkg_failed = untried; then @@ -17068,7 +17102,7 @@ JSON_SYSTEM_AVAILABLE="no" JSON_CFLAGS='-I$(top_srcdir)/ext_libs/json' - JSON_LIBS='$(top_builddir)/ext_libs/json/libjson.a' + JSON_LIBS='$(top_builddir)/ext_libs/json/libjson.la' else @@ -17233,9 +17267,21 @@ fi + CFLAGS="$CFLAGS -DMST_UL" CXXFLAGS="$CXXFLAGS -DMST_UL" + +CCVER=`${CC} --version | grep ${CC} | grep -E -o "( )[4-9]\.[0-9]\.[0-9]" | head -1` +{ $as_echo "$as_me:${as_lineno-$LINENO}: Compiler: ${CC}; version: ${CCVER};" >&5 +$as_echo "$as_me: Compiler: ${CC}; version: ${CCVER};" >&6;} +if test ${CCVER} \> 5.0.0 ; then + CXXFLAGS="$CXXFLAGS -std=c++14" + CFLAGS="$CFLAGS -fgnu89-inline" +elif test ${CCVER} \> 4.7.2 ; then + CXXFLAGS="$CXXFLAGS -std=c++11" +fi + AM_CFLAGS="-Wall" AM_CXXFLAGS="-Wall" @@ -17553,9 +17599,10 @@ fi + if test "x$enable_openssl" = "xyes"; then : - if test x"`(openssl version|awk '{print $2}';echo $MIN_OPENSSL_VER)|sort -V|head -n1`" = x"$MIN_OPENSSL_VER"; then : + if test x"$OPENSSL_VERSION_VALID" = "xyes"; then : ac_config_files="$ac_config_files mlxsign_lib/Makefile" else @@ -18215,7 +18262,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by mstflint $as_me 4.17.0, which was +This file was extended by mstflint $as_me 4.21.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -18281,7 +18328,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -mstflint config.status 4.17.0 +mstflint config.status 4.21.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -19528,9 +19575,7 @@ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "Something went wrong bootstrapping makefile fragments - for automatic dependency tracking. If GNU make was not used, consider - re-running the configure script with MAKE=\"gmake\" (or whatever is - necessary). You can also try re-running configure with the + for automatic dependency tracking. Try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking). See \`config.log' for more details" "$LINENO" 5; } diff -Nru mstflint-4.17.0+1/autom4te.cache/requests mstflint-4.21.0+1/autom4te.cache/requests --- mstflint-4.17.0+1/autom4te.cache/requests 2021-06-29 08:51:15.000000000 +0000 +++ mstflint-4.21.0+1/autom4te.cache/requests 2022-07-31 09:34:43.000000000 +0000 @@ -11,227 +11,231 @@ ], [ '/usr/share/autoconf/autoconf/autoconf.m4f', - '/usr/local/share/aclocal-1.16/internal/ac-config-macro-dirs.m4', - '/usr/local/share/aclocal/libtool.m4', - '/usr/local/share/aclocal/ltargz.m4', - '/usr/local/share/aclocal/ltdl.m4', - '/usr/local/share/aclocal/ltoptions.m4', - '/usr/local/share/aclocal/ltsugar.m4', - '/usr/local/share/aclocal/ltversion.m4', - '/usr/local/share/aclocal/lt~obsolete.m4', - '/usr/local/share/aclocal/pkg.m4', - '/usr/local/share/aclocal-1.16/amversion.m4', - '/usr/local/share/aclocal-1.16/ar-lib.m4', - '/usr/local/share/aclocal-1.16/auxdir.m4', - '/usr/local/share/aclocal-1.16/cond.m4', - '/usr/local/share/aclocal-1.16/depend.m4', - '/usr/local/share/aclocal-1.16/depout.m4', - '/usr/local/share/aclocal-1.16/init.m4', - '/usr/local/share/aclocal-1.16/install-sh.m4', - '/usr/local/share/aclocal-1.16/lead-dot.m4', - '/usr/local/share/aclocal-1.16/make.m4', - '/usr/local/share/aclocal-1.16/missing.m4', - '/usr/local/share/aclocal-1.16/options.m4', - '/usr/local/share/aclocal-1.16/prog-cc-c-o.m4', - '/usr/local/share/aclocal-1.16/runlog.m4', - '/usr/local/share/aclocal-1.16/sanity.m4', - '/usr/local/share/aclocal-1.16/silent.m4', - '/usr/local/share/aclocal-1.16/strip.m4', - '/usr/local/share/aclocal-1.16/substnot.m4', - '/usr/local/share/aclocal-1.16/tar.m4', + '-', + '/usr/share/aclocal-1.16/internal/ac-config-macro-dirs.m4', + '/usr/share/aclocal/libtool.m4', + '/usr/share/aclocal/ltargz.m4', + '/usr/share/aclocal/ltdl.m4', + '/usr/share/aclocal/ltoptions.m4', + '/usr/share/aclocal/ltsugar.m4', + '/usr/share/aclocal/ltversion.m4', + '/usr/share/aclocal/lt~obsolete.m4', + '/usr/share/aclocal/pkg.m4', + '/usr/share/aclocal-1.16/amversion.m4', + '/usr/share/aclocal-1.16/ar-lib.m4', + '/usr/share/aclocal-1.16/auxdir.m4', + '/usr/share/aclocal-1.16/cond.m4', + '/usr/share/aclocal-1.16/depend.m4', + '/usr/share/aclocal-1.16/depout.m4', + '/usr/share/aclocal-1.16/init.m4', + '/usr/share/aclocal-1.16/install-sh.m4', + '/usr/share/aclocal-1.16/lead-dot.m4', + '/usr/share/aclocal-1.16/make.m4', + '/usr/share/aclocal-1.16/missing.m4', + '/usr/share/aclocal-1.16/options.m4', + '/usr/share/aclocal-1.16/prog-cc-c-o.m4', + '/usr/share/aclocal-1.16/runlog.m4', + '/usr/share/aclocal-1.16/sanity.m4', + '/usr/share/aclocal-1.16/silent.m4', + '/usr/share/aclocal-1.16/strip.m4', + '/usr/share/aclocal-1.16/substnot.m4', + '/usr/share/aclocal-1.16/tar.m4', 'm4/ax_check_compile_flag.m4', 'configure.ac' ], { - 'AM_ENABLE_STATIC' => 1, - 'AC_LIBTOOL_LANG_RC_CONFIG' => 1, - '_LT_AC_SHELL_INIT' => 1, - 'AC_DEFUN' => 1, + 'PKG_WITH_MODULES' => 1, + 'LTOPTIONS_VERSION' => 1, + 'AC_LIBTOOL_LINKER_OPTION' => 1, '_LT_AC_LANG_CXX_CONFIG' => 1, - 'AC_PROG_LIBTOOL' => 1, + 'AM_MAKE_INCLUDE' => 1, + 'PKG_CHECK_MODULES' => 1, + 'AC_LIBTOOL_PICMODE' => 1, + '_LT_AC_TRY_DLOPEN_SELF' => 1, + '_LT_PATH_TOOL_PREFIX' => 1, + 'PKG_INSTALLDIR' => 1, + '_LT_AC_FILE_LTDLL_C' => 1, + 'PKG_CHECK_EXISTS' => 1, + '_LT_PROG_CXX' => 1, + '_AM_PROG_TAR' => 1, + '_LT_AC_LANG_GCJ_CONFIG' => 1, + '_AM_OUTPUT_DEPENDENCY_COMMANDS' => 1, + '_LT_LINKER_OPTION' => 1, + 'include' => 1, + '_LTDL_SETUP' => 1, + 'AC_LIBTOOL_LANG_RC_CONFIG' => 1, + '_LT_AC_SYS_COMPILER' => 1, + 'AC_LTDL_PREOPEN' => 1, + 'AC_LIBTOOL_SYS_MAX_CMD_LEN' => 1, + 'AC_PROG_NM' => 1, 'AM_AUTOMAKE_VERSION' => 1, - 'AM_SUBST_NOTMAKE' => 1, - 'AM_MISSING_PROG' => 1, - 'AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH' => 1, - '_LT_AC_LANG_C_CONFIG' => 1, - 'AM_PROG_INSTALL_STRIP' => 1, - '_m4_warn' => 1, - 'AC_LIBTOOL_OBJDIR' => 1, - 'AM_SANITY_CHECK' => 1, - 'LTOBSOLETE_VERSION' => 1, - 'AC_LIBTOOL_LANG_GCJ_CONFIG' => 1, - 'AC_LIBTOOL_PROG_COMPILER_PIC' => 1, 'LT_LIB_M' => 1, - '_LT_AC_CHECK_DLFCN' => 1, - 'AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE' => 1, + 'AM_ENABLE_STATIC' => 1, + 'AC_LIBTOOL_OBJDIR' => 1, + 'LT_SUPPORTED_TAG' => 1, 'LTSUGAR_VERSION' => 1, - '_LT_PROG_LTMAIN' => 1, - 'LT_SYS_SYMBOL_USCORE' => 1, - '_AM_PROG_TAR' => 1, - 'AC_LIBTOOL_GCJ' => 1, - '_LT_WITH_SYSROOT' => 1, - 'LT_FUNC_DLSYM_USCORE' => 1, - 'LT_SYS_DLOPEN_DEPLIBS' => 1, - '_LT_AC_LANG_F77' => 1, - 'AC_LIBTOOL_CONFIG' => 1, - 'AC_CONFIG_MACRO_DIR' => 1, - 'AC_LTDL_DLLIB' => 1, - '_AM_SUBST_NOTMAKE' => 1, + 'AC_LIBTOOL_WIN32_DLL' => 1, + 'AC_DEFUN_ONCE' => 1, + 'AC_LTDL_DLSYM_USCORE' => 1, + 'LT_AC_PROG_EGREP' => 1, + 'AC_DEPLIBS_CHECK_METHOD' => 1, + 'AC_CONFIG_MACRO_DIR_TRACE' => 1, + 'AC_ENABLE_STATIC' => 1, + 'LTOBSOLETE_VERSION' => 1, '_AM_AUTOCONF_VERSION' => 1, - 'AM_DISABLE_SHARED' => 1, - '_LT_PROG_ECHO_BACKSLASH' => 1, - '_LTDL_SETUP' => 1, - 'PKG_CHECK_VAR' => 1, - 'AM_PROG_LIBTOOL' => 1, - '_LT_AC_LANG_CXX' => 1, - 'AM_PROG_LD' => 1, - '_LT_AC_FILE_LTDLL_C' => 1, - 'AC_LIB_LTDL' => 1, - 'AU_DEFUN' => 1, - 'AC_PROG_NM' => 1, + '_AM_CONFIG_MACRO_DIRS' => 1, + '_m4_warn' => 1, + 'LT_OUTPUT' => 1, + 'AM_SILENT_RULES' => 1, + 'LT_PATH_NM' => 1, + 'AM_AUX_DIR_EXPAND' => 1, 'AC_LIBTOOL_DLOPEN' => 1, + '_LT_DLL_DEF_P' => 1, + 'AC_LIBTOOL_CXX' => 1, + 'AC_LIBLTDL_INSTALLABLE' => 1, 'AC_PROG_LD' => 1, - 'AC_PROG_LD_GNU' => 1, - 'AC_ENABLE_FAST_INSTALL' => 1, - 'AC_LIBTOOL_FC' => 1, - 'LTDL_CONVENIENCE' => 1, - '_AM_SET_OPTION' => 1, - 'AC_LTDL_PREOPEN' => 1, - '_LT_LINKER_BOILERPLATE' => 1, - '_LT_PREPARE_SED_QUOTE_VARS' => 1, + 'AC_LIBTOOL_CONFIG' => 1, + 'AM_CONDITIONAL' => 1, + 'LTDL_INIT' => 1, + 'AM_SET_DEPDIR' => 1, + 'AC_LIBTOOL_COMPILER_OPTION' => 1, + 'AC_PATH_MAGIC' => 1, + '_LT_PROG_FC' => 1, + 'LT_SYS_DLOPEN_DEPLIBS' => 1, + 'AC_DISABLE_SHARED' => 1, 'AC_LIBTOOL_LANG_CXX_CONFIG' => 1, - 'AC_LIBTOOL_PROG_CC_C_O' => 1, - 'LT_SUPPORTED_TAG' => 1, - 'AM_OUTPUT_DEPENDENCY_COMMANDS' => 1, - 'LT_PROG_RC' => 1, - 'LT_SYS_MODULE_EXT' => 1, - 'AC_DEFUN_ONCE' => 1, + 'LT_AC_PROG_RC' => 1, + 'LTDL_INSTALLABLE' => 1, + 'AM_PROG_CC_C_O' => 1, + 'AM_MISSING_HAS_RUN' => 1, + 'm4_pattern_forbid' => 1, 'AX_CHECK_COMPILE_FLAG' => 1, - '_LT_AC_LANG_GCJ' => 1, + 'AC_LIBTOOL_PROG_COMPILER_PIC' => 1, + 'm4_pattern_allow' => 1, + '_LT_COMPILER_BOILERPLATE' => 1, + '_LT_COMPILER_OPTION' => 1, + 'AM_SANITY_CHECK' => 1, + 'AC_LIBTOOL_LANG_F77_CONFIG' => 1, + '_LT_AC_TAGVAR' => 1, + '_AM_PROG_CC_C_O' => 1, + 'AM_ENABLE_SHARED' => 1, + 'PKG_CHECK_VAR' => 1, 'AC_LTDL_OBJDIR' => 1, - '_LT_PATH_TOOL_PREFIX' => 1, - 'AC_LIBTOOL_RC' => 1, + '_LT_AC_LANG_F77' => 1, + 'LT_PROG_GO' => 1, + 'AC_LIBTOOL_POSTDEP_PREDEP' => 1, + 'AC_LIBTOOL_PROG_COMPILER_NO_RTTI' => 1, + 'AM_RUN_LOG' => 1, + 'AC_LIBTOOL_LANG_GCJ_CONFIG' => 1, '_LT_AC_PROG_ECHO_BACKSLASH' => 1, - 'AC_DISABLE_FAST_INSTALL' => 1, - 'AM_SILENT_RULES' => 1, - 'include' => 1, - '_LT_AC_TRY_DLOPEN_SELF' => 1, + 'AC_LTDL_SHLIBPATH' => 1, + 'AC_PROG_LD_GNU' => 1, + 'AM_DEP_TRACK' => 1, + 'PKG_HAVE_DEFINE_WITH_MODULES' => 1, + 'LTDL_CONVENIENCE' => 1, + 'AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH' => 1, + 'PKG_HAVE_WITH_MODULES' => 1, + 'AC_DEFUN' => 1, + 'PKG_PROG_PKG_CONFIG' => 1, + 'LT_INIT' => 1, + 'LT_CONFIG_LTDL_DIR' => 1, + 'LTVERSION_VERSION' => 1, + 'LT_SYS_MODULE_PATH' => 1, + 'AM_DISABLE_STATIC' => 1, + 'AC_PROG_LIBTOOL' => 1, + 'LT_LANG' => 1, + 'LT_SYS_SYMBOL_USCORE' => 1, '_LT_AC_SYS_LIBPATH_AIX' => 1, - 'LT_AC_PROG_SED' => 1, - 'AM_ENABLE_SHARED' => 1, - 'AM_PROG_AR' => 1, - 'LTDL_INSTALLABLE' => 1, - '_LT_AC_LANG_GCJ_CONFIG' => 1, - 'AC_ENABLE_SHARED' => 1, - '_LT_REQUIRED_DARWIN_CHECKS' => 1, - 'AC_LIBTOOL_SYS_HARD_LINK_LOCKS' => 1, - 'AC_ENABLE_STATIC' => 1, - '_LT_AC_TAGVAR' => 1, - 'AM_PROG_CC_C_O' => 1, - 'AC_LIBTOOL_LANG_F77_CONFIG' => 1, - 'AM_CONDITIONAL' => 1, + '_AM_DEPENDENCIES' => 1, + 'AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE' => 1, + 'AC_LTDL_SYMBOL_USCORE' => 1, + 'AC_LTDL_SYS_DLOPEN_DEPLIBS' => 1, 'LT_LIB_DLLOAD' => 1, - 'LTVERSION_VERSION' => 1, - '_LT_PROG_CXX' => 1, - '_LT_PROG_F77' => 1, - 'LTDL_INIT' => 1, 'm4_include' => 1, + 'AC_LIBTOOL_SYS_DYNAMIC_LINKER' => 1, + 'AM_PROG_LD' => 1, + '_AM_IF_OPTION' => 1, + '_LT_AC_LOCK' => 1, + 'LT_PROG_RC' => 1, 'AM_PROG_INSTALL_SH' => 1, + '_LT_PROG_F77' => 1, 'AC_PROG_EGREP' => 1, - 'AC_PATH_MAGIC' => 1, - '_AC_AM_CONFIG_HEADER_HOOK' => 1, - 'AC_LTDL_SYSSEARCHPATH' => 1, - 'AM_MAKE_INCLUDE' => 1, - 'LT_CMD_MAX_LEN' => 1, - '_LT_AC_TAGCONFIG' => 1, - 'm4_pattern_forbid' => 1, - '_LT_LINKER_OPTION' => 1, - 'AC_LIBTOOL_COMPILER_OPTION' => 1, - 'PKG_INSTALLDIR' => 1, - 'AC_DISABLE_SHARED' => 1, - '_LT_COMPILER_BOILERPLATE' => 1, - 'AC_LIBTOOL_WIN32_DLL' => 1, + 'LT_SYS_DLOPEN_SELF' => 1, + 'AC_ENABLE_SHARED' => 1, + '_PKG_SHORT_ERRORS_SUPPORTED' => 1, 'AC_LIBTOOL_SETUP' => 1, + 'AC_DISABLE_FAST_INSTALL' => 1, + '_AC_PROG_LIBTOOL' => 1, + 'LT_AC_PROG_SED' => 1, 'AC_PROG_LD_RELOAD_FLAG' => 1, - 'AC_LTDL_DLSYM_USCORE' => 1, - 'AM_MISSING_HAS_RUN' => 1, - 'LT_LANG' => 1, - 'LT_SYS_DLSEARCH_PATH' => 1, - 'LT_CONFIG_LTDL_DIR' => 1, - 'AC_LIBTOOL_DLOPEN_SELF' => 1, - 'LT_OUTPUT' => 1, + 'AC_CONFIG_MACRO_DIR' => 1, 'AC_LIBTOOL_PROG_LD_SHLIBS' => 1, - '_AM_CONFIG_MACRO_DIRS' => 1, - '_PKG_SHORT_ERRORS_SUPPORTED' => 1, - 'LT_FUNC_ARGZ' => 1, - 'AC_WITH_LTDL' => 1, - 'AC_LIBTOOL_LINKER_OPTION' => 1, - 'PKG_CHECK_EXISTS' => 1, - 'LT_AC_PROG_RC' => 1, - 'AC_LIBTOOL_CXX' => 1, - 'LT_INIT' => 1, - 'LT_AC_PROG_GCJ' => 1, - 'LT_SYS_DLOPEN_SELF' => 1, + '_LT_AC_LANG_F77_CONFIG' => 1, + 'AM_SUBST_NOTMAKE' => 1, + 'AC_LIBTOOL_F77' => 1, + 'AM_DISABLE_SHARED' => 1, + '_LT_LIBOBJ' => 1, + '_LT_AC_LANG_CXX' => 1, + 'AC_LIBTOOL_DLOPEN_SELF' => 1, + '_LT_LINKER_BOILERPLATE' => 1, + 'AM_SET_CURRENT_AUTOMAKE_VERSION' => 1, + 'LT_FUNC_DLSYM_USCORE' => 1, + '_LT_AC_LANG_RC_CONFIG' => 1, '_LT_AC_PROG_CXXCPP' => 1, - 'AM_DEP_TRACK' => 1, - 'AM_DISABLE_STATIC' => 1, - '_AC_PROG_LIBTOOL' => 1, - '_AM_IF_OPTION' => 1, + '_LT_PROG_LTMAIN' => 1, 'PKG_NOARCH_INSTALLDIR' => 1, + 'AC_WITH_LTDL' => 1, + 'LT_FUNC_ARGZ' => 1, 'AC_PATH_TOOL_PREFIX' => 1, - 'm4_pattern_allow' => 1, - 'AC_LIBTOOL_F77' => 1, - 'AM_SET_LEADING_DOT' => 1, - '_LT_PROG_FC' => 1, - 'LT_AC_PROG_EGREP' => 1, - '_AM_DEPENDENCIES' => 1, - 'AC_LIBTOOL_LANG_C_CONFIG' => 1, - 'LTOPTIONS_VERSION' => 1, - '_LT_AC_SYS_COMPILER' => 1, - 'AM_PROG_NM' => 1, - '_AM_PROG_CC_C_O' => 1, - 'PKG_CHECK_MODULES' => 1, - 'AC_LIBLTDL_CONVENIENCE' => 1, - 'AC_DEPLIBS_CHECK_METHOD' => 1, - 'AC_LIBLTDL_INSTALLABLE' => 1, - 'AM_SET_CURRENT_AUTOMAKE_VERSION' => 1, + '_LT_PREPARE_SED_QUOTE_VARS' => 1, + '_LT_REQUIRED_DARWIN_CHECKS' => 1, + 'AC_LIB_LTDL' => 1, + 'AM_PROG_INSTALL_STRIP' => 1, + 'LT_CMD_MAX_LEN' => 1, + 'AC_LIBTOOL_FC' => 1, + 'AU_DEFUN' => 1, + 'AC_LIBTOOL_SYS_LIB_STRIP' => 1, + '_AM_MANGLE_OPTION' => 1, + 'AC_LIBTOOL_PROG_CC_C_O' => 1, + '_AM_SUBST_NOTMAKE' => 1, + 'LT_PATH_LD' => 1, + 'LT_SYS_DLSEARCH_PATH' => 1, + '_LT_PROG_ECHO_BACKSLASH' => 1, + '_LT_AC_SHELL_INIT' => 1, 'AC_LTDL_ENABLE_INSTALL' => 1, - 'LT_PROG_GCJ' => 1, - 'AC_LIBTOOL_SYS_DYNAMIC_LINKER' => 1, - 'AM_INIT_AUTOMAKE' => 1, - 'AC_DISABLE_STATIC' => 1, - 'LT_PATH_NM' => 1, - 'AC_LTDL_SHLIBEXT' => 1, - '_LT_AC_LOCK' => 1, - '_LT_AC_LANG_RC_CONFIG' => 1, - 'LT_PROG_GO' => 1, - '_LT_DLL_DEF_P' => 1, - 'LT_SYS_MODULE_PATH' => 1, + '_LT_CC_BASENAME' => 1, + 'LT_AC_PROG_GCJ' => 1, + '_LT_AC_LANG_C_CONFIG' => 1, + 'AC_LIBTOOL_SYS_OLD_ARCHIVE' => 1, + '_AM_SET_OPTIONS' => 1, 'PKG_CHECK_MODULES_STATIC' => 1, + 'LT_PROG_GCJ' => 1, + 'AC_LIBTOOL_RC' => 1, + 'AC_LIBTOOL_GCJ' => 1, + 'AM_OUTPUT_DEPENDENCY_COMMANDS' => 1, + '_AC_AM_CONFIG_HEADER_HOOK' => 1, + 'LT_SYS_MODULE_EXT' => 1, + 'AM_PROG_NM' => 1, + 'AM_SET_LEADING_DOT' => 1, + '_LT_WITH_SYSROOT' => 1, + 'AC_LTDL_DLLIB' => 1, + '_LT_AC_TAGCONFIG' => 1, 'LT_WITH_LTDL' => 1, - 'AC_LIBTOOL_POSTDEP_PREDEP' => 1, - 'AC_LTDL_SHLIBPATH' => 1, - 'AM_AUX_DIR_EXPAND' => 1, - 'AC_LIBTOOL_PROG_COMPILER_NO_RTTI' => 1, - '_LT_AC_LANG_F77_CONFIG' => 1, - '_LT_COMPILER_OPTION' => 1, - '_AM_SET_OPTIONS' => 1, - 'AM_RUN_LOG' => 1, - '_AM_OUTPUT_DEPENDENCY_COMMANDS' => 1, - 'AC_LIBTOOL_PICMODE' => 1, - 'AC_LTDL_SYS_DLOPEN_DEPLIBS' => 1, - 'AC_LIBTOOL_SYS_OLD_ARCHIVE' => 1, + 'AM_MISSING_PROG' => 1, 'AC_CHECK_LIBM' => 1, - 'LT_PATH_LD' => 1, - 'AC_LIBTOOL_SYS_LIB_STRIP' => 1, - '_AM_MANGLE_OPTION' => 1, - 'AC_CONFIG_MACRO_DIR_TRACE' => 1, - 'AC_LIBTOOL_SYS_MAX_CMD_LEN' => 1, - 'AC_LTDL_SYMBOL_USCORE' => 1, - 'AM_SET_DEPDIR' => 1, - 'PKG_PROG_PKG_CONFIG' => 1, - '_LT_CC_BASENAME' => 1, - '_LT_LIBOBJ' => 1 + '_LT_AC_CHECK_DLFCN' => 1, + 'AC_LTDL_SHLIBEXT' => 1, + 'AM_PROG_AR' => 1, + 'AM_INIT_AUTOMAKE' => 1, + 'AC_DISABLE_STATIC' => 1, + 'AC_ENABLE_FAST_INSTALL' => 1, + 'AM_PROG_LIBTOOL' => 1, + 'AC_LIBTOOL_LANG_C_CONFIG' => 1, + 'AC_LIBLTDL_CONVENIENCE' => 1, + '_AM_SET_OPTION' => 1, + 'AC_LTDL_SYSSEARCHPATH' => 1, + '_LT_AC_LANG_GCJ' => 1, + 'AC_LIBTOOL_SYS_HARD_LINK_LOCKS' => 1 } ], 'Autom4te::Request' ), bless( [ @@ -246,65 +250,65 @@ 'configure.ac' ], { - '_LT_AC_TAGCONFIG' => 1, - 'AM_PROG_F77_C_O' => 1, + 'AC_CONFIG_SUBDIRS' => 1, + 'AC_SUBST' => 1, + 'AC_CANONICAL_TARGET' => 1, + 'sinclude' => 1, 'AC_INIT' => 1, - 'm4_pattern_forbid' => 1, '_AM_COND_IF' => 1, - 'AC_CANONICAL_TARGET' => 1, - 'AC_SUBST' => 1, - 'AC_CONFIG_LIBOBJ_DIR' => 1, - 'AM_EXTRA_RECURSIVE_TARGETS' => 1, - 'AC_FC_SRCEXT' => 1, - 'AC_CANONICAL_HOST' => 1, - 'AC_PROG_LIBTOOL' => 1, - 'AM_PROG_MKDIR_P' => 1, - 'AM_INIT_AUTOMAKE' => 1, - 'AM_PATH_GUILE' => 1, - 'AC_CONFIG_SUBDIRS' => 1, + '_LT_AC_TAGCONFIG' => 1, 'AM_AUTOMAKE_VERSION' => 1, - 'LT_CONFIG_LTDL_DIR' => 1, - 'AC_REQUIRE_AUX_FILE' => 1, - 'AC_CONFIG_LINKS' => 1, - 'm4_sinclude' => 1, + 'AC_CANONICAL_SYSTEM' => 1, + 'm4_include' => 1, + 'AC_SUBST_TRACE' => 1, 'LT_SUPPORTED_TAG' => 1, - 'AM_MAINTAINER_MODE' => 1, - 'AM_NLS' => 1, - 'AC_FC_PP_DEFINE' => 1, + 'AM_POT_TOOLS' => 1, 'AM_GNU_GETTEXT_INTL_SUBDIR' => 1, - 'AM_MAKEFILE_INCLUDE' => 1, + 'AC_REQUIRE_AUX_FILE' => 1, + 'AM_EXTRA_RECURSIVE_TARGETS' => 1, + '_AM_COND_ELSE' => 1, + 'AC_CONFIG_HEADERS' => 1, + 'AM_CONDITIONAL' => 1, + 'AM_PROG_MKDIR_P' => 1, + 'AC_CONFIG_LIBOBJ_DIR' => 1, '_m4_warn' => 1, - 'AM_PROG_CXX_C_O' => 1, - '_AM_COND_ENDIF' => 1, + 'AM_PROG_F77_C_O' => 1, + 'AC_LIBSOURCE' => 1, + 'AM_PROG_AR' => 1, '_AM_MAKEFILE_INCLUDE' => 1, - 'AM_ENABLE_MULTILIB' => 1, - 'AM_PROG_MOC' => 1, + 'AM_XGETTEXT_OPTION' => 1, 'AM_SILENT_RULES' => 1, - 'AC_CONFIG_FILES' => 1, - 'include' => 1, + 'AM_INIT_AUTOMAKE' => 1, + 'AM_PROG_FC_C_O' => 1, + 'm4_pattern_forbid' => 1, 'LT_INIT' => 1, - 'AM_PROG_AR' => 1, + 'LT_CONFIG_LTDL_DIR' => 1, 'AM_GNU_GETTEXT' => 1, - 'AC_LIBSOURCE' => 1, - 'AM_PROG_FC_C_O' => 1, + 'AC_CONFIG_FILES' => 1, + 'AC_FC_PP_DEFINE' => 1, 'AC_CANONICAL_BUILD' => 1, - 'AC_FC_FREEFORM' => 1, - 'AH_OUTPUT' => 1, - 'AC_FC_PP_SRCEXT' => 1, - '_AM_SUBST_NOTMAKE' => 1, 'AC_CONFIG_AUX_DIR' => 1, - 'sinclude' => 1, - 'AM_PROG_CC_C_O' => 1, 'm4_pattern_allow' => 1, - 'AM_XGETTEXT_OPTION' => 1, - 'AC_CANONICAL_SYSTEM' => 1, - 'AM_CONDITIONAL' => 1, - 'AC_CONFIG_HEADERS' => 1, + 'AH_OUTPUT' => 1, + 'AC_FC_SRCEXT' => 1, + 'AM_PROG_MOC' => 1, + 'AM_PROG_CC_C_O' => 1, + 'AM_NLS' => 1, + 'include' => 1, 'AC_DEFINE_TRACE_LITERAL' => 1, - 'AM_POT_TOOLS' => 1, - 'm4_include' => 1, - '_AM_COND_ELSE' => 1, - 'AC_SUBST_TRACE' => 1 + 'AM_PROG_CXX_C_O' => 1, + 'AC_PROG_LIBTOOL' => 1, + 'AM_ENABLE_MULTILIB' => 1, + 'AM_MAKEFILE_INCLUDE' => 1, + 'AC_CONFIG_LINKS' => 1, + 'AM_PATH_GUILE' => 1, + 'AC_FC_FREEFORM' => 1, + 'm4_sinclude' => 1, + 'AC_CANONICAL_HOST' => 1, + '_AM_SUBST_NOTMAKE' => 1, + 'AC_FC_PP_SRCEXT' => 1, + 'AM_MAINTAINER_MODE' => 1, + '_AM_COND_ENDIF' => 1 } ], 'Autom4te::Request' ) ); diff -Nru mstflint-4.17.0+1/autom4te.cache/traces.0 mstflint-4.21.0+1/autom4te.cache/traces.0 --- mstflint-4.17.0+1/autom4te.cache/traces.0 2021-06-29 08:51:01.000000000 +0000 +++ mstflint-4.21.0+1/autom4te.cache/traces.0 2022-07-31 09:34:39.000000000 +0000 @@ -1,4 +1,4 @@ -m4trace:/usr/local/share/aclocal/libtool.m4:61: -1- AC_DEFUN([LT_INIT], [AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK +m4trace:/usr/share/aclocal/libtool.m4:61: -1- AC_DEFUN([LT_INIT], [AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl AC_BEFORE([$0], [LT_LANG])dnl AC_BEFORE([$0], [LT_OUTPUT])dnl @@ -33,15 +33,15 @@ # Only expand once: m4_define([LT_INIT]) ]) -m4trace:/usr/local/share/aclocal/libtool.m4:99: -1- AU_DEFUN([AC_PROG_LIBTOOL], [m4_if($#, 0, [LT_INIT], [LT_INIT($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:99: -1- AC_DEFUN([AC_PROG_LIBTOOL], [AC_DIAGNOSE([obsolete], [The macro `AC_PROG_LIBTOOL' is obsolete. +m4trace:/usr/share/aclocal/libtool.m4:99: -1- AU_DEFUN([AC_PROG_LIBTOOL], [m4_if($#, 0, [LT_INIT], [LT_INIT($@)])]) +m4trace:/usr/share/aclocal/libtool.m4:99: -1- AC_DEFUN([AC_PROG_LIBTOOL], [AC_DIAGNOSE([obsolete], [The macro `AC_PROG_LIBTOOL' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [LT_INIT], [LT_INIT($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:100: -1- AU_DEFUN([AM_PROG_LIBTOOL], [m4_if($#, 0, [LT_INIT], [LT_INIT($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:100: -1- AC_DEFUN([AM_PROG_LIBTOOL], [AC_DIAGNOSE([obsolete], [The macro `AM_PROG_LIBTOOL' is obsolete. +m4trace:/usr/share/aclocal/libtool.m4:100: -1- AU_DEFUN([AM_PROG_LIBTOOL], [m4_if($#, 0, [LT_INIT], [LT_INIT($@)])]) +m4trace:/usr/share/aclocal/libtool.m4:100: -1- AC_DEFUN([AM_PROG_LIBTOOL], [AC_DIAGNOSE([obsolete], [The macro `AM_PROG_LIBTOOL' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [LT_INIT], [LT_INIT($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:619: -1- AC_DEFUN([LT_OUTPUT], [: ${CONFIG_LT=./config.lt} +m4trace:/usr/share/aclocal/libtool.m4:619: -1- AC_DEFUN([LT_OUTPUT], [: ${CONFIG_LT=./config.lt} AC_MSG_NOTICE([creating $CONFIG_LT]) _LT_GENERATED_FILE_INIT(["$CONFIG_LT"], [# Run this file to recreate a libtool stub with the current configuration.]) @@ -126,8 +126,8 @@ exec AS_MESSAGE_LOG_FD>>config.log $lt_cl_success || AS_EXIT(1) ]) -m4trace:/usr/local/share/aclocal/libtool.m4:812: -1- AC_DEFUN([LT_SUPPORTED_TAG], []) -m4trace:/usr/local/share/aclocal/libtool.m4:823: -1- AC_DEFUN([LT_LANG], [AC_BEFORE([$0], [LT_OUTPUT])dnl +m4trace:/usr/share/aclocal/libtool.m4:812: -1- AC_DEFUN([LT_SUPPORTED_TAG], []) +m4trace:/usr/share/aclocal/libtool.m4:823: -1- AC_DEFUN([LT_LANG], [AC_BEFORE([$0], [LT_OUTPUT])dnl m4_case([$1], [C], [_LT_LANG(C)], [C++], [_LT_LANG(CXX)], @@ -140,27 +140,27 @@ [_LT_LANG($1)], [m4_fatal([$0: unsupported language: "$1"])])])dnl ]) -m4trace:/usr/local/share/aclocal/libtool.m4:915: -1- AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) -m4trace:/usr/local/share/aclocal/libtool.m4:915: -1- AC_DEFUN([AC_LIBTOOL_CXX], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_CXX' is obsolete. +m4trace:/usr/share/aclocal/libtool.m4:915: -1- AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) +m4trace:/usr/share/aclocal/libtool.m4:915: -1- AC_DEFUN([AC_LIBTOOL_CXX], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_CXX' is obsolete. You should run autoupdate.])dnl LT_LANG(C++)]) -m4trace:/usr/local/share/aclocal/libtool.m4:916: -1- AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) -m4trace:/usr/local/share/aclocal/libtool.m4:916: -1- AC_DEFUN([AC_LIBTOOL_F77], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_F77' is obsolete. +m4trace:/usr/share/aclocal/libtool.m4:916: -1- AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) +m4trace:/usr/share/aclocal/libtool.m4:916: -1- AC_DEFUN([AC_LIBTOOL_F77], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_F77' is obsolete. You should run autoupdate.])dnl LT_LANG(Fortran 77)]) -m4trace:/usr/local/share/aclocal/libtool.m4:917: -1- AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) -m4trace:/usr/local/share/aclocal/libtool.m4:917: -1- AC_DEFUN([AC_LIBTOOL_FC], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_FC' is obsolete. +m4trace:/usr/share/aclocal/libtool.m4:917: -1- AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) +m4trace:/usr/share/aclocal/libtool.m4:917: -1- AC_DEFUN([AC_LIBTOOL_FC], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_FC' is obsolete. You should run autoupdate.])dnl LT_LANG(Fortran)]) -m4trace:/usr/local/share/aclocal/libtool.m4:918: -1- AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) -m4trace:/usr/local/share/aclocal/libtool.m4:918: -1- AC_DEFUN([AC_LIBTOOL_GCJ], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_GCJ' is obsolete. +m4trace:/usr/share/aclocal/libtool.m4:918: -1- AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) +m4trace:/usr/share/aclocal/libtool.m4:918: -1- AC_DEFUN([AC_LIBTOOL_GCJ], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_GCJ' is obsolete. You should run autoupdate.])dnl LT_LANG(Java)]) -m4trace:/usr/local/share/aclocal/libtool.m4:919: -1- AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) -m4trace:/usr/local/share/aclocal/libtool.m4:919: -1- AC_DEFUN([AC_LIBTOOL_RC], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_RC' is obsolete. +m4trace:/usr/share/aclocal/libtool.m4:919: -1- AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) +m4trace:/usr/share/aclocal/libtool.m4:919: -1- AC_DEFUN([AC_LIBTOOL_RC], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_RC' is obsolete. You should run autoupdate.])dnl LT_LANG(Windows Resource)]) -m4trace:/usr/local/share/aclocal/libtool.m4:1247: -1- AC_DEFUN([_LT_WITH_SYSROOT], [AC_MSG_CHECKING([for sysroot]) +m4trace:/usr/share/aclocal/libtool.m4:1247: -1- AC_DEFUN([_LT_WITH_SYSROOT], [AC_MSG_CHECKING([for sysroot]) AC_ARG_WITH([sysroot], [AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@], [Search for dependent libraries within DIR (or the compiler's sysroot @@ -190,7 +190,7 @@ AC_MSG_RESULT([${lt_sysroot:-no}]) _LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl [dependent libraries, and where our libraries should be installed.])]) -m4trace:/usr/local/share/aclocal/libtool.m4:1578: -1- AC_DEFUN([_LT_COMPILER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4trace:/usr/share/aclocal/libtool.m4:1578: -1- AC_DEFUN([_LT_COMPILER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no @@ -229,11 +229,11 @@ m4_if([$6], , :, [$6]) fi ]) -m4trace:/usr/local/share/aclocal/libtool.m4:1620: -1- AU_DEFUN([AC_LIBTOOL_COMPILER_OPTION], [m4_if($#, 0, [_LT_COMPILER_OPTION], [_LT_COMPILER_OPTION($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:1620: -1- AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_COMPILER_OPTION' is obsolete. +m4trace:/usr/share/aclocal/libtool.m4:1620: -1- AU_DEFUN([AC_LIBTOOL_COMPILER_OPTION], [m4_if($#, 0, [_LT_COMPILER_OPTION], [_LT_COMPILER_OPTION($@)])]) +m4trace:/usr/share/aclocal/libtool.m4:1620: -1- AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_COMPILER_OPTION' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [_LT_COMPILER_OPTION], [_LT_COMPILER_OPTION($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:1629: -1- AC_DEFUN([_LT_LINKER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4trace:/usr/share/aclocal/libtool.m4:1629: -1- AC_DEFUN([_LT_LINKER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no @@ -265,11 +265,11 @@ m4_if([$5], , :, [$5]) fi ]) -m4trace:/usr/local/share/aclocal/libtool.m4:1664: -1- AU_DEFUN([AC_LIBTOOL_LINKER_OPTION], [m4_if($#, 0, [_LT_LINKER_OPTION], [_LT_LINKER_OPTION($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:1664: -1- AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_LINKER_OPTION' is obsolete. +m4trace:/usr/share/aclocal/libtool.m4:1664: -1- AU_DEFUN([AC_LIBTOOL_LINKER_OPTION], [m4_if($#, 0, [_LT_LINKER_OPTION], [_LT_LINKER_OPTION($@)])]) +m4trace:/usr/share/aclocal/libtool.m4:1664: -1- AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_LINKER_OPTION' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [_LT_LINKER_OPTION], [_LT_LINKER_OPTION($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:1671: -1- AC_DEFUN([LT_CMD_MAX_LEN], [AC_REQUIRE([AC_CANONICAL_HOST])dnl +m4trace:/usr/share/aclocal/libtool.m4:1671: -1- AC_DEFUN([LT_CMD_MAX_LEN], [AC_REQUIRE([AC_CANONICAL_HOST])dnl # find the maximum length of command line arguments AC_MSG_CHECKING([the maximum length of command line arguments]) AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl @@ -405,11 +405,11 @@ _LT_DECL([], [max_cmd_len], [0], [What is the maximum length of a command?]) ]) -m4trace:/usr/local/share/aclocal/libtool.m4:1810: -1- AU_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], [m4_if($#, 0, [LT_CMD_MAX_LEN], [LT_CMD_MAX_LEN($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:1810: -1- AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_SYS_MAX_CMD_LEN' is obsolete. +m4trace:/usr/share/aclocal/libtool.m4:1810: -1- AU_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], [m4_if($#, 0, [LT_CMD_MAX_LEN], [LT_CMD_MAX_LEN($@)])]) +m4trace:/usr/share/aclocal/libtool.m4:1810: -1- AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_SYS_MAX_CMD_LEN' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [LT_CMD_MAX_LEN], [LT_CMD_MAX_LEN($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:1921: -1- AC_DEFUN([LT_SYS_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl +m4trace:/usr/share/aclocal/libtool.m4:1921: -1- AC_DEFUN([LT_SYS_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test yes != "$enable_dlopen"; then enable_dlopen=unknown enable_dlopen_self=unknown @@ -531,11 +531,11 @@ _LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], [Whether dlopen of statically linked programs is supported]) ]) -m4trace:/usr/local/share/aclocal/libtool.m4:2046: -1- AU_DEFUN([AC_LIBTOOL_DLOPEN_SELF], [m4_if($#, 0, [LT_SYS_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:2046: -1- AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_DLOPEN_SELF' is obsolete. +m4trace:/usr/share/aclocal/libtool.m4:2046: -1- AU_DEFUN([AC_LIBTOOL_DLOPEN_SELF], [m4_if($#, 0, [LT_SYS_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF($@)])]) +m4trace:/usr/share/aclocal/libtool.m4:2046: -1- AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_DLOPEN_SELF' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [LT_SYS_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:3155: -1- AC_DEFUN([_LT_PATH_TOOL_PREFIX], [m4_require([_LT_DECL_EGREP])dnl +m4trace:/usr/share/aclocal/libtool.m4:3158: -1- AC_DEFUN([_LT_PATH_TOOL_PREFIX], [m4_require([_LT_DECL_EGREP])dnl AC_MSG_CHECKING([for $1]) AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, [case $MAGIC_CMD in @@ -594,11 +594,11 @@ _LT_DECL([], [MAGIC_CMD], [0], [Used to examine libraries when file_magic_cmd begins with "file"])dnl ]) -m4trace:/usr/local/share/aclocal/libtool.m4:3217: -1- AU_DEFUN([AC_PATH_TOOL_PREFIX], [m4_if($#, 0, [_LT_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:3217: -1- AC_DEFUN([AC_PATH_TOOL_PREFIX], [AC_DIAGNOSE([obsolete], [The macro `AC_PATH_TOOL_PREFIX' is obsolete. +m4trace:/usr/share/aclocal/libtool.m4:3220: -1- AU_DEFUN([AC_PATH_TOOL_PREFIX], [m4_if($#, 0, [_LT_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX($@)])]) +m4trace:/usr/share/aclocal/libtool.m4:3220: -1- AC_DEFUN([AC_PATH_TOOL_PREFIX], [AC_DIAGNOSE([obsolete], [The macro `AC_PATH_TOOL_PREFIX' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [_LT_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:3240: -1- AC_DEFUN([LT_PATH_LD], [AC_REQUIRE([AC_PROG_CC])dnl +m4trace:/usr/share/aclocal/libtool.m4:3243: -1- AC_DEFUN([LT_PATH_LD], [AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl m4_require([_LT_DECL_SED])dnl @@ -684,15 +684,15 @@ _LT_TAGDECL([], [LD], [1], [The linker used to build libraries]) ]) -m4trace:/usr/local/share/aclocal/libtool.m4:3329: -1- AU_DEFUN([AM_PROG_LD], [m4_if($#, 0, [LT_PATH_LD], [LT_PATH_LD($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:3329: -1- AC_DEFUN([AM_PROG_LD], [AC_DIAGNOSE([obsolete], [The macro `AM_PROG_LD' is obsolete. +m4trace:/usr/share/aclocal/libtool.m4:3332: -1- AU_DEFUN([AM_PROG_LD], [m4_if($#, 0, [LT_PATH_LD], [LT_PATH_LD($@)])]) +m4trace:/usr/share/aclocal/libtool.m4:3332: -1- AC_DEFUN([AM_PROG_LD], [AC_DIAGNOSE([obsolete], [The macro `AM_PROG_LD' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [LT_PATH_LD], [LT_PATH_LD($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:3330: -1- AU_DEFUN([AC_PROG_LD], [m4_if($#, 0, [LT_PATH_LD], [LT_PATH_LD($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:3330: -1- AC_DEFUN([AC_PROG_LD], [AC_DIAGNOSE([obsolete], [The macro `AC_PROG_LD' is obsolete. +m4trace:/usr/share/aclocal/libtool.m4:3333: -1- AU_DEFUN([AC_PROG_LD], [m4_if($#, 0, [LT_PATH_LD], [LT_PATH_LD($@)])]) +m4trace:/usr/share/aclocal/libtool.m4:3333: -1- AC_DEFUN([AC_PROG_LD], [AC_DIAGNOSE([obsolete], [The macro `AC_PROG_LD' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [LT_PATH_LD], [LT_PATH_LD($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:3659: -1- AC_DEFUN([LT_PATH_NM], [AC_REQUIRE([AC_PROG_CC])dnl +m4trace:/usr/share/aclocal/libtool.m4:3662: -1- AC_DEFUN([LT_PATH_NM], [AC_REQUIRE([AC_PROG_CC])dnl AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, [if test -n "$NM"; then # Let the user override the test. @@ -784,15 +784,15 @@ fi rm -f conftest*]) ]) -m4trace:/usr/local/share/aclocal/libtool.m4:3754: -1- AU_DEFUN([AM_PROG_NM], [m4_if($#, 0, [LT_PATH_NM], [LT_PATH_NM($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:3754: -1- AC_DEFUN([AM_PROG_NM], [AC_DIAGNOSE([obsolete], [The macro `AM_PROG_NM' is obsolete. +m4trace:/usr/share/aclocal/libtool.m4:3757: -1- AU_DEFUN([AM_PROG_NM], [m4_if($#, 0, [LT_PATH_NM], [LT_PATH_NM($@)])]) +m4trace:/usr/share/aclocal/libtool.m4:3757: -1- AC_DEFUN([AM_PROG_NM], [AC_DIAGNOSE([obsolete], [The macro `AM_PROG_NM' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [LT_PATH_NM], [LT_PATH_NM($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:3755: -1- AU_DEFUN([AC_PROG_NM], [m4_if($#, 0, [LT_PATH_NM], [LT_PATH_NM($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:3755: -1- AC_DEFUN([AC_PROG_NM], [AC_DIAGNOSE([obsolete], [The macro `AC_PROG_NM' is obsolete. +m4trace:/usr/share/aclocal/libtool.m4:3758: -1- AU_DEFUN([AC_PROG_NM], [m4_if($#, 0, [LT_PATH_NM], [LT_PATH_NM($@)])]) +m4trace:/usr/share/aclocal/libtool.m4:3758: -1- AC_DEFUN([AC_PROG_NM], [AC_DIAGNOSE([obsolete], [The macro `AC_PROG_NM' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [LT_PATH_NM], [LT_PATH_NM($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:3826: -1- AC_DEFUN([_LT_DLL_DEF_P], [dnl +m4trace:/usr/share/aclocal/libtool.m4:3829: -1- AC_DEFUN([_LT_DLL_DEF_P], [dnl test DEF = "`$SED -n dnl -e '\''s/^[[ ]]*//'\'' dnl Strip leading whitespace -e '\''/^\(;.*\)*$/d'\'' dnl Delete empty lines and comments @@ -800,7 +800,7 @@ -e q dnl Only consider the first "real" line $1`" dnl ]) -m4trace:/usr/local/share/aclocal/libtool.m4:3840: -1- AC_DEFUN([LT_LIB_M], [AC_REQUIRE([AC_CANONICAL_HOST])dnl +m4trace:/usr/share/aclocal/libtool.m4:3843: -1- AC_DEFUN([LT_LIB_M], [AC_REQUIRE([AC_CANONICAL_HOST])dnl LIBM= case $host in *-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) @@ -816,33 +816,33 @@ esac AC_SUBST([LIBM]) ]) -m4trace:/usr/local/share/aclocal/libtool.m4:3859: -1- AU_DEFUN([AC_CHECK_LIBM], [m4_if($#, 0, [LT_LIB_M], [LT_LIB_M($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:3859: -1- AC_DEFUN([AC_CHECK_LIBM], [AC_DIAGNOSE([obsolete], [The macro `AC_CHECK_LIBM' is obsolete. +m4trace:/usr/share/aclocal/libtool.m4:3862: -1- AU_DEFUN([AC_CHECK_LIBM], [m4_if($#, 0, [LT_LIB_M], [LT_LIB_M($@)])]) +m4trace:/usr/share/aclocal/libtool.m4:3862: -1- AC_DEFUN([AC_CHECK_LIBM], [AC_DIAGNOSE([obsolete], [The macro `AC_CHECK_LIBM' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [LT_LIB_M], [LT_LIB_M($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:8122: -1- AC_DEFUN([LT_PROG_GCJ], [m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], +m4trace:/usr/share/aclocal/libtool.m4:8125: -1- AC_DEFUN([LT_PROG_GCJ], [m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], [AC_CHECK_TOOL(GCJ, gcj,) test set = "${GCJFLAGS+set}" || GCJFLAGS="-g -O2" AC_SUBST(GCJFLAGS)])])[]dnl ]) -m4trace:/usr/local/share/aclocal/libtool.m4:8131: -1- AU_DEFUN([LT_AC_PROG_GCJ], [m4_if($#, 0, [LT_PROG_GCJ], [LT_PROG_GCJ($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:8131: -1- AC_DEFUN([LT_AC_PROG_GCJ], [AC_DIAGNOSE([obsolete], [The macro `LT_AC_PROG_GCJ' is obsolete. +m4trace:/usr/share/aclocal/libtool.m4:8134: -1- AU_DEFUN([LT_AC_PROG_GCJ], [m4_if($#, 0, [LT_PROG_GCJ], [LT_PROG_GCJ($@)])]) +m4trace:/usr/share/aclocal/libtool.m4:8134: -1- AC_DEFUN([LT_AC_PROG_GCJ], [AC_DIAGNOSE([obsolete], [The macro `LT_AC_PROG_GCJ' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [LT_PROG_GCJ], [LT_PROG_GCJ($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:8138: -1- AC_DEFUN([LT_PROG_GO], [AC_CHECK_TOOL(GOC, gccgo,) +m4trace:/usr/share/aclocal/libtool.m4:8141: -1- AC_DEFUN([LT_PROG_GO], [AC_CHECK_TOOL(GOC, gccgo,) ]) -m4trace:/usr/local/share/aclocal/libtool.m4:8145: -1- AC_DEFUN([LT_PROG_RC], [AC_CHECK_TOOL(RC, windres,) +m4trace:/usr/share/aclocal/libtool.m4:8148: -1- AC_DEFUN([LT_PROG_RC], [AC_CHECK_TOOL(RC, windres,) ]) -m4trace:/usr/local/share/aclocal/libtool.m4:8150: -1- AU_DEFUN([LT_AC_PROG_RC], [m4_if($#, 0, [LT_PROG_RC], [LT_PROG_RC($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:8150: -1- AC_DEFUN([LT_AC_PROG_RC], [AC_DIAGNOSE([obsolete], [The macro `LT_AC_PROG_RC' is obsolete. +m4trace:/usr/share/aclocal/libtool.m4:8153: -1- AU_DEFUN([LT_AC_PROG_RC], [m4_if($#, 0, [LT_PROG_RC], [LT_PROG_RC($@)])]) +m4trace:/usr/share/aclocal/libtool.m4:8153: -1- AC_DEFUN([LT_AC_PROG_RC], [AC_DIAGNOSE([obsolete], [The macro `LT_AC_PROG_RC' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [LT_PROG_RC], [LT_PROG_RC($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:8270: -1- AU_DEFUN([LT_AC_PROG_SED], [m4_if($#, 0, [AC_PROG_SED], [AC_PROG_SED($@)])]) -m4trace:/usr/local/share/aclocal/libtool.m4:8270: -1- AC_DEFUN([LT_AC_PROG_SED], [AC_DIAGNOSE([obsolete], [The macro `LT_AC_PROG_SED' is obsolete. +m4trace:/usr/share/aclocal/libtool.m4:8273: -1- AU_DEFUN([LT_AC_PROG_SED], [m4_if($#, 0, [AC_PROG_SED], [AC_PROG_SED($@)])]) +m4trace:/usr/share/aclocal/libtool.m4:8273: -1- AC_DEFUN([LT_AC_PROG_SED], [AC_DIAGNOSE([obsolete], [The macro `LT_AC_PROG_SED' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [AC_PROG_SED], [AC_PROG_SED($@)])]) -m4trace:/usr/local/share/aclocal/ltargz.m4:12: -1- AC_DEFUN([LT_FUNC_ARGZ], [ +m4trace:/usr/share/aclocal/ltargz.m4:12: -1- AC_DEFUN([LT_FUNC_ARGZ], [ AC_CHECK_HEADERS([argz.h], [], [], [AC_INCLUDES_DEFAULT]) AC_CHECK_TYPES([error_t], @@ -905,10 +905,10 @@ AC_SUBST([LT_ARGZ_H]) ]) -m4trace:/usr/local/share/aclocal/ltdl.m4:16: -1- AC_DEFUN([LT_CONFIG_LTDL_DIR], [AC_BEFORE([$0], [LTDL_INIT]) +m4trace:/usr/share/aclocal/ltdl.m4:16: -1- AC_DEFUN([LT_CONFIG_LTDL_DIR], [AC_BEFORE([$0], [LTDL_INIT]) _$0($*) ]) -m4trace:/usr/local/share/aclocal/ltdl.m4:68: -1- AC_DEFUN([LTDL_CONVENIENCE], [AC_BEFORE([$0], [LTDL_INIT])dnl +m4trace:/usr/share/aclocal/ltdl.m4:68: -1- AC_DEFUN([LTDL_CONVENIENCE], [AC_BEFORE([$0], [LTDL_INIT])dnl dnl Although the argument is deprecated and no longer documented, dnl LTDL_CONVENIENCE used to take a DIRECTORY orgument, if we have one dnl here make sure it is the same as any other declaration of libltdl's @@ -917,13 +917,13 @@ m4_ifval([$1], [_LT_CONFIG_LTDL_DIR([$1])])dnl _$0() ]) -m4trace:/usr/local/share/aclocal/ltdl.m4:81: -1- AU_DEFUN([AC_LIBLTDL_CONVENIENCE], [_LT_CONFIG_LTDL_DIR([m4_default([$1], [libltdl])]) +m4trace:/usr/share/aclocal/ltdl.m4:81: -1- AU_DEFUN([AC_LIBLTDL_CONVENIENCE], [_LT_CONFIG_LTDL_DIR([m4_default([$1], [libltdl])]) _LTDL_CONVENIENCE]) -m4trace:/usr/local/share/aclocal/ltdl.m4:81: -1- AC_DEFUN([AC_LIBLTDL_CONVENIENCE], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBLTDL_CONVENIENCE' is obsolete. +m4trace:/usr/share/aclocal/ltdl.m4:81: -1- AC_DEFUN([AC_LIBLTDL_CONVENIENCE], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBLTDL_CONVENIENCE' is obsolete. You should run autoupdate.])dnl _LT_CONFIG_LTDL_DIR([m4_default([$1], [libltdl])]) _LTDL_CONVENIENCE]) -m4trace:/usr/local/share/aclocal/ltdl.m4:124: -1- AC_DEFUN([LTDL_INSTALLABLE], [AC_BEFORE([$0], [LTDL_INIT])dnl +m4trace:/usr/share/aclocal/ltdl.m4:124: -1- AC_DEFUN([LTDL_INSTALLABLE], [AC_BEFORE([$0], [LTDL_INIT])dnl dnl Although the argument is deprecated and no longer documented, dnl LTDL_INSTALLABLE used to take a DIRECTORY orgument, if we have one dnl here make sure it is the same as any other declaration of libltdl's @@ -932,17 +932,17 @@ m4_ifval([$1], [_LT_CONFIG_LTDL_DIR([$1])])dnl _$0() ]) -m4trace:/usr/local/share/aclocal/ltdl.m4:137: -1- AU_DEFUN([AC_LIBLTDL_INSTALLABLE], [_LT_CONFIG_LTDL_DIR([m4_default([$1], [libltdl])]) +m4trace:/usr/share/aclocal/ltdl.m4:137: -1- AU_DEFUN([AC_LIBLTDL_INSTALLABLE], [_LT_CONFIG_LTDL_DIR([m4_default([$1], [libltdl])]) _LTDL_INSTALLABLE]) -m4trace:/usr/local/share/aclocal/ltdl.m4:137: -1- AC_DEFUN([AC_LIBLTDL_INSTALLABLE], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBLTDL_INSTALLABLE' is obsolete. +m4trace:/usr/share/aclocal/ltdl.m4:137: -1- AC_DEFUN([AC_LIBLTDL_INSTALLABLE], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBLTDL_INSTALLABLE' is obsolete. You should run autoupdate.])dnl _LT_CONFIG_LTDL_DIR([m4_default([$1], [libltdl])]) _LTDL_INSTALLABLE]) -m4trace:/usr/local/share/aclocal/ltdl.m4:213: -1- AC_DEFUN([_LT_LIBOBJ], [ +m4trace:/usr/share/aclocal/ltdl.m4:213: -1- AC_DEFUN([_LT_LIBOBJ], [ m4_pattern_allow([^_LT_LIBOBJS$]) _LT_LIBOBJS="$_LT_LIBOBJS $1.$ac_objext" ]) -m4trace:/usr/local/share/aclocal/ltdl.m4:226: -1- AC_DEFUN([LTDL_INIT], [dnl Parse OPTIONS +m4trace:/usr/share/aclocal/ltdl.m4:226: -1- AC_DEFUN([LTDL_INIT], [dnl Parse OPTIONS _LT_SET_OPTIONS([$0], [$1]) dnl We need to keep our own list of libobjs separate from our parent project, @@ -1065,19 +1065,19 @@ # Only expand once: m4_define([LTDL_INIT]) ]) -m4trace:/usr/local/share/aclocal/ltdl.m4:352: -1- AU_DEFUN([AC_LIB_LTDL], [LTDL_INIT($@)]) -m4trace:/usr/local/share/aclocal/ltdl.m4:352: -1- AC_DEFUN([AC_LIB_LTDL], [AC_DIAGNOSE([obsolete], [The macro `AC_LIB_LTDL' is obsolete. +m4trace:/usr/share/aclocal/ltdl.m4:352: -1- AU_DEFUN([AC_LIB_LTDL], [LTDL_INIT($@)]) +m4trace:/usr/share/aclocal/ltdl.m4:352: -1- AC_DEFUN([AC_LIB_LTDL], [AC_DIAGNOSE([obsolete], [The macro `AC_LIB_LTDL' is obsolete. You should run autoupdate.])dnl LTDL_INIT($@)]) -m4trace:/usr/local/share/aclocal/ltdl.m4:353: -1- AU_DEFUN([AC_WITH_LTDL], [LTDL_INIT($@)]) -m4trace:/usr/local/share/aclocal/ltdl.m4:353: -1- AC_DEFUN([AC_WITH_LTDL], [AC_DIAGNOSE([obsolete], [The macro `AC_WITH_LTDL' is obsolete. +m4trace:/usr/share/aclocal/ltdl.m4:353: -1- AU_DEFUN([AC_WITH_LTDL], [LTDL_INIT($@)]) +m4trace:/usr/share/aclocal/ltdl.m4:353: -1- AC_DEFUN([AC_WITH_LTDL], [AC_DIAGNOSE([obsolete], [The macro `AC_WITH_LTDL' is obsolete. You should run autoupdate.])dnl LTDL_INIT($@)]) -m4trace:/usr/local/share/aclocal/ltdl.m4:354: -1- AU_DEFUN([LT_WITH_LTDL], [LTDL_INIT($@)]) -m4trace:/usr/local/share/aclocal/ltdl.m4:354: -1- AC_DEFUN([LT_WITH_LTDL], [AC_DIAGNOSE([obsolete], [The macro `LT_WITH_LTDL' is obsolete. +m4trace:/usr/share/aclocal/ltdl.m4:354: -1- AU_DEFUN([LT_WITH_LTDL], [LTDL_INIT($@)]) +m4trace:/usr/share/aclocal/ltdl.m4:354: -1- AC_DEFUN([LT_WITH_LTDL], [AC_DIAGNOSE([obsolete], [The macro `LT_WITH_LTDL' is obsolete. You should run autoupdate.])dnl LTDL_INIT($@)]) -m4trace:/usr/local/share/aclocal/ltdl.m4:367: -1- AC_DEFUN([_LTDL_SETUP], [AC_REQUIRE([AC_PROG_CC])dnl +m4trace:/usr/share/aclocal/ltdl.m4:367: -1- AC_DEFUN([_LTDL_SETUP], [AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([LT_SYS_MODULE_EXT])dnl AC_REQUIRE([LT_SYS_MODULE_PATH])dnl AC_REQUIRE([LT_SYS_DLSEARCH_PATH])dnl @@ -1131,7 +1131,7 @@ eval "LTDLOPEN=\"$libname_spec\"" AC_SUBST([LTDLOPEN]) ]) -m4trace:/usr/local/share/aclocal/ltdl.m4:443: -1- AC_DEFUN([LT_SYS_DLOPEN_DEPLIBS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl +m4trace:/usr/share/aclocal/ltdl.m4:443: -1- AC_DEFUN([LT_SYS_DLOPEN_DEPLIBS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_CACHE_CHECK([whether deplibs are loaded by dlopen], [lt_cv_sys_dlopen_deplibs], [# PORTME does your system automatically load deplibs for dlopen? @@ -1230,11 +1230,11 @@ [Define if the OS needs help to load dependent libraries for dlopen().]) fi ]) -m4trace:/usr/local/share/aclocal/ltdl.m4:545: -1- AU_DEFUN([AC_LTDL_SYS_DLOPEN_DEPLIBS], [m4_if($#, 0, [LT_SYS_DLOPEN_DEPLIBS], [LT_SYS_DLOPEN_DEPLIBS($@)])]) -m4trace:/usr/local/share/aclocal/ltdl.m4:545: -1- AC_DEFUN([AC_LTDL_SYS_DLOPEN_DEPLIBS], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_SYS_DLOPEN_DEPLIBS' is obsolete. +m4trace:/usr/share/aclocal/ltdl.m4:545: -1- AU_DEFUN([AC_LTDL_SYS_DLOPEN_DEPLIBS], [m4_if($#, 0, [LT_SYS_DLOPEN_DEPLIBS], [LT_SYS_DLOPEN_DEPLIBS($@)])]) +m4trace:/usr/share/aclocal/ltdl.m4:545: -1- AC_DEFUN([AC_LTDL_SYS_DLOPEN_DEPLIBS], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_SYS_DLOPEN_DEPLIBS' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [LT_SYS_DLOPEN_DEPLIBS], [LT_SYS_DLOPEN_DEPLIBS($@)])]) -m4trace:/usr/local/share/aclocal/ltdl.m4:552: -1- AC_DEFUN([LT_SYS_MODULE_EXT], [m4_require([_LT_SYS_DYNAMIC_LINKER])dnl +m4trace:/usr/share/aclocal/ltdl.m4:552: -1- AC_DEFUN([LT_SYS_MODULE_EXT], [m4_require([_LT_SYS_DYNAMIC_LINKER])dnl AC_CACHE_CHECK([what extension is used for runtime loadable modules], [libltdl_cv_shlibext], [ @@ -1259,11 +1259,11 @@ [Define to the shared archive member specification, say "(shr.o)".]) fi ]) -m4trace:/usr/local/share/aclocal/ltdl.m4:580: -1- AU_DEFUN([AC_LTDL_SHLIBEXT], [m4_if($#, 0, [LT_SYS_MODULE_EXT], [LT_SYS_MODULE_EXT($@)])]) -m4trace:/usr/local/share/aclocal/ltdl.m4:580: -1- AC_DEFUN([AC_LTDL_SHLIBEXT], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_SHLIBEXT' is obsolete. +m4trace:/usr/share/aclocal/ltdl.m4:580: -1- AU_DEFUN([AC_LTDL_SHLIBEXT], [m4_if($#, 0, [LT_SYS_MODULE_EXT], [LT_SYS_MODULE_EXT($@)])]) +m4trace:/usr/share/aclocal/ltdl.m4:580: -1- AC_DEFUN([AC_LTDL_SHLIBEXT], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_SHLIBEXT' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [LT_SYS_MODULE_EXT], [LT_SYS_MODULE_EXT($@)])]) -m4trace:/usr/local/share/aclocal/ltdl.m4:587: -1- AC_DEFUN([LT_SYS_MODULE_PATH], [m4_require([_LT_SYS_DYNAMIC_LINKER])dnl +m4trace:/usr/share/aclocal/ltdl.m4:587: -1- AC_DEFUN([LT_SYS_MODULE_PATH], [m4_require([_LT_SYS_DYNAMIC_LINKER])dnl AC_CACHE_CHECK([what variable specifies run-time module search path], [lt_cv_module_path_var], [lt_cv_module_path_var=$shlibpath_var]) if test -n "$lt_cv_module_path_var"; then @@ -1272,11 +1272,11 @@ [Define to the name of the environment variable that determines the run-time module search path.]) fi ]) -m4trace:/usr/local/share/aclocal/ltdl.m4:599: -1- AU_DEFUN([AC_LTDL_SHLIBPATH], [m4_if($#, 0, [LT_SYS_MODULE_PATH], [LT_SYS_MODULE_PATH($@)])]) -m4trace:/usr/local/share/aclocal/ltdl.m4:599: -1- AC_DEFUN([AC_LTDL_SHLIBPATH], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_SHLIBPATH' is obsolete. +m4trace:/usr/share/aclocal/ltdl.m4:599: -1- AU_DEFUN([AC_LTDL_SHLIBPATH], [m4_if($#, 0, [LT_SYS_MODULE_PATH], [LT_SYS_MODULE_PATH($@)])]) +m4trace:/usr/share/aclocal/ltdl.m4:599: -1- AC_DEFUN([AC_LTDL_SHLIBPATH], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_SHLIBPATH' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [LT_SYS_MODULE_PATH], [LT_SYS_MODULE_PATH($@)])]) -m4trace:/usr/local/share/aclocal/ltdl.m4:606: -1- AC_DEFUN([LT_SYS_DLSEARCH_PATH], [m4_require([_LT_SYS_DYNAMIC_LINKER])dnl +m4trace:/usr/share/aclocal/ltdl.m4:606: -1- AC_DEFUN([LT_SYS_DLSEARCH_PATH], [m4_require([_LT_SYS_DYNAMIC_LINKER])dnl AC_CACHE_CHECK([for the default library search path], [lt_cv_sys_dlsearch_path], [lt_cv_sys_dlsearch_path=$sys_lib_dlsearch_path_spec]) @@ -1294,11 +1294,11 @@ [Define to the system default library search path.]) fi ]) -m4trace:/usr/local/share/aclocal/ltdl.m4:627: -1- AU_DEFUN([AC_LTDL_SYSSEARCHPATH], [m4_if($#, 0, [LT_SYS_DLSEARCH_PATH], [LT_SYS_DLSEARCH_PATH($@)])]) -m4trace:/usr/local/share/aclocal/ltdl.m4:627: -1- AC_DEFUN([AC_LTDL_SYSSEARCHPATH], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_SYSSEARCHPATH' is obsolete. +m4trace:/usr/share/aclocal/ltdl.m4:627: -1- AU_DEFUN([AC_LTDL_SYSSEARCHPATH], [m4_if($#, 0, [LT_SYS_DLSEARCH_PATH], [LT_SYS_DLSEARCH_PATH($@)])]) +m4trace:/usr/share/aclocal/ltdl.m4:627: -1- AC_DEFUN([AC_LTDL_SYSSEARCHPATH], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_SYSSEARCHPATH' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [LT_SYS_DLSEARCH_PATH], [LT_SYS_DLSEARCH_PATH($@)])]) -m4trace:/usr/local/share/aclocal/ltdl.m4:653: -1- AC_DEFUN([LT_LIB_DLLOAD], [m4_pattern_allow([^LT_DLLOADERS$]) +m4trace:/usr/share/aclocal/ltdl.m4:653: -1- AC_DEFUN([LT_LIB_DLLOAD], [m4_pattern_allow([^LT_DLLOADERS$]) LT_DLLOADERS= AC_SUBST([LT_DLLOADERS]) @@ -1390,11 +1390,11 @@ LIBS=$lt_dlload_save_LIBS AC_LANG_POP ]) -m4trace:/usr/local/share/aclocal/ltdl.m4:748: -1- AU_DEFUN([AC_LTDL_DLLIB], [m4_if($#, 0, [LT_LIB_DLLOAD], [LT_LIB_DLLOAD($@)])]) -m4trace:/usr/local/share/aclocal/ltdl.m4:748: -1- AC_DEFUN([AC_LTDL_DLLIB], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_DLLIB' is obsolete. +m4trace:/usr/share/aclocal/ltdl.m4:748: -1- AU_DEFUN([AC_LTDL_DLLIB], [m4_if($#, 0, [LT_LIB_DLLOAD], [LT_LIB_DLLOAD($@)])]) +m4trace:/usr/share/aclocal/ltdl.m4:748: -1- AC_DEFUN([AC_LTDL_DLLIB], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_DLLIB' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [LT_LIB_DLLOAD], [LT_LIB_DLLOAD($@)])]) -m4trace:/usr/local/share/aclocal/ltdl.m4:756: -1- AC_DEFUN([LT_SYS_SYMBOL_USCORE], [m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4trace:/usr/share/aclocal/ltdl.m4:756: -1- AC_DEFUN([LT_SYS_SYMBOL_USCORE], [m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl AC_CACHE_CHECK([for _ prefix in compiled symbols], [lt_cv_sys_symbol_underscore], [lt_cv_sys_symbol_underscore=no @@ -1428,11 +1428,11 @@ sys_symbol_underscore=$lt_cv_sys_symbol_underscore AC_SUBST([sys_symbol_underscore]) ]) -m4trace:/usr/local/share/aclocal/ltdl.m4:793: -1- AU_DEFUN([AC_LTDL_SYMBOL_USCORE], [m4_if($#, 0, [LT_SYS_SYMBOL_USCORE], [LT_SYS_SYMBOL_USCORE($@)])]) -m4trace:/usr/local/share/aclocal/ltdl.m4:793: -1- AC_DEFUN([AC_LTDL_SYMBOL_USCORE], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_SYMBOL_USCORE' is obsolete. +m4trace:/usr/share/aclocal/ltdl.m4:793: -1- AU_DEFUN([AC_LTDL_SYMBOL_USCORE], [m4_if($#, 0, [LT_SYS_SYMBOL_USCORE], [LT_SYS_SYMBOL_USCORE($@)])]) +m4trace:/usr/share/aclocal/ltdl.m4:793: -1- AC_DEFUN([AC_LTDL_SYMBOL_USCORE], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_SYMBOL_USCORE' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [LT_SYS_SYMBOL_USCORE], [LT_SYS_SYMBOL_USCORE($@)])]) -m4trace:/usr/local/share/aclocal/ltdl.m4:800: -1- AC_DEFUN([LT_FUNC_DLSYM_USCORE], [AC_REQUIRE([_LT_COMPILER_PIC])dnl for lt_prog_compiler_wl +m4trace:/usr/share/aclocal/ltdl.m4:800: -1- AC_DEFUN([LT_FUNC_DLSYM_USCORE], [AC_REQUIRE([_LT_COMPILER_PIC])dnl for lt_prog_compiler_wl AC_REQUIRE([LT_SYS_SYMBOL_USCORE])dnl for lt_cv_sys_symbol_underscore AC_REQUIRE([LT_SYS_MODULE_EXT])dnl for libltdl_cv_shlibext if test yes = "$lt_cv_sys_symbol_underscore"; then @@ -1536,30 +1536,30 @@ [Define if dlsym() requires a leading underscore in symbol names.]) fi ]) -m4trace:/usr/local/share/aclocal/ltdl.m4:907: -1- AU_DEFUN([AC_LTDL_DLSYM_USCORE], [m4_if($#, 0, [LT_FUNC_DLSYM_USCORE], [LT_FUNC_DLSYM_USCORE($@)])]) -m4trace:/usr/local/share/aclocal/ltdl.m4:907: -1- AC_DEFUN([AC_LTDL_DLSYM_USCORE], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_DLSYM_USCORE' is obsolete. +m4trace:/usr/share/aclocal/ltdl.m4:907: -1- AU_DEFUN([AC_LTDL_DLSYM_USCORE], [m4_if($#, 0, [LT_FUNC_DLSYM_USCORE], [LT_FUNC_DLSYM_USCORE($@)])]) +m4trace:/usr/share/aclocal/ltdl.m4:907: -1- AC_DEFUN([AC_LTDL_DLSYM_USCORE], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_DLSYM_USCORE' is obsolete. You should run autoupdate.])dnl m4_if($#, 0, [LT_FUNC_DLSYM_USCORE], [LT_FUNC_DLSYM_USCORE($@)])]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:14: -1- AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:113: -1- AU_DEFUN([AC_LIBTOOL_DLOPEN], [_LT_SET_OPTION([LT_INIT], [dlopen]) +m4trace:/usr/share/aclocal/ltoptions.m4:14: -1- AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) +m4trace:/usr/share/aclocal/ltoptions.m4:113: -1- AU_DEFUN([AC_LIBTOOL_DLOPEN], [_LT_SET_OPTION([LT_INIT], [dlopen]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'dlopen' option into LT_INIT's first parameter.]) ]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:113: -1- AC_DEFUN([AC_LIBTOOL_DLOPEN], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_DLOPEN' is obsolete. +m4trace:/usr/share/aclocal/ltoptions.m4:113: -1- AC_DEFUN([AC_LIBTOOL_DLOPEN], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_DLOPEN' is obsolete. You should run autoupdate.])dnl _LT_SET_OPTION([LT_INIT], [dlopen]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'dlopen' option into LT_INIT's first parameter.]) ]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:148: -1- AU_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_REQUIRE([AC_CANONICAL_HOST])dnl +m4trace:/usr/share/aclocal/ltoptions.m4:148: -1- AU_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_REQUIRE([AC_CANONICAL_HOST])dnl _LT_SET_OPTION([LT_INIT], [win32-dll]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'win32-dll' option into LT_INIT's first parameter.]) ]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:148: -1- AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_WIN32_DLL' is obsolete. +m4trace:/usr/share/aclocal/ltoptions.m4:148: -1- AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_WIN32_DLL' is obsolete. You should run autoupdate.])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl _LT_SET_OPTION([LT_INIT], [win32-dll]) @@ -1567,124 +1567,124 @@ [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'win32-dll' option into LT_INIT's first parameter.]) ]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:197: -1- AC_DEFUN([AC_ENABLE_SHARED], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) +m4trace:/usr/share/aclocal/ltoptions.m4:197: -1- AC_DEFUN([AC_ENABLE_SHARED], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) ]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:201: -1- AC_DEFUN([AC_DISABLE_SHARED], [_LT_SET_OPTION([LT_INIT], [disable-shared]) +m4trace:/usr/share/aclocal/ltoptions.m4:201: -1- AC_DEFUN([AC_DISABLE_SHARED], [_LT_SET_OPTION([LT_INIT], [disable-shared]) ]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:205: -1- AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:205: -1- AC_DEFUN([AM_ENABLE_SHARED], [AC_DIAGNOSE([obsolete], [The macro `AM_ENABLE_SHARED' is obsolete. +m4trace:/usr/share/aclocal/ltoptions.m4:205: -1- AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) +m4trace:/usr/share/aclocal/ltoptions.m4:205: -1- AC_DEFUN([AM_ENABLE_SHARED], [AC_DIAGNOSE([obsolete], [The macro `AM_ENABLE_SHARED' is obsolete. You should run autoupdate.])dnl AC_ENABLE_SHARED($@)]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:206: -1- AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:206: -1- AC_DEFUN([AM_DISABLE_SHARED], [AC_DIAGNOSE([obsolete], [The macro `AM_DISABLE_SHARED' is obsolete. +m4trace:/usr/share/aclocal/ltoptions.m4:206: -1- AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) +m4trace:/usr/share/aclocal/ltoptions.m4:206: -1- AC_DEFUN([AM_DISABLE_SHARED], [AC_DIAGNOSE([obsolete], [The macro `AM_DISABLE_SHARED' is obsolete. You should run autoupdate.])dnl AC_DISABLE_SHARED($@)]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:251: -1- AC_DEFUN([AC_ENABLE_STATIC], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) +m4trace:/usr/share/aclocal/ltoptions.m4:251: -1- AC_DEFUN([AC_ENABLE_STATIC], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) ]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:255: -1- AC_DEFUN([AC_DISABLE_STATIC], [_LT_SET_OPTION([LT_INIT], [disable-static]) +m4trace:/usr/share/aclocal/ltoptions.m4:255: -1- AC_DEFUN([AC_DISABLE_STATIC], [_LT_SET_OPTION([LT_INIT], [disable-static]) ]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:259: -1- AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:259: -1- AC_DEFUN([AM_ENABLE_STATIC], [AC_DIAGNOSE([obsolete], [The macro `AM_ENABLE_STATIC' is obsolete. +m4trace:/usr/share/aclocal/ltoptions.m4:259: -1- AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) +m4trace:/usr/share/aclocal/ltoptions.m4:259: -1- AC_DEFUN([AM_ENABLE_STATIC], [AC_DIAGNOSE([obsolete], [The macro `AM_ENABLE_STATIC' is obsolete. You should run autoupdate.])dnl AC_ENABLE_STATIC($@)]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:260: -1- AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:260: -1- AC_DEFUN([AM_DISABLE_STATIC], [AC_DIAGNOSE([obsolete], [The macro `AM_DISABLE_STATIC' is obsolete. +m4trace:/usr/share/aclocal/ltoptions.m4:260: -1- AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) +m4trace:/usr/share/aclocal/ltoptions.m4:260: -1- AC_DEFUN([AM_DISABLE_STATIC], [AC_DIAGNOSE([obsolete], [The macro `AM_DISABLE_STATIC' is obsolete. You should run autoupdate.])dnl AC_DISABLE_STATIC($@)]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:305: -1- AU_DEFUN([AC_ENABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) +m4trace:/usr/share/aclocal/ltoptions.m4:305: -1- AU_DEFUN([AC_ENABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'fast-install' option into LT_INIT's first parameter.]) ]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:305: -1- AC_DEFUN([AC_ENABLE_FAST_INSTALL], [AC_DIAGNOSE([obsolete], [The macro `AC_ENABLE_FAST_INSTALL' is obsolete. +m4trace:/usr/share/aclocal/ltoptions.m4:305: -1- AC_DEFUN([AC_ENABLE_FAST_INSTALL], [AC_DIAGNOSE([obsolete], [The macro `AC_ENABLE_FAST_INSTALL' is obsolete. You should run autoupdate.])dnl _LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'fast-install' option into LT_INIT's first parameter.]) ]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:312: -1- AU_DEFUN([AC_DISABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], [disable-fast-install]) +m4trace:/usr/share/aclocal/ltoptions.m4:312: -1- AU_DEFUN([AC_DISABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], [disable-fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'disable-fast-install' option into LT_INIT's first parameter.]) ]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:312: -1- AC_DEFUN([AC_DISABLE_FAST_INSTALL], [AC_DIAGNOSE([obsolete], [The macro `AC_DISABLE_FAST_INSTALL' is obsolete. +m4trace:/usr/share/aclocal/ltoptions.m4:312: -1- AC_DEFUN([AC_DISABLE_FAST_INSTALL], [AC_DIAGNOSE([obsolete], [The macro `AC_DISABLE_FAST_INSTALL' is obsolete. You should run autoupdate.])dnl _LT_SET_OPTION([LT_INIT], [disable-fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'disable-fast-install' option into LT_INIT's first parameter.]) ]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:411: -1- AU_DEFUN([AC_LIBTOOL_PICMODE], [_LT_SET_OPTION([LT_INIT], [pic-only]) +m4trace:/usr/share/aclocal/ltoptions.m4:411: -1- AU_DEFUN([AC_LIBTOOL_PICMODE], [_LT_SET_OPTION([LT_INIT], [pic-only]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'pic-only' option into LT_INIT's first parameter.]) ]) -m4trace:/usr/local/share/aclocal/ltoptions.m4:411: -1- AC_DEFUN([AC_LIBTOOL_PICMODE], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_PICMODE' is obsolete. +m4trace:/usr/share/aclocal/ltoptions.m4:411: -1- AC_DEFUN([AC_LIBTOOL_PICMODE], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_PICMODE' is obsolete. You should run autoupdate.])dnl _LT_SET_OPTION([LT_INIT], [pic-only]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'pic-only' option into LT_INIT's first parameter.]) ]) -m4trace:/usr/local/share/aclocal/ltsugar.m4:14: -1- AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) -m4trace:/usr/local/share/aclocal/ltversion.m4:18: -1- AC_DEFUN([LTVERSION_VERSION], [macro_version='2.4.6' +m4trace:/usr/share/aclocal/ltsugar.m4:14: -1- AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) +m4trace:/usr/share/aclocal/ltversion.m4:18: -1- AC_DEFUN([LTVERSION_VERSION], [macro_version='2.4.6' macro_revision='2.4.6' _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) _LT_DECL(, macro_revision, 0) ]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:37: -1- AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:41: -1- AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:42: -1- AC_DEFUN([_LT_AC_SHELL_INIT]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:43: -1- AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:45: -1- AC_DEFUN([_LT_AC_TAGVAR]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:46: -1- AC_DEFUN([AC_LTDL_ENABLE_INSTALL]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:47: -1- AC_DEFUN([AC_LTDL_PREOPEN]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:48: -1- AC_DEFUN([_LT_AC_SYS_COMPILER]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:49: -1- AC_DEFUN([_LT_AC_LOCK]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:50: -1- AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:51: -1- AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:52: -1- AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:53: -1- AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:54: -1- AC_DEFUN([AC_LIBTOOL_OBJDIR]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:55: -1- AC_DEFUN([AC_LTDL_OBJDIR]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:56: -1- AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:57: -1- AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:58: -1- AC_DEFUN([AC_PATH_MAGIC]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:59: -1- AC_DEFUN([AC_PROG_LD_GNU]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:60: -1- AC_DEFUN([AC_PROG_LD_RELOAD_FLAG]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:61: -1- AC_DEFUN([AC_DEPLIBS_CHECK_METHOD]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:62: -1- AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:63: -1- AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:64: -1- AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:65: -1- AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:66: -1- AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:67: -1- AC_DEFUN([LT_AC_PROG_EGREP]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:72: -1- AC_DEFUN([_AC_PROG_LIBTOOL]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:73: -1- AC_DEFUN([AC_LIBTOOL_SETUP]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:74: -1- AC_DEFUN([_LT_AC_CHECK_DLFCN]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:75: -1- AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:76: -1- AC_DEFUN([_LT_AC_TAGCONFIG]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:78: -1- AC_DEFUN([_LT_AC_LANG_CXX]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:79: -1- AC_DEFUN([_LT_AC_LANG_F77]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:80: -1- AC_DEFUN([_LT_AC_LANG_GCJ]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:81: -1- AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:82: -1- AC_DEFUN([_LT_AC_LANG_C_CONFIG]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:83: -1- AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:84: -1- AC_DEFUN([_LT_AC_LANG_CXX_CONFIG]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:85: -1- AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:86: -1- AC_DEFUN([_LT_AC_LANG_F77_CONFIG]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:87: -1- AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:88: -1- AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:89: -1- AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:90: -1- AC_DEFUN([_LT_AC_LANG_RC_CONFIG]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:91: -1- AC_DEFUN([AC_LIBTOOL_CONFIG]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:92: -1- AC_DEFUN([_LT_AC_FILE_LTDLL_C]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:94: -1- AC_DEFUN([_LT_AC_PROG_CXXCPP]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:97: -1- AC_DEFUN([_LT_PROG_F77]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:98: -1- AC_DEFUN([_LT_PROG_FC]) -m4trace:/usr/local/share/aclocal/lt~obsolete.m4:99: -1- AC_DEFUN([_LT_PROG_CXX]) -m4trace:/usr/local/share/aclocal/pkg.m4:58: -1- AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:37: -1- AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:41: -1- AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:42: -1- AC_DEFUN([_LT_AC_SHELL_INIT]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:43: -1- AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:45: -1- AC_DEFUN([_LT_AC_TAGVAR]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:46: -1- AC_DEFUN([AC_LTDL_ENABLE_INSTALL]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:47: -1- AC_DEFUN([AC_LTDL_PREOPEN]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:48: -1- AC_DEFUN([_LT_AC_SYS_COMPILER]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:49: -1- AC_DEFUN([_LT_AC_LOCK]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:50: -1- AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:51: -1- AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:52: -1- AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:53: -1- AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:54: -1- AC_DEFUN([AC_LIBTOOL_OBJDIR]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:55: -1- AC_DEFUN([AC_LTDL_OBJDIR]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:56: -1- AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:57: -1- AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:58: -1- AC_DEFUN([AC_PATH_MAGIC]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:59: -1- AC_DEFUN([AC_PROG_LD_GNU]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:60: -1- AC_DEFUN([AC_PROG_LD_RELOAD_FLAG]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:61: -1- AC_DEFUN([AC_DEPLIBS_CHECK_METHOD]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:62: -1- AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:63: -1- AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:64: -1- AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:65: -1- AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:66: -1- AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:67: -1- AC_DEFUN([LT_AC_PROG_EGREP]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:72: -1- AC_DEFUN([_AC_PROG_LIBTOOL]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:73: -1- AC_DEFUN([AC_LIBTOOL_SETUP]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:74: -1- AC_DEFUN([_LT_AC_CHECK_DLFCN]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:75: -1- AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:76: -1- AC_DEFUN([_LT_AC_TAGCONFIG]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:78: -1- AC_DEFUN([_LT_AC_LANG_CXX]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:79: -1- AC_DEFUN([_LT_AC_LANG_F77]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:80: -1- AC_DEFUN([_LT_AC_LANG_GCJ]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:81: -1- AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:82: -1- AC_DEFUN([_LT_AC_LANG_C_CONFIG]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:83: -1- AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:84: -1- AC_DEFUN([_LT_AC_LANG_CXX_CONFIG]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:85: -1- AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:86: -1- AC_DEFUN([_LT_AC_LANG_F77_CONFIG]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:87: -1- AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:88: -1- AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:89: -1- AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:90: -1- AC_DEFUN([_LT_AC_LANG_RC_CONFIG]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:91: -1- AC_DEFUN([AC_LIBTOOL_CONFIG]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:92: -1- AC_DEFUN([_LT_AC_FILE_LTDLL_C]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:94: -1- AC_DEFUN([_LT_AC_PROG_CXXCPP]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:97: -1- AC_DEFUN([_LT_PROG_F77]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:98: -1- AC_DEFUN([_LT_PROG_FC]) +m4trace:/usr/share/aclocal/lt~obsolete.m4:99: -1- AC_DEFUN([_LT_PROG_CXX]) +m4trace:/usr/share/aclocal/pkg.m4:58: -1- AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) @@ -1705,21 +1705,21 @@ fi fi[]dnl ]) -m4trace:/usr/local/share/aclocal/pkg.m4:92: -1- AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +m4trace:/usr/share/aclocal/pkg.m4:92: -1- AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) -m4trace:/usr/local/share/aclocal/pkg.m4:121: -1- AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +m4trace:/usr/share/aclocal/pkg.m4:121: -1- AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ]) -m4trace:/usr/local/share/aclocal/pkg.m4:139: -1- AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +m4trace:/usr/share/aclocal/pkg.m4:139: -1- AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl @@ -1772,13 +1772,13 @@ $3 fi[]dnl ]) -m4trace:/usr/local/share/aclocal/pkg.m4:208: -1- AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +m4trace:/usr/share/aclocal/pkg.m4:208: -1- AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl _save_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" PKG_CHECK_MODULES($@) PKG_CONFIG=$_save_PKG_CONFIG[]dnl ]) -m4trace:/usr/local/share/aclocal/pkg.m4:226: -1- AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) +m4trace:/usr/share/aclocal/pkg.m4:226: -1- AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], @@ -1788,7 +1788,7 @@ m4_popdef([pkg_default]) m4_popdef([pkg_description]) ]) -m4trace:/usr/local/share/aclocal/pkg.m4:248: -1- AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) +m4trace:/usr/share/aclocal/pkg.m4:248: -1- AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], @@ -1798,7 +1798,7 @@ m4_popdef([pkg_default]) m4_popdef([pkg_description]) ]) -m4trace:/usr/local/share/aclocal/pkg.m4:267: -1- AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +m4trace:/usr/share/aclocal/pkg.m4:267: -1- AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) @@ -1806,17 +1806,58 @@ AS_VAR_IF([$1], [""], [$5], [$4])dnl ]) -m4trace:/usr/local/share/aclocal-1.16/amversion.m4:14: -1- AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.16' +m4trace:/usr/share/aclocal/pkg.m4:285: -1- AC_DEFUN([PKG_WITH_MODULES], [ +m4_pushdef([with_arg], m4_tolower([$1])) + +m4_pushdef([description], + [m4_default([$5], [build with ]with_arg[ support])]) + +m4_pushdef([def_arg], [m4_default([$6], [auto])]) +m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes]) +m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no]) + +m4_case(def_arg, + [yes],[m4_pushdef([with_without], [--without-]with_arg)], + [m4_pushdef([with_without],[--with-]with_arg)]) + +AC_ARG_WITH(with_arg, + AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),, + [AS_TR_SH([with_]with_arg)=def_arg]) + +AS_CASE([$AS_TR_SH([with_]with_arg)], + [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)], + [auto],[PKG_CHECK_MODULES([$1],[$2], + [m4_n([def_action_if_found]) $3], + [m4_n([def_action_if_not_found]) $4])]) + +m4_popdef([with_arg]) +m4_popdef([description]) +m4_popdef([def_arg]) + +]) +m4trace:/usr/share/aclocal/pkg.m4:322: -1- AC_DEFUN([PKG_HAVE_WITH_MODULES], [ +PKG_WITH_MODULES([$1],[$2],,,[$3],[$4]) + +AM_CONDITIONAL([HAVE_][$1], + [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"]) +]) +m4trace:/usr/share/aclocal/pkg.m4:337: -1- AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES], [ +PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4]) + +AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"], + [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])]) +]) +m4trace:/usr/share/aclocal-1.16/amversion.m4:14: -1- AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.16' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.16.3], [], +m4_if([$1], [1.16.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) -m4trace:/usr/local/share/aclocal-1.16/amversion.m4:33: -1- AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.16.3])dnl +m4trace:/usr/share/aclocal-1.16/amversion.m4:33: -1- AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.16.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) -m4trace:/usr/local/share/aclocal-1.16/ar-lib.m4:13: -1- AC_DEFUN([AM_PROG_AR], [AC_BEFORE([$0], [LT_INIT])dnl +m4trace:/usr/share/aclocal-1.16/ar-lib.m4:13: -1- AC_DEFUN([AM_PROG_AR], [AC_BEFORE([$0], [LT_INIT])dnl AC_BEFORE([$0], [AC_PROG_LIBTOOL])dnl AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([ar-lib])dnl @@ -1863,11 +1904,11 @@ esac AC_SUBST([AR])dnl ]) -m4trace:/usr/local/share/aclocal-1.16/auxdir.m4:47: -1- AC_DEFUN([AM_AUX_DIR_EXPAND], [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +m4trace:/usr/share/aclocal-1.16/auxdir.m4:47: -1- AC_DEFUN([AM_AUX_DIR_EXPAND], [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) -m4trace:/usr/local/share/aclocal-1.16/cond.m4:12: -1- AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl +m4trace:/usr/share/aclocal-1.16/cond.m4:12: -1- AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl @@ -1887,7 +1928,7 @@ AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) -m4trace:/usr/local/share/aclocal-1.16/depend.m4:26: -1- AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl +m4trace:/usr/share/aclocal-1.16/depend.m4:26: -1- AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl @@ -2017,10 +2058,10 @@ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) -m4trace:/usr/local/share/aclocal-1.16/depend.m4:163: -1- AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl +m4trace:/usr/share/aclocal-1.16/depend.m4:163: -1- AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) -m4trace:/usr/local/share/aclocal-1.16/depend.m4:171: -1- AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE([dependency-tracking], [dnl +m4trace:/usr/share/aclocal-1.16/depend.m4:171: -1- AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE([dependency-tracking], [dnl AS_HELP_STRING( [--enable-dependency-tracking], [do not reject slow dependency extractors]) @@ -2038,7 +2079,7 @@ AC_SUBST([am__nodep])dnl _AM_SUBST_NOTMAKE([am__nodep])dnl ]) -m4trace:/usr/local/share/aclocal-1.16/depout.m4:11: -1- AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ +m4trace:/usr/share/aclocal-1.16/depout.m4:11: -1- AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. @@ -2068,9 +2109,7 @@ done if test $am_rc -ne 0; then AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments - for automatic dependency tracking. If GNU make was not used, consider - re-running the configure script with MAKE="gmake" (or whatever is - necessary). You can also try re-running configure with the + for automatic dependency tracking. Try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking).]) fi @@ -2081,10 +2120,10 @@ rm -f conftest-deps.mk } ]) -m4trace:/usr/local/share/aclocal-1.16/depout.m4:64: -1- AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], +m4trace:/usr/share/aclocal-1.16/depout.m4:62: -1- AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])]) -m4trace:/usr/local/share/aclocal-1.16/init.m4:29: -1- AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl +m4trace:/usr/share/aclocal-1.16/init.m4:29: -1- AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl @@ -2226,7 +2265,7 @@ dnl backward compatibility and to allow trailing 'dnl'-style comments dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. ]) -m4trace:/usr/local/share/aclocal-1.16/init.m4:186: -1- AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. +m4trace:/usr/share/aclocal-1.16/init.m4:186: -1- AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do @@ -2238,7 +2277,7 @@ esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) -m4trace:/usr/local/share/aclocal-1.16/install-sh.m4:11: -1- AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +m4trace:/usr/share/aclocal-1.16/install-sh.m4:11: -1- AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) @@ -2248,7 +2287,7 @@ esac fi AC_SUBST([install_sh])]) -m4trace:/usr/local/share/aclocal-1.16/lead-dot.m4:10: -1- AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null +m4trace:/usr/share/aclocal-1.16/lead-dot.m4:10: -1- AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. @@ -2257,7 +2296,7 @@ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) -m4trace:/usr/local/share/aclocal-1.16/make.m4:13: -1- AC_DEFUN([AM_MAKE_INCLUDE], [AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive]) +m4trace:/usr/share/aclocal-1.16/make.m4:13: -1- AC_DEFUN([AM_MAKE_INCLUDE], [AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive]) cat > confinc.mk << 'END' am__doit: @echo this is the am__doit target >confinc.out @@ -2285,14 +2324,19 @@ rm -f confinc.* confmf.* AC_MSG_RESULT([${_am_result}]) AC_SUBST([am__include])]) -m4trace:/usr/local/share/aclocal-1.16/make.m4:42: -1- m4_pattern_allow([^am__quote$]) -m4trace:/usr/local/share/aclocal-1.16/missing.m4:11: -1- AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) +m4trace:/usr/share/aclocal-1.16/make.m4:42: -1- m4_pattern_allow([^am__quote$]) +m4trace:/usr/share/aclocal-1.16/missing.m4:11: -1- AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) -m4trace:/usr/local/share/aclocal-1.16/missing.m4:20: -1- AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +m4trace:/usr/share/aclocal-1.16/missing.m4:20: -1- AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then - MISSING="\${SHELL} '$am_aux_dir/missing'" + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then @@ -2302,11 +2346,11 @@ AC_MSG_WARN(['missing' script is too old or missing]) fi ]) -m4trace:/usr/local/share/aclocal-1.16/options.m4:11: -1- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) -m4trace:/usr/local/share/aclocal-1.16/options.m4:17: -1- AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) -m4trace:/usr/local/share/aclocal-1.16/options.m4:23: -1- AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) -m4trace:/usr/local/share/aclocal-1.16/options.m4:29: -1- AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) -m4trace:/usr/local/share/aclocal-1.16/prog-cc-c-o.m4:12: -1- AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +m4trace:/usr/share/aclocal-1.16/options.m4:11: -1- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) +m4trace:/usr/share/aclocal-1.16/options.m4:17: -1- AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) +m4trace:/usr/share/aclocal-1.16/options.m4:23: -1- AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) +m4trace:/usr/share/aclocal-1.16/options.m4:29: -1- AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) +m4trace:/usr/share/aclocal-1.16/prog-cc-c-o.m4:12: -1- AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl AC_LANG_PUSH([C])dnl AC_CACHE_CHECK( @@ -2338,13 +2382,13 @@ CC="$am_aux_dir/compile $CC" fi AC_LANG_POP([C])]) -m4trace:/usr/local/share/aclocal-1.16/prog-cc-c-o.m4:47: -1- AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) -m4trace:/usr/local/share/aclocal-1.16/runlog.m4:12: -1- AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD +m4trace:/usr/share/aclocal-1.16/prog-cc-c-o.m4:47: -1- AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) +m4trace:/usr/share/aclocal-1.16/runlog.m4:12: -1- AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) -m4trace:/usr/local/share/aclocal-1.16/sanity.m4:11: -1- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) +m4trace:/usr/share/aclocal-1.16/sanity.m4:11: -1- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' @@ -2415,7 +2459,7 @@ AC_MSG_RESULT([done])]) rm -f conftest.file ]) -m4trace:/usr/local/share/aclocal-1.16/silent.m4:12: -1- AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl +m4trace:/usr/share/aclocal-1.16/silent.m4:12: -1- AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) @@ -2463,7 +2507,7 @@ AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) -m4trace:/usr/local/share/aclocal-1.16/strip.m4:17: -1- AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +m4trace:/usr/share/aclocal-1.16/strip.m4:17: -1- AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake @@ -2474,9 +2518,9 @@ fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) -m4trace:/usr/local/share/aclocal-1.16/substnot.m4:12: -1- AC_DEFUN([_AM_SUBST_NOTMAKE]) -m4trace:/usr/local/share/aclocal-1.16/substnot.m4:17: -1- AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) -m4trace:/usr/local/share/aclocal-1.16/tar.m4:23: -1- AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used +m4trace:/usr/share/aclocal-1.16/substnot.m4:12: -1- AC_DEFUN([_AM_SUBST_NOTMAKE]) +m4trace:/usr/share/aclocal-1.16/substnot.m4:17: -1- AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) +m4trace:/usr/share/aclocal-1.16/tar.m4:23: -1- AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) @@ -2658,7 +2702,7 @@ m4trace:configure.ac:45: -1- AM_INIT_AUTOMAKE([foreign]) m4trace:configure.ac:45: -1- m4_pattern_allow([^AM_[A-Z]+FLAGS$]) m4trace:configure.ac:45: -1- AM_SET_CURRENT_AUTOMAKE_VERSION -m4trace:configure.ac:45: -1- AM_AUTOMAKE_VERSION([1.16.3]) +m4trace:configure.ac:45: -1- AM_AUTOMAKE_VERSION([1.16.1]) m4trace:configure.ac:45: -1- _AM_AUTOCONF_VERSION([2.69]) m4trace:configure.ac:45: -1- m4_pattern_allow([^INSTALL_PROGRAM$]) m4trace:configure.ac:45: -1- m4_pattern_allow([^INSTALL_SCRIPT$]) @@ -2798,7 +2842,7 @@ m4trace:configure.ac:50: -1- m4_pattern_allow([^AR$]) m4trace:configure.ac:51: -1- AC_PROG_LIBTOOL m4trace:configure.ac:51: -1- _m4_warn([obsolete], [The macro `AC_PROG_LIBTOOL' is obsolete. -You should run autoupdate.], [/usr/local/share/aclocal/libtool.m4:99: AC_PROG_LIBTOOL is expanded from... +You should run autoupdate.], [/usr/share/aclocal/libtool.m4:99: AC_PROG_LIBTOOL is expanded from... configure.ac:51: the top level]) m4trace:configure.ac:51: -1- LT_INIT m4trace:configure.ac:51: -1- m4_pattern_forbid([^_?LT_[A-Z_]+$]) @@ -2894,139 +2938,139 @@ _LT_TAGVAR(lt_prog_compiler_can_build_shared, CXX)=no]) m4trace:configure.ac:51: -1- _LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], [lt_cv_prog_compiler_static_works_CXX], [$lt_tmp_static_flag], [], [_LT_TAGVAR(lt_prog_compiler_static, CXX)=]) m4trace:configure.ac:51: -1- m4_pattern_allow([^LT_SYS_LIBRARY_PATH$]) -m4trace:configure.ac:73: -1- m4_pattern_allow([^MTCR_CONF_DIR$]) -m4trace:configure.ac:74: -1- m4_pattern_allow([^LDL$]) -m4trace:configure.ac:75: -1- m4_pattern_allow([^default_en_inband$]) -m4trace:configure.ac:88: -1- m4_pattern_allow([^COMPILER_FPIC$]) -m4trace:configure.ac:98: -1- m4_pattern_allow([^HAVE_LIBZ$]) -m4trace:configure.ac:111: -1- m4_pattern_allow([^HAVE_LIBZ$]) -m4trace:configure.ac:113: -1- m4_pattern_allow([^HAVE_LIBBOOST_REGEX$]) -m4trace:configure.ac:126: -1- m4_pattern_allow([^HAVE_EXPAT_H$]) -m4trace:configure.ac:133: -1- m4_pattern_allow([^HAVE_BOOST_FOREACH_HPP$]) -m4trace:configure.ac:138: -1- m4_pattern_allow([^CURL_INC_DIR$]) -m4trace:configure.ac:140: -1- AM_CONDITIONAL([ENABLE_FWMGR], [test "x$enable_fw_mgr" = "xyes" ]) -m4trace:configure.ac:140: -1- m4_pattern_allow([^ENABLE_FWMGR_TRUE$]) -m4trace:configure.ac:140: -1- m4_pattern_allow([^ENABLE_FWMGR_FALSE$]) -m4trace:configure.ac:140: -1- _AM_SUBST_NOTMAKE([ENABLE_FWMGR_TRUE]) -m4trace:configure.ac:140: -1- _AM_SUBST_NOTMAKE([ENABLE_FWMGR_FALSE]) -m4trace:configure.ac:141: -1- m4_pattern_allow([^ENABLE_FWMGR$]) -m4trace:configure.ac:143: -1- m4_pattern_allow([^ENABLE_FWMGR$]) -m4trace:configure.ac:160: -1- m4_pattern_allow([^HAVE_LIBXML2$]) -m4trace:configure.ac:164: -1- AM_CONDITIONAL([DISABLE_XML2], [test "x$enable_xml2" = "xno"]) -m4trace:configure.ac:164: -1- m4_pattern_allow([^DISABLE_XML2_TRUE$]) -m4trace:configure.ac:164: -1- m4_pattern_allow([^DISABLE_XML2_FALSE$]) -m4trace:configure.ac:164: -1- _AM_SUBST_NOTMAKE([DISABLE_XML2_TRUE]) -m4trace:configure.ac:164: -1- _AM_SUBST_NOTMAKE([DISABLE_XML2_FALSE]) -m4trace:configure.ac:165: -1- m4_pattern_allow([^DISABLE_XML2$]) -m4trace:configure.ac:167: -1- AM_CONDITIONAL([ENABLE_DC], [test "x$enable_dc" = "xyes"]) -m4trace:configure.ac:167: -1- m4_pattern_allow([^ENABLE_DC_TRUE$]) -m4trace:configure.ac:167: -1- m4_pattern_allow([^ENABLE_DC_FALSE$]) -m4trace:configure.ac:167: -1- _AM_SUBST_NOTMAKE([ENABLE_DC_TRUE]) -m4trace:configure.ac:167: -1- _AM_SUBST_NOTMAKE([ENABLE_DC_FALSE]) -m4trace:configure.ac:168: -1- m4_pattern_allow([^ENABLE_DC$]) -m4trace:configure.ac:183: -1- m4_pattern_allow([^MAD_IFC$]) -m4trace:configure.ac:185: -1- AM_CONDITIONAL([ENABLE_INBAND], [test "x$enable_inband" = "xyes"]) -m4trace:configure.ac:185: -1- m4_pattern_allow([^ENABLE_INBAND_TRUE$]) -m4trace:configure.ac:185: -1- m4_pattern_allow([^ENABLE_INBAND_FALSE$]) -m4trace:configure.ac:185: -1- _AM_SUBST_NOTMAKE([ENABLE_INBAND_TRUE]) -m4trace:configure.ac:185: -1- _AM_SUBST_NOTMAKE([ENABLE_INBAND_FALSE]) -m4trace:configure.ac:235: -1- m4_pattern_allow([^TOOLS_CRYPTO$]) -m4trace:configure.ac:236: -1- AM_CONDITIONAL([ENABLE_OPENSSL], [test "x$enable_openssl" = "xyes" ]) -m4trace:configure.ac:236: -1- m4_pattern_allow([^ENABLE_OPENSSL_TRUE$]) -m4trace:configure.ac:236: -1- m4_pattern_allow([^ENABLE_OPENSSL_FALSE$]) -m4trace:configure.ac:236: -1- _AM_SUBST_NOTMAKE([ENABLE_OPENSSL_TRUE]) -m4trace:configure.ac:236: -1- _AM_SUBST_NOTMAKE([ENABLE_OPENSSL_FALSE]) -m4trace:configure.ac:237: -1- AM_CONDITIONAL([ENABLE_CS], [test "x$enable_cs" = "xyes" || test "x$enable_openssl" = "xyes" ]) -m4trace:configure.ac:237: -1- m4_pattern_allow([^ENABLE_CS_TRUE$]) -m4trace:configure.ac:237: -1- m4_pattern_allow([^ENABLE_CS_FALSE$]) -m4trace:configure.ac:237: -1- _AM_SUBST_NOTMAKE([ENABLE_CS_TRUE]) -m4trace:configure.ac:237: -1- _AM_SUBST_NOTMAKE([ENABLE_CS_FALSE]) -m4trace:configure.ac:253: -1- m4_pattern_allow([^HAVE_INIPARSER_H$]) -m4trace:configure.ac:263: -1- m4_pattern_allow([^INIPARSER_CFLAGS$]) -m4trace:configure.ac:265: -1- m4_pattern_allow([^INIPARSER_LIBS$]) -m4trace:configure.ac:268: -1- AM_CONDITIONAL([USE_LOCAL_INIPARSER], [test "$INIPARSER_SYSTEM_AVAILABLE" = no]) -m4trace:configure.ac:268: -1- m4_pattern_allow([^USE_LOCAL_INIPARSER_TRUE$]) -m4trace:configure.ac:268: -1- m4_pattern_allow([^USE_LOCAL_INIPARSER_FALSE$]) -m4trace:configure.ac:268: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_INIPARSER_TRUE]) -m4trace:configure.ac:268: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_INIPARSER_FALSE]) -m4trace:configure.ac:274: -1- PKG_CHECK_MODULES([JSON], [jsoncpp], [JSON_SYSTEM_AVAILABLE="yes"], [ +m4trace:configure.ac:81: -1- m4_pattern_allow([^MTCR_CONF_DIR$]) +m4trace:configure.ac:82: -1- m4_pattern_allow([^LDL$]) +m4trace:configure.ac:83: -1- m4_pattern_allow([^default_en_inband$]) +m4trace:configure.ac:96: -1- m4_pattern_allow([^COMPILER_FPIC$]) +m4trace:configure.ac:106: -1- m4_pattern_allow([^HAVE_LIBZ$]) +m4trace:configure.ac:119: -1- m4_pattern_allow([^HAVE_LIBZ$]) +m4trace:configure.ac:121: -1- m4_pattern_allow([^HAVE_LIBBOOST_REGEX$]) +m4trace:configure.ac:134: -1- m4_pattern_allow([^HAVE_EXPAT_H$]) +m4trace:configure.ac:141: -1- m4_pattern_allow([^HAVE_BOOST_FOREACH_HPP$]) +m4trace:configure.ac:146: -1- m4_pattern_allow([^CURL_INC_DIR$]) +m4trace:configure.ac:148: -1- AM_CONDITIONAL([ENABLE_FWMGR], [test "x$enable_fw_mgr" = "xyes" ]) +m4trace:configure.ac:148: -1- m4_pattern_allow([^ENABLE_FWMGR_TRUE$]) +m4trace:configure.ac:148: -1- m4_pattern_allow([^ENABLE_FWMGR_FALSE$]) +m4trace:configure.ac:148: -1- _AM_SUBST_NOTMAKE([ENABLE_FWMGR_TRUE]) +m4trace:configure.ac:148: -1- _AM_SUBST_NOTMAKE([ENABLE_FWMGR_FALSE]) +m4trace:configure.ac:149: -1- m4_pattern_allow([^ENABLE_FWMGR$]) +m4trace:configure.ac:151: -1- m4_pattern_allow([^ENABLE_FWMGR$]) +m4trace:configure.ac:168: -1- m4_pattern_allow([^HAVE_LIBXML2$]) +m4trace:configure.ac:172: -1- AM_CONDITIONAL([DISABLE_XML2], [test "x$enable_xml2" = "xno"]) +m4trace:configure.ac:172: -1- m4_pattern_allow([^DISABLE_XML2_TRUE$]) +m4trace:configure.ac:172: -1- m4_pattern_allow([^DISABLE_XML2_FALSE$]) +m4trace:configure.ac:172: -1- _AM_SUBST_NOTMAKE([DISABLE_XML2_TRUE]) +m4trace:configure.ac:172: -1- _AM_SUBST_NOTMAKE([DISABLE_XML2_FALSE]) +m4trace:configure.ac:173: -1- m4_pattern_allow([^DISABLE_XML2$]) +m4trace:configure.ac:175: -1- AM_CONDITIONAL([ENABLE_DC], [test "x$enable_dc" = "xyes"]) +m4trace:configure.ac:175: -1- m4_pattern_allow([^ENABLE_DC_TRUE$]) +m4trace:configure.ac:175: -1- m4_pattern_allow([^ENABLE_DC_FALSE$]) +m4trace:configure.ac:175: -1- _AM_SUBST_NOTMAKE([ENABLE_DC_TRUE]) +m4trace:configure.ac:175: -1- _AM_SUBST_NOTMAKE([ENABLE_DC_FALSE]) +m4trace:configure.ac:176: -1- m4_pattern_allow([^ENABLE_DC$]) +m4trace:configure.ac:191: -1- m4_pattern_allow([^MAD_IFC$]) +m4trace:configure.ac:193: -1- AM_CONDITIONAL([ENABLE_INBAND], [test "x$enable_inband" = "xyes"]) +m4trace:configure.ac:193: -1- m4_pattern_allow([^ENABLE_INBAND_TRUE$]) +m4trace:configure.ac:193: -1- m4_pattern_allow([^ENABLE_INBAND_FALSE$]) +m4trace:configure.ac:193: -1- _AM_SUBST_NOTMAKE([ENABLE_INBAND_TRUE]) +m4trace:configure.ac:193: -1- _AM_SUBST_NOTMAKE([ENABLE_INBAND_FALSE]) +m4trace:configure.ac:243: -1- m4_pattern_allow([^TOOLS_CRYPTO$]) +m4trace:configure.ac:244: -1- AM_CONDITIONAL([ENABLE_OPENSSL], [test "x$enable_openssl" = "xyes" ]) +m4trace:configure.ac:244: -1- m4_pattern_allow([^ENABLE_OPENSSL_TRUE$]) +m4trace:configure.ac:244: -1- m4_pattern_allow([^ENABLE_OPENSSL_FALSE$]) +m4trace:configure.ac:244: -1- _AM_SUBST_NOTMAKE([ENABLE_OPENSSL_TRUE]) +m4trace:configure.ac:244: -1- _AM_SUBST_NOTMAKE([ENABLE_OPENSSL_FALSE]) +m4trace:configure.ac:245: -1- AM_CONDITIONAL([ENABLE_CS], [test "x$enable_cs" = "xyes" || test "x$enable_openssl" = "xyes" ]) +m4trace:configure.ac:245: -1- m4_pattern_allow([^ENABLE_CS_TRUE$]) +m4trace:configure.ac:245: -1- m4_pattern_allow([^ENABLE_CS_FALSE$]) +m4trace:configure.ac:245: -1- _AM_SUBST_NOTMAKE([ENABLE_CS_TRUE]) +m4trace:configure.ac:245: -1- _AM_SUBST_NOTMAKE([ENABLE_CS_FALSE]) +m4trace:configure.ac:261: -1- m4_pattern_allow([^HAVE_INIPARSER_H$]) +m4trace:configure.ac:271: -1- m4_pattern_allow([^INIPARSER_CFLAGS$]) +m4trace:configure.ac:273: -1- m4_pattern_allow([^INIPARSER_LIBS$]) +m4trace:configure.ac:276: -1- AM_CONDITIONAL([USE_LOCAL_INIPARSER], [test "$INIPARSER_SYSTEM_AVAILABLE" = no]) +m4trace:configure.ac:276: -1- m4_pattern_allow([^USE_LOCAL_INIPARSER_TRUE$]) +m4trace:configure.ac:276: -1- m4_pattern_allow([^USE_LOCAL_INIPARSER_FALSE$]) +m4trace:configure.ac:276: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_INIPARSER_TRUE]) +m4trace:configure.ac:276: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_INIPARSER_FALSE]) +m4trace:configure.ac:282: -1- PKG_CHECK_MODULES([JSON], [jsoncpp], [JSON_SYSTEM_AVAILABLE="yes"], [ JSON_SYSTEM_AVAILABLE="no" JSON_CFLAGS='-I$(top_srcdir)/ext_libs/json' AC_SUBST(JSON_CFLAGS) - JSON_LIBS='$(top_builddir)/ext_libs/json/libjson.a' + JSON_LIBS='$(top_builddir)/ext_libs/json/libjson.la' AC_SUBST(JSON_LIBS) ]) -m4trace:configure.ac:274: -1- PKG_PROG_PKG_CONFIG -m4trace:configure.ac:274: -1- m4_pattern_forbid([^_?PKG_[A-Z_]+$]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^PKG_CONFIG$]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^PKG_CONFIG_PATH$]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^PKG_CONFIG_LIBDIR$]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^PKG_CONFIG$]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^JSON_CFLAGS$]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^JSON_LIBS$]) -m4trace:configure.ac:274: -1- PKG_CHECK_EXISTS([jsoncpp], [pkg_cv_[]JSON_CFLAGS=`$PKG_CONFIG --[]cflags "jsoncpp" 2>/dev/null` +m4trace:configure.ac:282: -1- PKG_PROG_PKG_CONFIG +m4trace:configure.ac:282: -1- m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^PKG_CONFIG$]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^PKG_CONFIG_PATH$]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^PKG_CONFIG_LIBDIR$]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^PKG_CONFIG$]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^JSON_CFLAGS$]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^JSON_LIBS$]) +m4trace:configure.ac:282: -1- PKG_CHECK_EXISTS([jsoncpp], [pkg_cv_[]JSON_CFLAGS=`$PKG_CONFIG --[]cflags "jsoncpp" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) -m4trace:configure.ac:274: -1- PKG_CHECK_EXISTS([jsoncpp], [pkg_cv_[]JSON_LIBS=`$PKG_CONFIG --[]libs "jsoncpp" 2>/dev/null` +m4trace:configure.ac:282: -1- PKG_CHECK_EXISTS([jsoncpp], [pkg_cv_[]JSON_LIBS=`$PKG_CONFIG --[]libs "jsoncpp" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) -m4trace:configure.ac:274: -1- _PKG_SHORT_ERRORS_SUPPORTED -m4trace:configure.ac:274: -1- m4_pattern_allow([^JSON_CFLAGS$]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^JSON_LIBS$]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^JSON_CFLAGS$]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^JSON_LIBS$]) -m4trace:configure.ac:281: -1- AM_CONDITIONAL([USE_LOCAL_JSON], [test "$JSON_SYSTEM_AVAILABLE" = no]) -m4trace:configure.ac:281: -1- m4_pattern_allow([^USE_LOCAL_JSON_TRUE$]) -m4trace:configure.ac:281: -1- m4_pattern_allow([^USE_LOCAL_JSON_FALSE$]) -m4trace:configure.ac:281: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_JSON_TRUE]) -m4trace:configure.ac:281: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_JSON_FALSE]) -m4trace:configure.ac:283: -1- m4_pattern_allow([^MUPARSER_CFLAGS$]) -m4trace:configure.ac:283: -1- m4_pattern_allow([^MUPARSER_LIBS$]) -m4trace:configure.ac:290: -1- AM_CONDITIONAL([USE_LOCAL_MUPARSER], [test "$MUPARSER_SYSTEM_AVAILABLE" = no]) -m4trace:configure.ac:290: -1- m4_pattern_allow([^USE_LOCAL_MUPARSER_TRUE$]) -m4trace:configure.ac:290: -1- m4_pattern_allow([^USE_LOCAL_MUPARSER_FALSE$]) -m4trace:configure.ac:290: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_MUPARSER_TRUE]) -m4trace:configure.ac:290: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_MUPARSER_FALSE]) -m4trace:configure.ac:292: -1- m4_pattern_allow([^SQLITE_CFLAGS$]) -m4trace:configure.ac:292: -1- m4_pattern_allow([^SQLITE_LIBS$]) -m4trace:configure.ac:299: -1- AM_CONDITIONAL([USE_LOCAL_SQLITE], [test "$SQLITE_SYSTEM_AVAILABLE" = no]) -m4trace:configure.ac:299: -1- m4_pattern_allow([^USE_LOCAL_SQLITE_TRUE$]) -m4trace:configure.ac:299: -1- m4_pattern_allow([^USE_LOCAL_SQLITE_FALSE$]) -m4trace:configure.ac:299: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_SQLITE_TRUE]) -m4trace:configure.ac:299: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_SQLITE_FALSE]) -m4trace:configure.ac:315: -1- AX_CHECK_COMPILE_FLAG([-Wno-format-overflow], [CFLAGS="$CFLAGS -Wno-format-overflow"], [], []) -m4trace:configure.ac:316: -1- AX_CHECK_COMPILE_FLAG([-Wno-implicit-fallthrough], [CFLAGS="$CFLAGS -Wno-implicit-fallthrough"], [], []) -m4trace:configure.ac:317: -1- AX_CHECK_COMPILE_FLAG([-Wno-cast-function-type], [CFLAGS="$CFLAGS -Wno-cast-function-type"], [], []) -m4trace:configure.ac:324: -1- AX_CHECK_COMPILE_FLAG([-Wno-implicit-fallthrough], [CXXFLAGS="$CXXFLAGS -Wno-implicit-fallthrough"], [], []) -m4trace:configure.ac:325: -1- AX_CHECK_COMPILE_FLAG([-Wno-format-overflow], [CXXFLAGS="$CXXFLAGS -Wno-format-overflow"], [], []) -m4trace:configure.ac:326: -1- AX_CHECK_COMPILE_FLAG([-Wno-cast-function-type], [CXXFLAGS="$CXXFLAGS -Wno-cast-function-type"], [], []) -m4trace:configure.ac:327: -1- AX_CHECK_COMPILE_FLAG([-Wno-class-memaccess], [CXXFLAGS="$CXXFLAGS -Wno-class-memaccess"], [], []) -m4trace:configure.ac:335: -1- m4_pattern_allow([^TOOLS_BUILD_TIME$]) -m4trace:configure.ac:337: -1- m4_pattern_allow([^MSTFLINT_VERSION_STR$]) -m4trace:configure.ac:342: -1- m4_pattern_allow([^MSTFLINT_VERSION_STR$]) -m4trace:configure.ac:343: -1- m4_pattern_allow([^MSTFLINT_VERSION_STR$]) -m4trace:configure.ac:344: -1- m4_pattern_allow([^MSTFLINT_VERSION_STR$]) -m4trace:configure.ac:347: -1- m4_pattern_allow([^TOOLS_GIT_SHA$]) -m4trace:configure.ac:351: -1- m4_pattern_allow([^TOOLS_GIT_SHA$]) -m4trace:configure.ac:352: -1- m4_pattern_allow([^TOOLS_GIT_SHA$]) -m4trace:configure.ac:353: -1- m4_pattern_allow([^TOOLS_GIT_SHA$]) -m4trace:configure.ac:377: -1- m4_pattern_allow([^FW_MGR_TOOLS$]) -m4trace:configure.ac:401: -1- m4_pattern_allow([^ADABE_TOOLS$]) -m4trace:configure.ac:402: -1- m4_pattern_allow([^ADABE_DBS$]) -m4trace:configure.ac:403: -1- m4_pattern_allow([^ADABE_DBS_EXTRA_DIST$]) -m4trace:configure.ac:404: -1- m4_pattern_allow([^XZ_UTILS_DIR$]) -m4trace:configure.ac:415: -1- _m4_warn([obsolete], [AC_OUTPUT should be used without arguments. +m4trace:configure.ac:282: -1- _PKG_SHORT_ERRORS_SUPPORTED +m4trace:configure.ac:282: -1- m4_pattern_allow([^JSON_CFLAGS$]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^JSON_LIBS$]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^JSON_CFLAGS$]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^JSON_LIBS$]) +m4trace:configure.ac:289: -1- AM_CONDITIONAL([USE_LOCAL_JSON], [test "$JSON_SYSTEM_AVAILABLE" = no]) +m4trace:configure.ac:289: -1- m4_pattern_allow([^USE_LOCAL_JSON_TRUE$]) +m4trace:configure.ac:289: -1- m4_pattern_allow([^USE_LOCAL_JSON_FALSE$]) +m4trace:configure.ac:289: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_JSON_TRUE]) +m4trace:configure.ac:289: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_JSON_FALSE]) +m4trace:configure.ac:291: -1- m4_pattern_allow([^MUPARSER_CFLAGS$]) +m4trace:configure.ac:291: -1- m4_pattern_allow([^MUPARSER_LIBS$]) +m4trace:configure.ac:298: -1- AM_CONDITIONAL([USE_LOCAL_MUPARSER], [test "$MUPARSER_SYSTEM_AVAILABLE" = no]) +m4trace:configure.ac:298: -1- m4_pattern_allow([^USE_LOCAL_MUPARSER_TRUE$]) +m4trace:configure.ac:298: -1- m4_pattern_allow([^USE_LOCAL_MUPARSER_FALSE$]) +m4trace:configure.ac:298: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_MUPARSER_TRUE]) +m4trace:configure.ac:298: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_MUPARSER_FALSE]) +m4trace:configure.ac:300: -1- m4_pattern_allow([^SQLITE_CFLAGS$]) +m4trace:configure.ac:300: -1- m4_pattern_allow([^SQLITE_LIBS$]) +m4trace:configure.ac:307: -1- AM_CONDITIONAL([USE_LOCAL_SQLITE], [test "$SQLITE_SYSTEM_AVAILABLE" = no]) +m4trace:configure.ac:307: -1- m4_pattern_allow([^USE_LOCAL_SQLITE_TRUE$]) +m4trace:configure.ac:307: -1- m4_pattern_allow([^USE_LOCAL_SQLITE_FALSE$]) +m4trace:configure.ac:307: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_SQLITE_TRUE]) +m4trace:configure.ac:307: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_SQLITE_FALSE]) +m4trace:configure.ac:334: -1- AX_CHECK_COMPILE_FLAG([-Wno-format-overflow], [CFLAGS="$CFLAGS -Wno-format-overflow"], [], []) +m4trace:configure.ac:335: -1- AX_CHECK_COMPILE_FLAG([-Wno-implicit-fallthrough], [CFLAGS="$CFLAGS -Wno-implicit-fallthrough"], [], []) +m4trace:configure.ac:336: -1- AX_CHECK_COMPILE_FLAG([-Wno-cast-function-type], [CFLAGS="$CFLAGS -Wno-cast-function-type"], [], []) +m4trace:configure.ac:343: -1- AX_CHECK_COMPILE_FLAG([-Wno-implicit-fallthrough], [CXXFLAGS="$CXXFLAGS -Wno-implicit-fallthrough"], [], []) +m4trace:configure.ac:344: -1- AX_CHECK_COMPILE_FLAG([-Wno-format-overflow], [CXXFLAGS="$CXXFLAGS -Wno-format-overflow"], [], []) +m4trace:configure.ac:345: -1- AX_CHECK_COMPILE_FLAG([-Wno-cast-function-type], [CXXFLAGS="$CXXFLAGS -Wno-cast-function-type"], [], []) +m4trace:configure.ac:346: -1- AX_CHECK_COMPILE_FLAG([-Wno-class-memaccess], [CXXFLAGS="$CXXFLAGS -Wno-class-memaccess"], [], []) +m4trace:configure.ac:354: -1- m4_pattern_allow([^TOOLS_BUILD_TIME$]) +m4trace:configure.ac:356: -1- m4_pattern_allow([^MSTFLINT_VERSION_STR$]) +m4trace:configure.ac:361: -1- m4_pattern_allow([^MSTFLINT_VERSION_STR$]) +m4trace:configure.ac:362: -1- m4_pattern_allow([^MSTFLINT_VERSION_STR$]) +m4trace:configure.ac:363: -1- m4_pattern_allow([^MSTFLINT_VERSION_STR$]) +m4trace:configure.ac:366: -1- m4_pattern_allow([^TOOLS_GIT_SHA$]) +m4trace:configure.ac:370: -1- m4_pattern_allow([^TOOLS_GIT_SHA$]) +m4trace:configure.ac:371: -1- m4_pattern_allow([^TOOLS_GIT_SHA$]) +m4trace:configure.ac:372: -1- m4_pattern_allow([^TOOLS_GIT_SHA$]) +m4trace:configure.ac:397: -1- m4_pattern_allow([^FW_MGR_TOOLS$]) +m4trace:configure.ac:421: -1- m4_pattern_allow([^ADABE_TOOLS$]) +m4trace:configure.ac:422: -1- m4_pattern_allow([^ADABE_DBS$]) +m4trace:configure.ac:423: -1- m4_pattern_allow([^ADABE_DBS_EXTRA_DIST$]) +m4trace:configure.ac:424: -1- m4_pattern_allow([^XZ_UTILS_DIR$]) +m4trace:configure.ac:435: -1- _m4_warn([obsolete], [AC_OUTPUT should be used without arguments. You should run autoupdate.], []) -m4trace:configure.ac:415: -1- m4_pattern_allow([^LIB@&t@OBJS$]) -m4trace:configure.ac:415: -1- m4_pattern_allow([^LTLIBOBJS$]) -m4trace:configure.ac:415: -1- AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"]) -m4trace:configure.ac:415: -1- m4_pattern_allow([^am__EXEEXT_TRUE$]) -m4trace:configure.ac:415: -1- m4_pattern_allow([^am__EXEEXT_FALSE$]) -m4trace:configure.ac:415: -1- _AM_SUBST_NOTMAKE([am__EXEEXT_TRUE]) -m4trace:configure.ac:415: -1- _AM_SUBST_NOTMAKE([am__EXEEXT_FALSE]) -m4trace:configure.ac:415: -1- _AC_AM_CONFIG_HEADER_HOOK(["$ac_file"]) -m4trace:configure.ac:415: -1- _AM_OUTPUT_DEPENDENCY_COMMANDS -m4trace:configure.ac:415: -1- AM_RUN_LOG([cd "$am_dirpart" \ +m4trace:configure.ac:435: -1- m4_pattern_allow([^LIB@&t@OBJS$]) +m4trace:configure.ac:435: -1- m4_pattern_allow([^LTLIBOBJS$]) +m4trace:configure.ac:435: -1- AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"]) +m4trace:configure.ac:435: -1- m4_pattern_allow([^am__EXEEXT_TRUE$]) +m4trace:configure.ac:435: -1- m4_pattern_allow([^am__EXEEXT_FALSE$]) +m4trace:configure.ac:435: -1- _AM_SUBST_NOTMAKE([am__EXEEXT_TRUE]) +m4trace:configure.ac:435: -1- _AM_SUBST_NOTMAKE([am__EXEEXT_FALSE]) +m4trace:configure.ac:435: -1- _AC_AM_CONFIG_HEADER_HOOK(["$ac_file"]) +m4trace:configure.ac:435: -1- _AM_OUTPUT_DEPENDENCY_COMMANDS +m4trace:configure.ac:435: -1- AM_RUN_LOG([cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles]) -m4trace:configure.ac:415: -1- _LT_PROG_LTMAIN +m4trace:configure.ac:435: -1- _LT_PROG_LTMAIN diff -Nru mstflint-4.17.0+1/autom4te.cache/traces.1 mstflint-4.21.0+1/autom4te.cache/traces.1 --- mstflint-4.17.0+1/autom4te.cache/traces.1 2021-06-29 08:51:06.000000000 +0000 +++ mstflint-4.21.0+1/autom4te.cache/traces.1 2022-07-31 09:34:41.000000000 +0000 @@ -1,8 +1,8 @@ -m4trace:aclocal.m4:10046: -1- AC_SUBST([am__quote]) -m4trace:aclocal.m4:10046: -1- AC_SUBST_TRACE([am__quote]) -m4trace:aclocal.m4:10046: -1- m4_pattern_allow([^am__quote$]) -m4trace:aclocal.m4:10498: -1- m4_include([m4/ax_check_compile_flag.m4]) -m4trace:configure.ac:34: -1- AC_INIT([mstflint], [4.17.0], [eranj@mellanox.co.il]) +m4trace:aclocal.m4:10115: -1- AC_SUBST([am__quote]) +m4trace:aclocal.m4:10115: -1- AC_SUBST_TRACE([am__quote]) +m4trace:aclocal.m4:10115: -1- m4_pattern_allow([^am__quote$]) +m4trace:aclocal.m4:10572: -1- m4_include([m4/ax_check_compile_flag.m4]) +m4trace:configure.ac:34: -1- AC_INIT([mstflint], [4.21.0], [eranj@mellanox.co.il]) m4trace:configure.ac:34: -1- m4_pattern_forbid([^_?A[CHUM]_]) m4trace:configure.ac:34: -1- m4_pattern_forbid([_AC_]) m4trace:configure.ac:34: -1- m4_pattern_forbid([^LIBOBJS$], [do not use LIBOBJS directly, use AC_LIBOBJ (see section `AC_LIBOBJ vs LIBOBJS']) @@ -167,7 +167,7 @@ m4trace:configure.ac:43: -1- AC_CONFIG_AUX_DIR([config]) m4trace:configure.ac:45: -1- AM_INIT_AUTOMAKE([foreign]) m4trace:configure.ac:45: -1- m4_pattern_allow([^AM_[A-Z]+FLAGS$]) -m4trace:configure.ac:45: -1- AM_AUTOMAKE_VERSION([1.16.3]) +m4trace:configure.ac:45: -1- AM_AUTOMAKE_VERSION([1.16.1]) m4trace:configure.ac:45: -1- AC_REQUIRE_AUX_FILE([install-sh]) m4trace:configure.ac:45: -1- AC_SUBST([INSTALL_PROGRAM]) m4trace:configure.ac:45: -1- AC_SUBST_TRACE([INSTALL_PROGRAM]) @@ -560,300 +560,300 @@ m4trace:configure.ac:51: -1- AC_SUBST_TRACE([LT_SYS_LIBRARY_PATH]) m4trace:configure.ac:51: -1- m4_pattern_allow([^LT_SYS_LIBRARY_PATH$]) m4trace:configure.ac:52: -1- AC_CONFIG_HEADERS([config.h ]) -m4trace:configure.ac:73: -1- AC_SUBST([MTCR_CONF_DIR]) -m4trace:configure.ac:73: -1- AC_SUBST_TRACE([MTCR_CONF_DIR]) -m4trace:configure.ac:73: -1- m4_pattern_allow([^MTCR_CONF_DIR$]) -m4trace:configure.ac:74: -1- AC_SUBST([LDL]) -m4trace:configure.ac:74: -1- AC_SUBST_TRACE([LDL]) -m4trace:configure.ac:74: -1- m4_pattern_allow([^LDL$]) -m4trace:configure.ac:75: -1- AC_SUBST([default_en_inband]) -m4trace:configure.ac:75: -1- AC_SUBST_TRACE([default_en_inband]) -m4trace:configure.ac:75: -1- m4_pattern_allow([^default_en_inband$]) -m4trace:configure.ac:88: -1- AC_SUBST([COMPILER_FPIC]) -m4trace:configure.ac:88: -1- AC_SUBST_TRACE([COMPILER_FPIC]) -m4trace:configure.ac:88: -1- m4_pattern_allow([^COMPILER_FPIC$]) -m4trace:configure.ac:98: -1- AH_OUTPUT([HAVE_LIBZ], [/* Define to 1 if you have the `z\' library (-lz). */ +m4trace:configure.ac:81: -1- AC_SUBST([MTCR_CONF_DIR]) +m4trace:configure.ac:81: -1- AC_SUBST_TRACE([MTCR_CONF_DIR]) +m4trace:configure.ac:81: -1- m4_pattern_allow([^MTCR_CONF_DIR$]) +m4trace:configure.ac:82: -1- AC_SUBST([LDL]) +m4trace:configure.ac:82: -1- AC_SUBST_TRACE([LDL]) +m4trace:configure.ac:82: -1- m4_pattern_allow([^LDL$]) +m4trace:configure.ac:83: -1- AC_SUBST([default_en_inband]) +m4trace:configure.ac:83: -1- AC_SUBST_TRACE([default_en_inband]) +m4trace:configure.ac:83: -1- m4_pattern_allow([^default_en_inband$]) +m4trace:configure.ac:96: -1- AC_SUBST([COMPILER_FPIC]) +m4trace:configure.ac:96: -1- AC_SUBST_TRACE([COMPILER_FPIC]) +m4trace:configure.ac:96: -1- m4_pattern_allow([^COMPILER_FPIC$]) +m4trace:configure.ac:106: -1- AH_OUTPUT([HAVE_LIBZ], [/* Define to 1 if you have the `z\' library (-lz). */ @%:@undef HAVE_LIBZ]) -m4trace:configure.ac:98: -1- AC_DEFINE_TRACE_LITERAL([HAVE_LIBZ]) -m4trace:configure.ac:98: -1- m4_pattern_allow([^HAVE_LIBZ$]) -m4trace:configure.ac:111: -1- AH_OUTPUT([HAVE_LIBZ], [/* Define to 1 if you have the `z\' library (-lz). */ +m4trace:configure.ac:106: -1- AC_DEFINE_TRACE_LITERAL([HAVE_LIBZ]) +m4trace:configure.ac:106: -1- m4_pattern_allow([^HAVE_LIBZ$]) +m4trace:configure.ac:119: -1- AH_OUTPUT([HAVE_LIBZ], [/* Define to 1 if you have the `z\' library (-lz). */ @%:@undef HAVE_LIBZ]) -m4trace:configure.ac:111: -1- AC_DEFINE_TRACE_LITERAL([HAVE_LIBZ]) -m4trace:configure.ac:111: -1- m4_pattern_allow([^HAVE_LIBZ$]) -m4trace:configure.ac:113: -1- AH_OUTPUT([HAVE_LIBBOOST_REGEX], [/* Define to 1 if you have the `boost_regex\' library (-lboost_regex). */ +m4trace:configure.ac:119: -1- AC_DEFINE_TRACE_LITERAL([HAVE_LIBZ]) +m4trace:configure.ac:119: -1- m4_pattern_allow([^HAVE_LIBZ$]) +m4trace:configure.ac:121: -1- AH_OUTPUT([HAVE_LIBBOOST_REGEX], [/* Define to 1 if you have the `boost_regex\' library (-lboost_regex). */ @%:@undef HAVE_LIBBOOST_REGEX]) -m4trace:configure.ac:113: -1- AC_DEFINE_TRACE_LITERAL([HAVE_LIBBOOST_REGEX]) -m4trace:configure.ac:113: -1- m4_pattern_allow([^HAVE_LIBBOOST_REGEX$]) -m4trace:configure.ac:126: -1- AH_OUTPUT([HAVE_EXPAT_H], [/* Define to 1 if you have the header file. */ +m4trace:configure.ac:121: -1- AC_DEFINE_TRACE_LITERAL([HAVE_LIBBOOST_REGEX]) +m4trace:configure.ac:121: -1- m4_pattern_allow([^HAVE_LIBBOOST_REGEX$]) +m4trace:configure.ac:134: -1- AH_OUTPUT([HAVE_EXPAT_H], [/* Define to 1 if you have the header file. */ @%:@undef HAVE_EXPAT_H]) -m4trace:configure.ac:126: -1- AC_DEFINE_TRACE_LITERAL([HAVE_EXPAT_H]) -m4trace:configure.ac:126: -1- m4_pattern_allow([^HAVE_EXPAT_H$]) -m4trace:configure.ac:133: -1- AH_OUTPUT([HAVE_BOOST_FOREACH_HPP], [/* Define to 1 if you have the header file. */ +m4trace:configure.ac:134: -1- AC_DEFINE_TRACE_LITERAL([HAVE_EXPAT_H]) +m4trace:configure.ac:134: -1- m4_pattern_allow([^HAVE_EXPAT_H$]) +m4trace:configure.ac:141: -1- AH_OUTPUT([HAVE_BOOST_FOREACH_HPP], [/* Define to 1 if you have the header file. */ @%:@undef HAVE_BOOST_FOREACH_HPP]) -m4trace:configure.ac:133: -1- AC_DEFINE_TRACE_LITERAL([HAVE_BOOST_FOREACH_HPP]) -m4trace:configure.ac:133: -1- m4_pattern_allow([^HAVE_BOOST_FOREACH_HPP$]) -m4trace:configure.ac:138: -1- AC_SUBST([CURL_INC_DIR]) -m4trace:configure.ac:138: -1- AC_SUBST_TRACE([CURL_INC_DIR]) -m4trace:configure.ac:138: -1- m4_pattern_allow([^CURL_INC_DIR$]) -m4trace:configure.ac:140: -1- AM_CONDITIONAL([ENABLE_FWMGR], [test "x$enable_fw_mgr" = "xyes" ]) -m4trace:configure.ac:140: -1- AC_SUBST([ENABLE_FWMGR_TRUE]) -m4trace:configure.ac:140: -1- AC_SUBST_TRACE([ENABLE_FWMGR_TRUE]) -m4trace:configure.ac:140: -1- m4_pattern_allow([^ENABLE_FWMGR_TRUE$]) -m4trace:configure.ac:140: -1- AC_SUBST([ENABLE_FWMGR_FALSE]) -m4trace:configure.ac:140: -1- AC_SUBST_TRACE([ENABLE_FWMGR_FALSE]) -m4trace:configure.ac:140: -1- m4_pattern_allow([^ENABLE_FWMGR_FALSE$]) -m4trace:configure.ac:140: -1- _AM_SUBST_NOTMAKE([ENABLE_FWMGR_TRUE]) -m4trace:configure.ac:140: -1- _AM_SUBST_NOTMAKE([ENABLE_FWMGR_FALSE]) -m4trace:configure.ac:141: -1- AC_SUBST([ENABLE_FWMGR]) -m4trace:configure.ac:141: -1- AC_SUBST_TRACE([ENABLE_FWMGR]) -m4trace:configure.ac:141: -1- m4_pattern_allow([^ENABLE_FWMGR$]) -m4trace:configure.ac:143: -1- AC_SUBST([ENABLE_FWMGR]) -m4trace:configure.ac:143: -1- AC_SUBST_TRACE([ENABLE_FWMGR]) -m4trace:configure.ac:143: -1- m4_pattern_allow([^ENABLE_FWMGR$]) -m4trace:configure.ac:160: -1- AH_OUTPUT([HAVE_LIBXML2], [/* Define to 1 if you have the `xml2\' library (-lxml2). */ +m4trace:configure.ac:141: -1- AC_DEFINE_TRACE_LITERAL([HAVE_BOOST_FOREACH_HPP]) +m4trace:configure.ac:141: -1- m4_pattern_allow([^HAVE_BOOST_FOREACH_HPP$]) +m4trace:configure.ac:146: -1- AC_SUBST([CURL_INC_DIR]) +m4trace:configure.ac:146: -1- AC_SUBST_TRACE([CURL_INC_DIR]) +m4trace:configure.ac:146: -1- m4_pattern_allow([^CURL_INC_DIR$]) +m4trace:configure.ac:148: -1- AM_CONDITIONAL([ENABLE_FWMGR], [test "x$enable_fw_mgr" = "xyes" ]) +m4trace:configure.ac:148: -1- AC_SUBST([ENABLE_FWMGR_TRUE]) +m4trace:configure.ac:148: -1- AC_SUBST_TRACE([ENABLE_FWMGR_TRUE]) +m4trace:configure.ac:148: -1- m4_pattern_allow([^ENABLE_FWMGR_TRUE$]) +m4trace:configure.ac:148: -1- AC_SUBST([ENABLE_FWMGR_FALSE]) +m4trace:configure.ac:148: -1- AC_SUBST_TRACE([ENABLE_FWMGR_FALSE]) +m4trace:configure.ac:148: -1- m4_pattern_allow([^ENABLE_FWMGR_FALSE$]) +m4trace:configure.ac:148: -1- _AM_SUBST_NOTMAKE([ENABLE_FWMGR_TRUE]) +m4trace:configure.ac:148: -1- _AM_SUBST_NOTMAKE([ENABLE_FWMGR_FALSE]) +m4trace:configure.ac:149: -1- AC_SUBST([ENABLE_FWMGR]) +m4trace:configure.ac:149: -1- AC_SUBST_TRACE([ENABLE_FWMGR]) +m4trace:configure.ac:149: -1- m4_pattern_allow([^ENABLE_FWMGR$]) +m4trace:configure.ac:151: -1- AC_SUBST([ENABLE_FWMGR]) +m4trace:configure.ac:151: -1- AC_SUBST_TRACE([ENABLE_FWMGR]) +m4trace:configure.ac:151: -1- m4_pattern_allow([^ENABLE_FWMGR$]) +m4trace:configure.ac:168: -1- AH_OUTPUT([HAVE_LIBXML2], [/* Define to 1 if you have the `xml2\' library (-lxml2). */ @%:@undef HAVE_LIBXML2]) -m4trace:configure.ac:160: -1- AC_DEFINE_TRACE_LITERAL([HAVE_LIBXML2]) -m4trace:configure.ac:160: -1- m4_pattern_allow([^HAVE_LIBXML2$]) -m4trace:configure.ac:164: -1- AM_CONDITIONAL([DISABLE_XML2], [test "x$enable_xml2" = "xno"]) -m4trace:configure.ac:164: -1- AC_SUBST([DISABLE_XML2_TRUE]) -m4trace:configure.ac:164: -1- AC_SUBST_TRACE([DISABLE_XML2_TRUE]) -m4trace:configure.ac:164: -1- m4_pattern_allow([^DISABLE_XML2_TRUE$]) -m4trace:configure.ac:164: -1- AC_SUBST([DISABLE_XML2_FALSE]) -m4trace:configure.ac:164: -1- AC_SUBST_TRACE([DISABLE_XML2_FALSE]) -m4trace:configure.ac:164: -1- m4_pattern_allow([^DISABLE_XML2_FALSE$]) -m4trace:configure.ac:164: -1- _AM_SUBST_NOTMAKE([DISABLE_XML2_TRUE]) -m4trace:configure.ac:164: -1- _AM_SUBST_NOTMAKE([DISABLE_XML2_FALSE]) -m4trace:configure.ac:165: -1- AC_SUBST([DISABLE_XML2]) -m4trace:configure.ac:165: -1- AC_SUBST_TRACE([DISABLE_XML2]) -m4trace:configure.ac:165: -1- m4_pattern_allow([^DISABLE_XML2$]) -m4trace:configure.ac:167: -1- AM_CONDITIONAL([ENABLE_DC], [test "x$enable_dc" = "xyes"]) -m4trace:configure.ac:167: -1- AC_SUBST([ENABLE_DC_TRUE]) -m4trace:configure.ac:167: -1- AC_SUBST_TRACE([ENABLE_DC_TRUE]) -m4trace:configure.ac:167: -1- m4_pattern_allow([^ENABLE_DC_TRUE$]) -m4trace:configure.ac:167: -1- AC_SUBST([ENABLE_DC_FALSE]) -m4trace:configure.ac:167: -1- AC_SUBST_TRACE([ENABLE_DC_FALSE]) -m4trace:configure.ac:167: -1- m4_pattern_allow([^ENABLE_DC_FALSE$]) -m4trace:configure.ac:167: -1- _AM_SUBST_NOTMAKE([ENABLE_DC_TRUE]) -m4trace:configure.ac:167: -1- _AM_SUBST_NOTMAKE([ENABLE_DC_FALSE]) -m4trace:configure.ac:168: -1- AC_SUBST([ENABLE_DC]) -m4trace:configure.ac:168: -1- AC_SUBST_TRACE([ENABLE_DC]) -m4trace:configure.ac:168: -1- m4_pattern_allow([^ENABLE_DC$]) -m4trace:configure.ac:183: -1- AC_SUBST([MAD_IFC]) -m4trace:configure.ac:183: -1- AC_SUBST_TRACE([MAD_IFC]) -m4trace:configure.ac:183: -1- m4_pattern_allow([^MAD_IFC$]) -m4trace:configure.ac:185: -1- AM_CONDITIONAL([ENABLE_INBAND], [test "x$enable_inband" = "xyes"]) -m4trace:configure.ac:185: -1- AC_SUBST([ENABLE_INBAND_TRUE]) -m4trace:configure.ac:185: -1- AC_SUBST_TRACE([ENABLE_INBAND_TRUE]) -m4trace:configure.ac:185: -1- m4_pattern_allow([^ENABLE_INBAND_TRUE$]) -m4trace:configure.ac:185: -1- AC_SUBST([ENABLE_INBAND_FALSE]) -m4trace:configure.ac:185: -1- AC_SUBST_TRACE([ENABLE_INBAND_FALSE]) -m4trace:configure.ac:185: -1- m4_pattern_allow([^ENABLE_INBAND_FALSE$]) -m4trace:configure.ac:185: -1- _AM_SUBST_NOTMAKE([ENABLE_INBAND_TRUE]) -m4trace:configure.ac:185: -1- _AM_SUBST_NOTMAKE([ENABLE_INBAND_FALSE]) -m4trace:configure.ac:235: -1- AC_SUBST([TOOLS_CRYPTO]) -m4trace:configure.ac:235: -1- AC_SUBST_TRACE([TOOLS_CRYPTO]) -m4trace:configure.ac:235: -1- m4_pattern_allow([^TOOLS_CRYPTO$]) -m4trace:configure.ac:236: -1- AM_CONDITIONAL([ENABLE_OPENSSL], [test "x$enable_openssl" = "xyes" ]) -m4trace:configure.ac:236: -1- AC_SUBST([ENABLE_OPENSSL_TRUE]) -m4trace:configure.ac:236: -1- AC_SUBST_TRACE([ENABLE_OPENSSL_TRUE]) -m4trace:configure.ac:236: -1- m4_pattern_allow([^ENABLE_OPENSSL_TRUE$]) -m4trace:configure.ac:236: -1- AC_SUBST([ENABLE_OPENSSL_FALSE]) -m4trace:configure.ac:236: -1- AC_SUBST_TRACE([ENABLE_OPENSSL_FALSE]) -m4trace:configure.ac:236: -1- m4_pattern_allow([^ENABLE_OPENSSL_FALSE$]) -m4trace:configure.ac:236: -1- _AM_SUBST_NOTMAKE([ENABLE_OPENSSL_TRUE]) -m4trace:configure.ac:236: -1- _AM_SUBST_NOTMAKE([ENABLE_OPENSSL_FALSE]) -m4trace:configure.ac:237: -1- AM_CONDITIONAL([ENABLE_CS], [test "x$enable_cs" = "xyes" || test "x$enable_openssl" = "xyes" ]) -m4trace:configure.ac:237: -1- AC_SUBST([ENABLE_CS_TRUE]) -m4trace:configure.ac:237: -1- AC_SUBST_TRACE([ENABLE_CS_TRUE]) -m4trace:configure.ac:237: -1- m4_pattern_allow([^ENABLE_CS_TRUE$]) -m4trace:configure.ac:237: -1- AC_SUBST([ENABLE_CS_FALSE]) -m4trace:configure.ac:237: -1- AC_SUBST_TRACE([ENABLE_CS_FALSE]) -m4trace:configure.ac:237: -1- m4_pattern_allow([^ENABLE_CS_FALSE$]) -m4trace:configure.ac:237: -1- _AM_SUBST_NOTMAKE([ENABLE_CS_TRUE]) -m4trace:configure.ac:237: -1- _AM_SUBST_NOTMAKE([ENABLE_CS_FALSE]) -m4trace:configure.ac:253: -1- AH_OUTPUT([HAVE_INIPARSER_H], [/* Define to 1 if you have the header file. */ +m4trace:configure.ac:168: -1- AC_DEFINE_TRACE_LITERAL([HAVE_LIBXML2]) +m4trace:configure.ac:168: -1- m4_pattern_allow([^HAVE_LIBXML2$]) +m4trace:configure.ac:172: -1- AM_CONDITIONAL([DISABLE_XML2], [test "x$enable_xml2" = "xno"]) +m4trace:configure.ac:172: -1- AC_SUBST([DISABLE_XML2_TRUE]) +m4trace:configure.ac:172: -1- AC_SUBST_TRACE([DISABLE_XML2_TRUE]) +m4trace:configure.ac:172: -1- m4_pattern_allow([^DISABLE_XML2_TRUE$]) +m4trace:configure.ac:172: -1- AC_SUBST([DISABLE_XML2_FALSE]) +m4trace:configure.ac:172: -1- AC_SUBST_TRACE([DISABLE_XML2_FALSE]) +m4trace:configure.ac:172: -1- m4_pattern_allow([^DISABLE_XML2_FALSE$]) +m4trace:configure.ac:172: -1- _AM_SUBST_NOTMAKE([DISABLE_XML2_TRUE]) +m4trace:configure.ac:172: -1- _AM_SUBST_NOTMAKE([DISABLE_XML2_FALSE]) +m4trace:configure.ac:173: -1- AC_SUBST([DISABLE_XML2]) +m4trace:configure.ac:173: -1- AC_SUBST_TRACE([DISABLE_XML2]) +m4trace:configure.ac:173: -1- m4_pattern_allow([^DISABLE_XML2$]) +m4trace:configure.ac:175: -1- AM_CONDITIONAL([ENABLE_DC], [test "x$enable_dc" = "xyes"]) +m4trace:configure.ac:175: -1- AC_SUBST([ENABLE_DC_TRUE]) +m4trace:configure.ac:175: -1- AC_SUBST_TRACE([ENABLE_DC_TRUE]) +m4trace:configure.ac:175: -1- m4_pattern_allow([^ENABLE_DC_TRUE$]) +m4trace:configure.ac:175: -1- AC_SUBST([ENABLE_DC_FALSE]) +m4trace:configure.ac:175: -1- AC_SUBST_TRACE([ENABLE_DC_FALSE]) +m4trace:configure.ac:175: -1- m4_pattern_allow([^ENABLE_DC_FALSE$]) +m4trace:configure.ac:175: -1- _AM_SUBST_NOTMAKE([ENABLE_DC_TRUE]) +m4trace:configure.ac:175: -1- _AM_SUBST_NOTMAKE([ENABLE_DC_FALSE]) +m4trace:configure.ac:176: -1- AC_SUBST([ENABLE_DC]) +m4trace:configure.ac:176: -1- AC_SUBST_TRACE([ENABLE_DC]) +m4trace:configure.ac:176: -1- m4_pattern_allow([^ENABLE_DC$]) +m4trace:configure.ac:191: -1- AC_SUBST([MAD_IFC]) +m4trace:configure.ac:191: -1- AC_SUBST_TRACE([MAD_IFC]) +m4trace:configure.ac:191: -1- m4_pattern_allow([^MAD_IFC$]) +m4trace:configure.ac:193: -1- AM_CONDITIONAL([ENABLE_INBAND], [test "x$enable_inband" = "xyes"]) +m4trace:configure.ac:193: -1- AC_SUBST([ENABLE_INBAND_TRUE]) +m4trace:configure.ac:193: -1- AC_SUBST_TRACE([ENABLE_INBAND_TRUE]) +m4trace:configure.ac:193: -1- m4_pattern_allow([^ENABLE_INBAND_TRUE$]) +m4trace:configure.ac:193: -1- AC_SUBST([ENABLE_INBAND_FALSE]) +m4trace:configure.ac:193: -1- AC_SUBST_TRACE([ENABLE_INBAND_FALSE]) +m4trace:configure.ac:193: -1- m4_pattern_allow([^ENABLE_INBAND_FALSE$]) +m4trace:configure.ac:193: -1- _AM_SUBST_NOTMAKE([ENABLE_INBAND_TRUE]) +m4trace:configure.ac:193: -1- _AM_SUBST_NOTMAKE([ENABLE_INBAND_FALSE]) +m4trace:configure.ac:243: -1- AC_SUBST([TOOLS_CRYPTO]) +m4trace:configure.ac:243: -1- AC_SUBST_TRACE([TOOLS_CRYPTO]) +m4trace:configure.ac:243: -1- m4_pattern_allow([^TOOLS_CRYPTO$]) +m4trace:configure.ac:244: -1- AM_CONDITIONAL([ENABLE_OPENSSL], [test "x$enable_openssl" = "xyes" ]) +m4trace:configure.ac:244: -1- AC_SUBST([ENABLE_OPENSSL_TRUE]) +m4trace:configure.ac:244: -1- AC_SUBST_TRACE([ENABLE_OPENSSL_TRUE]) +m4trace:configure.ac:244: -1- m4_pattern_allow([^ENABLE_OPENSSL_TRUE$]) +m4trace:configure.ac:244: -1- AC_SUBST([ENABLE_OPENSSL_FALSE]) +m4trace:configure.ac:244: -1- AC_SUBST_TRACE([ENABLE_OPENSSL_FALSE]) +m4trace:configure.ac:244: -1- m4_pattern_allow([^ENABLE_OPENSSL_FALSE$]) +m4trace:configure.ac:244: -1- _AM_SUBST_NOTMAKE([ENABLE_OPENSSL_TRUE]) +m4trace:configure.ac:244: -1- _AM_SUBST_NOTMAKE([ENABLE_OPENSSL_FALSE]) +m4trace:configure.ac:245: -1- AM_CONDITIONAL([ENABLE_CS], [test "x$enable_cs" = "xyes" || test "x$enable_openssl" = "xyes" ]) +m4trace:configure.ac:245: -1- AC_SUBST([ENABLE_CS_TRUE]) +m4trace:configure.ac:245: -1- AC_SUBST_TRACE([ENABLE_CS_TRUE]) +m4trace:configure.ac:245: -1- m4_pattern_allow([^ENABLE_CS_TRUE$]) +m4trace:configure.ac:245: -1- AC_SUBST([ENABLE_CS_FALSE]) +m4trace:configure.ac:245: -1- AC_SUBST_TRACE([ENABLE_CS_FALSE]) +m4trace:configure.ac:245: -1- m4_pattern_allow([^ENABLE_CS_FALSE$]) +m4trace:configure.ac:245: -1- _AM_SUBST_NOTMAKE([ENABLE_CS_TRUE]) +m4trace:configure.ac:245: -1- _AM_SUBST_NOTMAKE([ENABLE_CS_FALSE]) +m4trace:configure.ac:261: -1- AH_OUTPUT([HAVE_INIPARSER_H], [/* Define to 1 if you have the header file. */ @%:@undef HAVE_INIPARSER_H]) -m4trace:configure.ac:253: -1- AC_DEFINE_TRACE_LITERAL([HAVE_INIPARSER_H]) -m4trace:configure.ac:253: -1- m4_pattern_allow([^HAVE_INIPARSER_H$]) -m4trace:configure.ac:263: -1- AC_SUBST([INIPARSER_CFLAGS]) -m4trace:configure.ac:263: -1- AC_SUBST_TRACE([INIPARSER_CFLAGS]) -m4trace:configure.ac:263: -1- m4_pattern_allow([^INIPARSER_CFLAGS$]) -m4trace:configure.ac:265: -1- AC_SUBST([INIPARSER_LIBS]) -m4trace:configure.ac:265: -1- AC_SUBST_TRACE([INIPARSER_LIBS]) -m4trace:configure.ac:265: -1- m4_pattern_allow([^INIPARSER_LIBS$]) -m4trace:configure.ac:268: -1- AM_CONDITIONAL([USE_LOCAL_INIPARSER], [test "$INIPARSER_SYSTEM_AVAILABLE" = no]) -m4trace:configure.ac:268: -1- AC_SUBST([USE_LOCAL_INIPARSER_TRUE]) -m4trace:configure.ac:268: -1- AC_SUBST_TRACE([USE_LOCAL_INIPARSER_TRUE]) -m4trace:configure.ac:268: -1- m4_pattern_allow([^USE_LOCAL_INIPARSER_TRUE$]) -m4trace:configure.ac:268: -1- AC_SUBST([USE_LOCAL_INIPARSER_FALSE]) -m4trace:configure.ac:268: -1- AC_SUBST_TRACE([USE_LOCAL_INIPARSER_FALSE]) -m4trace:configure.ac:268: -1- m4_pattern_allow([^USE_LOCAL_INIPARSER_FALSE$]) -m4trace:configure.ac:268: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_INIPARSER_TRUE]) -m4trace:configure.ac:268: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_INIPARSER_FALSE]) -m4trace:configure.ac:274: -1- m4_pattern_forbid([^_?PKG_[A-Z_]+$]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) -m4trace:configure.ac:274: -1- AC_SUBST([PKG_CONFIG]) -m4trace:configure.ac:274: -1- AC_SUBST_TRACE([PKG_CONFIG]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^PKG_CONFIG$]) -m4trace:configure.ac:274: -1- AC_SUBST([PKG_CONFIG_PATH]) -m4trace:configure.ac:274: -1- AC_SUBST_TRACE([PKG_CONFIG_PATH]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^PKG_CONFIG_PATH$]) -m4trace:configure.ac:274: -1- AC_SUBST([PKG_CONFIG_LIBDIR]) -m4trace:configure.ac:274: -1- AC_SUBST_TRACE([PKG_CONFIG_LIBDIR]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^PKG_CONFIG_LIBDIR$]) -m4trace:configure.ac:274: -1- AC_SUBST([PKG_CONFIG]) -m4trace:configure.ac:274: -1- AC_SUBST_TRACE([PKG_CONFIG]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^PKG_CONFIG$]) -m4trace:configure.ac:274: -1- AC_SUBST([JSON_CFLAGS]) -m4trace:configure.ac:274: -1- AC_SUBST_TRACE([JSON_CFLAGS]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^JSON_CFLAGS$]) -m4trace:configure.ac:274: -1- AC_SUBST([JSON_LIBS]) -m4trace:configure.ac:274: -1- AC_SUBST_TRACE([JSON_LIBS]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^JSON_LIBS$]) -m4trace:configure.ac:274: -1- AC_SUBST([JSON_CFLAGS]) -m4trace:configure.ac:274: -1- AC_SUBST_TRACE([JSON_CFLAGS]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^JSON_CFLAGS$]) -m4trace:configure.ac:274: -1- AC_SUBST([JSON_LIBS]) -m4trace:configure.ac:274: -1- AC_SUBST_TRACE([JSON_LIBS]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^JSON_LIBS$]) -m4trace:configure.ac:274: -1- AC_SUBST([JSON_CFLAGS]) -m4trace:configure.ac:274: -1- AC_SUBST_TRACE([JSON_CFLAGS]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^JSON_CFLAGS$]) -m4trace:configure.ac:274: -1- AC_SUBST([JSON_LIBS]) -m4trace:configure.ac:274: -1- AC_SUBST_TRACE([JSON_LIBS]) -m4trace:configure.ac:274: -1- m4_pattern_allow([^JSON_LIBS$]) -m4trace:configure.ac:281: -1- AM_CONDITIONAL([USE_LOCAL_JSON], [test "$JSON_SYSTEM_AVAILABLE" = no]) -m4trace:configure.ac:281: -1- AC_SUBST([USE_LOCAL_JSON_TRUE]) -m4trace:configure.ac:281: -1- AC_SUBST_TRACE([USE_LOCAL_JSON_TRUE]) -m4trace:configure.ac:281: -1- m4_pattern_allow([^USE_LOCAL_JSON_TRUE$]) -m4trace:configure.ac:281: -1- AC_SUBST([USE_LOCAL_JSON_FALSE]) -m4trace:configure.ac:281: -1- AC_SUBST_TRACE([USE_LOCAL_JSON_FALSE]) -m4trace:configure.ac:281: -1- m4_pattern_allow([^USE_LOCAL_JSON_FALSE$]) -m4trace:configure.ac:281: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_JSON_TRUE]) -m4trace:configure.ac:281: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_JSON_FALSE]) -m4trace:configure.ac:283: -1- AC_SUBST([MUPARSER_CFLAGS]) -m4trace:configure.ac:283: -1- AC_SUBST_TRACE([MUPARSER_CFLAGS]) -m4trace:configure.ac:283: -1- m4_pattern_allow([^MUPARSER_CFLAGS$]) -m4trace:configure.ac:283: -1- AC_SUBST([MUPARSER_LIBS]) -m4trace:configure.ac:283: -1- AC_SUBST_TRACE([MUPARSER_LIBS]) -m4trace:configure.ac:283: -1- m4_pattern_allow([^MUPARSER_LIBS$]) -m4trace:configure.ac:290: -1- AM_CONDITIONAL([USE_LOCAL_MUPARSER], [test "$MUPARSER_SYSTEM_AVAILABLE" = no]) -m4trace:configure.ac:290: -1- AC_SUBST([USE_LOCAL_MUPARSER_TRUE]) -m4trace:configure.ac:290: -1- AC_SUBST_TRACE([USE_LOCAL_MUPARSER_TRUE]) -m4trace:configure.ac:290: -1- m4_pattern_allow([^USE_LOCAL_MUPARSER_TRUE$]) -m4trace:configure.ac:290: -1- AC_SUBST([USE_LOCAL_MUPARSER_FALSE]) -m4trace:configure.ac:290: -1- AC_SUBST_TRACE([USE_LOCAL_MUPARSER_FALSE]) -m4trace:configure.ac:290: -1- m4_pattern_allow([^USE_LOCAL_MUPARSER_FALSE$]) -m4trace:configure.ac:290: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_MUPARSER_TRUE]) -m4trace:configure.ac:290: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_MUPARSER_FALSE]) -m4trace:configure.ac:292: -1- AC_SUBST([SQLITE_CFLAGS]) -m4trace:configure.ac:292: -1- AC_SUBST_TRACE([SQLITE_CFLAGS]) -m4trace:configure.ac:292: -1- m4_pattern_allow([^SQLITE_CFLAGS$]) -m4trace:configure.ac:292: -1- AC_SUBST([SQLITE_LIBS]) -m4trace:configure.ac:292: -1- AC_SUBST_TRACE([SQLITE_LIBS]) -m4trace:configure.ac:292: -1- m4_pattern_allow([^SQLITE_LIBS$]) -m4trace:configure.ac:299: -1- AM_CONDITIONAL([USE_LOCAL_SQLITE], [test "$SQLITE_SYSTEM_AVAILABLE" = no]) -m4trace:configure.ac:299: -1- AC_SUBST([USE_LOCAL_SQLITE_TRUE]) -m4trace:configure.ac:299: -1- AC_SUBST_TRACE([USE_LOCAL_SQLITE_TRUE]) -m4trace:configure.ac:299: -1- m4_pattern_allow([^USE_LOCAL_SQLITE_TRUE$]) -m4trace:configure.ac:299: -1- AC_SUBST([USE_LOCAL_SQLITE_FALSE]) -m4trace:configure.ac:299: -1- AC_SUBST_TRACE([USE_LOCAL_SQLITE_FALSE]) -m4trace:configure.ac:299: -1- m4_pattern_allow([^USE_LOCAL_SQLITE_FALSE$]) -m4trace:configure.ac:299: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_SQLITE_TRUE]) -m4trace:configure.ac:299: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_SQLITE_FALSE]) -m4trace:configure.ac:307: -1- AC_CONFIG_FILES([mstflint.spec ]) -m4trace:configure.ac:335: -1- AC_SUBST([TOOLS_BUILD_TIME]) -m4trace:configure.ac:335: -1- AC_SUBST_TRACE([TOOLS_BUILD_TIME]) -m4trace:configure.ac:335: -1- m4_pattern_allow([^TOOLS_BUILD_TIME$]) -m4trace:configure.ac:337: -1- AC_SUBST([MSTFLINT_VERSION_STR]) -m4trace:configure.ac:337: -1- AC_SUBST_TRACE([MSTFLINT_VERSION_STR]) -m4trace:configure.ac:337: -1- m4_pattern_allow([^MSTFLINT_VERSION_STR$]) -m4trace:configure.ac:342: -1- AC_SUBST([MSTFLINT_VERSION_STR]) -m4trace:configure.ac:342: -1- AC_SUBST_TRACE([MSTFLINT_VERSION_STR]) -m4trace:configure.ac:342: -1- m4_pattern_allow([^MSTFLINT_VERSION_STR$]) -m4trace:configure.ac:343: -1- AC_DEFINE_TRACE_LITERAL([MSTFLINT_VERSION_STR]) -m4trace:configure.ac:343: -1- m4_pattern_allow([^MSTFLINT_VERSION_STR$]) -m4trace:configure.ac:343: -1- AH_OUTPUT([MSTFLINT_VERSION_STR], [/* The MSTFLINT version */ +m4trace:configure.ac:261: -1- AC_DEFINE_TRACE_LITERAL([HAVE_INIPARSER_H]) +m4trace:configure.ac:261: -1- m4_pattern_allow([^HAVE_INIPARSER_H$]) +m4trace:configure.ac:271: -1- AC_SUBST([INIPARSER_CFLAGS]) +m4trace:configure.ac:271: -1- AC_SUBST_TRACE([INIPARSER_CFLAGS]) +m4trace:configure.ac:271: -1- m4_pattern_allow([^INIPARSER_CFLAGS$]) +m4trace:configure.ac:273: -1- AC_SUBST([INIPARSER_LIBS]) +m4trace:configure.ac:273: -1- AC_SUBST_TRACE([INIPARSER_LIBS]) +m4trace:configure.ac:273: -1- m4_pattern_allow([^INIPARSER_LIBS$]) +m4trace:configure.ac:276: -1- AM_CONDITIONAL([USE_LOCAL_INIPARSER], [test "$INIPARSER_SYSTEM_AVAILABLE" = no]) +m4trace:configure.ac:276: -1- AC_SUBST([USE_LOCAL_INIPARSER_TRUE]) +m4trace:configure.ac:276: -1- AC_SUBST_TRACE([USE_LOCAL_INIPARSER_TRUE]) +m4trace:configure.ac:276: -1- m4_pattern_allow([^USE_LOCAL_INIPARSER_TRUE$]) +m4trace:configure.ac:276: -1- AC_SUBST([USE_LOCAL_INIPARSER_FALSE]) +m4trace:configure.ac:276: -1- AC_SUBST_TRACE([USE_LOCAL_INIPARSER_FALSE]) +m4trace:configure.ac:276: -1- m4_pattern_allow([^USE_LOCAL_INIPARSER_FALSE$]) +m4trace:configure.ac:276: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_INIPARSER_TRUE]) +m4trace:configure.ac:276: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_INIPARSER_FALSE]) +m4trace:configure.ac:282: -1- m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) +m4trace:configure.ac:282: -1- AC_SUBST([PKG_CONFIG]) +m4trace:configure.ac:282: -1- AC_SUBST_TRACE([PKG_CONFIG]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^PKG_CONFIG$]) +m4trace:configure.ac:282: -1- AC_SUBST([PKG_CONFIG_PATH]) +m4trace:configure.ac:282: -1- AC_SUBST_TRACE([PKG_CONFIG_PATH]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^PKG_CONFIG_PATH$]) +m4trace:configure.ac:282: -1- AC_SUBST([PKG_CONFIG_LIBDIR]) +m4trace:configure.ac:282: -1- AC_SUBST_TRACE([PKG_CONFIG_LIBDIR]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^PKG_CONFIG_LIBDIR$]) +m4trace:configure.ac:282: -1- AC_SUBST([PKG_CONFIG]) +m4trace:configure.ac:282: -1- AC_SUBST_TRACE([PKG_CONFIG]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^PKG_CONFIG$]) +m4trace:configure.ac:282: -1- AC_SUBST([JSON_CFLAGS]) +m4trace:configure.ac:282: -1- AC_SUBST_TRACE([JSON_CFLAGS]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^JSON_CFLAGS$]) +m4trace:configure.ac:282: -1- AC_SUBST([JSON_LIBS]) +m4trace:configure.ac:282: -1- AC_SUBST_TRACE([JSON_LIBS]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^JSON_LIBS$]) +m4trace:configure.ac:282: -1- AC_SUBST([JSON_CFLAGS]) +m4trace:configure.ac:282: -1- AC_SUBST_TRACE([JSON_CFLAGS]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^JSON_CFLAGS$]) +m4trace:configure.ac:282: -1- AC_SUBST([JSON_LIBS]) +m4trace:configure.ac:282: -1- AC_SUBST_TRACE([JSON_LIBS]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^JSON_LIBS$]) +m4trace:configure.ac:282: -1- AC_SUBST([JSON_CFLAGS]) +m4trace:configure.ac:282: -1- AC_SUBST_TRACE([JSON_CFLAGS]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^JSON_CFLAGS$]) +m4trace:configure.ac:282: -1- AC_SUBST([JSON_LIBS]) +m4trace:configure.ac:282: -1- AC_SUBST_TRACE([JSON_LIBS]) +m4trace:configure.ac:282: -1- m4_pattern_allow([^JSON_LIBS$]) +m4trace:configure.ac:289: -1- AM_CONDITIONAL([USE_LOCAL_JSON], [test "$JSON_SYSTEM_AVAILABLE" = no]) +m4trace:configure.ac:289: -1- AC_SUBST([USE_LOCAL_JSON_TRUE]) +m4trace:configure.ac:289: -1- AC_SUBST_TRACE([USE_LOCAL_JSON_TRUE]) +m4trace:configure.ac:289: -1- m4_pattern_allow([^USE_LOCAL_JSON_TRUE$]) +m4trace:configure.ac:289: -1- AC_SUBST([USE_LOCAL_JSON_FALSE]) +m4trace:configure.ac:289: -1- AC_SUBST_TRACE([USE_LOCAL_JSON_FALSE]) +m4trace:configure.ac:289: -1- m4_pattern_allow([^USE_LOCAL_JSON_FALSE$]) +m4trace:configure.ac:289: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_JSON_TRUE]) +m4trace:configure.ac:289: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_JSON_FALSE]) +m4trace:configure.ac:291: -1- AC_SUBST([MUPARSER_CFLAGS]) +m4trace:configure.ac:291: -1- AC_SUBST_TRACE([MUPARSER_CFLAGS]) +m4trace:configure.ac:291: -1- m4_pattern_allow([^MUPARSER_CFLAGS$]) +m4trace:configure.ac:291: -1- AC_SUBST([MUPARSER_LIBS]) +m4trace:configure.ac:291: -1- AC_SUBST_TRACE([MUPARSER_LIBS]) +m4trace:configure.ac:291: -1- m4_pattern_allow([^MUPARSER_LIBS$]) +m4trace:configure.ac:298: -1- AM_CONDITIONAL([USE_LOCAL_MUPARSER], [test "$MUPARSER_SYSTEM_AVAILABLE" = no]) +m4trace:configure.ac:298: -1- AC_SUBST([USE_LOCAL_MUPARSER_TRUE]) +m4trace:configure.ac:298: -1- AC_SUBST_TRACE([USE_LOCAL_MUPARSER_TRUE]) +m4trace:configure.ac:298: -1- m4_pattern_allow([^USE_LOCAL_MUPARSER_TRUE$]) +m4trace:configure.ac:298: -1- AC_SUBST([USE_LOCAL_MUPARSER_FALSE]) +m4trace:configure.ac:298: -1- AC_SUBST_TRACE([USE_LOCAL_MUPARSER_FALSE]) +m4trace:configure.ac:298: -1- m4_pattern_allow([^USE_LOCAL_MUPARSER_FALSE$]) +m4trace:configure.ac:298: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_MUPARSER_TRUE]) +m4trace:configure.ac:298: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_MUPARSER_FALSE]) +m4trace:configure.ac:300: -1- AC_SUBST([SQLITE_CFLAGS]) +m4trace:configure.ac:300: -1- AC_SUBST_TRACE([SQLITE_CFLAGS]) +m4trace:configure.ac:300: -1- m4_pattern_allow([^SQLITE_CFLAGS$]) +m4trace:configure.ac:300: -1- AC_SUBST([SQLITE_LIBS]) +m4trace:configure.ac:300: -1- AC_SUBST_TRACE([SQLITE_LIBS]) +m4trace:configure.ac:300: -1- m4_pattern_allow([^SQLITE_LIBS$]) +m4trace:configure.ac:307: -1- AM_CONDITIONAL([USE_LOCAL_SQLITE], [test "$SQLITE_SYSTEM_AVAILABLE" = no]) +m4trace:configure.ac:307: -1- AC_SUBST([USE_LOCAL_SQLITE_TRUE]) +m4trace:configure.ac:307: -1- AC_SUBST_TRACE([USE_LOCAL_SQLITE_TRUE]) +m4trace:configure.ac:307: -1- m4_pattern_allow([^USE_LOCAL_SQLITE_TRUE$]) +m4trace:configure.ac:307: -1- AC_SUBST([USE_LOCAL_SQLITE_FALSE]) +m4trace:configure.ac:307: -1- AC_SUBST_TRACE([USE_LOCAL_SQLITE_FALSE]) +m4trace:configure.ac:307: -1- m4_pattern_allow([^USE_LOCAL_SQLITE_FALSE$]) +m4trace:configure.ac:307: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_SQLITE_TRUE]) +m4trace:configure.ac:307: -1- _AM_SUBST_NOTMAKE([USE_LOCAL_SQLITE_FALSE]) +m4trace:configure.ac:326: -1- AC_CONFIG_FILES([mstflint.spec ]) +m4trace:configure.ac:354: -1- AC_SUBST([TOOLS_BUILD_TIME]) +m4trace:configure.ac:354: -1- AC_SUBST_TRACE([TOOLS_BUILD_TIME]) +m4trace:configure.ac:354: -1- m4_pattern_allow([^TOOLS_BUILD_TIME$]) +m4trace:configure.ac:356: -1- AC_SUBST([MSTFLINT_VERSION_STR]) +m4trace:configure.ac:356: -1- AC_SUBST_TRACE([MSTFLINT_VERSION_STR]) +m4trace:configure.ac:356: -1- m4_pattern_allow([^MSTFLINT_VERSION_STR$]) +m4trace:configure.ac:361: -1- AC_SUBST([MSTFLINT_VERSION_STR]) +m4trace:configure.ac:361: -1- AC_SUBST_TRACE([MSTFLINT_VERSION_STR]) +m4trace:configure.ac:361: -1- m4_pattern_allow([^MSTFLINT_VERSION_STR$]) +m4trace:configure.ac:362: -1- AC_DEFINE_TRACE_LITERAL([MSTFLINT_VERSION_STR]) +m4trace:configure.ac:362: -1- m4_pattern_allow([^MSTFLINT_VERSION_STR$]) +m4trace:configure.ac:362: -1- AH_OUTPUT([MSTFLINT_VERSION_STR], [/* The MSTFLINT version */ @%:@undef MSTFLINT_VERSION_STR]) -m4trace:configure.ac:344: -1- AC_DEFINE_TRACE_LITERAL([MSTFLINT_VERSION_STR]) -m4trace:configure.ac:344: -1- m4_pattern_allow([^MSTFLINT_VERSION_STR$]) -m4trace:configure.ac:347: -1- AC_SUBST([TOOLS_GIT_SHA]) -m4trace:configure.ac:347: -1- AC_SUBST_TRACE([TOOLS_GIT_SHA]) -m4trace:configure.ac:347: -1- m4_pattern_allow([^TOOLS_GIT_SHA$]) -m4trace:configure.ac:351: -1- AC_SUBST([TOOLS_GIT_SHA]) -m4trace:configure.ac:351: -1- AC_SUBST_TRACE([TOOLS_GIT_SHA]) -m4trace:configure.ac:351: -1- m4_pattern_allow([^TOOLS_GIT_SHA$]) -m4trace:configure.ac:352: -1- AC_DEFINE_TRACE_LITERAL([TOOLS_GIT_SHA]) -m4trace:configure.ac:352: -1- m4_pattern_allow([^TOOLS_GIT_SHA$]) -m4trace:configure.ac:352: -1- AH_OUTPUT([TOOLS_GIT_SHA], [/* The MSTFLINT tree git sha hash version */ +m4trace:configure.ac:363: -1- AC_DEFINE_TRACE_LITERAL([MSTFLINT_VERSION_STR]) +m4trace:configure.ac:363: -1- m4_pattern_allow([^MSTFLINT_VERSION_STR$]) +m4trace:configure.ac:366: -1- AC_SUBST([TOOLS_GIT_SHA]) +m4trace:configure.ac:366: -1- AC_SUBST_TRACE([TOOLS_GIT_SHA]) +m4trace:configure.ac:366: -1- m4_pattern_allow([^TOOLS_GIT_SHA$]) +m4trace:configure.ac:370: -1- AC_SUBST([TOOLS_GIT_SHA]) +m4trace:configure.ac:370: -1- AC_SUBST_TRACE([TOOLS_GIT_SHA]) +m4trace:configure.ac:370: -1- m4_pattern_allow([^TOOLS_GIT_SHA$]) +m4trace:configure.ac:371: -1- AC_DEFINE_TRACE_LITERAL([TOOLS_GIT_SHA]) +m4trace:configure.ac:371: -1- m4_pattern_allow([^TOOLS_GIT_SHA$]) +m4trace:configure.ac:371: -1- AH_OUTPUT([TOOLS_GIT_SHA], [/* The MSTFLINT tree git sha hash version */ @%:@undef TOOLS_GIT_SHA]) -m4trace:configure.ac:353: -1- AC_DEFINE_TRACE_LITERAL([TOOLS_GIT_SHA]) -m4trace:configure.ac:353: -1- m4_pattern_allow([^TOOLS_GIT_SHA$]) -m4trace:configure.ac:355: -1- AC_CONFIG_FILES([tools_crypto/Makefile]) -m4trace:configure.ac:359: -1- AC_CONFIG_FILES([mlxsign_lib/Makefile]) -m4trace:configure.ac:370: -1- AC_CONFIG_FILES([mlxfwupdate/Makefile]) -m4trace:configure.ac:370: -1- AC_CONFIG_FILES([ext_libs/iniParser/Makefile]) -m4trace:configure.ac:370: -1- AC_CONFIG_FILES([mlxarchive/Makefile]) -m4trace:configure.ac:377: -1- AC_SUBST([FW_MGR_TOOLS]) -m4trace:configure.ac:377: -1- AC_SUBST_TRACE([FW_MGR_TOOLS]) -m4trace:configure.ac:377: -1- m4_pattern_allow([^FW_MGR_TOOLS$]) -m4trace:configure.ac:379: -1- AC_CONFIG_FILES([adb_parser/Makefile]) -m4trace:configure.ac:379: -1- AC_CONFIG_FILES([tools_layouts/adb/Makefile]) -m4trace:configure.ac:379: -1- AC_CONFIG_FILES([tools_layouts/adb/prm/Makefile]) -m4trace:configure.ac:379: -1- AC_CONFIG_FILES([tools_layouts/adb/prm/hca/Makefile]) -m4trace:configure.ac:379: -1- AC_CONFIG_FILES([tools_layouts/adb/prm/hca/ext/Makefile]) -m4trace:configure.ac:379: -1- AC_CONFIG_FILES([tools_layouts/adb/prm/switch/Makefile]) -m4trace:configure.ac:379: -1- AC_CONFIG_FILES([tools_layouts/adb/prm/switch/ext/Makefile]) -m4trace:configure.ac:379: -1- AC_CONFIG_FILES([mlxreg/Makefile]) -m4trace:configure.ac:379: -1- AC_CONFIG_FILES([mlxlink/Makefile]) -m4trace:configure.ac:379: -1- AC_CONFIG_FILES([mlxlink/modules/Makefile]) -m4trace:configure.ac:379: -1- AC_CONFIG_FILES([mlxlink/modules/printutil/Makefile]) -m4trace:configure.ac:396: -1- AC_CONFIG_FILES([xz_utils/Makefile]) -m4trace:configure.ac:401: -1- AC_SUBST([ADABE_TOOLS]) -m4trace:configure.ac:401: -1- AC_SUBST_TRACE([ADABE_TOOLS]) -m4trace:configure.ac:401: -1- m4_pattern_allow([^ADABE_TOOLS$]) -m4trace:configure.ac:402: -1- AC_SUBST([ADABE_DBS]) -m4trace:configure.ac:402: -1- AC_SUBST_TRACE([ADABE_DBS]) -m4trace:configure.ac:402: -1- m4_pattern_allow([^ADABE_DBS$]) -m4trace:configure.ac:403: -1- AC_SUBST([ADABE_DBS_EXTRA_DIST]) -m4trace:configure.ac:403: -1- AC_SUBST_TRACE([ADABE_DBS_EXTRA_DIST]) -m4trace:configure.ac:403: -1- m4_pattern_allow([^ADABE_DBS_EXTRA_DIST$]) -m4trace:configure.ac:404: -1- AC_SUBST([XZ_UTILS_DIR]) -m4trace:configure.ac:404: -1- AC_SUBST_TRACE([XZ_UTILS_DIR]) -m4trace:configure.ac:404: -1- m4_pattern_allow([^XZ_UTILS_DIR$]) -m4trace:configure.ac:408: -1- AC_CONFIG_FILES([mtcr_freebsd/Makefile]) -m4trace:configure.ac:411: -1- AC_CONFIG_FILES([mtcr_ul/Makefile]) -m4trace:configure.ac:415: -1- AC_CONFIG_FILES([Makefile common/Makefile mft_utils/Makefile mft_utils/hsmclient/Makefile mtcr_py/Makefile ext_libs/Makefile ext_libs/sqlite/Makefile ext_libs/muparser/Makefile ext_libs/json/Makefile ext_libs/minixz/Makefile dev_mgt/Makefile \ +m4trace:configure.ac:372: -1- AC_DEFINE_TRACE_LITERAL([TOOLS_GIT_SHA]) +m4trace:configure.ac:372: -1- m4_pattern_allow([^TOOLS_GIT_SHA$]) +m4trace:configure.ac:374: -1- AC_CONFIG_FILES([tools_crypto/Makefile]) +m4trace:configure.ac:379: -1- AC_CONFIG_FILES([mlxsign_lib/Makefile]) +m4trace:configure.ac:390: -1- AC_CONFIG_FILES([mlxfwupdate/Makefile]) +m4trace:configure.ac:390: -1- AC_CONFIG_FILES([ext_libs/iniParser/Makefile]) +m4trace:configure.ac:390: -1- AC_CONFIG_FILES([mlxarchive/Makefile]) +m4trace:configure.ac:397: -1- AC_SUBST([FW_MGR_TOOLS]) +m4trace:configure.ac:397: -1- AC_SUBST_TRACE([FW_MGR_TOOLS]) +m4trace:configure.ac:397: -1- m4_pattern_allow([^FW_MGR_TOOLS$]) +m4trace:configure.ac:399: -1- AC_CONFIG_FILES([adb_parser/Makefile]) +m4trace:configure.ac:399: -1- AC_CONFIG_FILES([tools_layouts/adb/Makefile]) +m4trace:configure.ac:399: -1- AC_CONFIG_FILES([tools_layouts/adb/prm/Makefile]) +m4trace:configure.ac:399: -1- AC_CONFIG_FILES([tools_layouts/adb/prm/hca/Makefile]) +m4trace:configure.ac:399: -1- AC_CONFIG_FILES([tools_layouts/adb/prm/hca/ext/Makefile]) +m4trace:configure.ac:399: -1- AC_CONFIG_FILES([tools_layouts/adb/prm/switch/Makefile]) +m4trace:configure.ac:399: -1- AC_CONFIG_FILES([tools_layouts/adb/prm/switch/ext/Makefile]) +m4trace:configure.ac:399: -1- AC_CONFIG_FILES([mlxreg/Makefile]) +m4trace:configure.ac:399: -1- AC_CONFIG_FILES([mlxlink/Makefile]) +m4trace:configure.ac:399: -1- AC_CONFIG_FILES([mlxlink/modules/Makefile]) +m4trace:configure.ac:399: -1- AC_CONFIG_FILES([mlxlink/modules/printutil/Makefile]) +m4trace:configure.ac:416: -1- AC_CONFIG_FILES([xz_utils/Makefile]) +m4trace:configure.ac:421: -1- AC_SUBST([ADABE_TOOLS]) +m4trace:configure.ac:421: -1- AC_SUBST_TRACE([ADABE_TOOLS]) +m4trace:configure.ac:421: -1- m4_pattern_allow([^ADABE_TOOLS$]) +m4trace:configure.ac:422: -1- AC_SUBST([ADABE_DBS]) +m4trace:configure.ac:422: -1- AC_SUBST_TRACE([ADABE_DBS]) +m4trace:configure.ac:422: -1- m4_pattern_allow([^ADABE_DBS$]) +m4trace:configure.ac:423: -1- AC_SUBST([ADABE_DBS_EXTRA_DIST]) +m4trace:configure.ac:423: -1- AC_SUBST_TRACE([ADABE_DBS_EXTRA_DIST]) +m4trace:configure.ac:423: -1- m4_pattern_allow([^ADABE_DBS_EXTRA_DIST$]) +m4trace:configure.ac:424: -1- AC_SUBST([XZ_UTILS_DIR]) +m4trace:configure.ac:424: -1- AC_SUBST_TRACE([XZ_UTILS_DIR]) +m4trace:configure.ac:424: -1- m4_pattern_allow([^XZ_UTILS_DIR$]) +m4trace:configure.ac:428: -1- AC_CONFIG_FILES([mtcr_freebsd/Makefile]) +m4trace:configure.ac:431: -1- AC_CONFIG_FILES([mtcr_ul/Makefile]) +m4trace:configure.ac:435: -1- AC_CONFIG_FILES([Makefile common/Makefile mft_utils/Makefile mft_utils/hsmclient/Makefile mtcr_py/Makefile ext_libs/Makefile ext_libs/sqlite/Makefile ext_libs/muparser/Makefile ext_libs/json/Makefile ext_libs/minixz/Makefile dev_mgt/Makefile \ tools_layouts/Makefile reg_access/Makefile cmdif/Makefile libmfa/Makefile pldmlib/Makefile tools_res_mgmt/Makefile mlxconfig/Makefile mlxconfig/mlxconfig_dbs/Makefile mflash/Makefile mlxfwops/Makefile mlxfwops/lib/Makefile cmdparser/Makefile \ flint/Makefile small_utils/Makefile small_utils/mlxfwresetlib/Makefile mstdump/Makefile mstdump/crd_lib/Makefile mstdump/crd_main/Makefile mstdump/mstdump_dbs/Makefile mvpd/Makefile \ fw_comps_mgr/Makefile mad_ifc/Makefile tracers/Makefile tracers/fwtrace/Makefile resourcedump/Makefile resourceparse/Makefile ]) -m4trace:configure.ac:415: -1- _m4_warn([obsolete], [AC_OUTPUT should be used without arguments. +m4trace:configure.ac:435: -1- _m4_warn([obsolete], [AC_OUTPUT should be used without arguments. You should run autoupdate.], []) -m4trace:configure.ac:415: -1- AC_SUBST([LIB@&t@OBJS], [$ac_libobjs]) -m4trace:configure.ac:415: -1- AC_SUBST_TRACE([LIB@&t@OBJS]) -m4trace:configure.ac:415: -1- m4_pattern_allow([^LIB@&t@OBJS$]) -m4trace:configure.ac:415: -1- AC_SUBST([LTLIBOBJS], [$ac_ltlibobjs]) -m4trace:configure.ac:415: -1- AC_SUBST_TRACE([LTLIBOBJS]) -m4trace:configure.ac:415: -1- m4_pattern_allow([^LTLIBOBJS$]) -m4trace:configure.ac:415: -1- AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"]) -m4trace:configure.ac:415: -1- AC_SUBST([am__EXEEXT_TRUE]) -m4trace:configure.ac:415: -1- AC_SUBST_TRACE([am__EXEEXT_TRUE]) -m4trace:configure.ac:415: -1- m4_pattern_allow([^am__EXEEXT_TRUE$]) -m4trace:configure.ac:415: -1- AC_SUBST([am__EXEEXT_FALSE]) -m4trace:configure.ac:415: -1- AC_SUBST_TRACE([am__EXEEXT_FALSE]) -m4trace:configure.ac:415: -1- m4_pattern_allow([^am__EXEEXT_FALSE$]) -m4trace:configure.ac:415: -1- _AM_SUBST_NOTMAKE([am__EXEEXT_TRUE]) -m4trace:configure.ac:415: -1- _AM_SUBST_NOTMAKE([am__EXEEXT_FALSE]) -m4trace:configure.ac:415: -1- AC_SUBST_TRACE([top_builddir]) -m4trace:configure.ac:415: -1- AC_SUBST_TRACE([top_build_prefix]) -m4trace:configure.ac:415: -1- AC_SUBST_TRACE([srcdir]) -m4trace:configure.ac:415: -1- AC_SUBST_TRACE([abs_srcdir]) -m4trace:configure.ac:415: -1- AC_SUBST_TRACE([top_srcdir]) -m4trace:configure.ac:415: -1- AC_SUBST_TRACE([abs_top_srcdir]) -m4trace:configure.ac:415: -1- AC_SUBST_TRACE([builddir]) -m4trace:configure.ac:415: -1- AC_SUBST_TRACE([abs_builddir]) -m4trace:configure.ac:415: -1- AC_SUBST_TRACE([abs_top_builddir]) -m4trace:configure.ac:415: -1- AC_SUBST_TRACE([INSTALL]) -m4trace:configure.ac:415: -1- AC_SUBST_TRACE([MKDIR_P]) -m4trace:configure.ac:415: -1- AC_REQUIRE_AUX_FILE([ltmain.sh]) +m4trace:configure.ac:435: -1- AC_SUBST([LIB@&t@OBJS], [$ac_libobjs]) +m4trace:configure.ac:435: -1- AC_SUBST_TRACE([LIB@&t@OBJS]) +m4trace:configure.ac:435: -1- m4_pattern_allow([^LIB@&t@OBJS$]) +m4trace:configure.ac:435: -1- AC_SUBST([LTLIBOBJS], [$ac_ltlibobjs]) +m4trace:configure.ac:435: -1- AC_SUBST_TRACE([LTLIBOBJS]) +m4trace:configure.ac:435: -1- m4_pattern_allow([^LTLIBOBJS$]) +m4trace:configure.ac:435: -1- AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"]) +m4trace:configure.ac:435: -1- AC_SUBST([am__EXEEXT_TRUE]) +m4trace:configure.ac:435: -1- AC_SUBST_TRACE([am__EXEEXT_TRUE]) +m4trace:configure.ac:435: -1- m4_pattern_allow([^am__EXEEXT_TRUE$]) +m4trace:configure.ac:435: -1- AC_SUBST([am__EXEEXT_FALSE]) +m4trace:configure.ac:435: -1- AC_SUBST_TRACE([am__EXEEXT_FALSE]) +m4trace:configure.ac:435: -1- m4_pattern_allow([^am__EXEEXT_FALSE$]) +m4trace:configure.ac:435: -1- _AM_SUBST_NOTMAKE([am__EXEEXT_TRUE]) +m4trace:configure.ac:435: -1- _AM_SUBST_NOTMAKE([am__EXEEXT_FALSE]) +m4trace:configure.ac:435: -1- AC_SUBST_TRACE([top_builddir]) +m4trace:configure.ac:435: -1- AC_SUBST_TRACE([top_build_prefix]) +m4trace:configure.ac:435: -1- AC_SUBST_TRACE([srcdir]) +m4trace:configure.ac:435: -1- AC_SUBST_TRACE([abs_srcdir]) +m4trace:configure.ac:435: -1- AC_SUBST_TRACE([top_srcdir]) +m4trace:configure.ac:435: -1- AC_SUBST_TRACE([abs_top_srcdir]) +m4trace:configure.ac:435: -1- AC_SUBST_TRACE([builddir]) +m4trace:configure.ac:435: -1- AC_SUBST_TRACE([abs_builddir]) +m4trace:configure.ac:435: -1- AC_SUBST_TRACE([abs_top_builddir]) +m4trace:configure.ac:435: -1- AC_SUBST_TRACE([INSTALL]) +m4trace:configure.ac:435: -1- AC_SUBST_TRACE([MKDIR_P]) +m4trace:configure.ac:435: -1- AC_REQUIRE_AUX_FILE([ltmain.sh]) diff -Nru mstflint-4.17.0+1/.clang-format mstflint-4.21.0+1/.clang-format --- mstflint-4.17.0+1/.clang-format 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/.clang-format 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,77 @@ +--- +# BasedOnStyle: Google +AccessModifierOffset: -4 +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +ConstructorInitializerIndentWidth: 4 +ConstructorInitializerAllOnOneLineOrOnePerLine: true +AlignEscapedNewlinesLeft: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakTemplateDeclarations: true +AlwaysBreakBeforeMultilineStrings: false +BreakBeforeBinaryOperators: false +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: AfterColon +BreakConstructorInitializersBeforeComma: false +BinPackParameters: false +Standard: Auto +IndentWidth: 4 +TabWidth: 8 +UseTab: Never +BraceWrapping: + AfterClass: false + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + IndentBraces: true + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +CommentPragmas: '^ ignore_styling:' +ColumnLimit: 200 +DerivePointerBinding: true +DerivePointerAlignment: false +ExperimentalAutoDetectBinPacking: true +IndentCaseLabels: true +KeepEmptyLinesAtTheStartOfBlocks: false +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCSpaceBeforeProtocolList: false +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 60 +PenaltyBreakString: 60 +PenaltyBreakFirstLessLess: 60 +PenaltyExcessCharacter: 60 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerBindsToType: true +PointerAlignment: Left +SpacesBeforeTrailingComments: 1 +Cpp11BracedListStyle: true +BreakBeforeBraces: Allman +IndentFunctionDeclarationAfterType: true +SpacesInParentheses: false +SpacesInAngles: false +SpaceInEmptyParentheses: false +SpacesInCStyleCastParentheses: false +SpaceAfterControlStatementKeyword: true +SpaceBeforeAssignmentOperators: true +ContinuationIndentWidth: 2 +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: false +SpaceAfterTemplateKeyword: false +SpacesInContainerLiterals: false +AllowShortFunctionsOnASingleLine: Inline +BreakStringLiterals: false +... + diff -Nru mstflint-4.17.0+1/cmdif/cmdif.py mstflint-4.21.0+1/cmdif/cmdif.py --- mstflint-4.17.0+1/cmdif/cmdif.py 2021-06-29 08:49:47.000000000 +0000 +++ mstflint-4.21.0+1/cmdif/cmdif.py 2022-07-31 09:33:57.000000000 +0000 @@ -1,5 +1,5 @@ -# Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved. +# Copyright (c) 2004-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU @@ -28,7 +28,7 @@ # 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. -#-- +# -- from __future__ import print_function import os @@ -37,28 +37,36 @@ import ctypes import mtcr + def ones(n): return (1 << n) - 1 + def extractField(val, start, size): - return (val & (ones(size)<> start + return (val & (ones(size) << start)) >> start + def insertField(val1, start1, val2, start2, size): - return val1 & ~(ones(size)<in), _data); \ - } \ - _rc = icmd_send_command(mf, op, _data, _cmd_size, skip_write); \ - if (_rc) { \ - free(_data); \ - return convert_rc(_rc); \ - } \ - struct_name##_unpack(cmd_struct, _data); \ - free(_data); \ + int _rc; \ + int _cmd_size = struct_name##_size(); \ + u_int8_t* _data = (u_int8_t*)malloc(sizeof(u_int8_t) * _cmd_size); \ + if (!_data) \ + { \ + return GCIF_STATUS_NO_MEM; \ + } \ + memset(_data, 0, sizeof(u_int8_t) * _cmd_size); \ + if (should_pack) \ + { \ + struct_name##_in_pack(&(cmd_struct->in), _data); \ + } \ + _rc = icmd_send_command(mf, op, _data, _cmd_size, skip_write); \ + if (_rc) \ + { \ + free(_data); \ + return convert_rc(_rc); \ + } \ + struct_name##_out_unpack(&(cmd_struct->out), _data); \ + free(_data); \ return GCIF_STATUS_SUCCESS - #endif diff -Nru mstflint-4.17.0+1/cmdif/icmd_cif_open.c mstflint-4.21.0+1/cmdif/icmd_cif_open.c --- mstflint-4.17.0+1/cmdif/icmd_cif_open.c 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/cmdif/icmd_cif_open.c 2022-07-31 09:33:57.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) Jan 2013 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2013-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -38,15 +38,13 @@ #include "icmd_cif_open.h" #include "icmd_cif_macros.h" - -#define MH_SYNC_OPCODE 0x8402 +#define MH_SYNC_OPCODE 0x8402 #define MH_SYNC_STATUS_OPCODE 0x8403 /* * gcif_get_fw_info */ -int gcif_get_fw_info(mfile *mf, - OUT struct connectib_icmd_get_fw_info *fw_info) +int gcif_get_fw_info(mfile* mf, OUT struct connectib_icmd_get_fw_info* fw_info) { SEND_ICMD_FLOW(mf, GET_FW_INFO, connectib_icmd_get_fw_info, fw_info, 0, 1); } @@ -54,29 +52,28 @@ /* * get_icmd_query_cap */ -int get_icmd_query_cap(mfile *mf, struct icmd_hca_icmd_query_cap_general *icmd_query_caps) +int get_icmd_query_cap(mfile* mf, struct icmd_hca_icmd_query_cap_general* icmd_query_caps) { - SEND_ICMD_FLOW(mf, GET_ICMD_QUERY_CAP, icmd_hca_icmd_query_cap_general, - icmd_query_caps, 1, 0); + SEND_ICMD_FLOW(mf, GET_ICMD_QUERY_CAP, icmd_hca_icmd_query_cap_general, icmd_query_caps, 1, 0); } -int gcif_mh_sync(mfile *mf, struct connectx4_icmd_mh_sync *mh_sync) +int gcif_mh_sync(mfile* mf, struct connectx4_icmd_mh_sync* mh_sync) { SEND_ICMD_FLOW(mf, MH_SYNC_OPCODE, connectx4_icmd_mh_sync, mh_sync, 1, 0); } -int gcif_mh_sync_status(mfile *mf, struct connectx4_icmd_mh_sync *mh_sync_out) +int gcif_mh_sync_status(mfile* mf, struct connectx4_icmd_mh_sync* mh_sync_out) { memset(mh_sync_out, 0x0, sizeof(*mh_sync_out)); SEND_ICMD_FLOW(mf, MH_SYNC_STATUS_OPCODE, connectx4_icmd_mh_sync, mh_sync_out, 1, 0); } -int gcif_set_itrace(mfile *mf, struct connectib_itrace *itrace) +int gcif_set_itrace(mfile* mf, struct connectib_itrace* itrace) { SEND_ICMD_FLOW(mf, SET_ITRACE, connectib_itrace, itrace, 1, 0); } -int gcif_set_port_sniffer(mfile *mf, struct connectib_icmd_set_port_sniffer *set_port_sniffer) +int gcif_set_port_sniffer(mfile* mf, struct connectib_icmd_set_port_sniffer* set_port_sniffer) { SEND_ICMD_FLOW(mf, SET_PORT_SNIFFER, connectib_icmd_set_port_sniffer, set_port_sniffer, 1, 0); } diff -Nru mstflint-4.17.0+1/cmdif/icmd_cif_open.h mstflint-4.21.0+1/cmdif/icmd_cif_open.h --- mstflint-4.17.0+1/cmdif/icmd_cif_open.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/cmdif/icmd_cif_open.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) Jan 2013 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2013-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -30,11 +30,12 @@ * SOFTWARE. */ -#ifndef _ICMD_OPEN_LIB /* guard */ +#ifndef _ICMD_OPEN_LIB /* guard */ #define _ICMD_OPEN_LIB #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif #include @@ -55,31 +56,31 @@ #define INOUT #endif -enum { - GET_FW_INFO = 0x8007, - FLASH_REG_ACCESS = 0x9001, -}; + enum + { + GET_FW_INFO = 0x8007, + FLASH_REG_ACCESS = 0x9001, + }; #ifdef MST_UL -// instead of cib_cif.h in mstflint -enum { - GET_ICMD_QUERY_CAP = 0x8400, - SET_ITRACE = 0xf003, - SET_PORT_SNIFFER = 0xc002, -}; + // instead of cib_cif.h in mstflint + enum + { + GET_ICMD_QUERY_CAP = 0x8400, + SET_ITRACE = 0xf003, + SET_PORT_SNIFFER = 0xc002, + }; #endif -int gcif_get_fw_info(mfile *mf, - OUT struct connectib_icmd_get_fw_info *fw_info); + int gcif_get_fw_info(mfile* mf, OUT struct connectib_icmd_get_fw_info* fw_info); -int get_icmd_query_cap(mfile *mf, - struct icmd_hca_icmd_query_cap_general *icmd_query_caps); + int get_icmd_query_cap(mfile* mf, struct icmd_hca_icmd_query_cap_general* icmd_query_caps); -int gcif_mh_sync(mfile *mf, struct connectx4_icmd_mh_sync *mh_sync); + int gcif_mh_sync(mfile* mf, struct connectx4_icmd_mh_sync* mh_sync); -int gcif_mh_sync_status(mfile *mf, struct connectx4_icmd_mh_sync *mh_sync); + int gcif_mh_sync_status(mfile* mf, struct connectx4_icmd_mh_sync* mh_sync); -int gcif_set_port_sniffer(mfile *mf, struct connectib_icmd_set_port_sniffer *set_port_sniffer); + int gcif_set_port_sniffer(mfile* mf, struct connectib_icmd_set_port_sniffer* set_port_sniffer); #ifdef __cplusplus } diff -Nru mstflint-4.17.0+1/cmdif/Makefile.am mstflint-4.21.0+1/cmdif/Makefile.am --- mstflint-4.17.0+1/cmdif/Makefile.am 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/cmdif/Makefile.am 2022-04-28 11:50:03.000000000 +0000 @@ -1,4 +1,5 @@ # Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved. +# Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU @@ -37,7 +38,7 @@ AM_CPPFLAGS = -I. -I../common -I../tools_layouts -I${MTCR_DIR} -I.. -I$(USER_DIR)/${MTCR_CONF_DIR} CCMDIF_SO = ccmdif.so -AM_CFLAGS = -W -Wall -g -MP -MD $(COMPILER_FPIC) -DCMDIF_EXPORTS +AM_CFLAGS = -W -Wall -g -MP -MD $(COMPILER_FPIC) -DCMDIF_EXPORTS CMDIF_VERSION = 1 lib_LTLIBRARIES = libcmdif.a diff -Nru mstflint-4.17.0+1/cmdif/Makefile.in mstflint-4.21.0+1/cmdif/Makefile.in --- mstflint-4.17.0+1/cmdif/Makefile.in 2021-06-29 08:51:07.000000000 +0000 +++ mstflint-4.21.0+1/cmdif/Makefile.in 2022-07-31 09:34:41.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.3 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2020 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -15,6 +15,7 @@ @SET_MAKE@ # Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved. +# Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU @@ -394,7 +395,7 @@ TOOLS_LAYOUTS_DIR = $(USER_DIR)/tools_layouts AM_CPPFLAGS = -I. -I../common -I../tools_layouts -I${MTCR_DIR} -I.. -I$(USER_DIR)/${MTCR_CONF_DIR} CCMDIF_SO = ccmdif.so -AM_CFLAGS = -W -Wall -g -MP -MD $(COMPILER_FPIC) -DCMDIF_EXPORTS +AM_CFLAGS = -W -Wall -g -MP -MD $(COMPILER_FPIC) -DCMDIF_EXPORTS CMDIF_VERSION = 1 lib_LTLIBRARIES = libcmdif.a cmdifincludedir = $(includedir)/mstflint/cmdif/ diff -Nru mstflint-4.17.0+1/cmdif/tools_cif.c mstflint-4.21.0+1/cmdif/tools_cif.c --- mstflint-4.17.0+1/cmdif/tools_cif.c 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/cmdif/tools_cif.c 2022-07-31 09:33:57.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) Jan 2013 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2013-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -53,78 +53,81 @@ #define QPC_READ_OP 0x67 #define QPC_WRITE_OP 0x69 -#define CHECK_RC(rc) \ - if (rc) {return (MError)rc;} - -#define BE32_TO_CPU(s, n) do { \ - u_int32_t i; \ - u_int32_t *p = (u_int32_t*)(s); \ - for (i = 0; i < (n); i++, p++) \ - *p = __be32_to_cpu(*p); \ -} while (0) +#define CHECK_RC(rc) \ + if (rc) \ + { \ + return (MError)rc; \ + } + +#define BE32_TO_CPU(s, n) \ + do \ + { \ + u_int32_t i; \ + u_int32_t* p = (u_int32_t*)(s); \ + for (i = 0; i < (n); i++, p++) \ + *p = __be32_to_cpu(*p); \ + } while (0) #if __BYTE_ORDER == __BIG_ENDIAN -#define SWAP_DW_BE(uint64_num) \ - (((uint64_num) & 0xffffffffULL) << 32) | (((uint64_num) >> 32) & 0xffffffffULL) +#define SWAP_DW_BE(uint64_num) (((uint64_num)&0xffffffffULL) << 32) | (((uint64_num) >> 32) & 0xffffffffULL) #else #define SWAP_DW_BE(uint64_num) (uint64_num) #endif -//TODO: adrianc: if we find ourselves adding more and more commands consider using a macro to save code. -//TODO: adrianc: when library expands consider returning its own error code +// TODO: adrianc: if we find ourselves adding more and more commands consider using a macro to save code. +// TODO: adrianc: when library expands consider returning its own error code -MError tcif_query_dev_cap(mfile *dev, u_int32_t offset, u_int64_t *data) +MError tcif_query_dev_cap(mfile* dev, u_int32_t offset, u_int64_t* data) { - int rc = tools_cmdif_send_mbox_command(dev, 0, QUERY_DEV_CAP_OP, 0, offset, (u_int32_t*)data, 8, 1); CHECK_RC(rc); + int rc = tools_cmdif_send_mbox_command(dev, 0, QUERY_DEV_CAP_OP, 0, offset, (u_int32_t*)data, 8, 1); + CHECK_RC(rc); BE32_TO_CPU(data, 2); *data = SWAP_DW_BE(*data); return ME_OK; } - -MError tcif_query_global_def_params(mfile *dev, struct tools_open_query_def_params_global *global_params) +MError tcif_query_global_def_params(mfile* dev, struct tools_open_query_def_params_global* global_params) { u_int8_t data[TOOLS_OPEN_QUERY_DEF_PARAMS_GLOBAL_SIZE] = {0}; - int rc = tools_cmdif_send_mbox_command(dev, 0, QUERY_DEF_PARAMS_OP, 0, 0, data, sizeof(data), 0); CHECK_RC(rc); + int rc = tools_cmdif_send_mbox_command(dev, 0, QUERY_DEF_PARAMS_OP, 0, 0, data, sizeof(data), 0); + CHECK_RC(rc); tools_open_query_def_params_global_unpack(global_params, data); return ME_OK; } - -MError tcif_query_per_port_def_params(mfile *dev, u_int8_t port, struct tools_open_query_def_params_per_port *port_params) +MError + tcif_query_per_port_def_params(mfile* dev, u_int8_t port, struct tools_open_query_def_params_per_port* port_params) { u_int8_t data[TOOLS_OPEN_QUERY_DEF_PARAMS_PER_PORT_SIZE] = {0}; - int rc = tools_cmdif_send_mbox_command(dev, 0, QUERY_DEF_PARAMS_OP, port, 0, data, sizeof(data), 0); CHECK_RC(rc); + int rc = tools_cmdif_send_mbox_command(dev, 0, QUERY_DEF_PARAMS_OP, port, 0, data, sizeof(data), 0); + CHECK_RC(rc); tools_open_query_def_params_per_port_unpack(port_params, data); return ME_OK; } - -MError tcif_qpc_context_read(mfile *dev, u_int32_t qpn, u_int32_t source, u_int8_t *data, u_int32_t len) +MError tcif_qpc_context_read(mfile* dev, u_int32_t qpn, u_int32_t source, u_int8_t* data, u_int32_t len) { u_int32_t input_mod = 0; - input_mod = MERGE(input_mod, source, 24, 8); - input_mod = MERGE(input_mod, qpn, 0, 24); + input_mod = MERGE(input_mod, source, 24, 8); + input_mod = MERGE(input_mod, qpn, 0, 24); int rc = tools_cmdif_send_mbox_command(dev, input_mod, QPC_READ_OP, 0, 0, data, len, 1); CHECK_RC(rc); return ME_OK; } - -MError tcif_qpc_context_write(mfile *dev, u_int32_t qpn, u_int32_t source, u_int8_t *data, u_int32_t len) +MError tcif_qpc_context_write(mfile* dev, u_int32_t qpn, u_int32_t source, u_int8_t* data, u_int32_t len) { u_int32_t input_mod = 0; - input_mod = MERGE(input_mod, source, 24, 8); - input_mod = MERGE(input_mod, qpn, 0, 24); + input_mod = MERGE(input_mod, source, 24, 8); + input_mod = MERGE(input_mod, qpn, 0, 24); int rc = tools_cmdif_send_mbox_command(dev, input_mod, QPC_WRITE_OP, 0, 0, data, len, 0); CHECK_RC(rc); return ME_OK; } - -MError tcif_hw_access(mfile *dev, u_int64_t key, int lock_unlock) +MError tcif_hw_access(mfile* dev, u_int64_t key, int lock_unlock) { return (MError)tools_cmdif_send_inline_cmd(dev, key, NULL, 0, HW_ACCESS_OP, lock_unlock); } @@ -133,8 +136,7 @@ return m_err2str(rc); } - -MError tcif_cr_mbox_supported(mfile *dev) +MError tcif_cr_mbox_supported(mfile* dev) { #if defined(__FreeBSD__) || defined(UEFI_BUILD) (void)dev; @@ -142,5 +144,4 @@ #else return (MError)tools_cmdif_is_cr_mbox_supported(dev); #endif - } diff -Nru mstflint-4.17.0+1/cmdif/tools_cif.h mstflint-4.21.0+1/cmdif/tools_cif.h --- mstflint-4.17.0+1/cmdif/tools_cif.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/cmdif/tools_cif.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) Jan 2013 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2013-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -38,92 +38,93 @@ #define TOOLS_CIF_H #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif #include #include - -/** - * tcif_query_dev_cap: - * @param[in] dev A pointer to a device context. - * @param[in] offs offset in even dword to read from the DEV_CAP vector - * @param[out] data Quad-word read from the device capabilities vector from offset: offs - - * @return One of the MError* values, or a raw - **/ -MError tcif_query_dev_cap(mfile *dev, u_int32_t offs, u_int64_t *data); - -/** - * tcif_query_global_def_params: - * @param[in] dev A pointer to a device context. - * @param[in/out] global_params pointer to global params struct - - * @return One of the MError* values, or a raw - **/ -MError tcif_query_global_def_params(mfile *dev, struct tools_open_query_def_params_global *global_params); - -/** - * tcif_query_per_port_def_params: - * @param[in] dev A pointer to a device context. - * @param[in] port Port that the query will be performed on (1 or 2) - * @param[in/out] port_params Pointer to port params struct - - * @return One of the MError* values, or a raw - **/ -MError tcif_query_per_port_def_params(mfile *dev, u_int8_t port, struct tools_open_query_def_params_per_port *port_params); - -/** - * tcif_qpc_context_read: - * @param[in] dev A pointer to a device context. - * @param[in] qpn QP Number - * @param[in] source QP Source - * @param[in] data Data that was read - * - * @return One of the MError* values, or a raw - **/ -MError tcif_qpc_context_read(mfile *dev, u_int32_t qpn, u_int32_t source, u_int8_t *data, u_int32_t len); - -/** - * tcif_qpc_context_write: - * @param[in] dev A pointer to a device context. - * @param[in] qpn QP Number - * @param[in] source QP Source - * @param[in] data Data to be written - * - * @return One of the MError* values, or a raw - **/ -MError tcif_qpc_context_write(mfile *dev, u_int32_t qpn, u_int32_t source, u_int8_t *data, u_int32_t len); - - -/** - * tcif_hw_access: - * @param[in] dev A pointer to a device context. - * @param[in] key key with which we attempt to lock/unlock the cr-space (should be 0 for locking) - * @param[out] lock_unlock 1-lock , 0-unlock - - * @return One of the MError* values, or a raw - **/ -MError tcif_hw_access(mfile *dev, u_int64_t key, int lock_unlock); - -/** - * tcif_cr_mailbox_supported: - * @param[in] dev A pointer to a device context. - - * @return ME_OK - cr mailbox supported - * ME_SEM_LOCKED - tools HCR semaphore locked - * ME_CMDIF_NOT_SUPP - cr mailbox not supported - **/ -MError tcif_cr_mbox_supported(mfile *dev); - -/** - * tcif_err2str: - * @param[in] rc return code from one of the above functions - - * @return string describing the error occurred. - **/ -const char* tcif_err2str(MError rc); + /** + * tcif_query_dev_cap: + * @param[in] dev A pointer to a device context. + * @param[in] offs offset in even dword to read from the DEV_CAP vector + * @param[out] data Quad-word read from the device capabilities vector from offset: offs + + * @return One of the MError* values, or a raw + **/ + MError tcif_query_dev_cap(mfile* dev, u_int32_t offs, u_int64_t* data); + + /** + * tcif_query_global_def_params: + * @param[in] dev A pointer to a device context. + * @param[in/out] global_params pointer to global params struct + + * @return One of the MError* values, or a raw + **/ + MError tcif_query_global_def_params(mfile* dev, struct tools_open_query_def_params_global* global_params); + + /** + * tcif_query_per_port_def_params: + * @param[in] dev A pointer to a device context. + * @param[in] port Port that the query will be performed on (1 or 2) + * @param[in/out] port_params Pointer to port params struct + + * @return One of the MError* values, or a raw + **/ + MError tcif_query_per_port_def_params(mfile* dev, + u_int8_t port, + struct tools_open_query_def_params_per_port* port_params); + + /** + * tcif_qpc_context_read: + * @param[in] dev A pointer to a device context. + * @param[in] qpn QP Number + * @param[in] source QP Source + * @param[in] data Data that was read + * + * @return One of the MError* values, or a raw + **/ + MError tcif_qpc_context_read(mfile* dev, u_int32_t qpn, u_int32_t source, u_int8_t* data, u_int32_t len); + + /** + * tcif_qpc_context_write: + * @param[in] dev A pointer to a device context. + * @param[in] qpn QP Number + * @param[in] source QP Source + * @param[in] data Data to be written + * + * @return One of the MError* values, or a raw + **/ + MError tcif_qpc_context_write(mfile* dev, u_int32_t qpn, u_int32_t source, u_int8_t* data, u_int32_t len); + + /** + * tcif_hw_access: + * @param[in] dev A pointer to a device context. + * @param[in] key key with which we attempt to lock/unlock the cr-space (should be 0 for locking) + * @param[out] lock_unlock 1-lock , 0-unlock + + * @return One of the MError* values, or a raw + **/ + MError tcif_hw_access(mfile* dev, u_int64_t key, int lock_unlock); + + /** + * tcif_cr_mailbox_supported: + * @param[in] dev A pointer to a device context. + + * @return ME_OK - cr mailbox supported + * ME_SEM_LOCKED - tools HCR semaphore locked + * ME_CMDIF_NOT_SUPP - cr mailbox not supported + **/ + MError tcif_cr_mbox_supported(mfile* dev); + + /** + * tcif_err2str: + * @param[in] rc return code from one of the above functions + + * @return string describing the error occurred. + **/ + const char* tcif_err2str(MError rc); #ifdef __cplusplus } diff -Nru mstflint-4.17.0+1/cmdparser/cmdparser.cpp mstflint-4.21.0+1/cmdparser/cmdparser.cpp --- mstflint-4.17.0+1/cmdparser/cmdparser.cpp 2021-06-29 08:49:47.000000000 +0000 +++ mstflint-4.21.0+1/cmdparser/cmdparser.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) Jan 2013 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2013-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -30,7 +30,6 @@ * SOFTWARE. */ - /*Includes*/ #include #include @@ -42,7 +41,6 @@ #include "my_getopt.h" #include "cmdparser.h" - /*Constants*/ #define IDENT_LENGTH 4 #define PREFIX_SPACE_SIZE 8 @@ -51,45 +49,54 @@ #define COLON_AND_SPACE_SIZE 2 #define TAB_SIZE 4 - /*Declarations*/ -typedef vector < bool > vec_bool; +typedef vector vec_bool; static string CreateIndentFromInt(int ident_size); static void FindAndReplace(string& source, string::size_type pos, string const& find, string const& replace); -static bool FindAndHandleNewLinePos(string& str, int& product_length, int& local_space_size, int& str_type_len, - int& new_line_pos, string newline_and_ident, int deli_is_space); +static bool FindAndHandleNewLinePos(string& str, + int& product_length, + int& local_space_size, + int& str_type_len, + int& new_line_pos, + string newline_and_ident, + int deli_is_space); /****************************************************/ /************class CommandLineRequester**************/ /****************************************************/ /* private methods */ -string CommandLineRequester::BuildOptStr(option_ifc_t &opt) +string CommandLineRequester::BuildOptStr(option_ifc_t& opt) { string str; - - if (opt.option_short_name != ' ') { + str += CreateIndentFromInt(opt.numOfSpaces * 4); + if (opt.option_short_name != ' ') + { str += "-"; str += opt.option_short_name; str += "|"; } str += "--"; str += opt.option_name; - if (opt.option_value != "") { + if (opt.option_value != "") + { str += " "; str += opt.option_value; } return str; } -string CommandLineRequester::FormatUsageStr(string str, int str_type_len, int ident_size, - int is_not_description, bool is_left_side) +string CommandLineRequester::FormatUsageStr(string str, + int str_type_len, + int ident_size, + int is_not_description, + bool is_left_side) { string ident = CreateIndentFromInt(ident_size); string newline_and_ident = "\n" + ident; - //replace tabs in spaces + // replace tabs in spaces FindAndReplace(str, 0, "\t", CreateIndentFromInt(TAB_SIZE)); - //helper variables for formatting + // helper variables for formatting int residual = str.size(); int initial_residual = residual; int product_length = 0; @@ -97,38 +104,43 @@ int new_line_pos = 0; int space_not_exists_count = 0; - //iterate while the remained text is longer then a valid line length - while (residual >= str_type_len + local_space_size) { - //search for a user defined new line. + // iterate while the remained text is longer then a valid line length + while (residual >= str_type_len + local_space_size) + { + // search for a user defined new line. if (FindAndHandleNewLinePos(str, product_length, local_space_size, str_type_len, new_line_pos, - newline_and_ident, 0)) { + newline_and_ident, 0)) + { goto next_iteration; } - //if no such is found. try to find a space to break the line at it's position. + // if no such is found. try to find a space to break the line at it's position. if (FindAndHandleNewLinePos(str, product_length, local_space_size, str_type_len, new_line_pos, - newline_and_ident, 1)) { + newline_and_ident, 1)) + { goto next_iteration; } - //if not found, break the line in a middle of a spaceless word. + // if not found, break the line in a middle of a spaceless word. space_not_exists_count++; new_line_pos = local_space_size + str_type_len + product_length; str.insert(new_line_pos, newline_and_ident); -next_iteration: - //update iteration variables + next_iteration: + // update iteration variables local_space_size = ident_size; initial_residual += ident_size; residual = initial_residual - new_line_pos - 1 + space_not_exists_count; product_length = new_line_pos; } - //handle the last line remained. if it is the left string - add aligned colon. - if (is_left_side) { - while (residual < str_type_len + ident_size) { + // handle the last line remained. if it is the left string - add aligned colon. + if (is_left_side) + { + while (residual < str_type_len + ident_size) + { str += " "; residual++; } str += ": "; } - //Add user defined new lines in last line remained. + // Add user defined new lines in last line remained. FindAndReplace(str, product_length + local_space_size + 1, "\n", newline_and_ident); return str; } @@ -146,20 +158,22 @@ sec_data.str2 = str2; for (optional_sections_t::iterator it = this->optional_sections_vec.begin(); - it != this->optional_sections_vec.end(); ++it) { - if (it->first == section_name) { + it != this->optional_sections_vec.end(); + ++it) + { + if (it->first == section_name) + { pos = distance(optional_sections_vec.begin(), it); this->optional_sections_vec[pos].second.push_back(sec_data); return; } } - this->optional_sections_vec.push_back(pair < string, vector > - (section_name, vector < struct optional_help_section_data >())); + this->optional_sections_vec.push_back(pair >( + section_name, vector())); pos = optional_sections_vec.size() - 1; this->optional_sections_vec[pos].second.push_back(sec_data); } - string CommandLineRequester::GetUsageSynopsis(bool hidden_options) { string prefix_space = CreateIndentFromInt(PREFIX_SPACE_SIZE); @@ -169,21 +183,29 @@ int curr_line_len = PREFIX_SPACE_SIZE; int new_line_pos; - for (vec_option_t::iterator it = this->options.begin(); it != this->options.end(); ++it) { - if (hidden_options == false) { //display only not hidden options - if ((*it).hidden == true) { + for (vec_option_t::iterator it = this->options.begin(); it != this->options.end(); ++it) + { + if (hidden_options == false) + { // display only not hidden options + if ((*it).hidden == true) + { continue; } - } else { //display only hidden options - if ((*it).hidden == false) { + } + else + { // display only hidden options + if ((*it).hidden == false) + { continue; } } curr_str += (*it).is_mandatory ? "<" : "["; curr_str += this->BuildOptStr(*it); curr_str += (*it).is_mandatory ? ">" : "]"; - if (curr_str.size() + curr_line_len < RIGHT_STR_MAX_LEN + LEFT_STR_MAX_LEN) { - if (curr_line_len > PREFIX_SPACE_SIZE) { + if (curr_str.size() + curr_line_len < RIGHT_STR_MAX_LEN + LEFT_STR_MAX_LEN) + { + if (curr_line_len > PREFIX_SPACE_SIZE) + { str += " "; } curr_line_len += curr_str.size(); @@ -191,20 +213,26 @@ curr_str = ""; continue; } - while (curr_str.size() + curr_line_len >= RIGHT_STR_MAX_LEN + LEFT_STR_MAX_LEN) { - if (curr_str.size() < RIGHT_STR_MAX_LEN + LEFT_STR_MAX_LEN) { + while (curr_str.size() + curr_line_len >= RIGHT_STR_MAX_LEN + LEFT_STR_MAX_LEN) + { + if (curr_str.size() < RIGHT_STR_MAX_LEN + LEFT_STR_MAX_LEN) + { str += "\n" + prefix_space + curr_str; curr_line_len = PREFIX_SPACE_SIZE + curr_str.size(); curr_str = ""; continue; } new_line_pos = curr_str.find_last_of(" ", RIGHT_STR_MAX_LEN + LEFT_STR_MAX_LEN); - if ((size_t)new_line_pos == curr_str.npos) { + if ((size_t)new_line_pos == curr_str.npos) + { new_line_pos = RIGHT_STR_MAX_LEN + LEFT_STR_MAX_LEN; - } else { + } + else + { new_line_pos += 1; } - if (curr_line_len > PREFIX_SPACE_SIZE) { + if (curr_line_len > PREFIX_SPACE_SIZE) + { str += "\n" + prefix_space; curr_line_len = PREFIX_SPACE_SIZE; } @@ -213,24 +241,26 @@ str += "\n" + prefix_space; curr_line_len = PREFIX_SPACE_SIZE; } - if (curr_str != "") { + if (curr_str != "") + { str += curr_str; curr_line_len += PREFIX_SPACE_SIZE + curr_str.size(); curr_str = ""; } } - if (curr_str != prefix_space) { + if (curr_str != prefix_space) + { str += curr_str; str += "\n"; } - if (str != "") { + if (str != "") + { str = prefix_short_space + this->name + "\n" + str; } return str; } - string CommandLineRequester::GetUsageDescription() { optional_sec_data_t desc_data; @@ -241,7 +271,6 @@ return this->GetUsageOptionalSection(desc_vector); } - string CommandLineRequester::GetUsageOptions(bool hidden_options) { string str = ""; @@ -249,13 +278,19 @@ string prefix_space = CreateIndentFromInt(PREFIX_SPACE_SIZE); string prefix_short_space = CreateIndentFromInt(IDENT_LENGTH); - for (vec_option_t::iterator it = this->options.begin(); it != this->options.end(); ++it) { - if (hidden_options == false) { //display only not hidden options - if ((*it).hidden == true) { + for (vec_option_t::iterator it = this->options.begin(); it != this->options.end(); ++it) + { + if (hidden_options == false) + { // display only not hidden options + if ((*it).hidden == true) + { continue; } - } else { //display only hidden options - if ((*it).hidden == false) { + } + else + { // display only hidden options + if ((*it).hidden == false) + { continue; } } @@ -263,25 +298,27 @@ opt_str += this->BuildOptStr(*it); opt_str = FormatUsageStr(opt_str, LEFT_STR_MAX_LEN, PREFIX_SPACE_SIZE, 1, true); str += opt_str; - desc_str = FormatUsageStr((*it).description, RIGHT_STR_MAX_LEN, LEFT_STR_MAX_LEN + PREFIX_SPACE_SIZE + - COLON_AND_SPACE_SIZE, 0, false); + desc_str = FormatUsageStr((*it).description, RIGHT_STR_MAX_LEN, + LEFT_STR_MAX_LEN + PREFIX_SPACE_SIZE + COLON_AND_SPACE_SIZE, 0, false); str += desc_str; str += "\n"; } - if (str != "") { + if (str != "") + { str = prefix_short_space + this->name + "\n" + str; } return str; } - string CommandLineRequester::GetUsageOptionalSections(vector excluded_sections) { string res = ""; - for (unsigned i = 0; i < optional_sections_vec.size(); ++i) { + for (unsigned i = 0; i < optional_sections_vec.size(); ++i) + { if (find(excluded_sections.begin(), excluded_sections.end(), optional_sections_vec[i].first) != - excluded_sections.end()) { + excluded_sections.end()) + { continue; } res += optional_sections_vec[i].first + "\n"; @@ -294,8 +331,8 @@ { string data_string(""); string prefix_space = CreateIndentFromInt(PREFIX_SPACE_SIZE); - data_string += FormatUsageStr(prefix_space + it->str1, LEFT_STR_MAX_LEN + RIGHT_STR_MAX_LEN, PREFIX_SPACE_SIZE, 1, - false); + data_string += + FormatUsageStr(prefix_space + it->str1, LEFT_STR_MAX_LEN + RIGHT_STR_MAX_LEN, PREFIX_SPACE_SIZE, 1, false); data_string += "\n"; return data_string; } @@ -306,7 +343,7 @@ string prefix_space = CreateIndentFromInt(PREFIX_SPACE_SIZE); data_string += FormatUsageStr(prefix_space + it->str1, LEFT_STR_MAX_LEN, PREFIX_SPACE_SIZE, 1, true); data_string += FormatUsageStr(it->str2, RIGHT_STR_MAX_LEN, - LEFT_STR_MAX_LEN + PREFIX_SPACE_SIZE + COLON_AND_SPACE_SIZE, 0, false); + LEFT_STR_MAX_LEN + PREFIX_SPACE_SIZE + COLON_AND_SPACE_SIZE, 0, false); data_string += "\n"; return data_string; } @@ -314,56 +351,60 @@ string CommandLineRequester::GetUsageOptionalSection(vector section_vec) { string section_str(""); - if (section_vec.empty()) { + if (section_vec.empty()) + { return section_str; } - for (vector::iterator it = section_vec.begin(); it != section_vec.end(); ++it) { - if (it->str2 != "") { + for (vector::iterator it = section_vec.begin(); it != section_vec.end(); ++it) + { + if (it->str2 != "") + { section_str += this->GetUsageOptional2Str(it); - } else { + } + else + { section_str += this->GetUsageOptional1Str(it); } } return section_str; } - /****************************************************/ /**************class CommandLineParser***************/ /****************************************************/ /* private methods */ -void CommandLineParser::SetLastError(const char *fmt, ...) +void CommandLineParser::SetLastError(const char* fmt, ...) { - char buff[1024]; + const int MAX_BUFF = 1024; + char buff[MAX_BUFF]; va_list args; memset(buff, 0, sizeof(buff)); va_start(args, fmt); - vsprintf(buff, fmt, args); + vsnprintf(buff, MAX_BUFF, fmt, args); va_end(args); this->last_error.assign(buff); return; } - /* public methods */ -int CommandLineParser::AddRequester(CommandLineRequester *p_req) +int CommandLineParser::AddRequester(CommandLineRequester* p_req) { - for (vec_option_t::iterator it = p_req->GetOptions().begin(); - it != p_req->GetOptions().end(); ++it) { - //long option must be valid - if ((*it).option_name == "") { - this->SetLastError("Requester \"%s\" has long option empty", - p_req->GetName().c_str()); + for (vec_option_t::iterator it = p_req->GetOptions().begin(); it != p_req->GetOptions().end(); ++it) + { + // long option must be valid + if ((*it).option_name == "") + { + this->SetLastError("Requester \"%s\" has long option empty", p_req->GetName().c_str()); return 1; } - //check if long option already exists - map_str_p_command_line_req::iterator it2 = - this->long_opt_to_req_map.find((*it).option_name); - if (it2 != this->long_opt_to_req_map.end()) { - this->SetLastError("Option \"%s\" from requester \"%s\" " \ + // check if long option already exists + map_str_p_command_line_req::iterator it2 = this->long_opt_to_req_map.find((*it).option_name); + if (it2 != this->long_opt_to_req_map.end()) + { + this->SetLastError("Option \"%s\" from requester \"%s\" " "already exists in requester \"%s\"", (*it).option_name.c_str(), p_req->GetName().c_str(), @@ -371,12 +412,13 @@ return 1; } - //check if short option already exists - if ((*it).option_short_name != ' ') { //if space than no short option - map_char_str::iterator it2 = - this->short_opt_to_long_opt.find((*it).option_short_name); - if (it2 != this->short_opt_to_long_opt.end()) { - this->SetLastError("Option \'%c\' from requester \"%s\" " \ + // check if short option already exists + if ((*it).option_short_name != ' ') + { // if space than no short option + map_char_str::iterator it2 = this->short_opt_to_long_opt.find((*it).option_short_name); + if (it2 != this->short_opt_to_long_opt.end()) + { + this->SetLastError("Option \'%c\' from requester \"%s\" " "already exists in requester \"%s\"", (*it).option_short_name, p_req->GetName().c_str(), @@ -386,11 +428,12 @@ } } - //finally add the requester - for (vec_option_t::iterator it = p_req->GetOptions().begin(); - it != p_req->GetOptions().end(); ++it) { + // finally add the requester + for (vec_option_t::iterator it = p_req->GetOptions().begin(); it != p_req->GetOptions().end(); ++it) + { this->long_opt_to_req_map[(*it).option_name] = p_req; - if ((*it).option_short_name != ' ') { //if space than no short option + if ((*it).option_short_name != ' ') + { // if space than no short option this->short_opt_to_long_opt[(*it).option_short_name] = (*it).option_name; } } @@ -403,16 +446,17 @@ string res; string prefix_short_space = CreateIndentFromInt(IDENT_LENGTH); - //name + // name res = "NAME\n"; res += prefix_short_space; res += this->name; res += "\n\n"; - //synopsis + // synopsis res += "SYNOPSIS\n"; - for (list_p_command_line_req::iterator it = this->p_requesters_list.begin(); - it != this->p_requesters_list.end(); ++it) { + for (list_p_command_line_req::iterator it = this->p_requesters_list.begin(); it != this->p_requesters_list.end(); + ++it) + { res += (*it)->GetUsageSynopsis(hidden_options); } res += "\n"; @@ -424,39 +468,46 @@ { string res; - //synopsis + // synopsis res = GetSynopsis(hidden_options); - //description - if (hidden_options == false) { + // description + if (hidden_options == false) + { res += "DESCRIPTION\n"; for (list_p_command_line_req::iterator it = this->p_requesters_list.begin(); - it != this->p_requesters_list.end(); ++it) { + it != this->p_requesters_list.end(); + ++it) + { res += (*it)->GetUsageDescription() + "\n"; } } - //options + // options res += "OPTIONS\n"; - for (list_p_command_line_req::iterator it = this->p_requesters_list.begin(); - it != this->p_requesters_list.end(); ++it) { + for (list_p_command_line_req::iterator it = this->p_requesters_list.begin(); it != this->p_requesters_list.end(); + ++it) + { res += (*it)->GetUsageOptions(hidden_options) + "\n"; } - //optinal sections + // optinal sections for (list_p_command_line_req::iterator req_it = this->p_requesters_list.begin(); - req_it != this->p_requesters_list.end(); ++req_it) { + req_it != this->p_requesters_list.end(); + ++req_it) + { res += (*req_it)->GetUsageOptionalSections(excluded_sections); } return res; } - -ParseStatus CommandLineParser::ParseOptions(int argc, char **argv, - bool to_ignore_unknown_options, list_p_command_line_req *p_ignored_requesters_list) +ParseStatus CommandLineParser::ParseOptions(int argc, + char** argv, + bool to_ignore_unknown_options, + list_p_command_line_req* p_ignored_requesters_list) { - char **internal_argv; - struct option *options_arr = NULL; + char** internal_argv; + struct option* options_arr = NULL; string options_str = ""; vec_bool returned_option_types_vec; ParseStatus rc = PARSE_ERROR; @@ -465,18 +516,22 @@ int option_index = 0; unsigned i = 0; - //allocate internal_argv + // allocate internal_argv internal_argv = new char*[argc]; - if (!internal_argv) { + if (!internal_argv) + { this->SetLastError("Fail to allocate internal argv for parsing"); return PARSE_ERROR; } - for (int j = 0; j < argc; ++j) { + for (int j = 0; j < argc; ++j) + { internal_argv[j] = NULL; } - for (int j = 0; j < argc; ++j) { + for (int j = 0; j < argc; ++j) + { internal_argv[j] = new char[strlen(argv[j]) + 1]; - if (!internal_argv[j]) { + if (!internal_argv[j]) + { this->SetLastError("Fail to allocate internal argv[%u] for parsing", j); rc = PARSE_ERROR; goto argv_err_exit; @@ -484,10 +539,11 @@ strcpy(internal_argv[j], argv[j]); } - //allocate long_options_arr + // allocate long_options_arr num_options = this->long_opt_to_req_map.size(); options_arr = new struct option[num_options + 1]; - if (!options_arr) { + if (!options_arr) + { this->SetLastError("Fail to allocate long_options_arr"); rc = PARSE_ERROR; goto parse_exit; @@ -501,87 +557,117 @@ * that can be return by getopt_long_only() */ i = 0; - for (list_p_command_line_req::iterator it = this->p_requesters_list.begin(); - it != this->p_requesters_list.end(); ++it) { - for (vec_option_t::iterator it2 = (*it)->GetOptions().begin(); - it2 != (*it)->GetOptions().end(); ++it2) { + for (list_p_command_line_req::iterator it = this->p_requesters_list.begin(); it != this->p_requesters_list.end(); + ++it) + { + for (vec_option_t::iterator it2 = (*it)->GetOptions().begin(); it2 != (*it)->GetOptions().end(); ++it2) + { options_str += (*it2).option_short_name; options_arr[i].name = (char*)(*it2).option_name.c_str(); - if ((*it2).option_value != "") { + if ((*it2).option_value != "") + { options_arr[i].has_arg = 1; options_str += ":"; - } else { + } + else + { options_arr[i].has_arg = 0; } options_arr[i].flag = 0; - if ((*it2).option_short_name != ' ') { + if ((*it2).option_short_name != ' ') + { options_arr[i].val = (*it2).option_short_name; - } else { + } + else + { options_arr[i].val = 0; } - if (returned_option_types_vec.empty() || (returned_option_types_vec.size() < (unsigned int)options_arr[i].val + 1)) { + if (returned_option_types_vec.empty() || + (returned_option_types_vec.size() < (unsigned int)options_arr[i].val + 1)) + { for (unsigned j = returned_option_types_vec.size(); j < (unsigned int)options_arr[i].val + 1; ++j) returned_option_types_vec.push_back(false); } returned_option_types_vec[options_arr[i].val] = true; - //printf("%c %d ", options_arr[i].val, options_arr[i].val); + // printf("%c %d ", options_arr[i].val, options_arr[i].val); ++i; } } - //finally parse all options - if (to_ignore_unknown_options == true) { + // finally parse all options + if (to_ignore_unknown_options == true) + { tools_opterr = 0; - } else { + } + else + { tools_opterr = 1; } tools_optind = 0; ParseStatus curr_result; - while ((option_type = tools_getopt_long_only(argc, internal_argv, options_str.c_str(), - options_arr, &option_index)) != -1) { - //printf("option_type=\'%c\'\n", option_type); + while ((option_type = + tools_getopt_long_only(argc, internal_argv, options_str.c_str(), options_arr, &option_index)) != -1) + { + // printf("option_type=\'%c\'\n", option_type); string long_opt_name; - if (option_type == 0) { + if (option_type == 0) + { long_opt_name = options_arr[option_index].name; goto do_handle_option; - } else if (option_type == '?') { - if (to_ignore_unknown_options == true) { + } + else if (option_type == '?') + { + if (to_ignore_unknown_options == true) + { continue; } this->SetLastError("Bad input parameter"); rc = PARSE_ERROR_SHOW_USAGE; goto argv_err_exit; - } else if (returned_option_types_vec[option_type] == true) { + } + else if (returned_option_types_vec[option_type] == true) + { long_opt_name = this->short_opt_to_long_opt[option_type]; goto do_handle_option; - } else { + } + else + { this->SetLastError("getopt_long_only() returned character code 0%o", option_type); goto parse_exit; } -do_handle_option: - CommandLineRequester * p_req = this->long_opt_to_req_map[long_opt_name]; + do_handle_option: + CommandLineRequester* p_req = this->long_opt_to_req_map[long_opt_name]; bool ignore_this_req = false; - if (p_ignored_requesters_list) { + if (p_ignored_requesters_list) + { for (list_p_command_line_req::iterator it = p_ignored_requesters_list->begin(); - it != p_ignored_requesters_list->end(); ++it) { - if (p_req == (*it)) { + it != p_ignored_requesters_list->end(); + ++it) + { + if (p_req == (*it)) + { ignore_this_req = true; break; } } } - if (ignore_this_req == false) { - try{ + if (ignore_this_req == false) + { + try + { curr_result = p_req->HandleOption(long_opt_name, (tools_optarg == NULL) ? "" : tools_optarg); - }catch (std::exception&){ - this->SetLastError("Failed to handle option %s", long_opt_name.c_str()); + } + catch (std::exception& exc) + { + this->SetLastError(exc.what()); goto parse_exit; } - if (curr_result) { + if (curr_result) + { rc = curr_result; this->SetLastError("Failed to handle option %s", long_opt_name.c_str()); goto parse_exit; @@ -590,8 +676,10 @@ continue; } - if (tools_optind < argc) { - while (tools_optind < argc) { + if (tools_optind < argc) + { + while (tools_optind < argc) + { this->last_unknown_options += internal_argv[tools_optind]; this->last_unknown_options += " "; ++tools_optind; @@ -599,7 +687,8 @@ /*if (this->last_unknown_options != "") //remove last space this->last_unknown_options.erase(this->last_unknown_options.end() - 1, this->last_unknown_options.end());*/ - if (to_ignore_unknown_options == false) { + if (to_ignore_unknown_options == false) + { string str = "Found some non-option ARGV-elements "; str += this->last_unknown_options; this->SetLastError(str.c_str()); @@ -610,54 +699,62 @@ rc = PARSE_OK; parse_exit: for (int i = 0; i < argc; ++i) - delete [] internal_argv[i]; - delete [] internal_argv; - delete [] options_arr; + delete[] internal_argv[i]; + delete[] internal_argv; + delete[] options_arr; return rc; argv_err_exit: for (int i = 0; i < argc; ++i) - delete [] internal_argv[i]; - delete [] internal_argv; - if (options_arr) { - delete [] options_arr; + delete[] internal_argv[i]; + delete[] internal_argv; + if (options_arr) + { + delete[] options_arr; } return rc; } - /*Static functions*/ static string CreateIndentFromInt(int ident_size) { string ident = ""; int i; - for (i = 0; i < ident_size; i++) { + for (i = 0; i < ident_size; i++) + { ident += " "; } return ident; } - static void FindAndReplace(string& source, string::size_type pos, string const& find, string const& replace) { - for (string::size_type i = pos; (i = source.find(find, i)) != string::npos;) { + for (string::size_type i = pos; (i = source.find(find, i)) != string::npos;) + { source.replace(i, find.length(), replace); i += replace.length(); } } - -static bool FindAndHandleNewLinePos(string& str, int& product_length, int& local_space_size, int& str_type_len, - int& new_line_pos, string newline_and_ident, int deli_is_space) +static bool FindAndHandleNewLinePos(string& str, + int& product_length, + int& local_space_size, + int& str_type_len, + int& new_line_pos, + string newline_and_ident, + int deli_is_space) { int plus_one_for_space = 1 * deli_is_space; - if (!deli_is_space) { + if (!deli_is_space) + { new_line_pos = str.substr(product_length + local_space_size, str_type_len).find("\n"); - } else { - new_line_pos = str.substr(product_length + local_space_size + 1, str.npos). - find_last_of(" ", str_type_len - 1); } - if ((size_t)new_line_pos != str.npos) { + else + { + new_line_pos = str.substr(product_length + local_space_size + 1, str.npos).find_last_of(" ", str_type_len - 1); + } + if ((size_t)new_line_pos != str.npos) + { new_line_pos += local_space_size + product_length + plus_one_for_space; str.replace(new_line_pos, 1, newline_and_ident); return true; diff -Nru mstflint-4.17.0+1/cmdparser/cmdparser.h mstflint-4.21.0+1/cmdparser/cmdparser.h --- mstflint-4.17.0+1/cmdparser/cmdparser.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/cmdparser/cmdparser.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) Jan 2013 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2013-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -30,11 +30,9 @@ * SOFTWARE. */ - #ifndef _OPT_PARSER_H_ #define _OPT_PARSER_H_ - #include #include #include @@ -42,38 +40,41 @@ #include using namespace std; - /******************************************************/ typedef enum { - //Returns: 0 - OK / 1 - parsing error / 2 - OK but need to exit / 3 - some other error + // Returns: 0 - OK / 1 - parsing error / 2 - OK but need to exit / 3 - some other error PARSE_OK = 0, PARSE_ERROR = 1, PARSE_OK_WITH_EXIT = 3, PARSE_ERROR_SHOW_USAGE = 4 } ParseStatus; -typedef struct option_ifc { - string option_name; //must be valid - char option_short_name; //can be used as short option, if space than no short flag - string option_value; //if "" has no argument - string description; //will be display in usage - bool hidden; //if hidden than will not be diaplayed in regular usage +typedef struct option_ifc +{ + string option_name; // must be valid + char option_short_name; // can be used as short option, if space than no short flag + string option_value; // if "" has no argument + string description; // will be display in usage + int numOfSpaces; + bool hidden; // if hidden than will not be diaplayed in regular usage bool is_mandatory; } option_ifc_t; -typedef struct optional_help_section_data { +typedef struct optional_help_section_data +{ // format is: // : (with alignments) - string name; //section's name - string str1; //left string - string str2; //right string + string name; // section's name + string str1; // left string + string str2; // right string } optional_sec_data_t; -typedef vector < struct option_ifc > vec_option_t; -typedef vector < pair < string, vector < struct optional_help_section_data > > > optional_sections_t; +typedef vector vec_option_t; +typedef vector > > optional_sections_t; -class CommandLineRequester { +class CommandLineRequester +{ protected: // members vec_option_t options; @@ -82,7 +83,7 @@ optional_sections_t optional_sections_vec; // methods - string BuildOptStr(option_ifc_t &opt); + string BuildOptStr(option_ifc_t& opt); string FormatUsageStr(string str, int str_type_len, int ident_size, int is_not_description, bool is_left_side); string GetUsageOptionalSection(vector section_vec); string GetUsageOptional1Str(vector::iterator it); @@ -92,10 +93,7 @@ // methods CommandLineRequester(string req_name) : name(req_name) {} virtual ~CommandLineRequester() {} - inline void setToolName(string req_name) - { - name = req_name; - } + inline void setToolName(string req_name) { name = req_name; } inline void AddOptions(option_ifc_t options[], int arr_size) { for (int i = 0; i < arr_size; ++i) @@ -106,21 +104,20 @@ string option_value, string description, bool hidden = false, - bool is_mandatory = false) + bool is_mandatory = false, + int numOfSpaces = 0) { option_ifc_t opt; opt.option_name = option_name; opt.option_short_name = option_short_name; opt.option_value = option_value; opt.description = description; + opt.numOfSpaces = numOfSpaces; opt.hidden = hidden; opt.is_mandatory = is_mandatory; this->options.push_back(opt); } - inline void AddDescription(string desc) - { - this->description = desc; - } + inline void AddDescription(string desc) { this->description = desc; } inline vec_option_t& GetOptions() { return this->options; } inline string& GetName() { return this->name; } @@ -137,14 +134,13 @@ virtual ParseStatus HandleOption(string name, string value) = 0; }; - /******************************************************/ -typedef list < CommandLineRequester* > list_p_command_line_req; -typedef map < char, string > map_char_str; -typedef map < string, CommandLineRequester* > map_str_p_command_line_req; - +typedef list list_p_command_line_req; +typedef map map_char_str; +typedef map map_str_p_command_line_req; -class CommandLineParser { +class CommandLineParser +{ private: // members list_p_command_line_req p_requesters_list; @@ -156,30 +152,25 @@ string last_unknown_options; // methods - void SetLastError(const char *fmt, ...); + void SetLastError(const char* fmt, ...); + public: // methods - CommandLineParser(string parser_name) : - name(parser_name), last_error(""), last_unknown_options("") {} + CommandLineParser(string parser_name) : name(parser_name), last_error(""), last_unknown_options("") {} ~CommandLineParser() {} inline const char* GetErrDesc() { return this->last_error.c_str(); } inline const char* GetUnknownOptions() { return this->last_unknown_options.c_str(); } - inline void setParserName(string parser_name) - { - name = parser_name; - } - int AddRequester(CommandLineRequester *p_req); //if multiple option than fail + inline void setParserName(string parser_name) { name = parser_name; } + int AddRequester(CommandLineRequester* p_req); // if multiple option than fail - ParseStatus ParseOptions(int argc, char **argv, + ParseStatus ParseOptions(int argc, + char** argv, bool to_ignore_unknown_options = false, - list_p_command_line_req *p_ignored_requesters_list = NULL); + list_p_command_line_req* p_ignored_requesters_list = NULL); string GetSynopsis(bool hidden_options = false); string GetUsage(bool hidden_options = false, vector excluded_sections = vector()); }; - -#endif /* not defined _OPT_PARSER_H_ */ - - +#endif /* not defined _OPT_PARSER_H_ */ diff -Nru mstflint-4.17.0+1/cmdparser/Makefile.am mstflint-4.21.0+1/cmdparser/Makefile.am --- mstflint-4.17.0+1/cmdparser/Makefile.am 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/cmdparser/Makefile.am 2022-04-28 11:50:03.000000000 +0000 @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved. +# Copyright (c) 2004-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU @@ -34,7 +34,7 @@ INCLUDES = -I. -I../common -AM_CPPFLAGS = -W -g -MP -MD $(COMPILER_FPIC) +AM_CPPFLAGS = -W -g -MP -MD $(COMPILER_FPIC) $(COMPILER_FPIE) noinst_LIBRARIES = libcmdparser.a diff -Nru mstflint-4.17.0+1/cmdparser/Makefile.in mstflint-4.21.0+1/cmdparser/Makefile.in --- mstflint-4.17.0+1/cmdparser/Makefile.in 2021-06-29 08:51:07.000000000 +0000 +++ mstflint-4.21.0+1/cmdparser/Makefile.in 2022-07-31 09:34:41.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.3 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2020 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -15,7 +15,7 @@ @SET_MAKE@ #-- -# Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved. +# Copyright (c) 2004-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU @@ -380,7 +380,7 @@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ INCLUDES = -I. -I../common -AM_CPPFLAGS = -W -g -MP -MD $(COMPILER_FPIC) +AM_CPPFLAGS = -W -g -MP -MD $(COMPILER_FPIC) $(COMPILER_FPIE) noinst_LIBRARIES = libcmdparser.a libcmdparser_a_SOURCES = cmdparser.cpp my_getopt.c cmdparser.h my_getopt.h all: all-am diff -Nru mstflint-4.17.0+1/cmdparser/my_getopt.c mstflint-4.21.0+1/cmdparser/my_getopt.c --- mstflint-4.17.0+1/cmdparser/my_getopt.c 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/cmdparser/my_getopt.c 2022-07-31 09:33:57.000000000 +0000 @@ -23,24 +23,24 @@ /* This tells Alpha OSF/1 not to define a getopt prototype in . Ditto for AIX 3.2 and . */ - #ifndef _NO_PROTO - #define _NO_PROTO - #endif - - #ifdef HAVE_CONFIG_H - #include - #endif +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif - #if !defined(__STDC__) || !__STDC__ +#if !defined(__STDC__) || !__STDC__ /* This is a separate conditional since some stdc systems reject `defined (const)'. */ - #ifndef const - #define const - #endif - #endif +#ifndef const +#define const +#endif +#endif - #include - #include +#include +#include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C @@ -50,50 +50,48 @@ program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ - #define GETOPT_INTERFACE_VERSION 2 - #if !defined(_LIBC) && defined(__GLIBC__) && __GLIBC__ >= 2 - #include - #if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION - #define ELIDE_CODE - #endif - #endif - - +#define GETOPT_INTERFACE_VERSION 2 +#if !defined(_LIBC) && defined(__GLIBC__) && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ - #ifdef __GNU_LIBRARY__ +#ifdef __GNU_LIBRARY__ /* Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting prototypes for getopt. */ - #include - #include - #endif /* GNU C library. */ - - #ifdef HAVE_STRING_H - # include /* strstr / strdup */ - #else - # ifdef HAVE_STRINGS_H - # include /* strstr / strdup */ - # endif - #endif - - #ifdef VMS - #include - #if HAVE_STRING_H - 0 - #include - #endif - #endif +#include +#include +#endif /* GNU C library. */ + +#ifdef HAVE_STRING_H +#include /* strstr / strdup */ +#else +#ifdef HAVE_STRINGS_H +#include /* strstr / strdup */ +#endif +#endif + +#ifdef VMS +#include +#if HAVE_STRING_H - 0 +#include +#endif +#endif #include - #ifndef _ +#ifndef _ /* This is for other GNU distributions with internationalized messages. When compiling libc, the _ macro is predefined. */ - #ifdef HAVE_LIBINTL_H - # include - # define _(msgid) gettext(msgid) - #else - # define _(msgid) (msgid) - #endif - #endif +#ifdef HAVE_LIBINTL_H +#include +#define _(msgid) gettext(msgid) +#else +#define _(msgid) (msgid) +#endif +#endif /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user @@ -109,7 +107,7 @@ GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ - #include "my_getopt.h" +#include "my_getopt.h" /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, @@ -117,7 +115,7 @@ Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ -char *tools_optarg = NULL; +char* tools_optarg = NULL; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller @@ -147,7 +145,7 @@ If this is zero, or a null string, it means resume the scan by advancing to the next ARGV-element. */ -static char *nextchar; +static char* nextchar; /* Callers store zero here to inhibit the error message for unrecognized options. */ @@ -189,30 +187,30 @@ of the value of `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return -1 with `tools_optind' != ARGC. */ -static enum -{ - REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER -} ordering; +static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; /* Value of POSIXLY_CORRECT environment variable. */ -static char *posixly_correct; +static char* posixly_correct; - #ifdef __GNU_LIBRARY__ +#ifdef __GNU_LIBRARY__ /* We want to avoid inclusion of string.h with non-GNU libraries because there are many ways it can cause trouble. On some systems, it contains special magic macros that don't work in GCC. */ - #include - #define my_index strchr - #else +#include +#define my_index strchr +#else /* Avoid depending on library functions or files whose names are inconsistent. */ -static char* my_index(const char *str, const char chr) { - while (*str) { - if (*str == chr) { - return (char*) str; +static char* my_index(const char* str, const char chr) +{ + while (*str) + { + if (*str == chr) + { + return (char*)str; } str++; } @@ -221,17 +219,17 @@ /* If using GCC, we can safely declare strlen this way. If not using GCC, it is ok not to declare it. */ - #ifdef __GNUC__ +#ifdef __GNUC__ /* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. That was relevant to code that was here before. */ - #if !defined(__STDC__) || !__STDC__ +#if !defined(__STDC__) || !__STDC__ /* gcc with -traditional declares the built-in strlen to return int, and has done so at least since version 2.4.5. -- rms. */ extern int strlen(const char*); - #endif /* not __STDC__ */ - #endif /* __GNUC__ */ +#endif /* not __STDC__ */ +#endif /* __GNUC__ */ - #endif /* not __GNU_LIBRARY__ */ +#endif /* not __GNU_LIBRARY__ */ /* Handle permutation of arguments. */ @@ -242,44 +240,43 @@ static int first_nonopt; static int last_nonopt; - #ifdef _LIBC +#ifdef _LIBC /* Bash 2.0 gives us an environment variable containing flags indicating ARGV elements that should not be considered arguments. */ /* Defined in getopt_init.c */ -extern char *__getopt_nonoption_flags; +extern char* __getopt_nonoption_flags; static int nonoption_flags_max_len; static int nonoption_flags_len; static int original_argc; -static char*const *original_argv; +static char* const* original_argv; /* Make sure the environment variable bash 2.0 puts in the environment is valid for the getopt call we must make sure that the ARGV passed to getopt is that one passed to the process. */ -static void -__attribute__ ((unused)) store_args_and_env(int argc, char*const *argv) +static void __attribute__((unused)) store_args_and_env(int argc, char* const* argv) { /* XXX This is no good solution. We should rather copy the args so that we can compare them later. But we must not use malloc(3). */ original_argc = argc; original_argv = argv; } - # ifdef text_set_element +#ifdef text_set_element text_set_element(__libc_subinit, store_args_and_env); - # endif /* text_set_element */ +#endif /* text_set_element */ - # define SWAP_FLAGS(ch1, ch2) \ - if (nonoption_flags_len > 0) \ - { \ - char __tmp = __getopt_nonoption_flags[ch1]; \ - __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ - __getopt_nonoption_flags[ch2] = __tmp; \ - } - #else /* !_LIBC */ - # define SWAP_FLAGS(ch1, ch2) - #endif /* _LIBC */ +#define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +#else /* !_LIBC */ +#define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) @@ -290,50 +287,56 @@ `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. */ - #if defined(__STDC__) && __STDC__ +#if defined(__STDC__) && __STDC__ static void exchange(char**); - #endif +#endif -static void exchange(char **argv) +static void exchange(char** argv) { int bottom = first_nonopt; int middle = last_nonopt; int top = tools_optind; - char *tem; + char* tem; /* Exchange the shorter segment with the far end of the longer segment. That puts the shorter segment into the right place. It leaves the longer segment in the right place overall, but it consists of two parts that need to be swapped next. */ - #ifdef _LIBC +#ifdef _LIBC /* First make sure the handling of the `__getopt_nonoption_flags' string can work normally. Our top argument must be in the range of the string. */ - if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) { + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { /* We must extend the array. The user plays games with us and presents new arguments. */ - char *new_str = malloc(top + 1); - if (new_str == NULL) { + char* new_str = malloc(top + 1); + if (new_str == NULL) + { nonoption_flags_len = nonoption_flags_max_len = 0; - } else { - memset(__mempcpy(new_str, __getopt_nonoption_flags, - nonoption_flags_max_len), - '\0', top + 1 - nonoption_flags_max_len); + } + else + { + memset(__mempcpy(new_str, __getopt_nonoption_flags, nonoption_flags_max_len), '\0', + top + 1 - nonoption_flags_max_len); nonoption_flags_max_len = top + 1; __getopt_nonoption_flags = new_str; } } - #endif +#endif - while (top > middle && middle > bottom) { - if (top - middle > middle - bottom) { + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { /* Bottom segment is the short one. */ int len = middle - bottom; register int i; /* Swap it with the top part of the top segment. */ - for (i = 0; i < len; i++) { + for (i = 0; i < len; i++) + { tem = argv[bottom + i]; argv[bottom + i] = argv[top - (middle - bottom) + i]; argv[top - (middle - bottom) + i] = tem; @@ -341,13 +344,16 @@ } /* Exclude the moved bottom segment from further swapping. */ top -= len; - } else { + } + else + { /* Top segment is the short one. */ int len = top - middle; register int i; /* Swap it with the bottom part of the bottom segment. */ - for (i = 0; i < len; i++) { + for (i = 0; i < len; i++) + { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; @@ -366,17 +372,17 @@ /* Initialize the internal data when the first call is made. */ - #if defined(__STDC__) && __STDC__ -static const char* _getopt_initialize(int, char*const*, const char*); - #endif -static const char* _getopt_initialize(int argc, char* const *argv, const char *optstring) +#if defined(__STDC__) && __STDC__ +static const char* _getopt_initialize(int, char* const*, const char*); +#endif +static const char* _getopt_initialize(int argc, char* const* argv, const char* optstring) { /* Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ // avoid compiler warnings - (void) argc; - (void) argv; + (void)argc; + (void)argv; first_nonopt = last_nonopt = tools_optind; nextchar = NULL; @@ -385,46 +391,60 @@ /* Determine how to handle the ordering of options and nonoptions. */ - if (optstring[0] == '-') { + if (optstring[0] == '-') + { ordering = RETURN_IN_ORDER; ++optstring; - } else if (optstring[0] == '+') { + } + else if (optstring[0] == '+') + { ordering = REQUIRE_ORDER; ++optstring; - } else if (posixly_correct != NULL) { + } + else if (posixly_correct != NULL) + { ordering = REQUIRE_ORDER; - } else { + } + else + { ordering = PERMUTE; } - #ifdef _LIBC - if (posixly_correct == NULL - && argc == original_argc && argv == original_argv) { - if (nonoption_flags_max_len == 0) { - if (__getopt_nonoption_flags == NULL - || __getopt_nonoption_flags[0] == '\0') { +#ifdef _LIBC + if (posixly_correct == NULL && argc == original_argc && argv == original_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL || __getopt_nonoption_flags[0] == '\0') + { nonoption_flags_max_len = -1; - } else { - const char *orig_str = __getopt_nonoption_flags; + } + else + { + const char* orig_str = __getopt_nonoption_flags; int len = nonoption_flags_max_len = strlen(orig_str); - if (nonoption_flags_max_len < argc) { + if (nonoption_flags_max_len < argc) + { nonoption_flags_max_len = argc; } - __getopt_nonoption_flags = - (char*) malloc(nonoption_flags_max_len); - if (__getopt_nonoption_flags == NULL) { + __getopt_nonoption_flags = (char*)malloc(nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + { nonoption_flags_max_len = -1; - } else { - memset(__mempcpy(__getopt_nonoption_flags, orig_str, len), - '\0', nonoption_flags_max_len - len); + } + else + { + memset(__mempcpy(__getopt_nonoption_flags, orig_str, len), '\0', nonoption_flags_max_len - len); } } } nonoption_flags_len = nonoption_flags_max_len; - } else { + } + else + { nonoption_flags_len = 0; } - #endif +#endif return optstring; } @@ -485,13 +505,20 @@ If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. */ -int _getopt_internal(int argc, char* const *argv, const char *optstring, const struct option *longopts, int *longind, int long_only) +int _getopt_internal(int argc, + char* const* argv, + const char* optstring, + const struct option* longopts, + int* longind, + int long_only) { tools_optarg = NULL; - if (tools_optind == 0 || !__getopt_initialized) { - if (tools_optind == 0) { - tools_optind = 1; /* Don't scan ARGV[0], the program name. */ + if (tools_optind == 0 || !__getopt_initialized) + { + if (tools_optind == 0) + { + tools_optind = 1; /* Don't scan ARGV[0], the program name. */ } optstring = _getopt_initialize(argc, argv, optstring); __getopt_initialized = 1; @@ -501,40 +528,48 @@ Either it does not have option syntax, or there is an environment flag from the shell indicating it is not an option. The later information is only used when the used in the GNU libc. */ - #ifdef _LIBC - #define NONOPTION_P (argv[tools_optind][0] != '-' || argv[tools_optind][1] == '\0' \ - || (tools_optind < nonoption_flags_len \ - && __getopt_nonoption_flags[tools_optind] == '1')) - #else - #define NONOPTION_P (argv[tools_optind][0] != '-' || argv[tools_optind][1] == '\0') - #endif +#ifdef _LIBC +#define NONOPTION_P \ + (argv[tools_optind][0] != '-' || argv[tools_optind][1] == '\0' || \ + (tools_optind < nonoption_flags_len && __getopt_nonoption_flags[tools_optind] == '1')) +#else +#define NONOPTION_P (argv[tools_optind][0] != '-' || argv[tools_optind][1] == '\0') +#endif - if (nextchar == NULL || *nextchar == '\0') { + if (nextchar == NULL || *nextchar == '\0') + { /* Advance to the next ARGV-element. */ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been moved back by the user (who may also have changed the arguments). */ - if (last_nonopt > tools_optind) { + if (last_nonopt > tools_optind) + { last_nonopt = tools_optind; } - if (first_nonopt > tools_optind) { + if (first_nonopt > tools_optind) + { first_nonopt = tools_optind; } - if (ordering == PERMUTE) { + if (ordering == PERMUTE) + { /* If we have just processed some options following some non-options, exchange them so that the options come first. */ - if (first_nonopt != last_nonopt && last_nonopt != tools_optind) { - exchange((char**) argv); - } else if (last_nonopt != tools_optind) { + if (first_nonopt != last_nonopt && last_nonopt != tools_optind) + { + exchange((char**)argv); + } + else if (last_nonopt != tools_optind) + { first_nonopt = tools_optind; } /* Skip any additional non-options and extend the range of non-options previously skipped. */ - while (tools_optind < argc && NONOPTION_P) { + while (tools_optind < argc && NONOPTION_P) + { tools_optind++; } last_nonopt = tools_optind; @@ -545,12 +580,16 @@ then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ - if (tools_optind != argc && !strcmp(argv[tools_optind], "--")) { + if (tools_optind != argc && !strcmp(argv[tools_optind], "--")) + { tools_optind++; - if (first_nonopt != last_nonopt && last_nonopt != tools_optind) { - exchange((char**) argv); - } else if (first_nonopt == last_nonopt) { + if (first_nonopt != last_nonopt && last_nonopt != tools_optind) + { + exchange((char**)argv); + } + else if (first_nonopt == last_nonopt) + { first_nonopt = tools_optind; } last_nonopt = argc; @@ -561,10 +600,12 @@ /* If we have done all the ARGV-elements, stop the scan and back over any non-options that we skipped and permuted. */ - if (tools_optind == argc) { + if (tools_optind == argc) + { /* Set the next-arg-index to point at the non-options that we previously skipped, so the caller will digest them. */ - if (first_nonopt != last_nonopt) { + if (first_nonopt != last_nonopt) + { tools_optind = first_nonopt; } return -1; @@ -573,8 +614,10 @@ /* If we have come to a non-option and did not permute it, either stop the scan or describe it to the caller and pass it by. */ - if (NONOPTION_P) { - if (ordering == REQUIRE_ORDER) { + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + { return -1; } tools_optarg = argv[tools_optind++]; @@ -584,8 +627,7 @@ /* We have found another option-ARGV-element. Skip the initial punctuation. */ - nextchar = (argv[tools_optind] + 1 - + (longopts != NULL && argv[tools_optind][1] == '-')); + nextchar = (argv[tools_optind] + 1 + (longopts != NULL && argv[tools_optind][1] == '-')); } /* Decode the current option-ARGV-element. */ @@ -603,12 +645,12 @@ This distinction seems to be the most useful approach. */ - if (longopts != NULL - && (argv[tools_optind][1] == '-' - || (long_only && (argv[tools_optind][2] || !my_index(optstring, argv[tools_optind][1]))))) { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; + if (longopts != NULL && (argv[tools_optind][1] == '-' || + (long_only && (argv[tools_optind][2] || !my_index(optstring, argv[tools_optind][1]))))) + { + char* nameend; + const struct option* p; + const struct option* pfound = NULL; int exact = 0; int ambig = 0; int indfound = -1; @@ -620,28 +662,34 @@ /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp(p->name, nextchar, nameend - nextchar)) { - if ((unsigned int) (nameend - nextchar) - == (unsigned int) strlen(p->name)) { + if (!strncmp(p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int)(nameend - nextchar) == (unsigned int)strlen(p->name)) + { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; - } else if (pfound == NULL) { + } + else if (pfound == NULL) + { /* First nonexact match found. */ pfound = p; indfound = option_index; - } else { + } + else + { /* Second or later nonexact match found. */ ambig = 1; } } - if (ambig && !exact) { - if (tools_opterr) { - fprintf(stderr, _("%s: option `%s' is ambiguous\n"), - argv[0], argv[tools_optind]); + if (ambig && !exact) + { + if (tools_opterr) + { + fprintf(stderr, _("%s: option `%s' is ambiguous\n"), argv[0], argv[tools_optind]); } nextchar += strlen(nextchar); tools_optind++; @@ -649,26 +697,32 @@ return '?'; } - if (pfound != NULL) { + if (pfound != NULL) + { option_index = indfound; tools_optind++; - if (*nameend) { + if (*nameend) + { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ - if (pfound->has_arg) { + if (pfound->has_arg) + { tools_optarg = nameend + 1; - } else { - if (tools_opterr) { - if (argv[tools_optind - 1][1] == '-') { + } + else + { + if (tools_opterr) + { + if (argv[tools_optind - 1][1] == '-') + { /* --option */ - fprintf(stderr, - _("%s: option `--%s' doesn't allow an argument\n"), - argv[0], pfound->name); - } else { + fprintf(stderr, _("%s: option `--%s' doesn't allow an argument\n"), argv[0], pfound->name); + } + else + { /* +option or -option */ - fprintf(stderr, - _("%s: option `%c%s' doesn't allow an argument\n"), - argv[0], argv[tools_optind - 1][0], pfound->name); + fprintf(stderr, _("%s: option `%c%s' doesn't allow an argument\n"), argv[0], + argv[tools_optind - 1][0], pfound->name); } } nextchar += strlen(nextchar); @@ -676,14 +730,18 @@ tools_optopt = pfound->val; return '?'; } - } else if (pfound->has_arg == 1) { - if (tools_optind < argc) { + } + else if (pfound->has_arg == 1) + { + if (tools_optind < argc) + { tools_optarg = argv[tools_optind++]; - } else { - if (tools_opterr) { - fprintf(stderr, - _("%s: option `%s' requires an argument\n"), - argv[0], argv[tools_optind - 1]); + } + else + { + if (tools_opterr) + { + fprintf(stderr, _("%s: option `%s' requires an argument\n"), argv[0], argv[tools_optind - 1]); } nextchar += strlen(nextchar); tools_optopt = pfound->val; @@ -691,10 +749,12 @@ } } nextchar += strlen(nextchar); - if (longind != NULL) { + if (longind != NULL) + { *longind = option_index; } - if (pfound->flag) { + if (pfound->flag) + { *(pfound->flag) = pfound->val; return 0; } @@ -705,20 +765,22 @@ or the option starts with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a short option. */ - if (!long_only || argv[tools_optind][1] == '-' - || my_index(optstring, *nextchar) == NULL) { - if (tools_opterr) { - if (argv[tools_optind][1] == '-') { + if (!long_only || argv[tools_optind][1] == '-' || my_index(optstring, *nextchar) == NULL) + { + if (tools_opterr) + { + if (argv[tools_optind][1] == '-') + { /* --option */ - fprintf(stderr, _("%s: unrecognized option `--%s'\n"), - argv[0], nextchar); - } else { + fprintf(stderr, _("%s: unrecognized option `--%s'\n"), argv[0], nextchar); + } + else + { /* +option or -option */ - fprintf(stderr, _("%s: unrecognized option `%c%s'\n"), - argv[0], argv[tools_optind][0], nextchar); + fprintf(stderr, _("%s: unrecognized option `%c%s'\n"), argv[0], argv[tools_optind][0], nextchar); } } - nextchar = (char*) ""; + nextchar = (char*)""; tools_optind++; tools_optopt = 0; return '?'; @@ -729,57 +791,70 @@ { char c = *nextchar++; - char *temp = my_index(optstring, c); + char* temp = my_index(optstring, c); /* Increment `tools_optind' when we start to process its last character. */ - if (*nextchar == '\0') { + if (*nextchar == '\0') + { ++tools_optind; } - if (temp == NULL || c == ':') { - if (tools_opterr) { - if (posixly_correct) { + if (temp == NULL || c == ':') + { + if (tools_opterr) + { + if (posixly_correct) + { /* 1003.2 specifies the format of this message. */ - fprintf(stderr, _("%s: illegal option -- %c\n"), - argv[0], c); - } else { - fprintf(stderr, _("%s: invalid option -- %c\n"), - argv[0], c); + fprintf(stderr, _("%s: illegal option -- %c\n"), argv[0], c); + } + else + { + fprintf(stderr, _("%s: invalid option -- %c\n"), argv[0], c); } } tools_optopt = c; return '?'; } /* Convenience. Treat POSIX -W foo same as long option --foo */ - if (temp[0] == 'W' && temp[1] == ';') { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; + if (temp[0] == 'W' && temp[1] == ';') + { + char* nameend; + const struct option* p; + const struct option* pfound = NULL; int exact = 0; int ambig = 0; int indfound = 0; int option_index; /* This is an option that requires an argument. */ - if (*nextchar != '\0') { + if (*nextchar != '\0') + { tools_optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ tools_optind++; - } else if (tools_optind == argc) { - if (tools_opterr) { + } + else if (tools_optind == argc) + { + if (tools_opterr) + { /* 1003.2 specifies the format of this message. */ - fprintf(stderr, _("%s: option requires an argument -- %c\n"), - argv[0], c); + fprintf(stderr, _("%s: option requires an argument -- %c\n"), argv[0], c); } tools_optopt = c; - if (optstring[0] == ':') { + if (optstring[0] == ':') + { c = ':'; - } else { + } + else + { c = '?'; } return c; - } else { + } + else + { /* We already incremented `tools_optind' once; increment it again when taking next ARGV-elt as argument. */ tools_optarg = argv[tools_optind++]; @@ -794,40 +869,53 @@ /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp(p->name, nextchar, nameend - nextchar)) { - if ((unsigned int) (nameend - nextchar) == strlen(p->name)) { + if (!strncmp(p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int)(nameend - nextchar) == strlen(p->name)) + { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; - } else if (pfound == NULL) { + } + else if (pfound == NULL) + { /* First nonexact match found. */ pfound = p; indfound = option_index; - } else { + } + else + { /* Second or later nonexact match found. */ ambig = 1; } } - if (ambig && !exact) { - if (tools_opterr) { - fprintf(stderr, _("%s: option `-W %s' is ambiguous\n"), - argv[0], argv[tools_optind]); + if (ambig && !exact) + { + if (tools_opterr) + { + fprintf(stderr, _("%s: option `-W %s' is ambiguous\n"), argv[0], argv[tools_optind]); } nextchar += strlen(nextchar); tools_optind++; return '?'; } - if (pfound != NULL) { + if (pfound != NULL) + { option_index = indfound; - if (*nameend) { + if (*nameend) + { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ - if (pfound->has_arg) { + if (pfound->has_arg) + { tools_optarg = nameend + 1; - } else { - if (tools_opterr) { + } + else + { + if (tools_opterr) + { fprintf(stderr, _("\ %s: option `-W %s' doesn't allow an argument\n"), argv[0], pfound->name); @@ -836,63 +924,84 @@ nextchar += strlen(nextchar); return '?'; } - } else if (pfound->has_arg == 1) { - if (tools_optind < argc) { + } + else if (pfound->has_arg == 1) + { + if (tools_optind < argc) + { tools_optarg = argv[tools_optind++]; - } else { - if (tools_opterr) { - fprintf(stderr, - _("%s: option `%s' requires an argument\n"), - argv[0], argv[tools_optind - 1]); + } + else + { + if (tools_opterr) + { + fprintf(stderr, _("%s: option `%s' requires an argument\n"), argv[0], + argv[tools_optind - 1]); } nextchar += strlen(nextchar); return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen(nextchar); - if (longind != NULL) { + if (longind != NULL) + { *longind = option_index; } - if (pfound->flag) { + if (pfound->flag) + { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } nextchar = NULL; - return 'W'; /* Let the application handle it. */ + return 'W'; /* Let the application handle it. */ } - if (temp[1] == ':') { - if (temp[2] == ':') { + if (temp[1] == ':') + { + if (temp[2] == ':') + { /* This is an option that accepts an argument optionally. */ - if (*nextchar != '\0') { + if (*nextchar != '\0') + { tools_optarg = nextchar; tools_optind++; - } else { + } + else + { tools_optarg = NULL; } nextchar = NULL; - } else { + } + else + { /* This is an option that requires an argument. */ - if (*nextchar != '\0') { + if (*nextchar != '\0') + { tools_optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ tools_optind++; - } else if (tools_optind == argc) { - if (tools_opterr) { + } + else if (tools_optind == argc) + { + if (tools_opterr) + { /* 1003.2 specifies the format of this message. */ - fprintf(stderr, - _("%s: option requires an argument -- %c\n"), - argv[0], c); + fprintf(stderr, _("%s: option requires an argument -- %c\n"), argv[0], c); } tools_optopt = c; - if (optstring[0] == ':') { + if (optstring[0] == ':') + { c = ':'; - } else { + } + else + { c = '?'; } - } else { + } + else + { /* We already incremented `tools_optind' once; increment it again when taking next ARGV-elt as argument. */ tools_optarg = argv[tools_optind++]; @@ -904,92 +1013,90 @@ } } -int tools_getopt(int argc, char* const *argv, const char *optstring) +int tools_getopt(int argc, char* const* argv, const char* optstring) { - return _getopt_internal(argc, argv, optstring, - (const struct option*) 0, - (int*) 0, - 0); + return _getopt_internal(argc, argv, optstring, (const struct option*)0, (int*)0, 0); } -int tools_getopt_long(int argc, char* const *argv, const char *optstring, const struct option *longopts, int *longindex) +int tools_getopt_long(int argc, char* const* argv, const char* optstring, const struct option* longopts, int* longindex) { - return _getopt_internal(argc, argv, optstring, - longopts, - longindex, - 0); + return _getopt_internal(argc, argv, optstring, longopts, longindex, 0); } - -int tools_getopt_long_only(int argc, char* const *argv, const char *optstring, const struct option *longopts, int *longindex) +int tools_getopt_long_only(int argc, + char* const* argv, + const char* optstring, + const struct option* longopts, + int* longindex) { - return _getopt_internal(argc, argv, optstring, - longopts, - longindex, - 1); + return _getopt_internal(argc, argv, optstring, longopts, longindex, 1); } - - #ifdef TEST +#ifdef TEST /* Compile with -DTEST to make an executable for use in testing the above definition of `getopt'. */ -int main(int argc, char **argv) +int main(int argc, char** argv) { int c; int digit_optind = 0; - while (1) { + while (1) + { int this_option_optind = tools_optind ? tools_optind : 1; c = getopt(argc, argv, "abc:d:0123456789"); - if (c == -1) { + if (c == -1) + { break; } switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (digit_optind != 0 && digit_optind != this_option_optind) { - printf("digits occur in two different argv-elements.\n"); - } - digit_optind = this_option_optind; - printf("option %c\n", c); - break; - - case 'a': - printf("option a\n"); - break; - - case 'b': - printf("option b\n"); - break; - - case 'c': - printf("option c with value `%s'\n", tools_optarg); - break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + { + printf("digits occur in two different argv-elements.\n"); + } + digit_optind = this_option_optind; + printf("option %c\n", c); + break; + + case 'a': + printf("option a\n"); + break; + + case 'b': + printf("option b\n"); + break; + + case 'c': + printf("option c with value `%s'\n", tools_optarg); + break; - case '?': - break; + case '?': + break; - default: - printf("?? getopt returned character code 0%o ??\n", c); + default: + printf("?? getopt returned character code 0%o ??\n", c); } } - if (tools_optind < argc) { + if (tools_optind < argc) + { printf("non-option ARGV-elements: "); - while (tools_optind < argc) { + while (tools_optind < argc) + { printf("%s ", argv[tools_optind++]); } printf("\n"); @@ -998,7 +1105,7 @@ exit(0); } - #endif /* TEST */ +#endif /* TEST */ int hello() { diff -Nru mstflint-4.17.0+1/cmdparser/my_getopt.h mstflint-4.21.0+1/cmdparser/my_getopt.h --- mstflint-4.17.0+1/cmdparser/my_getopt.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/cmdparser/my_getopt.h 2022-07-31 09:33:57.000000000 +0000 @@ -17,106 +17,99 @@ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - #ifndef _TOOLS_GETOPT_H - #define _TOOLS_GETOPT_H +#ifndef _TOOLS_GETOPT_H +#define _TOOLS_GETOPT_H - #ifdef __cplusplus -extern "C" { - #endif +#ifdef __cplusplus +extern "C" +{ +#endif -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ + /* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ -extern char *tools_optarg; + extern char* tools_optarg; -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. + /* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. - On entry to `getopt', zero means this is the first call; initialize. + On entry to `getopt', zero means this is the first call; initialize. - When `getopt' returns -1, this is the index of the first of the - non-option elements that the caller should itself scan. + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ -extern int tools_optind; + extern int tools_optind; -/* Callers store zero here to inhibit the error message `getopt' prints - for unrecognized options. */ + /* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ -extern int tools_opterr; + extern int tools_opterr; -/* Set to an option character which was unrecognized. */ + /* Set to an option character which was unrecognized. */ -extern int tools_optopt; + extern int tools_optopt; -/* Describe the long-named options requested by the application. - The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector - of `struct option' terminated by an element containing a name which is - zero. + /* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. - The field `has_arg' is: - no_argument (or 0) if the option does not take an argument, - required_argument (or 1) if the option requires an argument, - optional_argument (or 2) if the option takes an optional argument. + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. - If the field `flag' is not NULL, it points to a variable that is set - to the value given in the field `val' when the option is found, but - left unchanged if the option is not found. + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. - To have a long-named option do something other than set an `int' to - a compiled-in constant, such as set a value from `optarg', set the - option's `flag' field to zero and its `val' field to a nonzero - value (the equivalent single-letter option character, if there is - one). For long options that have a zero `flag' field, `getopt' - returns the contents of the `val' field. */ + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ -struct option -{ - #if defined(__STDC__) && __STDC__ - const char *name; - #else - char *name; - #endif - /* has_arg can't be an enum because some compilers complain about - type mismatches in all the code that assumes it is an int. */ - int has_arg; - int *flag; - int val; -}; - -/* Names for the values of the `has_arg' field of `struct option'. */ - - #define tools_no_argument 0 - #define tools_required_argument 1 - #define tools_optional_argument 2 - -int tools_getopt( - int argc, - char*const *argv, - const char *shortopts); - -int tools_getopt_long( - int argc, - char*const *argv, - const char *shortopts, - const struct option *longopts, - int *longind); - -int tools_getopt_long_only( - int argc, - char*const *argv, - const char *shortopts, - const struct option *longopts, - int *longind); + struct option + { +#if defined(__STDC__) && __STDC__ + const char* name; +#else + char* name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int* flag; + int val; + }; + + /* Names for the values of the `has_arg' field of `struct option'. */ + +#define tools_no_argument 0 +#define tools_required_argument 1 +#define tools_optional_argument 2 + + int tools_getopt(int argc, char* const* argv, const char* shortopts); + + int + tools_getopt_long(int argc, char* const* argv, const char* shortopts, const struct option* longopts, int* longind); + + int tools_getopt_long_only(int argc, + char* const* argv, + const char* shortopts, + const struct option* longopts, + int* longind); - #ifdef __cplusplus +#ifdef __cplusplus } - #endif +#endif - #endif /* _TOOLS_GETOPT_H */ +#endif /* _TOOLS_GETOPT_H */ diff -Nru mstflint-4.17.0+1/common/autocomplete/Makefile.am mstflint-4.21.0+1/common/autocomplete/Makefile.am --- mstflint-4.17.0+1/common/autocomplete/Makefile.am 2021-06-29 08:49:47.000000000 +0000 +++ mstflint-4.21.0+1/common/autocomplete/Makefile.am 2022-04-28 11:49:49.000000000 +0000 @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2021 Mellanox Technologies LTD. All rights reserved. +# Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU diff -Nru mstflint-4.17.0+1/common/autocomplete/mft_autocomplete_skeleton mstflint-4.21.0+1/common/autocomplete/mft_autocomplete_skeleton --- mstflint-4.17.0+1/common/autocomplete/mft_autocomplete_skeleton 2021-06-29 08:49:47.000000000 +0000 +++ mstflint-4.21.0+1/common/autocomplete/mft_autocomplete_skeleton 2022-04-28 11:50:03.000000000 +0000 @@ -1,4 +1,5 @@ -# Copyright (c) Jun 2021 Mellanox Technologies LTD. All rights reserved. +#-- +# Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU @@ -27,6 +28,7 @@ # 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. +#-- . /etc/bash_completion.d/mft/mft_base_autocomplete diff -Nru mstflint-4.17.0+1/common/autocomplete/mft_base_autocomplete mstflint-4.21.0+1/common/autocomplete/mft_base_autocomplete --- mstflint-4.17.0+1/common/autocomplete/mft_base_autocomplete 2021-06-29 08:49:47.000000000 +0000 +++ mstflint-4.21.0+1/common/autocomplete/mft_base_autocomplete 2022-04-28 11:50:03.000000000 +0000 @@ -1,4 +1,5 @@ -# Copyright (c) Jun 2021 Mellanox Technologies LTD. All rights reserved. +#-- +# Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU @@ -27,6 +28,7 @@ # 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. +#-- PYTHON_EXEC=`find /usr/bin /bin/ /usr/local/bin -iname 'python*' 2>&1 | grep -e='*python[0-9,.]*' | sort -d | head -n 1` which python3 >/dev/null 2>&1 @@ -194,19 +196,33 @@ } _getLastTypeFourValues () { - currentVal="${COMP_WORDS[COMP_CWORD]}" - str=$1 - curLength=${#str} - currValLen=${#currentVal} - curLength=$(( currValLen - curLength )) - - echo "${currentVal: 0 : $curLength}" + if [[ $cur == "" ]]; then + echo ""; + else + currentVal="${COMP_WORDS[COMP_CWORD]}" + str=$1 + curLength=${#str} + currValLen=${#currentVal} + curLength=$(( currValLen - curLength )) + echo "${currentVal: 0 : $curLength}"; + fi; } _deleteSpeedsFromCur () { - local cur=$1 + local cur="$1" speedsList=( $(compgen -W "$2" ) ) - for speed in ${speedsList[@]}; do cur=$( echo $cur | sed "s/$speed//g" ); done + found=0 + subOfOtherSpeed=0 + for speed in ${speedsList[@]}; do + if [[ $speed == $cur ]]; then + found=1; + elif [[ $speed == *"$cur"* ]]; then + subOfOtherSpeed=1 + fi; + done; + + if [[ $found == 1 ]] && [[ $subOfOtherSpeed == 0 ]]; then cur=""; fi; + if [[ $found == 0 ]] && [[ $subOfOtherSpeed == 0 ]]; then for speed in ${speedsList[@]}; do cur=$( echo $cur | sed "s/$speed//g" ); done; fi; echo $cur } @@ -232,7 +248,6 @@ cur="$3"; sonsList="$( _getSonsFromHash $prev )"; if [[ $( _getLastCharOfString $cur ) == "," ]] ; then cur=""; else cur="$( _getLastStringElemnentOnDelimeter "$cur" "," )"; fi; - if [[ $( _isWordslistContainsWord "$sonsList" "$cur" ) -eq 1 ]]; then cur=""; fi; cur=$( _deleteSpeedsFromCur "$cur" "$sonsList" ); echo "$cur" @@ -249,10 +264,15 @@ _isLastValPartOfSons () { speedsList=( $(compgen -W "$1" ) ) - lastVal=$2 - local ret=0 - for speed in ${speedsList[@]}; do if [[ $speed == *"$lastVal"* ]]; then ret=1; fi ; done + isUpperCaseNeed=$( _getUpperNeedFromHash $3 ) + if [[ $isUpperCaseNeed == 1 ]]; then + lastVal=$(echo $2 | awk '{print toupper($0)}') # $2 + else lastVal=$2 + + fi; + local ret=0 + for speed in ${speedsList[@]}; do if [[ $speed == "$lastVal"* ]]; then ret=1; fi ; done echo $ret } @@ -274,7 +294,7 @@ local lastVal="$3" local speedShown="" local extraSons="$( _getExtraFromHash $prev )" - if [[ $( _isLastValPartOfSons "$sonsList" "$lastVal" ) -eq 0 ]]; then + if [[ $( _isLastValPartOfSons "$sonsList" "$lastVal" "$prev" ) -eq 0 ]]; then sonsList="StringVal" elif [[ $( _isWordslistContainsWord "$sonsList" "$( _getLastStringElemnentOnDelimeter "$cur" "," )" ) -eq 1 ]] && [[ $( _getLastCharOfString $cur ) != "," ]]; then if [[ $extraSons == "" ]]; then @@ -284,7 +304,12 @@ sonsList=","; fi; else - sonsList=$( _addDelimiterToStringList "$sonsList" "," );sonsList="$sonsList ""$extraSons"; + tempCur=$( _getCurrent $prev 4 ); + if [[ $tempCur == "" ]]; then + sonsList=$( _addDelimiterToStringList "$sonsList" "," );sonsList="$sonsList ""$extraSons"; + else + sonsList="$sonsList ""$extraSons"; + fi; fi elif [[ $( _isWordslistContainsWord "${COMP_WORDS[@]}" "," ) -eq 1 ]]; then sonsList="$sonsList ""$extraSons"; fi; @@ -299,7 +324,11 @@ local cmdLine="$1" cmdLines=( $(compgen -W "$cmdLine" ) ) - for line in ${cmdLines[@]}; do shortCutsVal="$( _getSonFromShortCutsFromHash "$line" )"; if [[ $shortCutsVal != "" ]]; then cmdLine=$( echo $cmdLine | sed "s/$line/$shortCutsVal/g" );fi; done + for line in ${cmdLines[@]}; do + if [[ $( _isWordslistContainsWord "$( _getShortCutsListFromHash )" "$line" ) -eq 1 ]]; then + shortCutsVal="$( _getSonFromShortCutsFromHash "$line" )"; if [[ $shortCutsVal != "" ]]; then cmdLine=$( echo $cmdLine | sed "s/$line/$shortCutsVal/g" );fi; + fi; + done echo "$cmdLine" } @@ -368,12 +397,16 @@ } _getFourTypeOneValComplite () { - + compreplyVal=$1 if [[ $( _isWordslistContainsWord "$( _getSonsFromHash $prev )" "$compreplyVal" ) ]]; then - compreplyVal="$( _getLastTypeFourValues $cur)""$compreplyVal"; - else + prefix="$( _getLastTypeFourValues $cur)" + compreplyVal=$( echo "$compreplyVal" | sed "s/[^ ]* */$prefix&/g" ) + else compreplyVal=" $compreplyVal"; fi; + if [[ $(_isWordslistContainsWord "$( _getSonsFromHash $prev )" $cur ) == 1 ]]; then + compreplyVal="$compreplyVal "$cur", "$extraSons; + fi; echo $compreplyVal } @@ -398,14 +431,13 @@ extraSonsList=( $extraSons ) extraSonsLen=${#extraSonsList[@]} #$( _getLenOfListFromString "$( _getExtraFromHash $prev )") local lastCommandIndex=$( _getLastCommandIndexFromHash $prev ) - if [[ isUpperCaseNeed -eq 1 ]] && [[ $( _isLastCommand $index $(( lastCommandIndex + 1 )) ) -eq 1 ]]; then compreplyVal="$(compgen -W "$sonsList" -- $(echo ${cur} | awk '{print toupper($0)}') )"; else compreplyVal="$(compgen -W "$sonsList" -- ${cur} )"; fi compreplyValList=( $compreplyVal ) - if [[ nodeType -eq 4 ]] && [[ ${#compreplyValList[@]} -eq 1 ]]; then compreplyVal="$( _getFourTypeOneValComplite $compreplyVal)"; fi; + if [[ nodeType -eq 4 ]] && [[ $( _getLastCharOfString $cur ) != "," ]]; then compreplyVal="$( _getFourTypeOneValComplite "$compreplyVal" "$extraSons" "$sonsList")"; fi; if [[ $sonsList != "StringVal" ]]; then if [[ $sonsList != "StringValSpace" ]]; then COMPREPLY=( $compreplyVal ); @@ -437,7 +469,7 @@ if [[ ${cur} == \/* || ${cur} == \.\/* ]] ; then COMPREPLY=() _filedir - return 0 + return 0 fi; local prev="" local sonsList="" @@ -445,7 +477,7 @@ local index=1 while [[ $sonsList == "" ]]; do if [[ index -gt COMP_CWORD ]]; then return 0; fi; - prev="${COMP_WORDS[COMP_CWORD-$index]}" + prev="${COMP_WORDS[COMP_CWORD-$index]}" prev="$( _getFullNodeIfShortcut $prev )" nodeType=$( _getNodeTypeIfExist $prev ); cur=$(_getCurrent $prev $nodeType) diff -Nru mstflint-4.17.0+1/common/autocomplete/mft_help_to_completion.py mstflint-4.21.0+1/common/autocomplete/mft_help_to_completion.py --- mstflint-4.17.0+1/common/autocomplete/mft_help_to_completion.py 2021-06-29 08:49:47.000000000 +0000 +++ mstflint-4.21.0+1/common/autocomplete/mft_help_to_completion.py 2022-07-31 09:33:57.000000000 +0000 @@ -1,4 +1,5 @@ -# Copyright (c) Jun 2021 Mellanox Technologies LTD. All rights reserved. +# -- +# Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU @@ -27,49 +28,55 @@ # 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. +# -- #!/usr/bin/python ### imports ### import os +import sys import subprocess import argparse -def buildTree( linesList, tabIndex, nodeSons ): + +def buildTree(linesList, tabIndex, nodeSons): if len(linesList) == 0: - return ("" , nodeSons) + return ("", nodeSons) nodeStr = linesList[0] if not isNodeStr(nodeStr): - return buildTree( linesList[1:], tabIndex, nodeSons ) + return buildTree(linesList[1:], tabIndex, nodeSons) nodeTab = getNodeTab(nodeStr) if nodeTab == tabIndex: return (nodeSons, linesList) if (tabIndex - nodeTab) == 1: - nodeName=getNodeName(nodeStr) - remineLines, nodeSons = buildTree(linesList[1:], nodeTab, [] ) - nodeStruct={ 'nodeName' : nodeName , 'nodeSons' : nodeSons } - return buildTree( linesList[1:], nodeTab, nodeSons.add(nodeStruct) ) + nodeName = getNodeName(nodeStr) + remineLines, nodeSons = buildTree(linesList[1:], nodeTab, []) + nodeStruct = {'nodeName': nodeName, 'nodeSons': nodeSons} + return buildTree(linesList[1:], nodeTab, nodeSons.add(nodeStruct)) + def isNewNode(line, queryCommand): if queryCommand: if ":" in line: - index = line.find(":") - charBF = line[index-1] + index = line.find(":") + charBF = line[index - 1] return (charBF == "" or charBF == " ") removeLeadingZero = line.strip(" ") if removeLeadingZero and removeLeadingZero[0] == "-": return True return False + def isInfoFlag(line): if line[0] != " " and line[0] != "-": return True return False + def keepOnlyNodes(filesList): - helpLineOnlyNodes=[] + helpLineOnlyNodes = [] onNodedescription = False queryCommand = False - node="" + node = "" for line in filesList: if line.strip(" ") == "" or line == '\n': continue @@ -84,133 +91,143 @@ onNodedescription = False continue if isNewNode(line, queryCommand): - if node!= "": + if node != "": helpLineOnlyNodes.append(node) onNodedescription = True - node=line + node = line if ":" not in line: - node=node+" :" + node = node + " :" continue if onNodedescription: - node=node+" " + line.strip(" ") + node = node + " " + line.strip(" ") helpLineOnlyNodes.append(node) return helpLineOnlyNodes -#-l |--loopback : Configure Loopback Mode [NO(no loopback)/PH(phy loopback)/EX(external loopback)] +# -l |--loopback : Configure Loopback Mode [NO(no loopback)/PH(phy loopback)/EX(external loopback)] def isShortCutsLine(line): if "|" in line: - lineSplit=line.split("|")[0].strip(" ") - lineSpaceSplit=lineSplit.split(" ") - lineSpaceWords = list(filter( lambda x: x != "" ,lineSpaceSplit)) + lineSplit = line.split("|")[0].strip(" ") + lineSpaceSplit = lineSplit.split(" ") + lineSpaceWords = list(filter(lambda x: x != "", lineSpaceSplit)) return (len(lineSpaceWords) <= 1) return False + def getNodeShortCutsAndName(line): - shortCuts="" - name="" + shortCuts = "" + name = "" line = line.strip(" ") if isShortCutsLine(line): lineSplit = line.split("|") - rightFlag=lineSplit[0].strip(" ") - leftFlag=lineSplit[1].strip(" ").split(" ")[0] + rightFlag = lineSplit[0].strip(" ") + leftFlag = lineSplit[1].strip(" ").split(" ")[0] if len(rightFlag) > len(leftFlag): return leftFlag, rightFlag else: return rightFlag, leftFlag - name=line.split(" ")[0] + name = line.split(" ")[0] return shortCuts, name + def getSonsAndUpperNeededFromVals(sonsValsSplit): - upperNeed="1" - sons="" - lastVal="" + upperNeed = "1" + sons = "" + lastVal = "" if "..." in sonsValsSplit: threePointsIndex = sonsValsSplit.index("...") - if threePointsIndex < len(sonsValsSplit)-1: - numBefore = int(sonsValsSplit[threePointsIndex-1]) + 1 - numAfter = int(sonsValsSplit[threePointsIndex+1]) - arr=range(numBefore,numAfter) - sonsValsSplit[threePointsIndex] = " ".join(map(lambda x : str(x), arr)) + if threePointsIndex < len(sonsValsSplit) - 1: + numBefore = int(sonsValsSplit[threePointsIndex - 1]) + 1 + numAfter = int(sonsValsSplit[threePointsIndex + 1]) + arr = range(numBefore, numAfter) + sonsValsSplit[threePointsIndex] = " ".join(map(lambda x: str(x), arr)) for val in sonsValsSplit: sonVal = val.split('(')[0] - sons=sons+ " " + sonVal + sons = sons + " " + sonVal if not sonVal.isupper(): - upperNeed="" + upperNeed = "" return sons, upperNeed + def getSonsValsFromLine(line): - ret="" + ret = "" lineSplit = line.split("[")[1:] for sons in lineSplit: - ret=ret + sons.split("]")[0] + ret = ret + sons.split("]")[0] return ret + def getNodeTypeSonsUpperNeedAndLastCommandIndex(line): - lastCommandIndex="-1" - sons="" - nodeType="0" - upperNeed="" + lastCommandIndex = "-1" + sons = "" + nodeType = "0" + upperNeed = "" if "[" in line: - lastCommandIndex="1" - sonsVals=getSonsValsFromLine(line) - sonsValsSplit=[] + lastCommandIndex = "1" + sonsVals = getSonsValsFromLine(line) + sonsValsSplit = [] if "/" in sonsVals: - nodeType="3" - sonsValsSplit=sonsVals.split("/") + nodeType = "3" + sonsValsSplit = sonsVals.split("/") elif "," in sonsVals: - nodeType="4" - sonsValsSplit=sonsVals.split(",") + nodeType = "4" + sonsValsSplit = sonsVals.split(",") else: - nodeType="3" - sonsValsSplit=[sonsVals] + nodeType = "3" + sonsValsSplit = [sonsVals] sons, upperNeed = getSonsAndUpperNeededFromVals(sonsValsSplit) return nodeType, sons, upperNeed, lastCommandIndex + def parseLine(line): - node={ 'shortCut': "" , 'name' : "" , 'lastCommandIndex' : "" , 'nodeType' : "" , 'sons' : "" , 'extra' : "" , 'upperNeed' : "" , 'description' : "" } + node = {'shortCut': "", 'name': "", 'lastCommandIndex': "", 'nodeType': "", 'sons': "", 'extra': "", 'upperNeed': "", 'description': ""} lineSplitByColoumn = line.split(":") shortCut, name = getNodeShortCutsAndName(lineSplitByColoumn[0]) - node["shortCut"]=" ".join(shortCut.split()) - node["name"] =" ".join(name.split()) - node["description"]=lineSplitByColoumn[1] - nodeType,sons,upperNeed,lastCommandIndex = getNodeTypeSonsUpperNeedAndLastCommandIndex(line) - node["nodeType"]=nodeType - node["sons"]=sons - node["upperNeed"]=upperNeed - node["lastCommandIndex"]=lastCommandIndex + node["shortCut"] = " ".join(shortCut.split()) + node["name"] = " ".join(name.split()) + node["description"] = lineSplitByColoumn[1] + nodeType, sons, upperNeed, lastCommandIndex = getNodeTypeSonsUpperNeedAndLastCommandIndex(line) + node["nodeType"] = nodeType + node["sons"] = sons + node["upperNeed"] = upperNeed + node["lastCommandIndex"] = lastCommandIndex return node + def getNumOfLeadingZeroFromLine(line): - count=0 + count = 0 for char in line: if char == " ": - count+=1 + count += 1 elif char == "\t": - count+=4 + count += 4 else: return count return count + def getTabLevelFromLine(line): numOfLeadingZeros = getNumOfLeadingZeroFromLine(line) return int(numOfLeadingZeros / 4) + def getUserInput(): my_parser = argparse.ArgumentParser() - my_parser.add_argument('-n','--name', action='store', type=str , help="Tool Name", required=True) - my_parser.add_argument('-o','--output', action='store', type=int, default=1, choices=[1], help="Output: 1- build structs for Auto-Compeletion(Default)") - my_parser.add_argument('-c','--command', action='store', type=str, default="", help="Full Command Line: ") + my_parser.add_argument('-n', '--name', action='store', type=str, help="Tool Name", required=True) + my_parser.add_argument('-o', '--output', action='store', type=int, default=1, choices=[1], help="Output: 1- build structs for Auto-Compeletion(Default)") + my_parser.add_argument('-c', '--command', action='store', type=str, default="", help="Full Command Line: ") args = my_parser.parse_args() return vars(args) + def isCommandNode(node): return node["name"][0] != "-" + def isNeedUpdateNode(prevNode, prevIndex, currNode, CurrIndex): - if prevNode == None: + if prevNode is None: return True if isCommandNode(prevNode): if isCommandNode(currNode): @@ -220,143 +237,157 @@ else: return CurrIndex > prevIndex + def buildMSTNodesTree(commandLine): - nodesTree=[] + nodesTree = [] filesList = subprocess.check_output("sudo mst help", shell=True).decode().split("\n") - options="" - params="" - sons="" - extra="" - lastNodeStruct={ 'shortCut': "" , 'name' : "" , 'lastCommandIndex' : "-1" , 'nodeType' : "0" , 'sons' : "" , 'extra' : "" , 'upperNeed' : "" , 'description' : ""} - mstMainNodeSons="" + options = "" + params = "" + sons = "" + extra = "" + lastNodeStruct = {'shortCut': "", 'name': "", 'lastCommandIndex': "1", 'nodeType': "0", 'sons': "", 'extra': "", 'upperNeed': "", 'description': ""} + mstMainNodeSons = "" for line in reversed(filesList): lineRemovingLeadingZero = line.strip(" ") if len(lineRemovingLeadingZero) == 0: continue if lineRemovingLeadingZero[0:3] == "mst": splitLine = lineRemovingLeadingZero[4:].split(" ") - nodeName=splitLine.pop(0) - if nodeName not in mstMainNodeSons: - mstMainNodeSons = mstMainNodeSons + " " + nodeName + nodeName = splitLine.pop(0) + if nodeName not in mstMainNodeSons.split(" "): + mstMainNodeSons = mstMainNodeSons + " " + nodeName if nodeName not in commandLine.split(" "): continue if lastNodeStruct["name"] != nodeName: if lastNodeStruct["name"] != "" and lastNodeStruct["sons"].strip(" ") != "": nodesTree.append(lastNodeStruct) - lastNodeStruct={ 'shortCut': "" , 'name' : nodeName , 'lastCommandIndex' : "-1" , 'nodeType' : "0" , 'sons' : "" , 'extra' : "" , 'upperNeed' : "" , 'description' : ""} + lastNodeStruct = {'shortCut': "", 'name': nodeName, 'lastCommandIndex': "1", 'nodeType': "0", 'sons': "", 'extra': "", 'upperNeed': "", 'description': ""} for son in reversed(splitLine): if son[0] == "<": continue if son[0] == "[": - sonName=son.replace("[", "").replace("]","") + sonName = son.replace("[", "").replace("]", "") if sonName == "OPTIONS": - extra=extra+options+sons - sons="" + extra = extra + options + sons + sons = "" elif sonName == "params": - extra=params + extra = params else: - sons=sons+" "+sonName + sons = sons + " " + sonName else: + if sons == "": + if extra != "": + sons = extra + extra = "" if sons != "": - nodesTree.append({ 'shortCut': "" , 'name' : son , 'lastCommandIndex' : "-1" , 'nodeType' : "0" , 'sons' : sons , 'extra' : extra , 'upperNeed' : "" , 'description' : ""}) - sons=son - params="" - options="" - lastNodeStruct["sons"]= lastNodeStruct["sons"] + " " + sons - sons="" - params="" - options="" + nodesTree.append({'shortCut': "", 'name': son, 'lastCommandIndex': "1", 'nodeType': "0", 'sons': sons, 'extra': extra, 'upperNeed': "", 'description': ""}) + sons = son + params = "" + options = "" + lastNodeStruct["sons"] = lastNodeStruct["sons"] + " " + sons + sons = "" + params = "" + options = "" elif "params:" in line: paramsSplit = lineRemovingLeadingZero.split(" ") for param in paramsSplit: if param[0] == "[": - paramMame = param.replace("[", "").replace("]","") - params=params + " " + paramMame + paramMame = param.replace("[", "").replace("]", "") + params = params + " " + paramMame elif lineRemovingLeadingZero[0] == "-": - options=options + " " + lineRemovingLeadingZero.split(" ")[0].replace(":", "") + options = options + " " + lineRemovingLeadingZero.split(" ")[0].replace(":", "") nodesTree.append(lastNodeStruct) - nodesTree.append({ 'shortCut': "" , 'name' : "mst" , 'lastCommandIndex' : "-1" , 'nodeType' : "0" , 'sons' : mstMainNodeSons , 'extra' : "" , 'upperNeed' : "" , 'description' : ""}) + nodesTree.append({'shortCut': "", 'name': "mst", 'lastCommandIndex': "1", 'nodeType': "0", 'sons': mstMainNodeSons, 'extra': "", 'upperNeed': "", 'description': ""}) return nodesTree -def buildMFTTollNodesTree(toolName,commandLine): - nodesTree=[] - nodesTabs=["","","","","","","","","","","","","","","","","","",""] - filesList = subprocess.check_output(toolName + " -h", shell=True).decode().split("\n") + +def buildMFTTollNodesTree(toolName, commandLine): + nodesTree = [] + nodesTabs = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""] + try: + filesList = subprocess.check_output(toolName + " -h", shell=True).decode().split("\n") + # some tools supply --help only when executed by root. at this point auto-complete can not support them + except subprocess.SubprocessError: + sys.exit(0) + helpLineOnlyNodes = keepOnlyNodes(filesList) - tabLevel=0 + tabLevel = 0 tabLevelBestIndex = [0] * 8 - tabLevelBestNode = [None] * 8 + tabLevelBestNode = [None] * 8 for line in reversed(helpLineOnlyNodes): parsedLine = parseLine(line) tabLevel = getTabLevelFromLine(line) nodesTabs[tabLevel] = nodesTabs[tabLevel] + " " + parsedLine["name"] - sons = nodesTabs[tabLevel+1] - nodesTabs[tabLevel+1]="" + sons = nodesTabs[tabLevel + 1] + nodesTabs[tabLevel + 1] = "" if parsedLine["sons"] == "": - parsedLine["sons"]=sons + parsedLine["sons"] = sons else: - parsedLine["extra"]=sons + parsedLine["extra"] = sons flagIndex = -1 splitCommand = commandLine.split(" ") if parsedLine["name"] in splitCommand: flagIndex = splitCommand.index(parsedLine["name"]) elif parsedLine["shortCut"] in splitCommand and parsedLine["shortCut"] != "": - flagIndex = splitCommand.index(parsedLine["shortCut"]) + flagIndex = splitCommand.index(parsedLine["shortCut"]) if flagIndex > -1: if isNeedUpdateNode(tabLevelBestNode[tabLevel], tabLevelBestIndex[tabLevel], parsedLine, flagIndex): tabLevelBestNode[tabLevel] = parsedLine - tabLevelBestIndex[tabLevel]= flagIndex - for i in range(tabLevel+1, 8): + tabLevelBestIndex[tabLevel] = flagIndex + for i in range(tabLevel + 1, 8): if tabLevelBestIndex[i] < flagIndex: tabLevelBestNode[i] = None for bestTab in tabLevelBestNode: - if bestTab != None: + if bestTab is not None: nodesTree.append(bestTab) - nodesTree.append({ 'shortCut': "" , 'name' : toolName , 'lastCommandIndex' : "-1" , 'nodeType' : "0" , 'sons' : " ".join(nodesTabs) , 'extra' : "" , 'upperNeed' : "" , 'description' : ""}) + nodesTree.append({'shortCut': "", 'name': toolName, 'lastCommandIndex': "-1", 'nodeType': "0", 'sons': " ".join(nodesTabs), 'extra': "", 'upperNeed': "", 'description': ""}) return nodesTree -def buildNodesTree(toolName,commandLine): + +def buildNodesTree(toolName, commandLine): if toolName == "mst": return buildMSTNodesTree(commandLine) else: - return buildMFTTollNodesTree(toolName,commandLine) + return buildMFTTollNodesTree(toolName, commandLine) def buildShortCutsNodeListNodesDeclary(nodesTree): - shortCuts="" - nodesList="" + shortCuts = "" + nodesList = "" nodesDeclareArray = [] for node in nodesTree: - shortCutVal = node["shortCut"] - nodeNAme = node["name"] - nodeSons = node["sons"] + shortCutVal = node["shortCut"] + nodeNAme = node["name"] + nodeSons = node["sons"] + extra = node["extra"] if nodeNAme == "--device" or nodeNAme == "-d" or nodeNAme == "-dev" or shortCutVal == "-d": - nodeSons="temp" - node["lastCommandIndex"]="1" - node["nodeType"]="2" + nodeSons = "temp" + node["lastCommandIndex"] = "1" + node["nodeType"] = "2" if shortCutVal != "": - shortCuts=shortCuts + " [\"" + shortCutVal + "\"]=\"" + nodeNAme + "\"" - if nodeSons != "": - nodesList=nodesList + " " + nodeNAme - nodeDeclare="declare -A "+ nodeNAme.replace("-", "")+"=( [\"lastCommandIndex\"]="+node["lastCommandIndex"]+" [\"nodeType\"]=\""+node["nodeType"]+"\" [\"sons\"]=\""+nodeSons+"\" [\"extra\"]=\""+node["extra"]+"\" [\"upperNeed\"]="+node["upperNeed"]+" )\n" + shortCuts = shortCuts + " [\"" + shortCutVal + "\"]=\"" + nodeNAme + "\"" + if nodeSons != "" or extra != "": + nodesList = nodesList + " " + nodeNAme + nodeDeclare = "declare -A " + nodeNAme.replace("-", "") + "=( [\"lastCommandIndex\"]=" + node["lastCommandIndex"] + " [\"nodeType\"]=\"" + node["nodeType"] + "\" [\"sons\"]=\"" + nodeSons + "\" [\"extra\"]=\"" + node["extra"] + "\" [\"upperNeed\"]=" + node["upperNeed"] + " )\n" nodesDeclareArray.append(nodeDeclare) - return shortCuts,nodesList,nodesDeclareArray + return shortCuts, nodesList, nodesDeclareArray + def printNodesStructures(nodesTree): - shortCuts,nodesList,nodesDeclareArray = buildShortCutsNodeListNodesDeclary(nodesTree) + shortCuts, nodesList, nodesDeclareArray = buildShortCutsNodeListNodesDeclary(nodesTree) - print ("declare -A shortcuts=( " + shortCuts + " )\n") - print ("nodes=\""+nodesList+"\"\n") + print("declare -A shortcuts=( " + shortCuts + " )\n") + print("nodes=\"" + nodesList + "\"\n") for declareLine in nodesDeclareArray: - print (declareLine) + print(declareLine) if __name__ == "__main__": userInput = getUserInput() - toolName=userInput["name"] - commandLine=userInput["command"] + toolName = userInput["name"] + commandLine = userInput["command"] - nodesTree = buildNodesTree(toolName,commandLine) + nodesTree = buildNodesTree(toolName, commandLine) printNodesStructures(nodesTree) diff -Nru mstflint-4.17.0+1/common/bit_slice.h mstflint-4.21.0+1/common/bit_slice.h --- mstflint-4.17.0+1/common/bit_slice.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/common/bit_slice.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) Jan 2013 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2013-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -34,30 +34,36 @@ #define BIT_SLICE_H // BIT Slicing macros -#define ONES32(size) ((size) ? (0xffffffff >> (32 - (size))) : 0) -#define MASK32(offset, size) (ONES32(size) << (offset)) +#define ONES32(size) ((size) ? (0xffffffff >> (32 - (size))) : 0) +#define MASK32(offset, size) (ONES32(size) << (offset)) -#define EXTRACT_C(source, offset, size) ((((unsigned)(source)) >> (offset)) & ONES32(size)) -#define EXTRACT(src, start, len) (((len) == 32) ? (src) : EXTRACT_C(src, start, len)) -#define EXT(src, end, start) EXTRACT(src, start, end - start + 1) - -#define MERGE_C(rsrc1, rsrc2, start, len) ((((rsrc2) << (start)) & (MASK32((start), (len)))) | ((rsrc1) & (~MASK32((start), (len))))) -#define MERGE(rsrc1, rsrc2, start, len) (((len) == 32) ? (rsrc2) : MERGE_C(rsrc1, rsrc2, start, len)) +#define EXTRACT_C(source, offset, size) ((((unsigned)(source)) >> (offset)) & ONES32(size)) +#define EXTRACT(src, start, len) (((len) == 32) ? (src) : EXTRACT_C(src, start, len)) +#define EXT(src, end, start) EXTRACT(src, start, end - start + 1) + +#define MERGE_C(rsrc1, rsrc2, start, len) \ + ((((rsrc2) << (start)) & (MASK32((start), (len)))) | ((rsrc1) & (~MASK32((start), (len))))) +#define MERGE(rsrc1, rsrc2, start, len) (((len) == 32) ? (rsrc2) : MERGE_C(rsrc1, rsrc2, start, len)) #define INSERTF(src1, start1, src2, start2, len) MERGE((src1), EXTRACT((src2), (start2), (len)), (start1), (len)) -#define ONES64(size) ((size) ? (0xffffffffffffffffULL >> (64 - (size))) : 0) -#define MASK64(offset, size) (ONES64(size) << (offset)) +#define ONES64(size) ((size) ? (0xffffffffffffffffULL >> (64 - (size))) : 0) +#define MASK64(offset, size) (ONES64(size) << (offset)) -#define EXTRACT_C64(source, offset, size) ((((unsigned long long)(source)) >> (offset)) & ONES64(size)) -#define EXTRACT64(src, start, len) (((len) == 64) ? (src) : EXTRACT_C64(src, start, len)) +#define EXTRACT_C64(source, offset, size) ((((unsigned long long)(source)) >> (offset)) & ONES64(size)) +#define EXTRACT64(src, start, len) (((len) == 64) ? (src) : EXTRACT_C64(src, start, len)) -#define MERGE_C64(rsrc1, rsrc2, start, len) ((((u_int64_t)(rsrc2) << (start)) & (MASK64((start), (len)))) | ((rsrc1) & (~MASK64((start), (len))))) -#define MERGE64(rsrc1, rsrc2, start, len) (((len) == 64) ? (rsrc2) : MERGE_C64(rsrc1, rsrc2, start, len)) +#define MERGE_C64(rsrc1, rsrc2, start, len) \ + ((((u_int64_t)(rsrc2) << (start)) & (MASK64((start), (len)))) | ((rsrc1) & (~MASK64((start), (len))))) +#define MERGE64(rsrc1, rsrc2, start, len) (((len) == 64) ? (rsrc2) : MERGE_C64(rsrc1, rsrc2, start, len)) #define INSERTF64(src1, start1, src2, start2, len) MERGE64((src1), EXTRACT64((src2), (start2), (len)), (start1), (len)) -#define EXT64(src, end, start) EXTRACT64(src, start, end - start + 1) +#define EXT64(src, end, start) EXTRACT64(src, start, end - start + 1) #ifndef __cplusplus -enum cpp_bool {false = 0, true}; +enum cpp_bool +{ + false = 0, + true +}; typedef unsigned char bool; #endif diff -Nru mstflint-4.17.0+1/common/compatibility.h mstflint-4.21.0+1/common/compatibility.h --- mstflint-4.17.0+1/common/compatibility.h 2021-06-29 08:49:47.000000000 +0000 +++ mstflint-4.21.0+1/common/compatibility.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,5 +1,6 @@ /* * Copyright (C) Jan 2006 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -41,182 +42,177 @@ #include #if defined(__ia64__) || defined(__x86_64__) || defined(__PPC64__) || defined(__arm__) - #define U64L "l" +#define U64L "l" #else - #define U64L "ll" +#define U64L "ll" #endif - /* define macros to the architecture of the CPU */ #if defined(__linux__) || defined(__FreeBSD__) -# if defined(__i386__) -# define ARCH_x86 -# elif defined(__x86_64__) -# define ARCH_x86_64 -# elif defined(__ia64__) -# define ARCH_ia64 -# elif defined(__m68k__) -# define ARCH_m68k -# elif defined(__hppa__) -# define ARCH_hppa -# elif defined(__PPC64__) || defined(__s390x__) -# define ARCH_ppc64 -# elif defined(__PPC__) -# define ARCH_ppc -# elif defined(__aarch64__) -# define ARCH_arm64 -# elif defined(__arm__) -# define ARCH_arm6l -# elif defined(__riscv) -# define ARCH_riscv -# else -# error Unknown CPU architecture using the linux OS -# endif -#elif defined(__MINGW32__) || defined(__MINGW64__) /* Windows MINGW */ -# if defined(__MINGW32__) -# define ARCH_x86 -# elif defined(__MINGW64__) -# define ARCH_x86_64 -# else -# error Unknown CPU architecture using the windows-mingw OS -# endif -#elif defined(_WIN32) || defined(_WIN64) /* Windows */ -# if defined(_WIN32) -# define ARCH_x86 -# elif defined(_WIN64) -# define ARCH_x86_64 -# else -# error Unknown CPU architecture using the windows OS -# endif -#else /* Unknown */ -# error Unknown OS +#if defined(__i386__) +#define ARCH_x86 +#elif defined(__x86_64__) +#define ARCH_x86_64 +#elif defined(__ia64__) +#define ARCH_ia64 +#elif defined(__m68k__) +#define ARCH_m68k +#elif defined(__hppa__) +#define ARCH_hppa +#elif defined(__PPC64__) || defined(__s390x__) +#define ARCH_ppc64 +#elif defined(__PPC__) +#define ARCH_ppc +#elif defined(__aarch64__) +#define ARCH_arm64 +#elif defined(__arm__) +#define ARCH_arm6l +#elif defined(__riscv) +#define ARCH_riscv +#else +#error Unknown CPU architecture using the linux OS +#endif +#elif defined(__MINGW32__) || defined(__MINGW64__) /* Windows MINGW */ +#if defined(__MINGW32__) +#define ARCH_x86 +#elif defined(__MINGW64__) +#define ARCH_x86_64 +#else +#error Unknown CPU architecture using the windows-mingw OS +#endif +#elif defined(_WIN32) || defined(_WIN64) /* Windows */ +#if defined(_WIN32) +#define ARCH_x86 +#elif defined(_WIN64) +#define ARCH_x86_64 +#else +#error Unknown CPU architecture using the windows OS +#endif +#else /* Unknown */ +#error Unknown OS #endif /* define macros for print fields */ -#define U32D_FMT "%u" -#define U32H_FMT "0x%08x" -#define UH_FMT "0x%x" -#define STR_FMT "%s" -#define U16H_FMT "0x%04x" -#define U8H_FMT "0x%02x" - -#if defined(ARCH_x86) || defined(ARCH_ppc) || defined(UEFI_BUILD) || defined(ARCH_arm6l) || defined(ARCH_m68k) || defined(ARCH_hppa) -# if defined(__MINGW32__) || defined(__MINGW64__) -# include -# define U64D_FMT "0x%" PRId64 -# define U64H_FMT "0x%" PRIx64 -# define U64H_FMT_GEN "" PRIx64 -# define U48H_FMT "0x%" PRIx64 -# define U64D_FMT_GEN "" PRId64 -# else -# define U64D_FMT "%llu" -# define U64H_FMT "0x%016llx" -# define U64H_FMT_GEN "llx" -# define U48H_FMT "0x%012llx" -# define U64D_FMT_GEN "llu" -# endif +#define U32D_FMT "%u" +#define U32H_FMT "0x%08x" +#define UH_FMT "0x%x" +#define STR_FMT "%s" +#define U16H_FMT "0x%04x" +#define U8H_FMT "0x%02x" + +#if defined(ARCH_x86) || defined(ARCH_ppc) || defined(UEFI_BUILD) || defined(ARCH_arm6l) || defined(ARCH_m68k) || \ + defined(ARCH_hppa) +#if defined(__MINGW32__) || defined(__MINGW64__) +#include +#define U64D_FMT "0x%" PRId64 +#define U64H_FMT "0x%" PRIx64 +#define U64H_FMT_GEN "" PRIx64 +#define U48H_FMT "0x%" PRIx64 +#define U64D_FMT_GEN "" PRId64 +#else +#define U64D_FMT "%llu" +#define U64H_FMT "0x%016llx" +#define U64H_FMT_GEN "llx" +#define U48H_FMT "0x%012llx" +#define U64D_FMT_GEN "llu" +#endif #elif defined(ARCH_ia64) || defined(ARCH_x86_64) || defined(ARCH_ppc64) || defined(ARCH_arm64) || defined(ARCH_riscv) -# define U64D_FMT "%lu" -# define U64H_FMT "0x%016lx" -# define U48H_FMT "0x%012lx" -# define U64H_FMT_GEN "lx" -# define U64D_FMT_GEN "lu" +#define U64D_FMT "%lu" +#define U64H_FMT "0x%016lx" +#define U48H_FMT "0x%012lx" +#define U64H_FMT_GEN "lx" +#define U64D_FMT_GEN "lu" #else -# error Unknown architecture -#endif /* ARCH */ +#error Unknown architecture +#endif /* ARCH */ /* * Only for architectures which can't do swab by themselves */ #define ___my_swab16(x) \ - ((u_int16_t)( \ - (((u_int16_t)(x) & (u_int16_t)0x00ffU) << 8) | \ - (((u_int16_t)(x) & (u_int16_t)0xff00U) >> 8) )) -#define ___my_swab32(x) \ - ((u_int32_t)( \ - (((u_int32_t)(x) & (u_int32_t)0x000000ffUL) << 24) | \ - (((u_int32_t)(x) & (u_int32_t)0x0000ff00UL) << 8) | \ - (((u_int32_t)(x) & (u_int32_t)0x00ff0000UL) >> 8) | \ - (((u_int32_t)(x) & (u_int32_t)0xff000000UL) >> 24) )) -#define ___my_swab64(x) \ - ((u_int64_t)( \ - (u_int64_t)(((u_int64_t)(x) & (u_int64_t)0x00000000000000ffULL) << 56) | \ - (u_int64_t)(((u_int64_t)(x) & (u_int64_t)0x000000000000ff00ULL) << 40) | \ - (u_int64_t)(((u_int64_t)(x) & (u_int64_t)0x0000000000ff0000ULL) << 24) | \ - (u_int64_t)(((u_int64_t)(x) & (u_int64_t)0x00000000ff000000ULL) << 8) | \ - (u_int64_t)(((u_int64_t)(x) & (u_int64_t)0x000000ff00000000ULL) >> 8) | \ - (u_int64_t)(((u_int64_t)(x) & (u_int64_t)0x0000ff0000000000ULL) >> 24) | \ - (u_int64_t)(((u_int64_t)(x) & (u_int64_t)0x00ff000000000000ULL) >> 40) | \ - (u_int64_t)(((u_int64_t)(x) & (u_int64_t)0xff00000000000000ULL) >> 56) )) + ((u_int16_t)((((u_int16_t)(x) & (u_int16_t)0x00ffU) << 8) | (((u_int16_t)(x) & (u_int16_t)0xff00U) >> 8))) +#define ___my_swab32(x) \ + ((u_int32_t)( \ + (((u_int32_t)(x) & (u_int32_t)0x000000ffUL) << 24) | (((u_int32_t)(x) & (u_int32_t)0x0000ff00UL) << 8) | \ + (((u_int32_t)(x) & (u_int32_t)0x00ff0000UL) >> 8) | (((u_int32_t)(x) & (u_int32_t)0xff000000UL) >> 24))) +#define ___my_swab64(x) \ + ((u_int64_t)((u_int64_t)(((u_int64_t)(x) & (u_int64_t)0x00000000000000ffULL) << 56) | \ + (u_int64_t)(((u_int64_t)(x) & (u_int64_t)0x000000000000ff00ULL) << 40) | \ + (u_int64_t)(((u_int64_t)(x) & (u_int64_t)0x0000000000ff0000ULL) << 24) | \ + (u_int64_t)(((u_int64_t)(x) & (u_int64_t)0x00000000ff000000ULL) << 8) | \ + (u_int64_t)(((u_int64_t)(x) & (u_int64_t)0x000000ff00000000ULL) >> 8) | \ + (u_int64_t)(((u_int64_t)(x) & (u_int64_t)0x0000ff0000000000ULL) >> 24) | \ + (u_int64_t)(((u_int64_t)(x) & (u_int64_t)0x00ff000000000000ULL) >> 40) | \ + (u_int64_t)(((u_int64_t)(x) & (u_int64_t)0xff00000000000000ULL) >> 56))) /* * Linux */ #if defined(__linux__) || defined(__FreeBSD__) // #include - #include - #include +#include +#include - #if defined(__FreeBSD__) - #include +#if defined(__FreeBSD__) +#include // WA: on FBSD the BYTE ORDER AND ENDIANESS names are different - #ifndef __BYTE_ORDER - #define __BYTE_ORDER _BYTE_ORDER - #endif - #ifndef __LITTLE_ENDIAN - #define __LITTLE_ENDIAN _LITTLE_ENDIAN - #endif - #ifndef __BIG_ENDIAN - #define __BIG_ENDIAN _BIG_ENDIAN - #endif - #else - #include - #endif - - #undef __be64_to_cpu - #undef __be32_to_cpu - #undef __be16_to_cpu - #undef __cpu_to_be64 - #undef __cpu_to_be32 - #undef __cpu_to_be16 - #undef __le64_to_cpu - #undef __le32_to_cpu - #undef __le16_to_cpu - #undef __cpu_to_le64 - #undef __cpu_to_le32 - #undef __cpu_to_le16 - - #if __BYTE_ORDER == __LITTLE_ENDIAN - - #define __be64_to_cpu(x) ___my_swab64(x) - #define __be32_to_cpu(x) ___my_swab32(x) - #define __be16_to_cpu(x) ___my_swab16(x) - #define __cpu_to_be64(x) ___my_swab64(x) - #define __cpu_to_be32(x) ___my_swab32(x) - #define __cpu_to_be16(x) ___my_swab16(x) - #define __le64_to_cpu(x) (x) - #define __le32_to_cpu(x) (x) - #define __le16_to_cpu(x) (x) - #define __cpu_to_le64(x) (x) - #define __cpu_to_le32(x) (x) - #define __cpu_to_le16(x) (x) - - #elif __BYTE_ORDER == __BIG_ENDIAN - - #define __be64_to_cpu(x) (x) - #define __be32_to_cpu(x) (x) - #define __be16_to_cpu(x) (x) - #define __cpu_to_be64(x) (x) - #define __cpu_to_be32(x) (x) - #define __cpu_to_be16(x) (x) - #define __le64_to_cpu(x) ___my_swab64(x) - #define __le32_to_cpu(x) ___my_swab32(x) - #define __le16_to_cpu(x) ___my_swab16(x) - #define __cpu_to_le64(x) ___my_swab64(x) - #define __cpu_to_le32(x) ___my_swab32(x) - #define __cpu_to_le16(x) ___my_swab16(x) +#ifndef __BYTE_ORDER +#define __BYTE_ORDER _BYTE_ORDER +#endif +#ifndef __LITTLE_ENDIAN +#define __LITTLE_ENDIAN _LITTLE_ENDIAN +#endif +#ifndef __BIG_ENDIAN +#define __BIG_ENDIAN _BIG_ENDIAN +#endif +#else +#include +#endif - #endif // __BYTE_ORDER +#undef __be64_to_cpu +#undef __be32_to_cpu +#undef __be16_to_cpu +#undef __cpu_to_be64 +#undef __cpu_to_be32 +#undef __cpu_to_be16 +#undef __le64_to_cpu +#undef __le32_to_cpu +#undef __le16_to_cpu +#undef __cpu_to_le64 +#undef __cpu_to_le32 +#undef __cpu_to_le16 + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +#define __be64_to_cpu(x) ___my_swab64(x) +#define __be32_to_cpu(x) ___my_swab32(x) +#define __be16_to_cpu(x) ___my_swab16(x) +#define __cpu_to_be64(x) ___my_swab64(x) +#define __cpu_to_be32(x) ___my_swab32(x) +#define __cpu_to_be16(x) ___my_swab16(x) +#define __le64_to_cpu(x) (x) +#define __le32_to_cpu(x) (x) +#define __le16_to_cpu(x) (x) +#define __cpu_to_le64(x) (x) +#define __cpu_to_le32(x) (x) +#define __cpu_to_le16(x) (x) + +#elif __BYTE_ORDER == __BIG_ENDIAN + +#define __be64_to_cpu(x) (x) +#define __be32_to_cpu(x) (x) +#define __be16_to_cpu(x) (x) +#define __cpu_to_be64(x) (x) +#define __cpu_to_be32(x) (x) +#define __cpu_to_be16(x) (x) +#define __le64_to_cpu(x) ___my_swab64(x) +#define __le32_to_cpu(x) ___my_swab32(x) +#define __le16_to_cpu(x) ___my_swab16(x) +#define __cpu_to_le64(x) ___my_swab64(x) +#define __cpu_to_le32(x) ___my_swab32(x) +#define __cpu_to_le16(x) ___my_swab16(x) + +#endif // __BYTE_ORDER #endif @@ -224,21 +220,21 @@ * Windows (CYGWIN) */ #if defined(__CYGWIN32__) - #include - #include +#include +#include - #define __be64_to_cpu(x) ___my_swab64(x) - #define __be32_to_cpu(x) ___my_swab32(x) - #define __be16_to_cpu(x) ___my_swab16(x) - #define __cpu_to_be64(x) ___my_swab64(x) - #define __cpu_to_be32(x) ___my_swab32(x) - #define __cpu_to_be16(x) ___my_swab16(x) - #define __le64_to_cpu(x) (x) - #define __le32_to_cpu(x) (x) - #define __le16_to_cpu(x) (x) - #define __cpu_to_le64(x) (x) - #define __cpu_to_le32(x) (x) - #define __cpu_to_le16(x) (x) +#define __be64_to_cpu(x) ___my_swab64(x) +#define __be32_to_cpu(x) ___my_swab32(x) +#define __be16_to_cpu(x) ___my_swab16(x) +#define __cpu_to_be64(x) ___my_swab64(x) +#define __cpu_to_be32(x) ___my_swab32(x) +#define __cpu_to_be16(x) ___my_swab16(x) +#define __le64_to_cpu(x) (x) +#define __le32_to_cpu(x) (x) +#define __le16_to_cpu(x) (x) +#define __cpu_to_le64(x) (x) +#define __cpu_to_le32(x) (x) +#define __cpu_to_le16(x) (x) #endif @@ -246,47 +242,45 @@ * Windows (DDK) */ #if defined(__WIN__) - #include - #include - #include - - - #define __LITTLE_ENDIAN 1234 - #define __BIG_ENDIAN 4321 - #define __BYTE_ORDER __LITTLE_ENDIAN - - #define __be64_to_cpu(x) ___my_swab64(x) - #define __be32_to_cpu(x) ___my_swab32(x) - #define __be16_to_cpu(x) ___my_swab16(x) - #define __cpu_to_be64(x) ___my_swab64(x) - #define __cpu_to_be32(x) ___my_swab32(x) - #define __cpu_to_be16(x) ___my_swab16(x) - #define __le64_to_cpu(x) (x) - #define __le32_to_cpu(x) (x) - #define __le16_to_cpu(x) (x) - #define __cpu_to_le64(x) (x) - #define __cpu_to_le32(x) (x) - #define __cpu_to_le16(x) (x) - - - #if defined(_WIN32) || defined(_WIN64) || defined(__MINGW32__) || defined(__MINGW64__) - #include - #ifndef MFT_TOOLS_VARS - #define MFT_TOOLS_VARS +#include +#include +#include + +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 +#define __BYTE_ORDER __LITTLE_ENDIAN + +#define __be64_to_cpu(x) ___my_swab64(x) +#define __be32_to_cpu(x) ___my_swab32(x) +#define __be16_to_cpu(x) ___my_swab16(x) +#define __cpu_to_be64(x) ___my_swab64(x) +#define __cpu_to_be32(x) ___my_swab32(x) +#define __cpu_to_be16(x) ___my_swab16(x) +#define __le64_to_cpu(x) (x) +#define __le32_to_cpu(x) (x) +#define __le16_to_cpu(x) (x) +#define __cpu_to_le64(x) (x) +#define __cpu_to_le32(x) (x) +#define __cpu_to_le16(x) (x) + +#if defined(_WIN32) || defined(_WIN64) || defined(__MINGW32__) || defined(__MINGW64__) +#include +#ifndef MFT_TOOLS_VARS +#define MFT_TOOLS_VARS typedef uint8_t u_int8_t; typedef uint16_t u_int16_t; typedef uint32_t u_int32_t; typedef uint64_t u_int64_t; - #endif - #include // Get a define for strcasecmp - #if defined(_MSC_VER) +#endif +#include // Get a define for strcasecmp +#if defined(_MSC_VER) typedef size_t ssize_t; - #define strcasecmp _stricmp - #define strncasecmp _strnicmp - #endif +#define strcasecmp _stricmp +#define strncasecmp _strnicmp +#endif - #else +#else typedef unsigned __int8 u_int8_t; typedef __int8 int8_t; typedef unsigned __int16 u_int16_t; @@ -296,31 +290,33 @@ typedef unsigned __int64 u_int64_t; typedef __int64 int64_t; - #define strcasecmp _stricmp - #define strtoll _strtoi64 - #define strtoull _strtoui64 - #define strdup _strdup +#define strcasecmp _stricmp +#define strtoll _strtoi64 +#define strtoull _strtoui64 +#define strdup _strdup - #endif +#endif - #ifndef __WIN__ // TBD : investigate crash on win +#ifndef __WIN__ // TBD : investigate crash on win inline - #endif +#endif + +#define COMP_CDECL __cdecl +#define COMP_OPEN ::_open +#define COMP_CLOSE ::_close +#define COMP_READ ::_read +#define COMP_FSTAT ::_fstat - #define COMP_CDECL __cdecl - #define COMP_OPEN ::_open - #define COMP_CLOSE ::_close - #define COMP_READ ::_read - #define COMP_FSTAT ::_fstat - - #ifndef __cplusplus - #define close _close - #endif +#ifndef __cplusplus +#define close _close +#endif -typedef struct _stat Stat; + typedef struct _stat Stat; - #define PRIx64 "I64x" - #define PRIu64 "I64u" +#ifndef PRIx64 +#define PRIx64 "I64x" +#define PRIu64 "I64u" +#endif /* #ifdef __cplusplus static inline int pread(int fd, void *buf, int count, u_int64_t offset) {return -1;} @@ -332,47 +328,45 @@ */ #else - #define COMP_CDECL - #define COMP_OPEN ::open - #define COMP_CLOSE ::close - #define COMP_READ ::read - #define COMP_FSTAT ::fstat +#define COMP_CDECL +#define COMP_OPEN ::open +#define COMP_CLOSE ::close +#define COMP_READ ::read +#define COMP_FSTAT ::fstat typedef struct stat Stat; - #include - #include +#include +#include #endif - - /* * MAC (Darwin) */ #if defined(__APPLE_CC__) - #include +#include - #define __swab64(x) NXSwapLongLong(x) - #define __swab32(x) NXSwapLong(x) - #define __swab16(x) NXSwapShort(x) - #define __be64_to_cpu(x) NXSwapBigLongLongToHost(x) - #define __be32_to_cpu(x) NXSwapBigLongToHost(x) - #define __be16_to_cpu(x) NXSwapBigShortToHost(x) - #define __le64_to_cpu(x) NXSwapLittleLongLongToHost(x) - #define __le32_to_cpu(x) NXSwapLittleLongToHost(x) - #define __le16_to_cpu(x) NXSwapLittleShortToHost(x) - #define __cpu_to_be64(x) NXSwapHostLongLongToBig(x) - #define __cpu_to_be32(x) NXSwapHostLongToBig(x) - #define __cpu_to_be16(x) NXSwapHostShortToBig(x) - #define __cpu_to_le64(x) NXSwapHostLongLongToLittle(x) - #define __cpu_to_le32(x) NXSwapHostLongToLittle(x) - #define __cpu_to_le16(x) NXSwapHostShortToLittle(x) - - #define __LITTLE_ENDIAN 1234 - #define __BIG_ENDIAN 4321 - #define __PDP_ENDIAN 3412 - #define __BYTE_ORDER __BIG_ENDIAN +#define __swab64(x) NXSwapLongLong(x) +#define __swab32(x) NXSwapLong(x) +#define __swab16(x) NXSwapShort(x) +#define __be64_to_cpu(x) NXSwapBigLongLongToHost(x) +#define __be32_to_cpu(x) NXSwapBigLongToHost(x) +#define __be16_to_cpu(x) NXSwapBigShortToHost(x) +#define __le64_to_cpu(x) NXSwapLittleLongLongToHost(x) +#define __le32_to_cpu(x) NXSwapLittleLongToHost(x) +#define __le16_to_cpu(x) NXSwapLittleShortToHost(x) +#define __cpu_to_be64(x) NXSwapHostLongLongToBig(x) +#define __cpu_to_be32(x) NXSwapHostLongToBig(x) +#define __cpu_to_be16(x) NXSwapHostShortToBig(x) +#define __cpu_to_le64(x) NXSwapHostLongLongToLittle(x) +#define __cpu_to_le32(x) NXSwapHostLongToLittle(x) +#define __cpu_to_le16(x) NXSwapHostShortToLittle(x) + +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 +#define __PDP_ENDIAN 3412 +#define __BYTE_ORDER __BIG_ENDIAN #endif /* @@ -380,22 +374,22 @@ * ------- */ #if defined(__sparc__) - #include - #include - #include - - #define __be64_to_cpu(x) (x) - #define __be32_to_cpu(x) (x) - #define __be16_to_cpu(x) (x) - #define __cpu_to_be64(x) (x) - #define __cpu_to_be32(x) (x) - #define __cpu_to_be16(x) (x) - #define __le64_to_cpu(x) ___my_swab64(x) - #define __le32_to_cpu(x) ___my_swab32(x) - #define __le16_to_cpu(x) ___my_swab16(x) - #define __cpu_to_le64(x) ___my_swab64(x) - #define __cpu_to_le32(x) ___my_swab32(x) - #define __cpu_to_le16(x) ___my_swab16(x) +#include +#include +#include + +#define __be64_to_cpu(x) (x) +#define __be32_to_cpu(x) (x) +#define __be16_to_cpu(x) (x) +#define __cpu_to_be64(x) (x) +#define __cpu_to_be32(x) (x) +#define __cpu_to_be16(x) (x) +#define __le64_to_cpu(x) ___my_swab64(x) +#define __le32_to_cpu(x) ___my_swab32(x) +#define __le16_to_cpu(x) ___my_swab16(x) +#define __cpu_to_le64(x) ___my_swab64(x) +#define __cpu_to_le32(x) ___my_swab32(x) +#define __cpu_to_le16(x) ___my_swab16(x) typedef uint64_t u_int64_t; typedef uint32_t u_int32_t; @@ -406,39 +400,45 @@ /* define msleep(x) - sleeps for x milliseconds */ #if defined(_WIN32) || defined(_WIN64) || defined(__MINGW32__) || defined(__MINGW64__) - #define msleep(x) Sleep(x) +#define msleep(x) Sleep(x) #else - #define msleep(x) usleep(((unsigned long)x) * 1000) +#define msleep(x) usleep(((unsigned long)x) * 1000) #endif // Convert BYTES - DWORDS with MEMCPY BE -#define BYTES_TO_DWORD_BE(dw_dest, byte_src) do { u_int32_t tmp; \ - memcpy(&tmp, byte_src, 4); \ - *(dw_dest) = __be32_to_cpu(tmp); \ -} while (0) - -#define DWORD_TO_BYTES_BE(bytes_dest, dw_src) do { u_int32_t tmp; \ - tmp = __cpu_to_be32(*(dw_src)); \ - memcpy(bytes_dest, &tmp, 4); \ -} while (0) +#define BYTES_TO_DWORD_BE(dw_dest, byte_src) \ + do \ + { \ + u_int32_t tmp; \ + memcpy(&tmp, byte_src, 4); \ + *(dw_dest) = __be32_to_cpu(tmp); \ + } while (0) + +#define DWORD_TO_BYTES_BE(bytes_dest, dw_src) \ + do \ + { \ + u_int32_t tmp; \ + tmp = __cpu_to_be32(*(dw_src)); \ + memcpy(bytes_dest, &tmp, 4); \ + } while (0) /* * Old GCC * ------- */ #if !defined(__WIN__) && defined(__cplusplus) && __GNUG__ < 3 - #include +#include #endif #define __STDC_FORMAT_MACROS #if !defined(__WIN__) && !defined(UEFI_BUILD) - #include +#include #endif #if defined(__VMKERNEL_UW_NATIVE__) - #define ROOT_PATH "/opt/mellanox/" +#define ROOT_PATH "/opt/mellanox/" #else - #define ROOT_PATH "/" +#define ROOT_PATH "/" #endif #ifndef IN @@ -457,4 +457,3 @@ #endif #endif - diff -Nru mstflint-4.17.0+1/common/gitversion.h mstflint-4.21.0+1/common/gitversion.h --- mstflint-4.17.0+1/common/gitversion.h 2021-06-29 08:50:59.000000000 +0000 +++ mstflint-4.21.0+1/common/gitversion.h 2022-07-31 09:34:38.000000000 +0000 @@ -1 +1 @@ -#define TOOLS_GIT_SHA "646cd3b" +#define TOOLS_GIT_SHA "d354465" diff -Nru mstflint-4.17.0+1/common/Makefile.am mstflint-4.21.0+1/common/Makefile.am --- mstflint-4.17.0+1/common/Makefile.am 2021-06-29 08:49:47.000000000 +0000 +++ mstflint-4.21.0+1/common/Makefile.am 2022-04-28 11:49:49.000000000 +0000 @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved. +# Copyright (c) 2004-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU diff -Nru mstflint-4.17.0+1/common/Makefile.in mstflint-4.21.0+1/common/Makefile.in --- mstflint-4.17.0+1/common/Makefile.in 2021-06-29 08:51:07.000000000 +0000 +++ mstflint-4.21.0+1/common/Makefile.in 2022-07-31 09:34:41.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.3 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2020 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -15,7 +15,7 @@ @SET_MAKE@ #-- -# Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved. +# Copyright (c) 2004-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU diff -Nru mstflint-4.17.0+1/common/mft_logger.py mstflint-4.21.0+1/common/mft_logger.py --- mstflint-4.17.0+1/common/mft_logger.py 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/common/mft_logger.py 2022-07-31 09:33:57.000000000 +0000 @@ -1,4 +1,5 @@ # Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved. +# Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU @@ -27,7 +28,7 @@ # 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. -#-- +# -- import logging @@ -48,7 +49,6 @@ else: log_level = self._levels[level] - logging.basicConfig(level=log_level, format='%(asctime)s - %(name)s - %(levelname)s - {%(pathname)s:%(lineno)d} - %(message)s') logger = logging.getLogger(name) return logger diff -Nru mstflint-4.17.0+1/common/python_wrapper.sh mstflint-4.21.0+1/common/python_wrapper.sh --- mstflint-4.17.0+1/common/python_wrapper.sh 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/common/python_wrapper.sh 2022-04-28 11:49:49.000000000 +0000 @@ -10,11 +10,12 @@ MSTFLINT_EXT_PYTHON_LIB_DIR=$mlibdir/mstflint/python_ext_libs MSTFLINT_PYTHON_TOOLS=$mlibdir/mstflint/python_tools +PY_TOOLS_REL_PATHS=$(dirname $0)/../lib/mstflint/python_tools:$(dirname $0)/../lib64/mstflint/python_tools if test -z "${PYTHONPATH}"; then - PYTHONPATH=$MSTFLINT_PYTHON_TOOLS:$MSTFLINT_EXT_PYTHON_LIB_DIR + PYTHONPATH=$MSTFLINT_PYTHON_TOOLS:$PY_TOOLS_REL_PATHS:$MSTFLINT_EXT_PYTHON_LIB_DIR else - PYTHONPATH=$MSTFLINT_PYTHON_TOOLS:$MSTFLINT_EXT_PYTHON_LIB_DIR:${PYTHONPATH} + PYTHONPATH=$MSTFLINT_PYTHON_TOOLS:$PY_TOOLS_REL_PATHS:$MSTFLINT_EXT_PYTHON_LIB_DIR:${PYTHONPATH} fi export PYTHONPATH @@ -35,7 +36,7 @@ SCRIPT_PATH=$MSTFLINT_PYTHON_TOOLS/${EXEC_NAME}/${EXEC_NAME}.py if test ! -f $SCRIPT_PATH; then - SCRIPT_PATH=$(find $MSTFLINT_PYTHON_TOOLS -name ${EXEC_NAME}.py) + SCRIPT_PATH=$(find $MSTFLINT_PYTHON_TOOLS $(echo $PY_TOOLS_REL_PATHS | sed "s/:/ /g") -name ${EXEC_NAME}.py 2>/dev/null | head -n 1) fi if test -z "${SCRIPT_PATH=}"; then diff -Nru mstflint-4.17.0+1/common/tools_utils.h mstflint-4.21.0+1/common/tools_utils.h --- mstflint-4.17.0+1/common/tools_utils.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/common/tools_utils.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) Jan 2013 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2013-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -35,34 +35,33 @@ #include "compatibility.h" -#define CPU_TO_BE32(x) __cpu_to_be32(x) -#define CPU_TO_LE32(x) __cpu_to_le32(x) -#define BE32_TO_CPU(x) __be32_to_cpu(x) -#define LE32_TO_CPU(x) __le32_to_cpu(x) -#define CPU_TO_BE16(x) __cpu_to_be16(x) -#define CPU_TO_LE16(x) __cpu_to_le16(x) -#define BE16_TO_CPU(x) __be16_to_cpu(x) -#define LE16_TO_CPU(x) __le16_to_cpu(x) -#define CPU_TO_BE64(x) __cpu_to_be64(x) -#define CPU_TO_LE64(x) __cpu_to_le64(x) -#define BE64_TO_CPU(x) __be64_to_cpu(x) -#define LE64_TO_CPU(x) __le64_to_cpu(x) +#define CPU_TO_BE32(x) __cpu_to_be32(x) +#define CPU_TO_LE32(x) __cpu_to_le32(x) +#define BE32_TO_CPU(x) __be32_to_cpu(x) +#define LE32_TO_CPU(x) __le32_to_cpu(x) +#define CPU_TO_BE16(x) __cpu_to_be16(x) +#define CPU_TO_LE16(x) __cpu_to_le16(x) +#define BE16_TO_CPU(x) __be16_to_cpu(x) +#define LE16_TO_CPU(x) __le16_to_cpu(x) +#define CPU_TO_BE64(x) __cpu_to_be64(x) +#define CPU_TO_LE64(x) __cpu_to_le64(x) +#define BE64_TO_CPU(x) __be64_to_cpu(x) +#define LE64_TO_CPU(x) __le64_to_cpu(x) #define FIELD_8_OF_BUFF(buf, offset) (*(u_int8_t*)((u_int8_t*)(buf) + (offset))) #define FIELD_16_OF_BUFF(buf, offset) (*(u_int16_t*)((u_int8_t*)(buf) + (offset))) #define FIELD_32_OF_BUFF(buf, offset) (*(u_int32_t*)((u_int8_t*)(buf) + (offset))) #define FIELD_64_OF_BUFF(buf, offset) (*(u_int64_t*)((u_int8_t*)(buf) + (offset))) -#define BYTE_N(buf, n) FIELD_8_OF_BUFF((buf), (n)) -#define WORD_N(buf, n) FIELD_16_OF_BUFF((buf), (n) * 2) -#define DWORD_N(buf, n) FIELD_32_OF_BUFF((buf), (n) * 4) -#define QWORD_N(buf, n) FIELD_64_OF_BUFF((buf), (n) * 8) +#define BYTE_N(buf, n) FIELD_8_OF_BUFF((buf), (n)) +#define WORD_N(buf, n) FIELD_16_OF_BUFF((buf), (n)*2) +#define DWORD_N(buf, n) FIELD_32_OF_BUFF((buf), (n)*4) +#define QWORD_N(buf, n) FIELD_64_OF_BUFF((buf), (n)*8) #define QWORD_EXTRACT_DWORD_HI(qword) ((u_int64_t)(qword) >> 32) #define QWORD_EXTRACT_DWORD_LO(qword) ((u_int64_t)(qword) & (u_int64_t)0xffffffff) #define QWORD_INSERT_DWORD_HI(qword, dword) qword = ((qword & (u_int64_t)0xffffffff) | ((u_int64_t)dword << 32)) #define QWORD_INSERT_DWORD_LO(qword, dword) qword = ((qword & (u_int64_t)0xffffffff00000000) | ((u_int64_t)dword)) #define U32_TO_U64(dword1, dword2) ((((u_int64_t)(dword1)) << 32) | (((u_int64_t)(dword2)) & 0xffffffff)) - #define TOOLS_MAX(a, b) ((a) > (b) ? (a) : (b)) #define TOOLS_MIN(a, b) ((a) < (b) ? (a) : (b)) @@ -73,6 +72,4 @@ */ #define TOOLS_UNUSED(var) ((void)var) - #endif // TOOLS_UTILS_H - diff -Nru mstflint-4.17.0+1/common/tools_version.h mstflint-4.21.0+1/common/tools_version.h --- mstflint-4.17.0+1/common/tools_version.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/common/tools_version.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,5 +1,6 @@ /* * Copyright (C) Jan 2013 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -36,56 +37,51 @@ #ifndef TOOLS_VERSION_H #define TOOLS_VERSION_H - #include #include #include -#ifdef HAVE_CONFIG_H - #include - #ifndef MSTFLINT_VERSION_STR - #define MSTFLINT_VERSION_STR PACKAGE_STRING - #endif -#else - // To be replaced by an external script: - #ifdef MST_UL - #include "gitversion.h" - #endif +// To be replaced by an external script: +#ifdef MST_UL +#include "gitversion.h" #endif - #ifndef TOOLS_GIT_SHA - #define TOOLS_GIT_SHA "6469M" +#define TOOLS_GIT_SHA "6469M" #endif +#ifdef HAVE_CONFIG_H +#include #ifndef MSTFLINT_VERSION_STR - #define MSTFLINT_VERSION_STR "mstflint V.V.V-R" +#define MSTFLINT_VERSION_STR PACKAGE_STRING +#endif #endif -static inline -int get_version_string(char *buf, int buf_size, const char *exe_name, const char *tool_version) +#ifndef MFT_VERSION_STR +#define MFT_VERSION_STR "mft V.V.V-R" +#endif + +static inline int get_version_string(char* buf, int buf_size, const char* exe_name, const char* tool_version) { int len = 0; // "svn ci" updates the below line - - if (tool_version == NULL || !strcmp(tool_version, "")) { + if (tool_version == NULL || !strcmp(tool_version, "")) + { len = snprintf(buf, buf_size, "%s, ", exe_name); - } else { + } + else + { len = snprintf(buf, buf_size, "%s %s, ", exe_name, tool_version); } // cut out first and last "$" from the SVN version string: - len += snprintf(buf + len, buf_size - len, "%s, built on %s, %s. Git SHA Hash: %s", - MSTFLINT_VERSION_STR, - __DATE__, - __TIME__, - TOOLS_GIT_SHA); + len += snprintf(buf + len, buf_size - len, "%s, built on %s, %s. Git SHA Hash: %s", MSTFLINT_VERSION_STR, __DATE__, + __TIME__, TOOLS_GIT_SHA); return len; } -static inline -void print_version_string(const char *exe_name, const char *tool_version) +static inline void print_version_string(const char* exe_name, const char* tool_version) { - char buf[1024]; + char buf[1024] = {0}; get_version_string(buf, sizeof(buf), exe_name, tool_version); printf("%s\n", buf); } diff -Nru mstflint-4.17.0+1/config/ar-lib mstflint-4.21.0+1/config/ar-lib --- mstflint-4.17.0+1/config/ar-lib 2021-06-29 08:51:07.000000000 +0000 +++ mstflint-4.21.0+1/config/ar-lib 2022-07-31 09:34:41.000000000 +0000 @@ -2,9 +2,9 @@ # Wrapper for Microsoft lib.exe me=ar-lib -scriptversion=2019-07-04.01; # UTC +scriptversion=2012-03-01.08; # UTC -# Copyright (C) 2010-2020 Free Software Foundation, Inc. +# Copyright (C) 2010-2018 Free Software Foundation, Inc. # Written by Peter Rosin . # # This program is free software; you can redistribute it and/or modify @@ -53,7 +53,7 @@ MINGW*) file_conv=mingw ;; - CYGWIN* | MSYS*) + CYGWIN*) file_conv=cygwin ;; *) @@ -65,7 +65,7 @@ mingw) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; - cygwin | msys) + cygwin) file=`cygpath -m "$file" || echo "$file"` ;; wine) @@ -224,11 +224,10 @@ esac done else - $AR -NOLOGO -LIST "$archive" | tr -d '\r' | sed -e 's/\\/\\\\/g' \ - | while read member - do - $AR -NOLOGO -EXTRACT:"$member" "$archive" || exit $? - done + $AR -NOLOGO -LIST "$archive" | sed -e 's/\\/\\\\/g' | while read member + do + $AR -NOLOGO -EXTRACT:"$member" "$archive" || exit $? + done fi elif test -n "$quick$replace"; then diff -Nru mstflint-4.17.0+1/config/compile mstflint-4.21.0+1/config/compile --- mstflint-4.17.0+1/config/compile 2021-06-29 08:51:07.000000000 +0000 +++ mstflint-4.21.0+1/config/compile 2022-07-31 09:34:41.000000000 +0000 @@ -3,7 +3,7 @@ scriptversion=2018-03-07.03; # UTC -# Copyright (C) 1999-2020 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify @@ -53,7 +53,7 @@ MINGW*) file_conv=mingw ;; - CYGWIN* | MSYS*) + CYGWIN*) file_conv=cygwin ;; *) @@ -67,7 +67,7 @@ mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; - cygwin/* | msys/*) + cygwin/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) diff -Nru mstflint-4.17.0+1/config/config.guess mstflint-4.21.0+1/config/config.guess --- mstflint-4.17.0+1/config/config.guess 2021-06-29 08:51:07.000000000 +0000 +++ mstflint-4.21.0+1/config/config.guess 2022-07-31 09:34:41.000000000 +0000 @@ -1,8 +1,8 @@ #! /bin/sh # Attempt to guess a canonical system name. -# Copyright 1992-2020 Free Software Foundation, Inc. +# Copyright 1992-2018 Free Software Foundation, Inc. -timestamp='2020-11-07' +timestamp='2018-03-08' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -32,7 +32,7 @@ # Please send patches to . -me=$(echo "$0" | sed -e 's,.*/,,') +me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] @@ -50,7 +50,7 @@ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright 1992-2020 Free Software Foundation, Inc. +Copyright 1992-2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -84,6 +84,8 @@ exit 1 fi +trap 'exit 1' 1 2 15 + # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a @@ -94,47 +96,41 @@ # Portable tmp directory creation inspired by the Autoconf team. -tmp= -# shellcheck disable=SC2172 -trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 - -set_cc_for_build() { - # prevent multiple calls if $tmp is already set - test "$tmp" && return 0 - : "${TMPDIR=/tmp}" - # shellcheck disable=SC2039 - { tmp=$( (umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null) && test -n "$tmp" && test -d "$tmp" ; } || - { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || - { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || - { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } - dummy=$tmp/dummy - case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in - ,,) echo "int x;" > "$dummy.c" - for driver in cc gcc c89 c99 ; do - if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then - CC_FOR_BUILD="$driver" - break - fi - done - if test x"$CC_FOR_BUILD" = x ; then - CC_FOR_BUILD=no_compiler_found - fi - ;; - ,,*) CC_FOR_BUILD=$CC ;; - ,*,*) CC_FOR_BUILD=$HOST_CC ;; - esac -} +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > "$dummy.c" ; + for c in cc gcc c89 c99 ; do + if ($c -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) -if test -f /.attbin/uname ; then +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi -UNAME_MACHINE=$( (uname -m) 2>/dev/null) || UNAME_MACHINE=unknown -UNAME_RELEASE=$( (uname -r) 2>/dev/null) || UNAME_RELEASE=unknown -UNAME_SYSTEM=$( (uname -s) 2>/dev/null) || UNAME_SYSTEM=unknown -UNAME_VERSION=$( (uname -v) 2>/dev/null) || UNAME_VERSION=unknown +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "$UNAME_SYSTEM" in Linux|GNU|GNU/*) @@ -142,7 +138,7 @@ # We could probably try harder. LIBC=gnu - set_cc_for_build + eval "$set_cc_for_build" cat <<-EOF > "$dummy.c" #include #if defined(__UCLIBC__) @@ -150,15 +146,17 @@ #elif defined(__dietlibc__) LIBC=dietlibc #else - #include - #ifdef __DEFINED_va_list - LIBC=musl - #else LIBC=gnu #endif - #endif EOF - eval "$($CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g')" + eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`" + + # If ldd exists, use it to detect musl libc. + if command -v ldd >/dev/null && \ + ldd --version 2>&1 | grep -q ^musl + then + LIBC=musl + fi ;; esac @@ -177,20 +175,19 @@ # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" - UNAME_MACHINE_ARCH=$( (uname -p 2>/dev/null || \ + UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ "/sbin/$sysctl" 2>/dev/null || \ "/usr/sbin/$sysctl" 2>/dev/null || \ - echo unknown)) + echo unknown)` case "$UNAME_MACHINE_ARCH" in - aarch64eb) machine=aarch64_be-unknown ;; armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) - arch=$(echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,') - endian=$(echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p') + arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` machine="${arch}${endian}"-unknown ;; *) machine="$UNAME_MACHINE_ARCH"-unknown ;; @@ -202,7 +199,7 @@ os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) - set_cc_for_build + eval "$set_cc_for_build" if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then @@ -221,7 +218,7 @@ case "$UNAME_MACHINE_ARCH" in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' - abi=$(echo "$UNAME_MACHINE_ARCH" | sed -e "$expr") + abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` ;; esac # The OS release @@ -234,24 +231,24 @@ release='-gnu' ;; *) - release=$(echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2) + release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "$machine-${os}${release}${abi-}" + echo "$machine-${os}${release}${abi}" exit ;; *:Bitrig:*:*) - UNAME_MACHINE_ARCH=$(arch | sed 's/Bitrig.//') + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" exit ;; *:OpenBSD:*:*) - UNAME_MACHINE_ARCH=$(arch | sed 's/OpenBSD.//') + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" exit ;; *:LibertyBSD:*:*) - UNAME_MACHINE_ARCH=$(arch | sed 's/^.*BSD\.//') + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" exit ;; *:MidnightBSD:*:*) @@ -263,9 +260,6 @@ *:SolidBSD:*:*) echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" exit ;; - *:OS108:*:*) - echo "$UNAME_MACHINE"-unknown-os108_"$UNAME_RELEASE" - exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd"$UNAME_RELEASE" exit ;; @@ -275,29 +269,26 @@ *:Sortix:*:*) echo "$UNAME_MACHINE"-unknown-sortix exit ;; - *:Twizzler:*:*) - echo "$UNAME_MACHINE"-unknown-twizzler - exit ;; *:Redox:*:*) echo "$UNAME_MACHINE"-unknown-redox exit ;; mips:OSF1:*.*) - echo mips-dec-osf1 - exit ;; + echo mips-dec-osf1 + exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) - UNAME_RELEASE=$(/usr/sbin/sizer -v | awk '{print $3}') + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) - UNAME_RELEASE=$(/usr/sbin/sizer -v | awk '{print $4}') + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. - ALPHA_CPU_TYPE=$(/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1) + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE=alpha ;; @@ -335,7 +326,7 @@ # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. - echo "$UNAME_MACHINE"-dec-osf"$(echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz)" + echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`" # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 @@ -369,7 +360,7 @@ exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. - if test "$( (/bin/universe) 2>/dev/null)" = att ; then + if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd @@ -382,28 +373,28 @@ echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) - case $(/usr/bin/uname -p) in + case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) - echo "$UNAME_MACHINE"-ibm-solaris2"$(echo "$UNAME_RELEASE" | sed -e 's/[^.]*//')" + echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" + echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2"$(echo "$UNAME_RELEASE" | sed -e 's/[^.]*//')" + echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux"$UNAME_RELEASE" exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) - set_cc_for_build + eval "$set_cc_for_build" SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. - if test "$CC_FOR_BUILD" != no_compiler_found; then + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null @@ -411,30 +402,30 @@ SUN_ARCH=x86_64 fi fi - echo "$SUN_ARCH"-pc-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" + echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" + echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:*:*) - case "$(/usr/bin/arch -k)" in + case "`/usr/bin/arch -k`" in Series*|S4*) - UNAME_RELEASE=$(uname -v) + UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos"$(echo "$UNAME_RELEASE"|sed -e 's/-/_/')" + echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`" exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos"$UNAME_RELEASE" exit ;; sun*:*:4.2BSD:*) - UNAME_RELEASE=$( (sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 - case "$(/bin/arch)" in + case "`/bin/arch`" in sun3) echo m68k-sun-sunos"$UNAME_RELEASE" ;; @@ -491,7 +482,7 @@ echo clipper-intergraph-clix"$UNAME_RELEASE" exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) - set_cc_for_build + eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ @@ -514,8 +505,8 @@ } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && - dummyarg=$(echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p') && - SYSTEM_NAME=$("$dummy" "$dummyarg") && + dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos"$UNAME_RELEASE" exit ;; @@ -542,11 +533,11 @@ exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures - UNAME_PROCESSOR=$(/usr/bin/uname -p) - if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ] then - if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ - test "$TARGET_BINARY_INTERFACE"x = x + if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \ + [ "$TARGET_BINARY_INTERFACE"x = x ] then echo m88k-dg-dgux"$UNAME_RELEASE" else @@ -570,17 +561,17 @@ echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) - echo mips-sgi-irix"$(echo "$UNAME_RELEASE"|sed -e 's/-/_/g')" + echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`" exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id - exit ;; # Note that: echo "'$(uname -s)'" gives 'AIX ' + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) - if test -x /usr/bin/oslevel ; then - IBM_REV=$(/usr/bin/oslevel) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` else IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi @@ -588,7 +579,7 @@ exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - set_cc_for_build + eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #include @@ -600,7 +591,7 @@ exit(0); } EOF - if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=$("$dummy") + if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then echo "$SYSTEM_NAME" else @@ -613,15 +604,15 @@ fi exit ;; *:AIX:*:[4567]) - IBM_CPU_ID=$(/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }') + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi - if test -x /usr/bin/lslpp ; then - IBM_REV=$(/usr/bin/lslpp -Lqc bos.rte.libc | - awk -F: '{ print $3 }' | sed s/[0-9]*$/0/) + if [ -x /usr/bin/lslpp ] ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi @@ -649,14 +640,14 @@ echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) - HPUX_REV=$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//') + HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` case "$UNAME_MACHINE" in 9000/31?) HP_ARCH=m68000 ;; 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) - if test -x /usr/bin/getconf; then - sc_cpu_version=$(/usr/bin/getconf SC_CPU_VERSION 2>/dev/null) - sc_kernel_bits=$(/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "$sc_cpu_version" in 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 @@ -668,8 +659,8 @@ esac ;; esac fi - if test "$HP_ARCH" = ""; then - set_cc_for_build + if [ "$HP_ARCH" = "" ]; then + eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE @@ -703,13 +694,13 @@ exit (0); } EOF - (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=$("$dummy") + (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac - if test "$HP_ARCH" = hppa2.0w + if [ "$HP_ARCH" = hppa2.0w ] then - set_cc_for_build + eval "$set_cc_for_build" # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler @@ -731,11 +722,11 @@ echo "$HP_ARCH"-hp-hpux"$HPUX_REV" exit ;; ia64:HP-UX:*:*) - HPUX_REV=$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//') + HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux"$HPUX_REV" exit ;; 3050*:HI-UX:*:*) - set_cc_for_build + eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #include int @@ -761,7 +752,7 @@ exit (0); } EOF - $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=$("$dummy") && + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; @@ -781,7 +772,7 @@ echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) - if test -x /usr/sbin/sysversion ; then + if [ -x /usr/sbin/sysversion ] ; then echo "$UNAME_MACHINE"-unknown-osf1mk else echo "$UNAME_MACHINE"-unknown-osf1 @@ -830,14 +821,14 @@ echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=$(uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz) - FUJITSU_SYS=$(uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///') - FUJITSU_REL=$(echo "$UNAME_RELEASE" | sed -e 's/ /_/') + FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) - FUJITSU_SYS=$(uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///') - FUJITSU_REL=$(echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/') + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) @@ -849,26 +840,15 @@ *:BSD/OS:*:*) echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" exit ;; - arm:FreeBSD:*:*) - UNAME_PROCESSOR=$(uname -p) - set_cc_for_build - if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ARM_PCS_VFP - then - echo "${UNAME_PROCESSOR}"-unknown-freebsd"$(echo ${UNAME_RELEASE}|sed -e 's/[-(].*//')"-gnueabi - else - echo "${UNAME_PROCESSOR}"-unknown-freebsd"$(echo ${UNAME_RELEASE}|sed -e 's/[-(].*//')"-gnueabihf - fi - exit ;; *:FreeBSD:*:*) - UNAME_PROCESSOR=$(/usr/bin/uname -p) + UNAME_PROCESSOR=`/usr/bin/uname -p` case "$UNAME_PROCESSOR" in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac - echo "$UNAME_PROCESSOR"-unknown-freebsd"$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')" + echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" exit ;; i*:CYGWIN*:*) echo "$UNAME_MACHINE"-pc-cygwin @@ -901,21 +881,21 @@ echo "$UNAME_MACHINE"-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) - echo x86_64-pc-cygwin + echo x86_64-unknown-cygwin exit ;; prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" + echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; *:GNU:*:*) # the GNU system - echo "$(echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,')-unknown-$LIBC$(echo "$UNAME_RELEASE"|sed -e 's,/.*$,,')" + echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`" exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland - echo "$UNAME_MACHINE-unknown-$(echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]")$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')-$LIBC" + echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC" exit ;; - *:Minix:*:*) - echo "$UNAME_MACHINE"-unknown-minix + i*86:Minix:*:*) + echo "$UNAME_MACHINE"-pc-minix exit ;; aarch64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" @@ -925,7 +905,7 @@ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; alpha:Linux:*:*) - case $(sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null) in + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; @@ -942,7 +922,7 @@ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arm*:Linux:*:*) - set_cc_for_build + eval "$set_cc_for_build" if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then @@ -991,51 +971,23 @@ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; mips:Linux:*:* | mips64:Linux:*:*) - set_cc_for_build - IS_GLIBC=0 - test x"${LIBC}" = xgnu && IS_GLIBC=1 + eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #undef CPU - #undef mips - #undef mipsel - #undef mips64 - #undef mips64el - #if ${IS_GLIBC} && defined(_ABI64) - LIBCABI=gnuabi64 - #else - #if ${IS_GLIBC} && defined(_ABIN32) - LIBCABI=gnuabin32 - #else - LIBCABI=${LIBC} - #endif - #endif - - #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 - CPU=mipsisa64r6 - #else - #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 - CPU=mipsisa32r6 - #else - #if defined(__mips64) - CPU=mips64 - #else - CPU=mips - #endif - #endif - #endif - + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - MIPS_ENDIAN=el + CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - MIPS_ENDIAN= + CPU=${UNAME_MACHINE} #else - MIPS_ENDIAN= + CPU= #endif #endif EOF - eval "$($CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI')" - test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } + eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU'`" + test "x$CPU" != x && { echo "$CPU-unknown-linux-$LIBC"; exit; } ;; mips64el:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" @@ -1054,7 +1006,7 @@ exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level - case $(grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2) in + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; *) echo hppa-unknown-linux-"$LIBC" ;; @@ -1094,17 +1046,7 @@ echo "$UNAME_MACHINE"-dec-linux-"$LIBC" exit ;; x86_64:Linux:*:*) - set_cc_for_build - LIBCABI=$LIBC - if test "$CC_FOR_BUILD" != no_compiler_found; then - if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_X32 >/dev/null - then - LIBCABI="$LIBC"x32 - fi - fi - echo "$UNAME_MACHINE"-pc-linux-"$LIBCABI" + echo "$UNAME_MACHINE"-pc-linux-"$LIBC" exit ;; xtensa*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" @@ -1144,7 +1086,7 @@ echo "$UNAME_MACHINE"-pc-msdosdjgpp exit ;; i*86:*:4.*:*) - UNAME_REL=$(echo "$UNAME_RELEASE" | sed 's/\/MP$//') + UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" else @@ -1153,19 +1095,19 @@ exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. - case $(/bin/uname -X | grep "^Machine") in + case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac - echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}" + echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}{$UNAME_VERSION}" exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then - UNAME_REL=$(sed -n 's/.*Version //p' /dev/null >/dev/null ; then - UNAME_REL=$( (/bin/uname -X|grep Release|sed -e 's/.*= //')) + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 @@ -1215,7 +1157,7 @@ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ - && OS_REL=.$(sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid) + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ @@ -1226,7 +1168,7 @@ NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ - && OS_REL=.$(sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid) + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ @@ -1259,7 +1201,7 @@ exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then - UNAME_MACHINE=$( (uname -p) 2>/dev/null) + UNAME_MACHINE=`(uname -p) 2>/dev/null` echo "$UNAME_MACHINE"-sni-sysv4 else echo ns32k-sni-sysv @@ -1293,7 +1235,7 @@ echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) - if test -d /usr/nec; then + if [ -d /usr/nec ]; then echo mips-nec-sysv"$UNAME_RELEASE" else echo mips-unknown-sysv"$UNAME_RELEASE" @@ -1341,48 +1283,44 @@ *:Rhapsody:*:*) echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" exit ;; - arm64:Darwin:*:*) - echo aarch64-apple-darwin"$UNAME_RELEASE" - exit ;; *:Darwin:*:*) - UNAME_PROCESSOR=$(uname -p) - case $UNAME_PROCESSOR in - unknown) UNAME_PROCESSOR=powerpc ;; - esac - if command -v xcode-select > /dev/null 2> /dev/null && \ - ! xcode-select --print-path > /dev/null 2> /dev/null ; then - # Avoid executing cc if there is no toolchain installed as - # cc will be a stub that puts up a graphical alert - # prompting the user to install developer tools. - CC_FOR_BUILD=no_compiler_found - else - set_cc_for_build - fi - if test "$CC_FOR_BUILD" != no_compiler_found; then - if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - case $UNAME_PROCESSOR in - i386) UNAME_PROCESSOR=x86_64 ;; - powerpc) UNAME_PROCESSOR=powerpc64 ;; - esac - fi - # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc - if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_PPC >/dev/null - then - UNAME_PROCESSOR=powerpc + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + eval "$set_cc_for_build" + if test "$UNAME_PROCESSOR" = unknown ; then + UNAME_PROCESSOR=powerpc + fi + if test "`echo "$UNAME_RELEASE" | sed -e 's/\..*//'`" -le 10 ; then + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc + if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_PPC >/dev/null + then + UNAME_PROCESSOR=powerpc + fi fi elif test "$UNAME_PROCESSOR" = i386 ; then - # uname -m returns i386 or x86_64 - UNAME_PROCESSOR=$UNAME_MACHINE + # Avoid executing cc on OS X 10.9, as it ships with a stub + # that puts up a graphical alert prompting to install + # developer tools. Any system running Mac OS X 10.7 or + # later (Darwin 11 and later) is required to have a 64-bit + # processor. This is not true of the ARM version of Darwin + # that Apple uses in portable devices. + UNAME_PROCESSOR=x86_64 fi echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) - UNAME_PROCESSOR=$(uname -p) + UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc @@ -1420,7 +1358,6 @@ # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. - # shellcheck disable=SC2154 if test "$cputype" = 386; then UNAME_MACHINE=i386 else @@ -1450,10 +1387,10 @@ echo mips-sei-seiux"$UNAME_RELEASE" exit ;; *:DragonFly:*:*) - echo "$UNAME_MACHINE"-unknown-dragonfly"$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')" + echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" exit ;; *:*VMS:*:*) - UNAME_MACHINE=$( (uname -p) 2>/dev/null) + UNAME_MACHINE=`(uname -p) 2>/dev/null` case "$UNAME_MACHINE" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; @@ -1463,7 +1400,7 @@ echo i386-pc-xenix exit ;; i*86:skyos:*:*) - echo "$UNAME_MACHINE"-pc-skyos"$(echo "$UNAME_RELEASE" | sed -e 's/ .*$//')" + echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`" exit ;; i*86:rdos:*:*) echo "$UNAME_MACHINE"-pc-rdos @@ -1477,148 +1414,8 @@ amd64:Isilon\ OneFS:*:*) echo x86_64-unknown-onefs exit ;; - *:Unleashed:*:*) - echo "$UNAME_MACHINE"-unknown-unleashed"$UNAME_RELEASE" - exit ;; esac -# No uname command or uname output not recognized. -set_cc_for_build -cat > "$dummy.c" < -#include -#endif -#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) -#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) -#include -#if defined(_SIZE_T_) || defined(SIGLOST) -#include -#endif -#endif -#endif -main () -{ -#if defined (sony) -#if defined (MIPSEB) - /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, - I don't know.... */ - printf ("mips-sony-bsd\n"); exit (0); -#else -#include - printf ("m68k-sony-newsos%s\n", -#ifdef NEWSOS4 - "4" -#else - "" -#endif - ); exit (0); -#endif -#endif - -#if defined (NeXT) -#if !defined (__ARCHITECTURE__) -#define __ARCHITECTURE__ "m68k" -#endif - int version; - version=$( (hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null); - if (version < 4) - printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); - else - printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); - exit (0); -#endif - -#if defined (MULTIMAX) || defined (n16) -#if defined (UMAXV) - printf ("ns32k-encore-sysv\n"); exit (0); -#else -#if defined (CMU) - printf ("ns32k-encore-mach\n"); exit (0); -#else - printf ("ns32k-encore-bsd\n"); exit (0); -#endif -#endif -#endif - -#if defined (__386BSD__) - printf ("i386-pc-bsd\n"); exit (0); -#endif - -#if defined (sequent) -#if defined (i386) - printf ("i386-sequent-dynix\n"); exit (0); -#endif -#if defined (ns32000) - printf ("ns32k-sequent-dynix\n"); exit (0); -#endif -#endif - -#if defined (_SEQUENT_) - struct utsname un; - - uname(&un); - if (strncmp(un.version, "V2", 2) == 0) { - printf ("i386-sequent-ptx2\n"); exit (0); - } - if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ - printf ("i386-sequent-ptx1\n"); exit (0); - } - printf ("i386-sequent-ptx\n"); exit (0); -#endif - -#if defined (vax) -#if !defined (ultrix) -#include -#if defined (BSD) -#if BSD == 43 - printf ("vax-dec-bsd4.3\n"); exit (0); -#else -#if BSD == 199006 - printf ("vax-dec-bsd4.3reno\n"); exit (0); -#else - printf ("vax-dec-bsd\n"); exit (0); -#endif -#endif -#else - printf ("vax-dec-bsd\n"); exit (0); -#endif -#else -#if defined(_SIZE_T_) || defined(SIGLOST) - struct utsname un; - uname (&un); - printf ("vax-dec-ultrix%s\n", un.release); exit (0); -#else - printf ("vax-dec-ultrix\n"); exit (0); -#endif -#endif -#endif -#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) -#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) -#if defined(_SIZE_T_) || defined(SIGLOST) - struct utsname *un; - uname (&un); - printf ("mips-dec-ultrix%s\n", un.release); exit (0); -#else - printf ("mips-dec-ultrix\n"); exit (0); -#endif -#endif -#endif - -#if defined (alliant) && defined (i860) - printf ("i860-alliant-bsd\n"); exit (0); -#endif - - exit (1); -} -EOF - -$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=$($dummy) && - { echo "$SYSTEM_NAME"; exit; } - -# Apollos put the system type in the environment. -test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } - echo "$0: unable to guess system type" >&2 case "$UNAME_MACHINE:$UNAME_SYSTEM" in @@ -1641,12 +1438,6 @@ https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess and https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub -EOF - -year=$(echo $timestamp | sed 's,-.*,,') -# shellcheck disable=SC2003 -if test "$(expr "$(date +%Y)" - "$year")" -lt 3 ; then - cat >&2 </dev/null || echo unknown) -uname -r = $( (uname -r) 2>/dev/null || echo unknown) -uname -s = $( (uname -s) 2>/dev/null || echo unknown) -uname -v = $( (uname -v) 2>/dev/null || echo unknown) - -/usr/bin/uname -p = $( (/usr/bin/uname -p) 2>/dev/null) -/bin/uname -X = $( (/bin/uname -X) 2>/dev/null) - -hostinfo = $( (hostinfo) 2>/dev/null) -/bin/universe = $( (/bin/universe) 2>/dev/null) -/usr/bin/arch -k = $( (/usr/bin/arch -k) 2>/dev/null) -/bin/arch = $( (/bin/arch) 2>/dev/null) -/usr/bin/oslevel = $( (/usr/bin/oslevel) 2>/dev/null) -/usr/convex/getsysinfo = $( (/usr/convex/getsysinfo) 2>/dev/null) +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = "$UNAME_MACHINE" UNAME_RELEASE = "$UNAME_RELEASE" UNAME_SYSTEM = "$UNAME_SYSTEM" UNAME_VERSION = "$UNAME_VERSION" EOF -fi exit 1 diff -Nru mstflint-4.17.0+1/config/config.sub mstflint-4.21.0+1/config/config.sub --- mstflint-4.17.0+1/config/config.sub 2021-06-29 08:51:07.000000000 +0000 +++ mstflint-4.21.0+1/config/config.sub 2022-07-31 09:34:41.000000000 +0000 @@ -1,8 +1,8 @@ #! /bin/sh # Configuration validation subroutine script. -# Copyright 1992-2020 Free Software Foundation, Inc. +# Copyright 1992-2018 Free Software Foundation, Inc. -timestamp='2020-11-07' +timestamp='2018-05-05' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -50,7 +50,7 @@ # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. -me=$(echo "$0" | sed -e 's,.*/,,') +me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS @@ -67,7 +67,7 @@ version="\ GNU config.sub ($timestamp) -Copyright 1992-2020 Free Software Foundation, Inc. +Copyright 1992-2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -89,7 +89,7 @@ - ) # Use stdin as input. break ;; -* ) - echo "$me: invalid option $1$help" >&2 + echo "$me: invalid option $1$help" exit 1 ;; *local*) @@ -110,1167 +110,1249 @@ exit 1;; esac -# Split fields of configuration type -# shellcheck disable=SC2162 -IFS="-" read field1 field2 field3 field4 <&2 - exit 1 - ;; *-*-*-*) basic_machine=$field1-$field2 - basic_os=$field3-$field4 + os=-$field3-$field4 ;; *-*-*) # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two # parts maybe_os=$field2-$field3 case $maybe_os in - nto-qnx* | linux-* | uclinux-uclibc* \ + nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc \ + | linux-newlib* | linux-musl* | linux-uclibc* | uclinux-uclibc* \ | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ | storm-chaos* | os2-emx* | rtmk-nova*) basic_machine=$field1 - basic_os=$maybe_os + os=-$maybe_os ;; android-linux) basic_machine=$field1-unknown - basic_os=linux-android + os=-linux-android ;; *) basic_machine=$field1-$field2 - basic_os=$field3 + os=-$field3 ;; esac ;; *-*) - # A lone config we happen to match not fitting any pattern - case $field1-$field2 in - decstation-3100) - basic_machine=mips-dec - basic_os= - ;; - *-*) - # Second component is usually, but not always the OS - case $field2 in - # Prevent following clause from handling this valid os - sun*os*) - basic_machine=$field1 - basic_os=$field2 - ;; - # Manufacturers - dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ - | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ - | unicom* | ibm* | next | hp | isi* | apollo | altos* \ - | convergent* | ncr* | news | 32* | 3600* | 3100* \ - | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ - | ultra | tti* | harris | dolphin | highlevel | gould \ - | cbm | ns | masscomp | apple | axis | knuth | cray \ - | microblaze* | sim | cisco \ - | oki | wec | wrs | winbond) - basic_machine=$field1-$field2 - basic_os= - ;; - *) - basic_machine=$field1 - basic_os=$field2 - ;; - esac - ;; - esac + basic_machine=$field1 + os=-$field2 ;; *) - # Convert single-component short-hands not valid as part of - # multi-component configurations. - case $field1 in - 386bsd) - basic_machine=i386-pc - basic_os=bsd - ;; - a29khif) - basic_machine=a29k-amd - basic_os=udi - ;; - adobe68k) - basic_machine=m68010-adobe - basic_os=scout - ;; - alliant) - basic_machine=fx80-alliant - basic_os= - ;; - altos | altos3068) - basic_machine=m68k-altos - basic_os= - ;; - am29k) - basic_machine=a29k-none - basic_os=bsd - ;; - amdahl) - basic_machine=580-amdahl - basic_os=sysv - ;; - amiga) - basic_machine=m68k-unknown - basic_os= - ;; - amigaos | amigados) - basic_machine=m68k-unknown - basic_os=amigaos - ;; - amigaunix | amix) - basic_machine=m68k-unknown - basic_os=sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - basic_os=sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - basic_os=bsd - ;; - aros) - basic_machine=i386-pc - basic_os=aros - ;; - aux) - basic_machine=m68k-apple - basic_os=aux - ;; - balance) - basic_machine=ns32k-sequent - basic_os=dynix - ;; - blackfin) - basic_machine=bfin-unknown - basic_os=linux - ;; - cegcc) - basic_machine=arm-unknown - basic_os=cegcc - ;; - convex-c1) - basic_machine=c1-convex - basic_os=bsd - ;; - convex-c2) - basic_machine=c2-convex - basic_os=bsd - ;; - convex-c32) - basic_machine=c32-convex - basic_os=bsd - ;; - convex-c34) - basic_machine=c34-convex - basic_os=bsd - ;; - convex-c38) - basic_machine=c38-convex - basic_os=bsd - ;; - cray) - basic_machine=j90-cray - basic_os=unicos - ;; - crds | unos) - basic_machine=m68k-crds - basic_os= - ;; - da30) - basic_machine=m68k-da30 - basic_os= - ;; - decstation | pmax | pmin | dec3100 | decstatn) - basic_machine=mips-dec - basic_os= - ;; - delta88) - basic_machine=m88k-motorola - basic_os=sysv3 - ;; - dicos) - basic_machine=i686-pc - basic_os=dicos - ;; - djgpp) - basic_machine=i586-pc - basic_os=msdosdjgpp - ;; - ebmon29k) - basic_machine=a29k-amd - basic_os=ebmon - ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - basic_os=ose - ;; - gmicro) - basic_machine=tron-gmicro - basic_os=sysv - ;; - go32) - basic_machine=i386-pc - basic_os=go32 - ;; - h8300hms) - basic_machine=h8300-hitachi - basic_os=hms - ;; - h8300xray) - basic_machine=h8300-hitachi - basic_os=xray - ;; - h8500hms) - basic_machine=h8500-hitachi - basic_os=hms - ;; - harris) - basic_machine=m88k-harris - basic_os=sysv3 - ;; - hp300 | hp300hpux) - basic_machine=m68k-hp - basic_os=hpux - ;; - hp300bsd) - basic_machine=m68k-hp - basic_os=bsd - ;; - hppaosf) - basic_machine=hppa1.1-hp - basic_os=osf - ;; - hppro) - basic_machine=hppa1.1-hp - basic_os=proelf - ;; - i386mach) - basic_machine=i386-mach - basic_os=mach - ;; - isi68 | isi) - basic_machine=m68k-isi - basic_os=sysv - ;; - m68knommu) - basic_machine=m68k-unknown - basic_os=linux - ;; - magnum | m3230) - basic_machine=mips-mips - basic_os=sysv - ;; - merlin) - basic_machine=ns32k-utek - basic_os=sysv - ;; - mingw64) - basic_machine=x86_64-pc - basic_os=mingw64 - ;; - mingw32) - basic_machine=i686-pc - basic_os=mingw32 - ;; - mingw32ce) - basic_machine=arm-unknown - basic_os=mingw32ce - ;; - monitor) - basic_machine=m68k-rom68k - basic_os=coff - ;; - morphos) - basic_machine=powerpc-unknown - basic_os=morphos - ;; - moxiebox) - basic_machine=moxie-unknown - basic_os=moxiebox - ;; - msdos) - basic_machine=i386-pc - basic_os=msdos - ;; - msys) - basic_machine=i686-pc - basic_os=msys - ;; - mvs) - basic_machine=i370-ibm - basic_os=mvs - ;; - nacl) - basic_machine=le32-unknown - basic_os=nacl - ;; - ncr3000) - basic_machine=i486-ncr - basic_os=sysv4 - ;; - netbsd386) - basic_machine=i386-pc - basic_os=netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - basic_os=linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - basic_os=newsos - ;; - news1000) - basic_machine=m68030-sony - basic_os=newsos - ;; - necv70) - basic_machine=v70-nec - basic_os=sysv - ;; - nh3000) - basic_machine=m68k-harris - basic_os=cxux - ;; - nh[45]000) - basic_machine=m88k-harris - basic_os=cxux - ;; - nindy960) - basic_machine=i960-intel - basic_os=nindy - ;; - mon960) - basic_machine=i960-intel - basic_os=mon960 - ;; - nonstopux) - basic_machine=mips-compaq - basic_os=nonstopux - ;; - os400) - basic_machine=powerpc-ibm - basic_os=os400 - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - basic_os=ose - ;; - os68k) - basic_machine=m68k-none - basic_os=os68k - ;; - paragon) - basic_machine=i860-intel - basic_os=osf - ;; - parisc) - basic_machine=hppa-unknown - basic_os=linux - ;; - psp) - basic_machine=mipsallegrexel-sony - basic_os=psp - ;; - pw32) - basic_machine=i586-unknown - basic_os=pw32 - ;; - rdos | rdos64) - basic_machine=x86_64-pc - basic_os=rdos - ;; - rdos32) - basic_machine=i386-pc - basic_os=rdos - ;; - rom68k) - basic_machine=m68k-rom68k - basic_os=coff - ;; - sa29200) - basic_machine=a29k-amd - basic_os=udi - ;; - sei) - basic_machine=mips-sei - basic_os=seiux - ;; - sequent) - basic_machine=i386-sequent - basic_os= - ;; - sps7) - basic_machine=m68k-bull - basic_os=sysv2 - ;; - st2000) - basic_machine=m68k-tandem - basic_os= - ;; - stratus) - basic_machine=i860-stratus - basic_os=sysv4 - ;; - sun2) - basic_machine=m68000-sun - basic_os= - ;; - sun2os3) - basic_machine=m68000-sun - basic_os=sunos3 - ;; - sun2os4) - basic_machine=m68000-sun - basic_os=sunos4 - ;; - sun3) - basic_machine=m68k-sun - basic_os= - ;; - sun3os3) - basic_machine=m68k-sun - basic_os=sunos3 - ;; - sun3os4) - basic_machine=m68k-sun - basic_os=sunos4 - ;; - sun4) - basic_machine=sparc-sun - basic_os= - ;; - sun4os3) - basic_machine=sparc-sun - basic_os=sunos3 - ;; - sun4os4) - basic_machine=sparc-sun - basic_os=sunos4 - ;; - sun4sol2) - basic_machine=sparc-sun - basic_os=solaris2 - ;; - sun386 | sun386i | roadrunner) - basic_machine=i386-sun - basic_os= - ;; - sv1) - basic_machine=sv1-cray - basic_os=unicos - ;; - symmetry) - basic_machine=i386-sequent - basic_os=dynix - ;; - t3e) - basic_machine=alphaev5-cray - basic_os=unicos - ;; - t90) - basic_machine=t90-cray - basic_os=unicos - ;; - toad1) - basic_machine=pdp10-xkl - basic_os=tops20 - ;; - tpf) - basic_machine=s390x-ibm - basic_os=tpf - ;; - udi29k) - basic_machine=a29k-amd - basic_os=udi - ;; - ultra3) - basic_machine=a29k-nyu - basic_os=sym1 - ;; - v810 | necv810) - basic_machine=v810-nec - basic_os=none - ;; - vaxv) - basic_machine=vax-dec - basic_os=sysv - ;; - vms) - basic_machine=vax-dec - basic_os=vms - ;; - vsta) - basic_machine=i386-pc - basic_os=vsta - ;; - vxworks960) - basic_machine=i960-wrs - basic_os=vxworks - ;; - vxworks68) - basic_machine=m68k-wrs - basic_os=vxworks - ;; - vxworks29k) - basic_machine=a29k-wrs - basic_os=vxworks - ;; - xbox) - basic_machine=i686-pc - basic_os=mingw32 - ;; - ymp) - basic_machine=ymp-cray - basic_os=unicos - ;; - *) - basic_machine=$1 - basic_os= - ;; - esac + basic_machine=$1 + os= + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray | -microblaze*) + os= + basic_machine=$1 + ;; + -bluegene*) + os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*178) + os=-lynxos178 + ;; + -lynx*5) + os=-lynxos5 + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo "$1" | sed -e 's/86-.*/86-sequent/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint ;; esac -# Decode 1-component or ad-hoc basic machines +# Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in - # Here we handle the default manufacturer of certain CPU types. It is in - # some cases the only manufacturer, in others, it is the most popular. - w89k) - cpu=hppa1.1 - vendor=winbond + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | aarch64 | aarch64_be \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arceb \ + | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv6m | armv[78][arm] \ + | avr | avr32 \ + | ba \ + | be32 | be64 \ + | bfin \ + | c4x | c8051 | clipper | csky \ + | d10v | d30v | dlx | dsp16xx \ + | e2k | epiphany \ + | fido | fr30 | frv | ft32 \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i860 | i960 | ia16 | ia64 \ + | ip2k | iq2000 \ + | k1om \ + | le32 | le64 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r6 | mipsisa32r6el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r6 | mipsisa64r6el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nfp \ + | nios | nios2 | nios2eb | nios2el \ + | ns16k | ns32k \ + | open8 | or1k | or1knd | or32 \ + | pdp10 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle \ + | pru \ + | pyramid \ + | riscv32 | riscv64 \ + | rl78 | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu \ + | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ + | ubicom32 \ + | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ + | visium \ + | wasm32 \ + | x86 | xc16x | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown ;; - op50n) - cpu=hppa1.1 - vendor=oki + c54x) + basic_machine=tic54x-unknown ;; - op60c) - cpu=hppa1.1 - vendor=oki + c55x) + basic_machine=tic55x-unknown ;; - ibm*) - cpu=i370 - vendor=ibm + c6x) + basic_machine=tic6x-unknown ;; - orion105) - cpu=clipper - vendor=highlevel + leon|leon[3-9]) + basic_machine=sparc-$basic_machine ;; - mac | mpw | mac-mpw) - cpu=m68k - vendor=apple + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) + basic_machine=$basic_machine-unknown + os=-none ;; - pmac | pmac-mpw) - cpu=powerpc - vendor=apple + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65) + ;; + m9s12z | m68hcs12z | hcs12z | s12z) + basic_machine=s12z-unknown + os=-none + ;; + ms1) + basic_machine=mt-unknown ;; + strongarm | thumb | xscale) + basic_machine=arm-unknown + ;; + xgate) + basic_machine=$basic_machine-unknown + os=-none + ;; + xscaleeb) + basic_machine=armeb-unknown + ;; + + xscaleel) + basic_machine=armel-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | aarch64-* | aarch64_be-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | ba-* \ + | be32-* | be64-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* \ + | c8051-* | clipper-* | craynv-* | csky-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | e2k-* | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | hexagon-* \ + | i*86-* | i860-* | i960-* | ia16-* | ia64-* \ + | ip2k-* | iq2000-* \ + | k1om-* \ + | le32-* | le64-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ + | microblaze-* | microblazeel-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64octeon-* | mips64octeonel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64r5900-* | mips64r5900el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa32r6-* | mipsisa32r6el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64r6-* | mipsisa64r6el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipsr5900-* | mipsr5900el-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nds32-* | nds32le-* | nds32be-* \ + | nfp-* \ + | nios-* | nios2-* | nios2eb-* | nios2el-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | open8-* \ + | or1k*-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ + | pru-* \ + | pyramid-* \ + | riscv32-* | riscv64-* \ + | rl78-* | romp-* | rs6000-* | rx-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ + | tahoe-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tile*-* \ + | tron-* \ + | ubicom32-* \ + | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ + | vax-* \ + | visium-* \ + | wasm32-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown + ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-pc + os=-bsd + ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) - cpu=m68000 - vendor=att + basic_machine=m68000-att ;; 3b*) - cpu=we32k - vendor=att + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo "$basic_machine" | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aros) + basic_machine=i386-pc + os=-aros + ;; + asmjs) + basic_machine=asmjs-unknown + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'` + os=-linux ;; bluegene*) - cpu=powerpc - vendor=ibm - basic_os=cnk + basic_machine=powerpc-ibm + os=-cnk + ;; + c54x-*) + basic_machine=tic54x-`echo "$basic_machine" | sed 's/^[^-]*-//'` + ;; + c55x-*) + basic_machine=tic55x-`echo "$basic_machine" | sed 's/^[^-]*-//'` + ;; + c6x-*) + basic_machine=tic6x-`echo "$basic_machine" | sed 's/^[^-]*-//'` + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16 | cr16-*) + basic_machine=cr16-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec ;; decsystem10* | dec10*) - cpu=pdp10 - vendor=dec - basic_os=tops10 + basic_machine=pdp10-dec + os=-tops10 ;; decsystem20* | dec20*) - cpu=pdp10 - vendor=dec - basic_os=tops20 + basic_machine=pdp10-dec + os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) - cpu=m68k - vendor=motorola + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dicos) + basic_machine=i686-pc + os=-dicos + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx ;; dpx2*) - cpu=m68k - vendor=bull - basic_os=sysv3 + basic_machine=m68k-bull + os=-sysv3 ;; - encore | umax | mmax) - cpu=ns32k - vendor=encore + e500v[12]) + basic_machine=powerpc-unknown + os=$os"spe" + ;; + e500v[12]-*) + basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` + os=$os"spe" + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon ;; elxsi) - cpu=elxsi - vendor=elxsi - basic_os=${basic_os:-bsd} + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose ;; fx2800) - cpu=i860 - vendor=alliant + basic_machine=i860-alliant ;; genix) - cpu=ns32k - vendor=ns + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 ;; h3050r* | hiux*) - cpu=hppa1.1 - vendor=hitachi - basic_os=hiuxwe2 + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) - cpu=hppa1.0 - vendor=hp + basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) - cpu=m68000 - vendor=hp + basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) - cpu=m68k - vendor=hp + basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) - cpu=hppa1.0 - vendor=hp + basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) - cpu=hppa1.1 - vendor=hp + basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp - cpu=hppa1.1 - vendor=hp + basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp - cpu=hppa1.1 - vendor=hp + basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) - cpu=hppa1.1 - vendor=hp + basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) - cpu=hppa1.0 - vendor=hp + basic_machine=hppa1.0-hp + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm ;; i*86v32) - cpu=$(echo "$1" | sed -e 's/86.*/86/') - vendor=pc - basic_os=sysv32 + basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` + os=-sysv32 ;; i*86v4*) - cpu=$(echo "$1" | sed -e 's/86.*/86/') - vendor=pc - basic_os=sysv4 + basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` + os=-sysv4 ;; i*86v) - cpu=$(echo "$1" | sed -e 's/86.*/86/') - vendor=pc - basic_os=sysv + basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` + os=-sysv ;; i*86sol2) - cpu=$(echo "$1" | sed -e 's/86.*/86/') - vendor=pc - basic_os=solaris2 - ;; - j90 | j90-cray) - cpu=j90 - vendor=cray - basic_os=${basic_os:-unicos} + basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + vsta) + basic_machine=i386-unknown + os=-vsta ;; iris | iris4d) - cpu=mips - vendor=sgi - case $basic_os in - irix*) + basic_machine=mips-sgi + case $os in + -irix*) ;; *) - basic_os=irix4 + os=-irix4 ;; esac ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + leon-*|leon[3-9]-*) + basic_machine=sparc-`echo "$basic_machine" | sed 's/-.*//'` + ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo "$basic_machine" | sed 's/^[^-]*-//'` + os=-linux + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + microblaze*) + basic_machine=microblaze-xilinx + ;; + mingw64) + basic_machine=x86_64-pc + os=-mingw64 + ;; + mingw32) + basic_machine=i686-pc + os=-mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; miniframe) - cpu=m68000 - vendor=convergent + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint ;; - *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) - cpu=m68k - vendor=atari - basic_os=mint + mips3*-*) + basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + moxiebox) + basic_machine=moxie-unknown + os=-moxiebox + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo "$basic_machine" | sed -e 's/ms1-/mt-/'` + ;; + msys) + basic_machine=i686-pc + os=-msys + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + nacl) + basic_machine=le32-unknown + os=-nacl + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos ;; news-3600 | risc-news) - cpu=mips - vendor=sony - basic_os=newsos + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv ;; next | m*-next) - cpu=m68k - vendor=next - case $basic_os in - openstep*) - ;; - nextstep*) + basic_machine=m68k-next + case $os in + -nextstep* ) ;; - ns2*) - basic_os=nextstep2 + -ns2*) + os=-nextstep2 ;; *) - basic_os=nextstep3 + os=-nextstep3 ;; esac ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; np1) - cpu=np1 - vendor=gould + basic_machine=np1-gould + ;; + neo-tandem) + basic_machine=neo-tandem + ;; + nse-tandem) + basic_machine=nse-tandem + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + nsv-tandem) + basic_machine=nsv-tandem + ;; + nsx-tandem) + basic_machine=nsx-tandem ;; op50n-* | op60c-*) - cpu=hppa1.1 - vendor=oki - basic_os=proelf + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k ;; pa-hitachi) - cpu=hppa1.1 - vendor=hitachi - basic_os=hiuxwe2 + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + parisc) + basic_machine=hppa-unknown + os=-linux + ;; + parisc-*) + basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'` + os=-linux ;; pbd) - cpu=sparc - vendor=tti + basic_machine=sparc-tti ;; pbb) - cpu=m68k - vendor=tti + basic_machine=m68k-tti ;; - pc532) - cpu=ns32k - vendor=pc532 + pc532 | pc532-*) + basic_machine=ns32k-pc532 ;; - pn) - cpu=pn - vendor=gould + pc98) + basic_machine=i386-pc ;; - power) - cpu=power - vendor=ibm + pc98-*) + basic_machine=i386-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; - ps2) - cpu=i386 - vendor=ibm + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc ;; - rm[46]00) - cpu=mips - vendor=siemens + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc ;; - rtpc | rtpc-*) - cpu=romp - vendor=ibm + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc ;; - sde) - cpu=mipsisa32 - vendor=sde - basic_os=${basic_os:-elf} + pentium4) + basic_machine=i786-pc ;; - simso-wrs) - cpu=sparclite - vendor=wrs - basic_os=vxworks + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; - tower | tower-32) - cpu=m68k - vendor=ncr + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; - vpp*|vx|vx-*) - cpu=f301 - vendor=fujitsu + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; - w65) - cpu=w65 - vendor=wdc + pentium4-*) + basic_machine=i786-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; - w89k-*) - cpu=hppa1.1 - vendor=winbond - basic_os=proelf + pn) + basic_machine=pn-gould ;; - none) - cpu=none - vendor=none + power) basic_machine=power-ibm ;; - leon|leon[3-9]) - cpu=sparc - vendor=$basic_machine + ppc | ppcbe) basic_machine=powerpc-unknown ;; - leon-*|leon[3-9]-*) - cpu=sparc - vendor=$(echo "$basic_machine" | sed 's/-.*//') + ppc-* | ppcbe-*) + basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; - - *-*) - # shellcheck disable=SC2162 - IFS="-" read cpu vendor <&2 - exit 1 - ;; - esac + echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 + exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. -case $vendor in - digital*) - vendor=dec +case $basic_machine in + *-digital*) + basic_machine=`echo "$basic_machine" | sed 's/digital.*/dec/'` ;; - commodore*) - vendor=cbm + *-commodore*) + basic_machine=`echo "$basic_machine" | sed 's/commodore.*/cbm/'` ;; *) ;; @@ -1278,213 +1360,209 @@ # Decode manufacturer-specific aliases for certain operating systems. -if test x$basic_os != x +if [ x$os != x ] then - -# First recognize some ad-hoc caes, or perhaps split kernel-os, or else just -# set os. -case $basic_os in - gnu/linux*) - kernel=linux - os=$(echo $basic_os | sed -e 's|gnu/linux|gnu|') - ;; - os2-emx) - kernel=os2 - os=$(echo $basic_os | sed -e 's|os2-emx|emx|') - ;; - nto-qnx*) - kernel=nto - os=$(echo $basic_os | sed -e 's|nto-qnx|qnx|') - ;; - *-*) - # shellcheck disable=SC2162 - IFS="-" read kernel os <&2 + exit 1 ;; esac - else # Here we handle the default operating systems that come with various machines. @@ -1497,352 +1575,254 @@ # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. -kernel= -case $cpu-$vendor in +case $basic_machine in score-*) - os=elf + os=-elf ;; spu-*) - os=elf + os=-elf ;; *-acorn) - os=riscix1.2 + os=-riscix1.2 ;; arm*-rebel) - kernel=linux - os=gnu + os=-linux ;; arm*-semi) - os=aout + os=-aout ;; c4x-* | tic4x-*) - os=coff + os=-coff ;; c8051-*) - os=elf - ;; - clipper-intergraph) - os=clix + os=-elf ;; hexagon-*) - os=elf + os=-elf ;; tic54x-*) - os=coff + os=-coff ;; tic55x-*) - os=coff + os=-coff ;; tic6x-*) - os=coff + os=-coff ;; # This must come before the *-dec entry. pdp10-*) - os=tops20 + os=-tops20 ;; pdp11-*) - os=none + os=-none ;; *-dec | vax-*) - os=ultrix4.2 + os=-ultrix4.2 ;; m68*-apollo) - os=domain + os=-domain ;; i386-sun) - os=sunos4.0.2 + os=-sunos4.0.2 ;; m68000-sun) - os=sunos3 + os=-sunos3 ;; m68*-cisco) - os=aout + os=-aout ;; mep-*) - os=elf + os=-elf ;; mips*-cisco) - os=elf + os=-elf ;; mips*-*) - os=elf + os=-elf ;; or32-*) - os=coff + os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. - os=sysv3 + os=-sysv3 ;; sparc-* | *-sun) - os=sunos4.1.1 + os=-sunos4.1.1 ;; pru-*) - os=elf + os=-elf ;; *-be) - os=beos + os=-beos ;; *-ibm) - os=aix + os=-aix ;; *-knuth) - os=mmixware + os=-mmixware ;; *-wec) - os=proelf + os=-proelf ;; *-winbond) - os=proelf + os=-proelf ;; *-oki) - os=proelf + os=-proelf ;; *-hp) - os=hpux + os=-hpux ;; *-hitachi) - os=hiux + os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) - os=sysv + os=-sysv ;; *-cbm) - os=amigaos + os=-amigaos ;; *-dg) - os=dgux + os=-dgux ;; *-dolphin) - os=sysv3 + os=-sysv3 ;; m68k-ccur) - os=rtu + os=-rtu ;; m88k-omron*) - os=luna + os=-luna ;; *-next) - os=nextstep + os=-nextstep ;; *-sequent) - os=ptx + os=-ptx ;; *-crds) - os=unos + os=-unos ;; *-ns) - os=genix + os=-genix ;; i370-*) - os=mvs + os=-mvs ;; *-gould) - os=sysv + os=-sysv ;; *-highlevel) - os=bsd + os=-bsd ;; *-encore) - os=bsd + os=-bsd ;; *-sgi) - os=irix + os=-irix ;; *-siemens) - os=sysv4 + os=-sysv4 ;; *-masscomp) - os=rtu + os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) - os=uxpv + os=-uxpv ;; *-rom68k) - os=coff + os=-coff ;; *-*bug) - os=coff + os=-coff ;; *-apple) - os=macos + os=-macos ;; *-atari*) - os=mint - ;; - *-wrs) - os=vxworks + os=-mint ;; *) - os=none + os=-none ;; esac - fi -# Now, validate our (potentially fixed-up) OS. -case $os in - # Sometimes we do "kernel-abi", so those need to count as OSes. - musl* | newlib* | uclibc*) - ;; - # Likewise for "kernel-libc" - eabi | eabihf | gnueabi | gnueabihf) - ;; - # Now accept the basic system types. - # The portable systems comes first. - # Each alternative MUST end in a * to match a version number. - gnu* | android* | bsd* | mach* | minix* | genix* | ultrix* | irix* \ - | *vms* | esix* | aix* | cnk* | sunos | sunos[34]* \ - | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ - | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ - | hiux* | abug | nacl* | netware* | windows* \ - | os9* | macos* | osx* | ios* \ - | mpw* | magic* | mmixware* | mon960* | lnews* \ - | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ - | aos* | aros* | cloudabi* | sortix* | twizzler* \ - | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \ - | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \ - | mirbsd* | netbsd* | dicos* | openedition* | ose* \ - | bitrig* | openbsd* | solidbsd* | libertybsd* | os108* \ - | ekkobsd* | freebsd* | riscix* | lynxos* | os400* \ - | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \ - | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \ - | udi* | lites* | ieee* | go32* | aux* | hcos* \ - | chorusrdb* | cegcc* | glidix* \ - | cygwin* | msys* | pe* | moss* | proelf* | rtems* \ - | midipix* | mingw32* | mingw64* | mint* \ - | uxpv* | beos* | mpeix* | udk* | moxiebox* \ - | interix* | uwin* | mks* | rhapsody* | darwin* \ - | openstep* | oskit* | conix* | pw32* | nonstopux* \ - | storm-chaos* | tops10* | tenex* | tops20* | its* \ - | os2* | vos* | palmos* | uclinux* | nucleus* | morphos* \ - | scout* | superux* | sysv* | rtmk* | tpf* | windiss* \ - | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \ - | skyos* | haiku* | rdos* | toppers* | drops* | es* \ - | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ - | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ - | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx*) - ;; - # This one is extra strict with allowed versions - sco3.2v2 | sco3.2v[4-9]* | sco5v6*) - # Don't forget version if it is 3.2v4 or newer. - ;; - none) - ;; - *) - echo Invalid configuration \`"$1"\': OS \`"$os"\' not recognized 1>&2 - exit 1 - ;; -esac - -# As a final step for OS-related things, validate the OS-kernel combination -# (given a valid OS), if there is a kernel. -case $kernel-$os in - linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* | linux-musl* | linux-uclibc* ) - ;; - uclinux-uclibc* ) - ;; - -dietlibc* | -newlib* | -musl* | -uclibc* ) - # These are just libc implementations, not actual OSes, and thus - # require a kernel. - echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2 - exit 1 - ;; - kfreebsd*-gnu* | kopensolaris*-gnu*) - ;; - nto-qnx*) - ;; - os2-emx) - ;; - *-eabi* | *-gnueabi*) - ;; - -*) - # Blank kernel with real OS is always fine. - ;; - *-*) - echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2 - exit 1 - ;; -esac - # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. -case $vendor in - unknown) - case $cpu-$os in - *-riscix*) +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) vendor=acorn ;; - *-sunos*) + -sunos*) vendor=sun ;; - *-cnk* | *-aix*) + -cnk*|-aix*) vendor=ibm ;; - *-beos*) + -beos*) vendor=be ;; - *-hpux*) + -hpux*) vendor=hp ;; - *-mpeix*) + -mpeix*) vendor=hp ;; - *-hiux*) + -hiux*) vendor=hitachi ;; - *-unos*) + -unos*) vendor=crds ;; - *-dgux*) + -dgux*) vendor=dg ;; - *-luna*) + -luna*) vendor=omron ;; - *-genix*) + -genix*) vendor=ns ;; - *-clix*) - vendor=intergraph - ;; - *-mvs* | *-opened*) - vendor=ibm - ;; - *-os400*) + -mvs* | -opened*) vendor=ibm ;; - s390-* | s390x-*) + -os400*) vendor=ibm ;; - *-ptx*) + -ptx*) vendor=sequent ;; - *-tpf*) + -tpf*) vendor=ibm ;; - *-vxsim* | *-vxworks* | *-windiss*) + -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; - *-aux*) + -aux*) vendor=apple ;; - *-hms*) + -hms*) vendor=hitachi ;; - *-mpw* | *-macos*) + -mpw* | -macos*) vendor=apple ;; - *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; - *-vos*) + -vos*) vendor=stratus ;; esac + basic_machine=`echo "$basic_machine" | sed "s/unknown/$vendor/"` ;; esac -echo "$cpu-$vendor-${kernel:+$kernel-}$os" +echo "$basic_machine$os" exit # Local variables: diff -Nru mstflint-4.17.0+1/config/depcomp mstflint-4.21.0+1/config/depcomp --- mstflint-4.17.0+1/config/depcomp 2021-06-29 08:51:07.000000000 +0000 +++ mstflint-4.21.0+1/config/depcomp 2022-07-31 09:34:41.000000000 +0000 @@ -3,7 +3,7 @@ scriptversion=2018-03-07.03; # UTC -# Copyright (C) 1999-2020 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff -Nru mstflint-4.17.0+1/config/install-sh mstflint-4.21.0+1/config/install-sh --- mstflint-4.17.0+1/config/install-sh 2021-06-29 08:51:07.000000000 +0000 +++ mstflint-4.21.0+1/config/install-sh 2022-07-31 09:34:41.000000000 +0000 @@ -1,7 +1,7 @@ #!/bin/sh # install - install a program, script, or datafile -scriptversion=2020-11-14.01; # UTC +scriptversion=2018-03-11.20; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the @@ -69,11 +69,6 @@ # Desired mode of installed file. mode=0755 -# Create dirs (including intermediate dirs) using mode 755. -# This is like GNU 'install' as of coreutils 8.32 (2020). -mkdir_umask=22 - -backupsuffix= chgrpcmd= chmodcmd=$chmodprog chowncmd= @@ -104,28 +99,18 @@ --version display version info and exit. -c (ignored) - -C install only if different (preserve data modification time) + -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. - -p pass -p to $cpprog. -s $stripprog installed files. - -S SUFFIX attempt to back up existing files, with suffix SUFFIX. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG - -By default, rm is invoked with -f; when overridden with RMPROG, -it's up to you to specify -f if you want it. - -If -S is not specified, no backups are attempted. - -Email bug reports to bug-automake@gnu.org. -Automake home page: https://www.gnu.org/software/automake/ " while test $# -ne 0; do @@ -152,13 +137,8 @@ -o) chowncmd="$chownprog $2" shift;; - -p) cpprog="$cpprog -p";; - -s) stripcmd=$stripprog;; - -S) backupsuffix="$2" - shift;; - -t) is_target_a_directory=always dst_arg=$2 @@ -275,10 +255,6 @@ dstdir=$dst test -d "$dstdir" dstdir_status=$? - # Don't chown directories that already exist. - if test $dstdir_status = 0; then - chowncmd="" - fi else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command @@ -325,6 +301,22 @@ if test $dstdir_status != 0; then case $posix_mkdir in '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then @@ -334,49 +326,52 @@ fi posix_mkdir=false - # The $RANDOM variable is not portable (e.g., dash). Use it - # here however when possible just to lower collision chance. - tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ - - trap ' - ret=$? - rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null - exit $ret - ' 0 - - # Because "mkdir -p" follows existing symlinks and we likely work - # directly in world-writeable /tmp, make sure that the '$tmpdir' - # directory is successfully created first before we actually test - # 'mkdir -p'. - if (umask $mkdir_umask && - $mkdirprog $mkdir_mode "$tmpdir" && - exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 - then - if test -z "$dir_arg" || { - # Check for POSIX incompatibilities with -m. - # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or - # other-writable bit of parent directory when it shouldn't. - # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. - test_tmpdir="$tmpdir/a" - ls_ld_tmpdir=`ls -ld "$test_tmpdir"` - case $ls_ld_tmpdir in - d????-?r-*) different_mode=700;; - d????-?--*) different_mode=755;; - *) false;; - esac && - $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { - ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` - test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" - } - } - then posix_mkdir=: - fi - rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" - else - # Remove any dirs left behind by ancient mkdir implementations. - rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null - fi - trap '' 0;; + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + # Note that $RANDOM variable is not portable (e.g. dash); Use it + # here however when possible just to lower collision chance. + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + + trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0 + + # Because "mkdir -p" follows existing symlinks and we likely work + # directly in world-writeable /tmp, make sure that the '$tmpdir' + # directory is successfully created first before we actually test + # 'mkdir -p' feature. + if (umask $mkdir_umask && + $mkdirprog $mkdir_mode "$tmpdir" && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + test_tmpdir="$tmpdir/a" + ls_ld_tmpdir=`ls -ld "$test_tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null + fi + trap '' 0;; + esac;; esac if @@ -387,7 +382,7 @@ then : else - # mkdir does not conform to POSIX, + # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. @@ -416,7 +411,7 @@ prefixes= else if $posix_mkdir; then - (umask $mkdir_umask && + (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 @@ -456,18 +451,7 @@ trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. - (umask $cp_umask && - { test -z "$stripcmd" || { - # Create $dsttmp read-write so that cp doesn't create it read-only, - # which would cause strip to fail. - if test -z "$doit"; then - : >"$dsttmp" # No need to fork-exec 'touch'. - else - $doit touch "$dsttmp" - fi - } - } && - $doit_exec $cpprog "$src" "$dsttmp") && + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # @@ -493,13 +477,6 @@ then rm -f "$dsttmp" else - # If $backupsuffix is set, and the file being installed - # already exists, attempt a backup. Don't worry if it fails, - # e.g., if mv doesn't support -f. - if test -n "$backupsuffix" && test -f "$dst"; then - $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null - fi - # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || @@ -514,9 +491,9 @@ # file should still install successfully. { test ! -f "$dst" || - $doit $rmcmd "$dst" 2>/dev/null || + $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && - { $doit $rmcmd "$rmtmp" 2>/dev/null; :; } + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 diff -Nru mstflint-4.17.0+1/config/ltmain.sh mstflint-4.21.0+1/config/ltmain.sh --- mstflint-4.17.0+1/config/ltmain.sh 2021-06-29 08:51:03.000000000 +0000 +++ mstflint-4.21.0+1/config/ltmain.sh 2022-07-31 09:34:40.000000000 +0000 @@ -7272,10 +7272,12 @@ # -tp=* Portland pgcc target processor selection # --sysroot=* for sysroot support # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization + # -specs=* GCC specs files # -stdlib=* select c++ std lib with clang -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ - -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*) + -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \ + -specs=*) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result func_append compile_command " $arg" diff -Nru mstflint-4.17.0+1/config/missing mstflint-4.21.0+1/config/missing --- mstflint-4.17.0+1/config/missing 2021-06-29 08:51:07.000000000 +0000 +++ mstflint-4.21.0+1/config/missing 2022-07-31 09:34:41.000000000 +0000 @@ -3,7 +3,7 @@ scriptversion=2018-03-07.03; # UTC -# Copyright (C) 1996-2020 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify diff -Nru mstflint-4.17.0+1/configure mstflint-4.21.0+1/configure --- mstflint-4.17.0+1/configure 2021-06-29 08:51:15.000000000 +0000 +++ mstflint-4.21.0+1/configure 2022-07-31 09:34:43.000000000 +0000 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for mstflint 4.17.0. +# Generated by GNU Autoconf 2.69 for mstflint 4.21.0. # # Report bugs to . # @@ -590,8 +590,8 @@ # Identity of this package. PACKAGE_NAME='mstflint' PACKAGE_TARNAME='mstflint' -PACKAGE_VERSION='4.17.0' -PACKAGE_STRING='mstflint 4.17.0' +PACKAGE_VERSION='4.21.0' +PACKAGE_STRING='mstflint 4.21.0' PACKAGE_BUGREPORT='eranj@mellanox.co.il' PACKAGE_URL='' @@ -1391,7 +1391,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures mstflint 4.17.0 to adapt to many kinds of systems. +\`configure' configures mstflint 4.21.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1461,7 +1461,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of mstflint 4.17.0:";; + short | recursive ) echo "Configuration of mstflint 4.21.0:";; esac cat <<\_ACEOF @@ -1596,7 +1596,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -mstflint configure 4.17.0 +mstflint configure 4.21.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2177,7 +2177,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by mstflint $as_me 4.17.0, which was +It was created by mstflint $as_me 4.21.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2535,7 +2535,7 @@ cat >>confdefs.h <<_ACEOF -#define VERSION "4.17.0" +#define VERSION "4.21.0" _ACEOF @@ -2747,7 +2747,12 @@ am_aux_dir=`cd "$ac_aux_dir" && pwd` if test x"${MISSING+set}" != xset; then - MISSING="\${SHELL} '$am_aux_dir/missing'" + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then @@ -3052,7 +3057,7 @@ # Define the identity of the package. PACKAGE='mstflint' - VERSION='4.17.0' + VERSION='4.21.0' cat >>confdefs.h <<_ACEOF @@ -11790,6 +11795,9 @@ # before this can be enabled. hardcode_into_libs=yes + # Add ABI-specific directories to the system library path. + sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib" + # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command @@ -11798,7 +11806,7 @@ # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + sys_lib_dlsearch_path_spec="$sys_lib_dlsearch_path_spec $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on @@ -15752,6 +15760,9 @@ # before this can be enabled. hardcode_into_libs=yes + # Add ABI-specific directories to the system library path. + sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib" + # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command @@ -15760,7 +15771,7 @@ # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + sys_lib_dlsearch_path_spec="$sys_lib_dlsearch_path_spec $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on @@ -16124,9 +16135,32 @@ ac_config_headers="$ac_config_headers config.h" -OS=$(uname -s) +# OPENSSL_VERSION_NUMBER < 0x100020bf +#0x100020bfL = OpenSSL 1.0.2k - 26 Jan 2017 +MIN_OPENSSL_VER="1.0.2k" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#if OPENSSL_VERSION_NUMBER < 0x100020bf + ERROR: OPENSSL_VERSION_NUMBER version must be >= 0x100020bf ("OpenSSL 1.0.2k - 26 Jan 2017") +#endif + +int +main () +{ -MIN_OPENSSL_VER="1.1.0" + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + OPENSSL_VERSION_VALID=yes +else + OPENSSL_VERSION_VALID=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +OS=$(uname -s) MTCR_CONF_DIR="" LDL="" default_en_inband="" @@ -17058,7 +17092,7 @@ JSON_SYSTEM_AVAILABLE="no" JSON_CFLAGS='-I$(top_srcdir)/ext_libs/json' - JSON_LIBS='$(top_builddir)/ext_libs/json/libjson.a' + JSON_LIBS='$(top_builddir)/ext_libs/json/libjson.la' elif test $pkg_failed = untried; then @@ -17068,7 +17102,7 @@ JSON_SYSTEM_AVAILABLE="no" JSON_CFLAGS='-I$(top_srcdir)/ext_libs/json' - JSON_LIBS='$(top_builddir)/ext_libs/json/libjson.a' + JSON_LIBS='$(top_builddir)/ext_libs/json/libjson.la' else @@ -17233,9 +17267,21 @@ fi + CFLAGS="$CFLAGS -DMST_UL" CXXFLAGS="$CXXFLAGS -DMST_UL" + +CCVER=`${CC} --version | grep ${CC} | grep -E -o "( )[4-9]\.[0-9]\.[0-9]" | head -1` +{ $as_echo "$as_me:${as_lineno-$LINENO}: Compiler: ${CC}; version: ${CCVER};" >&5 +$as_echo "$as_me: Compiler: ${CC}; version: ${CCVER};" >&6;} +if test ${CCVER} \> 5.0.0 ; then + CXXFLAGS="$CXXFLAGS -std=c++14" + CFLAGS="$CFLAGS -fgnu89-inline" +elif test ${CCVER} \> 4.7.2 ; then + CXXFLAGS="$CXXFLAGS -std=c++11" +fi + AM_CFLAGS="-Wall" AM_CXXFLAGS="-Wall" @@ -17553,9 +17599,10 @@ fi + if test "x$enable_openssl" = "xyes"; then : - if test x"`(openssl version|awk '{print $2}';echo $MIN_OPENSSL_VER)|sort -V|head -n1`" = x"$MIN_OPENSSL_VER"; then : + if test x"$OPENSSL_VERSION_VALID" = "xyes"; then : ac_config_files="$ac_config_files mlxsign_lib/Makefile" else @@ -18215,7 +18262,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by mstflint $as_me 4.17.0, which was +This file was extended by mstflint $as_me 4.21.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -18281,7 +18328,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -mstflint config.status 4.17.0 +mstflint config.status 4.21.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -19528,9 +19575,7 @@ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "Something went wrong bootstrapping makefile fragments - for automatic dependency tracking. If GNU make was not used, consider - re-running the configure script with MAKE=\"gmake\" (or whatever is - necessary). You can also try re-running configure with the + for automatic dependency tracking. Try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking). See \`config.log' for more details" "$LINENO" 5; } diff -Nru mstflint-4.17.0+1/configure.ac mstflint-4.21.0+1/configure.ac --- mstflint-4.17.0+1/configure.ac 2021-06-29 08:49:48.000000000 +0000 +++ mstflint-4.21.0+1/configure.ac 2022-07-31 09:33:57.000000000 +0000 @@ -31,12 +31,12 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT(mstflint, 4.17.0, eranj@mellanox.co.il) +AC_INIT(mstflint, 4.21.0, eranj@mellanox.co.il) AC_DEFINE_UNQUOTED([PROJECT], ["mstflint"], [Define the project name.]) AC_SUBST([PROJECT]) -AC_DEFINE_UNQUOTED([VERSION], ["4.17.0"], [Define the project version.]) +AC_DEFINE_UNQUOTED([VERSION], ["4.21.0"], [Define the project version.]) AC_SUBST([VERSION]) AC_CONFIG_MACRO_DIR([m4]) @@ -51,9 +51,17 @@ AC_PROG_LIBTOOL AC_CONFIG_HEADERS( config.h ) +# OPENSSL_VERSION_NUMBER < 0x100020bf +#0x100020bfL = OpenSSL 1.0.2k - 26 Jan 2017 +MIN_OPENSSL_VER="1.0.2k" +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[ +#include +#if OPENSSL_VERSION_NUMBER < 0x100020bf + ERROR: OPENSSL_VERSION_NUMBER version must be >= 0x100020bf ("OpenSSL 1.0.2k - 26 Jan 2017") +#endif +]])], [OPENSSL_VERSION_VALID=yes], [OPENSSL_VERSION_VALID=no]) OS=$(uname -s) - -MIN_OPENSSL_VER="1.1.0" MTCR_CONF_DIR="" LDL="" default_en_inband="" @@ -275,7 +283,7 @@ JSON_SYSTEM_AVAILABLE="no" JSON_CFLAGS='-I$(top_srcdir)/ext_libs/json' AC_SUBST(JSON_CFLAGS) - JSON_LIBS='$(top_builddir)/ext_libs/json/libjson.a' + JSON_LIBS='$(top_builddir)/ext_libs/json/libjson.la' AC_SUBST(JSON_LIBS) ]) AM_CONDITIONAL([USE_LOCAL_JSON], [test "$JSON_SYSTEM_AVAILABLE" = no]) @@ -298,9 +306,20 @@ ]) AM_CONDITIONAL([USE_LOCAL_SQLITE], [test "$SQLITE_SYSTEM_AVAILABLE" = no]) + CFLAGS="$CFLAGS -DMST_UL" CXXFLAGS="$CXXFLAGS -DMST_UL" + +CCVER=`${CC} --version | grep ${CC} | grep -E -o "( )[[4-9]]\.[[0-9]]\.[[0-9]]" | head -1` +AC_MSG_NOTICE(Compiler: ${CC}; version: ${CCVER};) +if test ${CCVER} \> 5.0.0 ; then + CXXFLAGS="$CXXFLAGS -std=c++14" + CFLAGS="$CFLAGS -fgnu89-inline" +elif test ${CCVER} \> 4.7.2 ; then + CXXFLAGS="$CXXFLAGS -std=c++11" +fi + AM_CFLAGS="-Wall" AM_CXXFLAGS="-Wall" @@ -356,8 +375,9 @@ AC_CONFIG_FILES(tools_crypto/Makefile) ]) + AS_IF([test "x$enable_openssl" = "xyes"], [ - AS_IF(test x"`(openssl version|awk '{print $2}';echo $MIN_OPENSSL_VER)|sort -V|head -n1`" = x"$MIN_OPENSSL_VER", + AS_IF(test x"$OPENSSL_VERSION_VALID" = "xyes", [AC_CONFIG_FILES(mlxsign_lib/Makefile)], [AC_MSG_ERROR([Required OpenSSL version >= $MIN_OPENSSL_VER (or use --disable-openssl)])]) ]) diff -Nru mstflint-4.17.0+1/debian/changelog mstflint-4.21.0+1/debian/changelog --- mstflint-4.17.0+1/debian/changelog 2021-12-14 07:49:32.000000000 +0000 +++ mstflint-4.21.0+1/debian/changelog 2022-08-17 09:12:52.000000000 +0000 @@ -1,14 +1,12 @@ -mstflint (4.17.0+1-1build2) jammy; urgency=medium +mstflint (4.21.0+1-1) unstable; urgency=medium - * No-change rebuild against libjsoncpp25 + * New upstream release: + - 0003-Fix-man-page-errors.patch removed: merged upstream + - malloc_delete_free.patch removed: merged upstream + * Add man-proper-header-line.patch + * Add typos.patch - -- Steve Langasek Tue, 14 Dec 2021 07:49:32 +0000 - -mstflint (4.17.0+1-1build1) jammy; urgency=medium - - * No-change rebuild against openssl3 - - -- Simon Chopin Mon, 29 Nov 2021 13:46:35 +0100 + -- Tzafrir Cohen Wed, 17 Aug 2022 12:12:52 +0300 mstflint (4.17.0+1-1) unstable; urgency=medium diff -Nru mstflint-4.17.0+1/debian/control mstflint-4.21.0+1/debian/control --- mstflint-4.17.0+1/debian/control 2021-12-14 07:49:31.000000000 +0000 +++ mstflint-4.21.0+1/debian/control 2022-08-17 02:42:23.000000000 +0000 @@ -1,11 +1,9 @@ Source: mstflint Section: net Priority: optional -Maintainer: Ubuntu Developers -XSBC-Original-Maintainer: Debian HPC Team +Maintainer: Debian HPC Team Uploaders: Mehdi Dogguy , - Tzafrir Cohen , - Benjamin Drung + Tzafrir Cohen Build-Depends: debhelper-compat (= 13), libboost-filesystem-dev, libboost-regex-dev, @@ -17,6 +15,7 @@ libmuparser-dev, libsqlite3-dev, libssl-dev, + libiniparser-dev, pkg-config, zlib1g-dev Standards-Version: 4.5.1 diff -Nru mstflint-4.17.0+1/debian/patches/0001-Remove-date-and-time-from-version-string-to-ensure-r.patch mstflint-4.21.0+1/debian/patches/0001-Remove-date-and-time-from-version-string-to-ensure-r.patch --- mstflint-4.17.0+1/debian/patches/0001-Remove-date-and-time-from-version-string-to-ensure-r.patch 2020-08-26 21:16:29.000000000 +0000 +++ mstflint-4.21.0+1/debian/patches/0001-Remove-date-and-time-from-version-string-to-ensure-r.patch 2022-08-14 19:53:52.000000000 +0000 @@ -11,18 +11,17 @@ index 32fe3c7..7ce75ce 100644 --- a/common/tools_version.h +++ b/common/tools_version.h -@@ -74,10 +74,8 @@ int get_version_string(char *buf, int buf_size, const char *exe_name, const char +@@ -74,8 +74,8 @@ int get_version_string(char *buf, int buf_size, const char *exe_name, const char len = snprintf(buf, buf_size, "%s %s, ", exe_name, tool_version); } // cut out first and last "$" from the SVN version string: -- len += snprintf(buf + len, buf_size - len, "%s, built on %s, %s. Git SHA Hash: %s", -+ len += snprintf(buf + len, buf_size - len, "%s. Git SHA Hash: %s", - MSTFLINT_VERSION_STR, -- __DATE__, -- __TIME__, - TOOLS_GIT_SHA); +- len += snprintf(buf + len, buf_size - len, "%s, built on %s, %s. Git SHA Hash: %s", MSTFLINT_VERSION_STR, __DATE__, +- __TIME__, TOOLS_GIT_SHA); ++ len += snprintf(buf + len, buf_size - len, "%s, Git SHA Hash: %s", MSTFLINT_VERSION_STR, ++ TOOLS_GIT_SHA); return len; } + -- 2.20.1 diff -Nru mstflint-4.17.0+1/debian/patches/0003-Fix-man-page-errors.patch mstflint-4.21.0+1/debian/patches/0003-Fix-man-page-errors.patch --- mstflint-4.17.0+1/debian/patches/0003-Fix-man-page-errors.patch 2021-01-31 19:21:28.000000000 +0000 +++ mstflint-4.21.0+1/debian/patches/0003-Fix-man-page-errors.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,909 +0,0 @@ -From 488174b55a3e77557aac69f25d63f485ede960e5 Mon Sep 17 00:00:00 2001 -From: Benjamin Drung -Date: Tue, 3 Jan 2017 20:01:35 +0100 -Subject: Fix man page errors - -lintian found some issues with the man pages. Fix the reported issue, -but do not start the needed full adjustment of the formatting. - -Bug: https://github.com/Mellanox/mstflint/issues/38 -Signed-off-by: Benjamin Drung ---- - man/mstarchive.1 | 2 +- - man/mstconfig.1 | 72 +++++++++++----------- - man/mstcongestion.1 | 2 +- - man/mstflint.1 | 138 ++++++++++++++++++++---------------------- - man/mstfwmanager.1 | 2 +- - man/mstfwreset.1 | 2 +- - man/mstfwtrace.1 | 2 +- - man/mstlink.1 | 30 ++++----- - man/mstmcra.1 | 2 +- - man/mstmread.1 | 2 +- - man/mstmtserver.1 | 2 +- - man/mstmwrite.1 | 2 +- - man/mstprivhost.1 | 2 +- - man/mstreg.1 | 2 +- - man/mstregdump.1 | 25 ++++---- - man/mstresourcedump.1 | 2 +- - man/mstvpd.1 | 2 +- - 17 files changed, 140 insertions(+), 151 deletions(-) - -Index: mstflint/man/mstarchive.1 -=================================================================== ---- mstflint.orig/man/mstarchive.1 2020-08-27 18:56:17.541016533 +0300 -+++ mstflint/man/mstarchive.1 2020-08-27 18:56:17.537016558 +0300 -@@ -1,7 +1,7 @@ - .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. - .TH MSTARCHIVE "1" "March 2020" "mstarchive NAME" "User Commands" - .SH NAME --mstarchive \- manual page for mstarchive NAME -+mstarchive \- generate MFA2 archives - .SH DESCRIPTION - NAME - .IP -Index: mstflint/man/mstconfig.1 -=================================================================== ---- mstflint.orig/man/mstconfig.1 2020-08-27 18:56:17.541016533 +0300 -+++ mstflint/man/mstconfig.1 2020-08-27 18:56:17.537016558 +0300 -@@ -1,14 +1,14 @@ - .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. - .TH MSTCONFIG, "1" "March 2020" "mstconfig, mstflint 4.14.0, built on Mar 3 2020, 14:01:56. Git SHA Hash: N/A" "User Commands" - .SH NAME --mstconfig, \- manual page for mstconfig, mstflint 4.14.0, built on Mar 3 2020, 14:01:56. Git SHA Hash: N/A -+mstconfig \- set or query non-volatile configurable options for Mellanox HCAs - .SH DESCRIPTION - .IP --NAME: -+NAME - .IP - mstconfig - .IP --SYNOPSIS: -+SYNOPSIS - .IP - mstconfig [Options] [Parameters] - .IP -@@ -20,78 +20,78 @@ - OPTIONS: - .TP - \fB\-d\fR|\-\-dev --: Perform operation for a specified MST device. -+Perform operation for a specified MST device. - .TP - \fB\-b\fR|\-\-db --: Use a specific database file. -+Use a specific database file. - .TP - \fB\-f\fR|\-\-file --: raw configuration file. -+raw configuration file. - .TP - \fB\-h\fR|\-\-help --: Display help message. -+Display help message. - .TP - \fB\-v\fR|\-\-version --: Display version info. -+Display version info. - .TP - \fB\-e\fR|\-\-enable_verbosity --: Show default and current configurations. -+Show default and current configurations. - .TP - \fB\-y\fR|\-\-yes --: Answer yes in prompt. -+Answer yes in prompt. - .TP - \fB\-a\fR|\-\-all_attrs --: Show all attributes in the XML template -+Show all attributes in the XML template - .TP - \fB\-p\fR|\-\-private_key --: pem file for private key -+pem file for private key - .TP - \fB\-u\fR|\-\-key_uuid --: keypair uuid --.IP --COMMANDS: -+keypair uuid -+.SH -+COMMANDS - .TP - clear_semaphore --: clear the tool semaphore. -+clear the tool semaphore. - .TP - i[show_confs] --: display information about all configurations. -+display information about all configurations. - .TP - q[uery] --: query supported configurations. -+query supported configurations. - .TP - r[eset] --: reset all configurations to their default value. -+reset all configurations to their default value. - .TP - s[et] --: set configurations to a specific device. -+set configurations to a specific device. - .TP - set_raw --: set raw configuration file.(only Connect\-IB/Connect\-X4/LX.) -+set raw configuration file.(only Connect\-IB/Connect\-X4/LX.) - .TP - backup --: backup configurations to a file (only Connect\-IB/Connect\-X4/LX.). Use set_raw command to restore file. -+backup configurations to a file (only Connect\-IB/Connect\-X4/LX.). Use set_raw command to restore file. - .TP - gen_tlvs_file --: Generate List of all TLVs. TLVs output file name must be specified. (*) -+Generate List of all TLVs. TLVs output file name must be specified. (*) - .TP - g[en_xml_template] --: Generate XML template. TLVs input file name and XML output file name must be specified. (*) -+Generate XML template. TLVs input file name and XML output file name must be specified. (*) - .TP - xml2raw --: Generate Raw file from XML file. XML input file name and raw output file name must be specified. (*) -+Generate Raw file from XML file. XML input file name and raw output file name must be specified. (*) - .TP - raw2xml --: Generate XML file from Raw file. raw input file name and XML output file name must be specified. (*) -+Generate XML file from Raw file. raw input file name and XML output file name must be specified. (*) - .TP - xml2bin --: Generate Bin file from XML file. XML input file name and bin output file name must be specified. (*) -+Generate Bin file from XML file. XML input file name and bin output file name must be specified. (*) - .TP - create_conf --: Generate Configuration file from XML file. XML input file name and bin output file name must be specified. (*) -+Generate Configuration file from XML file. XML input file name and bin output file name must be specified. (*) - .TP - apply --: Apply a Configuration file. bin input file name must be specified. (*) -+Apply a Configuration file. bin input file name must be specified. (*) - .IP - (*) These commands do not require MST device - .IP -@@ -100,16 +100,16 @@ - Examples: - .TP - To query configurations --: mstconfig \fB\-d\fR 04:00.0 query -+mstconfig \fB\-d\fR 04:00.0 query - .TP - To set configuration --: mstconfig \fB\-d\fR 04:00.0 set SRIOV_EN=1 NUM_OF_VFS=16 WOL_MAGIC_EN_P1=1 -+mstconfig \fB\-d\fR 04:00.0 set SRIOV_EN=1 NUM_OF_VFS=16 WOL_MAGIC_EN_P1=1 - .TP - To set raw configuration --: mstconfig \fB\-d\fR 05:00.0 \fB\-f\fR conf_file set_raw -+mstconfig \fB\-d\fR 05:00.0 \fB\-f\fR conf_file set_raw - .TP - To reset configuration --: mstconfig \fB\-d\fR 04:00.0 reset -+mstconfig \fB\-d\fR 04:00.0 reset - .IP - Supported devices: - .IP -@@ -119,13 +119,13 @@ - Note: query device to view supported configurations by Firmware. - .SH "SEE ALSO" - The full documentation for --.B mstconfig, -+.B mstconfig - is maintained as a Texinfo manual. If the - .B info - and --.B mstconfig, -+.B mstconfig - programs are properly installed at your site, the command - .IP --.B info mstconfig, -+.B info mstconfig - .PP - should give you access to the complete manual. -Index: mstflint/man/mstcongestion.1 -=================================================================== ---- mstflint.orig/man/mstcongestion.1 2020-08-27 18:56:17.541016533 +0300 -+++ mstflint/man/mstcongestion.1 2020-08-27 18:56:17.537016558 +0300 -@@ -1,7 +1,7 @@ - .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. - .TH MSTCONGESTION, "1" "March 2020" "mstcongestion, mstflint 4.14.0, built on Mar 3 2020, 14:02:45. Git SHA Hash: N/A" "User Commands" - .SH NAME --mstcongestion, \- manual page for mstcongestion, mstflint 4.14.0, built on Mar 3 2020, 14:02:45. Git SHA Hash: N/A -+mstcongestion \- utility for configuring Mellanox device's receive congestion handling - .SH DESCRIPTION - NAME - .IP -Index: mstflint/man/mstflint.1 -=================================================================== ---- mstflint.orig/man/mstflint.1 2020-08-27 18:56:17.541016533 +0300 -+++ mstflint/man/mstflint.1 2020-08-27 18:56:17.537016558 +0300 -@@ -1,15 +1,14 @@ - .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. - .TH MSTFLINT, "1" "March 2020" "mstflint, mstflint 4.14.0, built on Mar 3 2020, 14:02:39. Git SHA Hash: N/A" "User Commands" - .SH NAME --mstflint, \- manual page for mstflint, mstflint 4.14.0, built on Mar 3 2020, 14:02:39. Git SHA Hash: N/A -+mstflint \- Flash Interface - .SH DESCRIPTION - NAME --.IP --MstFlint \- Flash Interface --.PP -+mstflint \- Flash Interface -+.SH - SYNOPSIS - .IP --MstFlint [OPTIONS] [Parameters] -+mstflint [OPTIONS] [Parameters] - .IP - [\-d|\-\-device ] [\-i|\-\-image ] [\-\-latest_fw] [\-\-ir] [\-h|\-\-help] [\-\-hh] - [\-y|\-\-yes] [\-\-no] [\-\-guid ] [\-\-guids ] [\-\-mac ] -@@ -27,17 +26,15 @@ - .IP - flint is a FW (firmware) burning and flash memory operations tool for Mellanox Infiniband - HCAs,Ethernet NIC cards, and switch devices. --.PP -+.SH - OPTIONS --.IP --MstFlint [OPTIONS] [Parameters] - .TP - \fB\-d\fR|\-\-device --: Device flash is connected to. -+Device flash is connected to. - Commands affected: all - .TP - \fB\-i\fR|\-\-image --: Binary image file. -+Binary image file. - Commands affected: burn, verify - .TP - \fB\-\-latest_fw\fR -@@ -47,32 +44,32 @@ - : Commands affected: burn - .TP - \fB\-h\fR|\-\-help --: Prints this message and exits -+Prints this message and exits - .TP - \fB\-\-hh\fR --: Prints extended command help -+Prints extended command help - .TP - \fB\-y\fR|\-\-yes --: Non interactive mode \- assume answer "yes" to -+Non interactive mode \- assume answer "yes" to - all questions. - Commands affected: all - .TP - \fB\-\-no\fR --: Non interactive mode \- assume answer "no" to -+Non interactive mode \- assume answer "no" to - all questions. - Commands affected: all - .TP - \fB\-\-guid\fR --: GUID base value. 4 GUIDs are automatically -+GUID base value. 4 GUIDs are automatically - assigned to the following values: - .TP - guid - \-> node GUID --.IP -+.TP - guid+1 \-> port1 - guid+2 \-> port2 - guid+3 \-> system image GUID. --.IP -+.TP - Note: port2 guid will be assigned even for a - single port HCA \- The HCA ignores this - value. -@@ -80,11 +77,11 @@ - Commands affected: burn, sg - .TP - \fB\-\-guids\fR --: 4 GUIDs must be specified here. -+4 GUIDs must be specified here. - The specified GUIDs are assigned to the - following fields, respectively: - node, port1, port2 and system image GUID. --.IP -+.TP - Note: port2 guid must be specified even for - a single port HCA \- The HCA ignores this - value. -@@ -93,7 +90,7 @@ - Commands affected: burn, sg - .TP - \fB\-\-mac\fR --: MAC address base value. 2 MACs are -+MAC address base value. 2 MACs are - automatically assigned to the following - values: - .TP -@@ -106,25 +103,25 @@ - Commands affected: burn, sg - .TP - \fB\-\-macs\fR --: 2 MACs must be specified here. -+2 MACs must be specified here. - The specified MACs are assigned to port1, - port2, respectively. - Commands affected: burn, sg --.IP -+.TP - Note: \fB\-mac\fR/\-macs flags are applicable only - for Mellanox - .IP - Technologies ethernet products. - .TP - \fB\-\-uid\fR --: ConnectIB/SwitchIB only. Derive and set the -+ConnectIB/SwitchIB only. Derive and set the - device UIDs (GUIDs, MACs, WWNs). - UIDs are derived from the given base UID - according to Mellanox Methodology - Commands affected: burn, sg - .TP - \fB\-\-blank_guids\fR --: Burn the image with blank GUIDs and MACs -+Burn the image with blank GUIDs and MACs - (where applicable). These values can be set - later using the "sg" command (see details - below). -@@ -132,7 +129,7 @@ - Commands affected: burn - .TP - \fB\-\-clear_semaphore\fR --: Force clear the flash semaphore on the -+Force clear the flash semaphore on the - device. - No command is allowed when this flag is - used. -@@ -142,7 +139,7 @@ - Exercise caution. - .TP - \fB\-\-qq\fR --: Run a quick query. When specified, flint will -+Run a quick query. When specified, flint will - not perform full image integrity checks - during the query operation. This may shorten - execution time when running over slow -@@ -160,15 +157,15 @@ - Commands affected: query - .TP - \fB\-\-nofs\fR --: Burn image in a non failsafe manner. -+Burn image in a non failsafe manner. - .TP - \fB\-\-allow_rom_change\fR --: Allow burning/removing a ROM to/from FW image -+Allow burning/removing a ROM to/from FW image - when product version is present. - Use only if you know what you are doing - .TP - \fB\-\-override_cache_replacement\fR --: On SwitchX/ConnectIB devices: -+On SwitchX/ConnectIB devices: - Allow accessing the flash even if the cache - replacement mode is enabled. - NOTE: This flag is intended for advanced -@@ -177,74 +174,74 @@ - to hang. - .TP - \fB\-\-no_flash_verify\fR --: Do not verify each write on the flash. -+Do not verify each write on the flash. - .TP - \fB\-\-use_fw\fR --: Flash access will be done using FW -+Flash access will be done using FW - (ConnectX\-3/ConnectX\-3Pro only). - .TP - \fB\-s\fR|\-\-silent --: Do not print burn progress flyer. -+Do not print burn progress flyer. - Commands affected: burn - .TP - \fB\-\-vsd\fR --: Write this string, of up to 208 characters, -+Write this string, of up to 208 characters, - to VSD when burn. - .TP - \fB\-\-use_image_ps\fR --: Burn vsd as appears in the given image \- do -+Burn vsd as appears in the given image \- do - not keep existing VSD on flash. - Commands affected: burn - .TP - \fB\-\-use_image_guids\fR --: Burn (guids/macs) as appears in the given -+Burn (guids/macs) as appears in the given - image. - Commands affected: burn - .TP - \fB\-\-use_image_rom\fR --: Do not save the ROM which exists in the -+Do not save the ROM which exists in the - device. - Commands affected: burn - .TP - \fB\-\-use_dev_rom\fR --: Save the ROM which exists in the device. -+Save the ROM which exists in the device. - Commands affected: burn - .TP - \fB\-\-ignore_dev_data\fR --: Do not attempt to take device data sections -+Do not attempt to take device data sections - from device(sections will be taken from the - image. FS3 Only). - Commands affected: burn - .TP - \fB\-\-no_fw_ctrl\fR --: Do not attempt to work with the FW Ctrl -+Do not attempt to work with the FW Ctrl - update commands - .TP - \fB\-\-dual_image\fR --: Make the burn process burn two images on -+Make the burn process burn two images on - flash (previously default algorithm). - Current default failsafe burn process burns - a single image (in alternating locations). - Commands affected: burn - .TP - \fB\-\-striped_image\fR --: Use this flag to indicate that the given -+Use this flag to indicate that the given - image file is in a "striped image" format. - Commands affected: query verify - .TP - \fB\-\-banks\fR --: Set the number of attached flash devices -+Set the number of attached flash devices - (banks) - .TP - \fB\-\-log\fR --: Print the burning status to the specified log -+Print the burning status to the specified log - file - .HP - \fB\-\-flash_params\fR --: Use the given parameters to access the flash --.IP -+Use the given parameters to access the flash -+.TP - instead of reading them from the flash. - Supported parameters: - Type: The type of the flash, such -@@ -257,47 +254,47 @@ - flashes connected to the device. - .TP - \fB\-v\fR|\-\-version --: Version info. -+Version info. - .TP - \fB\-\-private_key\fR --: path to PEM formatted private key to be used -+path to PEM formatted private key to be used - by the sign command - .TP - \fB\-\-key_uuid\fR --: UUID matching the given private key to be -+UUID matching the given private key to be - used by the sign command - .TP - \fB\-\-private_key2\fR --: path to PEM formatted private key to be used -+path to PEM formatted private key to be used - by the sign command - .TP - \fB\-\-hmac_key\fR --: path to file containing key (For FS4 image -+path to file containing key (For FS4 image - only). - .TP - \fB\-\-key_uuid2\fR --: UUID matching the given private key to be -+UUID matching the given private key to be - used by the sign command - .PP - COMMANDS SUMMARY - .TP - burn|b \fB\-ir\fR --: Burn flash. Use "\-ir burn" flag to perform -+Burn flash. Use "\-ir burn" flag to perform - .IP - image reactivation prior burning. - .TP - query|q [full] --: Query misc. flash/firmware characteristics, -+Query misc. flash/firmware characteristics, - .IP - use "full" to get more information. - .TP - verify|v [showitoc] --: Verify entire flash, use "showitoc" to see -+Verify entire flash, use "showitoc" to see - .IP - ITOC headers in FS3/FS4 image only. - .TP - swreset --: SW reset the target switch device.This -+SW reset the target switch device.This - .IP - command is supported only in the In\-Band - access method. -@@ -306,13 +303,13 @@ - : Burn the specified ROM file on the flash. - .TP - drom --: Remove the ROM section from the flash. -+Remove the ROM section from the flash. - .TP - rrom - : Read the ROM section from the flash. - .TP - bb --: Burn Block \- Burns the given image as is. No -+Burn Block \- Burns the given image as is. No - .IP - checks are done. - .TP -@@ -334,7 +331,7 @@ - .TP - set_key - [key] : Set/Update the HW access key which is used to --.IP -+.TP - enable/disable access to HW. - The key can be provided in the command line - or interactively typed after the command is -@@ -344,7 +341,7 @@ - .TP - hw_access - [key] : Enable/disable the access to the HW. --.IP -+.TP - The key can be provided in the command line - or interactively typed after the command is - given -@@ -353,7 +350,7 @@ - query : Query HW info and flash attributes. - .TP - erase|e --: Erases sector. -+Erases sector. - .TP - rw - : Read one dword from flash -@@ -378,13 +375,13 @@ - [out\-file] : Read a data block from flash - .TP - clear_semaphore --: Clear flash semaphore. -+Clear flash semaphore. - .TP - qrom --: query ROM image. -+query ROM image. - .TP - checksum|cs --: perform MD5 checksum on FW. -+perform MD5 checksum on FW. - .IP - timestamp|ts [timestamp] - [FW version] : FW time stamping. -@@ -407,28 +404,27 @@ - .IP - given BIN file. If there is a silent mode, - no progress is displayed. --.PP -+.SH - RETURN VALUES - .TP - 0 --: Successful completion. -+Successful completion. - .TP - 1 --: An error has occurred. -+An error has occurred. - .TP - 7 --: For burn command \- FW already updated \- burn --.IP -+For burn command \- FW already updated \- burn - was aborted. - .SH "SEE ALSO" - The full documentation for --.B mstflint, -+.B mstflint - is maintained as a Texinfo manual. If the - .B info - and --.B mstflint, -+.B mstflint - programs are properly installed at your site, the command - .IP --.B info mstflint, -+.B info mstflint - .PP - should give you access to the complete manual. -Index: mstflint/man/mstfwmanager.1 -=================================================================== ---- mstflint.orig/man/mstfwmanager.1 2020-08-27 18:56:17.541016533 +0300 -+++ mstflint/man/mstfwmanager.1 2020-08-27 18:56:17.537016558 +0300 -@@ -1,7 +1,7 @@ - .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. - .TH MSTFWMANAGER "1" "March 2020" "mstfwmanager 1.0, mstflint 4.14.0, built on Mar 3 2020, 14:02:20. Git SHA Hash: N/A" "User Commands" - .SH NAME --mstfwmanager \- manual page for mstfwmanager 1.0, mstflint 4.14.0, built on Mar 3 2020, 14:02:20. Git SHA Hash: N/A -+mstfwmanager \- Mellanox Firmware Manager - .SH DESCRIPTION - NAME - .IP -Index: mstflint/man/mstfwreset.1 -=================================================================== ---- mstflint.orig/man/mstfwreset.1 2020-08-27 18:56:17.541016533 +0300 -+++ mstflint/man/mstfwreset.1 2020-08-27 18:56:17.537016558 +0300 -@@ -1,7 +1,7 @@ - .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. - .TH MSTFWRESET "1" "March 2020" "mstfwreset 1.0.0, mstflint 4.14.0, built on Mar 03 2020, 14:01:10. Git SHA Hash: N/A" "User Commands" - .SH NAME --mstfwreset \- manual page for mstfwreset 1.0.0, mstflint 4.14.0, built on Mar 03 2020, 14:01:10. Git SHA Hash: N/A -+mstfwreset \- Query and perform reset operation on the device - .SH DESCRIPTION - usage: mstfwreset \fB\-\-device\fR DEVICE [\-\-level {0,3,4,5}] [\-\-type {0,1}] [\-\-yes] - .IP -Index: mstflint/man/mstfwtrace.1 -=================================================================== ---- mstflint.orig/man/mstfwtrace.1 2020-08-27 18:56:17.541016533 +0300 -+++ mstflint/man/mstfwtrace.1 2020-08-27 18:56:17.537016558 +0300 -@@ -1,7 +1,7 @@ - .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. - .TH MSTFWTRACE, "1" "March 2020" "mstfwtrace, mstflint 4.14.0, built on Mar 03 2020, 14:01:10. Git SHA Hash: N/A" "User Commands" - .SH NAME --mstfwtrace, \- manual page for mstfwtrace, mstflint 4.14.0, built on Mar 03 2020, 14:01:10. Git SHA Hash: N/A -+mstfwtrace \- Extracts and prints trace messages generated by the firmware of 5th generation devices - .SH DESCRIPTION - usage: mstfwtrace \fB\-d\fR|\-\-device DEVICE [options] - .SS "optional arguments:" -Index: mstflint/man/mstlink.1 -=================================================================== ---- mstflint.orig/man/mstlink.1 2020-08-27 18:56:17.541016533 +0300 -+++ mstflint/man/mstlink.1 2020-08-27 18:56:17.537016558 +0300 -@@ -1,23 +1,16 @@ - .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. - .TH MSTLINK, "1" "March 2020" "mstlink, mstflint 4.14.0, built on Mar 3 2020, 14:03:31. Git SHA Hash: N/A" "User Commands" - .SH NAME --mstlink, \- manual page for mstlink, mstflint 4.14.0, built on Mar 3 2020, 14:03:31. Git SHA Hash: N/A --.SH DESCRIPTION --.IP --NAME: --.IP --mstlink --.IP --SYNOPSIS: --.IP -+mstlink \- check and debug link status and issues related to them -+.SH -+SYNOPSIS - mstlink [OPTIONS] --.IP --DESCRIPTION: --.IP --The mstlink tool is used to check and debug link status and issues related to them. -+.SH -+DESCRIPTION -+The mlxlink tool is used to check and debug link status and issues related to them. - The tool can be used on different links and cables (passive, active, transceiver and backplane). --.IP --OPTIONS: -+.SH -+OPTIONS - .TP - \fB\-h\fR |\-\-help - : Display help message. -@@ -136,8 +129,8 @@ - .TP - \fB\-\-twisted_pair_force_mode\fR - : Twisted Pair Force Mode [MA(Master)/SL(Slave)] --.IP --Examples: -+.SH -+EXAMPLES - .TP - get info of , - : -@@ -179,7 +172,8 @@ - .IP - Configure Transmitter Parameters (on lane, to database): - .IP --mstlink \fB\-d\fR \fB\-p\fR \fB\-\-serdes_tx\fR ,,,,, (\fB\-\-serdes_tx_lane\fR ) (\fB\-\-database\fR) -+mstlink \fB\-d\fR \fB\-p\fR (\fB\-\-serdes_tx_lane\fR ) (\fB\-\-database\fR) \\ -+ \fB\-\-serdes_tx\fR ,,,,, - .SH "SEE ALSO" - The full documentation for - .B mstlink, -Index: mstflint/man/mstmcra.1 -=================================================================== ---- mstflint.orig/man/mstmcra.1 2020-08-27 18:56:17.541016533 +0300 -+++ mstflint/man/mstmcra.1 2020-08-27 18:56:17.537016558 +0300 -@@ -1,7 +1,7 @@ - .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. - .TH MSTMCRA "1" "March 2020" "mstmcra Mellanox Configuration Registers Access tool" "User Commands" - .SH NAME --mstmcra \- manual page for mstmcra Mellanox Configuration Registers Access tool -+mstmcra \- Mellanox Configuration Registers Access tool - .SH DESCRIPTION - .IP - Mellanox Configuration Registers Access tool -Index: mstflint/man/mstmread.1 -=================================================================== ---- mstflint.orig/man/mstmread.1 2020-08-27 18:56:17.541016533 +0300 -+++ mstflint/man/mstmread.1 2020-08-27 18:56:17.537016558 +0300 -@@ -1,7 +1,7 @@ - .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. - .TH MSTMREAD "1" "March 2020" "mstmread " "User Commands" - .SH NAME --mstmread \- manual page for mstmread -+mstmread \- Mellanox Read Configuration Register Tool - .SH DESCRIPTION - mstmread - .SH "SEE ALSO" -Index: mstflint/man/mstmwrite.1 -=================================================================== ---- mstflint.orig/man/mstmwrite.1 2020-08-27 18:56:17.541016533 +0300 -+++ mstflint/man/mstmwrite.1 2020-08-27 18:56:17.537016558 +0300 -@@ -1,7 +1,7 @@ - .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. - .TH MSTMWRITE "1" "March 2020" "mstmwrite " "User Commands" - .SH NAME --mstmwrite \- manual page for mstmwrite -+mstmwrite \- Mellanox Write Configuration Register Tool - .SH DESCRIPTION - mstmwrite - .SH "SEE ALSO" -Index: mstflint/man/mstprivhost.1 -=================================================================== ---- mstflint.orig/man/mstprivhost.1 2020-08-27 18:56:17.541016533 +0300 -+++ mstflint/man/mstprivhost.1 2020-08-27 18:56:17.537016558 +0300 -@@ -1,7 +1,7 @@ - .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. - .TH MSTPRIVHOST "1" "March 2020" "mstprivhost 1.0.0, mstflint 4.14.0, built on Mar 03 2020, 14:01:10. Git SHA Hash: N/A" "User Commands" - .SH NAME --mstprivhost \- manual page for mstprivhost 1.0.0, mstflint 4.14.0, built on Mar 03 2020, 14:01:10. Git SHA Hash: N/A -+mstprivhost \- privilege modification tool - .SH DESCRIPTION - usage: mstprivhost [\-h] [\-v] \fB\-\-device\fR DEVICE [\-\-disable_rshim] - .IP -Index: mstflint/man/mstreg.1 -=================================================================== ---- mstflint.orig/man/mstreg.1 2020-08-27 18:56:17.541016533 +0300 -+++ mstflint/man/mstreg.1 2020-08-27 18:56:17.537016558 +0300 -@@ -1,7 +1,7 @@ - .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. - .TH MSTREG, "1" "March 2020" "mstreg, mstflint 4.14.0, built on Mar 3 2020, 14:03:18. Git SHA Hash: N/A" "User Commands" - .SH NAME --mstreg, \- manual page for mstreg, mstflint 4.14.0, built on Mar 3 2020, 14:03:18. Git SHA Hash: N/A -+mstreg \- Expose supported access registers - .SH DESCRIPTION - .IP - NAME: -Index: mstflint/man/mstregdump.1 -=================================================================== ---- mstflint.orig/man/mstregdump.1 2020-08-27 18:56:17.541016533 +0300 -+++ mstflint/man/mstregdump.1 2020-08-27 18:56:17.537016558 +0300 -@@ -1,34 +1,33 @@ - .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. - .TH MSTREGDUMP, "1" "March 2020" "mstregdump, mstflint 4.14.0, built on Mar 3 2020, 14:02:48. Git SHA Hash: N/A" "User Commands" - .SH NAME --mstregdump, \- manual page for mstregdump, mstflint 4.14.0, built on Mar 3 2020, 14:02:48. Git SHA Hash: N/A --.SH DESCRIPTION --.IP --Mellanox mstregdump utility, dumps device internal configuration data --Usage: mstregdump [\-full] [i2c\-slave] [\-v[ersion] [\-h[elp]]] -+mstdump \- Mellanox mstdump utility, dumps device internal configuration data -+.SH SYNOPSIS -+mstregdump [\-full] [i2c\-slave] [\-v[ersion] [\-h[elp]]] -+.SH OPTIONS - .TP - \fB\-full\fR --: Dump more expanded list of addresses --Note : be careful when using this flag, None safe addresses might be read. -+Dump more expanded list of addresses -+Note: be careful when using this flag, None safe addresses might be read. - .TP - \fB\-v\fR | \fB\-\-version\fR --: Display version info -+Display version info - .TP - \fB\-h\fR | \fB\-\-help\fR --: Print this help message -+Print this help message - .IP --Example : -+.SH EXAMPLE - .IP - mstregdump 0b:00.0 - .SH "SEE ALSO" - The full documentation for --.B mstregdump, -+.B mstregdump - is maintained as a Texinfo manual. If the - .B info - and --.B mstregdump, -+.B mstregdump - programs are properly installed at your site, the command - .IP --.B info mstregdump, -+.B info mstregdump - .PP - should give you access to the complete manual. -Index: mstflint/man/mstresourcedump.1 -=================================================================== ---- mstflint.orig/man/mstresourcedump.1 2020-08-27 18:56:17.541016533 +0300 -+++ mstflint/man/mstresourcedump.1 2020-08-27 18:56:17.537016558 +0300 -@@ -1,7 +1,7 @@ - .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. - .TH MSTRESOURCEDUMP, "1" "March 2020" "mstresourcedump, mstflint 4.14.0, built on Mar 03 2020, 14:01:10. Git SHA Hash: N/A" "User Commands" - .SH NAME --mstresourcedump, \- manual page for mstresourcedump, mstflint 4.14.0, built on Mar 03 2020, 14:01:10. Git SHA Hash: N/A -+mstresourcedump \- dump resource information - .SH DESCRIPTION - usage: mstresourcedump [\-h] [\-v] {dump,query} ... - .SS "optional arguments:" -Index: mstflint/man/mstvpd.1 -=================================================================== ---- mstflint.orig/man/mstvpd.1 2020-08-27 18:56:17.541016533 +0300 -+++ mstflint/man/mstvpd.1 2020-08-27 18:56:17.537016558 +0300 -@@ -1,7 +1,7 @@ - .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. - .TH USAGE: "1" "March 2020" "Usage: mstvpd [-m|-n|-r] [-t ##] [-- keyword ...]" "User Commands" - .SH NAME --Usage: \- manual page for Usage: mstvpd [-m|-n|-r] [-t ##] [-- keyword ...] -+mstvpd \- Mellanox VPD read tool - .SH SYNOPSIS - .B mstvpd - [\fI-m|-n|-r\fR] [\fI-t ##\fR] \fI \fR[\fI-- keyword \fR...] -Index: mstflint/man/mstmtserver.1 -=================================================================== ---- mstflint.orig/man/mstmtserver.1 2020-08-27 19:32:00.975383603 +0300 -+++ mstflint/man/mstmtserver.1 2020-08-27 19:47:05.879028344 +0300 -@@ -1,7 +1,7 @@ - .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. - .TH MSTMTSERVER "1" "March 2020" - .SH NAME --mstmtserver -+mstmtserver \- mstflint mst mt server - .SH DESCRIPTION - Usage: - .IP -@@ -15,15 +15,4 @@ - \fB\-h[elp]\fR \- Print help message. - .HP - \fB\-v[ersion]\fR \- Print version. --.SH "SEE ALSO" --The full documentation for --.B mstmtserver --is maintained as a Texinfo manual. If the --.B info --and --.B mstmtserver --programs are properly installed at your site, the command --.IP --info mstmtserver --.PP --should give you access to the complete manual. -+ diff -Nru mstflint-4.17.0+1/debian/patches/0005-Add-missing-shebang-to-Python-scripts.patch mstflint-4.21.0+1/debian/patches/0005-Add-missing-shebang-to-Python-scripts.patch --- mstflint-4.17.0+1/debian/patches/0005-Add-missing-shebang-to-Python-scripts.patch 2021-10-27 08:24:40.000000000 +0000 +++ mstflint-4.21.0+1/debian/patches/0005-Add-missing-shebang-to-Python-scripts.patch 2022-08-14 19:58:35.000000000 +0000 @@ -14,36 +14,33 @@ small_utils/mstfwreset.py | 2 ++ 3 files changed, 6 insertions(+) -diff --git a/mlxconfig/mstprivhost.py b/mlxconfig/mstprivhost.py -index 4f143e4..9a33666 100644 ---- a/mlxconfig/mstprivhost.py -+++ b/mlxconfig/mstprivhost.py +Index: mstflint/mlxconfig/mstprivhost.py +=================================================================== +--- mstflint.orig/mlxconfig/mstprivhost.py 2022-08-14 22:58:23.864895708 +0300 ++++ mstflint/mlxconfig/mstprivhost.py 2022-08-14 22:58:23.864895708 +0300 @@ -1,3 +1,5 @@ +#!/usr/bin/python3 + - #copyright (c) 2004-2021 Mellanox Technologies LTD. All rights reserved. - # - # This software is available to you under a choice of one of two -diff --git a/resourcedump/mstresourcedump.py b/resourcedump/mstresourcedump.py -index 8b5883a..2dd7290 100755 ---- a/resourcedump/mstresourcedump.py -+++ b/resourcedump/mstresourcedump.py + # Copyright (c) 2004-2021 Mellanox Technologies LTD. All rights reserved. + # Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + # +Index: mstflint/resourcedump/mstresourcedump.py +=================================================================== +--- mstflint.orig/resourcedump/mstresourcedump.py 2022-08-14 22:58:23.864895708 +0300 ++++ mstflint/resourcedump/mstresourcedump.py 2022-08-14 22:58:23.864895708 +0300 @@ -1,3 +1,5 @@ +#!/usr/bin/python3 + - # Copyright (C) Jan 2020 Mellanox Technologies Ltd. All rights reserved. - # - # This software is available to you under a choice of one of two -diff --git a/small_utils/mstfwreset.py b/small_utils/mstfwreset.py -index 9a07078..b25905d 100755 ---- a/small_utils/mstfwreset.py -+++ b/small_utils/mstfwreset.py + # Copyright (C) Jan 2020 Mellanox Technologies Ltd. All rights reserved. + # Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + # +Index: mstflint/small_utils/mstfwreset.py +=================================================================== +--- mstflint.orig/small_utils/mstfwreset.py 2022-08-14 22:58:23.864895708 +0300 ++++ mstflint/small_utils/mstfwreset.py 2022-08-14 22:58:23.864895708 +0300 @@ -1,3 +1,5 @@ +#!/usr/bin/python3 + # Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved. + # Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # - # This software is available to you under a choice of one of two --- -2.20.1 - diff -Nru mstflint-4.17.0+1/debian/patches/cross.patch mstflint-4.21.0+1/debian/patches/cross.patch --- mstflint-4.17.0+1/debian/patches/cross.patch 2021-01-31 19:15:24.000000000 +0000 +++ mstflint-4.21.0+1/debian/patches/cross.patch 2022-08-14 19:59:10.000000000 +0000 @@ -2,9 +2,11 @@ Subject: Fix Makefile dependencies on cross-build Bug: https://bugs.debian.org/956757 ---- mstflint-4.13.3+2.orig/mlxlink/Makefile.am -+++ mstflint-4.13.3+2/mlxlink/Makefile.am -@@ -64,10 +64,9 @@ +Index: mstflint/mlxlink/Makefile.am +=================================================================== +--- mstflint.orig/mlxlink/Makefile.am 2022-08-14 22:58:45.256896413 +0300 ++++ mstflint/mlxlink/Makefile.am 2022-08-14 22:58:45.252896412 +0300 +@@ -67,10 +67,9 @@ $(USER_DIR)/reg_access/libreg_access.a \ $(LAYOUTS_DIR)/libtools_layouts.a \ $(USER_DIR)/xz_utils/libxz_utils.a \ diff -Nru mstflint-4.17.0+1/debian/patches/fix_interpreters.patch mstflint-4.21.0+1/debian/patches/fix_interpreters.patch --- mstflint-4.17.0+1/debian/patches/fix_interpreters.patch 2021-02-03 10:39:43.000000000 +0000 +++ mstflint-4.21.0+1/debian/patches/fix_interpreters.patch 2022-08-14 20:05:54.000000000 +0000 @@ -8,16 +8,16 @@ @@ -1,3 +1,5 @@ +#!/usr/bin/python3 + - # Copyright (C) Jan 2020 Mellanox Technologies Ltd. All rights reserved. - # - # This software is available to you under a choice of one of two + # Copyright (C) Jan 2020 Mellanox Technologies Ltd. All rights reserved. + # Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + # Index: mstflint/tracers/fwtrace/mstfwtrace.py =================================================================== --- mstflint.orig/tracers/fwtrace/mstfwtrace.py 2021-01-31 17:52:10.755023493 +0200 +++ mstflint/tracers/fwtrace/mstfwtrace.py 2021-02-03 12:37:18.940241577 +0200 -@@ -1,4 +1,4 @@ --#!/usr/bin/env python +@@ -1,3 +1,5 @@ +#!/usr/bin/env python3 - # -- - # - # Copyright (C) 2019 Mellanox Technologies Ltd. All rights reserved. ++ + # Copyright (c) 2019-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + # + # This software is available to you under a choice of one of two diff -Nru mstflint-4.17.0+1/debian/patches/malloc_delete_free.patch mstflint-4.21.0+1/debian/patches/malloc_delete_free.patch --- mstflint-4.17.0+1/debian/patches/malloc_delete_free.patch 2021-11-04 05:51:47.000000000 +0000 +++ mstflint-4.21.0+1/debian/patches/malloc_delete_free.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,125 +0,0 @@ -From: Tzafrir Cohen -Subject: Free malloc() allocations using free(), not delete - -https://github.com/Mellanox/mstflint/pull/447 -(Should be included in 4.18) - -diff --git a/mlxlink/modules/mlxlink_cables_commander.cpp b/mlxlink/modules/mlxlink_cables_commander.cpp -index 2a5c241..0bfa865 100644 ---- a/mlxlink/modules/mlxlink_cables_commander.cpp -+++ b/mlxlink/modules/mlxlink_cables_commander.cpp -@@ -95,7 +95,7 @@ void MlxlinkCablesCommander::writeMCIA(u_int32_t page, u_int32_t size, - sprintf(fieldName, "dword[%d]", i); - updateField(fieldName, __cpu_to_be32(dwordData[i])); - } -- delete dwordData; -+ free(dwordData); - genBuffSendRegister(regName, MACCESS_REG_METHOD_SET); - } - -@@ -304,9 +304,9 @@ void MlxlinkCablesCommander::prepareQsfpddDdmInfo() - loadEEPRMPage(PAGE_02, UPPER_PAGE_OFFSET, page2H); - getQsfpDdThresholds(page2H); - -- delete page0L; -- delete page11H; -- delete page2H; -+ free(page0L); -+ free(page11H); -+ free(page2H); - } - - void MlxlinkCablesCommander::getSfpDdThresholds(u_int8_t *page0H) -@@ -394,8 +394,8 @@ void MlxlinkCablesCommander::prepareSfpddDdmInfo() - loadEEPRMPage(PAGE_0, UPPER_PAGE_OFFSET, page0H); - getSfpDdThresholds(page0H); - -- delete page0H; -- delete page0L; -+ free(page0H); -+ free(page0L); - } - - // Preparing QSFP DDM information -@@ -459,7 +459,7 @@ void MlxlinkCablesCommander::prepareQSFPDdmInfo() - QSFP_TX_BIAS_1_HALARM); - } - -- delete page0L; -+ free(page0L); - } - - // Preparing SFP DDM information -@@ -516,7 +516,7 @@ void MlxlinkCablesCommander::prepareSFPDdmInfo() - _cableDdm.rx_power[0].high_alarm_flag = getStatusBit(SFP_CHANNELS, tempVal, - SFP51_RX_PWR_HALRM_FLG); - -- delete sfp51Page; -+ free(sfp51Page); - } - - // Preparing thresholds information -@@ -599,7 +599,7 @@ void MlxlinkCablesCommander::readCableDDMInfo() - - fixThresholdBytes(); - -- delete thresholdPage; -+ free(thresholdPage); - } - - // Preparing and formating DDM flags section -@@ -818,7 +818,7 @@ void MlxlinkCablesCommander::initValidPages() - loadEEPRMPage(PAGE_0, UPPER_PAGE_OFFSET, page0H); - u_int8_t optionsValue = 0; - readFromPage(page0H, 195 - 0x80, &optionsValue); -- delete page0H; -+ free(page0H); - if (optionsValue & 0x40) { //check bit 6 for support page_01 - p = (page_t){PAGE_01, UPPER_PAGE_OFFSET, I2C_ADDR_LOW}; - _validPages.push_back(p); -@@ -848,7 +848,7 @@ void MlxlinkCablesCommander::initValidPages() - u_int8_t *page0L = (u_int8_t*)malloc(sizeof(u_int8_t) * CABLE_PAGE_SIZE); - loadEEPRMPage(PAGE_0, LOWER_PAGE_OFFSET, page0L);; - readFromPage(page0L, EXTENDED_PAGES_1_2_10_11_ADDR, &extendedPages); -- delete page0L; -+ free(page0L); - if (!(extendedPages & EXTENDED_PAGES_1_2_10_11_MASK)) { - p = (page_t){PAGE_01, LOWER_PAGE_OFFSET, I2C_ADDR_LOW}; - _validPages.push_back(p); -@@ -902,7 +902,7 @@ void MlxlinkCablesCommander::initValidPages() - p = (page_t){PAGE_14, UPPER_PAGE_OFFSET, I2C_ADDR_LOW}; - _validPages.push_back(p); - } -- delete page1H; -+ free(page1H); - } - } - } -@@ -916,7 +916,7 @@ vector MlxlinkCablesCommander::getPagesToDump() - u_int8_t *pageP = (u_int8_t*)malloc(sizeof(u_int8_t) * CABLE_PAGE_SIZE); - loadEEPRMPage(_validPages[i].page, _validPages[i].offset, pageP, _validPages[i].i2cAddress);; - addPageToOutputVector(pageP, _validPages[i].page, _validPages[i].offset, CABLE_PAGE_SIZE); -- delete pageP; -+ free(pageP); - } - return _pagesToDump; - } -@@ -941,7 +941,7 @@ void MlxlinkCablesCommander::writeToEEPROM(u_int16_t page , u_int16_t offset, - i2cAddress = I2C_ADDR_HIGH; - } - writeMCIA(page, numberOfAllignedBytes, numberOfZeroBytes, offset, data, i2cAddress); -- delete data; -+ free(data); - } - // Checking read command parameters and initializing the valid pages - void MlxlinkCablesCommander::checkParams(u_int16_t page , u_int16_t offset, u_int16_t length) -@@ -1036,7 +1036,7 @@ MlxlinkCmdPrint MlxlinkCablesCommander::readFromEEPRM(u_int16_t page , u_int16_t - } - bytesOutput.toJsonFormat(_jsonRoot); - -- delete pageL; -- delete pageH; -+ free(pageL); -+ free(pageH); - return bytesOutput; - } diff -Nru mstflint-4.17.0+1/debian/patches/man-proper-header-line.patch mstflint-4.21.0+1/debian/patches/man-proper-header-line.patch --- mstflint-4.17.0+1/debian/patches/man-proper-header-line.patch 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/debian/patches/man-proper-header-line.patch 2022-08-15 14:35:05.000000000 +0000 @@ -0,0 +1,221 @@ +From 7835e91ed467320dffb874f1c7560a0eeeb198f4 Mon Sep 17 00:00:00 2001 +From: Tzafrir Cohen +Date: Mon, 15 Aug 2022 17:08:18 +0300 +Subject: [PATCH 1/2] man: proper header line +Bug: https://github.com/Mellanox/mstflint/pull/654 + +Parameters for .TH are: title section date source manual +* Some manpages had a commaa at the end of the name +* Replace generated description of tool with sourec name (mstflint) + +A proper header with source name and proper section number. +--- + man/mstarchive.1 | 3 +-- + man/mstconfig.1 | 3 +-- + man/mstcongestion.1 | 3 +-- + man/mstflint.1 | 3 +-- + man/mstfwmanager.1 | 3 +-- + man/mstfwreset.1 | 3 +-- + man/mstfwtrace.1 | 3 +-- + man/mstlink.1 | 3 +-- + man/mstmcra.1 | 3 +-- + man/mstmread.1 | 3 +-- + man/mstmtserver.1 | 3 +-- + man/mstmwrite.1 | 3 +-- + man/mstprivhost.1 | 3 +-- + man/mstreg.1 | 3 +-- + man/mstregdump.1 | 3 +-- + man/mstresourcedump.1 | 3 +-- + man/mstvpd.1 | 3 +-- + 17 files changed, 17 insertions(+), 34 deletions(-) + +diff --git a/man/mstarchive.1 b/man/mstarchive.1 +index 82ec294..ad3c90c 100644 +--- a/man/mstarchive.1 ++++ b/man/mstarchive.1 +@@ -1,5 +1,4 @@ +-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. +-.TH MSTARCHIVE "1" "March 2020" "mstarchive NAME" "User Commands" ++.TH MSTARCHIVE "1" "March 2020" "mstflint" "User Commands" + .SH NAME + mstarchive \- generate MFA2 archives + .SH DESCRIPTION +diff --git a/man/mstconfig.1 b/man/mstconfig.1 +index 302673b..91f252e 100644 +--- a/man/mstconfig.1 ++++ b/man/mstconfig.1 +@@ -1,5 +1,4 @@ +-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. +-.TH MSTCONFIG, "1" "March 2020" "mstconfig, mstflint 4.14.0, built on Mar 3 2020, 14:01:56. Git SHA Hash: N/A" "User Commands" ++.TH MSTCONFIG "1" "March 2020" "mstflint" "User Commands" + .SH NAME + mstconfig \- set or query non-volatile configurable options for Mellanox HCAs + .SH DESCRIPTION +diff --git a/man/mstcongestion.1 b/man/mstcongestion.1 +index 3fc0b45..d86162b 100644 +--- a/man/mstcongestion.1 ++++ b/man/mstcongestion.1 +@@ -1,5 +1,4 @@ +-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. +-.TH MSTCONGESTION, "1" "March 2020" "mstcongestion, mstflint 4.14.0, built on Mar 3 2020, 14:02:45. Git SHA Hash: N/A" "User Commands" ++.TH MSTCONGESTION "1" "March 2020" "mstflint" "User Commands" + .SH NAME + mstcongestion \- utility for configuring Mellanox device's receive congestion handling + .SH DESCRIPTION +diff --git a/man/mstflint.1 b/man/mstflint.1 +index b604e02..e99ad02 100644 +--- a/man/mstflint.1 ++++ b/man/mstflint.1 +@@ -1,5 +1,4 @@ +-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. +-.TH MSTFLINT, "1" "March 2020" "mstflint, mstflint 4.14.0, built on Mar 3 2020, 14:02:39. Git SHA Hash: N/A" "User Commands" ++.TH MSTFLINT "1" "March 2020" "mstflint" "User Commands" + .SH NAME + mstflint \- Flash Interface + .SH DESCRIPTION +diff --git a/man/mstfwmanager.1 b/man/mstfwmanager.1 +index bef0171..e9419e2 100644 +--- a/man/mstfwmanager.1 ++++ b/man/mstfwmanager.1 +@@ -1,5 +1,4 @@ +-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. +-.TH MSTFWMANAGER "1" "March 2020" "mstfwmanager 1.0, mstflint 4.14.0, built on Mar 3 2020, 14:02:20. Git SHA Hash: N/A" "User Commands" ++.TH MSTFWMANAGER "1" "March 2020" "mstflint" "User Commands" + .SH NAME + mstfwmanager \- Mellanox Firmware Manager + .SH DESCRIPTION +diff --git a/man/mstfwreset.1 b/man/mstfwreset.1 +index 4f0893a..f8c420b 100644 +--- a/man/mstfwreset.1 ++++ b/man/mstfwreset.1 +@@ -1,5 +1,4 @@ +-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. +-.TH MSTFWRESET "1" "March 2020" "mstfwreset 1.0.0, mstflint 4.14.0, built on Mar 03 2020, 14:01:10. Git SHA Hash: N/A" "User Commands" ++.TH MSTFWRESET "1" "March 2020" "mstflint" "User Commands" + .SH NAME + mstfwreset \- Query and perform reset operation on the device + .SH DESCRIPTION +diff --git a/man/mstfwtrace.1 b/man/mstfwtrace.1 +index 2fd6393..4bb2a11 100644 +--- a/man/mstfwtrace.1 ++++ b/man/mstfwtrace.1 +@@ -1,5 +1,4 @@ +-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. +-.TH MSTFWTRACE, "1" "March 2020" "mstfwtrace, mstflint 4.14.0, built on Mar 03 2020, 14:01:10. Git SHA Hash: N/A" "User Commands" ++.TH MSTFWTRACE "1" "March 2020" "mstflint" "User Commands" + .SH NAME + mstfwtrace \- Extracts and prints trace messages generated by the firmware of 5th generation devices + .SH DESCRIPTION +diff --git a/man/mstlink.1 b/man/mstlink.1 +index b7713b0..040f869 100644 +--- a/man/mstlink.1 ++++ b/man/mstlink.1 +@@ -1,5 +1,4 @@ +-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. +-.TH MSTLINK, "1" "March 2020" "mstlink, mstflint 4.14.0, built on Mar 3 2020, 14:03:31. Git SHA Hash: N/A" "User Commands" ++.TH MSTLINK "1" "March 2020" "mstflint" "User Commands" + .SH NAME + mstlink \- check and debug link status and issues related to them + .SH +diff --git a/man/mstmcra.1 b/man/mstmcra.1 +index 42fcfc6..e361cb1 100644 +--- a/man/mstmcra.1 ++++ b/man/mstmcra.1 +@@ -1,5 +1,4 @@ +-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. +-.TH MSTMCRA "1" "March 2020" "mstmcra Mellanox Configuration Registers Access tool" "User Commands" ++.TH MSTMCRA "1" "March 2020" "mstflint" "User Commands" + .SH NAME + mstmcra \- Mellanox Configuration Registers Access tool + .SH DESCRIPTION +diff --git a/man/mstmread.1 b/man/mstmread.1 +index cc5fefe..e7e0550 100644 +--- a/man/mstmread.1 ++++ b/man/mstmread.1 +@@ -1,5 +1,4 @@ +-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. +-.TH MSTMREAD "1" "March 2020" "mstmread " "User Commands" ++.TH MSTMREAD "1" "March 2020" "mstflint" "User Commands" + .SH NAME + mstmread \- Mellanox Read Configuration Register Tool + .SH DESCRIPTION +diff --git a/man/mstmtserver.1 b/man/mstmtserver.1 +index cf49527..d31e67c 100644 +--- a/man/mstmtserver.1 ++++ b/man/mstmtserver.1 +@@ -1,5 +1,4 @@ +-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. +-.TH MSTMTSERVER "1" "March 2020" ++.TH MSTMTSERVER "1" "March 2020" "mstflint" "User Commands" + .SH NAME + mstmtserver \- mstflint mst mt server + .SH DESCRIPTION +diff --git a/man/mstmwrite.1 b/man/mstmwrite.1 +index 110a6d1..3fc2fcd 100644 +--- a/man/mstmwrite.1 ++++ b/man/mstmwrite.1 +@@ -1,5 +1,4 @@ +-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. +-.TH MSTMWRITE "1" "March 2020" "mstmwrite " "User Commands" ++.TH MSTMWRITE "1" "March 2020" "mstflint" "User Commands" + .SH NAME + mstmwrite \- Mellanox Write Configuration Register Tool + .SH DESCRIPTION +diff --git a/man/mstprivhost.1 b/man/mstprivhost.1 +index 2c8b3ec..6a68e0e 100644 +--- a/man/mstprivhost.1 ++++ b/man/mstprivhost.1 +@@ -1,5 +1,4 @@ +-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. +-.TH MSTPRIVHOST "1" "March 2020" "mstprivhost 1.0.0, mstflint 4.14.0, built on Mar 03 2020, 14:01:10. Git SHA Hash: N/A" "User Commands" ++.TH MSTPRIVHOST "1" "March 2020" "mstflint" "User Commands" + .SH NAME + mstprivhost \- privilege modification tool + .SH DESCRIPTION +diff --git a/man/mstreg.1 b/man/mstreg.1 +index 8276064..cc4bb43 100644 +--- a/man/mstreg.1 ++++ b/man/mstreg.1 +@@ -1,5 +1,4 @@ +-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. +-.TH MSTREG, "1" "March 2020" "mstreg, mstflint 4.14.0, built on Mar 3 2020, 14:03:18. Git SHA Hash: N/A" "User Commands" ++.TH MSTREG "1" "March 2020" "mstflint" "User Commands" + .SH NAME + mstreg \- Expose supported access registers + .SH DESCRIPTION +diff --git a/man/mstregdump.1 b/man/mstregdump.1 +index 546b9f9..10e4189 100644 +--- a/man/mstregdump.1 ++++ b/man/mstregdump.1 +@@ -1,5 +1,4 @@ +-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. +-.TH MSTREGDUMP, "1" "March 2020" "mstregdump, mstflint 4.14.0, built on Mar 3 2020, 14:02:48. Git SHA Hash: N/A" "User Commands" ++.TH MSTREGDUMP "1" "March 2020" "mstflint" "User Commands" + .SH NAME + mstdump \- Mellanox mstdump utility, dumps device internal configuration data + .SH SYNOPSIS +diff --git a/man/mstresourcedump.1 b/man/mstresourcedump.1 +index 7d06cd2..b7fa94e 100644 +--- a/man/mstresourcedump.1 ++++ b/man/mstresourcedump.1 +@@ -1,5 +1,4 @@ +-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. +-.TH MSTRESOURCEDUMP, "1" "March 2020" "mstresourcedump, mstflint 4.14.0, built on Mar 03 2020, 14:01:10. Git SHA Hash: N/A" "User Commands" ++.TH MSTRESOURCEDUMP "1" "March 2020" "mstflint" "User Commands" + .SH NAME + mstresourcedump \- dump resource information + .SH DESCRIPTION +diff --git a/man/mstvpd.1 b/man/mstvpd.1 +index 1af3051..e6a4c3a 100644 +--- a/man/mstvpd.1 ++++ b/man/mstvpd.1 +@@ -1,5 +1,4 @@ +-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.41.1. +-.TH USAGE: "1" "March 2020" "Usage: mstvpd [-m|-n|-r] [-t ##] [-- keyword ...]" "User Commands" ++.TH MSTVPD "1" "March 2020" "mstflint" "User Commands" + .SH NAME + mstvpd \- Mellanox VPD read tool + .SH SYNOPSIS +-- +2.30.2 + diff -Nru mstflint-4.17.0+1/debian/patches/mipsel.patch mstflint-4.21.0+1/debian/patches/mipsel.patch --- mstflint-4.17.0+1/debian/patches/mipsel.patch 2021-10-27 08:33:27.000000000 +0000 +++ mstflint-4.21.0+1/debian/patches/mipsel.patch 2022-08-15 13:06:32.000000000 +0000 @@ -12,67 +12,56 @@ --- mstflint.orig/common/compatibility.h 2021-02-04 11:34:21.399276301 +0200 +++ mstflint/common/compatibility.h 2021-02-04 11:34:21.395276247 +0200 @@ -55,6 +55,8 @@ - # define ARCH_m68k - # elif defined(__hppa__) - # define ARCH_hppa -+# elif defined(__mips) && (__mips == 32) -+# define ARCH_mipsel - # elif defined(__PPC64__) || defined(__s390x__) - # define ARCH_ppc64 - # elif defined(__PPC__) -@@ -96,7 +100,8 @@ - #define U16H_FMT "0x%04x" - #define U8H_FMT "0x%02x" + #define ARCH_m68k + #elif defined(__hppa__) + #define ARCH_hppa ++#elif defined(__mips) && (__mips == 32) ++#define ARCH_mipsel + #elif defined(__PPC64__) || defined(__s390x__) + #define ARCH_ppc64 + #elif defined(__PPC__) +@@ -96,7 +100,7 @@ + #define U8H_FMT "0x%02x" --#if defined(ARCH_x86) || defined(ARCH_ppc) || defined(UEFI_BUILD) || defined(ARCH_arm6l) || defined(ARCH_m68k) || defined(ARCH_hppa) -+#if defined(ARCH_x86) || defined(ARCH_ppc) || defined(UEFI_BUILD) || defined(ARCH_arm6l) \ -+ || defined(ARCH_m68k) || defined(ARCH_hppa) || defined(ARCH_mipsel) - # if defined(__MINGW32__) || defined(__MINGW64__) - # include - # define U64D_FMT "0x%" PRId64 + #if defined(ARCH_x86) || defined(ARCH_ppc) || defined(UEFI_BUILD) || defined(ARCH_arm6l) || defined(ARCH_m68k) || \ +- defined(ARCH_hppa) ++ defined(ARCH_hppa) || defined(ARCH_mipsel) + #if defined(__MINGW32__) || defined(__MINGW64__) + #include + #define U64D_FMT "0x%" PRId64 Index: mstflint/tools_layouts/adb_to_c_utils.h =================================================================== --- mstflint.orig/tools_layouts/adb_to_c_utils.h 2021-02-04 11:34:21.399276301 +0200 +++ mstflint/tools_layouts/adb_to_c_utils.h 2021-02-04 11:34:21.395276247 +0200 @@ -132,6 +132,8 @@ - # define ARCH_m68k - # elif defined(__hppa__) - # define ARCH_hppa -+# elif defined(__mips) && (__mips == 32) -+# define ARCH_mipsel - # elif defined(__PPC64__) || defined(__s390x__) - # define ARCH_ppc64 - # elif defined(__PPC__) + #define ARCH_m68k + #elif defined(__hppa__) + #define ARCH_hppa ++#elif defined(__mips) && (__mips == 32) ++#define ARCH_mipsel + #elif defined(__PPC64__) || defined(__s390x__) + #define ARCH_ppc64 + #elif defined(__PPC__) @@ -174,7 +178,8 @@ - #define U16H_FMT "0x%04x" - #define U8H_FMT "0x%02x" + #define U16H_FMT "0x%04x" + #define U8H_FMT "0x%02x" -#if defined(ARCH_x86) || defined(ARCH_ppc) || defined(UEFI_BUILD) || defined(ARCH_arm6l) || defined(ARCH_m68k) || defined(ARCH_hppa) -+#if defined(ARCH_x86) || defined(ARCH_ppc) || defined(UEFI_BUILD) || defined(ARCH_arm6l) \ -+ || defined(ARCH_m68k) || defined(ARCH_hppa) || defined(ARCH_mipsel) - # if defined(__MINGW32__) || defined(__MINGW64__) - # include - # define U64D_FMT "0x%" PRId64 ++#if defined(ARCH_x86) || defined(ARCH_ppc) || defined(UEFI_BUILD) || defined(ARCH_arm6l) || defined(ARCH_m68k) || \ ++ defined(ARCH_hppa) || defined(ARCH_mipsel) + #if defined(__MINGW32__) || defined(__MINGW64__) + #include + #define U64D_FMT "0x%" PRId64 Index: mstflint/mtcr_ul/packets_common.h =================================================================== --- mstflint.orig/mtcr_ul/packets_common.h 2021-02-04 11:34:21.399276301 +0200 +++ mstflint/mtcr_ul/packets_common.h 2021-02-04 11:34:21.395276247 +0200 @@ -144,6 +144,8 @@ - # define ARCH_m68k - # elif defined(__hppa__) - # define ARCH_hppa -+# elif defined(__mips) && (__mips == 32) -+# define ARCH_mipsel - # elif defined(__PPC64__) || defined(__s390x__) - # define ARCH_ppc64 - # elif defined(__PPC__) -@@ -175,7 +179,8 @@ - # define U8H_FMT "0x%02x" - # define U32D_FMT "%u" - # define STR_FMT "%s" -- #elif defined(ARCH_x86) || defined(ARCH_ppc) || defined(__MINGW32__) || defined(UEFI_BUILD) || defined(ARCH_arm6l) defined(ARCH_m68k) || defined(ARCH_hppa) -+ #elif defined(ARCH_x86) || defined(ARCH_ppc) || defined(__MINGW32__) || defined(UEFI_BUILD) \ -+ || defined(ARCH_arm6l) defined(ARCH_m68k) || defined(ARCH_hppa) || defined(ARCH_mipsel) - # define U64H_FMT "0x%016llx" - # define U64D_FMT "%llu" - # define U32H_FMT "0x%08x" + #define ARCH_m68k + #elif defined(__hppa__) + #define ARCH_hppa ++#elif defined(__mips) && (__mips == 32) ++#define ARCH_mipsel + #elif defined(__PPC64__) || defined(__s390x__) + #define ARCH_ppc64 + #elif defined(__PPC__) diff -Nru mstflint-4.17.0+1/debian/patches/series mstflint-4.21.0+1/debian/patches/series --- mstflint-4.17.0+1/debian/patches/series 2021-10-27 11:21:30.000000000 +0000 +++ mstflint-4.21.0+1/debian/patches/series 2022-08-17 03:32:32.000000000 +0000 @@ -1,7 +1,7 @@ 0001-Remove-date-and-time-from-version-string-to-ensure-r.patch -0003-Fix-man-page-errors.patch 0005-Add-missing-shebang-to-Python-scripts.patch cross.patch fix_interpreters.patch mipsel.patch -malloc_delete_free.patch +man-proper-header-line.patch +typos.patch diff -Nru mstflint-4.17.0+1/debian/patches/typos.patch mstflint-4.21.0+1/debian/patches/typos.patch --- mstflint-4.17.0+1/debian/patches/typos.patch 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/debian/patches/typos.patch 2022-08-16 12:05:37.000000000 +0000 @@ -0,0 +1,152 @@ +From: Tzafrir Cohen +Subject: Fix types (lintian) +Bug: https://github.com/Mellanox/mstflint/pull/656 + +Index: mstflint/reg_access/reg_access.c +=================================================================== +--- mstflint.orig/reg_access/reg_access.c 2022-08-14 16:29:50.500128266 +0300 ++++ mstflint/reg_access/reg_access.c 2022-08-15 18:18:38.107059974 +0300 +@@ -679,7 +679,7 @@ + char* path_env = getenv("MCQS_DEBUG"); + if (path_env != NULL) + { +- printf("-I- MCQS: Recieved data --\n"); ++ printf("-I- MCQS: Received data --\n"); + reg_access_hca_mcqs_reg_dump(mcqs, stdout); + } + +@@ -700,7 +700,7 @@ + char* path_env = getenv("MCQI_DEBUG"); + if (path_env != NULL) + { +- printf("-I- MCQI: Recieved data --\n"); ++ printf("-I- MCQI: Received data --\n"); + reg_access_hca_mcqi_reg_dump(mcqi, stdout); + } + return ret; +Index: mstflint/ext_libs/iniParser/README.md +=================================================================== +--- mstflint.orig/ext_libs/iniParser/README.md 2021-01-31 17:52:10.367022491 +0200 ++++ mstflint/ext_libs/iniParser/README.md 2022-08-16 10:12:57.520120499 +0300 +@@ -29,7 +29,7 @@ + ## III - License + + This software is released under MIT License. +-See LICENSE for full informations ++See LICENSE for full information + + ## IV - Versions + +Index: mstflint/ext_libs/iniParser/dictionary.c +=================================================================== +--- mstflint.orig/ext_libs/iniParser/dictionary.c 2022-08-14 16:29:50.340128261 +0300 ++++ mstflint/ext_libs/iniParser/dictionary.c 2022-08-16 10:13:14.112121045 +0300 +@@ -6,7 +6,7 @@ + + This module implements a simple dictionary object, i.e. a list + of string/string associations. This object is useful to store e.g. +- informations retrieved from a configuration file (ini files). ++ information retrieved from a configuration file (ini files). + */ + /*--------------------------------------------------------------------------*/ + +Index: mstflint/ext_libs/iniParser/dictionary.h +=================================================================== +--- mstflint.orig/ext_libs/iniParser/dictionary.h 2022-08-14 16:29:50.340128261 +0300 ++++ mstflint/ext_libs/iniParser/dictionary.h 2022-08-16 10:13:22.256121313 +0300 +@@ -7,7 +7,7 @@ + + This module implements a simple dictionary object, i.e. a list + of string/string associations. This object is useful to store e.g. +- informations retrieved from a configuration file (ini files). ++ information retrieved from a configuration file (ini files). + */ + /*--------------------------------------------------------------------------*/ + +Index: mstflint/mlxconfig/mlxcfg_parser.cpp +=================================================================== +--- mstflint.orig/mlxconfig/mlxcfg_parser.cpp 2022-08-14 16:29:50.428128264 +0300 ++++ mstflint/mlxconfig/mlxcfg_parser.cpp 2022-08-15 18:21:28.443065581 +0300 +@@ -99,7 +99,7 @@ + printf("\n"); + printf(IDENT "COMMANDS SUMMARY\n"); + printf(IDENT2 "%-24s : %s\n", "clear_semaphore", "clear the tool semaphore."); +- printf(IDENT2 "%-24s : %s\n", "i|show_confs", "display informations about all configurations."); ++ printf(IDENT2 "%-24s : %s\n", "i|show_confs", "display information about all configurations."); + printf(IDENT2 "%-24s : %s\n", "q|query", "query supported configurations."); + printf(IDENT2 "%-24s : %s\n", "r|reset", "reset all configurations to their default value."); + printf(IDENT2 "%-24s : %s\n", "s|set", "set configurations to a specific device."); +Index: mstflint/ext_libs/iniParser/iniparser.c +=================================================================== +--- mstflint.orig/ext_libs/iniParser/iniparser.c 2022-08-14 16:29:50.340128261 +0300 ++++ mstflint/ext_libs/iniParser/iniparser.c 2022-08-16 10:14:49.520124186 +0300 +@@ -37,7 +37,7 @@ + @param in String to convert. + @param out Output buffer. + @param len Size of the out buffer. +- @return ptr to the out buffer or NULL if an error occured. ++ @return ptr to the out buffer or NULL if an error occurred. + + This function convert a string into lowercase. + At most len - 1 elements of the input string will be converted. +Index: mstflint/ext_libs/sqlite/sqlite3.c +=================================================================== +--- mstflint.orig/ext_libs/sqlite/sqlite3.c 2022-08-14 16:29:50.396128263 +0300 ++++ mstflint/ext_libs/sqlite/sqlite3.c 2022-08-16 10:15:14.956125023 +0300 +@@ -109831,7 +109831,7 @@ + } + + /* +-** 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 +@@ -214381,7 +214381,7 @@ + ** iterator pIter points to to the SELECT and attempts to seek to the table + ** entry. If a row is found, the SELECT statement left pointing at the row + ** and SQLITE_ROW is returned. Otherwise, if no row is found and no error +-** has occured, the statement is reset and SQLITE_OK is returned. If an ++** has occurred, the statement is reset and SQLITE_OK is returned. If an + ** error occurs, the statement is reset and an SQLite error code is returned. + ** + ** If this function returns SQLITE_ROW, the caller must eventually reset() +Index: mstflint/fw_comps_mgr/fw_comps_mgr_dma_access.cpp +=================================================================== +--- mstflint.orig/fw_comps_mgr/fw_comps_mgr_dma_access.cpp 2022-08-14 16:29:50.404128263 +0300 ++++ mstflint/fw_comps_mgr/fw_comps_mgr_dma_access.cpp 2022-08-16 10:15:48.700126134 +0300 +@@ -399,7 +399,7 @@ + } + catch (std::exception& e) + { +- DPRINTF(("DMAComponentAccess::Exception occured %s\n", e.what())); ++ DPRINTF(("DMAComponentAccess::Exception occurred %s\n", e.what())); + return false; + } + #endif +Index: mstflint/tools_layouts/tools_open_layouts.h +=================================================================== +--- mstflint.orig/tools_layouts/tools_open_layouts.h 2022-08-14 16:29:50.528128267 +0300 ++++ mstflint/tools_layouts/tools_open_layouts.h 2022-08-16 10:16:04.796126664 +0300 +@@ -2844,7 +2844,7 @@ + /* Description - Status of current operation that FW sends to host. */ + /* 0x20.0 - 0x20.7 */ + u_int8_t status; +- /* Description - Last error index, if occured. */ ++ /* Description - Last error index, if occurred. */ + /* 0x20.8 - 0x20.15 */ + u_int8_t error; + /* Description - Reserved. */ +Index: mstflint/mlxconfig/mlxcfg_generic_commander.cpp +=================================================================== +--- mstflint.orig/mlxconfig/mlxcfg_generic_commander.cpp 2022-08-14 16:29:50.424128264 +0300 ++++ mstflint/mlxconfig/mlxcfg_generic_commander.cpp 2022-08-16 10:18:14.232130924 +0300 +@@ -1503,7 +1503,7 @@ + if ((keySize != KEY_SIZE_256) && (keySize != KEY_SIZE_512)) + { + throw MlxcfgException("Invalid length of private key(%d bytes). It is " +- "recommanded to use 4096 bit key.\n", ++ "recommended to use 4096 bit key.\n", + keySize); + } + // Init successfuly with valid key -> can continue to sign the massage diff -Nru mstflint-4.17.0+1/dev_mgt/dev_mgt.py mstflint-4.21.0+1/dev_mgt/dev_mgt.py --- mstflint-4.17.0+1/dev_mgt/dev_mgt.py 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/dev_mgt/dev_mgt.py 2022-07-31 09:33:57.000000000 +0000 @@ -1,4 +1,5 @@ # Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved. +# Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU @@ -27,7 +28,7 @@ # 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. -#-- +# -- from __future__ import print_function import os @@ -36,6 +37,7 @@ import ctypes import mtcr + class DevMgtException(Exception): pass @@ -45,12 +47,15 @@ try: from ctypes import * if platform.system() == "Windows" or os.name == "nt": - DEV_MGT = CDLL("c_dev_mgt.dll") + try: + DEV_MGT = CDLL("c_dev_mgt.dll") + except BaseException: + DEV_MGT = CDLL(os.path.join(os.path.dirname(os.path.realpath(__file__)), "c_dev_mgt.dll")) else: try: - #c_dev_mgt.so must be specified explicitly for pyinstaller collection + # c_dev_mgt.so must be specified explicitly for pyinstaller collection DEV_MGT = CDLL("c_dev_mgt.so") - except: + except BaseException: DEV_MGT = CDLL(os.path.join(os.path.dirname(os.path.realpath(__file__)), "c_dev_mgt.so")) except Exception as exp: raise DevMgtException("Failed to load shared library c_dev_mgt: %s" % exp) @@ -58,20 +63,99 @@ if DEV_MGT: class DevMgt: ########################## - def __init__(self, dev): + def __init__(self, dev, i2c_secondary= -1): self.mf = 0 - self._isLivefishModeFunc = DEV_MGT.dm_is_livefish_mode # dm_is_livefish_mode(mfile* mf) - if isinstance(dev,(mtcr.MstDevice)): - self._mstdev = dev + self._isLivefishModeFunc = DEV_MGT.dm_is_livefish_mode # dm_is_livefish_mode(mfile* mf) + self._getDeviceIdFunc = DEV_MGT.dm_get_device_id + if isinstance(dev, (mtcr.MstDevice)): + self._mstdev = dev else: - self._mstdev = mtcr.MstDevice(dev) + self._mstdev = mtcr.MstDevice(dev, i2c_secondary) if not self._mstdev: raise DevMgtException("Failed to open device (%s): %s" % (dev, os.strerror(ctypes.get_errno()))) - ########################## def isLivefishMode(self): rc = self._isLivefishModeFunc(self._mstdev.mf) return not (rc == 0) + + ########################## + def getHwId(self): + dm_dev_id = ctypes.c_uint16() + hw_dev_id = ctypes.c_uint32() + hw_rev = ctypes.c_uint32() + + rc = DEV_MGT.dm_get_device_id(self._mstdev.mf, ctypes.byref(dm_dev_id), ctypes.byref(hw_dev_id), ctypes.byref(hw_rev)) + return {"return_code": rc, "dm_device_id": dm_dev_id, "hw_device_id": hw_dev_id, "hw_revision": hw_rev} + + @classmethod + def getDevIdOffline(cls, hw_dev_id): + dm_dev_id = ctypes.c_uint16() + rc = DEV_MGT.dm_get_device_id_offline(hw_dev_id, -1, ctypes.byref(dm_dev_id)) + return {"return_code": rc, "dm_device_id": dm_dev_id, "hw_device_id": hw_dev_id, "hw_revision": -1} + + @classmethod + def str2hwId(cls, name): + name_char_p = ctypes.c_char_p(name.lower().encode('utf-8')) + dm_dev_id = ctypes.c_uint16() + hw_dev_id = ctypes.c_uint32() + hw_rev = ctypes.c_uint32() + + dm_dev_id = DEV_MGT.dm_dev_aproxstr2type(name_char_p) + + rc = 0 + hw_dev_id = DEV_MGT.dm_get_hw_dev_id(dm_dev_id) + hw_rev = DEV_MGT.dm_get_hw_rev_id(dm_dev_id) + + if (hw_dev_id == 0 or hw_rev == 0): + rc = 1 + + return {"return_code": rc, "dm_device_id": dm_dev_id, "hw_device_id": hw_dev_id, "hw_revision": hw_rev} + + @classmethod + def type2str(cls, dm_dev_id): + DEV_MGT.dm_dev_type2str.restype = ctypes.c_char_p + return DEV_MGT.dm_dev_type2str(dm_dev_id).decode("utf-8") + + @classmethod + def is_ib_switch(cls, dm_dev_id): + return DEV_MGT.dm_dev_is_ib_switch(dm_dev_id) + + @classmethod + def is_connectib(cls, dm_dev_id): + return DEV_MGT.dm_is_connectib(dm_dev_id) + + @classmethod + def is_gb100(cls, dm_dev_id): + return DEV_MGT.dm_is_gb100(dm_dev_id) + + @classmethod + def is_cx7(cls, dm_dev_id): + return DEV_MGT.dm_is_cx7(dm_dev_id) + + @classmethod + def is_fs3(cls, dm_dev_id): + return DEV_MGT.dm_dev_is_fs3(dm_dev_id) + + @classmethod + def is_fs4(cls, dm_dev_id): + return DEV_MGT.dm_dev_is_fs4(dm_dev_id) + + @classmethod + def is_4th_gen(cls, dm_dev_id): + return DEV_MGT.dm_is_4th_gen(dm_dev_id) + + @classmethod + def sw_id2hw_id(cls, sw_dev_id): + dm_dev_id = DEV_MGT.dm_dev_sw_id2type(sw_dev_id) + rc = 0 + hw_dev_id = DEV_MGT.dm_get_hw_dev_id(dm_dev_id) + hw_rev = DEV_MGT.dm_get_hw_rev_id(dm_dev_id) + + if (hw_dev_id == 0 or hw_rev == 0): + rc = 1 + + return {"return_code": rc, "dm_device_id": dm_dev_id, "hw_device_id": hw_dev_id, "hw_revision": hw_rev} + else: - raise DevMgtException("Failed to load c_dev_mgt.so/libdev_mgt.dll") + raise DevMgtException("Failed to load c_dev_mgt.so/libdev_mgt.dll") diff -Nru mstflint-4.17.0+1/dev_mgt/Makefile.am mstflint-4.21.0+1/dev_mgt/Makefile.am --- mstflint-4.17.0+1/dev_mgt/Makefile.am 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/dev_mgt/Makefile.am 2022-04-28 11:50:03.000000000 +0000 @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved. +# Copyright (c) 2004-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU @@ -34,7 +34,7 @@ USER_DIR = .. AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir) -I$(top_srcdir)/common -I$(top_srcdir)/include/mtcr_ul -AM_CFLAGS = -W -Wall -g -MP -MD -Wswitch-enum $(COMPILER_FPIC) -DMTCR_EXPORT +AM_CFLAGS = -W -Wall -g -MP -MD -Wswitch-enum $(COMPILER_FPIC) -DMTCR_EXPORT lib_LTLIBRARIES = libdev_mgt.a libdev_mgt_a_SOURCES = \ diff -Nru mstflint-4.17.0+1/dev_mgt/Makefile.in mstflint-4.21.0+1/dev_mgt/Makefile.in --- mstflint-4.17.0+1/dev_mgt/Makefile.in 2021-06-29 08:51:07.000000000 +0000 +++ mstflint-4.21.0+1/dev_mgt/Makefile.in 2022-07-31 09:34:41.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.3 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2020 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -15,7 +15,7 @@ @SET_MAKE@ #-- -# Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved. +# Copyright (c) 2004-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU @@ -389,7 +389,7 @@ # Makefile.am -- Process this file with automake to produce Makefile.in USER_DIR = .. AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir) -I$(top_srcdir)/common -I$(top_srcdir)/include/mtcr_ul -AM_CFLAGS = -W -Wall -g -MP -MD -Wswitch-enum $(COMPILER_FPIC) -DMTCR_EXPORT +AM_CFLAGS = -W -Wall -g -MP -MD -Wswitch-enum $(COMPILER_FPIC) -DMTCR_EXPORT lib_LTLIBRARIES = libdev_mgt.a libdev_mgt_a_SOURCES = \ tools_dev_types.c \ diff -Nru mstflint-4.17.0+1/dev_mgt/tools_dev_types.c mstflint-4.21.0+1/dev_mgt/tools_dev_types.c --- mstflint-4.17.0+1/dev_mgt/tools_dev_types.c 2021-06-29 08:49:48.000000000 +0000 +++ mstflint-4.21.0+1/dev_mgt/tools_dev_types.c 2022-07-31 09:33:57.000000000 +0000 @@ -1,5 +1,6 @@ /* * Copyright (C) Jan 2013 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -47,31 +48,34 @@ #include "tools_dev_types.h" #include "mflash/mflash_types.h" -enum dm_dev_type { +enum dm_dev_type +{ DM_UNKNOWN = -1, DM_HCA, DM_SWITCH, DM_BRIDGE, DM_QSFP_CABLE, + DM_CMIS_CABLE, DM_SFP_CABLE, DM_LINKX, // linkx chip DM_GEARBOX }; -struct device_info { +struct device_info +{ dm_dev_id_t dm_id; u_int16_t hw_dev_id; - int hw_rev_id; /* -1 means all revisions match this record */ - int sw_dev_id; /* -1 means all hw ids match this record */ - const char *name; + int hw_rev_id; /* -1 means all revisions match this record */ + int sw_dev_id; /* -1 means all hw ids match this record */ + const char* name; int port_num; enum dm_dev_type dev_type; }; -#define DEVID_ADDR 0xf0014 -#define CABLEID_ADDR 0x0 -#define SFP_DIGITAL_DIAGNOSTIC_MONITORING_IMPLEMENTED_ADDR 92 -#define SFP_PAGING_IMPLEMENTED_INDICATOR_ADDR 64 +#define DEVID_ADDR 0xf0014 +#define CABLEID_ADDR 0x0 +#define SFP_DIGITAL_DIAGNOSTIC_MONITORING_IMPLEMENTED_ADDR 92 +#define SFP_PAGING_IMPLEMENTED_INDICATOR_ADDR 64 #define ARDBEG_REV0_DEVID 0x6e #define ARDBEG_REV1_DEVID 0x7e @@ -81,378 +85,437 @@ #define MENHIT_DEVID_VER0 0x6f #define MENHIT_DEVID_VER1 0x72 #define MENHIT_DEVID_VER2 0x73 - +#define ARCUS_P_TC_DEVID 0x7f +#define ARCUS_P_REV0_DEVID 0x80 +#define ARCUS_E_REV0_DEVID 0x81 #ifdef CABLES_SUPP enum dm_dev_type getCableType(u_int8_t id) { - switch (id) { - case 0xd: - case 0x11: - case 0x19: // Stallion2 - case 0xe: - case 0xc: - return DM_QSFP_CABLE; - - case 0x3: - return DM_SFP_CABLE; - - default: - return DM_UNKNOWN; + switch (id) + { + case 0xd: + case 0x11: + case 0xe: + case 0xc: + return DM_QSFP_CABLE; + + case 0x3: + return DM_SFP_CABLE; + case 0x18: + case 0x19: // Stallion2 + case 0x1e: + return DM_CMIS_CABLE; + default: + return DM_UNKNOWN; } } #endif -static struct device_info g_devs_info[] = { - { - DeviceInfiniScaleIV, //dm_id - 0x01b3, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "InfiniScaleIV", //name - 36, //port_num - DM_SWITCH //dev_type - }, - { - DeviceSwitchX, //dm_id - 0x0245, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "SwitchX", //name - 64, //port_num - DM_SWITCH //dev_type - }, - { - DeviceConnectX2, //dm_id - 0x190, //hw_dev_id - 0xb0, //hw_rev_id - -1, //sw_dev_id - "ConnectX2", //name - 2, //port_num - DM_HCA //dev_type - }, - { - DeviceConnectX3, //dm_id - 0x1f5, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "ConnectX3", //name - 2, //port_num - DM_HCA //dev_type - }, - { - DeviceConnectIB, //dm_id - 0x1ff, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "ConnectIB", //name - 2, //port_num - DM_HCA //dev_type - }, - { - DeviceConnectX3Pro, //dm_id - 0x1f7, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "ConnectX3Pro", //name - 2, //port_num - DM_HCA //dev_type - }, - { - DeviceSwitchIB, //dm_id - 0x247, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "SwitchIB", //name - 36, //port_num - DM_SWITCH //dev_type - }, - { - DeviceSpectrum, //dm_id - 0x249, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "Spectrum", //name - 64, //port_num - DM_SWITCH //dev_type - }, - { - DeviceConnectX4, //dm_id - 0x209, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "ConnectX4", //name - 2, //port_num - DM_HCA //dev_type - }, - { - DeviceConnectX4LX, //dm_id - 0x20b, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "ConnectX4LX", //name - 2, //port_num - DM_HCA //dev_type - }, - { - DeviceConnectX5, //dm_id - 0x20d, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "ConnectX5", //name - 2, //port_num - DM_HCA //dev_type - }, - { - DeviceConnectX6, //dm_id - 0x20f, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "ConnectX6", //name - 2, //port_num - DM_HCA //dev_type - }, - { - DeviceConnectX6DX, //dm_id - 0x212, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "ConnectX6DX", //name - 2, //port_num - DM_HCA //dev_type - }, - { - DeviceConnectX6LX, //dm_id - 0x216, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "ConnectX6LX", //name - 2, //port_num - DM_HCA //dev_type - }, - { - DeviceConnectX7, //dm_id - 0x218, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "ConnectX7", //name - 4, //port_num - DM_HCA //dev_type - }, - { - DeviceBlueField, //dm_id - 0x211, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "BlueField", //name - 2, //port_num - DM_HCA //dev_type - }, - { - DeviceBlueField2, //dm_id - 0x214, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "BlueField2", //name - 2, //port_num - DM_HCA //dev_type - }, - { - DeviceBlueField3, //dm_id - 0x21c, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "BlueField3", //name - 4, //port_num - DM_HCA //dev_type - }, - { - DeviceFPGANewton, //dm_id - 0xfff, //hw_dev_id - Dummy device ID till we have official one - -1, //hw_rev_id - -1, //sw_dev_id - "FPGA_NEWTON", //name - 2, //port_num - DM_HCA //dev_type - }, - { - DeviceSwitchIB2, //dm_id - 0x24b, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "SwitchIB2", //name - 36, //port_num - DM_SWITCH //dev_type - }, - { - DeviceCableQSFP, //dm_id - 0x0d, //hw_dev_id - 0, //hw_rev_id - -1, //sw_dev_id - "CableQSFP", //name - -1, //port_num - DM_QSFP_CABLE //dev_type - }, - { - DeviceCableQSFPaging, //dm_id - 0x11, //hw_dev_id - 0xab, //hw_rev_id - -1, //sw_dev_id - "CableQSFPaging", //name - -1, //port_num - DM_QSFP_CABLE //dev_type - }, - { - DeviceCableSFP, //dm_id - 0x03, //hw_dev_id - 1, //hw_rev_id - -1, //sw_dev_id - "CableSFP", //name - -1, //port_num - DM_SFP_CABLE //dev_type - }, - { - DeviceCableSFP51, //dm_id - 0x03, //hw_dev_id - 1, //hw_rev_id - -1, //sw_dev_id - "CableSFP51", //name - -1, //port_num - DM_SFP_CABLE //dev_type - }, - { - DeviceCableSFP51Paging, //dm_id - 0x03, //hw_dev_id - 1, //hw_rev_id - -1, //sw_dev_id - "CableSFP51Paging", //name - -1, //port_num - DM_SFP_CABLE //dev_type - }, - { - DeviceSpectrum2, //dm_id - 0x24e, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "Spectrum2", //name - 128, //port_num - DM_SWITCH //dev_type - }, - { - DeviceDummy, //dm_id - 0x1, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "DummyDevice", //name - 2, //port_num - DM_HCA //dev_type - }, - { - DeviceQuantum, //dm_id - 0x24d, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "Quantum", //name - 80, //port_num - DM_SWITCH //dev_type - }, - { - DeviceArdbeg, //dm_id - 0x6e, //hw_dev_id (ArdbegMirror 0x70) - -1, //hw_rev_id - -1, //sw_dev_id - "Ardbeg", //name - -1, //port_num - DM_LINKX //dev_type - }, - { - DeviceBaritone, //dm_id - 0x6b, //hw_dev_id (BaritoneMirror 0x71) - -1, //hw_rev_id - -1, //sw_dev_id - "Baritone", //name - -1, //port_num - DM_LINKX //dev_type - }, - { - DeviceMenhit, //dm_id - 0x72, //hw_dev_id (other versions 0x6f,0x73) - -1, //hw_rev_id - -1, //sw_dev_id - "Menhit", //name - -1, //port_num - DM_LINKX //dev_type - }, - { - DeviceSecureHost, //dm_id - 0xcafe, //hw_dev_id - 0xd0, //hw_rev_id - 0, //sw_dev_id - "Unknown Device", //name - -1, //port_num - DM_UNKNOWN //dev_type - }, - { - DeviceSpectrum3, //dm_id - 0x250, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "Spectrum3", //name - 128, //port_num NEED_CHECK - DM_SWITCH //dev_type - }, - { - DeviceSpectrum4, //dm_id - 0x254, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "Spectrum4", //name - 128, //port_num NEED_CHECK - DM_SWITCH //dev_type - }, - { - DeviceQuantum2, //dm_id - 0x257, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "Quantum2", //name - 128, //port_num NEED_CHECK - DM_SWITCH //dev_type - }, - { - DeviceGearBox, //dm_id - 0x252, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "Amose GearBox", //name - 128, //port_num NEED_CHECK - DM_GEARBOX //dev_type - }, - { - DeviceGearBoxManager, //dm_id - 0x253, //hw_dev_id - -1, //hw_rev_id - -1, //sw_dev_id - "Amos GearBox Managaer",//name - -1, //port_num NEED_CHECK - DM_GEARBOX //dev_type - }, - { - DeviceUnknown, //dm_id - 0, //hw_dev_id - 0, //hw_rev_id - 0, //sw_dev_id - "Unknown Device", //name - -1, //port_num - DM_UNKNOWN //dev_type - } -}; +static struct device_info g_devs_info[] = {{ + DeviceConnectX3, // dm_id + 0x1f5, // hw_dev_id + -1, // hw_rev_id + -1, // sw_dev_id + "ConnectX3", // name + 2, // port_num + DM_HCA // dev_type + }, + { + DeviceConnectIB, // dm_id + 0x1ff, // hw_dev_id + -1, // hw_rev_id + 4113, // sw_dev_id + "ConnectIB", // name + 2, // port_num + DM_HCA // dev_type + }, + { + DeviceConnectX3Pro, // dm_id + 0x1f7, // hw_dev_id + -1, // hw_rev_id + 4103, // sw_dev_id + "ConnectX3Pro", // name + 2, // port_num + DM_HCA // dev_type + }, + { + DeviceSwitchIB, // dm_id + 0x247, // hw_dev_id + -1, // hw_rev_id + 52000, // sw_dev_id + "SwitchIB", // name + 36, // port_num + DM_SWITCH // dev_type + }, + { + DeviceSpectrum, // dm_id + 0x249, // hw_dev_id + -1, // hw_rev_id + 52100, // sw_dev_id + "Spectrum", // name + 64, // port_num + DM_SWITCH // dev_type + }, + { + DeviceConnectX4, // dm_id + 0x209, // hw_dev_id + -1, // hw_rev_id + 4115, // sw_dev_id + "ConnectX4", // name + 2, // port_num + DM_HCA // dev_type + }, + { + DeviceConnectX4LX, // dm_id + 0x20b, // hw_dev_id + -1, // hw_rev_id + 4117, // sw_dev_id + "ConnectX4LX", // name + 2, // port_num + DM_HCA // dev_type + }, + { + DeviceConnectX5, // dm_id + 0x20d, // hw_dev_id + -1, // hw_rev_id + 4119, // sw_dev_id + "ConnectX5", // name + 2, // port_num + DM_HCA // dev_type + }, + { + DeviceConnectX6, // dm_id + 0x20f, // hw_dev_id + -1, // hw_rev_id + 4123, // sw_dev_id + "ConnectX6", // name + 2, // port_num + DM_HCA // dev_type + }, + { + DeviceConnectX6DX, // dm_id + 0x212, // hw_dev_id + -1, // hw_rev_id + 4125, // sw_dev_id + "ConnectX6DX", // name + 2, // port_num + DM_HCA // dev_type + }, + { + DeviceConnectX6LX, // dm_id + 0x216, // hw_dev_id + -1, // hw_rev_id + 4127, // sw_dev_id + "ConnectX6LX", // name + 2, // port_num + DM_HCA // dev_type + }, + { + DeviceConnectX7, // dm_id + 0x218, // hw_dev_id + -1, // hw_rev_id + 4129, // sw_dev_id + "ConnectX7", // name + 4, // port_num + DM_HCA // dev_type + }, + { + DeviceConnectX8, // dm_id + 0x21e, // hw_dev_id + -1, // hw_rev_id + -1, // sw_dev_id + "ConnectX8", // name + 4, // port_num + DM_HCA // dev_type + }, + { + DeviceBlueField, // dm_id + 0x211, // hw_dev_id + -1, // hw_rev_id + 41682, // sw_dev_id + "BlueField", // name + 2, // port_num + DM_HCA // dev_type + }, + { + DeviceBlueField2, // dm_id + 0x214, // hw_dev_id + -1, // hw_rev_id + 41686, // sw_dev_id + "BlueField2", // name + 2, // port_num + DM_HCA // dev_type + }, + { + DeviceBlueField3, // dm_id + 0x21c, // hw_dev_id + -1, // hw_rev_id + 41692, // sw_dev_id + "BlueField3", // name + 4, // port_num + DM_HCA // dev_type + }, + { + DeviceBlueField4, // dm_id + 0x220, // hw_dev_id + -1, // hw_rev_id + 41694, // sw_dev_id + "BlueField4", // name + 4, // port_num + DM_HCA // dev_type + }, + { + DeviceSwitchIB2, // dm_id + 0x24b, // hw_dev_id + -1, // hw_rev_id + 53000, // sw_dev_id + "SwitchIB2", // name + 36, // port_num + DM_SWITCH // dev_type + }, + { + DeviceCableQSFP, // dm_id + 0x0d, // hw_dev_id + 0, // hw_rev_id + -1, // sw_dev_id + "CableQSFP", // name + -1, // port_num + DM_QSFP_CABLE // dev_type + }, + { + DeviceCableQSFPaging, // dm_id + 0x11, // hw_dev_id + 0xab, // hw_rev_id + -1, // sw_dev_id + "CableQSFPaging", // name + -1, // port_num + DM_QSFP_CABLE // dev_type + }, + { + DeviceCableCMIS, // dm_id + 0x19, // hw_dev_id + 0, // hw_rev_id + -1, // sw_dev_id + "CableCMIS", // name + -1, // port_num + DM_CMIS_CABLE // dev_type + }, + { + DeviceCableCMISPaging, // dm_id + 0x19, // hw_dev_id + 0xab, // hw_rev_id + -1, // sw_dev_id + "CableCMISPaging", // name + -1, // port_num + DM_CMIS_CABLE // dev_type + }, + { + DeviceCableSFP, // dm_id + 0x03, // hw_dev_id + 1, // hw_rev_id + -1, // sw_dev_id + "CableSFP", // name + -1, // port_num + DM_SFP_CABLE // dev_type + }, + { + DeviceCableSFP51, // dm_id + 0x03, // hw_dev_id + 1, // hw_rev_id + -1, // sw_dev_id + "CableSFP51", // name + -1, // port_num + DM_SFP_CABLE // dev_type + }, + { + DeviceCableSFP51Paging, // dm_id + 0x03, // hw_dev_id + 1, // hw_rev_id + -1, // sw_dev_id + "CableSFP51Paging", // name + -1, // port_num + DM_SFP_CABLE // dev_type + }, + { + DeviceSpectrum2, // dm_id + 0x24e, // hw_dev_id + -1, // hw_rev_id + 53100, // sw_dev_id + "Spectrum2", // name + 128, // port_num + DM_SWITCH // dev_type + }, + { + DeviceDummy, // dm_id + 0x1, // hw_dev_id + -1, // hw_rev_id + -1, // sw_dev_id + "DummyDevice", // name + 2, // port_num + DM_HCA // dev_type + }, + { + DeviceQuantum, // dm_id + 0x24d, // hw_dev_id + -1, // hw_rev_id + 54000, // sw_dev_id + "Quantum", // name + 80, // port_num + DM_SWITCH // dev_type + }, + { + DeviceArdbeg, // dm_id + 0x6e, // hw_dev_id (ArdbegMirror 0x70) + -1, // hw_rev_id + -1, // sw_dev_id + "Ardbeg", // name + -1, // port_num + DM_LINKX // dev_type + }, + { + DeviceBaritone, // dm_id + 0x6b, // hw_dev_id (BaritoneMirror 0x71) + -1, // hw_rev_id + -1, // sw_dev_id + "Baritone", // name + -1, // port_num + DM_LINKX // dev_type + }, + { + DeviceMenhit, // dm_id + 0x72, // hw_dev_id (other versions 0x6f,0x73) + -1, // hw_rev_id + -1, // sw_dev_id + "Menhit", // name + -1, // port_num + DM_LINKX // dev_type + }, + { + DeviceArcusPTC, // dm_id + 0x7f, // hw_dev_id + -1, // hw_rev_id + -1, // sw_dev_id + "ArcusP Test-Chip", // name + -1, // port_num + DM_LINKX // dev_type + }, + { + DeviceArcusP, // dm_id + 0x80, // hw_dev_id (other versions 0x6f,0x73) + -1, // hw_rev_id + -1, // sw_dev_id + "ArcusP", // name + -1, // port_num + DM_LINKX // dev_type + }, + { + DeviceArcusE, // dm_id + 0x81, // hw_dev_id (other versions 0x6f,0x73) + -1, // hw_rev_id + -1, // sw_dev_id + "ArcusE", // name + -1, // port_num + DM_LINKX // dev_type + }, + { + DeviceSecureHost, // dm_id + 0xcafe, // hw_dev_id + 0xd0, // hw_rev_id + 0, // sw_dev_id + "Unknown Device", // name + -1, // port_num + DM_UNKNOWN // dev_type + }, + { + DeviceSpectrum3, // dm_id + 0x250, // hw_dev_id + -1, // hw_rev_id + 53104, // sw_dev_id + "Spectrum3", // name + 128, // port_num NEED_CHECK + DM_SWITCH // dev_type + }, + { + DeviceSpectrum4, // dm_id + 0x254, // hw_dev_id + -1, // hw_rev_id + 53120, // sw_dev_id + "Spectrum4", // name + 128, // port_num NEED_CHECK + DM_SWITCH // dev_type + }, + { + DeviceQuantum2, // dm_id + 0x257, // hw_dev_id + -1, // hw_rev_id + 54002, // sw_dev_id + "Quantum2", // name + 128, // port_num NEED_CHECK + DM_SWITCH // dev_type + }, + { + DeviceQuantum3, // dm_id + 0x25b, // hw_dev_id + -1, // hw_rev_id + 54004, // sw_dev_id + "Quantum3", // name + 128, // port_num NEED_CHECK + DM_SWITCH // dev_type + }, + { + DeviceGB100, // dm_id + 0x2900, // hw_dev_id + -1, // hw_rev_id + 10496, // sw_dev_id + "GB100", // name + 128, // port_num NEED_CHECK + DM_SWITCH // dev_type + }, + { + DeviceGearBox, // dm_id + 0x252, // hw_dev_id + -1, // hw_rev_id + 53108, // sw_dev_id + "AmosGearBox", // name + 128, // port_num NEED_CHECK + DM_GEARBOX // dev_type + }, + { + DeviceGearBoxManager, // dm_id + 0x253, // hw_dev_id + -1, // hw_rev_id + -1, // sw_dev_id + "AmosGearBoxManager", // name + -1, // port_num NEED_CHECK + DM_GEARBOX // dev_type + }, + { + DeviceAbirGearBox, // dm_id + 0x256, // hw_dev_id + -1, // hw_rev_id + -1, // sw_dev_id + "AbirGearBox", // name + -1, // port_num NEED_CHECK + DM_GEARBOX // dev_type + }, + { + DeviceUnknown, // dm_id + 0, // hw_dev_id + 0, // hw_rev_id + 0, // sw_dev_id + "Unknown Device", // name + -1, // port_num + DM_UNKNOWN // dev_type + }}; static const struct device_info* get_entry(dm_dev_id_t type) { - const struct device_info *p = g_devs_info; - while (p->dm_id != DeviceUnknown) { - if (type == p->dm_id) { + const struct device_info* p = g_devs_info; + while (p->dm_id != DeviceUnknown) + { + if (type == p->dm_id) + { break; } p++; @@ -462,10 +525,13 @@ static const struct device_info* get_entry_by_dev_rev_id(u_int32_t hw_dev_id, u_int32_t hw_rev_id) { - const struct device_info *p = g_devs_info; - while (p->dm_id != DeviceUnknown) { - if (hw_dev_id == p->hw_dev_id) { - if ((p->hw_rev_id == -1) || ((int)hw_rev_id == p->hw_rev_id)) { + const struct device_info* p = g_devs_info; + while (p->dm_id != DeviceUnknown) + { + if (hw_dev_id == p->hw_dev_id) + { + if ((p->hw_rev_id == -1) || ((int)hw_rev_id == p->hw_rev_id)) + { break; } } @@ -474,30 +540,25 @@ return p; } -/** - * Returns 0 on success and 1 on failure. - */ -int dm_get_device_id(mfile *mf, - dm_dev_id_t *ptr_dm_dev_id, - u_int32_t *ptr_hw_dev_id, - u_int32_t *ptr_hw_rev) +typedef enum get_dev_id_error_t +{ + GET_DEV_ID_SUCCESS, + GET_DEV_ID_ERROR, + CRSPACE_READ_ERROR, + CHECK_PTR_DEV_ID, +} dev_id_error; + +static int dm_get_device_id_inner(mfile* mf, dm_dev_id_t* ptr_dm_dev_id, u_int32_t* ptr_hw_dev_id, u_int32_t* ptr_hw_rev) { u_int32_t dword = 0; int rc; u_int32_t dev_flags; - //Special case: FPGA device: -#ifndef MST_UL - if (mf->tp == MST_FPGA_ICMD || mf->tp == MST_FPGA_DRIVER) { - *ptr_dm_dev_id = DeviceFPGANewton; - *ptr_hw_dev_id = 0xfff; - return 0; - } -#endif #ifdef CABLES_SUPP - if (mf->tp == MST_LINKX_CHIP){ - - switch (mf->linkx_chip_devid){ + if (mf->tp == MST_LINKX_CHIP) + { + switch (mf->linkx_chip_devid) + { case ARDBEG_REV0_DEVID: case ARDBEG_REV1_DEVID: case ARDBEG_MIRRORED_DEVID: @@ -512,114 +573,200 @@ case MENHIT_DEVID_VER2: *ptr_dm_dev_id = DeviceMenhit; break; + case ARCUS_P_TC_DEVID: + *ptr_dm_dev_id = DeviceArcusPTC; + break; + case ARCUS_P_REV0_DEVID: + *ptr_dm_dev_id = DeviceArcusP; + break; + case ARCUS_E_REV0_DEVID: + *ptr_dm_dev_id = DeviceArcusE; + break; default: - return 1; + return GET_DEV_ID_ERROR; break; } *ptr_hw_dev_id = (u_int32_t)mf->linkx_chip_devid; - return 0; - + return GET_DEV_ID_SUCCESS; } - if (mf->tp == MST_CABLE) { - //printf("-D- Getting cable ID\n"); - if (mread4(mf, CABLEID_ADDR, &dword) != 4) { - //printf("FATAL - crspace read (0x%x) failed: %s\n", DEVID_ADDR, strerror(errno)); - return 1; + if (mf->tp == MST_CABLE) + { + // printf("-D- Getting cable ID\n"); + if (mread4(mf, CABLEID_ADDR, &dword) != 4) + { + // printf("FATAL - crspace read (0x%x) failed: %s\n", DEVID_ADDR, strerror(errno)); + return GET_DEV_ID_ERROR; } - //dword = __cpu_to_le32(dword); // Cable pages are read in LE, no need to swap + // dword = __cpu_to_le32(dword); // Cable pages are read in LE, no need to swap *ptr_hw_dev_id = 0xffff; u_int8_t id = EXTRACT(dword, 0, 8); enum dm_dev_type cbl_type = getCableType(id); - if (cbl_type == DM_QSFP_CABLE) { + *ptr_hw_dev_id = id; + u_int8_t paging; + if (cbl_type == DM_QSFP_CABLE) + { // Get Byte 2 bit 2 ~ bit 18 (flat_mem : upper memory flat or paged. 0=paging, 1=page 0 only) - u_int8_t paging = EXTRACT(dword, 18, 1); - //printf("DWORD: %#x, paging: %d\n", dword, paging); - if (paging == 0) { + paging = EXTRACT(dword, 18, 1); + // printf("DWORD: %#x, paging: %d\n", dword, paging); + if (paging == 0) + { *ptr_dm_dev_id = DeviceCableQSFPaging; - } else { + } + else + { *ptr_dm_dev_id = DeviceCableQSFP; } - } else if (cbl_type == DM_SFP_CABLE) { + } + else if (cbl_type == DM_SFP_CABLE) + { *ptr_dm_dev_id = DeviceCableSFP; - if (mread4(mf, SFP_DIGITAL_DIAGNOSTIC_MONITORING_IMPLEMENTED_ADDR, &dword) != 4) { - //printf("FATAL - crspace read (0x%x) failed: %s\n", DEVID_ADDR, strerror(errno)); - return 1; + if (mread4(mf, SFP_DIGITAL_DIAGNOSTIC_MONITORING_IMPLEMENTED_ADDR, &dword) != 4) + { + // printf("FATAL - crspace read (0x%x) failed: %s\n", DEVID_ADDR, strerror(errno)); + return GET_DEV_ID_ERROR; } - u_int8_t byte = EXTRACT(dword, 6, 1); //Byte 92 bit 6 (digital diagnostic monitoring implemented) - if (byte) { + u_int8_t byte = EXTRACT(dword, 6, 1); // Byte 92 bit 6 (digital diagnostic monitoring implemented) + if (byte) + { *ptr_dm_dev_id = DeviceCableSFP51; - if (mread4(mf, SFP_PAGING_IMPLEMENTED_INDICATOR_ADDR, &dword) != 4) { - //printf("FATAL - crspace read (0x%x) failed: %s\n", DEVID_ADDR, strerror(errno)); - return 1; + if (mread4(mf, SFP_PAGING_IMPLEMENTED_INDICATOR_ADDR, &dword) != 4) + { + // printf("FATAL - crspace read (0x%x) failed: %s\n", DEVID_ADDR, strerror(errno)); + return GET_DEV_ID_ERROR; } - byte = EXTRACT(dword, 4, 1); //Byte 64 bit 4 (paging implemented indicator) - if (byte) { + byte = EXTRACT(dword, 4, 1); // Byte 64 bit 4 (paging implemented indicator) + if (byte) + { *ptr_dm_dev_id = DeviceCableSFP51Paging; } } - } else { + } + else if (cbl_type == DM_CMIS_CABLE) + { + // Get Byte 2 bit 7 ~ bit 23 (flat_mem : upper memory flat or paged. 0=paging, 1=page 0 only) + paging = EXTRACT(dword, 23, 1); + if (paging == 0) + { + *ptr_dm_dev_id = DeviceCableCMISPaging; + } + else + { + *ptr_dm_dev_id = DeviceCableCMIS; + } + } + else + { *ptr_dm_dev_id = DeviceUnknown; } - return 0; + return GET_DEV_ID_SUCCESS; } #endif - rc = mget_mdevs_flags(mf, &dev_flags); - if (rc) { + if (rc) + { dev_flags = 0; } // get hw id // Special case for MLNX OS getting dev_id using REG MGIR - if (dev_flags & MDEVS_MLNX_OS) { + if (dev_flags & MDEVS_MLNX_OS) + { reg_access_status_t rc; struct reg_access_hca_mgir mgir; memset(&mgir, 0, sizeof(mgir)); rc = reg_access_mgir(mf, REG_ACCESS_METHOD_GET, &mgir); - //printf("-D- RC[%s] -- REVID: %d -- DEVID: %d hw_dev_id: %d\n", m_err2str(rc), mgir.HWInfo.REVID, mgir.HWInfo.DEVID, mgir.HWInfo.hw_dev_id); - if (rc) { - dword = get_entry(DeviceSwitchX)->hw_dev_id; - *ptr_hw_rev = 0; - *ptr_hw_dev_id = get_entry(DeviceSwitchX)->hw_dev_id; - } else { + // printf("-D- RC[%s] -- REVID: %d -- DEVID: %d hw_dev_id: %d\n", m_err2str(rc), mgir.HWInfo.REVID, + // mgir.HWInfo.DEVID, mgir.HWInfo.hw_dev_id); + if (rc) + { + dword = get_entry(DeviceSwitchIB)->hw_dev_id; + *ptr_hw_rev = 0; + *ptr_hw_dev_id = get_entry(DeviceSwitchIB)->hw_dev_id; + } + else + { dword = mgir.hw_info.hw_dev_id; - if (dword == 0) { - dword = get_entry(DeviceSwitchX)->hw_dev_id; - *ptr_hw_dev_id = get_entry(DeviceSwitchX)->hw_dev_id; + if (dword == 0) + { + dword = get_entry(DeviceSwitchIB)->hw_dev_id; + *ptr_hw_dev_id = get_entry(DeviceSwitchIB)->hw_dev_id; *ptr_hw_rev = mgir.hw_info.device_hw_revision & 0xf; - } else { + } + else + { *ptr_hw_dev_id = dword; - *ptr_hw_rev = 0; //WA: MGIR should have also hw_rev_id and then we can use it. + *ptr_hw_rev = 0; // WA: MGIR should have also hw_rev_id and then we can use it. } } - } else { - if (mread4(mf, DEVID_ADDR, &dword) != 4) { - printf("FATAL - crspace read (0x%x) failed: %s\n", DEVID_ADDR, strerror(errno)); - return 1; + } + else + { + if (mread4(mf, DEVID_ADDR, &dword) != 4) + { + return CRSPACE_READ_ERROR; } *ptr_hw_dev_id = EXTRACT(dword, 0, 16); - *ptr_hw_rev = EXTRACT(dword, 16, 8); + *ptr_hw_rev = EXTRACT(dword, 16, 8); } *ptr_dm_dev_id = get_entry_by_dev_rev_id(*ptr_hw_dev_id, *ptr_hw_rev)->dm_id; + return CHECK_PTR_DEV_ID; +} - if (*ptr_dm_dev_id == DeviceUnknown) { +/** + * Returns 0 on success and 1 on failure. + */ +int dm_get_device_id(mfile* mf, dm_dev_id_t* ptr_dm_dev_id, u_int32_t* ptr_hw_dev_id, u_int32_t* ptr_hw_rev) +{ + int return_value = 1; + return_value = dm_get_device_id_inner(mf, ptr_dm_dev_id, ptr_hw_dev_id, ptr_hw_rev); + if (return_value == CRSPACE_READ_ERROR) + { + printf("FATAL - crspace read (0x%x) failed: %s\n", DEVID_ADDR, strerror(errno)); + return GET_DEV_ID_ERROR; + } + else if (return_value == CHECK_PTR_DEV_ID) + { + if (*ptr_dm_dev_id == DeviceUnknown) + { +// Due to issue 2719128, we prefer to skip this device +// and not filter using the 'pciconf' FreeBSD tool +#ifndef __FreeBSD__ + /* Dev id not matched in array */ + printf("FATAL - Can't find device id.\n"); +#endif + return MFE_UNSUPPORTED_DEVICE; + } + return GET_DEV_ID_SUCCESS; + } + return return_value; +} - /* Dev id not matched in array */ - printf("FATAL - Can't find device id.\n"); - return MFE_UNSUPPORTED_DEVICE; +// Due to issue 2846942, added this function to be used on mdevices) +int dm_get_device_id_without_prints(mfile* mf, + dm_dev_id_t* ptr_dm_dev_id, + u_int32_t* ptr_hw_dev_id, + u_int32_t* ptr_hw_rev) +{ + int return_value = 1; + return_value = dm_get_device_id_inner(mf, ptr_dm_dev_id, ptr_hw_dev_id, ptr_hw_rev); + if (return_value == CHECK_PTR_DEV_ID) + { + if (*ptr_dm_dev_id == DeviceUnknown) + { + return MFE_UNSUPPORTED_DEVICE; + } + return GET_DEV_ID_SUCCESS; } - return 0; + return return_value; } -int dm_get_device_id_offline(u_int32_t devid, - u_int32_t chip_rev, - dm_dev_id_t *ptr_dev_type) +int dm_get_device_id_offline(u_int32_t devid, u_int32_t chip_rev, dm_dev_id_t* ptr_dev_type) { *ptr_dev_type = get_entry_by_dev_rev_id(devid, chip_rev)->dm_id; - return *ptr_dev_type == DeviceUnknown ? MFE_UNSUPPORTED_DEVICE: MFE_OK; + return *ptr_dev_type == DeviceUnknown ? MFE_UNSUPPORTED_DEVICE : MFE_OK; } const char* dm_dev_type2str(dm_dev_id_t type) @@ -627,14 +774,64 @@ return get_entry(type)->name; } -dm_dev_id_t dm_dev_str2type(const char *str) +dm_dev_id_t dm_dev_str2type(const char* str) +{ + const struct device_info* p = g_devs_info; + if (!str) + { + return DeviceUnknown; + } + while (p->dm_id != DeviceUnknown) + { + if (strcmp(str, p->name) == 0) + { + return p->dm_id; + } + p++; + } + return DeviceUnknown; +} + +dm_dev_id_t dm_dev_aproxstr2type(const char* str) { - const struct device_info *p = g_devs_info; - if (!str) { + const struct device_info* p = g_devs_info; + char d_str[256]; + if (!str) + { return DeviceUnknown; } - while (p->dm_id != DeviceUnknown) { - if (strcmp(str, p->name) == 0) { + while (p->dm_id != DeviceUnknown) + { + // to lowercase + unsigned short i; + for (i = 0; i <= strlen(p->name); ++i) + { + if ((p->name)[i] >= 'A' && (p->name)[i] <= 'Z') + { + d_str[i] = (p->name)[i] + 'a' - 'A'; + } + else + { + d_str[i] = (p->name)[i]; + } + } + + if (strncmp(str, d_str, strlen(d_str)) == 0) + { + return p->dm_id; + } + p++; + } + return DeviceUnknown; +} + +dm_dev_id_t dm_dev_sw_id2type(int sw_dev_id) +{ + const struct device_info* p = g_devs_info; + while (p->dm_id != DeviceUnknown) + { + if (sw_dev_id == p->sw_dev_id) + { return p->dm_id; } p++; @@ -649,13 +846,29 @@ int dm_dev_is_hca(dm_dev_id_t type) { - return get_entry(type)->dev_type == DM_HCA; + return (!dm_dev_is_dummy(type)) && (get_entry(type)->dev_type == DM_HCA); +} + +int dm_dev_is_dummy(dm_dev_id_t type) +{ + return type == DeviceDummy; +} + +int dm_dev_is_gearbox(dm_dev_id_t type) +{ + return (get_entry(type)->dev_type == DM_GEARBOX); +} + +int dm_is_menhit(dm_dev_id_t type) +{ + return type == DeviceMenhit || type == DeviceArcusPTC || type == DeviceArcusP || type == DeviceArcusE; } int dm_dev_is_200g_speed_supported_hca(dm_dev_id_t type) { - bool isBlueField = (type == DeviceBlueField || type == DeviceBlueField2); - return !isBlueField && (dm_dev_is_hca(type) && (get_entry(type)->hw_dev_id >= get_entry(DeviceConnectX6)->hw_dev_id)); + bool isBlueField = (type == DeviceBlueField || type == DeviceBlueField2 || type == DeviceBlueField3); + return !isBlueField && + (dm_dev_is_hca(type) && (get_entry(type)->hw_dev_id >= get_entry(DeviceConnectX6)->hw_dev_id)); } int dm_dev_is_switch(dm_dev_id_t type) @@ -673,9 +886,25 @@ return get_entry(type)->dev_type == DM_BRIDGE; } +int dm_dev_is_qsfp_cable(dm_dev_id_t type) +{ + return get_entry(type)->dev_type == DM_QSFP_CABLE; +} + +int dm_dev_is_sfp_cable(dm_dev_id_t type) +{ + return get_entry(type)->dev_type == DM_SFP_CABLE; +} + +int dm_dev_is_cmis_cable(dm_dev_id_t type) +{ + return get_entry(type)->dev_type == DM_CMIS_CABLE; +} + int dm_dev_is_cable(dm_dev_id_t type) { - return (get_entry(type)->dev_type == DM_QSFP_CABLE || get_entry(type)->dev_type == DM_SFP_CABLE); + return (type == DeviceCable || dm_dev_is_qsfp_cable(type) || dm_dev_is_sfp_cable(type) || + dm_dev_is_cmis_cable(type)); } u_int32_t dm_get_hw_dev_id(dm_dev_id_t type) @@ -691,8 +920,8 @@ int dm_is_fpp_supported(dm_dev_id_t type) { // Function per port is supported only in HCAs that arrived after CX3 - const struct device_info *dp = get_entry(type); - return (dm_is_5th_gen_hca(dp->dm_id) || dm_is_newton(dp->dm_id)); + const struct device_info* dp = get_entry(type); + return dm_is_5th_gen_hca(dp->dm_id); } int dm_is_device_supported(dm_dev_id_t type) @@ -700,27 +929,33 @@ return get_entry(type)->dm_id != DeviceUnknown; } -int dm_is_livefish_mode(mfile *mf) +int dm_is_livefish_mode(mfile* mf) { - if (!mf || !mf->dinfo) { + if (!mf || !mf->dinfo) + { return 0; } - if (mf->tp == MST_SOFTWARE) { + if (mf->tp == MST_SOFTWARE) + { return 1; } dm_dev_id_t devid_t = DeviceUnknown; u_int32_t devid = 0; u_int32_t revid = 0; int rc = dm_get_device_id(mf, &devid_t, &devid, &revid); - if (rc) { + if (rc) + { // Could not detrmine , by default is not livefish return 0; } u_int32_t swid = mf->dinfo->pci.dev_id; - //printf("-D- swid: %#x, devid: %#x\n", swid, devid); - if (dm_is_4th_gen(devid_t) || dm_is_switchx(devid_t)) { + // printf("-D- swid: %#x, devid: %#x\n", swid, devid); + if (dm_is_4th_gen(devid_t)) + { return (devid == swid - 1); - } else { + } + else + { return (devid == swid); } @@ -729,9 +964,7 @@ int dm_is_4th_gen(dm_dev_id_t type) { - return (type == DeviceConnectX2 || - type == DeviceConnectX3 || - type == DeviceConnectX3Pro); + return (type == DeviceConnectX3 || type == DeviceConnectX3Pro); } int dm_is_5th_gen_hca(dm_dev_id_t type) @@ -739,33 +972,56 @@ return (dm_dev_is_hca(type) && !dm_is_4th_gen(type)); } -int dm_is_newton(dm_dev_id_t type) +int dm_is_connectib(dm_dev_id_t type) { - return (type == DeviceFPGANewton); + return (type == DeviceConnectIB); } -int dm_is_connectib(dm_dev_id_t type) +int dm_is_gb100(dm_dev_id_t type) { - return (type == DeviceConnectIB); + return (type == DeviceGB100); } -int dm_is_switchx(dm_dev_id_t type) +int dm_is_cx7(dm_dev_id_t type) { - return (type == DeviceSwitchX); + return (type == DeviceConnectX7); } int dm_is_new_gen_switch(dm_dev_id_t type) { - return (dm_dev_is_switch(type) && !dm_is_switchx(type)); + return dm_dev_is_switch(type); } int dm_dev_is_raven_family_switch(dm_dev_id_t type) { - return (dm_dev_is_switch(type) && (type == DeviceQuantum || type == DeviceQuantum2 || type == DeviceSpectrum2 || type == DeviceSpectrum3)); + return (dm_dev_is_switch(type) && + (type == DeviceQuantum || type == DeviceQuantum2 || type == DeviceQuantum3 || type == DeviceGB100 || + type == DeviceSpectrum2 || type == DeviceSpectrum3 || type == DeviceSpectrum4)); } int dm_dev_is_ib_switch(dm_dev_id_t type) { - return (dm_dev_is_switch(type) && (type == DeviceQuantum || type == DeviceQuantum2 || type == DeviceSwitchIB || type == DeviceSwitchIB2)); + return (dm_dev_is_switch(type) && (type == DeviceQuantum || type == DeviceQuantum2 || type == DeviceQuantum3 || + type == DeviceGB100 || type == DeviceSwitchIB || type == DeviceSwitchIB2)); } +int dm_dev_is_eth_switch(dm_dev_id_t type) +{ + return (dm_dev_is_switch(type) && + (type == DeviceSpectrum || type == DeviceSpectrum2 || type == DeviceSpectrum3 || type == DeviceSpectrum4)); +} + +int dm_dev_is_fs3(dm_dev_id_t type) +{ + return type == DeviceConnectIB || type == DeviceSwitchIB || type == DeviceConnectX4 || type == DeviceConnectX4LX || + type == DeviceSpectrum || type == DeviceSwitchIB2; +} + +int dm_dev_is_fs4(dm_dev_id_t type) +{ + return type == DeviceConnectX5 || type == DeviceConnectX6 || type == DeviceConnectX6DX || + type == DeviceConnectX6LX || type == DeviceConnectX7 || type == DeviceBlueField || + type == DeviceBlueField2 || type == DeviceBlueField3 || type == DeviceQuantum || type == DeviceQuantum2 || + type == DeviceSpectrum4 || type == DeviceSpectrum2 || type == DeviceSpectrum3 || type == DeviceGearBox || + type == DeviceGearBoxManager; +} \ No newline at end of file diff -Nru mstflint-4.17.0+1/dev_mgt/tools_dev_types.h mstflint-4.21.0+1/dev_mgt/tools_dev_types.h --- mstflint-4.17.0+1/dev_mgt/tools_dev_types.h 2021-06-29 08:49:48.000000000 +0000 +++ mstflint-4.21.0+1/dev_mgt/tools_dev_types.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,5 +1,6 @@ /* * Copyright (C) Jan 2013 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -43,75 +44,70 @@ #define TOOLS_DEV_TYPE_H #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif #include #include #include -enum dm_dev_id -{ - DeviceUnknown = -1, //Dummy Device - Marker for indicating error. - DeviceStartMarker = 0, // Dummy Device - Marker for first device - // to let user iterate from DeviceStartMarker to DeviceEndMarker - // Note: Call dm_is_device_supported() to see if a device is supported by the lib. - - DeviceInfiniScale = 0, // UnSupported - DeviceInfiniHost, // UnSupported - DeviceInfiniHostIIIEx, // UnSupported - DeviceInfiniHostIIIEx_MF,// UnSupported - DeviceInfiniScaleIII, // UnSupported - DeviceInfiniHostIIILx, // UnSupported - DeviceConnectX, // UnSupported - DeviceConnectX2, - DeviceInfiniScaleIV, - DeviceBridgeX, // UnSupported - DeviceSwitchX, - DeviceConnectX3, - DeviceConnectIB, - DeviceConnectX3Pro, - DeviceSwitchIB, - DeviceSpectrum, - DeviceQuantum, - DeviceConnectX4, - DeviceConnectX4LX, - DeviceConnectX5, - DeviceConnectX6, - DeviceBlueField, - DeviceBlueField2, - DeviceBlueField3, - DeviceFPGA, // UnSupported - DeviceSwitchIB2, - DeviceFPGANewton, - DeviceCable, - DeviceCableQSFP, - DeviceCableQSFPaging, - DeviceCableSFP, - DeviceCableSFP51, - DeviceCableSFP51Paging, - DeviceArdbeg, - DeviceBaritone, - DeviceMenhit, - DeviceSpectrum2, - DeviceDummy, - DeviceSecureHost, - DeviceConnectX6DX, - DeviceConnectX6LX, - DeviceConnectX7, - DeviceSpectrum3, // Firebird - DeviceSpectrum4, // Albatross - DeviceQuantum2, // Blackbird - DeviceGearBox, - DeviceGearBoxManager, - DeviceEndMarker // Dummy Device - Marker for indicating end of devices when iterating -}; + enum dm_dev_id + { + DeviceUnknown = -1, // Dummy Device - Marker for indicating error. + DeviceStartMarker = 0, // Dummy Device - Marker for first device + // to let user iterate from DeviceStartMarker to DeviceEndMarker + // Note: Call dm_is_device_supported() to see if a device is supported by the lib. + + DeviceConnectX3, + DeviceConnectIB, + DeviceConnectX3Pro, + DeviceSwitchIB, + DeviceSpectrum, + DeviceQuantum, + DeviceConnectX4, + DeviceConnectX4LX, + DeviceConnectX5, + DeviceConnectX6, + DeviceBlueField, + DeviceBlueField2, + DeviceBlueField3, + DeviceBlueField4, + DeviceSwitchIB2, + DeviceCable, + DeviceCableQSFP, + DeviceCableQSFPaging, + DeviceCableSFP, + DeviceCableSFP51, + DeviceCableSFP51Paging, + DeviceArdbeg, + DeviceBaritone, + DeviceMenhit, + DeviceSpectrum2, + DeviceDummy, + DeviceSecureHost, + DeviceConnectX6DX, + DeviceConnectX6LX, + DeviceConnectX7, + DeviceConnectX8, + DeviceSpectrum3, // Firebird + DeviceSpectrum4, // Albatross + DeviceQuantum2, // Blackbird + DeviceQuantum3, // Sunbird + DeviceGearBox, + DeviceGearBoxManager, + DeviceAbirGearBox, + DeviceCableCMIS, + DeviceCableCMISPaging, + DeviceGB100, // Blackwell + DeviceArcusPTC, // ArcusP Test Chip + DeviceArcusP, + DeviceArcusE, + DeviceEndMarker // Dummy Device - Marker for indicating end of devices when iterating + }; -enum hw_dev_id -{ - DeviceInfiniScale_HwId = 0x01b3, - DeviceSwitchX_HwId = 0x0245, - DeviceConnectX2_HwId = 0x190, + enum hw_dev_id + { DeviceConnectX3_HwId = 0x1f5, DeviceConnectIB_HwId = 0x1ff, DeviceConnectX3Pro_HwId = 0x1f7, @@ -124,18 +120,24 @@ DeviceConnectX6DX_HwId = 0x212, DeviceConnectX6LX_HwId = 0x216, DeviceConnectX7_HwId = 0x218, + DeviceConnectX8_HwId = 0x21e, DeviceBlueField_HwId = 0x211, DeviceBlueField2_HwId = 0x214, DeviceBlueField3_HwId = 0x21c, + DeviceBlueField4_HwId = 0x220, DeviceSwitchIB2_HwId = 0x24b, DeviceCableQSFP_HwId = 0x0d, DeviceCableQSFPaging_HwId = 0x11, + DeviceCableCMIS_HwId = 0x19, + DeviceCableCMISPaging_HwId = 0x19, DeviceCableSFP_HwId = 0x03, DeviceCableSFP51_HwId = 0x03, - DeviceCableSFP51Paging_HwId =0x03, + DeviceCableSFP51Paging_HwId = 0x03, DeviceSpectrum2_HwId = 0x24e, DeviceQuantum_HwId = 0x24d, DeviceQuantum2_HwId = 0x257, + DeviceQuantum3_HwId = 0x25b, + DeviceGB100_HwId = 0x2900, DeviceArdbeg_HwId = 0x6e, DeviceBaritone_HwId = 0x6b, DeviceMenhit_HwId = 0x72, @@ -143,115 +145,130 @@ DeviceSpectrum3_HwId = 0x250, DeviceSpectrum4_HwId = 0x254, DeviceGearBox_HwId = 0x252, - DeviceGearBoxManager_HwId = 0x253 -}; -typedef enum dm_dev_id dm_dev_id_t; - -/** - * Returns 0 on success and 1 on failure. - */ -int dm_get_device_id(mfile *mf, - dm_dev_id_t *ptr_dev_type, - u_int32_t *ptr_dev_id, - u_int32_t *ptr_chip_rev); - -/** - * Returns 0 on success and 1 on failure. - */ -int dm_get_device_id_offline(u_int32_t devid, - u_int32_t chip_rev, - dm_dev_id_t *ptr_dev_type); - - -/** - * Returns 1 if device is supported and 0 otherwise (library dependant) - */ -int dm_is_device_supported(dm_dev_id_t type); - - -/** - * Returns the device name as a "const char*" - */ -const char* dm_dev_type2str(dm_dev_id_t type); - -/** - * Returns the device id - */ -dm_dev_id_t dm_dev_str2type(const char *str); - -/** - * A predicate returning if the device is an hca - */ -int dm_dev_is_hca(dm_dev_id_t type); - -/** - * A predicate returning if the hca supports 200G speed and above - */ -int dm_dev_is_200g_speed_supported_hca(dm_dev_id_t type); - -/** - * A predicate returning if the device is a switch - */ -int dm_dev_is_switch(dm_dev_id_t type); - -/** - * A predicate returning if the switch supports 200G speed and above - */ -int dm_dev_is_200g_speed_supported_switch(dm_dev_id_t type); - -/** - * A predicate returning if the device is a bridge - */ -int dm_dev_is_bridge(dm_dev_id_t type); - -/** - * A predicate returning if the device is a Cable - */ -int dm_dev_is_cable(dm_dev_id_t type); - -/** - * Returns the max num of ports or -1 on error - */ -int dm_get_hw_ports_num(dm_dev_id_t type); - -/** - * Returns the HW device id of the givn device type or zero on failure. - */ -u_int32_t dm_get_hw_dev_id(dm_dev_id_t type); - -/** - * Returns the HW chip revision of the given device type or zero on failures, - * This is useful to distinguish between ConnectX2 and ConnectX. - */ -u_int32_t dm_get_hw_rev_id(dm_dev_id_t type); - -/** - * A predicate returning if the device supports Function Per Port - */ -int dm_is_fpp_supported(dm_dev_id_t type); - -int dm_is_4th_gen(dm_dev_id_t type); - -int dm_is_5th_gen_hca(dm_dev_id_t type); - -int dm_is_newton(dm_dev_id_t type); - -int dm_is_connectib(dm_dev_id_t type); - -int dm_is_switchx(dm_dev_id_t type); - -int dm_is_new_gen_switch(dm_dev_id_t type); - -int dm_dev_is_raven_family_switch(dm_dev_id_t type); - -int dm_dev_is_ib_switch(dm_dev_id_t type); -/* - * A predicate returning if the device is in LiveFish mode - */ -int dm_is_livefish_mode(mfile *mf); + DeviceGearBoxManager_HwId = 0x253, + DeviceAbirGearBox_HwId = 0x256, + DeviceArcusPTC_HwId = 0x7f, // ArcusP Test Chip + DeviceArcusP_HwId = 0x80, + DeviceArcusE_HwId = 0x81, + }; + typedef enum dm_dev_id dm_dev_id_t; + + /** + * Returns 0 on success and 1 on failure. + */ + int dm_get_device_id(mfile* mf, dm_dev_id_t* ptr_dev_type, u_int32_t* ptr_dev_id, u_int32_t* ptr_chip_rev); + + int dm_get_device_id_without_prints(mfile* mf, + dm_dev_id_t* ptr_dm_dev_id, + u_int32_t* ptr_hw_dev_id, + u_int32_t* ptr_hw_rev); + + /** + * Returns 0 on success and 1 on failure. + */ + int dm_get_device_id_offline(u_int32_t devid, u_int32_t chip_rev, dm_dev_id_t* ptr_dev_type); + + /** + * Returns 1 if device is supported and 0 otherwise (library dependant) + */ + int dm_is_device_supported(dm_dev_id_t type); + + /** + * Returns the device name as a "const char*" + */ + const char* dm_dev_type2str(dm_dev_id_t type); + + /** + * Returns the device id + */ + dm_dev_id_t dm_dev_str2type(const char* str); + + /** + * A predicate returning if the device is an hca + */ + int dm_dev_is_hca(dm_dev_id_t type); + + /** + * A predicate returning if the device is gearbox + */ + int dm_dev_is_gearbox(dm_dev_id_t type); + + /** + * A predicate returning if the device is menhit + */ + int dm_is_menhit(dm_dev_id_t type); + + /** + * A predicate returning if the device is dummy + */ + int dm_dev_is_dummy(dm_dev_id_t type); + + /** + * A predicate returning if the hca supports 200G speed and above + */ + int dm_dev_is_200g_speed_supported_hca(dm_dev_id_t type); + + /** + * A predicate returning if the device is a switch + */ + int dm_dev_is_switch(dm_dev_id_t type); + + /** + * A predicate returning if the switch supports 200G speed and above + */ + int dm_dev_is_200g_speed_supported_switch(dm_dev_id_t type); + + /** + * A predicate returning if the device is a bridge + */ + int dm_dev_is_bridge(dm_dev_id_t type); + + /** + * A predicate returning if the device is a Cable + */ + int dm_dev_is_cable(dm_dev_id_t type); + + /** + * Returns the max num of ports or -1 on error + */ + int dm_get_hw_ports_num(dm_dev_id_t type); + + /** + * Returns the HW device id of the givn device type or zero on failure. + */ + u_int32_t dm_get_hw_dev_id(dm_dev_id_t type); + + /** + * Returns the HW chip revision of the given device type or zero on failures, + * This is useful to distinguish between ConnectX2 and ConnectX. + */ + u_int32_t dm_get_hw_rev_id(dm_dev_id_t type); + + /** + * A predicate returning if the device supports Function Per Port + */ + int dm_is_fpp_supported(dm_dev_id_t type); + + int dm_is_4th_gen(dm_dev_id_t type); + + int dm_is_5th_gen_hca(dm_dev_id_t type); + + int dm_is_connectib(dm_dev_id_t type); + + int dm_is_new_gen_switch(dm_dev_id_t type); + + int dm_dev_is_raven_family_switch(dm_dev_id_t type); + + int dm_dev_is_ib_switch(dm_dev_id_t type); + + int dm_dev_is_eth_switch(dm_dev_id_t type); + /* + * A predicate returning if the device is in LiveFish mode + */ + int dm_is_livefish_mode(mfile* mf); #ifdef __cplusplus -} /* end of 'extern "C"' */ +} /* end of 'extern "C"' */ #endif #endif // TOOLS_DEV_TYPE_H diff -Nru mstflint-4.17.0+1/ext_libs/iniParser/dictionary.c mstflint-4.21.0+1/ext_libs/iniParser/dictionary.c --- mstflint-4.17.0+1/ext_libs/iniParser/dictionary.c 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/iniParser/dictionary.c 2022-07-31 09:33:57.000000000 +0000 @@ -20,13 +20,13 @@ #include /** Maximum value size for integers and doubles. */ -#define MAXVALSZ 1024 +#define MAXVALSZ 1024 /** Minimal allocated number of entries in a dictionary */ -#define DICTMINSZ 128 +#define DICTMINSZ 128 /** Invalid key token */ -#define DICT_INVALID_KEY ((char*)-1) +#define DICT_INVALID_KEY ((char*)-1) /*--------------------------------------------------------------------------- Private functions @@ -42,19 +42,20 @@ for systems that do not have it. */ /*--------------------------------------------------------------------------*/ -static char * xstrdup(const char * s) +static char* xstrdup(const char* s) { - char * t ; - size_t len ; + char* t; + size_t len; if (!s) - return NULL ; + return NULL; - len = strlen(s) + 1 ; - t = (char*) malloc(len) ; - if (t) { - memcpy(t, s, len) ; + len = strlen(s) + 1; + t = (char*)malloc(len); + if (t) + { + memcpy(t, s, len); } - return t ; + return t; } /*-------------------------------------------------------------------------*/ @@ -64,16 +65,17 @@ @return This function returns non-zero in case of failure */ /*--------------------------------------------------------------------------*/ -static int dictionary_grow(dictionary * d) +static int dictionary_grow(dictionary* d) { - char ** new_val ; - char ** new_key ; - unsigned * new_hash ; - - new_val = (char**) calloc(d->size * 2, sizeof *d->val); - new_key = (char**) calloc(d->size * 2, sizeof *d->key); - new_hash = (unsigned*) calloc(d->size * 2, sizeof *d->hash); - if (!new_val || !new_key || !new_hash) { + char** new_val; + char** new_key; + unsigned* new_hash; + + new_val = (char**)calloc(d->size * 2, sizeof *d->val); + new_key = (char**)calloc(d->size * 2, sizeof *d->key); + new_hash = (unsigned*)calloc(d->size * 2, sizeof *d->hash); + if (!new_val || !new_key || !new_hash) + { /* An allocation failed, leave the dictionary unchanged */ if (new_val) free(new_val); @@ -81,22 +83,22 @@ free(new_key); if (new_hash) free(new_hash); - return -1 ; + return -1; } /* Initialize the newly allocated space */ - memcpy(new_val, d->val, d->size * sizeof(char *)); - memcpy(new_key, d->key, d->size * sizeof(char *)); + memcpy(new_val, d->val, d->size * sizeof(char*)); + memcpy(new_key, d->key, d->size * sizeof(char*)); memcpy(new_hash, d->hash, d->size * sizeof(unsigned)); /* Delete previous data */ free(d->val); free(d->key); free(d->hash); /* Actually update the dictionary */ - d->size *= 2 ; + d->size *= 2; d->val = new_val; d->key = new_key; d->hash = new_hash; - return 0 ; + return 0; } /*--------------------------------------------------------------------------- @@ -114,25 +116,26 @@ by comparing the key itself in last resort. */ /*--------------------------------------------------------------------------*/ -unsigned dictionary_hash(const char * key) +unsigned dictionary_hash(const char* key) { - size_t len ; - unsigned hash ; - size_t i ; + size_t len; + unsigned hash; + size_t i; if (!key) - return 0 ; + return 0; len = strlen(key); - for (hash=0, i=0 ; i>6) ; - } - hash += (hash <<3); - hash ^= (hash >>11); - hash += (hash <<15); - return hash ; + for (hash = 0, i = 0; i < len; i++) + { + hash += (unsigned)key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; } /*-------------------------------------------------------------------------*/ @@ -146,22 +149,24 @@ dictionary, give size=0. */ /*-------------------------------------------------------------------------*/ -dictionary * dictionary_new(size_t size) +dictionary* dictionary_new(size_t size) { - dictionary * d ; + dictionary* d; /* If no size was specified, allocate space for DICTMINSZ */ - if (sizesize = size ; - d->val = (char**) calloc(size, sizeof *d->val); - d->key = (char**) calloc(size, sizeof *d->key); - d->hash = (unsigned*) calloc(size, sizeof *d->hash); + if (d) + { + d->size = size; + d->val = (char**)calloc(size, sizeof *d->val); + d->key = (char**)calloc(size, sizeof *d->key); + d->hash = (unsigned*)calloc(size, sizeof *d->hash); } - return d ; + return d; } /*-------------------------------------------------------------------------*/ @@ -173,22 +178,24 @@ Deallocate a dictionary object and all memory associated to it. */ /*--------------------------------------------------------------------------*/ -void dictionary_del(dictionary * d) +void dictionary_del(dictionary* d) { - ssize_t i ; + ssize_t i; - if (d==NULL) return ; - for (i=0 ; isize ; i++) { - if (d->key[i]!=NULL) + if (d == NULL) + return; + for (i = 0; i < d->size; i++) + { + if (d->key[i] != NULL) free(d->key[i]); - if (d->val[i]!=NULL) + if (d->val[i] != NULL) free(d->val[i]); } free(d->val); free(d->key); free(d->hash); free(d); - return ; + return; } /*-------------------------------------------------------------------------*/ @@ -205,24 +212,27 @@ dictionary object, you should not try to free it or modify it. */ /*--------------------------------------------------------------------------*/ -const char * dictionary_get(const dictionary * d, const char * key, const char * def) +const char* dictionary_get(const dictionary* d, const char* key, const char* def) { - unsigned hash ; - ssize_t i ; + unsigned hash; + ssize_t i; hash = dictionary_hash(key); - for (i=0 ; isize ; i++) { - if (d->key[i]==NULL) - continue ; + for (i = 0; i < d->size; i++) + { + if (d->key[i] == NULL) + continue; /* Compare hash */ - if (hash==d->hash[i]) { + if (hash == d->hash[i]) + { /* Compare string, to avoid hash collisions */ - if (!strcmp(key, d->key[i])) { - return d->val[i] ; + if (!strcmp(key, d->key[i])) + { + return d->val[i]; } } } - return def ; + return def; } /*-------------------------------------------------------------------------*/ @@ -251,35 +261,41 @@ This function returns non-zero in case of failure. */ /*--------------------------------------------------------------------------*/ -int dictionary_set(dictionary * d, const char * key, const char * val) +int dictionary_set(dictionary* d, const char* key, const char* val) { - ssize_t i ; - unsigned hash ; + ssize_t i; + unsigned hash; - if (d==NULL || key==NULL) return -1 ; + if (d == NULL || key == NULL) + return -1; /* Compute hash for this key */ - hash = dictionary_hash(key) ; + hash = dictionary_hash(key); /* Find if value is already in dictionary */ - if (d->n>0) { - for (i=0 ; isize ; i++) { - if (d->key[i]==NULL) - continue ; - if (hash==d->hash[i]) { /* Same hash value */ - if (!strcmp(key, d->key[i])) { /* Same key */ + if (d->n > 0) + { + for (i = 0; i < d->size; i++) + { + if (d->key[i] == NULL) + continue; + if (hash == d->hash[i]) + { /* Same hash value */ + if (!strcmp(key, d->key[i])) + { /* Same key */ /* Found a value: modify and return */ - if (d->val[i]!=NULL) + if (d->val[i] != NULL) free(d->val[i]); d->val[i] = (val ? xstrdup(val) : NULL); /* Value has been modified: return */ - return 0 ; + return 0; } } } } /* Add a new value */ /* See if dictionary needs to grow */ - if (d->n==d->size) { + if (d->n == d->size) + { /* Reached maximum size: reallocate dictionary */ if (dictionary_grow(d) != 0) return -1; @@ -288,15 +304,17 @@ /* Insert key in the first empty slot. Start at d->n and wrap at d->size. Because d->n < d->size this will necessarily terminate. */ - for (i=d->n ; d->key[i] ; ) { - if(++i == d->size) i = 0; + for (i = d->n; d->key[i];) + { + if (++i == d->size) + i = 0; } /* Copy key */ - d->key[i] = xstrdup(key); - d->val[i] = (val ? xstrdup(val) : NULL) ; + d->key[i] = xstrdup(key); + d->val[i] = (val ? xstrdup(val) : NULL); d->hash[i] = hash; - d->n ++ ; - return 0 ; + d->n++; + return 0; } /*-------------------------------------------------------------------------*/ @@ -310,41 +328,46 @@ key cannot be found. */ /*--------------------------------------------------------------------------*/ -void dictionary_unset(dictionary * d, const char * key) +void dictionary_unset(dictionary* d, const char* key) { - unsigned hash ; - ssize_t i ; + unsigned hash; + ssize_t i; - if (key == NULL || d == NULL) { + if (key == NULL || d == NULL) + { return; } hash = dictionary_hash(key); - for (i=0 ; isize ; i++) { - if (d->key[i]==NULL) - continue ; + for (i = 0; i < d->size; i++) + { + if (d->key[i] == NULL) + continue; /* Compare hash */ - if (hash==d->hash[i]) { + if (hash == d->hash[i]) + { /* Compare string, to avoid hash collisions */ - if (!strcmp(key, d->key[i])) { + if (!strcmp(key, d->key[i])) + { /* Found key */ - break ; + break; } } } - if (i>=d->size) + if (i >= d->size) /* Key not found */ - return ; + return; free(d->key[i]); - d->key[i] = NULL ; - if (d->val[i]!=NULL) { + d->key[i] = NULL; + if (d->val[i] != NULL) + { free(d->val[i]); - d->val[i] = NULL ; + d->val[i] = NULL; } - d->hash[i] = 0 ; - d->n -- ; - return ; + d->hash[i] = 0; + d->n--; + return; } /*-------------------------------------------------------------------------*/ @@ -359,21 +382,23 @@ output file pointers. */ /*--------------------------------------------------------------------------*/ -void dictionary_dump(const dictionary * d, FILE * out) +void dictionary_dump(const dictionary* d, FILE* out) { - ssize_t i ; + ssize_t i; - if (d==NULL || out==NULL) return ; - if (d->n<1) { + if (d == NULL || out == NULL) + return; + if (d->n < 1) + { fprintf(out, "empty dictionary\n"); - return ; + return; } - for (i=0 ; isize ; i++) { - if (d->key[i]) { - fprintf(out, "%20s\t[%s]\n", - d->key[i], - d->val[i] ? d->val[i] : "UNDEF"); + for (i = 0; i < d->size; i++) + { + if (d->key[i]) + { + fprintf(out, "%20s\t[%s]\n", d->key[i], d->val[i] ? d->val[i] : "UNDEF"); } } - return ; + return; } diff -Nru mstflint-4.17.0+1/ext_libs/iniParser/dictionary.h mstflint-4.21.0+1/ext_libs/iniParser/dictionary.h --- mstflint-4.17.0+1/ext_libs/iniParser/dictionary.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/iniParser/dictionary.h 2022-07-31 09:33:57.000000000 +0000 @@ -21,150 +21,151 @@ #include #include #include +#ifndef _MSC_VER #include - +#else +#include +#endif #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif -/*--------------------------------------------------------------------------- - New types - ---------------------------------------------------------------------------*/ - - -/*-------------------------------------------------------------------------*/ -/** - @brief Dictionary object - - This object contains a list of string/string associations. Each - association is identified by a unique string key. Looking up values - in the dictionary is speeded up by the use of a (hopefully collision-free) - hash function. - */ -/*-------------------------------------------------------------------------*/ -typedef struct _dictionary_ { - int n ; /** Number of entries in dictionary */ - ssize_t size ; /** Storage size */ - char ** val ; /** List of string values */ - char ** key ; /** List of string keys */ - unsigned * hash ; /** List of hash values for keys */ -} dictionary ; - - -/*--------------------------------------------------------------------------- - Function prototypes - ---------------------------------------------------------------------------*/ - -/*-------------------------------------------------------------------------*/ -/** - @brief Compute the hash key for a string. - @param key Character string to use for key. - @return 1 unsigned int on at least 32 bits. - - This hash function has been taken from an Article in Dr Dobbs Journal. - This is normally a collision-free function, distributing keys evenly. - The key is stored anyway in the struct so that collision can be avoided - by comparing the key itself in last resort. - */ -/*--------------------------------------------------------------------------*/ -unsigned dictionary_hash(const char * key); - -/*-------------------------------------------------------------------------*/ -/** - @brief Create a new dictionary object. - @param size Optional initial size of the dictionary. - @return 1 newly allocated dictionary objet. - - This function allocates a new dictionary object of given size and returns - it. If you do not know in advance (roughly) the number of entries in the - dictionary, give size=0. - */ -/*--------------------------------------------------------------------------*/ -dictionary * dictionary_new(size_t size); - -/*-------------------------------------------------------------------------*/ -/** - @brief Delete a dictionary object - @param d dictionary object to deallocate. - @return void - - Deallocate a dictionary object and all memory associated to it. - */ -/*--------------------------------------------------------------------------*/ -void dictionary_del(dictionary * vd); - -/*-------------------------------------------------------------------------*/ -/** - @brief Get a value from a dictionary. - @param d dictionary object to search. - @param key Key to look for in the dictionary. - @param def Default value to return if key not found. - @return 1 pointer to internally allocated character string. - - This function locates a key in a dictionary and returns a pointer to its - value, or the passed 'def' pointer if no such key can be found in - dictionary. The returned character pointer points to data internal to the - dictionary object, you should not try to free it or modify it. - */ -/*--------------------------------------------------------------------------*/ -const char * dictionary_get(const dictionary * d, const char * key, const char * def); - - -/*-------------------------------------------------------------------------*/ -/** - @brief Set a value in a dictionary. - @param d dictionary object to modify. - @param key Key to modify or add. - @param val Value to add. - @return int 0 if Ok, anything else otherwise - - If the given key is found in the dictionary, the associated value is - replaced by the provided one. If the key cannot be found in the - dictionary, it is added to it. - - It is Ok to provide a NULL value for val, but NULL values for the dictionary - or the key are considered as errors: the function will return immediately - in such a case. - - Notice that if you dictionary_set a variable to NULL, a call to - dictionary_get will return a NULL value: the variable will be found, and - its value (NULL) is returned. In other words, setting the variable - content to NULL is equivalent to deleting the variable from the - dictionary. It is not possible (in this implementation) to have a key in - the dictionary without value. - - This function returns non-zero in case of failure. - */ -/*--------------------------------------------------------------------------*/ -int dictionary_set(dictionary * vd, const char * key, const char * val); - -/*-------------------------------------------------------------------------*/ -/** - @brief Delete a key in a dictionary - @param d dictionary object to modify. - @param key Key to remove. - @return void - - This function deletes a key in a dictionary. Nothing is done if the - key cannot be found. - */ -/*--------------------------------------------------------------------------*/ -void dictionary_unset(dictionary * d, const char * key); - - -/*-------------------------------------------------------------------------*/ -/** - @brief Dump a dictionary to an opened file pointer. - @param d Dictionary to dump - @param f Opened file pointer. - @return void - - Dumps a dictionary onto an opened file pointer. Key pairs are printed out - as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as - output file pointers. - */ -/*--------------------------------------------------------------------------*/ -void dictionary_dump(const dictionary * d, FILE * out); + /*--------------------------------------------------------------------------- + New types + ---------------------------------------------------------------------------*/ + + /*-------------------------------------------------------------------------*/ + /** + @brief Dictionary object + + This object contains a list of string/string associations. Each + association is identified by a unique string key. Looking up values + in the dictionary is speeded up by the use of a (hopefully collision-free) + hash function. + */ + /*-------------------------------------------------------------------------*/ + typedef struct _dictionary_ + { + int n; /** Number of entries in dictionary */ + ssize_t size; /** Storage size */ + char** val; /** List of string values */ + char** key; /** List of string keys */ + unsigned* hash; /** List of hash values for keys */ + } dictionary; + + /*--------------------------------------------------------------------------- + Function prototypes + ---------------------------------------------------------------------------*/ + + /*-------------------------------------------------------------------------*/ + /** + @brief Compute the hash key for a string. + @param key Character string to use for key. + @return 1 unsigned int on at least 32 bits. + + This hash function has been taken from an Article in Dr Dobbs Journal. + This is normally a collision-free function, distributing keys evenly. + The key is stored anyway in the struct so that collision can be avoided + by comparing the key itself in last resort. + */ + /*--------------------------------------------------------------------------*/ + unsigned dictionary_hash(const char* key); + + /*-------------------------------------------------------------------------*/ + /** + @brief Create a new dictionary object. + @param size Optional initial size of the dictionary. + @return 1 newly allocated dictionary objet. + + This function allocates a new dictionary object of given size and returns + it. If you do not know in advance (roughly) the number of entries in the + dictionary, give size=0. + */ + /*--------------------------------------------------------------------------*/ + dictionary* dictionary_new(size_t size); + + /*-------------------------------------------------------------------------*/ + /** + @brief Delete a dictionary object + @param d dictionary object to deallocate. + @return void + + Deallocate a dictionary object and all memory associated to it. + */ + /*--------------------------------------------------------------------------*/ + void dictionary_del(dictionary* vd); + + /*-------------------------------------------------------------------------*/ + /** + @brief Get a value from a dictionary. + @param d dictionary object to search. + @param key Key to look for in the dictionary. + @param def Default value to return if key not found. + @return 1 pointer to internally allocated character string. + + This function locates a key in a dictionary and returns a pointer to its + value, or the passed 'def' pointer if no such key can be found in + dictionary. The returned character pointer points to data internal to the + dictionary object, you should not try to free it or modify it. + */ + /*--------------------------------------------------------------------------*/ + const char* dictionary_get(const dictionary* d, const char* key, const char* def); + + /*-------------------------------------------------------------------------*/ + /** + @brief Set a value in a dictionary. + @param d dictionary object to modify. + @param key Key to modify or add. + @param val Value to add. + @return int 0 if Ok, anything else otherwise + + If the given key is found in the dictionary, the associated value is + replaced by the provided one. If the key cannot be found in the + dictionary, it is added to it. + + It is Ok to provide a NULL value for val, but NULL values for the dictionary + or the key are considered as errors: the function will return immediately + in such a case. + + Notice that if you dictionary_set a variable to NULL, a call to + dictionary_get will return a NULL value: the variable will be found, and + its value (NULL) is returned. In other words, setting the variable + content to NULL is equivalent to deleting the variable from the + dictionary. It is not possible (in this implementation) to have a key in + the dictionary without value. + + This function returns non-zero in case of failure. + */ + /*--------------------------------------------------------------------------*/ + int dictionary_set(dictionary* vd, const char* key, const char* val); + + /*-------------------------------------------------------------------------*/ + /** + @brief Delete a key in a dictionary + @param d dictionary object to modify. + @param key Key to remove. + @return void + + This function deletes a key in a dictionary. Nothing is done if the + key cannot be found. + */ + /*--------------------------------------------------------------------------*/ + void dictionary_unset(dictionary* d, const char* key); + + /*-------------------------------------------------------------------------*/ + /** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump + @param f Opened file pointer. + @return void + + Dumps a dictionary onto an opened file pointer. Key pairs are printed out + as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as + output file pointers. + */ + /*--------------------------------------------------------------------------*/ + void dictionary_dump(const dictionary* d, FILE* out); #ifdef __cplusplus } diff -Nru mstflint-4.17.0+1/ext_libs/iniParser/iniparser.c mstflint-4.21.0+1/ext_libs/iniParser/iniparser.c --- mstflint-4.17.0+1/ext_libs/iniParser/iniparser.c 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/iniParser/iniparser.c 2022-07-31 09:33:57.000000000 +0000 @@ -12,8 +12,8 @@ #include "iniparser.h" /*---------------------------- Defines -------------------------------------*/ -#define ASCIILINESZ (1024) -#define INI_INVALID_KEY ((char*)-1) +#define ASCIILINESZ (1024) +#define INI_INVALID_KEY ((char*)-1) /*--------------------------------------------------------------------------- Private to this module @@ -21,14 +21,15 @@ /** * This enum stores the status for each parsed line (internal use only). */ -typedef enum _line_status_ { +typedef enum _line_status_ +{ LINE_UNPROCESSED, LINE_ERROR, LINE_EMPTY, LINE_COMMENT, LINE_SECTION, LINE_VALUE -} line_status ; +} line_status; /*-------------------------------------------------------------------------*/ /** @@ -42,18 +43,20 @@ At most len - 1 elements of the input string will be converted. */ /*--------------------------------------------------------------------------*/ -static const char * strlwc(const char * in, char *out, unsigned len) +static const char* strlwc(const char* in, char* out, unsigned len) { - unsigned i ; + unsigned i; - if (in==NULL || out == NULL || len==0) return NULL ; - i=0 ; - while (in[i] != '\0' && i < len-1) { + if (in == NULL || out == NULL || len == 0) + return NULL; + i = 0; + while (in[i] != '\0' && i < len - 1) + { out[i] = (char)tolower((int)in[i]); - i++ ; + i++; } out[i] = '\0'; - return out ; + return out; } /*-------------------------------------------------------------------------*/ @@ -66,19 +69,20 @@ for systems that do not have it. */ /*--------------------------------------------------------------------------*/ -static char * xstrdup(const char * s) +static char* xstrdup(const char* s) { - char * t ; - size_t len ; + char* t; + size_t len; if (!s) - return NULL ; + return NULL; - len = strlen(s) + 1 ; - t = (char*) malloc(len) ; - if (t) { - memcpy(t, s, len) ; + len = strlen(s) + 1; + t = (char*)malloc(len); + if (t) + { + memcpy(t, s, len); } - return t ; + return t; } /*-------------------------------------------------------------------------*/ @@ -88,23 +92,26 @@ @return unsigned New size of the string. */ /*--------------------------------------------------------------------------*/ -static unsigned strstrip(char * s) +static unsigned strstrip(char* s) { - char *last = NULL ; - char *dest = s; + char* last = NULL; + char* dest = s; - if (s==NULL) return 0; + if (s == NULL) + return 0; last = s + strlen(s); - while (isspace((int)*s) && *s) s++; - while (last > s) { - if (!isspace((int)*(last-1))) - break ; - last -- ; + while (isspace((int)*s) && *s) + s++; + while (last > s) + { + if (!isspace((int)*(last - 1))) + break; + last--; } *last = (char)0; - memmove(dest,s,last - s + 1); + memmove(dest, s, last - s + 1); return last - s; } @@ -113,14 +120,14 @@ @brief Default error callback for iniparser: wraps `fprintf(stderr, ...)`. */ /*--------------------------------------------------------------------------*/ -static int default_error_callback(const char *format, ...) +static int default_error_callback(const char* format, ...) { - int ret; - va_list argptr; - va_start(argptr, format); - ret = vfprintf(stderr, format, argptr); - va_end(argptr); - return ret; + int ret; + va_list argptr; + va_start(argptr, format); + ret = vfprintf(stderr, format, argptr); + va_end(argptr); + return ret; } static int (*iniparser_error_callback)(const char*, ...) = default_error_callback; @@ -134,13 +141,16 @@ as errback the error callback will be switched back to default. */ /*--------------------------------------------------------------------------*/ -void iniparser_set_error_callback(int (*errback)(const char *, ...)) +void iniparser_set_error_callback(int (*errback)(const char*, ...)) { - if (errback) { - iniparser_error_callback = errback; - } else { - iniparser_error_callback = default_error_callback; - } + if (errback) + { + iniparser_error_callback = errback; + } + else + { + iniparser_error_callback = default_error_callback; + } } /*-------------------------------------------------------------------------*/ @@ -161,21 +171,24 @@ This function returns -1 in case of error. */ /*--------------------------------------------------------------------------*/ -int iniparser_getnsec(const dictionary * d) +int iniparser_getnsec(const dictionary* d) { - int i ; - int nsec ; + int i; + int nsec; - if (d==NULL) return -1 ; - nsec=0 ; - for (i=0 ; isize ; i++) { - if (d->key[i]==NULL) - continue ; - if (strchr(d->key[i], ':')==NULL) { - nsec ++ ; + if (d == NULL) + return -1; + nsec = 0; + for (i = 0; i < d->size; i++) + { + if (d->key[i] == NULL) + continue; + if (strchr(d->key[i], ':') == NULL) + { + nsec++; } } - return nsec ; + return nsec; } /*-------------------------------------------------------------------------*/ @@ -192,26 +205,30 @@ This function returns NULL in case of error. */ /*--------------------------------------------------------------------------*/ -const char * iniparser_getsecname(const dictionary * d, int n) +const char* iniparser_getsecname(const dictionary* d, int n) { - int i ; - int foundsec ; + int i; + int foundsec; - if (d==NULL || n<0) return NULL ; - foundsec=0 ; - for (i=0 ; isize ; i++) { - if (d->key[i]==NULL) - continue ; - if (strchr(d->key[i], ':')==NULL) { - foundsec++ ; - if (foundsec>n) - break ; + if (d == NULL || n < 0) + return NULL; + foundsec = 0; + for (i = 0; i < d->size; i++) + { + if (d->key[i] == NULL) + continue; + if (strchr(d->key[i], ':') == NULL) + { + foundsec++; + if (foundsec > n) + break; } } - if (foundsec<=n) { - return NULL ; + if (foundsec <= n) + { + return NULL; } - return d->key[i] ; + return d->key[i]; } /*-------------------------------------------------------------------------*/ @@ -227,21 +244,26 @@ purposes mostly. */ /*--------------------------------------------------------------------------*/ -void iniparser_dump(const dictionary * d, FILE * f) +void iniparser_dump(const dictionary* d, FILE* f) { - int i ; + int i; - if (d==NULL || f==NULL) return ; - for (i=0 ; isize ; i++) { - if (d->key[i]==NULL) - continue ; - if (d->val[i]!=NULL) { + if (d == NULL || f == NULL) + return; + for (i = 0; i < d->size; i++) + { + if (d->key[i] == NULL) + continue; + if (d->val[i] != NULL) + { fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]); - } else { + } + else + { fprintf(f, "[%s]=UNDEF\n", d->key[i]); } } - return ; + return; } /*-------------------------------------------------------------------------*/ @@ -255,30 +277,34 @@ It is Ok to specify @c stderr or @c stdout as output files. */ /*--------------------------------------------------------------------------*/ -void iniparser_dump_ini(const dictionary * d, FILE * f) +void iniparser_dump_ini(const dictionary* d, FILE* f) { - int i ; - int nsec ; - const char * secname ; + int i; + int nsec; + const char* secname; - if (d==NULL || f==NULL) return ; + if (d == NULL || f == NULL) + return; nsec = iniparser_getnsec(d); - if (nsec<1) { + if (nsec < 1) + { /* No section in file: dump all keys as they are */ - for (i=0 ; isize ; i++) { - if (d->key[i]==NULL) - continue ; + for (i = 0; i < d->size; i++) + { + if (d->key[i] == NULL) + continue; fprintf(f, "%s = %s\n", d->key[i], d->val[i]); } - return ; + return; } - for (i=0 ; isize ; j++) { - if (d->key[j]==NULL) - continue ; - if (!strncmp(d->key[j], keym, seclen+1)) { - fprintf(f, - "%-30s = %s\n", - d->key[j]+seclen+1, - d->val[j] ? d->val[j] : ""); + for (j = 0; j < d->size; j++) + { + if (d->key[j] == NULL) + continue; + if (!strncmp(d->key[j], keym, seclen + 1)) + { + fprintf(f, "%-30s = %s\n", d->key[j] + seclen + 1, d->val[j] ? d->val[j] : ""); } } fprintf(f, "\n"); - return ; + return; } /*-------------------------------------------------------------------------*/ @@ -327,30 +354,32 @@ @return Number of keys in section */ /*--------------------------------------------------------------------------*/ -int iniparser_getsecnkeys(const dictionary * d, const char * s) +int iniparser_getsecnkeys(const dictionary* d, const char* s) { - int seclen, nkeys ; - char keym[ASCIILINESZ+1]; - int j ; + int seclen, nkeys; + char keym[ASCIILINESZ + 1]; + int j; nkeys = 0; - if (d==NULL) return nkeys; - if (! iniparser_find_entry(d, s)) return nkeys; + if (d == NULL) + return nkeys; + if (!iniparser_find_entry(d, s)) + return nkeys; - seclen = (int)strlen(s); + seclen = (int)strlen(s); strlwc(s, keym, sizeof(keym)); keym[seclen] = ':'; - for (j=0 ; jsize ; j++) { - if (d->key[j]==NULL) - continue ; - if (!strncmp(d->key[j], keym, seclen+1)) + for (j = 0; j < d->size; j++) + { + if (d->key[j] == NULL) + continue; + if (!strncmp(d->key[j], keym, seclen + 1)) nkeys++; } return nkeys; - } /*-------------------------------------------------------------------------*/ @@ -369,24 +398,28 @@ a string allocated in the dictionary; do not free or modify them. */ /*--------------------------------------------------------------------------*/ -const char ** iniparser_getseckeys(const dictionary * d, const char * s, const char ** keys) +const char** iniparser_getseckeys(const dictionary* d, const char* s, const char** keys) { - int i, j, seclen ; - char keym[ASCIILINESZ+1]; + int i, j, seclen; + char keym[ASCIILINESZ + 1]; - if (d==NULL || keys==NULL) return NULL; - if (! iniparser_find_entry(d, s)) return NULL; + if (d == NULL || keys == NULL) + return NULL; + if (!iniparser_find_entry(d, s)) + return NULL; - seclen = (int)strlen(s); + seclen = (int)strlen(s); strlwc(s, keym, sizeof(keym)); keym[seclen] = ':'; i = 0; - for (j=0 ; jsize ; j++) { - if (d->key[j]==NULL) - continue ; - if (!strncmp(d->key[j], keym, seclen+1)) { + for (j = 0; j < d->size; j++) + { + if (d->key[j] == NULL) + continue; + if (!strncmp(d->key[j], keym, seclen + 1)) + { keys[i] = d->key[j]; i++; } @@ -410,18 +443,18 @@ the dictionary, do not free or modify it. */ /*--------------------------------------------------------------------------*/ -const char * iniparser_getstring(const dictionary * d, const char * key, const char * def) +const char* iniparser_getstring(const dictionary* d, const char* key, const char* def) { - const char * lc_key ; - const char * sval ; - char tmp_str[ASCIILINESZ+1]; + const char* lc_key; + const char* sval; + char tmp_str[ASCIILINESZ + 1]; - if (d==NULL || key==NULL) - return def ; + if (d == NULL || key == NULL) + return def; lc_key = strlwc(key, tmp_str, sizeof(tmp_str)); sval = dictionary_get(d, lc_key, def); - return sval ; + return sval; } /*-------------------------------------------------------------------------*/ @@ -451,16 +484,16 @@ Credits: Thanks to A. Becker for suggesting strtol() */ /*--------------------------------------------------------------------------*/ -long int iniparser_getlongint(const dictionary * d, const char * key, long int notfound) +long int iniparser_getlongint(const dictionary* d, const char* key, long int notfound) { - const char * str ; + const char* str; str = iniparser_getstring(d, key, INI_INVALID_KEY); - if (str==INI_INVALID_KEY) return notfound ; + if (str == INI_INVALID_KEY) + return notfound; return strtol(str, NULL, 0); } - /*-------------------------------------------------------------------------*/ /** @brief Get the string associated to a key, convert to an int @@ -488,7 +521,7 @@ Credits: Thanks to A. Becker for suggesting strtol() */ /*--------------------------------------------------------------------------*/ -int iniparser_getint(const dictionary * d, const char * key, int notfound) +int iniparser_getint(const dictionary* d, const char* key, int notfound) { return (int)iniparser_getlongint(d, key, notfound); } @@ -506,12 +539,13 @@ the notfound value is returned. */ /*--------------------------------------------------------------------------*/ -double iniparser_getdouble(const dictionary * d, const char * key, double notfound) +double iniparser_getdouble(const dictionary* d, const char* key, double notfound) { - const char * str ; + const char* str; str = iniparser_getstring(d, key, INI_INVALID_KEY); - if (str==INI_INVALID_KEY) return notfound ; + if (str == INI_INVALID_KEY) + return notfound; return atof(str); } @@ -547,19 +581,25 @@ necessarily have to be 0 or 1. */ /*--------------------------------------------------------------------------*/ -int iniparser_getboolean(const dictionary * d, const char * key, int notfound) +int iniparser_getboolean(const dictionary* d, const char* key, int notfound) { - int ret ; - const char * c ; + int ret; + const char* c; c = iniparser_getstring(d, key, INI_INVALID_KEY); - if (c==INI_INVALID_KEY) return notfound ; - if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') { - ret = 1 ; - } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') { - ret = 0 ; - } else { - ret = notfound ; + if (c == INI_INVALID_KEY) + return notfound; + if (c[0] == 'y' || c[0] == 'Y' || c[0] == '1' || c[0] == 't' || c[0] == 'T') + { + ret = 1; + } + else if (c[0] == 'n' || c[0] == 'N' || c[0] == '0' || c[0] == 'f' || c[0] == 'F') + { + ret = 0; + } + else + { + ret = notfound; } return ret; } @@ -576,13 +616,14 @@ of querying for the presence of sections in a dictionary. */ /*--------------------------------------------------------------------------*/ -int iniparser_find_entry(const dictionary * ini, const char * entry) +int iniparser_find_entry(const dictionary* ini, const char* entry) { - int found=0 ; - if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) { - found = 1 ; + int found = 0; + if (iniparser_getstring(ini, entry, INI_INVALID_KEY) != INI_INVALID_KEY) + { + found = 1; } - return found ; + return found; } /*-------------------------------------------------------------------------*/ @@ -598,10 +639,10 @@ It is Ok to set val to NULL. */ /*--------------------------------------------------------------------------*/ -int iniparser_set(dictionary * ini, const char * entry, const char * val) +int iniparser_set(dictionary* ini, const char* entry, const char* val) { - char tmp_str[ASCIILINESZ+1]; - return dictionary_set(ini, strlwc(entry, tmp_str, sizeof(tmp_str)), val) ; + char tmp_str[ASCIILINESZ + 1]; + return dictionary_set(ini, strlwc(entry, tmp_str, sizeof(tmp_str)), val); } /*-------------------------------------------------------------------------*/ @@ -614,9 +655,9 @@ If the given entry can be found, it is deleted from the dictionary. */ /*--------------------------------------------------------------------------*/ -void iniparser_unset(dictionary * ini, const char * entry) +void iniparser_unset(dictionary* ini, const char* entry) { - char tmp_str[ASCIILINESZ+1]; + char tmp_str[ASCIILINESZ + 1]; dictionary_unset(ini, strlwc(entry, tmp_str, sizeof(tmp_str))); } @@ -630,40 +671,44 @@ @return line_status value */ /*--------------------------------------------------------------------------*/ -static line_status iniparser_line( - const char * input_line, - char * section, - char * key, - char * value) -{ - line_status sta ; - char * line = NULL; - size_t len ; +static line_status iniparser_line(const char* input_line, char* section, char* key, char* value) +{ + line_status sta; + char* line = NULL; + size_t len; line = xstrdup(input_line); len = strstrip(line); - sta = LINE_UNPROCESSED ; - if (len<1) { + sta = LINE_UNPROCESSED; + if (len < 1) + { /* Empty line */ - sta = LINE_EMPTY ; - } else if (line[0]=='#' || line[0]==';') { + sta = LINE_EMPTY; + } + else if (line[0] == '#' || line[0] == ';') + { /* Comment line */ - sta = LINE_COMMENT ; - } else if (line[0]=='[' && line[len-1]==']') { + sta = LINE_COMMENT; + } + else if (line[0] == '[' && line[len - 1] == ']') + { /* Section name */ sscanf(line, "[%[^]]", section); strstrip(section); strlwc(section, section, len); - sta = LINE_SECTION ; - } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2 - || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2) { + sta = LINE_SECTION; + } + else if (sscanf(line, "%[^=] = \"%[^\"]\"", key, value) == 2 || sscanf(line, "%[^=] = '%[^\']'", key, value) == 2) + { /* Usual key=value with quotes, with or without comments */ strstrip(key); strlwc(key, key, len); /* Don't strip spaces from values surrounded with quotes */ - sta = LINE_VALUE ; - } else if (sscanf (line, "%[^=] = %[^;#]", key, value) == 2) { + sta = LINE_VALUE; + } + else if (sscanf(line, "%[^=] = %[^;#]", key, value) == 2) + { /* Usual key=value without quotes, with or without comments */ strstrip(key); strlwc(key, key, len); @@ -672,12 +717,14 @@ * sscanf cannot handle '' or "" as empty values * this is done here */ - if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) { - value[0]=0 ; + if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) + { + value[0] = 0; } - sta = LINE_VALUE ; - } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2 - || sscanf(line, "%[^=] %[=]", key, value) == 2) { + sta = LINE_VALUE; + } + else if (sscanf(line, "%[^=] = %[;#]", key, value) == 2 || sscanf(line, "%[^=] %[=]", key, value) == 2) + { /* * Special cases: * key= @@ -686,15 +733,17 @@ */ strstrip(key); strlwc(key, key, len); - value[0]=0 ; - sta = LINE_VALUE ; - } else { + value[0] = 0; + sta = LINE_VALUE; + } + else + { /* Generate syntax error */ - sta = LINE_ERROR ; + sta = LINE_ERROR; } free(line); - return sta ; + return sta; } /*-------------------------------------------------------------------------*/ @@ -711,112 +760,116 @@ The returned dictionary must be freed using iniparser_freedict(). */ /*--------------------------------------------------------------------------*/ -dictionary * iniparser_load(const char * ininame) +dictionary* iniparser_load(const char* ininame) { - FILE * in ; - - char line [ASCIILINESZ+1] ; - char section [ASCIILINESZ+1] ; - char key [ASCIILINESZ+1] ; - char tmp [(ASCIILINESZ * 2) + 2] ; - char val [ASCIILINESZ+1] ; + FILE* in; - int last=0 ; - int len ; - int lineno=0 ; - int errs=0; - int mem_err=0; + char line[ASCIILINESZ + 1]; + char section[ASCIILINESZ + 1]; + char key[ASCIILINESZ + 1]; + char tmp[(ASCIILINESZ * 2) + 2]; + char val[ASCIILINESZ + 1]; + + int last = 0; + int len; + int lineno = 0; + int errs = 0; + int mem_err = 0; - dictionary * dict ; + dictionary* dict; - if ((in=fopen(ininame, "r"))==NULL) { + if ((in = fopen(ininame, "r")) == NULL) + { iniparser_error_callback("iniparser: cannot open %s\n", ininame); - return NULL ; + return NULL; } - dict = dictionary_new(0) ; - if (!dict) { + dict = dictionary_new(0); + if (!dict) + { fclose(in); - return NULL ; + return NULL; } - memset(line, 0, ASCIILINESZ); + memset(line, 0, ASCIILINESZ); memset(section, 0, ASCIILINESZ); - memset(key, 0, ASCIILINESZ); - memset(val, 0, ASCIILINESZ); - last=0 ; - - while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) { - lineno++ ; - len = (int)strlen(line)-1; - if (len<=0) + memset(key, 0, ASCIILINESZ); + memset(val, 0, ASCIILINESZ); + last = 0; + + while (fgets(line + last, ASCIILINESZ - last, in) != NULL) + { + lineno++; + len = (int)strlen(line) - 1; + if (len <= 0) continue; /* Safety check against buffer overflows */ - if (line[len]!='\n' && !feof(in)) { - iniparser_error_callback( - "iniparser: input line too long in %s (%d)\n", - ininame, - lineno); + if (line[len] != '\n' && !feof(in)) + { + iniparser_error_callback("iniparser: input line too long in %s (%d)\n", ininame, lineno); dictionary_del(dict); fclose(in); - return NULL ; + return NULL; } /* Get rid of \n and spaces at end of line */ - while ((len>=0) && - ((line[len]=='\n') || (isspace(line[len])))) { - line[len]=0 ; - len-- ; + while ((len >= 0) && ((line[len] == '\n') || (isspace(line[len])))) + { + line[len] = 0; + len--; } - if (len < 0) { /* Line was entirely \n and/or spaces */ + if (len < 0) + { /* Line was entirely \n and/or spaces */ len = 0; } /* Detect multi-line */ - if (line[len]=='\\') { + if (line[len] == '\\') + { /* Multi-line value */ - last=len ; - continue ; - } else { - last=0 ; + last = len; + continue; } - switch (iniparser_line(line, section, key, val)) { + else + { + last = 0; + } + switch (iniparser_line(line, section, key, val)) + { case LINE_EMPTY: case LINE_COMMENT: - break ; + break; case LINE_SECTION: - mem_err = dictionary_set(dict, section, NULL); - break ; + mem_err = dictionary_set(dict, section, NULL); + break; case LINE_VALUE: - sprintf(tmp, "%s:%s", section, key); - mem_err = dictionary_set(dict, tmp, val); - break ; + sprintf(tmp, "%s:%s", section, key); + mem_err = dictionary_set(dict, tmp, val); + break; case LINE_ERROR: - iniparser_error_callback( - "iniparser: syntax error in %s (%d):\n-> %s\n", - ininame, - lineno, - line); - errs++ ; - break; + iniparser_error_callback("iniparser: syntax error in %s (%d):\n-> %s\n", ininame, lineno, line); + errs++; + break; default: - break ; + break; } memset(line, 0, ASCIILINESZ); - last=0; - if (mem_err<0) { + last = 0; + if (mem_err < 0) + { iniparser_error_callback("iniparser: memory allocation failure\n"); - break ; + break; } } - if (errs) { + if (errs) + { dictionary_del(dict); - dict = NULL ; + dict = NULL; } fclose(in); - return dict ; + return dict; } /*-------------------------------------------------------------------------*/ @@ -830,7 +883,7 @@ gets out of the current context. */ /*--------------------------------------------------------------------------*/ -void iniparser_freedict(dictionary * d) +void iniparser_freedict(dictionary* d) { dictionary_del(d); } diff -Nru mstflint-4.17.0+1/ext_libs/iniParser/iniparser.h mstflint-4.21.0+1/ext_libs/iniParser/iniparser.h --- mstflint-4.17.0+1/ext_libs/iniParser/iniparser.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/iniParser/iniparser.h 2022-07-31 09:33:57.000000000 +0000 @@ -28,328 +28,323 @@ #include "dictionary.h" #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif -/*-------------------------------------------------------------------------*/ -/** - @brief Configure a function to receive the error messages. - @param errback Function to call. - - By default, the error will be printed on stderr. If a null pointer is passed - as errback the error callback will be switched back to default. - */ -/*--------------------------------------------------------------------------*/ - -void iniparser_set_error_callback(int (*errback)(const char *, ...)); - -/*-------------------------------------------------------------------------*/ -/** - @brief Get number of sections in a dictionary - @param d Dictionary to examine - @return int Number of sections found in dictionary - - This function returns the number of sections found in a dictionary. - The test to recognize sections is done on the string stored in the - dictionary: a section name is given as "section" whereas a key is - stored as "section:key", thus the test looks for entries that do not - contain a colon. - - This clearly fails in the case a section name contains a colon, but - this should simply be avoided. - - This function returns -1 in case of error. - */ -/*--------------------------------------------------------------------------*/ - -int iniparser_getnsec(const dictionary * d); - - -/*-------------------------------------------------------------------------*/ -/** - @brief Get name for section n in a dictionary. - @param d Dictionary to examine - @param n Section number (from 0 to nsec-1). - @return Pointer to char string - - This function locates the n-th section in a dictionary and returns - its name as a pointer to a string statically allocated inside the - dictionary. Do not free or modify the returned string! - - This function returns NULL in case of error. - */ -/*--------------------------------------------------------------------------*/ - -const char * iniparser_getsecname(const dictionary * d, int n); - - -/*-------------------------------------------------------------------------*/ -/** - @brief Save a dictionary to a loadable ini file - @param d Dictionary to dump - @param f Opened file pointer to dump to - @return void - - This function dumps a given dictionary into a loadable ini file. - It is Ok to specify @c stderr or @c stdout as output files. - */ -/*--------------------------------------------------------------------------*/ - -void iniparser_dump_ini(const dictionary * d, FILE * f); - -/*-------------------------------------------------------------------------*/ -/** - @brief Save a dictionary section to a loadable ini file - @param d Dictionary to dump - @param s Section name of dictionary to dump - @param f Opened file pointer to dump to - @return void - - This function dumps a given section of a given dictionary into a loadable ini - file. It is Ok to specify @c stderr or @c stdout as output files. - */ -/*--------------------------------------------------------------------------*/ - -void iniparser_dumpsection_ini(const dictionary * d, const char * s, FILE * f); - -/*-------------------------------------------------------------------------*/ -/** - @brief Dump a dictionary to an opened file pointer. - @param d Dictionary to dump. - @param f Opened file pointer to dump to. - @return void - - This function prints out the contents of a dictionary, one element by - line, onto the provided file pointer. It is OK to specify @c stderr - or @c stdout as output files. This function is meant for debugging - purposes mostly. - */ -/*--------------------------------------------------------------------------*/ -void iniparser_dump(const dictionary * d, FILE * f); - -/*-------------------------------------------------------------------------*/ -/** - @brief Get the number of keys in a section of a dictionary. - @param d Dictionary to examine - @param s Section name of dictionary to examine - @return Number of keys in section - */ -/*--------------------------------------------------------------------------*/ -int iniparser_getsecnkeys(const dictionary * d, const char * s); - -/*-------------------------------------------------------------------------*/ -/** - @brief Get the number of keys in a section of a dictionary. - @param d Dictionary to examine - @param s Section name of dictionary to examine - @param keys Already allocated array to store the keys in - @return The pointer passed as `keys` argument or NULL in case of error - - This function queries a dictionary and finds all keys in a given section. - The keys argument should be an array of pointers which size has been - determined by calling `iniparser_getsecnkeys` function prior to this one. - - Each pointer in the returned char pointer-to-pointer is pointing to - a string allocated in the dictionary; do not free or modify them. - */ -/*--------------------------------------------------------------------------*/ -const char ** iniparser_getseckeys(const dictionary * d, const char * s, const char ** keys); - - -/*-------------------------------------------------------------------------*/ -/** - @brief Get the string associated to a key - @param d Dictionary to search - @param key Key string to look for - @param def Default value to return if key not found. - @return pointer to statically allocated character string - - This function queries a dictionary for a key. A key as read from an - ini file is given as "section:key". If the key cannot be found, - the pointer passed as 'def' is returned. - The returned char pointer is pointing to a string allocated in - the dictionary, do not free or modify it. - */ -/*--------------------------------------------------------------------------*/ -const char * iniparser_getstring(const dictionary * d, const char * key, const char * def); - -/*-------------------------------------------------------------------------*/ -/** - @brief Get the string associated to a key, convert to an int - @param d Dictionary to search - @param key Key string to look for - @param notfound Value to return in case of error - @return integer - - This function queries a dictionary for a key. A key as read from an - ini file is given as "section:key". If the key cannot be found, - the notfound value is returned. - - Supported values for integers include the usual C notation - so decimal, octal (starting with 0) and hexadecimal (starting with 0x) - are supported. Examples: - - - "42" -> 42 - - "042" -> 34 (octal -> decimal) - - "0x42" -> 66 (hexa -> decimal) - - Warning: the conversion may overflow in various ways. Conversion is - totally outsourced to strtol(), see the associated man page for overflow - handling. - - Credits: Thanks to A. Becker for suggesting strtol() - */ -/*--------------------------------------------------------------------------*/ -int iniparser_getint(const dictionary * d, const char * key, int notfound); - -/*-------------------------------------------------------------------------*/ -/** - @brief Get the string associated to a key, convert to an long int - @param d Dictionary to search - @param key Key string to look for - @param notfound Value to return in case of error - @return integer - - This function queries a dictionary for a key. A key as read from an - ini file is given as "section:key". If the key cannot be found, - the notfound value is returned. - - Supported values for integers include the usual C notation - so decimal, octal (starting with 0) and hexadecimal (starting with 0x) - are supported. Examples: - - - "42" -> 42 - - "042" -> 34 (octal -> decimal) - - "0x42" -> 66 (hexa -> decimal) - - Warning: the conversion may overflow in various ways. Conversion is - totally outsourced to strtol(), see the associated man page for overflow - handling. - */ -/*--------------------------------------------------------------------------*/ -long int iniparser_getlongint(const dictionary * d, const char * key, long int notfound); - - -/*-------------------------------------------------------------------------*/ -/** - @brief Get the string associated to a key, convert to a double - @param d Dictionary to search - @param key Key string to look for - @param notfound Value to return in case of error - @return double - - This function queries a dictionary for a key. A key as read from an - ini file is given as "section:key". If the key cannot be found, - the notfound value is returned. - */ -/*--------------------------------------------------------------------------*/ -double iniparser_getdouble(const dictionary * d, const char * key, double notfound); - -/*-------------------------------------------------------------------------*/ -/** - @brief Get the string associated to a key, convert to a boolean - @param d Dictionary to search - @param key Key string to look for - @param notfound Value to return in case of error - @return integer - - This function queries a dictionary for a key. A key as read from an - ini file is given as "section:key". If the key cannot be found, - the notfound value is returned. - - A true boolean is found if one of the following is matched: - - - A string starting with 'y' - - A string starting with 'Y' - - A string starting with 't' - - A string starting with 'T' - - A string starting with '1' - - A false boolean is found if one of the following is matched: - - - A string starting with 'n' - - A string starting with 'N' - - A string starting with 'f' - - A string starting with 'F' - - A string starting with '0' - - The notfound value returned if no boolean is identified, does not - necessarily have to be 0 or 1. - */ -/*--------------------------------------------------------------------------*/ -int iniparser_getboolean(const dictionary * d, const char * key, int notfound); - - -/*-------------------------------------------------------------------------*/ -/** - @brief Set an entry in a dictionary. - @param ini Dictionary to modify. - @param entry Entry to modify (entry name) - @param val New value to associate to the entry. - @return int 0 if Ok, -1 otherwise. - - If the given entry can be found in the dictionary, it is modified to - contain the provided value. If it cannot be found, the entry is created. - It is Ok to set val to NULL. - */ -/*--------------------------------------------------------------------------*/ -int iniparser_set(dictionary * ini, const char * entry, const char * val); - - -/*-------------------------------------------------------------------------*/ -/** - @brief Delete an entry in a dictionary - @param ini Dictionary to modify - @param entry Entry to delete (entry name) - @return void - - If the given entry can be found, it is deleted from the dictionary. - */ -/*--------------------------------------------------------------------------*/ -void iniparser_unset(dictionary * ini, const char * entry); - -/*-------------------------------------------------------------------------*/ -/** - @brief Finds out if a given entry exists in a dictionary - @param ini Dictionary to search - @param entry Name of the entry to look for - @return integer 1 if entry exists, 0 otherwise - - Finds out if a given entry exists in the dictionary. Since sections - are stored as keys with NULL associated values, this is the only way - of querying for the presence of sections in a dictionary. - */ -/*--------------------------------------------------------------------------*/ -int iniparser_find_entry(const dictionary * ini, const char * entry) ; - -/*-------------------------------------------------------------------------*/ -/** - @brief Parse an ini file and return an allocated dictionary object - @param ininame Name of the ini file to read. - @return Pointer to newly allocated dictionary - - This is the parser for ini files. This function is called, providing - the name of the file to be read. It returns a dictionary object that - should not be accessed directly, but through accessor functions - instead. - - The returned dictionary must be freed using iniparser_freedict(). - */ -/*--------------------------------------------------------------------------*/ -dictionary * iniparser_load(const char * ininame); - -/*-------------------------------------------------------------------------*/ -/** - @brief Free all memory associated to an ini dictionary - @param d Dictionary to free - @return void - - Free all memory associated to an ini dictionary. - It is mandatory to call this function before the dictionary object - gets out of the current context. - */ -/*--------------------------------------------------------------------------*/ -void iniparser_freedict(dictionary * d); + /*-------------------------------------------------------------------------*/ + /** + @brief Configure a function to receive the error messages. + @param errback Function to call. + + By default, the error will be printed on stderr. If a null pointer is passed + as errback the error callback will be switched back to default. + */ + /*--------------------------------------------------------------------------*/ + + void iniparser_set_error_callback(int (*errback)(const char*, ...)); + + /*-------------------------------------------------------------------------*/ + /** + @brief Get number of sections in a dictionary + @param d Dictionary to examine + @return int Number of sections found in dictionary + + This function returns the number of sections found in a dictionary. + The test to recognize sections is done on the string stored in the + dictionary: a section name is given as "section" whereas a key is + stored as "section:key", thus the test looks for entries that do not + contain a colon. + + This clearly fails in the case a section name contains a colon, but + this should simply be avoided. + + This function returns -1 in case of error. + */ + /*--------------------------------------------------------------------------*/ + + int iniparser_getnsec(const dictionary* d); + + /*-------------------------------------------------------------------------*/ + /** + @brief Get name for section n in a dictionary. + @param d Dictionary to examine + @param n Section number (from 0 to nsec-1). + @return Pointer to char string + + This function locates the n-th section in a dictionary and returns + its name as a pointer to a string statically allocated inside the + dictionary. Do not free or modify the returned string! + + This function returns NULL in case of error. + */ + /*--------------------------------------------------------------------------*/ + + const char* iniparser_getsecname(const dictionary* d, int n); + + /*-------------------------------------------------------------------------*/ + /** + @brief Save a dictionary to a loadable ini file + @param d Dictionary to dump + @param f Opened file pointer to dump to + @return void + + This function dumps a given dictionary into a loadable ini file. + It is Ok to specify @c stderr or @c stdout as output files. + */ + /*--------------------------------------------------------------------------*/ + + void iniparser_dump_ini(const dictionary* d, FILE* f); + + /*-------------------------------------------------------------------------*/ + /** + @brief Save a dictionary section to a loadable ini file + @param d Dictionary to dump + @param s Section name of dictionary to dump + @param f Opened file pointer to dump to + @return void + + This function dumps a given section of a given dictionary into a loadable ini + file. It is Ok to specify @c stderr or @c stdout as output files. + */ + /*--------------------------------------------------------------------------*/ + + void iniparser_dumpsection_ini(const dictionary* d, const char* s, FILE* f); + + /*-------------------------------------------------------------------------*/ + /** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump. + @param f Opened file pointer to dump to. + @return void + + This function prints out the contents of a dictionary, one element by + line, onto the provided file pointer. It is OK to specify @c stderr + or @c stdout as output files. This function is meant for debugging + purposes mostly. + */ + /*--------------------------------------------------------------------------*/ + void iniparser_dump(const dictionary* d, FILE* f); + + /*-------------------------------------------------------------------------*/ + /** + @brief Get the number of keys in a section of a dictionary. + @param d Dictionary to examine + @param s Section name of dictionary to examine + @return Number of keys in section + */ + /*--------------------------------------------------------------------------*/ + int iniparser_getsecnkeys(const dictionary* d, const char* s); + + /*-------------------------------------------------------------------------*/ + /** + @brief Get the number of keys in a section of a dictionary. + @param d Dictionary to examine + @param s Section name of dictionary to examine + @param keys Already allocated array to store the keys in + @return The pointer passed as `keys` argument or NULL in case of error + + This function queries a dictionary and finds all keys in a given section. + The keys argument should be an array of pointers which size has been + determined by calling `iniparser_getsecnkeys` function prior to this one. + + Each pointer in the returned char pointer-to-pointer is pointing to + a string allocated in the dictionary; do not free or modify them. + */ + /*--------------------------------------------------------------------------*/ + const char** iniparser_getseckeys(const dictionary* d, const char* s, const char** keys); + + /*-------------------------------------------------------------------------*/ + /** + @brief Get the string associated to a key + @param d Dictionary to search + @param key Key string to look for + @param def Default value to return if key not found. + @return pointer to statically allocated character string + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the pointer passed as 'def' is returned. + The returned char pointer is pointing to a string allocated in + the dictionary, do not free or modify it. + */ + /*--------------------------------------------------------------------------*/ + const char* iniparser_getstring(const dictionary* d, const char* key, const char* def); + + /*-------------------------------------------------------------------------*/ + /** + @brief Get the string associated to a key, convert to an int + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + Supported values for integers include the usual C notation + so decimal, octal (starting with 0) and hexadecimal (starting with 0x) + are supported. Examples: + + - "42" -> 42 + - "042" -> 34 (octal -> decimal) + - "0x42" -> 66 (hexa -> decimal) + + Warning: the conversion may overflow in various ways. Conversion is + totally outsourced to strtol(), see the associated man page for overflow + handling. + + Credits: Thanks to A. Becker for suggesting strtol() + */ + /*--------------------------------------------------------------------------*/ + int iniparser_getint(const dictionary* d, const char* key, int notfound); + + /*-------------------------------------------------------------------------*/ + /** + @brief Get the string associated to a key, convert to an long int + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + Supported values for integers include the usual C notation + so decimal, octal (starting with 0) and hexadecimal (starting with 0x) + are supported. Examples: + + - "42" -> 42 + - "042" -> 34 (octal -> decimal) + - "0x42" -> 66 (hexa -> decimal) + + Warning: the conversion may overflow in various ways. Conversion is + totally outsourced to strtol(), see the associated man page for overflow + handling. + */ + /*--------------------------------------------------------------------------*/ + long int iniparser_getlongint(const dictionary* d, const char* key, long int notfound); + + /*-------------------------------------------------------------------------*/ + /** + @brief Get the string associated to a key, convert to a double + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return double + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + */ + /*--------------------------------------------------------------------------*/ + double iniparser_getdouble(const dictionary* d, const char* key, double notfound); + + /*-------------------------------------------------------------------------*/ + /** + @brief Get the string associated to a key, convert to a boolean + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + A true boolean is found if one of the following is matched: + + - A string starting with 'y' + - A string starting with 'Y' + - A string starting with 't' + - A string starting with 'T' + - A string starting with '1' + + A false boolean is found if one of the following is matched: + + - A string starting with 'n' + - A string starting with 'N' + - A string starting with 'f' + - A string starting with 'F' + - A string starting with '0' + + The notfound value returned if no boolean is identified, does not + necessarily have to be 0 or 1. + */ + /*--------------------------------------------------------------------------*/ + int iniparser_getboolean(const dictionary* d, const char* key, int notfound); + + /*-------------------------------------------------------------------------*/ + /** + @brief Set an entry in a dictionary. + @param ini Dictionary to modify. + @param entry Entry to modify (entry name) + @param val New value to associate to the entry. + @return int 0 if Ok, -1 otherwise. + + If the given entry can be found in the dictionary, it is modified to + contain the provided value. If it cannot be found, the entry is created. + It is Ok to set val to NULL. + */ + /*--------------------------------------------------------------------------*/ + int iniparser_set(dictionary* ini, const char* entry, const char* val); + + /*-------------------------------------------------------------------------*/ + /** + @brief Delete an entry in a dictionary + @param ini Dictionary to modify + @param entry Entry to delete (entry name) + @return void + + If the given entry can be found, it is deleted from the dictionary. + */ + /*--------------------------------------------------------------------------*/ + void iniparser_unset(dictionary* ini, const char* entry); + + /*-------------------------------------------------------------------------*/ + /** + @brief Finds out if a given entry exists in a dictionary + @param ini Dictionary to search + @param entry Name of the entry to look for + @return integer 1 if entry exists, 0 otherwise + + Finds out if a given entry exists in the dictionary. Since sections + are stored as keys with NULL associated values, this is the only way + of querying for the presence of sections in a dictionary. + */ + /*--------------------------------------------------------------------------*/ + int iniparser_find_entry(const dictionary* ini, const char* entry); + + /*-------------------------------------------------------------------------*/ + /** + @brief Parse an ini file and return an allocated dictionary object + @param ininame Name of the ini file to read. + @return Pointer to newly allocated dictionary + + This is the parser for ini files. This function is called, providing + the name of the file to be read. It returns a dictionary object that + should not be accessed directly, but through accessor functions + instead. + + The returned dictionary must be freed using iniparser_freedict(). + */ + /*--------------------------------------------------------------------------*/ + dictionary* iniparser_load(const char* ininame); + + /*-------------------------------------------------------------------------*/ + /** + @brief Free all memory associated to an ini dictionary + @param d Dictionary to free + @return void + + Free all memory associated to an ini dictionary. + It is mandatory to call this function before the dictionary object + gets out of the current context. + */ + /*--------------------------------------------------------------------------*/ + void iniparser_freedict(dictionary* d); #ifdef __cplusplus } diff -Nru mstflint-4.17.0+1/ext_libs/iniParser/Makefile.in mstflint-4.21.0+1/ext_libs/iniParser/Makefile.in --- mstflint-4.17.0+1/ext_libs/iniParser/Makefile.in 2021-06-29 08:51:08.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/iniParser/Makefile.in 2022-07-31 09:34:41.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.3 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2020 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, diff -Nru mstflint-4.17.0+1/ext_libs/iniParser/test/iniexample.c mstflint-4.21.0+1/ext_libs/iniParser/test/iniexample.c --- mstflint-4.17.0+1/ext_libs/iniParser/test/iniexample.c 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/iniParser/test/iniexample.c 2022-07-31 09:33:57.000000000 +0000 @@ -40,95 +40,96 @@ #include "iniparser.h" void create_example_ini_file(void); -int parse_ini_file(char * ini_name); +int parse_ini_file(char* ini_name); -int main(int argc, char * argv[]) +int main(int argc, char* argv[]) { - int status ; + int status; - if (argc<2) { - create_example_ini_file(); - status = parse_ini_file("example.ini"); - } else { - status = parse_ini_file(argv[1]); - } - return status ; + if (argc < 2) + { + create_example_ini_file(); + status = parse_ini_file("example.ini"); + } + else + { + status = parse_ini_file(argv[1]); + } + return status; } void create_example_ini_file(void) { - FILE * ini ; + FILE* ini; - ini = fopen("example.ini", "w"); - fprintf(ini, - "#\n" - "# This is an example of ini file\n" - "#\n" - "\n" - "[Pizza]\n" - "\n" - "Ham = yes ;\n" - "Mushrooms = TRUE ;\n" - "Capres = 0 ;\n" - "Cheese = Non ;\n" - "\n" - "\n" - "[Wine]\n" - "\n" - "Grape = Cabernet Sauvignon ;\n" - "Year = 1989 ;\n" - "Country = Spain ;\n" - "Alcohol = 12.5 ;\n" - "\n"); - fclose(ini); + ini = fopen("example.ini", "w"); + fprintf(ini, + "#\n" + "# This is an example of ini file\n" + "#\n" + "\n" + "[Pizza]\n" + "\n" + "Ham = yes ;\n" + "Mushrooms = TRUE ;\n" + "Capres = 0 ;\n" + "Cheese = Non ;\n" + "\n" + "\n" + "[Wine]\n" + "\n" + "Grape = Cabernet Sauvignon ;\n" + "Year = 1989 ;\n" + "Country = Spain ;\n" + "Alcohol = 12.5 ;\n" + "\n"); + fclose(ini); } - -int parse_ini_file(char * ini_name) +int parse_ini_file(char* ini_name) { - dictionary * ini ; + dictionary* ini; - /* Some temporary variables to hold query results */ - int b ; - int i ; - double d ; - char * s ; - - ini = iniparser_load(ini_name); - if (ini==NULL) { - fprintf(stderr, "cannot parse file: %s\n", ini_name); - return -1 ; - } - iniparser_dump(ini, stderr); - - /* Get pizza attributes */ - printf("Pizza:\n"); - - b = iniparser_getboolean(ini, "pizza:ham", -1); - printf("Ham: [%d]\n", b); - b = iniparser_getboolean(ini, "pizza:mushrooms", -1); - printf("Mushrooms: [%d]\n", b); - b = iniparser_getboolean(ini, "pizza:capres", -1); - printf("Capres: [%d]\n", b); - b = iniparser_getboolean(ini, "pizza:cheese", -1); - printf("Cheese: [%d]\n", b); - - /* Get wine attributes */ - printf("Wine:\n"); - s = iniparser_getstring(ini, "wine:grape", NULL); + /* Some temporary variables to hold query results */ + int b; + int i; + double d; + char* s; + + ini = iniparser_load(ini_name); + if (ini == NULL) + { + fprintf(stderr, "cannot parse file: %s\n", ini_name); + return -1; + } + iniparser_dump(ini, stderr); + + /* Get pizza attributes */ + printf("Pizza:\n"); + + b = iniparser_getboolean(ini, "pizza:ham", -1); + printf("Ham: [%d]\n", b); + b = iniparser_getboolean(ini, "pizza:mushrooms", -1); + printf("Mushrooms: [%d]\n", b); + b = iniparser_getboolean(ini, "pizza:capres", -1); + printf("Capres: [%d]\n", b); + b = iniparser_getboolean(ini, "pizza:cheese", -1); + printf("Cheese: [%d]\n", b); + + /* Get wine attributes */ + printf("Wine:\n"); + s = iniparser_getstring(ini, "wine:grape", NULL); printf("Grape: [%s]\n", s ? s : "UNDEF"); i = iniparser_getint(ini, "wine:year", -1); printf("Year: [%d]\n", i); - s = iniparser_getstring(ini, "wine:country", NULL); + s = iniparser_getstring(ini, "wine:country", NULL); printf("Country: [%s]\n", s ? s : "UNDEF"); d = iniparser_getdouble(ini, "wine:alcohol", -1.0); printf("Alcohol: [%g]\n", d); - iniparser_freedict(ini); - return 0 ; + iniparser_freedict(ini); + return 0; } - - diff -Nru mstflint-4.17.0+1/ext_libs/iniParser/test/parse.c mstflint-4.21.0+1/ext_libs/iniParser/test/parse.c --- mstflint-4.17.0+1/ext_libs/iniParser/test/parse.c 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/iniParser/test/parse.c 2022-07-31 09:33:57.000000000 +0000 @@ -39,20 +39,23 @@ #include "iniparser.h" -int main(int argc, char * argv[]) +int main(int argc, char* argv[]) { - dictionary * ini ; - char * ini_name ; + dictionary* ini; + char* ini_name; - if (argc<2) { + if (argc < 2) + { ini_name = "twisted.ini"; - } else { - ini_name = argv[1] ; - } + } + else + { + ini_name = argv[1]; + } ini = iniparser_load(ini_name); iniparser_dump(ini, stdout); iniparser_freedict(ini); - return 0 ; + return 0; } diff -Nru mstflint-4.17.0+1/ext_libs/iniParser/test/sandbox.c mstflint-4.21.0+1/ext_libs/iniParser/test/sandbox.c --- mstflint-4.17.0+1/ext_libs/iniParser/test/sandbox.c 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/iniParser/test/sandbox.c 2022-07-31 09:33:57.000000000 +0000 @@ -35,10 +35,11 @@ #include #include "../src/iniparser.h" -char *ini_file = "../example.ini"; +char* ini_file = "../example.ini"; -int main() { - dictionary* d = iniparser_load(ini_file); - iniparser_dump_ini(d, stdout); - iniparser_freedict(d); +int main() +{ + dictionary* d = iniparser_load(ini_file); + iniparser_dump_ini(d, stdout); + iniparser_freedict(d); } diff -Nru mstflint-4.17.0+1/ext_libs/iniParser/test/twisted-genhuge.py mstflint-4.21.0+1/ext_libs/iniParser/test/twisted-genhuge.py --- mstflint-4.17.0+1/ext_libs/iniParser/test/twisted-genhuge.py 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/iniParser/test/twisted-genhuge.py 2022-07-31 09:33:57.000000000 +0000 @@ -28,17 +28,16 @@ # 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. -#-- +# -- # -*- coding: utf-8 -*- import os import sys -if __name__=="__main__": - f=open('twisted-massive.ini', 'w') +if __name__ == "__main__": + f = open('twisted-massive.ini', 'w') for i in range(100): f.write('[%03d]\n' % i) for j in range(100): f.write('key-%03d=1;\n' % j) f.close() - diff -Nru mstflint-4.17.0+1/ext_libs/json/json/allocator.h mstflint-4.21.0+1/ext_libs/json/json/allocator.h --- mstflint-4.17.0+1/ext_libs/json/json/allocator.h 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/json/allocator.h 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,88 @@ +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_ALLOCATOR_H_INCLUDED +#define JSON_ALLOCATOR_H_INCLUDED + +#include +#include + +#pragma pack(push, 8) + +namespace Json { +template class SecureAllocator { +public: + // Type definitions + using value_type = T; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + /** + * Allocate memory for N items using the standard allocator. + */ + pointer allocate(size_type n) { + // allocate using "global operator new" + return static_cast(::operator new(n * sizeof(T))); + } + + /** + * Release memory which was allocated for N items at pointer P. + * + * The memory block is filled with zeroes before being released. + */ + void deallocate(pointer p, size_type n) { + // memset_s is used because memset may be optimized away by the compiler + memset_s(p, n * sizeof(T), 0, n * sizeof(T)); + // free using "global operator delete" + ::operator delete(p); + } + + /** + * Construct an item in-place at pointer P. + */ + template void construct(pointer p, Args&&... args) { + // construct using "placement new" and "perfect forwarding" + ::new (static_cast(p)) T(std::forward(args)...); + } + + size_type max_size() const { return size_t(-1) / sizeof(T); } + + pointer address(reference x) const { return std::addressof(x); } + + const_pointer address(const_reference x) const { return std::addressof(x); } + + /** + * Destroy an item in-place at pointer P. + */ + void destroy(pointer p) { + // destroy using "explicit destructor" + p->~T(); + } + + // Boilerplate + SecureAllocator() {} + template SecureAllocator(const SecureAllocator&) {} + template struct rebind { using other = SecureAllocator; }; +}; + +template +bool operator==(const SecureAllocator&, const SecureAllocator&) { + return true; +} + +template +bool operator!=(const SecureAllocator&, const SecureAllocator&) { + return false; +} + +} // namespace Json + +#pragma pack(pop) + +#endif // JSON_ALLOCATOR_H_INCLUDED diff -Nru mstflint-4.17.0+1/ext_libs/json/json/assertions.h mstflint-4.21.0+1/ext_libs/json/json/assertions.h --- mstflint-4.17.0+1/ext_libs/json/json/assertions.h 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/json/assertions.h 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,61 @@ +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_ASSERTIONS_H_INCLUDED +#define JSON_ASSERTIONS_H_INCLUDED + +#include +#include + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +/** It should not be possible for a maliciously designed file to + * cause an abort() or seg-fault, so these macros are used only + * for pre-condition violations and internal logic errors. + */ +#if JSON_USE_EXCEPTION + +// @todo <= add detail about condition in exception +#define JSON_ASSERT(condition) \ + do { \ + if (!(condition)) { \ + Json::throwLogicError("assert json failed"); \ + } \ + } while (0) + +#define JSON_FAIL_MESSAGE(message) \ + do { \ + OStringStream oss; \ + oss << message; \ + Json::throwLogicError(oss.str()); \ + abort(); \ + } while (0) + +#else // JSON_USE_EXCEPTION + +#define JSON_ASSERT(condition) assert(condition) + +// The call to assert() will show the failure message in debug builds. In +// release builds we abort, for a core-dump or debugger. +#define JSON_FAIL_MESSAGE(message) \ + { \ + OStringStream oss; \ + oss << message; \ + assert(false && oss.str().c_str()); \ + abort(); \ + } + +#endif + +#define JSON_ASSERT_MESSAGE(condition, message) \ + do { \ + if (!(condition)) { \ + JSON_FAIL_MESSAGE(message); \ + } \ + } while (0) + +#endif // JSON_ASSERTIONS_H_INCLUDED diff -Nru mstflint-4.17.0+1/ext_libs/json/json/autolink.h mstflint-4.21.0+1/ext_libs/json/json/autolink.h --- mstflint-4.17.0+1/ext_libs/json/json/autolink.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/json/autolink.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef JSON_AUTOLINK_H_INCLUDED -# define JSON_AUTOLINK_H_INCLUDED - -# include "config.h" - -# ifdef JSON_IN_CPPTL -# include -# endif - -# if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) && !defined(JSON_IN_CPPTL) -# define CPPTL_AUTOLINK_NAME "json" -# undef CPPTL_AUTOLINK_DLL -# ifdef JSON_DLL -# define CPPTL_AUTOLINK_DLL -# endif -# include "autolink.h" -# endif - -#endif // JSON_AUTOLINK_H_INCLUDED diff -Nru mstflint-4.17.0+1/ext_libs/json/json/config.h mstflint-4.21.0+1/ext_libs/json/json/config.h --- mstflint-4.17.0+1/ext_libs/json/json/config.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/json/config.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,48 +1,150 @@ -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #ifndef JSON_CONFIG_H_INCLUDED -# define JSON_CONFIG_H_INCLUDED - -/// If defined, indicates that json library is embedded in CppTL library. -//# define JSON_IN_CPPTL 1 - -/// If defined, indicates that json may leverage CppTL library -//# define JSON_USE_CPPTL 1 -/// If defined, indicates that cpptl vector based map should be used instead of std::map -/// as Value container. -//# define JSON_USE_CPPTL_SMALLMAP 1 -/// If defined, indicates that Json specific container should be used -/// (hash table & simple deque container with customizable allocator). -/// THIS FEATURE IS STILL EXPERIMENTAL! -//# define JSON_VALUE_USE_INTERNAL_MAP 1 -/// Force usage of standard new/malloc based allocator instead of memory pool based allocator. -/// The memory pools allocator used optimization (initializing Value and ValueInternalLink -/// as if it was a POD) that may cause some validation tool to report errors. -/// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined. -//# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1 - -/// If defined, indicates that Json use exception to report invalid type manipulation -/// instead of C assert macro. -# define JSON_USE_EXCEPTION 1 - -# ifdef JSON_IN_CPPTL -# include -# ifndef JSON_USE_CPPTL -# define JSON_USE_CPPTL 1 -# endif -# endif - -# ifdef JSON_IN_CPPTL -# define JSON_API CPPTL_API -# elif defined(JSON_DLL_BUILD) -# define JSON_API __declspec(dllexport) -# elif defined(JSON_DLL) -# define JSON_API __declspec(dllimport) -# else -# define JSON_API -# endif +#define JSON_CONFIG_H_INCLUDED +#include +#include +#include +#include +#include +#include +#include +#include + +// If non-zero, the library uses exceptions to report bad input instead of C +// assertion macros. The default is to use exceptions. +#ifndef JSON_USE_EXCEPTION +#define JSON_USE_EXCEPTION 1 +#endif + +// Temporary, tracked for removal with issue #982. +#ifndef JSON_USE_NULLREF +#define JSON_USE_NULLREF 1 +#endif + +/// If defined, indicates that the source file is amalgamated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgamated header. +// #define JSON_IS_AMALGAMATION + +// Export macros for DLL visibility +#if defined(JSON_DLL_BUILD) +#if defined(_MSC_VER) || defined(__MINGW32__) +#define JSON_API __declspec(dllexport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#elif defined(__GNUC__) || defined(__clang__) +#define JSON_API __attribute__((visibility("default"))) +#endif // if defined(_MSC_VER) + +#elif defined(JSON_DLL) +#if defined(_MSC_VER) || defined(__MINGW32__) +#define JSON_API __declspec(dllimport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#endif // ifdef JSON_DLL_BUILD + +#if !defined(JSON_API) +#define JSON_API +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1800 +#error \ + "ERROR: Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities" +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1900 +// As recommended at +// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 +extern JSON_API int msvc_pre1900_c99_snprintf(char* outBuf, size_t size, + const char* format, ...); +#define jsoncpp_snprintf msvc_pre1900_c99_snprintf +#else +#define jsoncpp_snprintf std::snprintf +#endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for +// integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools. +// C++11 should be used directly in JSONCPP. +#define JSONCPP_OVERRIDE override + +#ifdef __clang__ +#if __has_extension(attribute_deprecated_with_message) +#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) +#endif +#elif defined(__GNUC__) // not clang (gcc comes later since clang emulates gcc) +#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) +#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) +#endif // GNUC version +#elif defined(_MSC_VER) // MSVC (after clang because clang on Windows emulates + // MSVC) +#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#endif // __clang__ || __GNUC__ || _MSC_VER + +#if !defined(JSONCPP_DEPRECATED) +#define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6)) +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 +#endif + +#if !defined(JSON_IS_AMALGAMATION) + +#include "allocator.h" +#include "version.h" + +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { +using Int = int; +using UInt = unsigned int; +#if defined(JSON_NO_INT64) +using LargestInt = int; +using LargestUInt = unsigned int; +#undef JSON_HAS_INT64 +#else // if defined(JSON_NO_INT64) +// For Microsoft Visual use specific types as long long is not supported +#if defined(_MSC_VER) // Microsoft Visual Studio +using Int64 = __int64; +using UInt64 = unsigned __int64; +#else // if defined(_MSC_VER) // Other platforms, use long long +using Int64 = int64_t; +using UInt64 = uint64_t; +#endif // if defined(_MSC_VER) +using LargestInt = Int64; +using LargestUInt = UInt64; +#define JSON_HAS_INT64 +#endif // if defined(JSON_NO_INT64) + +template +using Allocator = + typename std::conditional, + std::allocator>::type; +using String = std::basic_string, Allocator>; +using IStringStream = + std::basic_istringstream; +using OStringStream = + std::basic_ostringstream; +using IStream = std::istream; +using OStream = std::ostream; +} // namespace Json + +// Legacy names (formerly macros). +using JSONCPP_STRING = Json::String; +using JSONCPP_ISTRINGSTREAM = Json::IStringStream; +using JSONCPP_OSTRINGSTREAM = Json::OStringStream; +using JSONCPP_ISTREAM = Json::IStream; +using JSONCPP_OSTREAM = Json::OStream; #endif // JSON_CONFIG_H_INCLUDED diff -Nru mstflint-4.17.0+1/ext_libs/json/json/features.h mstflint-4.21.0+1/ext_libs/json/json/features.h --- mstflint-4.17.0+1/ext_libs/json/json/features.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/json/features.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - - -#ifndef CPPTL_JSON_FEATURES_H_INCLUDED -# define CPPTL_JSON_FEATURES_H_INCLUDED - -# include "forwards.h" - -namespace Json { - - /** \brief Configuration passed to reader and writer. - * This configuration object can be used to force the Reader or Writer - * to behave in a standard conforming way. - */ - class JSON_API Features - { - public: - /** \brief A configuration that allows all features and assumes all strings are UTF-8. - * - C & C++ comments are allowed - * - Root object can be any JSON value - * - Assumes Value strings are encoded in UTF-8 - */ - static Features all(); - - /** \brief A configuration that is strictly compatible with the JSON specification. - * - Comments are forbidden. - * - Root object must be either an array or an object value. - * - Assumes Value strings are encoded in UTF-8 - */ - static Features strictMode(); - - /** \brief Initialize the configuration like JsonConfig::allFeatures; - */ - Features(); - - /// \c true if comments are allowed. Default: \c true. - bool allowComments_; - - /// \c true if root must be either an array or an object value. Default: \c false. - bool strictRoot_; - }; - -} // namespace Json - -#endif // CPPTL_JSON_FEATURES_H_INCLUDED diff -Nru mstflint-4.17.0+1/ext_libs/json/json/forwards.h mstflint-4.21.0+1/ext_libs/json/json/forwards.h --- mstflint-4.17.0+1/ext_libs/json/json/forwards.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/json/forwards.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,45 +1,43 @@ -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - #ifndef JSON_FORWARDS_H_INCLUDED -# define JSON_FORWARDS_H_INCLUDED +#define JSON_FORWARDS_H_INCLUDED -# include "config.h" +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) namespace Json { - // writer.h - class FastWriter; - class StyledWriter; - - // reader.h - class Reader; - - // features.h - class Features; - - // value.h - typedef int Int; - typedef unsigned int UInt; - class StaticString; - class Path; - class PathArgument; - class Value; - class ValueIteratorBase; - class ValueIterator; - class ValueConstIterator; -#ifdef JSON_VALUE_USE_INTERNAL_MAP - class ValueAllocator; - class ValueMapAllocator; - class ValueInternalLink; - class ValueInternalArray; - class ValueInternalMap; -#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP +// writer.h +class StreamWriter; +class StreamWriterBuilder; +class Writer; +class FastWriter; +class StyledWriter; +class StyledStreamWriter; + +// reader.h +class Reader; +class CharReader; +class CharReaderBuilder; + +// json_features.h +class Features; + +// value.h +using ArrayIndex = unsigned int; +class StaticString; +class Path; +class PathArgument; +class Value; +class ValueIteratorBase; +class ValueIterator; +class ValueConstIterator; } // namespace Json - #endif // JSON_FORWARDS_H_INCLUDED diff -Nru mstflint-4.17.0+1/ext_libs/json/json/json_features.h mstflint-4.21.0+1/ext_libs/json/json/json_features.h --- mstflint-4.17.0+1/ext_libs/json/json/json_features.h 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/json/json_features.h 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,61 @@ +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FEATURES_H_INCLUDED +#define JSON_FEATURES_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +#pragma pack(push, 8) + +namespace Json { + +/** \brief Configuration passed to reader and writer. + * This configuration object can be used to force the Reader or Writer + * to behave in a standard conforming way. + */ +class JSON_API Features { +public: + /** \brief A configuration that allows all features and assumes all strings + * are UTF-8. + * - C & C++ comments are allowed + * - Root object can be any JSON value + * - Assumes Value strings are encoded in UTF-8 + */ + static Features all(); + + /** \brief A configuration that is strictly compatible with the JSON + * specification. + * - Comments are forbidden. + * - Root object must be either an array or an object value. + * - Assumes Value strings are encoded in UTF-8 + */ + static Features strictMode(); + + /** \brief Initialize the configuration like JsonConfig::allFeatures; + */ + Features(); + + /// \c true if comments are allowed. Default: \c true. + bool allowComments_{true}; + + /// \c true if root must be either an array or an object value. Default: \c + /// false. + bool strictRoot_{false}; + + /// \c true if dropped null placeholders are allowed. Default: \c false. + bool allowDroppedNullPlaceholders_{false}; + + /// \c true if numeric object key are allowed. Default: \c false. + bool allowNumericKeys_{false}; +}; + +} // namespace Json + +#pragma pack(pop) + +#endif // JSON_FEATURES_H_INCLUDED diff -Nru mstflint-4.17.0+1/ext_libs/json/json/json.h mstflint-4.21.0+1/ext_libs/json/json/json.h --- mstflint-4.17.0+1/ext_libs/json/json/json.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/json/json.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,16 +1,15 @@ -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - #ifndef JSON_JSON_H_INCLUDED -# define JSON_JSON_H_INCLUDED +#define JSON_JSON_H_INCLUDED -# include "autolink.h" -# include "value.h" -# include "reader.h" -# include "writer.h" -# include "features.h" +#include "config.h" +#include "json_features.h" +#include "reader.h" +#include "value.h" +#include "writer.h" #endif // JSON_JSON_H_INCLUDED diff -Nru mstflint-4.17.0+1/ext_libs/json/json/reader.h mstflint-4.21.0+1/ext_libs/json/json/reader.h --- mstflint-4.17.0+1/ext_libs/json/json/reader.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/json/reader.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,202 +1,405 @@ -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE +#ifndef JSON_READER_H_INCLUDED +#define JSON_READER_H_INCLUDED -#ifndef CPPTL_JSON_READER_H_INCLUDED -# define CPPTL_JSON_READER_H_INCLUDED +#if !defined(JSON_IS_AMALGAMATION) +#include "json_features.h" +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) -# include "features.h" -# include "value.h" -# include -# include -# include -# include +#pragma pack(push, 8) namespace Json { - /** \brief Unserialize a JSON document into a Value. - * - */ - class JSON_API Reader - { - public: - typedef char Char; - typedef const Char *Location; - - /** \brief Constructs a Reader allowing all features - * for parsing. - */ - Reader(); - - /** \brief Constructs a Reader allowing the specified feature set - * for parsing. - */ - Reader( const Features &features ); - - /** \brief Read a Value from a JSON document. - * \param document UTF-8 encoded string containing the document to read. - * \param root [out] Contains the root value of the document if it was - * successfully parsed. - * \param collectComments \c true to collect comment and allow writing them back during - * serialization, \c false to discard comments. - * This parameter is ignored if Features::allowComments_ - * is \c false. - * \return \c true if the document was successfully parsed, \c false if an error occurred. - */ - bool parse( const std::string &document, - Value &root, - bool collectComments = true ); - - /** \brief Read a Value from a JSON document. - * \param document UTF-8 encoded string containing the document to read. - * \param root [out] Contains the root value of the document if it was - * successfully parsed. - * \param collectComments \c true to collect comment and allow writing them back during - * serialization, \c false to discard comments. - * This parameter is ignored if Features::allowComments_ - * is \c false. - * \return \c true if the document was successfully parsed, \c false if an error occurred. - */ - bool parse( const char *beginDoc, const char *endDoc, - Value &root, - bool collectComments = true ); - - /// \brief Parse from input stream. - /// \see Json::operator>>(std::istream&, Json::Value&). - bool parse( std::istream &is, - Value &root, - bool collectComments = true ); - - /** \brief Returns a user friendly string that list errors in the parsed document. - * \return Formatted error message with the list of errors with their location in - * the parsed document. An empty string is returned if no error occurred - * during parsing. - */ - std::string getFormatedErrorMessages() const; - - private: - enum TokenType - { - tokenEndOfStream = 0, - tokenObjectBegin, - tokenObjectEnd, - tokenArrayBegin, - tokenArrayEnd, - tokenString, - tokenNumber, - tokenTrue, - tokenFalse, - tokenNull, - tokenArraySeparator, - tokenMemberSeparator, - tokenComment, - tokenError - }; - - class Token - { - public: - TokenType type_; - Location start_; - Location end_; - }; - - class ErrorInfo - { - public: - Token token_; - std::string message_; - Location extra_; - }; - - typedef std::deque Errors; - - bool expectToken( TokenType type, Token &token, const char *message ); - bool readToken( Token &token ); - void skipSpaces(); - bool match( Location pattern, - int patternLength ); - bool readComment(); - bool readCStyleComment(); - bool readCppStyleComment(); - bool readString(); - void readNumber(); - bool readValue(); - bool readObject( Token &token ); - bool readArray( Token &token ); - bool decodeNumber( Token &token ); - bool decodeString( Token &token ); - bool decodeString( Token &token, std::string &decoded ); - bool decodeDouble( Token &token ); - bool decodeUnicodeCodePoint( Token &token, - Location ¤t, - Location end, - unsigned int &unicode ); - bool decodeUnicodeEscapeSequence( Token &token, - Location ¤t, - Location end, - unsigned int &unicode ); - bool addError( const std::string &message, - Token &token, - Location extra = 0 ); - bool recoverFromError( TokenType skipUntilToken ); - bool addErrorAndRecover( const std::string &message, - Token &token, - TokenType skipUntilToken ); - void skipUntilSpace(); - Value ¤tValue(); - Char getNextChar(); - void getLocationLineAndColumn( Location location, - int &line, - int &column ) const; - std::string getLocationLineAndColumn( Location location ) const; - void addComment( Location begin, - Location end, - CommentPlacement placement ); - void skipCommentTokens( Token &token ); - - typedef std::stack Nodes; - Nodes nodes_; - Errors errors_; - std::string document_; - Location begin_; - Location end_; - Location current_; - Location lastValueEnd_; - Value *lastValue_; - std::string commentsBefore_; - Features features_; - bool collectComments_; - }; - - /** \brief Read from 'sin' into 'root'. - - Always keep comments from the input JSON. - - This can be used to read a file into a particular sub-object. - For example: - \code - Json::Value root; - cin >> root["dir"]["file"]; - cout << root; - \endcode - Result: - \verbatim - { - "dir": { - "file": { - // The input stream JSON would be nested here. - } - } - } - \endverbatim - \throw std::exception on parse error. - \see Json::operator<<() +/** \brief Unserialize a JSON document into a + * Value. + * + * \deprecated Use CharReader and CharReaderBuilder. + */ + +class JSON_API Reader { +public: + using Char = char; + using Location = const Char*; + + /** \brief An error tagged with where in the JSON text it was encountered. + * + * The offsets give the [start, limit) range of bytes within the text. Note + * that this is bytes, not codepoints. */ - std::istream& operator>>( std::istream&, Value& ); + struct StructuredError { + ptrdiff_t offset_start; + ptrdiff_t offset_limit; + String message; + }; + + /** \brief Constructs a Reader allowing all features for parsing. + * \deprecated Use CharReader and CharReaderBuilder. + */ + Reader(); + + /** \brief Constructs a Reader allowing the specified feature set for parsing. + * \deprecated Use CharReader and CharReaderBuilder. + */ + Reader(const Features& features); + + /** \brief Read a Value from a JSON + * document. + * + * \param document UTF-8 encoded string containing the document + * to read. + * \param[out] root Contains the root value of the document if it + * was successfully parsed. + * \param collectComments \c true to collect comment and allow writing + * them back during serialization, \c false to + * discard comments. This parameter is ignored + * if Features::allowComments_ is \c false. + * \return \c true if the document was successfully parsed, \c false if an + * error occurred. + */ + bool parse(const std::string& document, Value& root, + bool collectComments = true); + + /** \brief Read a Value from a JSON + * document. + * + * \param beginDoc Pointer on the beginning of the UTF-8 encoded + * string of the document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string + * of the document to read. Must be >= beginDoc. + * \param[out] root Contains the root value of the document if it + * was successfully parsed. + * \param collectComments \c true to collect comment and allow writing + * them back during serialization, \c false to + * discard comments. This parameter is ignored + * if Features::allowComments_ is \c false. + * \return \c true if the document was successfully parsed, \c false if an + * error occurred. + */ + bool parse(const char* beginDoc, const char* endDoc, Value& root, + bool collectComments = true); + + /// \brief Parse from input stream. + /// \see Json::operator>>(std::istream&, Json::Value&). + bool parse(IStream& is, Value& root, bool collectComments = true); + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * + * \return Formatted error message with the list of errors with their + * location in the parsed document. An empty string is returned if no error + * occurred during parsing. + * \deprecated Use getFormattedErrorMessages() instead (typo fix). + */ + JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.") + String getFormatedErrorMessages() const; + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * + * \return Formatted error message with the list of errors with their + * location in the parsed document. An empty string is returned if no error + * occurred during parsing. + */ + String getFormattedErrorMessages() const; + + /** \brief Returns a vector of structured errors encountered while parsing. + * + * \return A (possibly empty) vector of StructuredError objects. Currently + * only one error can be returned, but the caller should tolerate multiple + * errors. This can occur if the parser recovers from a non-fatal parse + * error and then encounters additional errors. + */ + std::vector getStructuredErrors() const; + + /** \brief Add a semantic error message. + * + * \param value JSON Value location associated with the error + * \param message The error message. + * \return \c true if the error was successfully added, \c false if the Value + * offset exceeds the document size. + */ + bool pushError(const Value& value, const String& message); + + /** \brief Add a semantic error message with extra context. + * + * \param value JSON Value location associated with the error + * \param message The error message. + * \param extra Additional JSON Value location to contextualize the error + * \return \c true if the error was successfully added, \c false if either + * Value offset exceeds the document size. + */ + bool pushError(const Value& value, const String& message, const Value& extra); + + /** \brief Return whether there are any errors. + * + * \return \c true if there are no errors to report \c false if errors have + * occurred. + */ + bool good() const; + +private: + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + String message_; + Location extra_; + }; + + using Errors = std::deque; + + bool readToken(Token& token); + void skipSpaces(); + bool match(const Char* pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + void readNumber(); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, String& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + bool decodeUnicodeCodePoint(Token& token, Location& current, Location end, + unsigned int& unicode); + bool decodeUnicodeEscapeSequence(Token& token, Location& current, + Location end, unsigned int& unicode); + bool addError(const String& message, Token& token, Location extra = nullptr); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const String& message, Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void getLocationLineAndColumn(Location location, int& line, + int& column) const; + String getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + static bool containsNewLine(Location begin, Location end); + static String normalizeEOL(Location begin, Location end); + + using Nodes = std::stack; + Nodes nodes_; + Errors errors_; + String document_; + Location begin_{}; + Location end_{}; + Location current_{}; + Location lastValueEnd_{}; + Value* lastValue_{}; + String commentsBefore_; + Features features_; + bool collectComments_{}; +}; // Reader + +/** Interface for reading JSON from a char array. + */ +class JSON_API CharReader { +public: + virtual ~CharReader() = default; + /** \brief Read a Value from a JSON + * document. The document must be a UTF-8 encoded string containing the + * document to read. + * + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string + * of the document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + * document to read. Must be >= beginDoc. + * \param[out] root Contains the root value of the document if it was + * successfully parsed. + * \param[out] errs Formatted error messages (if not NULL) a user + * friendly string that lists errors in the parsed + * document. + * \return \c true if the document was successfully parsed, \c false if an + * error occurred. + */ + virtual bool parse(char const* beginDoc, char const* endDoc, Value* root, + String* errs) = 0; + + class JSON_API Factory { + public: + virtual ~Factory() = default; + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual CharReader* newCharReader() const = 0; + }; // Factory +}; // CharReader + +/** \brief Build a CharReader implementation. + * + * Usage: + * \code + * using namespace Json; + * CharReaderBuilder builder; + * builder["collectComments"] = false; + * Value value; + * String errs; + * bool ok = parseFromStream(builder, std::cin, &value, &errs); + * \endcode + */ +class JSON_API CharReaderBuilder : public CharReader::Factory { +public: + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + * These are case-sensitive. + * Available settings (case-sensitive): + * - `"collectComments": false or true` + * - true to collect comment and allow writing them back during + * serialization, false to discard comments. This parameter is ignored + * if allowComments is false. + * - `"allowComments": false or true` + * - true if comments are allowed. + * - `"allowTrailingCommas": false or true` + * - true if trailing commas in objects and arrays are allowed. + * - `"strictRoot": false or true` + * - true if root must be either an array or an object value + * - `"allowDroppedNullPlaceholders": false or true` + * - true if dropped null placeholders are allowed. (See + * StreamWriterBuilder.) + * - `"allowNumericKeys": false or true` + * - true if numeric object keys are allowed. + * - `"allowSingleQuotes": false or true` + * - true if '' are allowed for strings (both keys and values) + * - `"stackLimit": integer` + * - Exceeding stackLimit (recursive depth of `readValue()`) will cause an + * exception. + * - This is a security issue (seg-faults caused by deeply nested JSON), so + * the default is low. + * - `"failIfExtra": false or true` + * - If true, `parse()` returns false when extra non-whitespace trails the + * JSON value in the input string. + * - `"rejectDupKeys": false or true` + * - If true, `parse()` returns false when a key is duplicated within an + * object. + * - `"allowSpecialFloats": false or true` + * - If true, special float values (NaNs and infinities) are allowed and + * their values are lossfree restorable. + * - `"skipBom": false or true` + * - If true, if the input starts with the Unicode byte order mark (BOM), + * it is skipped. + * + * You can examine 'settings_` yourself to see the defaults. You can also + * write and read them just like any JSON Value. + * \sa setDefaults() + */ + Json::Value settings_; + + CharReaderBuilder(); + ~CharReaderBuilder() override; + + CharReader* newCharReader() const override; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + + /** A simple way to update a specific setting. + */ + Value& operator[](const String& key); + + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults + */ + static void setDefaults(Json::Value* settings); + /** Same as old Features::strictMode(). + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode + */ + static void strictMode(Json::Value* settings); +}; + +/** Consume entire stream and use its begin/end. + * Someday we might have a real StreamReader, but for now this + * is convenient. + */ +bool JSON_API parseFromStream(CharReader::Factory const&, IStream&, Value* root, + String* errs); + +/** \brief Read from 'sin' into 'root'. + * + * Always keep comments from the input JSON. + * + * This can be used to read a file into a particular sub-object. + * For example: + * \code + * Json::Value root; + * cin >> root["dir"]["file"]; + * cout << root; + * \endcode + * Result: + * \verbatim + * { + * "dir": { + * "file": { + * // The input stream JSON would be nested here. + * } + * } + * } + * \endverbatim + * \throw std::exception on parse error. + * \see Json::operator<<() + */ +JSON_API IStream& operator>>(IStream&, Value&); } // namespace Json -#endif // CPPTL_JSON_READER_H_INCLUDED +#pragma pack(pop) + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // JSON_READER_H_INCLUDED diff -Nru mstflint-4.17.0+1/ext_libs/json/json/value.h mstflint-4.21.0+1/ext_libs/json/json/value.h --- mstflint-4.17.0+1/ext_libs/json/json/value.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/json/value.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,1075 +1,935 @@ -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE +#ifndef JSON_H_INCLUDED +#define JSON_H_INCLUDED -#ifndef CPPTL_JSON_H_INCLUDED -# define CPPTL_JSON_H_INCLUDED +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +// Conditional NORETURN attribute on the throw functions would: +// a) suppress false positives from static code analysis +// b) possibly improve optimization opportunities. +#if !defined(JSONCPP_NORETURN) +#if defined(_MSC_VER) && _MSC_VER == 1800 +#define JSONCPP_NORETURN __declspec(noreturn) +#else +#define JSONCPP_NORETURN [[noreturn]] +#endif +#endif + +// Support for '= delete' with template declarations was a late addition +// to the c++11 standard and is rejected by clang 3.8 and Apple clang 8.2 +// even though these declare themselves to be c++11 compilers. +#if !defined(JSONCPP_TEMPLATE_DELETE) +#if defined(__clang__) && defined(__apple_build_version__) +#if __apple_build_version__ <= 8000042 +#define JSONCPP_TEMPLATE_DELETE +#endif +#elif defined(__clang__) +#if __clang_major__ == 3 && __clang_minor__ <= 8 +#define JSONCPP_TEMPLATE_DELETE +#endif +#endif +#if !defined(JSONCPP_TEMPLATE_DELETE) +#define JSONCPP_TEMPLATE_DELETE = delete +#endif +#endif -# include "forwards.h" -# include -# include - -# ifndef JSON_USE_CPPTL_SMALLMAP -# include -# else -# include -# endif -# ifdef JSON_USE_CPPTL -# include -# endif +#include +#include +#include +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251 4275) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#pragma pack(push, 8) /** \brief JSON (JavaScript Object Notation). */ namespace Json { - /** \brief Type of the value held by a Value object. - */ - enum ValueType - { - nullValue = 0, ///< 'null' value - intValue, ///< signed integer value - uintValue, ///< unsigned integer value - realValue, ///< double value - stringValue, ///< UTF-8 string value - booleanValue, ///< bool value - arrayValue, ///< array value (ordered list) - objectValue ///< object value (collection of name/value pairs). - }; - - enum CommentPlacement - { - commentBefore = 0, ///< a comment placed on the line before a value - commentAfterOnSameLine, ///< a comment just after a value on the same line - commentAfter, ///< a comment on the line after a value (only make sense for root value) - numberOfCommentPlacement - }; - -//# ifdef JSON_USE_CPPTL -// typedef CppTL::AnyEnumerator EnumMemberNames; -// typedef CppTL::AnyEnumerator EnumValues; -//# endif - - /** \brief Lightweight wrapper to tag static string. - * - * Value constructor and objectValue member assignement takes advantage of the - * StaticString and avoid the cost of string duplication when storing the - * string or the member name. - * - * Example of usage: - * \code - * Json::Value aValue( StaticString("some text") ); - * Json::Value object; - * static const StaticString code("code"); - * object[code] = 1234; - * \endcode - */ - class JSON_API StaticString - { - public: - explicit StaticString( const char *czstring ) - : str_( czstring ) - { - } - - operator const char *() const - { - return str_; - } - - const char *c_str() const - { - return str_; - } - - private: - const char *str_; - }; - - /** \brief Represents a JSON value. - * - * This class is a discriminated union wrapper that can represents a: - * - signed integer [range: Value::minInt - Value::maxInt] - * - unsigned integer (range: 0 - Value::maxUInt) - * - double - * - UTF-8 string - * - boolean - * - 'null' - * - an ordered list of Value - * - collection of name/value pairs (javascript object) - * - * The type of the held value is represented by a #ValueType and - * can be obtained using type(). - * - * values of an #objectValue or #arrayValue can be accessed using operator[]() methods. - * Non const methods will automatically create the a #nullValue element - * if it does not exist. - * The sequence of an #arrayValue will be automatically resize and initialized - * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. - * - * The get() methods can be used to obtanis default value in the case the required element - * does not exist. - * - * It is possible to iterate over the list of a #objectValue values using - * the getMemberNames() method. - */ - class JSON_API Value - { - friend class ValueIteratorBase; -# ifdef JSON_VALUE_USE_INTERNAL_MAP - friend class ValueInternalLink; - friend class ValueInternalMap; -# endif - public: - typedef std::vector Members; - typedef ValueIterator iterator; - typedef ValueConstIterator const_iterator; - typedef Json::UInt UInt; - typedef Json::Int Int; - typedef UInt ArrayIndex; - - static const Value null; - static const Int minInt; - static const Int maxInt; - static const UInt maxUInt; +#if JSON_USE_EXCEPTION +/** Base class for all exceptions we throw. + * + * We use nothing but these internally. Of course, STL can throw others. + */ +class JSON_API Exception : public std::exception { +public: + Exception(String msg); + ~Exception() noexcept override; + char const* what() const noexcept override; - private: -#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION -# ifndef JSON_VALUE_USE_INTERNAL_MAP - class CZString - { - public: - enum DuplicationPolicy - { - noDuplication = 0, - duplicate, - duplicateOnCopy - }; - CZString( int index ); - CZString( const char *cstr, DuplicationPolicy allocate ); - CZString( const CZString &other ); - ~CZString(); - CZString &operator =( const CZString &other ); - bool operator<( const CZString &other ) const; - bool operator==( const CZString &other ) const; - int index() const; - const char *c_str() const; - bool isStaticString() const; - private: - void swap( CZString &other ); - const char *cstr_; - int index_; - }; - - public: -# ifndef JSON_USE_CPPTL_SMALLMAP - typedef std::map ObjectValues; -# else - typedef CppTL::SmallMap ObjectValues; -# endif // ifndef JSON_USE_CPPTL_SMALLMAP -# endif // ifndef JSON_VALUE_USE_INTERNAL_MAP -#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION +protected: + String msg_; +}; - public: - /** \brief Create a default Value of the given type. +/** Exceptions which the user cannot easily avoid. + * + * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input + * + * \remark derived from Json::Exception + */ +class JSON_API RuntimeError : public Exception { +public: + RuntimeError(String const& msg); +}; - This is a very useful constructor. - To create an empty array, pass arrayValue. - To create an empty object, pass objectValue. - Another Value can then be set to this one by assignment. - This is useful since clear() and resize() will not alter types. - - Examples: - \code - Json::Value null_value; // null - Json::Value arr_value(Json::arrayValue); // [] - Json::Value obj_value(Json::objectValue); // {} - \endcode - */ - Value( ValueType type = nullValue ); - Value( Int value ); - Value( UInt value ); - Value( double value ); - Value( const char *value ); - Value( const char *beginValue, const char *endValue ); - /** \brief Constructs a value from a static string. - - * Like other value string constructor but do not duplicate the string for - * internal storage. The given string must remain alive after the call to this - * constructor. - * Example of usage: - * \code - * Json::Value aValue( StaticString("some text") ); - * \endcode - */ - Value( const StaticString &value ); - Value( const std::string &value ); -# ifdef JSON_USE_CPPTL - Value( const CppTL::ConstString &value ); -# endif - Value( bool value ); - Value( const Value &other ); - ~Value(); - - Value &operator=( const Value &other ); - /// Swap values. - /// \note Currently, comments are intentionally not swapped, for - /// both logic and efficiency. - void swap( Value &other ); - - ValueType type() const; - - bool operator <( const Value &other ) const; - bool operator <=( const Value &other ) const; - bool operator >=( const Value &other ) const; - bool operator >( const Value &other ) const; - - bool operator ==( const Value &other ) const; - bool operator !=( const Value &other ) const; - - int compare( const Value &other ); - - const char *asCString() const; - std::string asString() const; -# ifdef JSON_USE_CPPTL - CppTL::ConstString asConstString() const; -# endif - Int asInt() const; - UInt asUInt() const; - double asDouble() const; - bool asBool() const; - - bool isNull() const; - bool isBool() const; - bool isInt() const; - bool isUInt() const; - bool isIntegral() const; - bool isDouble() const; - bool isNumeric() const; - bool isString() const; - bool isArray() const; - bool isObject() const; - - bool isConvertibleTo( ValueType other ) const; - - /// Number of values in array or object - UInt size() const; - - /// \brief Return true if empty array, empty object, or null; - /// otherwise, false. - bool empty() const; - - /// Return isNull() - bool operator!() const; - - /// Remove all object members and array elements. - /// \pre type() is arrayValue, objectValue, or nullValue - /// \post type() is unchanged - void clear(); - - /// Resize the array to size elements. - /// New elements are initialized to null. - /// May only be called on nullValue or arrayValue. - /// \pre type() is arrayValue or nullValue - /// \post type() is arrayValue - void resize( UInt size ); - - /// Access an array element (zero based index ). - /// If the array contains less than index element, then null value are inserted - /// in the array so that its size is index+1. - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) - Value &operator[]( UInt index ); - /// Access an array element (zero based index ) - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) - const Value &operator[]( UInt index ) const; - /// If the array contains at least index+1 elements, returns the element value, - /// otherwise returns defaultValue. - Value get( UInt index, - const Value &defaultValue ) const; - /// Return true if index < size(). - bool isValidIndex( UInt index ) const; - /// \brief Append value to array at the end. - /// - /// Equivalent to jsonvalue[jsonvalue.size()] = value; - Value &append( const Value &value ); - - /// Access an object value by name, create a null member if it does not exist. - Value &operator[]( const char *key ); - /// Access an object value by name, returns null if there is no member with that name. - const Value &operator[]( const char *key ) const; - /// Access an object value by name, create a null member if it does not exist. - Value &operator[]( const std::string &key ); - /// Access an object value by name, returns null if there is no member with that name. - const Value &operator[]( const std::string &key ) const; - /** \brief Access an object value by name, create a null member if it does not exist. - - * If the object as no entry for that name, then the member name used to store - * the new entry is not duplicated. - * Example of use: - * \code - * Json::Value object; - * static const StaticString code("code"); - * object[code] = 1234; - * \endcode - */ - Value &operator[]( const StaticString &key ); -# ifdef JSON_USE_CPPTL - /// Access an object value by name, create a null member if it does not exist. - Value &operator[]( const CppTL::ConstString &key ); - /// Access an object value by name, returns null if there is no member with that name. - const Value &operator[]( const CppTL::ConstString &key ) const; -# endif - /// Return the member named key if it exist, defaultValue otherwise. - Value get( const char *key, - const Value &defaultValue ) const; - /// Return the member named key if it exist, defaultValue otherwise. - Value get( const std::string &key, - const Value &defaultValue ) const; -# ifdef JSON_USE_CPPTL - /// Return the member named key if it exist, defaultValue otherwise. - Value get( const CppTL::ConstString &key, - const Value &defaultValue ) const; -# endif - /// \brief Remove and return the named member. - /// - /// Do nothing if it did not exist. - /// \return the removed Value, or null. - /// \pre type() is objectValue or nullValue - /// \post type() is unchanged - Value removeMember( const char* key ); - /// Same as removeMember(const char*) - Value removeMember( const std::string &key ); - - /// Return true if the object has a member named key. - bool isMember( const char *key ) const; - /// Return true if the object has a member named key. - bool isMember( const std::string &key ) const; -# ifdef JSON_USE_CPPTL - /// Return true if the object has a member named key. - bool isMember( const CppTL::ConstString &key ) const; -# endif - - /// \brief Return a list of the member names. - /// - /// If null, return an empty list. - /// \pre type() is objectValue or nullValue - /// \post if type() was nullValue, it remains nullValue - Members getMemberNames() const; - -//# ifdef JSON_USE_CPPTL -// EnumMemberNames enumMemberNames() const; -// EnumValues enumValues() const; -//# endif - - /// Comments must be //... or /* ... */ - void setComment( const char *comment, - CommentPlacement placement ); - /// Comments must be //... or /* ... */ - void setComment( const std::string &comment, - CommentPlacement placement ); - bool hasComment( CommentPlacement placement ) const; - /// Include delimiters and embedded newlines. - std::string getComment( CommentPlacement placement ) const; - - std::string toStyledString() const; - - const_iterator begin() const; - const_iterator end() const; - - iterator begin(); - iterator end(); - - private: - Value &resolveReference( const char *key, - bool isStatic ); - -# ifdef JSON_VALUE_USE_INTERNAL_MAP - inline bool isItemAvailable() const - { - return itemIsUsed_ == 0; - } - - inline void setItemUsed( bool isUsed = true ) - { - itemIsUsed_ = isUsed ? 1 : 0; - } - - inline bool isMemberNameStatic() const - { - return memberNameIsStatic_ == 0; - } - - inline void setMemberNameIsStatic( bool isStatic ) - { - memberNameIsStatic_ = isStatic ? 1 : 0; - } -# endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP - - private: - struct CommentInfo - { - CommentInfo(); - ~CommentInfo(); - - void setComment( const char *text ); - - char *comment_; - }; - - //struct MemberNamesTransform - //{ - // typedef const char *result_type; - // const char *operator()( const CZString &name ) const - // { - // return name.c_str(); - // } - //}; - - union ValueHolder - { - Int int_; - UInt uint_; - double real_; - bool bool_; - char *string_; -# ifdef JSON_VALUE_USE_INTERNAL_MAP - ValueInternalArray *array_; - ValueInternalMap *map_; -#else - ObjectValues *map_; -# endif - } value_; - ValueType type_ : 8; - int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. -# ifdef JSON_VALUE_USE_INTERNAL_MAP - unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container. - int memberNameIsStatic_ : 1; // used by the ValueInternalMap container. -# endif - CommentInfo *comments_; - }; - - - /** \brief Experimental and untested: represents an element of the "path" to access a node. - */ - class PathArgument - { - public: - friend class Path; - - PathArgument(); - PathArgument( UInt index ); - PathArgument( const char *key ); - PathArgument( const std::string &key ); - - private: - enum Kind - { - kindNone = 0, - kindIndex, - kindKey - }; - std::string key_; - UInt index_; - Kind kind_; - }; - - /** \brief Experimental and untested: represents a "path" to access a node. - * - * Syntax: - * - "." => root node - * - ".[n]" => elements at index 'n' of root node (an array value) - * - ".name" => member named 'name' of root node (an object value) - * - ".name1.name2.name3" - * - ".[0][1][2].name1[3]" - * - ".%" => member name is provided as parameter - * - ".[%]" => index is provied as parameter - */ - class Path - { - public: - Path( const std::string &path, - const PathArgument &a1 = PathArgument(), - const PathArgument &a2 = PathArgument(), - const PathArgument &a3 = PathArgument(), - const PathArgument &a4 = PathArgument(), - const PathArgument &a5 = PathArgument() ); - - const Value &resolve( const Value &root ) const; - Value resolve( const Value &root, - const Value &defaultValue ) const; - /// Creates the "path" to access the specified node and returns a reference on the node. - Value &make( Value &root ) const; - - private: - typedef std::vector InArgs; - typedef std::vector Args; - - void makePath( const std::string &path, - const InArgs &in ); - void addPathInArg( const std::string &path, - const InArgs &in, - InArgs::const_iterator &itInArg, - PathArgument::Kind kind ); - void invalidPath( const std::string &path, - int location ); - - Args args_; - }; - - /** \brief Experimental do not use: Allocator to customize member name and string value memory management done by Value. - * - * - makeMemberName() and releaseMemberName() are called to respectively duplicate and - * free an Json::objectValue member name. - * - duplicateStringValue() and releaseStringValue() are called similarly to - * duplicate and free a Json::stringValue value. - */ - class ValueAllocator - { - public: - enum { unknown = (unsigned)-1 }; - - virtual ~ValueAllocator(); - - virtual char *makeMemberName( const char *memberName ) = 0; - virtual void releaseMemberName( char *memberName ) = 0; - virtual char *duplicateStringValue( const char *value, - unsigned int length = unknown ) = 0; - virtual void releaseStringValue( char *value ) = 0; - }; - -#ifdef JSON_VALUE_USE_INTERNAL_MAP - /** \brief Allocator to customize Value internal map. - * Below is an example of a simple implementation (default implementation actually - * use memory pool for speed). - * \code - class DefaultValueMapAllocator : public ValueMapAllocator - { - public: // overridden from ValueMapAllocator - virtual ValueInternalMap *newMap() - { - return new ValueInternalMap(); - } - - virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) - { - return new ValueInternalMap( other ); - } - - virtual void destructMap( ValueInternalMap *map ) - { - delete map; - } - - virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) - { - return new ValueInternalLink[size]; - } - - virtual void releaseMapBuckets( ValueInternalLink *links ) - { - delete [] links; - } - - virtual ValueInternalLink *allocateMapLink() - { - return new ValueInternalLink(); - } - - virtual void releaseMapLink( ValueInternalLink *link ) - { - delete link; - } - }; - * \endcode - */ - class JSON_API ValueMapAllocator - { - public: - virtual ~ValueMapAllocator(); - virtual ValueInternalMap *newMap() = 0; - virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) = 0; - virtual void destructMap( ValueInternalMap *map ) = 0; - virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) = 0; - virtual void releaseMapBuckets( ValueInternalLink *links ) = 0; - virtual ValueInternalLink *allocateMapLink() = 0; - virtual void releaseMapLink( ValueInternalLink *link ) = 0; - }; - - /** \brief ValueInternalMap hash-map bucket chain link (for internal use only). - * \internal previous_ & next_ allows for bidirectional traversal. - */ - class JSON_API ValueInternalLink - { - public: - enum { itemPerLink = 6 }; // sizeof(ValueInternalLink) = 128 on 32 bits architecture. - enum InternalFlags { - flagAvailable = 0, - flagUsed = 1 - }; - - ValueInternalLink(); - - ~ValueInternalLink(); - - Value items_[itemPerLink]; - char *keys_[itemPerLink]; - ValueInternalLink *previous_; - ValueInternalLink *next_; - }; - - - /** \brief A linked page based hash-table implementation used internally by Value. - * \internal ValueInternalMap is a tradional bucket based hash-table, with a linked - * list in each bucket to handle collision. There is an addional twist in that - * each node of the collision linked list is a page containing a fixed amount of - * value. This provides a better compromise between memory usage and speed. - * - * Each bucket is made up of a chained list of ValueInternalLink. The last - * link of a given bucket can be found in the 'previous_' field of the following bucket. - * The last link of the last bucket is stored in tailLink_ as it has no following bucket. - * Only the last link of a bucket may contains 'available' item. The last link always - * contains at least one element unless is it the bucket one very first link. - */ - class JSON_API ValueInternalMap - { - friend class ValueIteratorBase; - friend class Value; - public: - typedef unsigned int HashKey; - typedef unsigned int BucketIndex; - -# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - struct IteratorState - { - IteratorState() - : map_(0) - , link_(0) - , itemIndex_(0) - , bucketIndex_(0) - { - } - ValueInternalMap *map_; - ValueInternalLink *link_; - BucketIndex itemIndex_; - BucketIndex bucketIndex_; - }; -# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - - ValueInternalMap(); - ValueInternalMap( const ValueInternalMap &other ); - ValueInternalMap &operator =( const ValueInternalMap &other ); - ~ValueInternalMap(); - - void swap( ValueInternalMap &other ); - - BucketIndex size() const; - - void clear(); - - bool reserveDelta( BucketIndex growth ); - - bool reserve( BucketIndex newItemCount ); - - const Value *find( const char *key ) const; - - Value *find( const char *key ); - - Value &resolveReference( const char *key, - bool isStatic ); - - void remove( const char *key ); - - void doActualRemove( ValueInternalLink *link, - BucketIndex index, - BucketIndex bucketIndex ); - - ValueInternalLink *&getLastLinkInBucket( BucketIndex bucketIndex ); - - Value &setNewItem( const char *key, - bool isStatic, - ValueInternalLink *link, - BucketIndex index ); - - Value &unsafeAdd( const char *key, - bool isStatic, - HashKey hashedKey ); - - HashKey hash( const char *key ) const; - - int compare( const ValueInternalMap &other ) const; - - private: - void makeBeginIterator( IteratorState &it ) const; - void makeEndIterator( IteratorState &it ) const; - static bool equals( const IteratorState &x, const IteratorState &other ); - static void increment( IteratorState &iterator ); - static void incrementBucket( IteratorState &iterator ); - static void decrement( IteratorState &iterator ); - static const char *key( const IteratorState &iterator ); - static const char *key( const IteratorState &iterator, bool &isStatic ); - static Value &value( const IteratorState &iterator ); - static int distance( const IteratorState &x, const IteratorState &y ); - - private: - ValueInternalLink *buckets_; - ValueInternalLink *tailLink_; - BucketIndex bucketsSize_; - BucketIndex itemCount_; - }; - - /** \brief A simplified deque implementation used internally by Value. - * \internal - * It is based on a list of fixed "page", each page contains a fixed number of items. - * Instead of using a linked-list, a array of pointer is used for fast item look-up. - * Look-up for an element is as follow: - * - compute page index: pageIndex = itemIndex / itemsPerPage - * - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage] - * - * Insertion is amortized constant time (only the array containing the index of pointers - * need to be reallocated when items are appended). - */ - class JSON_API ValueInternalArray - { - friend class Value; - friend class ValueIteratorBase; - public: - enum { itemsPerPage = 8 }; // should be a power of 2 for fast divide and modulo. - typedef Value::ArrayIndex ArrayIndex; - typedef unsigned int PageIndex; - -# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - struct IteratorState // Must be a POD - { - IteratorState() - : array_(0) - , currentPageIndex_(0) - , currentItemIndex_(0) - { - } - ValueInternalArray *array_; - Value **currentPageIndex_; - unsigned int currentItemIndex_; - }; -# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - - ValueInternalArray(); - ValueInternalArray( const ValueInternalArray &other ); - ValueInternalArray &operator =( const ValueInternalArray &other ); - ~ValueInternalArray(); - void swap( ValueInternalArray &other ); - - void clear(); - void resize( ArrayIndex newSize ); - - Value &resolveReference( ArrayIndex index ); - - Value *find( ArrayIndex index ) const; - - ArrayIndex size() const; - - int compare( const ValueInternalArray &other ) const; - - private: - static bool equals( const IteratorState &x, const IteratorState &other ); - static void increment( IteratorState &iterator ); - static void decrement( IteratorState &iterator ); - static Value &dereference( const IteratorState &iterator ); - static Value &unsafeDereference( const IteratorState &iterator ); - static int distance( const IteratorState &x, const IteratorState &y ); - static ArrayIndex indexOf( const IteratorState &iterator ); - void makeBeginIterator( IteratorState &it ) const; - void makeEndIterator( IteratorState &it ) const; - void makeIterator( IteratorState &it, ArrayIndex index ) const; - - void makeIndexValid( ArrayIndex index ); - - Value **pages_; - ArrayIndex size_; - PageIndex pageCount_; - }; - - /** \brief Experimental: do not use. Allocator to customize Value internal array. - * Below is an example of a simple implementation (actual implementation use - * memory pool). - \code -class DefaultValueArrayAllocator : public ValueArrayAllocator -{ -public: // overridden from ValueArrayAllocator - virtual ~DefaultValueArrayAllocator() - { - } - - virtual ValueInternalArray *newArray() - { - return new ValueInternalArray(); - } - - virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) - { - return new ValueInternalArray( other ); - } - - virtual void destruct( ValueInternalArray *array ) - { - delete array; - } - - virtual void reallocateArrayPageIndex( Value **&indexes, - ValueInternalArray::PageIndex &indexCount, - ValueInternalArray::PageIndex minNewIndexCount ) - { - ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; - if ( minNewIndexCount > newIndexCount ) - newIndexCount = minNewIndexCount; - void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); - if ( !newIndexes ) - throw std::bad_alloc(); - indexCount = newIndexCount; - indexes = static_cast( newIndexes ); - } - virtual void releaseArrayPageIndex( Value **indexes, - ValueInternalArray::PageIndex indexCount ) - { - if ( indexes ) - free( indexes ); - } - - virtual Value *allocateArrayPage() - { - return static_cast( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); - } - - virtual void releaseArrayPage( Value *value ) - { - if ( value ) - free( value ); - } +/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros. + * + * These are precondition-violations (user bugs) and internal errors (our bugs). + * + * \remark derived from Json::Exception + */ +class JSON_API LogicError : public Exception { +public: + LogicError(String const& msg); }; - \endcode - */ - class JSON_API ValueArrayAllocator - { - public: - virtual ~ValueArrayAllocator(); - virtual ValueInternalArray *newArray() = 0; - virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) = 0; - virtual void destructArray( ValueInternalArray *array ) = 0; - /** \brief Reallocate array page index. - * Reallocates an array of pointer on each page. - * \param indexes [input] pointer on the current index. May be \c NULL. - * [output] pointer on the new index of at least - * \a minNewIndexCount pages. - * \param indexCount [input] current number of pages in the index. - * [output] number of page the reallocated index can handle. - * \b MUST be >= \a minNewIndexCount. - * \param minNewIndexCount Minimum number of page the new index must be able to - * handle. - */ - virtual void reallocateArrayPageIndex( Value **&indexes, - ValueInternalArray::PageIndex &indexCount, - ValueInternalArray::PageIndex minNewIndexCount ) = 0; - virtual void releaseArrayPageIndex( Value **indexes, - ValueInternalArray::PageIndex indexCount ) = 0; - virtual Value *allocateArrayPage() = 0; - virtual void releaseArrayPage( Value *value ) = 0; - }; -#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP - - - /** \brief base class for Value iterators. - * - */ - class ValueIteratorBase - { - public: - typedef unsigned int size_t; - typedef int difference_type; - typedef ValueIteratorBase SelfType; - - ValueIteratorBase(); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - explicit ValueIteratorBase( const Value::ObjectValues::iterator ¤t ); -#else - ValueIteratorBase( const ValueInternalArray::IteratorState &state ); - ValueIteratorBase( const ValueInternalMap::IteratorState &state ); #endif - bool operator ==( const SelfType &other ) const - { - return isEqual( other ); - } +/// used internally +JSONCPP_NORETURN void throwRuntimeError(String const& msg); +/// used internally +JSONCPP_NORETURN void throwLogicError(String const& msg); - bool operator !=( const SelfType &other ) const - { - return !isEqual( other ); - } - - difference_type operator -( const SelfType &other ) const - { - return computeDistance( other ); - } +/** \brief Type of the value held by a Value object. + */ +enum ValueType { + nullValue = 0, ///< 'null' value + intValue, ///< signed integer value + uintValue, ///< unsigned integer value + realValue, ///< double value + stringValue, ///< UTF-8 string value + booleanValue, ///< bool value + arrayValue, ///< array value (ordered list) + objectValue ///< object value (collection of name/value pairs). +}; - /// Return either the index or the member name of the referenced value as a Value. - Value key() const; +enum CommentPlacement { + commentBefore = 0, ///< a comment placed on the line before a value + commentAfterOnSameLine, ///< a comment just after a value on the same line + commentAfter, ///< a comment on the line after a value (only make sense for + /// root value) + numberOfCommentPlacement +}; - /// Return the index of the referenced Value. -1 if it is not an arrayValue. - UInt index() const; +/** \brief Type of precision for formatting of real values. + */ +enum PrecisionType { + significantDigits = 0, ///< we set max number of significant digits in string + decimalPlaces ///< we set max number of digits after "." in string +}; - /// Return the member name of the referenced Value. "" if it is not an objectValue. - const char *memberName() const; +/** \brief Lightweight wrapper to tag static string. + * + * Value constructor and objectValue member assignment takes advantage of the + * StaticString and avoid the cost of string duplication when storing the + * string or the member name. + * + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ +class JSON_API StaticString { +public: + explicit StaticString(const char* czstring) : c_str_(czstring) {} - protected: - Value &deref() const; + operator const char*() const { return c_str_; } - void increment(); + const char* c_str() const { return c_str_; } - void decrement(); +private: + const char* c_str_; +}; - difference_type computeDistance( const SelfType &other ) const; +/** \brief Represents a JSON value. + * + * This class is a discriminated union wrapper that can represents a: + * - signed integer [range: Value::minInt - Value::maxInt] + * - unsigned integer (range: 0 - Value::maxUInt) + * - double + * - UTF-8 string + * - boolean + * - 'null' + * - an ordered list of Value + * - collection of name/value pairs (javascript object) + * + * The type of the held value is represented by a #ValueType and + * can be obtained using type(). + * + * Values of an #objectValue or #arrayValue can be accessed using operator[]() + * methods. + * Non-const methods will automatically create the a #nullValue element + * if it does not exist. + * The sequence of an #arrayValue will be automatically resized and initialized + * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. + * + * The get() methods can be used to obtain default value in the case the + * required element does not exist. + * + * It is possible to iterate over the list of member keys of an object using + * the getMemberNames() method. + * + * \note #Value string-length fit in size_t, but keys must be < 2^30. + * (The reason is an implementation detail.) A #CharReader will raise an + * exception if a bound is exceeded to avoid security holes in your app, + * but the Value API does *not* check bounds. That is the responsibility + * of the caller. + */ +class JSON_API Value { + friend class ValueIteratorBase; - bool isEqual( const SelfType &other ) const; +public: + using Members = std::vector; + using iterator = ValueIterator; + using const_iterator = ValueConstIterator; + using UInt = Json::UInt; + using Int = Json::Int; +#if defined(JSON_HAS_INT64) + using UInt64 = Json::UInt64; + using Int64 = Json::Int64; +#endif // defined(JSON_HAS_INT64) + using LargestInt = Json::LargestInt; + using LargestUInt = Json::LargestUInt; + using ArrayIndex = Json::ArrayIndex; + + // Required for boost integration, e. g. BOOST_TEST + using value_type = std::string; + +#if JSON_USE_NULLREF + // Binary compatibility kludges, do not use. + static const Value& null; + static const Value& nullRef; +#endif - void copy( const SelfType &other ); + // null and nullRef are deprecated, use this instead. + static Value const& nullSingleton(); - private: -#ifndef JSON_VALUE_USE_INTERNAL_MAP - Value::ObjectValues::iterator current_; - // Indicates that iterator is for a null value. - bool isNull_; + /// Minimum signed integer value that can be stored in a Json::Value. + static constexpr LargestInt minLargestInt = + LargestInt(~(LargestUInt(-1) / 2)); + /// Maximum signed integer value that can be stored in a Json::Value. + static constexpr LargestInt maxLargestInt = LargestInt(LargestUInt(-1) / 2); + /// Maximum unsigned integer value that can be stored in a Json::Value. + static constexpr LargestUInt maxLargestUInt = LargestUInt(-1); + + /// Minimum signed int value that can be stored in a Json::Value. + static constexpr Int minInt = Int(~(UInt(-1) / 2)); + /// Maximum signed int value that can be stored in a Json::Value. + static constexpr Int maxInt = Int(UInt(-1) / 2); + /// Maximum unsigned int value that can be stored in a Json::Value. + static constexpr UInt maxUInt = UInt(-1); + +#if defined(JSON_HAS_INT64) + /// Minimum signed 64 bits int value that can be stored in a Json::Value. + static constexpr Int64 minInt64 = Int64(~(UInt64(-1) / 2)); + /// Maximum signed 64 bits int value that can be stored in a Json::Value. + static constexpr Int64 maxInt64 = Int64(UInt64(-1) / 2); + /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. + static constexpr UInt64 maxUInt64 = UInt64(-1); +#endif // defined(JSON_HAS_INT64) + /// Default precision for real value for string representation. + static constexpr UInt defaultRealPrecision = 17; + // The constant is hard-coded because some compiler have trouble + // converting Value::maxUInt64 to a double correctly (AIX/xlC). + // Assumes that UInt64 is a 64 bits integer. + static constexpr double maxUInt64AsDouble = 18446744073709551615.0; +// Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler +// when using gcc and clang backend compilers. CZString +// cannot be defined as private. See issue #486 +#ifdef __NVCC__ +public: #else - union - { - ValueInternalArray::IteratorState array_; - ValueInternalMap::IteratorState map_; - } iterator_; - bool isArray_; +private: #endif - }; +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + class CZString { + public: + enum DuplicationPolicy { noDuplication = 0, duplicate, duplicateOnCopy }; + CZString(ArrayIndex index); + CZString(char const* str, unsigned length, DuplicationPolicy allocate); + CZString(CZString const& other); + CZString(CZString&& other) noexcept; + ~CZString(); + CZString& operator=(const CZString& other); + CZString& operator=(CZString&& other) noexcept; + + bool operator<(CZString const& other) const; + bool operator==(CZString const& other) const; + ArrayIndex index() const; + // const char* c_str() const; ///< \deprecated + char const* data() const; + unsigned length() const; + bool isStaticString() const; + + private: + void swap(CZString& other); + + struct StringStorage { + unsigned policy_ : 2; + unsigned length_ : 30; // 1GB max + }; + + char const* cstr_; // actually, a prefixed string, unless policy is noDup + union { + ArrayIndex index_; + StringStorage storage_; + }; + }; - /** \brief const iterator for object and array value. - * - */ - class ValueConstIterator : public ValueIteratorBase - { - friend class Value; - public: - typedef unsigned int size_t; - typedef int difference_type; - typedef const Value &reference; - typedef const Value *pointer; - typedef ValueConstIterator SelfType; - - ValueConstIterator(); - private: - /*! \internal Use by Value to create an iterator. - */ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - explicit ValueConstIterator( const Value::ObjectValues::iterator ¤t ); -#else - ValueConstIterator( const ValueInternalArray::IteratorState &state ); - ValueConstIterator( const ValueInternalMap::IteratorState &state ); +public: + typedef std::map ObjectValues; +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +public: + /** + * \brief Create a default Value of the given type. + * + * This is a very useful constructor. + * To create an empty array, pass arrayValue. + * To create an empty object, pass objectValue. + * Another Value can then be set to this one by assignment. + * This is useful since clear() and resize() will not alter types. + * + * Examples: + * \code + * Json::Value null_value; // null + * Json::Value arr_value(Json::arrayValue); // [] + * Json::Value obj_value(Json::objectValue); // {} + * \endcode + */ + Value(ValueType type = nullValue); + Value(Int value); + Value(UInt value); +#if defined(JSON_HAS_INT64) + Value(Int64 value); + Value(UInt64 value); +#endif // if defined(JSON_HAS_INT64) + Value(double value); + Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.) + Value(const char* begin, const char* end); ///< Copy all, incl zeroes. + /** + * \brief Constructs a value from a static string. + * + * Like other value string constructor but do not duplicate the string for + * internal storage. The given string must remain alive after the call to + * this constructor. + * + * \note This works only for null-terminated strings. (We cannot change the + * size of this class, so we have nowhere to store the length, which might be + * computed later for various operations.) + * + * Example of usage: + * \code + * static StaticString foo("some text"); + * Json::Value aValue(foo); + * \endcode + */ + Value(const StaticString& value); + Value(const String& value); + Value(bool value); + Value(std::nullptr_t ptr) = delete; + Value(const Value& other); + Value(Value&& other) noexcept; + ~Value(); + + /// \note Overwrite existing comments. To preserve comments, use + /// #swapPayload(). + Value& operator=(const Value& other); + Value& operator=(Value&& other) noexcept; + + /// Swap everything. + void swap(Value& other); + /// Swap values but leave comments and source offsets in place. + void swapPayload(Value& other); + + /// copy everything. + void copy(const Value& other); + /// copy values but leave comments and source offsets in place. + void copyPayload(const Value& other); + + ValueType type() const; + + /// Compare payload only, not comments etc. + bool operator<(const Value& other) const; + bool operator<=(const Value& other) const; + bool operator>=(const Value& other) const; + bool operator>(const Value& other) const; + bool operator==(const Value& other) const; + bool operator!=(const Value& other) const; + int compare(const Value& other) const; + + const char* asCString() const; ///< Embedded zeroes could cause you trouble! +#if JSONCPP_USING_SECURE_MEMORY + unsigned getCStringLength() const; // Allows you to understand the length of + // the CString #endif - public: - SelfType &operator =( const ValueIteratorBase &other ); + String asString() const; ///< Embedded zeroes are possible. + /** Get raw char* of string-value. + * \return false if !string. (Seg-fault if str or end are NULL.) + */ + bool getString(char const** begin, char const** end) const; + Int asInt() const; + UInt asUInt() const; +#if defined(JSON_HAS_INT64) + Int64 asInt64() const; + UInt64 asUInt64() const; +#endif // if defined(JSON_HAS_INT64) + LargestInt asLargestInt() const; + LargestUInt asLargestUInt() const; + float asFloat() const; + double asDouble() const; + bool asBool() const; + + bool isNull() const; + bool isBool() const; + bool isInt() const; + bool isInt64() const; + bool isUInt() const; + bool isUInt64() const; + bool isIntegral() const; + bool isDouble() const; + bool isNumeric() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + /// The `as` and `is` member function templates and specializations. + template T as() const JSONCPP_TEMPLATE_DELETE; + template bool is() const JSONCPP_TEMPLATE_DELETE; + + bool isConvertibleTo(ValueType other) const; + + /// Number of values in array or object + ArrayIndex size() const; + + /// \brief Return true if empty array, empty object, or null; + /// otherwise, false. + bool empty() const; + + /// Return !isNull() + explicit operator bool() const; + + /// Remove all object members and array elements. + /// \pre type() is arrayValue, objectValue, or nullValue + /// \post type() is unchanged + void clear(); + + /// Resize the array to newSize elements. + /// New elements are initialized to null. + /// May only be called on nullValue or arrayValue. + /// \pre type() is arrayValue or nullValue + /// \post type() is arrayValue + void resize(ArrayIndex newSize); + + //@{ + /// Access an array element (zero based index). If the array contains less + /// than index element, then null value are inserted in the array so that + /// its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value& operator[](ArrayIndex index); + Value& operator[](int index); + //@} + + //@{ + /// Access an array element (zero based index). + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value& operator[](ArrayIndex index) const; + const Value& operator[](int index) const; + //@} + + /// If the array contains at least index+1 elements, returns the element + /// value, otherwise returns defaultValue. + Value get(ArrayIndex index, const Value& defaultValue) const; + /// Return true if index < size(). + bool isValidIndex(ArrayIndex index) const; + /// \brief Append value to array at the end. + /// + /// Equivalent to jsonvalue[jsonvalue.size()] = value; + Value& append(const Value& value); + Value& append(Value&& value); + + /// \brief Insert value in array at specific index + bool insert(ArrayIndex index, const Value& newValue); + bool insert(ArrayIndex index, Value&& newValue); + + /// Access an object value by name, create a null member if it does not exist. + /// \note Because of our implementation, keys are limited to 2^30 -1 chars. + /// Exceeding that will cause an exception. + Value& operator[](const char* key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const char* key) const; + /// Access an object value by name, create a null member if it does not exist. + /// \param key may contain embedded nulls. + Value& operator[](const String& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + /// \param key may contain embedded nulls. + const Value& operator[](const String& key) const; + /** \brief Access an object value by name, create a null member if it does not + * exist. + * + * If the object has no entry for that name, then the member name used to + * store the new entry is not duplicated. + * Example of use: + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + Value& operator[](const StaticString& key); + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + Value get(const char* key, const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \note key may contain embedded nulls. + Value get(const char* begin, const char* end, + const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \param key may contain embedded nulls. + Value get(const String& key, const Value& defaultValue) const; + /// Most general and efficient version of isMember()const, get()const, + /// and operator[]const + /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 + Value const* find(char const* begin, char const* end) const; + /// Most general and efficient version of object-mutators. + /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 + /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. + Value* demand(char const* begin, char const* end); + /// \brief Remove and return the named member. + /// + /// Do nothing if it did not exist. + /// \pre type() is objectValue or nullValue + /// \post type() is unchanged + void removeMember(const char* key); + /// Same as removeMember(const char*) + /// \param key may contain embedded nulls. + void removeMember(const String& key); + /// Same as removeMember(const char* begin, const char* end, Value* removed), + /// but 'key' is null-terminated. + bool removeMember(const char* key, Value* removed); + /** \brief Remove the named map member. + * + * Update 'removed' iff removed. + * \param key may contain embedded nulls. + * \return true iff removed (no exceptions) + */ + bool removeMember(String const& key, Value* removed); + /// Same as removeMember(String const& key, Value* removed) + bool removeMember(const char* begin, const char* end, Value* removed); + /** \brief Remove the indexed array element. + * + * O(n) expensive operations. + * Update 'removed' iff removed. + * \return true if removed (no exceptions) + */ + bool removeIndex(ArrayIndex index, Value* removed); - SelfType operator++( int ) - { - SelfType temp( *this ); - ++*this; - return temp; - } - - SelfType operator--( int ) - { - SelfType temp( *this ); - --*this; - return temp; - } - - SelfType &operator--() - { - decrement(); - return *this; - } - - SelfType &operator++() - { - increment(); - return *this; - } - - reference operator *() const - { - return deref(); - } - }; - - - /** \brief Iterator for object and array value. - */ - class ValueIterator : public ValueIteratorBase - { - friend class Value; - public: - typedef unsigned int size_t; - typedef int difference_type; - typedef Value &reference; - typedef Value *pointer; - typedef ValueIterator SelfType; - - ValueIterator(); - ValueIterator( const ValueConstIterator &other ); - ValueIterator( const ValueIterator &other ); - private: - /*! \internal Use by Value to create an iterator. - */ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - explicit ValueIterator( const Value::ObjectValues::iterator ¤t ); -#else - ValueIterator( const ValueInternalArray::IteratorState &state ); - ValueIterator( const ValueInternalMap::IteratorState &state ); + /// Return true if the object has a member named key. + /// \note 'key' must be null-terminated. + bool isMember(const char* key) const; + /// Return true if the object has a member named key. + /// \param key may contain embedded nulls. + bool isMember(const String& key) const; + /// Same as isMember(String const& key)const + bool isMember(const char* begin, const char* end) const; + + /// \brief Return a list of the member names. + /// + /// If null, return an empty list. + /// \pre type() is objectValue or nullValue + /// \post if type() was nullValue, it remains nullValue + Members getMemberNames() const; + + /// \deprecated Always pass len. + JSONCPP_DEPRECATED("Use setComment(String const&) instead.") + void setComment(const char* comment, CommentPlacement placement) { + setComment(String(comment, strlen(comment)), placement); + } + /// Comments must be //... or /* ... */ + void setComment(const char* comment, size_t len, CommentPlacement placement) { + setComment(String(comment, len), placement); + } + /// Comments must be //... or /* ... */ + void setComment(String comment, CommentPlacement placement); + bool hasComment(CommentPlacement placement) const; + /// Include delimiters and embedded newlines. + String getComment(CommentPlacement placement) const; + + String toStyledString() const; + + const_iterator begin() const; + const_iterator end() const; + + iterator begin(); + iterator end(); + + // Accessors for the [start, limit) range of bytes within the JSON text from + // which this value was parsed, if any. + void setOffsetStart(ptrdiff_t start); + void setOffsetLimit(ptrdiff_t limit); + ptrdiff_t getOffsetStart() const; + ptrdiff_t getOffsetLimit() const; + +private: + void setType(ValueType v) { + bits_.value_type_ = static_cast(v); + } + bool isAllocated() const { return bits_.allocated_; } + void setIsAllocated(bool v) { bits_.allocated_ = v; } + + void initBasic(ValueType type, bool allocated = false); + void dupPayload(const Value& other); + void releasePayload(); + void dupMeta(const Value& other); + + Value& resolveReference(const char* key); + Value& resolveReference(const char* key, const char* end); + + // struct MemberNamesTransform + //{ + // typedef const char *result_type; + // const char *operator()( const CZString &name ) const + // { + // return name.c_str(); + // } + //}; + + union ValueHolder { + LargestInt int_; + LargestUInt uint_; + double real_; + bool bool_; + char* string_; // if allocated_, ptr to { unsigned, char[] }. + ObjectValues* map_; + } value_; + + struct { + // Really a ValueType, but types should agree for bitfield packing. + unsigned int value_type_ : 8; + // Unless allocated_, string_ must be null-terminated. + unsigned int allocated_ : 1; + } bits_; + + class Comments { + public: + Comments() = default; + Comments(const Comments& that); + Comments(Comments&& that) noexcept; + Comments& operator=(const Comments& that); + Comments& operator=(Comments&& that) noexcept; + bool has(CommentPlacement slot) const; + String get(CommentPlacement slot) const; + void set(CommentPlacement slot, String comment); + + private: + using Array = std::array; + std::unique_ptr ptr_; + }; + Comments comments_; + + // [start, limit) byte offsets in the source JSON text from which this Value + // was extracted. + ptrdiff_t start_; + ptrdiff_t limit_; +}; + +template <> inline bool Value::as() const { return asBool(); } +template <> inline bool Value::is() const { return isBool(); } + +template <> inline Int Value::as() const { return asInt(); } +template <> inline bool Value::is() const { return isInt(); } + +template <> inline UInt Value::as() const { return asUInt(); } +template <> inline bool Value::is() const { return isUInt(); } + +#if defined(JSON_HAS_INT64) +template <> inline Int64 Value::as() const { return asInt64(); } +template <> inline bool Value::is() const { return isInt64(); } + +template <> inline UInt64 Value::as() const { return asUInt64(); } +template <> inline bool Value::is() const { return isUInt64(); } #endif - public: - SelfType &operator =( const SelfType &other ); +template <> inline double Value::as() const { return asDouble(); } +template <> inline bool Value::is() const { return isDouble(); } + +template <> inline String Value::as() const { return asString(); } +template <> inline bool Value::is() const { return isString(); } + +/// These `as` specializations are type conversions, and do not have a +/// corresponding `is`. +template <> inline float Value::as() const { return asFloat(); } +template <> inline const char* Value::as() const { + return asCString(); +} + +/** \brief Experimental and untested: represents an element of the "path" to + * access a node. + */ +class JSON_API PathArgument { +public: + friend class Path; + + PathArgument(); + PathArgument(ArrayIndex index); + PathArgument(const char* key); + PathArgument(String key); + +private: + enum Kind { kindNone = 0, kindIndex, kindKey }; + String key_; + ArrayIndex index_{}; + Kind kind_{kindNone}; +}; + +/** \brief Experimental and untested: represents a "path" to access a node. + * + * Syntax: + * - "." => root node + * - ".[n]" => elements at index 'n' of root node (an array value) + * - ".name" => member named 'name' of root node (an object value) + * - ".name1.name2.name3" + * - ".[0][1][2].name1[3]" + * - ".%" => member name is provided as parameter + * - ".[%]" => index is provided as parameter + */ +class JSON_API Path { +public: + Path(const String& path, const PathArgument& a1 = PathArgument(), + const PathArgument& a2 = PathArgument(), + const PathArgument& a3 = PathArgument(), + const PathArgument& a4 = PathArgument(), + const PathArgument& a5 = PathArgument()); + + const Value& resolve(const Value& root) const; + Value resolve(const Value& root, const Value& defaultValue) const; + /// Creates the "path" to access the specified node and returns a reference on + /// the node. + Value& make(Value& root) const; + +private: + using InArgs = std::vector; + using Args = std::vector; + + void makePath(const String& path, const InArgs& in); + void addPathInArg(const String& path, const InArgs& in, + InArgs::const_iterator& itInArg, PathArgument::Kind kind); + static void invalidPath(const String& path, int location); + + Args args_; +}; + +/** \brief base class for Value iterators. + * + */ +class JSON_API ValueIteratorBase { +public: + using iterator_category = std::bidirectional_iterator_tag; + using size_t = unsigned int; + using difference_type = int; + using SelfType = ValueIteratorBase; + + bool operator==(const SelfType& other) const { return isEqual(other); } + + bool operator!=(const SelfType& other) const { return !isEqual(other); } + + difference_type operator-(const SelfType& other) const { + return other.computeDistance(*this); + } + + /// Return either the index or the member name of the referenced value as a + /// Value. + Value key() const; + + /// Return the index of the referenced Value, or -1 if it is not an + /// arrayValue. + UInt index() const; + + /// Return the member name of the referenced Value, or "" if it is not an + /// objectValue. + /// \note Avoid `c_str()` on result, as embedded zeroes are possible. + String name() const; + + /// Return the member name of the referenced Value. "" if it is not an + /// objectValue. + /// \deprecated This cannot be used for UTF-8 strings, since there can be + /// embedded nulls. + JSONCPP_DEPRECATED("Use `key = name();` instead.") + char const* memberName() const; + /// Return the member name of the referenced Value, or NULL if it is not an + /// objectValue. + /// \note Better version than memberName(). Allows embedded nulls. + char const* memberName(char const** end) const; + +protected: + /*! Internal utility functions to assist with implementing + * other iterator functions. The const and non-const versions + * of the "deref" protected methods expose the protected + * current_ member variable in a way that can often be + * optimized away by the compiler. + */ + const Value& deref() const; + Value& deref(); + + void increment(); + + void decrement(); + + difference_type computeDistance(const SelfType& other) const; + + bool isEqual(const SelfType& other) const; - SelfType operator++( int ) - { - SelfType temp( *this ); - ++*this; - return temp; - } - - SelfType operator--( int ) - { - SelfType temp( *this ); - --*this; - return temp; - } - - SelfType &operator--() - { - decrement(); - return *this; - } - - SelfType &operator++() - { - increment(); - return *this; - } - - reference operator *() const - { - return deref(); - } - }; + void copy(const SelfType& other); +private: + Value::ObjectValues::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_{true}; + +public: + // For some reason, BORLAND needs these at the end, rather + // than earlier. No idea why. + ValueIteratorBase(); + explicit ValueIteratorBase(const Value::ObjectValues::iterator& current); +}; + +/** \brief const iterator for object and array value. + * + */ +class JSON_API ValueConstIterator : public ValueIteratorBase { + friend class Value; + +public: + using value_type = const Value; + // typedef unsigned int size_t; + // typedef int difference_type; + using reference = const Value&; + using pointer = const Value*; + using SelfType = ValueConstIterator; + + ValueConstIterator(); + ValueConstIterator(ValueIterator const& other); + +private: + /*! \internal Use by Value to create an iterator. + */ + explicit ValueConstIterator(const Value::ObjectValues::iterator& current); + +public: + SelfType& operator=(const ValueIteratorBase& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +/** \brief Iterator for object and array value. + */ +class JSON_API ValueIterator : public ValueIteratorBase { + friend class Value; + +public: + using value_type = Value; + using size_t = unsigned int; + using difference_type = int; + using reference = Value&; + using pointer = Value*; + using SelfType = ValueIterator; + + ValueIterator(); + explicit ValueIterator(const ValueConstIterator& other); + ValueIterator(const ValueIterator& other); + +private: + /*! \internal Use by Value to create an iterator. + */ + explicit ValueIterator(const Value::ObjectValues::iterator& current); + +public: + SelfType& operator=(const SelfType& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + /*! The return value of non-const iterators can be + * changed, so the these functions are not const + * because the returned references/pointers can be used + * to change state of the base class. + */ + reference operator*() const { return const_cast(deref()); } + pointer operator->() const { return const_cast(&deref()); } +}; + +inline void swap(Value& a, Value& b) { a.swap(b); } } // namespace Json +#pragma pack(pop) + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) -#endif // CPPTL_JSON_H_INCLUDED +#endif // JSON_H_INCLUDED diff -Nru mstflint-4.17.0+1/ext_libs/json/json/version.h mstflint-4.21.0+1/ext_libs/json/json/version.h --- mstflint-4.17.0+1/ext_libs/json/json/version.h 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/json/version.h 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,28 @@ +#ifndef JSON_VERSION_H_INCLUDED +#define JSON_VERSION_H_INCLUDED + +// Note: version must be updated in three places when doing a release. This +// annoying process ensures that amalgamate, CMake, and meson all report the +// correct version. +// 1. /meson.build +// 2. /include/json/version.h +// 3. /CMakeLists.txt +// IMPORTANT: also update the SOVERSION!! + +#define JSONCPP_VERSION_STRING "1.9.5" +#define JSONCPP_VERSION_MAJOR 1 +#define JSONCPP_VERSION_MINOR 9 +#define JSONCPP_VERSION_PATCH 5 +#define JSONCPP_VERSION_QUALIFIER +#define JSONCPP_VERSION_HEXA \ + ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \ + (JSONCPP_VERSION_PATCH << 8)) + +#ifdef JSONCPP_USING_SECURE_MEMORY +#undef JSONCPP_USING_SECURE_MEMORY +#endif +#define JSONCPP_USING_SECURE_MEMORY 0 +// If non-zero, the library zeroes any memory that it has allocated before +// it frees its memory. + +#endif // JSON_VERSION_H_INCLUDED diff -Nru mstflint-4.17.0+1/ext_libs/json/json/writer.h mstflint-4.21.0+1/ext_libs/json/json/writer.h --- mstflint-4.17.0+1/ext_libs/json/json/writer.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/json/writer.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,179 +1,369 @@ -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #ifndef JSON_WRITER_H_INCLUDED -# define JSON_WRITER_H_INCLUDED +#define JSON_WRITER_H_INCLUDED -# include "value.h" -# include -# include -# include +#if !defined(JSON_IS_AMALGAMATION) +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) && defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#pragma pack(push, 8) namespace Json { - class Value; +class Value; - /** \brief Abstract class for writers. - */ - class JSON_API Writer - { - public: - virtual ~Writer(); - - virtual std::string write( const Value &root, bool noNewLine=false ) = 0; - }; - - /** \brief Outputs a Value in JSON format without formatting (not human friendly). - * - * The JSON document is written in a single line. It is not intended for 'human' consumption, - * but may be usefull to support feature such as RPC where bandwith is limited. - * \sa Reader, Value - */ - class JSON_API FastWriter : public Writer - { - public: - FastWriter(); - virtual ~FastWriter(){} - - void enableYAMLCompatibility(); - - public: // overridden from Writer - virtual std::string write( const Value &root, bool noNewLine=false ); - - private: - void writeValue( const Value &value ); - - std::string document_; - bool yamlCompatiblityEnabled_; - }; - - /** \brief Writes a Value in JSON format in a human friendly way. - * - * The rules for line break and indent are as follow: - * - Object value: - * - if empty then print {} without indent and line break - * - if not empty the print '{', line break & indent, print one value per line - * and then unindent and line break and print '}'. - * - Array value: - * - if empty then print [] without indent and line break - * - if the array contains no object value, empty array or some other value types, - * and all the values fit on one lines, then print the array on a single line. - * - otherwise, it the values do not fit on one line, or the array contains - * object or non empty array, then print one value per line. - * - * If the Value have comments then they are outputed according to their #CommentPlacement. - * - * \sa Reader, Value, Value::setComment() - */ - class JSON_API StyledWriter: public Writer - { - public: - StyledWriter(); - virtual ~StyledWriter(){} - - public: // overridden from Writer - /** \brief Serialize a Value in JSON format. - * \param root Value to serialize. - * \return String containing the JSON document that represents the root value. - */ - virtual std::string write( const Value &root, bool noNewLine=false ); - - private: - void writeValue( const Value &value ); - void writeArrayValue( const Value &value ); - bool isMultineArray( const Value &value ); - void pushValue( const std::string &value ); - void writeIndent(); - void writeWithIndent( const std::string &value ); - void indent(); - void unindent(); - void writeCommentBeforeValue( const Value &root ); - void writeCommentAfterValueOnSameLine( const Value &root ); - bool hasCommentForValue( const Value &value ); - static std::string normalizeEOL( const std::string &text ); - - typedef std::vector ChildValues; - - ChildValues childValues_; - std::string document_; - std::string indentString_; - int rightMargin_; - int indentSize_; - bool addChildValues_; - }; - - /** \brief Writes a Value in JSON format in a human friendly way, - to a stream rather than to a string. - * - * The rules for line break and indent are as follow: - * - Object value: - * - if empty then print {} without indent and line break - * - if not empty the print '{', line break & indent, print one value per line - * and then unindent and line break and print '}'. - * - Array value: - * - if empty then print [] without indent and line break - * - if the array contains no object value, empty array or some other value types, - * and all the values fit on one lines, then print the array on a single line. - * - otherwise, it the values do not fit on one line, or the array contains - * object or non empty array, then print one value per line. - * - * If the Value have comments then they are outputed according to their #CommentPlacement. - * - * \param indentation Each level will be indented by this amount extra. - * \sa Reader, Value, Value::setComment() - */ - class JSON_API StyledStreamWriter - { - public: - StyledStreamWriter( std::string indentation="\t"); - ~StyledStreamWriter(){} - - public: - /** \brief Serialize a Value in JSON format. - * \param out Stream to write to. (Can be ostringstream, e.g.) - * \param root Value to serialize. - * \note There is no point in deriving from Writer, since write() should not return a value. - */ - void write( std::ostream &out, const Value &root, bool noNewLine=false ); - - private: - void writeValue( const Value &value ); - void writeArrayValue( const Value &value ); - bool isMultineArray( const Value &value ); - void pushValue( const std::string &value ); - void writeIndent(); - void writeWithIndent( const std::string &value ); - void indent(); - void unindent(); - void writeCommentBeforeValue( const Value &root ); - void writeCommentAfterValueOnSameLine( const Value &root ); - bool hasCommentForValue( const Value &value ); - static std::string normalizeEOL( const std::string &text ); - - typedef std::vector ChildValues; - - ChildValues childValues_; - std::ostream* document_; - std::string indentString_; - int rightMargin_; - std::string indentation_; - bool addChildValues_; - }; - - std::string JSON_API valueToString( Int value ); - std::string JSON_API valueToString( UInt value ); - std::string JSON_API valueToString( double value ); - std::string JSON_API valueToString( bool value ); - std::string JSON_API valueToQuotedString( const char *value ); - - /// \brief Output using the StyledStreamWriter. - /// \see Json::operator>>() - std::ostream& operator<<( std::ostream&, const Value &root ); +/** + * + * Usage: + * \code + * using namespace Json; + * void writeToStdout(StreamWriter::Factory const& factory, Value const& value) + * { std::unique_ptr const writer( factory.newStreamWriter()); + * writer->write(value, &std::cout); + * std::cout << std::endl; // add lf and flush + * } + * \endcode + */ +class JSON_API StreamWriter { +protected: + OStream* sout_; // not owned; will not delete +public: + StreamWriter(); + virtual ~StreamWriter(); + /** Write Value into document as configured in sub-class. + * Do not take ownership of sout, but maintain a reference during function. + * \pre sout != NULL + * \return zero on success (For now, we always return zero, so check the + * stream instead.) \throw std::exception possibly, depending on + * configuration + */ + virtual int write(Value const& root, OStream* sout) = 0; + + /** \brief A simple abstract factory. + */ + class JSON_API Factory { + public: + virtual ~Factory(); + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual StreamWriter* newStreamWriter() const = 0; + }; // Factory +}; // StreamWriter + +/** \brief Write into stringstream, then return string, for convenience. + * A StreamWriter will be created from the factory, used, and then deleted. + */ +String JSON_API writeString(StreamWriter::Factory const& factory, + Value const& root); + +/** \brief Build a StreamWriter implementation. + +* Usage: +* \code +* using namespace Json; +* Value value = ...; +* StreamWriterBuilder builder; +* builder["commentStyle"] = "None"; +* builder["indentation"] = " "; // or whatever you like +* std::unique_ptr writer( +* builder.newStreamWriter()); +* writer->write(value, &std::cout); +* std::cout << std::endl; // add lf and flush +* \endcode +*/ +class JSON_API StreamWriterBuilder : public StreamWriter::Factory { +public: + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + * Available settings (case-sensitive): + * - "commentStyle": "None" or "All" + * - "indentation": "". + * - Setting this to an empty string also omits newline characters. + * - "enableYAMLCompatibility": false or true + * - slightly change the whitespace around colons + * - "dropNullPlaceholders": false or true + * - Drop the "null" string from the writer's output for nullValues. + * Strictly speaking, this is not valid JSON. But when the output is being + * fed to a browser's JavaScript, it makes for smaller output and the + * browser can handle the output just fine. + * - "useSpecialFloats": false or true + * - If true, outputs non-finite floating point values in the following way: + * NaN values as "NaN", positive infinity as "Infinity", and negative + * infinity as "-Infinity". + * - "precision": int + * - Number of precision digits for formatting of real values. + * - "precisionType": "significant"(default) or "decimal" + * - Type of precision for formatting of real values. + * - "emitUTF8": false or true + * - If true, outputs raw UTF8 strings instead of escaping them. + + * You can examine 'settings_` yourself + * to see the defaults. You can also write and read them just like any + * JSON Value. + * \sa setDefaults() + */ + Json::Value settings_; + + StreamWriterBuilder(); + ~StreamWriterBuilder() override; + + /** + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + StreamWriter* newStreamWriter() const override; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + /** A simple way to update a specific setting. + */ + Value& operator[](const String& key); + + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults + */ + static void setDefaults(Json::Value* settings); +}; + +/** \brief Abstract class for writers. + * \deprecated Use StreamWriter. (And really, this is an implementation detail.) + */ +class JSON_API Writer { +public: + virtual ~Writer(); + + virtual String write(const Value& root) = 0; +}; + +/** \brief Outputs a Value in JSON format + *without formatting (not human friendly). + * + * The JSON document is written in a single line. It is not intended for 'human' + *consumption, + * but may be useful to support feature such as RPC where bandwidth is limited. + * \sa Reader, Value + * \deprecated Use StreamWriterBuilder. + */ +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) // Deriving from deprecated class +#endif +class JSON_API FastWriter + : public Writer { +public: + FastWriter(); + ~FastWriter() override = default; + + void enableYAMLCompatibility(); + + /** \brief Drop the "null" string from the writer's output for nullValues. + * Strictly speaking, this is not valid JSON. But when the output is being + * fed to a browser's JavaScript, it makes for smaller output and the + * browser can handle the output just fine. + */ + void dropNullPlaceholders(); + + void omitEndingLineFeed(); + +public: // overridden from Writer + String write(const Value& root) override; + +private: + void writeValue(const Value& value); + + String document_; + bool yamlCompatibilityEnabled_{false}; + bool dropNullPlaceholders_{false}; + bool omitEndingLineFeed_{true}; +}; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +/** \brief Writes a Value in JSON format in a + *human friendly way. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + *line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + *types, + * and all the values fit on one lines, then print the array on a single + *line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + *#CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) // Deriving from deprecated class +#endif +class JSON_API + StyledWriter : public Writer { +public: + StyledWriter(); + ~StyledWriter() override = default; + +public: // overridden from Writer + /** \brief Serialize a Value in JSON format. + * \param root Value to serialize. + * \return String containing the JSON document that represents the root value. + */ + String write(const Value& root) override; + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultilineArray(const Value& value); + void pushValue(const String& value); + void writeIndent(); + void writeWithIndent(const String& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + static bool hasCommentForValue(const Value& value); + static String normalizeEOL(const String& text); + + using ChildValues = std::vector; + + ChildValues childValues_; + String document_; + String indentString_; + unsigned int rightMargin_{74}; + unsigned int indentSize_{3}; + bool addChildValues_{false}; +}; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +/** \brief Writes a Value in JSON format in a + human friendly way, + to a stream rather than to a string. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + types, + * and all the values fit on one lines, then print the array on a single + line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + #CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) // Deriving from deprecated class +#endif +class JSON_API + StyledStreamWriter { +public: + /** + * \param indentation Each level will be indented by this amount extra. + */ + StyledStreamWriter(String indentation = "\t"); + ~StyledStreamWriter() = default; + +public: + /** \brief Serialize a Value in JSON format. + * \param out Stream to write to. (Can be ostringstream, e.g.) + * \param root Value to serialize. + * \note There is no point in deriving from Writer, since write() should not + * return a value. + */ + void write(OStream& out, const Value& root); + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultilineArray(const Value& value); + void pushValue(const String& value); + void writeIndent(); + void writeWithIndent(const String& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + static bool hasCommentForValue(const Value& value); + static String normalizeEOL(const String& text); + + using ChildValues = std::vector; + + ChildValues childValues_; + OStream* document_; + String indentString_; + unsigned int rightMargin_{74}; + String indentation_; + bool addChildValues_ : 1; + bool indented_ : 1; +}; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#if defined(JSON_HAS_INT64) +String JSON_API valueToString(Int value); +String JSON_API valueToString(UInt value); +#endif // if defined(JSON_HAS_INT64) +String JSON_API valueToString(LargestInt value); +String JSON_API valueToString(LargestUInt value); +String JSON_API valueToString( + double value, unsigned int precision = Value::defaultRealPrecision, + PrecisionType precisionType = PrecisionType::significantDigits); +String JSON_API valueToString(bool value); +String JSON_API valueToQuotedString(const char* value); + +/// \brief Output using the StyledStreamWriter. +/// \see Json::operator>>() +JSON_API OStream& operator<<(OStream&, const Value& root); } // namespace Json +#pragma pack(pop) +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #endif // JSON_WRITER_H_INCLUDED diff -Nru mstflint-4.17.0+1/ext_libs/json/json_batchallocator.h mstflint-4.21.0+1/ext_libs/json/json_batchallocator.h --- mstflint-4.17.0+1/ext_libs/json/json_batchallocator.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/json_batchallocator.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,130 +0,0 @@ -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef JSONCPP_BATCHALLOCATOR_H_INCLUDED -# define JSONCPP_BATCHALLOCATOR_H_INCLUDED - -# include -# include - -# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - -namespace Json { - -/* Fast memory allocator. - * - * This memory allocator allocates memory for a batch of object (specified by - * the page size, the number of object in each page). - * - * It does not allow the destruction of a single object. All the allocated objects - * can be destroyed at once. The memory can be either released or reused for future - * allocation. - * - * The in-place new operator must be used to construct the object using the pointer - * returned by allocate. - */ -template -class BatchAllocator -{ -public: - typedef AllocatedType Type; - - BatchAllocator( unsigned int objectsPerPage = 255 ) - : freeHead_( 0 ) - , objectsPerPage_( objectsPerPage ) - { -// printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() ); - assert( sizeof(AllocatedType) * objectPerAllocation >= sizeof(AllocatedType *) ); // We must be able to store a slist in the object free space. - assert( objectsPerPage >= 16 ); - batches_ = allocateBatch( 0 ); // allocated a dummy page - currentBatch_ = batches_; - } - - ~BatchAllocator() - { - for ( BatchInfo *batch = batches_; batch; ) - { - BatchInfo *nextBatch = batch->next_; - free( batch ); - batch = nextBatch; - } - } - - /// allocate space for an array of objectPerAllocation object. - /// @warning it is the responsability of the caller to call objects constructors. - AllocatedType *allocate() - { - if ( freeHead_ ) // returns node from free list. - { - AllocatedType *object = freeHead_; - freeHead_ = *(AllocatedType **)object; - return object; - } - if ( currentBatch_->used_ == currentBatch_->end_ ) - { - currentBatch_ = currentBatch_->next_; - while ( currentBatch_ && currentBatch_->used_ == currentBatch_->end_ ) - currentBatch_ = currentBatch_->next_; - - if ( !currentBatch_ ) // no free batch found, allocate a new one - { - currentBatch_ = allocateBatch( objectsPerPage_ ); - currentBatch_->next_ = batches_; // insert at the head of the list - batches_ = currentBatch_; - } - } - AllocatedType *allocated = currentBatch_->used_; - currentBatch_->used_ += objectPerAllocation; - return allocated; - } - - /// Release the object. - /// @warning it is the responsability of the caller to actually destruct the object. - void release( AllocatedType *object ) - { - assert( object != 0 ); - *(AllocatedType **)object = freeHead_; - freeHead_ = object; - } - -private: - struct BatchInfo - { - BatchInfo *next_; - AllocatedType *used_; - AllocatedType *end_; - AllocatedType buffer_[objectPerAllocation]; - }; - - // disabled copy constructor and assignement operator. - BatchAllocator( const BatchAllocator & ); - void operator =( const BatchAllocator &); - - static BatchInfo *allocateBatch( unsigned int objectsPerPage ) - { - const unsigned int mallocSize = sizeof(BatchInfo) - sizeof(AllocatedType)* objectPerAllocation - + sizeof(AllocatedType) * objectPerAllocation * objectsPerPage; - BatchInfo *batch = static_cast( malloc( mallocSize ) ); - batch->next_ = 0; - batch->used_ = batch->buffer_; - batch->end_ = batch->buffer_ + objectsPerPage; - return batch; - } - - BatchInfo *batches_; - BatchInfo *currentBatch_; - /// Head of a single linked list within the allocated space of freeed object - AllocatedType *freeHead_; - unsigned int objectsPerPage_; -}; - - -} // namespace Json - -# endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION - -#endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED - diff -Nru mstflint-4.17.0+1/ext_libs/json/json_internalarray.inl mstflint-4.21.0+1/ext_libs/json/json_internalarray.inl --- mstflint-4.17.0+1/ext_libs/json/json_internalarray.inl 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/json_internalarray.inl 1970-01-01 00:00:00.000000000 +0000 @@ -1,453 +0,0 @@ -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -// included by json_value.cpp -// everything is within Json namespace - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class ValueInternalArray -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -ValueArrayAllocator::~ValueArrayAllocator() -{ -} - -// ////////////////////////////////////////////////////////////////// -// class DefaultValueArrayAllocator -// ////////////////////////////////////////////////////////////////// -#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR -class DefaultValueArrayAllocator : public ValueArrayAllocator -{ -public: // overridden from ValueArrayAllocator - virtual ~DefaultValueArrayAllocator() - { - } - - virtual ValueInternalArray *newArray() - { - return new ValueInternalArray(); - } - - virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) - { - return new ValueInternalArray( other ); - } - - virtual void destructArray( ValueInternalArray *array ) - { - delete array; - } - - virtual void reallocateArrayPageIndex( Value **&indexes, - ValueInternalArray::PageIndex &indexCount, - ValueInternalArray::PageIndex minNewIndexCount ) - { - ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; - if ( minNewIndexCount > newIndexCount ) - newIndexCount = minNewIndexCount; - void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); - if ( !newIndexes ) - throw std::bad_alloc(); - indexCount = newIndexCount; - indexes = static_cast( newIndexes ); - } - virtual void releaseArrayPageIndex( Value **indexes, - ValueInternalArray::PageIndex indexCount ) - { - if ( indexes ) - free( indexes ); - } - - virtual Value *allocateArrayPage() - { - return static_cast( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); - } - - virtual void releaseArrayPage( Value *value ) - { - if ( value ) - free( value ); - } -}; - -#else // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR -/// @todo make this thread-safe (lock when accessign batch allocator) -class DefaultValueArrayAllocator : public ValueArrayAllocator -{ -public: // overridden from ValueArrayAllocator - virtual ~DefaultValueArrayAllocator() - { - } - - virtual ValueInternalArray *newArray() - { - ValueInternalArray *array = arraysAllocator_.allocate(); - new (array) ValueInternalArray(); // placement new - return array; - } - - virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) - { - ValueInternalArray *array = arraysAllocator_.allocate(); - new (array) ValueInternalArray( other ); // placement new - return array; - } - - virtual void destructArray( ValueInternalArray *array ) - { - if ( array ) - { - array->~ValueInternalArray(); - arraysAllocator_.release( array ); - } - } - - virtual void reallocateArrayPageIndex( Value **&indexes, - ValueInternalArray::PageIndex &indexCount, - ValueInternalArray::PageIndex minNewIndexCount ) - { - ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; - if ( minNewIndexCount > newIndexCount ) - newIndexCount = minNewIndexCount; - void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); - if ( !newIndexes ) - throw std::bad_alloc(); - indexCount = newIndexCount; - indexes = static_cast( newIndexes ); - } - virtual void releaseArrayPageIndex( Value **indexes, - ValueInternalArray::PageIndex indexCount ) - { - if ( indexes ) - free( indexes ); - } - - virtual Value *allocateArrayPage() - { - return static_cast( pagesAllocator_.allocate() ); - } - - virtual void releaseArrayPage( Value *value ) - { - if ( value ) - pagesAllocator_.release( value ); - } -private: - BatchAllocator arraysAllocator_; - BatchAllocator pagesAllocator_; -}; -#endif // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR - -static ValueArrayAllocator *&arrayAllocator() -{ - static DefaultValueArrayAllocator defaultAllocator; - static ValueArrayAllocator *arrayAllocator = &defaultAllocator; - return arrayAllocator; -} - -static struct DummyArrayAllocatorInitializer { - DummyArrayAllocatorInitializer() - { - arrayAllocator(); // ensure arrayAllocator() statics are initialized before main(). - } -} dummyArrayAllocatorInitializer; - -// ////////////////////////////////////////////////////////////////// -// class ValueInternalArray -// ////////////////////////////////////////////////////////////////// -bool -ValueInternalArray::equals( const IteratorState &x, - const IteratorState &other ) -{ - return x.array_ == other.array_ - && x.currentItemIndex_ == other.currentItemIndex_ - && x.currentPageIndex_ == other.currentPageIndex_; -} - - -void -ValueInternalArray::increment( IteratorState &it ) -{ - JSON_ASSERT_MESSAGE( it.array_ && - (it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_ - != it.array_->size_, - "ValueInternalArray::increment(): moving iterator beyond end" ); - ++(it.currentItemIndex_); - if ( it.currentItemIndex_ == itemsPerPage ) - { - it.currentItemIndex_ = 0; - ++(it.currentPageIndex_); - } -} - - -void -ValueInternalArray::decrement( IteratorState &it ) -{ - JSON_ASSERT_MESSAGE( it.array_ && it.currentPageIndex_ == it.array_->pages_ - && it.currentItemIndex_ == 0, - "ValueInternalArray::decrement(): moving iterator beyond end" ); - if ( it.currentItemIndex_ == 0 ) - { - it.currentItemIndex_ = itemsPerPage-1; - --(it.currentPageIndex_); - } - else - { - --(it.currentItemIndex_); - } -} - - -Value & -ValueInternalArray::unsafeDereference( const IteratorState &it ) -{ - return (*(it.currentPageIndex_))[it.currentItemIndex_]; -} - - -Value & -ValueInternalArray::dereference( const IteratorState &it ) -{ - JSON_ASSERT_MESSAGE( it.array_ && - (it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_ - < it.array_->size_, - "ValueInternalArray::dereference(): dereferencing invalid iterator" ); - return unsafeDereference( it ); -} - -void -ValueInternalArray::makeBeginIterator( IteratorState &it ) const -{ - it.array_ = const_cast( this ); - it.currentItemIndex_ = 0; - it.currentPageIndex_ = pages_; -} - - -void -ValueInternalArray::makeIterator( IteratorState &it, ArrayIndex index ) const -{ - it.array_ = const_cast( this ); - it.currentItemIndex_ = index % itemsPerPage; - it.currentPageIndex_ = pages_ + index / itemsPerPage; -} - - -void -ValueInternalArray::makeEndIterator( IteratorState &it ) const -{ - makeIterator( it, size_ ); -} - - -ValueInternalArray::ValueInternalArray() - : pages_( 0 ) - , size_( 0 ) - , pageCount_( 0 ) -{ -} - - -ValueInternalArray::ValueInternalArray( const ValueInternalArray &other ) - : pages_( 0 ) - , pageCount_( 0 ) - , size_( other.size_ ) -{ - PageIndex minNewPages = other.size_ / itemsPerPage; - arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages ); - JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, - "ValueInternalArray::reserve(): bad reallocation" ); - IteratorState itOther; - other.makeBeginIterator( itOther ); - Value *value; - for ( ArrayIndex index = 0; index < size_; ++index, increment(itOther) ) - { - if ( index % itemsPerPage == 0 ) - { - PageIndex pageIndex = index / itemsPerPage; - value = arrayAllocator()->allocateArrayPage(); - pages_[pageIndex] = value; - } - new (value) Value( dereference( itOther ) ); - } -} - - -ValueInternalArray & -ValueInternalArray::operator =( const ValueInternalArray &other ) -{ - ValueInternalArray temp( other ); - swap( temp ); - return *this; -} - - -ValueInternalArray::~ValueInternalArray() -{ - // destroy all constructed items - IteratorState it; - IteratorState itEnd; - makeBeginIterator( it); - makeEndIterator( itEnd ); - for ( ; !equals(it,itEnd); increment(it) ) - { - Value *value = &dereference(it); - value->~Value(); - } - // release all pages - PageIndex lastPageIndex = size_ / itemsPerPage; - for ( PageIndex pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex ) - arrayAllocator()->releaseArrayPage( pages_[pageIndex] ); - // release pages index - arrayAllocator()->releaseArrayPageIndex( pages_, pageCount_ ); -} - - -void -ValueInternalArray::swap( ValueInternalArray &other ) -{ - Value **tempPages = pages_; - pages_ = other.pages_; - other.pages_ = tempPages; - ArrayIndex tempSize = size_; - size_ = other.size_; - other.size_ = tempSize; - PageIndex tempPageCount = pageCount_; - pageCount_ = other.pageCount_; - other.pageCount_ = tempPageCount; -} - -void -ValueInternalArray::clear() -{ - ValueInternalArray dummy; - swap( dummy ); -} - - -void -ValueInternalArray::resize( ArrayIndex newSize ) -{ - if ( newSize == 0 ) - clear(); - else if ( newSize < size_ ) - { - IteratorState it; - IteratorState itEnd; - makeIterator( it, newSize ); - makeIterator( itEnd, size_ ); - for ( ; !equals(it,itEnd); increment(it) ) - { - Value *value = &dereference(it); - value->~Value(); - } - PageIndex pageIndex = (newSize + itemsPerPage - 1) / itemsPerPage; - PageIndex lastPageIndex = size_ / itemsPerPage; - for ( ; pageIndex < lastPageIndex; ++pageIndex ) - arrayAllocator()->releaseArrayPage( pages_[pageIndex] ); - size_ = newSize; - } - else if ( newSize > size_ ) - resolveReference( newSize ); -} - - -void -ValueInternalArray::makeIndexValid( ArrayIndex index ) -{ - // Need to enlarge page index ? - if ( index >= pageCount_ * itemsPerPage ) - { - PageIndex minNewPages = (index + 1) / itemsPerPage; - arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages ); - JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, "ValueInternalArray::reserve(): bad reallocation" ); - } - - // Need to allocate new pages ? - ArrayIndex nextPageIndex = - (size_ % itemsPerPage) != 0 ? size_ - (size_%itemsPerPage) + itemsPerPage - : size_; - if ( nextPageIndex <= index ) - { - PageIndex pageIndex = nextPageIndex / itemsPerPage; - PageIndex pageToAllocate = (index - nextPageIndex) / itemsPerPage + 1; - for ( ; pageToAllocate-- > 0; ++pageIndex ) - pages_[pageIndex] = arrayAllocator()->allocateArrayPage(); - } - - // Initialize all new entries - IteratorState it; - IteratorState itEnd; - makeIterator( it, size_ ); - size_ = index + 1; - makeIterator( itEnd, size_ ); - for ( ; !equals(it,itEnd); increment(it) ) - { - Value *value = &dereference(it); - new (value) Value(); // Construct a default value using placement new - } -} - -Value & -ValueInternalArray::resolveReference( ArrayIndex index ) -{ - if ( index >= size_ ) - makeIndexValid( index ); - return pages_[index/itemsPerPage][index%itemsPerPage]; -} - -Value * -ValueInternalArray::find( ArrayIndex index ) const -{ - if ( index >= size_ ) - return 0; - return &(pages_[index/itemsPerPage][index%itemsPerPage]); -} - -ValueInternalArray::ArrayIndex -ValueInternalArray::size() const -{ - return size_; -} - -int -ValueInternalArray::distance( const IteratorState &x, const IteratorState &y ) -{ - return indexOf(y) - indexOf(x); -} - - -ValueInternalArray::ArrayIndex -ValueInternalArray::indexOf( const IteratorState &iterator ) -{ - if ( !iterator.array_ ) - return ArrayIndex(-1); - return ArrayIndex( - (iterator.currentPageIndex_ - iterator.array_->pages_) * itemsPerPage - + iterator.currentItemIndex_ ); -} - - -int -ValueInternalArray::compare( const ValueInternalArray &other ) const -{ - int sizeDiff( size_ - other.size_ ); - if ( sizeDiff != 0 ) - return sizeDiff; - - for ( ArrayIndex index =0; index < size_; ++index ) - { - int diff = pages_[index/itemsPerPage][index%itemsPerPage].compare( - other.pages_[index/itemsPerPage][index%itemsPerPage] ); - if ( diff != 0 ) - return diff; - } - return 0; -} diff -Nru mstflint-4.17.0+1/ext_libs/json/json_internalmap.inl mstflint-4.21.0+1/ext_libs/json/json_internalmap.inl --- mstflint-4.17.0+1/ext_libs/json/json_internalmap.inl 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/json_internalmap.inl 1970-01-01 00:00:00.000000000 +0000 @@ -1,612 +0,0 @@ -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -// included by json_value.cpp -// everything is within Json namespace - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class ValueInternalMap -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -/** \internal MUST be safely initialized using memset( this, 0, sizeof(ValueInternalLink) ); - * This optimization is used by the fast allocator. - */ -ValueInternalLink::ValueInternalLink() - : previous_( 0 ) - , next_( 0 ) -{ -} - -ValueInternalLink::~ValueInternalLink() -{ - for ( int index =0; index < itemPerLink; ++index ) - { - if ( !items_[index].isItemAvailable() ) - { - if ( !items_[index].isMemberNameStatic() ) - free( keys_[index] ); - } - else - break; - } -} - - - -ValueMapAllocator::~ValueMapAllocator() -{ -} - -#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR -class DefaultValueMapAllocator : public ValueMapAllocator -{ -public: // overridden from ValueMapAllocator - virtual ValueInternalMap *newMap() - { - return new ValueInternalMap(); - } - - virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) - { - return new ValueInternalMap( other ); - } - - virtual void destructMap( ValueInternalMap *map ) - { - delete map; - } - - virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) - { - return new ValueInternalLink[size]; - } - - virtual void releaseMapBuckets( ValueInternalLink *links ) - { - delete [] links; - } - - virtual ValueInternalLink *allocateMapLink() - { - return new ValueInternalLink(); - } - - virtual void releaseMapLink( ValueInternalLink *link ) - { - delete link; - } -}; -#else -/// @todo make this thread-safe (lock when accessign batch allocator) -class DefaultValueMapAllocator : public ValueMapAllocator -{ -public: // overridden from ValueMapAllocator - virtual ValueInternalMap *newMap() - { - ValueInternalMap *map = mapsAllocator_.allocate(); - new (map) ValueInternalMap(); // placement new - return map; - } - - virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) - { - ValueInternalMap *map = mapsAllocator_.allocate(); - new (map) ValueInternalMap( other ); // placement new - return map; - } - - virtual void destructMap( ValueInternalMap *map ) - { - if ( map ) - { - map->~ValueInternalMap(); - mapsAllocator_.release( map ); - } - } - - virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) - { - return new ValueInternalLink[size]; - } - - virtual void releaseMapBuckets( ValueInternalLink *links ) - { - delete [] links; - } - - virtual ValueInternalLink *allocateMapLink() - { - ValueInternalLink *link = linksAllocator_.allocate(); - memset( link, 0, sizeof(ValueInternalLink) ); - return link; - } - - virtual void releaseMapLink( ValueInternalLink *link ) - { - link->~ValueInternalLink(); - linksAllocator_.release( link ); - } -private: - BatchAllocator mapsAllocator_; - BatchAllocator linksAllocator_; -}; -#endif - -static ValueMapAllocator *&mapAllocator() -{ - static DefaultValueMapAllocator defaultAllocator; - static ValueMapAllocator *mapAllocator = &defaultAllocator; - return mapAllocator; -} - -static struct DummyMapAllocatorInitializer { - DummyMapAllocatorInitializer() - { - mapAllocator(); // ensure mapAllocator() statics are initialized before main(). - } -} dummyMapAllocatorInitializer; - - - -// h(K) = value * K >> w ; with w = 32 & K prime w.r.t. 2^32. - -/* -use linked list hash map. -buckets array is a container. -linked list element contains 6 key/values. (memory = (16+4) * 6 + 4 = 124) -value have extra state: valid, available, deleted -*/ - - -ValueInternalMap::ValueInternalMap() - : buckets_( 0 ) - , tailLink_( 0 ) - , bucketsSize_( 0 ) - , itemCount_( 0 ) -{ -} - - -ValueInternalMap::ValueInternalMap( const ValueInternalMap &other ) - : buckets_( 0 ) - , tailLink_( 0 ) - , bucketsSize_( 0 ) - , itemCount_( 0 ) -{ - reserve( other.itemCount_ ); - IteratorState it; - IteratorState itEnd; - other.makeBeginIterator( it ); - other.makeEndIterator( itEnd ); - for ( ; !equals(it,itEnd); increment(it) ) - { - bool isStatic; - const char *memberName = key( it, isStatic ); - const Value &aValue = value( it ); - resolveReference(memberName, isStatic) = aValue; - } -} - - -ValueInternalMap & -ValueInternalMap::operator =( const ValueInternalMap &other ) -{ - ValueInternalMap dummy( other ); - swap( dummy ); - return *this; -} - - -ValueInternalMap::~ValueInternalMap() -{ - if ( buckets_ ) - { - for ( BucketIndex bucketIndex =0; bucketIndex < bucketsSize_; ++bucketIndex ) - { - ValueInternalLink *link = buckets_[bucketIndex].next_; - while ( link ) - { - ValueInternalLink *linkToRelease = link; - link = link->next_; - mapAllocator()->releaseMapLink( linkToRelease ); - } - } - mapAllocator()->releaseMapBuckets( buckets_ ); - } -} - - -void -ValueInternalMap::swap( ValueInternalMap &other ) -{ - ValueInternalLink *tempBuckets = buckets_; - buckets_ = other.buckets_; - other.buckets_ = tempBuckets; - ValueInternalLink *tempTailLink = tailLink_; - tailLink_ = other.tailLink_; - other.tailLink_ = tempTailLink; - BucketIndex tempBucketsSize = bucketsSize_; - bucketsSize_ = other.bucketsSize_; - other.bucketsSize_ = tempBucketsSize; - BucketIndex tempItemCount = itemCount_; - itemCount_ = other.itemCount_; - other.itemCount_ = tempItemCount; -} - - -void -ValueInternalMap::clear() -{ - ValueInternalMap dummy; - swap( dummy ); -} - - -ValueInternalMap::BucketIndex -ValueInternalMap::size() const -{ - return itemCount_; -} - -bool -ValueInternalMap::reserveDelta( BucketIndex growth ) -{ - return reserve( itemCount_ + growth ); -} - -bool -ValueInternalMap::reserve( BucketIndex newItemCount ) -{ - if ( !buckets_ && newItemCount > 0 ) - { - buckets_ = mapAllocator()->allocateMapBuckets( 1 ); - bucketsSize_ = 1; - tailLink_ = &buckets_[0]; - } -// BucketIndex idealBucketCount = (newItemCount + ValueInternalLink::itemPerLink) / ValueInternalLink::itemPerLink; - return true; -} - - -const Value * -ValueInternalMap::find( const char *key ) const -{ - if ( !bucketsSize_ ) - return 0; - HashKey hashedKey = hash( key ); - BucketIndex bucketIndex = hashedKey % bucketsSize_; - for ( const ValueInternalLink *current = &buckets_[bucketIndex]; - current != 0; - current = current->next_ ) - { - for ( BucketIndex index=0; index < ValueInternalLink::itemPerLink; ++index ) - { - if ( current->items_[index].isItemAvailable() ) - return 0; - if ( strcmp( key, current->keys_[index] ) == 0 ) - return ¤t->items_[index]; - } - } - return 0; -} - - -Value * -ValueInternalMap::find( const char *key ) -{ - const ValueInternalMap *constThis = this; - return const_cast( constThis->find( key ) ); -} - - -Value & -ValueInternalMap::resolveReference( const char *key, - bool isStatic ) -{ - HashKey hashedKey = hash( key ); - if ( bucketsSize_ ) - { - BucketIndex bucketIndex = hashedKey % bucketsSize_; - ValueInternalLink **previous = 0; - BucketIndex index; - for ( ValueInternalLink *current = &buckets_[bucketIndex]; - current != 0; - previous = ¤t->next_, current = current->next_ ) - { - for ( index=0; index < ValueInternalLink::itemPerLink; ++index ) - { - if ( current->items_[index].isItemAvailable() ) - return setNewItem( key, isStatic, current, index ); - if ( strcmp( key, current->keys_[index] ) == 0 ) - return current->items_[index]; - } - } - } - - reserveDelta( 1 ); - return unsafeAdd( key, isStatic, hashedKey ); -} - - -void -ValueInternalMap::remove( const char *key ) -{ - HashKey hashedKey = hash( key ); - if ( !bucketsSize_ ) - return; - BucketIndex bucketIndex = hashedKey % bucketsSize_; - for ( ValueInternalLink *link = &buckets_[bucketIndex]; - link != 0; - link = link->next_ ) - { - BucketIndex index; - for ( index =0; index < ValueInternalLink::itemPerLink; ++index ) - { - if ( link->items_[index].isItemAvailable() ) - return; - if ( strcmp( key, link->keys_[index] ) == 0 ) - { - doActualRemove( link, index, bucketIndex ); - return; - } - } - } -} - -void -ValueInternalMap::doActualRemove( ValueInternalLink *link, - BucketIndex index, - BucketIndex bucketIndex ) -{ - // find last item of the bucket and swap it with the 'removed' one. - // set removed items flags to 'available'. - // if last page only contains 'available' items, then desallocate it (it's empty) - ValueInternalLink *&lastLink = getLastLinkInBucket( index ); - BucketIndex lastItemIndex = 1; // a link can never be empty, so start at 1 - for ( ; - lastItemIndex < ValueInternalLink::itemPerLink; - ++lastItemIndex ) // may be optimized with dicotomic search - { - if ( lastLink->items_[lastItemIndex].isItemAvailable() ) - break; - } - - BucketIndex lastUsedIndex = lastItemIndex - 1; - Value *valueToDelete = &link->items_[index]; - Value *valueToPreserve = &lastLink->items_[lastUsedIndex]; - if ( valueToDelete != valueToPreserve ) - valueToDelete->swap( *valueToPreserve ); - if ( lastUsedIndex == 0 ) // page is now empty - { // remove it from bucket linked list and delete it. - ValueInternalLink *linkPreviousToLast = lastLink->previous_; - if ( linkPreviousToLast != 0 ) // can not deleted bucket link. - { - mapAllocator()->releaseMapLink( lastLink ); - linkPreviousToLast->next_ = 0; - lastLink = linkPreviousToLast; - } - } - else - { - Value dummy; - valueToPreserve->swap( dummy ); // restore deleted to default Value. - valueToPreserve->setItemUsed( false ); - } - --itemCount_; -} - - -ValueInternalLink *& -ValueInternalMap::getLastLinkInBucket( BucketIndex bucketIndex ) -{ - if ( bucketIndex == bucketsSize_ - 1 ) - return tailLink_; - ValueInternalLink *&previous = buckets_[bucketIndex+1].previous_; - if ( !previous ) - previous = &buckets_[bucketIndex]; - return previous; -} - - -Value & -ValueInternalMap::setNewItem( const char *key, - bool isStatic, - ValueInternalLink *link, - BucketIndex index ) -{ - char *duplicatedKey = valueAllocator()->makeMemberName( key ); - ++itemCount_; - link->keys_[index] = duplicatedKey; - link->items_[index].setItemUsed(); - link->items_[index].setMemberNameIsStatic( isStatic ); - return link->items_[index]; // items already default constructed. -} - - -Value & -ValueInternalMap::unsafeAdd( const char *key, - bool isStatic, - HashKey hashedKey ) -{ - JSON_ASSERT_MESSAGE( bucketsSize_ > 0, "ValueInternalMap::unsafeAdd(): internal logic error." ); - BucketIndex bucketIndex = hashedKey % bucketsSize_; - ValueInternalLink *&previousLink = getLastLinkInBucket( bucketIndex ); - ValueInternalLink *link = previousLink; - BucketIndex index; - for ( index =0; index < ValueInternalLink::itemPerLink; ++index ) - { - if ( link->items_[index].isItemAvailable() ) - break; - } - if ( index == ValueInternalLink::itemPerLink ) // need to add a new page - { - ValueInternalLink *newLink = mapAllocator()->allocateMapLink(); - index = 0; - link->next_ = newLink; - previousLink = newLink; - link = newLink; - } - return setNewItem( key, isStatic, link, index ); -} - - -ValueInternalMap::HashKey -ValueInternalMap::hash( const char *key ) const -{ - HashKey hash = 0; - while ( *key ) - hash += *key++ * 37; - return hash; -} - - -int -ValueInternalMap::compare( const ValueInternalMap &other ) const -{ - int sizeDiff( itemCount_ - other.itemCount_ ); - if ( sizeDiff != 0 ) - return sizeDiff; - // Strict order guaranty is required. Compare all keys FIRST, then compare values. - IteratorState it; - IteratorState itEnd; - makeBeginIterator( it ); - makeEndIterator( itEnd ); - for ( ; !equals(it,itEnd); increment(it) ) - { - if ( !other.find( key( it ) ) ) - return 1; - } - - // All keys are equals, let's compare values - makeBeginIterator( it ); - for ( ; !equals(it,itEnd); increment(it) ) - { - const Value *otherValue = other.find( key( it ) ); - int valueDiff = value(it).compare( *otherValue ); - if ( valueDiff != 0 ) - return valueDiff; - } - return 0; -} - - -void -ValueInternalMap::makeBeginIterator( IteratorState &it ) const -{ - it.map_ = const_cast( this ); - it.bucketIndex_ = 0; - it.itemIndex_ = 0; - it.link_ = buckets_; -} - - -void -ValueInternalMap::makeEndIterator( IteratorState &it ) const -{ - it.map_ = const_cast( this ); - it.bucketIndex_ = bucketsSize_; - it.itemIndex_ = 0; - it.link_ = 0; -} - - -bool -ValueInternalMap::equals( const IteratorState &x, const IteratorState &other ) -{ - return x.map_ == other.map_ - && x.bucketIndex_ == other.bucketIndex_ - && x.link_ == other.link_ - && x.itemIndex_ == other.itemIndex_; -} - - -void -ValueInternalMap::incrementBucket( IteratorState &iterator ) -{ - ++iterator.bucketIndex_; - JSON_ASSERT_MESSAGE( iterator.bucketIndex_ <= iterator.map_->bucketsSize_, - "ValueInternalMap::increment(): attempting to iterate beyond end." ); - if ( iterator.bucketIndex_ == iterator.map_->bucketsSize_ ) - iterator.link_ = 0; - else - iterator.link_ = &(iterator.map_->buckets_[iterator.bucketIndex_]); - iterator.itemIndex_ = 0; -} - - -void -ValueInternalMap::increment( IteratorState &iterator ) -{ - JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterator using invalid iterator." ); - ++iterator.itemIndex_; - if ( iterator.itemIndex_ == ValueInternalLink::itemPerLink ) - { - JSON_ASSERT_MESSAGE( iterator.link_ != 0, - "ValueInternalMap::increment(): attempting to iterate beyond end." ); - iterator.link_ = iterator.link_->next_; - if ( iterator.link_ == 0 ) - incrementBucket( iterator ); - } - else if ( iterator.link_->items_[iterator.itemIndex_].isItemAvailable() ) - { - incrementBucket( iterator ); - } -} - - -void -ValueInternalMap::decrement( IteratorState &iterator ) -{ - if ( iterator.itemIndex_ == 0 ) - { - JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterate using invalid iterator." ); - if ( iterator.link_ == &iterator.map_->buckets_[iterator.bucketIndex_] ) - { - JSON_ASSERT_MESSAGE( iterator.bucketIndex_ > 0, "Attempting to iterate beyond beginning." ); - --(iterator.bucketIndex_); - } - iterator.link_ = iterator.link_->previous_; - iterator.itemIndex_ = ValueInternalLink::itemPerLink - 1; - } -} - - -const char * -ValueInternalMap::key( const IteratorState &iterator ) -{ - JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); - return iterator.link_->keys_[iterator.itemIndex_]; -} - -const char * -ValueInternalMap::key( const IteratorState &iterator, bool &isStatic ) -{ - JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); - isStatic = iterator.link_->items_[iterator.itemIndex_].isMemberNameStatic(); - return iterator.link_->keys_[iterator.itemIndex_]; -} - - -Value & -ValueInternalMap::value( const IteratorState &iterator ) -{ - JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); - return iterator.link_->items_[iterator.itemIndex_]; -} - - -int -ValueInternalMap::distance( const IteratorState &x, const IteratorState &y ) -{ - int offset = 0; - IteratorState it = x; - while ( !equals( it, y ) ) - increment( it ); - return offset; -} diff -Nru mstflint-4.17.0+1/ext_libs/json/json_reader.cpp mstflint-4.21.0+1/ext_libs/json/json_reader.cpp --- mstflint-4.17.0+1/ext_libs/json/json_reader.cpp 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/json_reader.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -1,892 +1,1992 @@ -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2007-2011 Baptiste Lepilleur and The JsonCpp Authors +// Copyright (C) 2016 InfoTeCS JSC. All rights reserved. // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE +#if !defined(JSON_IS_AMALGAMATION) +#include "json_tool.h" +#include #include #include -#include -#include +#endif // if !defined(JSON_IS_AMALGAMATION) +#include #include #include #include -#include +#include +#include +#include +#include +#include +#include -#if _MSC_VER >= 1400 // VC++ 8.0 -#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. -#endif +#include +#if __cplusplus >= 201103L -namespace Json { +#if !defined(sscanf) +#define sscanf std::sscanf +#endif -// Implementation of class Features -// //////////////////////////////// +#endif //__cplusplus -Features::Features() - : allowComments_( true ) - , strictRoot_( false ) -{ -} +#if defined(_MSC_VER) +#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES) +#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 +#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES +#endif //_MSC_VER + +#if defined(_MSC_VER) +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif +// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile +// time to change the stack limit +#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT) +#define JSONCPP_DEPRECATED_STACK_LIMIT 1000 +#endif -Features -Features::all() -{ - return Features(); -} +static size_t const stackLimit_g = + JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue() +namespace Json { -Features -Features::strictMode() -{ - Features features; - features.allowComments_ = false; - features.strictRoot_ = true; - return features; -} +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) +using CharReaderPtr = std::unique_ptr; +#else +using CharReaderPtr = std::auto_ptr; +#endif -// Implementation of class Reader +// Implementation of class Features // //////////////////////////////// +Features::Features() = default; -static inline bool -in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 ) -{ - return c == c1 || c == c2 || c == c3 || c == c4; -} - -static inline bool -in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 ) -{ - return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; -} +Features Features::all() { return {}; } +Features Features::strictMode() { + Features features; + features.allowComments_ = false; + features.strictRoot_ = true; + features.allowDroppedNullPlaceholders_ = false; + features.allowNumericKeys_ = false; + return features; +} -static bool -containsNewLine( Reader::Location begin, - Reader::Location end ) -{ - for ( ;begin < end; ++begin ) - if ( *begin == '\n' || *begin == '\r' ) - return true; - return false; -} - -static std::string codePointToUTF8(unsigned int cp) -{ - std::string result; - - // based on description from http://en.wikipedia.org/wiki/UTF-8 - - if (cp <= 0x7f) - { - result.resize(1); - result[0] = static_cast(cp); - } - else if (cp <= 0x7FF) - { - result.resize(2); - result[1] = static_cast(0x80 | (0x3f & cp)); - result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); - } - else if (cp <= 0xFFFF) - { - result.resize(3); - result[2] = static_cast(0x80 | (0x3f & cp)); - result[1] = 0x80 | static_cast((0x3f & (cp >> 6))); - result[0] = 0xE0 | static_cast((0xf & (cp >> 12))); - } - else if (cp <= 0x10FFFF) - { - result.resize(4); - result[3] = static_cast(0x80 | (0x3f & cp)); - result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); - result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); - result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); - } +// Implementation of class Reader +// //////////////////////////////// - return result; +bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) { + return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; }); } - // Class Reader // ////////////////////////////////////////////////////////////////// -Reader::Reader() - : features_( Features::all() ) -{ -} - - -Reader::Reader( const Features &features ) - : features_( features ) -{ -} - - -bool -Reader::parse( const std::string &document, - Value &root, - bool collectComments ) -{ - document_ = document; - const char *begin = document_.c_str(); - const char *end = begin + document_.length(); - return parse( begin, end, root, collectComments ); -} - - -bool -Reader::parse( std::istream& sin, - Value &root, - bool collectComments ) -{ - //std::istream_iterator begin(sin); - //std::istream_iterator end; - // Those would allow streamed input from a file, if parse() were a - // template function. - - // Since std::string is reference-counted, this at least does not - // create an extra copy. - std::string doc; - std::getline(sin, doc, (char)EOF); - return parse( doc, root, collectComments ); -} - -bool -Reader::parse( const char *beginDoc, const char *endDoc, - Value &root, - bool collectComments ) -{ - if ( !features_.allowComments_ ) - { - collectComments = false; - } - - begin_ = beginDoc; - end_ = endDoc; - collectComments_ = collectComments; - current_ = begin_; - lastValueEnd_ = 0; - lastValue_ = 0; - commentsBefore_ = ""; - errors_.clear(); - while ( !nodes_.empty() ) - nodes_.pop(); - nodes_.push( &root ); - - bool successful = readValue(); - Token token; - skipCommentTokens( token ); - if ( collectComments_ && !commentsBefore_.empty() ) - root.setComment( commentsBefore_, commentAfter ); - if ( features_.strictRoot_ ) - { - if ( !root.isArray() && !root.isObject() ) - { - // Set error location to start of doc, ideally should be first token found in doc - token.type_ = tokenError; - token.start_ = beginDoc; - token.end_ = endDoc; - addError( "A valid JSON document must be either an array or an object value.", - token ); - return false; - } - } - return successful; -} - - -bool -Reader::readValue() -{ - Token token; - skipCommentTokens( token ); - bool successful = true; - - if ( collectComments_ && !commentsBefore_.empty() ) - { - currentValue().setComment( commentsBefore_, commentBefore ); - commentsBefore_ = ""; - } +Reader::Reader() : features_(Features::all()) {} +Reader::Reader(const Features& features) : features_(features) {} - switch ( token.type_ ) - { - case tokenObjectBegin: - successful = readObject( token ); - break; - case tokenArrayBegin: - successful = readArray( token ); - break; - case tokenNumber: - successful = decodeNumber( token ); - break; - case tokenString: - successful = decodeString( token ); - break; - case tokenTrue: - currentValue() = true; - break; - case tokenFalse: - currentValue() = false; - break; - case tokenNull: - currentValue() = Value(); +bool Reader::parse(const std::string& document, Value& root, + bool collectComments) { + document_.assign(document.begin(), document.end()); + const char* begin = document_.c_str(); + const char* end = begin + document_.length(); + return parse(begin, end, root, collectComments); +} + +bool Reader::parse(std::istream& is, Value& root, bool collectComments) { + // std::istream_iterator begin(is); + // std::istream_iterator end; + // Those would allow streamed input from a file, if parse() were a + // template function. + + // Since String is reference-counted, this at least does not + // create an extra copy. + String doc(std::istreambuf_iterator(is), {}); + return parse(doc.data(), doc.data() + doc.size(), root, collectComments); +} + +bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = nullptr; + lastValue_ = nullptr; + commentsBefore_.clear(); + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found + // in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool Reader::readValue() { + // readValue() may call itself only if it calls readObject() or ReadArray(). + // These methods execute nodes_.push() just before and nodes_.pop)() just + // after calling readValue(). parse() executes one nodes_.push(), so > instead + // of >=. + if (nodes_.size() > stackLimit_g) + throwRuntimeError("Exceeded stackLimit in readValue()."); + + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_.clear(); + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenArrayBegin: + successful = readArray(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: { + Value v(true); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenFalse: { + Value v(false); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenNull: { + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(current_ - begin_ - 1); + currentValue().setOffsetLimit(current_ - begin_); + break; + } // Else, fall through... + default: + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + return successful; +} + +void Reader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool Reader::readToken(Token& token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token.type_ = tokenNumber; + readNumber(); + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return ok; +} + +void Reader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else break; - default: - return addError( "Syntax error: value, object or array expected.", token ); - } - - if ( collectComments_ ) - { - lastValueEnd_ = current_; - lastValue_ = ¤tValue(); - } - - return successful; + } } - -void -Reader::skipCommentTokens( Token &token ) -{ - if ( features_.allowComments_ ) - { - do - { - readToken( token ); - } - while ( token.type_ == tokenComment ); - } - else - { - readToken( token ); - } +bool Reader::match(const Char* pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; } +bool Reader::readComment() { + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if (c == '*') + successful = readCStyleComment(); + else if (c == '/') + successful = readCppStyleComment(); + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +String Reader::normalizeEOL(Reader::Location begin, Reader::Location end) { + String normalized; + normalized.reserve(static_cast(end - begin)); + Reader::Location current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') { + if (current != end && *current == '\n') + // convert dos EOL + ++current; + // convert Mac EOL + normalized += '\n'; + } else { + normalized += c; + } + } + return normalized; +} + +void Reader::addComment(Location begin, Location end, + CommentPlacement placement) { + assert(collectComments_); + const String& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != nullptr); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +bool Reader::readCStyleComment() { + while ((current_ + 1) < end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool Reader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } + } + return true; +} + +void Reader::readNumber() { + Location p = current_; + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : '\0'; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } +} + +bool Reader::readString() { + Char c = '\0'; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + +bool Reader::readObject(Token& token) { + Token tokenName; + String name; + Value init(objectValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(token.start_ - begin_); + while (readToken(tokenName)) { + bool initialTokenOk = true; + while (tokenName.type_ == tokenComment && initialTokenOk) + initialTokenOk = readToken(tokenName); + if (!initialTokenOk) + break; + if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + return true; + name.clear(); + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { + Value numberName; + if (!decodeNumber(tokenName, numberName)) + return recoverFromError(tokenObjectEnd); + name = numberName.asString(); + } else { + break; + } + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover("Missing ':' after object member name", colon, + tokenObjectEnd); + } + Value& value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { + return addErrorAndRecover("Missing ',' or '}' in object declaration", + comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover("Missing '}' or object member name", tokenName, + tokenObjectEnd); +} + +bool Reader::readArray(Token& token) { + Value init(arrayValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(token.start_ - begin_); + skipSpaces(); + if (current_ != end_ && *current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + for (;;) { + Value& value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token currentToken; + // Accept Comment after last item in the array. + ok = readToken(currentToken); + while (currentToken.type_ == tokenComment && ok) { + ok = readToken(currentToken); + } + bool badTokenType = (currentToken.type_ != tokenArraySeparator && + currentToken.type_ != tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover("Missing ',' or ']' in array declaration", + currentToken, tokenArrayEnd); + } + if (currentToken.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool Reader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeNumber(Token& token, Value& decoded) { + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if (isNegative) + ++current; + // TODO: Help the compiler do the div and mod at compile time or get rid of + // them. + Value::LargestUInt maxIntegerValue = + isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1 + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + auto digit(static_cast(c - '0')); + if (value >= threshold) { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, b) this is the last digit, and + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || current != token.end_ || + digit > maxIntegerValue % 10) { + return decodeDouble(token, decoded); + } + } + value = value * 10 + digit; + } + if (isNegative && value == maxIntegerValue) + decoded = Value::minLargestInt; + else if (isNegative) + decoded = -Value::LargestInt(value); + else if (value <= Value::LargestUInt(Value::maxInt)) + decoded = Value::LargestInt(value); + else + decoded = value; + return true; +} + +bool Reader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeDouble(Token& token, Value& decoded) { + double value = 0; + String buffer(token.start_, token.end_); + IStringStream is(buffer); + if (!(is >> value)) + return addError( + "'" + String(token.start_, token.end_) + "' is not a number.", token); + decoded = value; + return true; +} + +bool Reader::decodeString(Token& token) { + String decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeString(Token& token, String& decoded) { + decoded.reserve(static_cast(token.end_ - token.start_ - 2)); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +bool Reader::decodeUnicodeCodePoint(Token& token, Location& current, + Location end, unsigned int& unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, current); + if (*(current++) == '\\' && *(current++) == 'u') { + unsigned int surrogatePair; + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError("expecting another \\u token to begin the second half of " + "a unicode surrogate pair", + token, current); + } + return true; +} + +bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current, + Location end, + unsigned int& ret_unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", token, + current); + int unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, current); + } + ret_unicode = static_cast(unicode); + return true; +} + +bool Reader::addError(const String& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool Reader::recoverFromError(TokenType skipUntilToken) { + size_t const errorCount = errors_.size(); + Token skip; + for (;;) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool Reader::addErrorAndRecover(const String& message, Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value& Reader::currentValue() { return *(nodes_.top()); } + +Reader::Char Reader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void Reader::getLocationLineAndColumn(Location location, int& line, + int& column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +String Reader::getLocationLineAndColumn(Location location) const { + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; + jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); + return buffer; +} + +// Deprecated. Preserved for backward compatibility +String Reader::getFormatedErrorMessages() const { + return getFormattedErrorMessages(); +} + +String Reader::getFormattedErrorMessages() const { + String formattedMessage; + for (const auto& error : errors_) { + formattedMessage += + "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += + "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + +std::vector Reader::getStructuredErrors() const { + std::vector allErrors; + for (const auto& error : errors_) { + Reader::StructuredError structured; + structured.offset_start = error.token_.start_ - begin_; + structured.offset_limit = error.token_.end_ - begin_; + structured.message = error.message_; + allErrors.push_back(structured); + } + return allErrors; +} + +bool Reader::pushError(const Value& value, const String& message) { + ptrdiff_t const length = end_ - begin_; + if (value.getOffsetStart() > length || value.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = begin_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = nullptr; + errors_.push_back(info); + return true; +} + +bool Reader::pushError(const Value& value, const String& message, + const Value& extra) { + ptrdiff_t const length = end_ - begin_; + if (value.getOffsetStart() > length || value.getOffsetLimit() > length || + extra.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = begin_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = begin_ + extra.getOffsetStart(); + errors_.push_back(info); + return true; +} + +bool Reader::good() const { return errors_.empty(); } + +// Originally copied from the Features class (now deprecated), used internally +// for features implementation. +class OurFeatures { +public: + static OurFeatures all(); + bool allowComments_; + bool allowTrailingCommas_; + bool strictRoot_; + bool allowDroppedNullPlaceholders_; + bool allowNumericKeys_; + bool allowSingleQuotes_; + bool failIfExtra_; + bool rejectDupKeys_; + bool allowSpecialFloats_; + bool skipBom_; + size_t stackLimit_; +}; // OurFeatures -bool -Reader::expectToken( TokenType type, Token &token, const char *message ) -{ - readToken( token ); - if ( token.type_ != type ) - return addError( message, token ); - return true; -} +OurFeatures OurFeatures::all() { return {}; } +// Implementation of class Reader +// //////////////////////////////// -bool -Reader::readToken( Token &token ) -{ - skipSpaces(); - token.start_ = current_; - Char c = getNextChar(); - bool ok = true; - switch ( c ) - { - case '{': - token.type_ = tokenObjectBegin; - break; - case '}': - token.type_ = tokenObjectEnd; - break; - case '[': - token.type_ = tokenArrayBegin; - break; - case ']': - token.type_ = tokenArrayEnd; - break; - case '"': +// Originally copied from the Reader class (now deprecated), used internally +// for implementing JSON reading. +class OurReader { +public: + using Char = char; + using Location = const Char*; + struct StructuredError { + ptrdiff_t offset_start; + ptrdiff_t offset_limit; + String message; + }; + + explicit OurReader(OurFeatures const& features); + bool parse(const char* beginDoc, const char* endDoc, Value& root, + bool collectComments = true); + String getFormattedErrorMessages() const; + std::vector getStructuredErrors() const; + +private: + OurReader(OurReader const&); // no impl + void operator=(OurReader const&); // no impl + + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenNaN, + tokenPosInf, + tokenNegInf, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + String message_; + Location extra_; + }; + + using Errors = std::deque; + + bool readToken(Token& token); + void skipSpaces(); + void skipBom(bool skipBom); + bool match(const Char* pattern, int patternLength); + bool readComment(); + bool readCStyleComment(bool* containsNewLineResult); + bool readCppStyleComment(); + bool readString(); + bool readStringSingleQuote(); + bool readNumber(bool checkInf); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, String& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + bool decodeUnicodeCodePoint(Token& token, Location& current, Location end, + unsigned int& unicode); + bool decodeUnicodeEscapeSequence(Token& token, Location& current, + Location end, unsigned int& unicode); + bool addError(const String& message, Token& token, Location extra = nullptr); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const String& message, Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void getLocationLineAndColumn(Location location, int& line, + int& column) const; + String getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + static String normalizeEOL(Location begin, Location end); + static bool containsNewLine(Location begin, Location end); + + using Nodes = std::stack; + + Nodes nodes_{}; + Errors errors_{}; + String document_{}; + Location begin_ = nullptr; + Location end_ = nullptr; + Location current_ = nullptr; + Location lastValueEnd_ = nullptr; + Value* lastValue_ = nullptr; + bool lastValueHasAComment_ = false; + String commentsBefore_{}; + + OurFeatures const features_; + bool collectComments_ = false; +}; // OurReader + +// complete copy of Read impl, for OurReader + +bool OurReader::containsNewLine(OurReader::Location begin, + OurReader::Location end) { + return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; }); +} + +OurReader::OurReader(OurFeatures const& features) : features_(features) {} + +bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = nullptr; + lastValue_ = nullptr; + commentsBefore_.clear(); + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + // skip byte order mark if it exists at the beginning of the UTF-8 text. + skipBom(features_.skipBom_); + bool successful = readValue(); + nodes_.pop(); + Token token; + skipCommentTokens(token); + if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) { + addError("Extra non-whitespace after JSON value.", token); + return false; + } + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found + // in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool OurReader::readValue() { + // To preserve the old behaviour we cast size_t to int. + if (nodes_.size() > features_.stackLimit_) + throwRuntimeError("Exceeded stackLimit in readValue()."); + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_.clear(); + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenArrayBegin: + successful = readArray(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: { + Value v(true); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenFalse: { + Value v(false); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenNull: { + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenNaN: { + Value v(std::numeric_limits::quiet_NaN()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenPosInf: { + Value v(std::numeric_limits::infinity()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenNegInf: { + Value v(-std::numeric_limits::infinity()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(current_ - begin_ - 1); + currentValue().setOffsetLimit(current_ - begin_); + break; + } // else, fall through ... + default: + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValueHasAComment_ = false; + lastValue_ = ¤tValue(); + } + + return successful; +} + +void OurReader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool OurReader::readToken(Token& token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '\'': + if (features_.allowSingleQuotes_) { token.type_ = tokenString; - ok = readString(); - break; - case '/': - token.type_ = tokenComment; - ok = readComment(); - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '-': + ok = readStringSingleQuote(); + } else { + // If we don't allow single quotes, this is a failure case. + ok = false; + } + break; + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + token.type_ = tokenNumber; + readNumber(false); + break; + case '-': + if (readNumber(true)) { token.type_ = tokenNumber; - readNumber(); - break; - case 't': - token.type_ = tokenTrue; - ok = match( "rue", 3 ); - break; - case 'f': - token.type_ = tokenFalse; - ok = match( "alse", 4 ); - break; - case 'n': - token.type_ = tokenNull; - ok = match( "ull", 3 ); - break; - case ',': - token.type_ = tokenArraySeparator; - break; - case ':': - token.type_ = tokenMemberSeparator; - break; - case 0: - token.type_ = tokenEndOfStream; - break; - default: + } else { + token.type_ = tokenNegInf; + ok = features_.allowSpecialFloats_ && match("nfinity", 7); + } + break; + case '+': + if (readNumber(true)) { + token.type_ = tokenNumber; + } else { + token.type_ = tokenPosInf; + ok = features_.allowSpecialFloats_ && match("nfinity", 7); + } + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case 'N': + if (features_.allowSpecialFloats_) { + token.type_ = tokenNaN; + ok = match("aN", 2); + } else { + ok = false; + } + break; + case 'I': + if (features_.allowSpecialFloats_) { + token.type_ = tokenPosInf; + ok = match("nfinity", 7); + } else { ok = false; + } + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return ok; +} + +void OurReader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else break; - } - if ( !ok ) - token.type_ = tokenError; - token.end_ = current_; - return true; + } } - -void -Reader::skipSpaces() -{ - while ( current_ != end_ ) - { - Char c = *current_; - if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' ) - ++current_; - else - break; - } -} - - -bool -Reader::match( Location pattern, - int patternLength ) -{ - if ( end_ - current_ < patternLength ) +void OurReader::skipBom(bool skipBom) { + // The default behavior is to skip BOM. + if (skipBom) { + if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) { + begin_ += 3; + current_ = begin_; + } + } +} + +bool OurReader::match(const Char* pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) return false; - int index = patternLength; - while ( index-- ) - if ( current_[index] != pattern[index] ) - return false; - current_ += patternLength; - return true; + current_ += patternLength; + return true; } - -bool -Reader::readComment() -{ - Location commentBegin = current_ - 1; - Char c = getNextChar(); - bool successful = false; - if ( c == '*' ) - successful = readCStyleComment(); - else if ( c == '/' ) - successful = readCppStyleComment(); - if ( !successful ) - return false; - - if ( collectComments_ ) - { - CommentPlacement placement = commentBefore; - if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) ) - { - if ( c != '*' || !containsNewLine( commentBegin, current_ ) ) - placement = commentAfterOnSameLine; +bool OurReader::readComment() { + const Location commentBegin = current_ - 1; + const Char c = getNextChar(); + bool successful = false; + bool cStyleWithEmbeddedNewline = false; + + const bool isCStyleComment = (c == '*'); + const bool isCppStyleComment = (c == '/'); + if (isCStyleComment) { + successful = readCStyleComment(&cStyleWithEmbeddedNewline); + } else if (isCppStyleComment) { + successful = readCppStyleComment(); + } + + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + + if (!lastValueHasAComment_) { + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (isCppStyleComment || !cStyleWithEmbeddedNewline) { + placement = commentAfterOnSameLine; + lastValueHasAComment_ = true; + } } + } - addComment( commentBegin, current_, placement ); - } - return true; -} - - -void -Reader::addComment( Location begin, - Location end, - CommentPlacement placement ) -{ - assert( collectComments_ ); - if ( placement == commentAfterOnSameLine ) - { - assert( lastValue_ != 0 ); - lastValue_->setComment( std::string( begin, end ), placement ); - } - else - { - if ( !commentsBefore_.empty() ) - commentsBefore_ += "\n"; - commentsBefore_ += std::string( begin, end ); - } -} - - -bool -Reader::readCStyleComment() -{ - while ( current_ != end_ ) - { - Char c = getNextChar(); - if ( c == '*' && *current_ == '/' ) - break; - } - return getNextChar() == '/'; -} - - -bool -Reader::readCppStyleComment() -{ - while ( current_ != end_ ) - { - Char c = getNextChar(); - if ( c == '\r' || c == '\n' ) - break; - } - return true; -} - - -void -Reader::readNumber() -{ - while ( current_ != end_ ) - { - if ( !(*current_ >= '0' && *current_ <= '9') && - !in( *current_, '.', 'e', 'E', '+', '-' ) ) - break; - ++current_; - } -} - -bool -Reader::readString() -{ - Char c = 0; - while ( current_ != end_ ) - { - c = getNextChar(); - if ( c == '\\' ) - getNextChar(); - else if ( c == '"' ) - break; - } - return c == '"'; -} - - -bool -Reader::readObject( Token &tokenStart ) -{ - Token tokenName; - std::string name; - currentValue() = Value( objectValue ); - (void) tokenStart; - while ( readToken( tokenName ) ) - { - bool initialTokenOk = true; - while ( tokenName.type_ == tokenComment && initialTokenOk ) - initialTokenOk = readToken( tokenName ); - if ( !initialTokenOk ) - break; - if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object - return true; - if ( tokenName.type_ != tokenString ) - break; - - name = ""; - if ( !decodeString( tokenName, name ) ) - return recoverFromError( tokenObjectEnd ); - - Token colon; - if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator ) - { - return addErrorAndRecover( "Missing ':' after object member name", - colon, - tokenObjectEnd ); - } - Value &value = currentValue()[ name ]; - nodes_.push( &value ); - bool ok = readValue(); - nodes_.pop(); - if ( !ok ) // error already set - return recoverFromError( tokenObjectEnd ); - - Token comma; - if ( !readToken( comma ) - || ( comma.type_ != tokenObjectEnd && - comma.type_ != tokenArraySeparator && - comma.type_ != tokenComment ) ) - { - return addErrorAndRecover( "Missing ',' or '}' in object declaration", - comma, - tokenObjectEnd ); - } - bool finalizeTokenOk = true; - while ( comma.type_ == tokenComment && - finalizeTokenOk ) - finalizeTokenOk = readToken( comma ); - if ( comma.type_ == tokenObjectEnd ) - return true; - } - return addErrorAndRecover( "Missing '}' or object member name", - tokenName, - tokenObjectEnd ); -} - - -bool -Reader::readArray( Token &tokenStart ) -{ - currentValue() = Value( arrayValue ); - skipSpaces(); - (void) tokenStart; - if ( *current_ == ']' ) // empty array - { + addComment(commentBegin, current_, placement); + } + return true; +} + +String OurReader::normalizeEOL(OurReader::Location begin, + OurReader::Location end) { + String normalized; + normalized.reserve(static_cast(end - begin)); + OurReader::Location current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') { + if (current != end && *current == '\n') + // convert dos EOL + ++current; + // convert Mac EOL + normalized += '\n'; + } else { + normalized += c; + } + } + return normalized; +} + +void OurReader::addComment(Location begin, Location end, + CommentPlacement placement) { + assert(collectComments_); + const String& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != nullptr); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +bool OurReader::readCStyleComment(bool* containsNewLineResult) { + *containsNewLineResult = false; + + while ((current_ + 1) < end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + if (c == '\n') + *containsNewLineResult = true; + } + + return getNextChar() == '/'; +} + +bool OurReader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } + } + return true; +} + +bool OurReader::readNumber(bool checkInf) { + Location p = current_; + if (checkInf && p != end_ && *p == 'I') { + current_ = ++p; + return false; + } + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : '\0'; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + return true; +} +bool OurReader::readString() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + +bool OurReader::readStringSingleQuote() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '\'') + break; + } + return c == '\''; +} + +bool OurReader::readObject(Token& token) { + Token tokenName; + String name; + Value init(objectValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(token.start_ - begin_); + while (readToken(tokenName)) { + bool initialTokenOk = true; + while (tokenName.type_ == tokenComment && initialTokenOk) + initialTokenOk = readToken(tokenName); + if (!initialTokenOk) + break; + if (tokenName.type_ == tokenObjectEnd && + (name.empty() || + features_.allowTrailingCommas_)) // empty object or trailing comma + return true; + name.clear(); + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { + Value numberName; + if (!decodeNumber(tokenName, numberName)) + return recoverFromError(tokenObjectEnd); + name = numberName.asString(); + } else { + break; + } + if (name.length() >= (1U << 30)) + throwRuntimeError("keylength >= 2^30"); + if (features_.rejectDupKeys_ && currentValue().isMember(name)) { + String msg = "Duplicate key: '" + name + "'"; + return addErrorAndRecover(msg, tokenName, tokenObjectEnd); + } + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover("Missing ':' after object member name", colon, + tokenObjectEnd); + } + Value& value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { + return addErrorAndRecover("Missing ',' or '}' in object declaration", + comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover("Missing '}' or object member name", tokenName, + tokenObjectEnd); +} + +bool OurReader::readArray(Token& token) { + Value init(arrayValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(token.start_ - begin_); + int index = 0; + for (;;) { + skipSpaces(); + if (current_ != end_ && *current_ == ']' && + (index == 0 || + (features_.allowTrailingCommas_ && + !features_.allowDroppedNullPlaceholders_))) // empty array or trailing + // comma + { Token endArray; - readToken( endArray ); + readToken(endArray); return true; - } - int index = 0; - while ( true ) - { - Value &value = currentValue()[ index++ ]; - nodes_.push( &value ); - bool ok = readValue(); - nodes_.pop(); - if ( !ok ) // error already set - return recoverFromError( tokenArrayEnd ); - - Token token; - // Accept Comment after last item in the array. - ok = readToken( token ); - while ( token.type_ == tokenComment && ok ) - { - ok = readToken( token ); - } - bool badTokenType = ( token.type_ == tokenArraySeparator && - token.type_ == tokenArrayEnd ); - if ( !ok || badTokenType ) - { - return addErrorAndRecover( "Missing ',' or ']' in array declaration", - token, - tokenArrayEnd ); - } - if ( token.type_ == tokenArrayEnd ) - break; - } - return true; -} - - -bool -Reader::decodeNumber( Token &token ) -{ - bool isDouble = false; - for ( Location inspect = token.start_; inspect != token.end_; ++inspect ) - { - isDouble = isDouble - || in( *inspect, '.', 'e', 'E', '+' ) - || ( *inspect == '-' && inspect != token.start_ ); - } - if ( isDouble ) - return decodeDouble( token ); - Location current = token.start_; - bool isNegative = *current == '-'; - if ( isNegative ) - ++current; - Value::UInt threshold = (isNegative ? Value::UInt(-Value::minInt) - : Value::maxUInt) / 10; - Value::UInt value = 0; - while ( current < token.end_ ) - { - Char c = *current++; - if ( c < '0' || c > '9' ) - return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); - if ( value >= threshold ) - return decodeDouble( token ); - value = value * 10 + Value::UInt(c - '0'); - } - if ( isNegative ) - currentValue() = -Value::Int( value ); - else if ( value <= Value::UInt(Value::maxInt) ) - currentValue() = Value::Int( value ); - else - currentValue() = value; - return true; -} - - -bool -Reader::decodeDouble( Token &token ) -{ - double value = 0; - const int bufferSize = 32; - int count; - int length = int(token.end_ - token.start_); - if ( length <= bufferSize ) - { - Char buffer[bufferSize]; - memcpy( buffer, token.start_, length ); - buffer[length] = 0; - count = sscanf( buffer, "%lf", &value ); - } - else - { - std::string buffer( token.start_, token.end_ ); - count = sscanf( buffer.c_str(), "%lf", &value ); - } - - if ( count != 1 ) - return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); - currentValue() = value; - return true; -} - - -bool -Reader::decodeString( Token &token ) -{ - std::string decoded; - if ( !decodeString( token, decoded ) ) - return false; - currentValue() = decoded; - return true; -} - - -bool -Reader::decodeString( Token &token, std::string &decoded ) -{ - decoded.reserve( token.end_ - token.start_ - 2 ); - Location current = token.start_ + 1; // skip '"' - Location end = token.end_ - 1; // do not include '"' - while ( current != end ) - { - Char c = *current++; - if ( c == '"' ) - break; - else if ( c == '\\' ) - { - if ( current == end ) - return addError( "Empty escape sequence in string", token, current ); - Char escape = *current++; - switch ( escape ) - { - case '"': decoded += '"'; break; - case '/': decoded += '/'; break; - case '\\': decoded += '\\'; break; - case 'b': decoded += '\b'; break; - case 'f': decoded += '\f'; break; - case 'n': decoded += '\n'; break; - case 'r': decoded += '\r'; break; - case 't': decoded += '\t'; break; - case 'u': - { - unsigned int unicode; - if ( !decodeUnicodeCodePoint( token, current, end, unicode ) ) - return false; - decoded += codePointToUTF8(unicode); - } - break; - default: - return addError( "Bad escape sequence in string", token, current ); - } + } + Value& value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token currentToken; + // Accept Comment after last item in the array. + ok = readToken(currentToken); + while (currentToken.type_ == tokenComment && ok) { + ok = readToken(currentToken); + } + bool badTokenType = (currentToken.type_ != tokenArraySeparator && + currentToken.type_ != tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover("Missing ',' or ']' in array declaration", + currentToken, tokenArrayEnd); + } + if (currentToken.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool OurReader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeNumber(Token& token, Value& decoded) { + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + const bool isNegative = *current == '-'; + if (isNegative) { + ++current; + } + + // We assume we can represent the largest and smallest integer types as + // unsigned integers with separate sign. This is only true if they can fit + // into an unsigned integer. + static_assert(Value::maxLargestInt <= Value::maxLargestUInt, + "Int must be smaller than UInt"); + + // We need to convert minLargestInt into a positive number. The easiest way + // to do this conversion is to assume our "threshold" value of minLargestInt + // divided by 10 can fit in maxLargestInt when absolute valued. This should + // be a safe assumption. + static_assert(Value::minLargestInt <= -Value::maxLargestInt, + "The absolute value of minLargestInt must be greater than or " + "equal to maxLargestInt"); + static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt, + "The absolute value of minLargestInt must be only 1 magnitude " + "larger than maxLargest Int"); + + static constexpr Value::LargestUInt positive_threshold = + Value::maxLargestUInt / 10; + static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10; + + // For the negative values, we have to be more careful. Since typically + // -Value::minLargestInt will cause an overflow, we first divide by 10 and + // then take the inverse. This assumes that minLargestInt is only a single + // power of 10 different in magnitude, which we check above. For the last + // digit, we take the modulus before negating for the same reason. + static constexpr auto negative_threshold = + Value::LargestUInt(-(Value::minLargestInt / 10)); + static constexpr auto negative_last_digit = + Value::UInt(-(Value::minLargestInt % 10)); + + const Value::LargestUInt threshold = + isNegative ? negative_threshold : positive_threshold; + const Value::UInt max_last_digit = + isNegative ? negative_last_digit : positive_last_digit; + + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + + const auto digit(static_cast(c - '0')); + if (value >= threshold) { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, meaing value == threshold, + // b) this is the last digit, or + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || current != token.end_ || + digit > max_last_digit) { + return decodeDouble(token, decoded); } - else - { - decoded += c; + } + value = value * 10 + digit; + } + + if (isNegative) { + // We use the same magnitude assumption here, just in case. + const auto last_digit = static_cast(value % 10); + decoded = -Value::LargestInt(value / 10) * 10 - last_digit; + } else if (value <= Value::LargestUInt(Value::maxLargestInt)) { + decoded = Value::LargestInt(value); + } else { + decoded = value; + } + + return true; +} + +bool OurReader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeDouble(Token& token, Value& decoded) { + double value = 0; + const String buffer(token.start_, token.end_); + IStringStream is(buffer); + if (!(is >> value)) { + return addError( + "'" + String(token.start_, token.end_) + "' is not a number.", token); + } + decoded = value; + return true; +} + +bool OurReader::decodeString(Token& token) { + String decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeString(Token& token, String& decoded) { + decoded.reserve(static_cast(token.end_ - token.start_ - 2)); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); } - } - return true; -} - -bool -Reader::decodeUnicodeCodePoint( Token &token, - Location ¤t, - Location end, - unsigned int &unicode ) -{ - - if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) ) - return false; - if (unicode >= 0xD800 && unicode <= 0xDBFF) - { - // surrogate pairs - if (end - current < 6) - return addError( "additional six characters expected to parse unicode surrogate pair.", token, current ); + } else { + decoded += c; + } + } + return true; +} + +bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current, + Location end, unsigned int& unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, current); + if (*(current++) == '\\' && *(current++) == 'u') { unsigned int surrogatePair; - if (*(current++) == '\\' && *(current++)== 'u') - { - if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair )) - { - unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); - } - else - return false; - } - else - return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current ); - } - return true; -} - -bool -Reader::decodeUnicodeEscapeSequence( Token &token, - Location ¤t, - Location end, - unsigned int &unicode ) -{ - if ( end - current < 4 ) - return addError( "Bad unicode escape sequence in string: four digits expected.", token, current ); - unicode = 0; - for ( int index =0; index < 4; ++index ) - { - Char c = *current++; - unicode *= 16; - if ( c >= '0' && c <= '9' ) - unicode += c - '0'; - else if ( c >= 'a' && c <= 'f' ) - unicode += c - 'a' + 10; - else if ( c >= 'A' && c <= 'F' ) - unicode += c - 'A' + 10; - else - return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current ); - } - return true; -} - - -bool -Reader::addError( const std::string &message, - Token &token, - Location extra ) -{ - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = extra; - errors_.push_back( info ); - return false; -} - - -bool -Reader::recoverFromError( TokenType skipUntilToken ) -{ - int errorCount = int(errors_.size()); - Token skip; - while ( true ) - { - if ( !readToken(skip) ) - errors_.resize( errorCount ); // discard errors caused by recovery - if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream ) - break; - } - errors_.resize( errorCount ); - return false; -} - - -bool -Reader::addErrorAndRecover( const std::string &message, - Token &token, - TokenType skipUntilToken ) -{ - addError( message, token ); - return recoverFromError( skipUntilToken ); -} - - -Value & -Reader::currentValue() -{ - return *(nodes_.top()); -} - - -Reader::Char -Reader::getNextChar() -{ - if ( current_ == end_ ) - return 0; - return *current_++; -} - - -void -Reader::getLocationLineAndColumn( Location location, - int &line, - int &column ) const -{ - Location current = begin_; - Location lastLineStart = current; - line = 0; - while ( current < location && current != end_ ) - { - Char c = *current++; - if ( c == '\r' ) - { - if ( *current == '\n' ) - ++current; - lastLineStart = current; - ++line; - } - else if ( c == '\n' ) - { - lastLineStart = current; - ++line; - } - } - // column & line start at 1 - column = int(location - lastLineStart) + 1; - ++line; -} - - -std::string -Reader::getLocationLineAndColumn( Location location ) const -{ - int line, column; - getLocationLineAndColumn( location, line, column ); - char buffer[18+16+16+1]; - sprintf( buffer, "Line %d, Column %d", line, column ); - return buffer; + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError("expecting another \\u token to begin the second half of " + "a unicode surrogate pair", + token, current); + } + return true; +} + +bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current, + Location end, + unsigned int& ret_unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", token, + current); + int unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, current); + } + ret_unicode = static_cast(unicode); + return true; +} + +bool OurReader::addError(const String& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool OurReader::recoverFromError(TokenType skipUntilToken) { + size_t errorCount = errors_.size(); + Token skip; + for (;;) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool OurReader::addErrorAndRecover(const String& message, Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value& OurReader::currentValue() { return *(nodes_.top()); } + +OurReader::Char OurReader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void OurReader::getLocationLineAndColumn(Location location, int& line, + int& column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +String OurReader::getLocationLineAndColumn(Location location) const { + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; + jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); + return buffer; +} + +String OurReader::getFormattedErrorMessages() const { + String formattedMessage; + for (const auto& error : errors_) { + formattedMessage += + "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += + "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + +std::vector OurReader::getStructuredErrors() const { + std::vector allErrors; + for (const auto& error : errors_) { + OurReader::StructuredError structured; + structured.offset_start = error.token_.start_ - begin_; + structured.offset_limit = error.token_.end_ - begin_; + structured.message = error.message_; + allErrors.push_back(structured); + } + return allErrors; +} + +class OurCharReader : public CharReader { + bool const collectComments_; + OurReader reader_; + +public: + OurCharReader(bool collectComments, OurFeatures const& features) + : collectComments_(collectComments), reader_(features) {} + bool parse(char const* beginDoc, char const* endDoc, Value* root, + String* errs) override { + bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); + if (errs) { + *errs = reader_.getFormattedErrorMessages(); + } + return ok; + } +}; + +CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); } +CharReaderBuilder::~CharReaderBuilder() = default; +CharReader* CharReaderBuilder::newCharReader() const { + bool collectComments = settings_["collectComments"].asBool(); + OurFeatures features = OurFeatures::all(); + features.allowComments_ = settings_["allowComments"].asBool(); + features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool(); + features.strictRoot_ = settings_["strictRoot"].asBool(); + features.allowDroppedNullPlaceholders_ = + settings_["allowDroppedNullPlaceholders"].asBool(); + features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); + features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool(); + + // Stack limit is always a size_t, so we get this as an unsigned int + // regardless of it we have 64-bit integer support enabled. + features.stackLimit_ = static_cast(settings_["stackLimit"].asUInt()); + features.failIfExtra_ = settings_["failIfExtra"].asBool(); + features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool(); + features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool(); + features.skipBom_ = settings_["skipBom"].asBool(); + return new OurCharReader(collectComments, features); +} + +bool CharReaderBuilder::validate(Json::Value* invalid) const { + static const auto& valid_keys = *new std::set{ + "collectComments", + "allowComments", + "allowTrailingCommas", + "strictRoot", + "allowDroppedNullPlaceholders", + "allowNumericKeys", + "allowSingleQuotes", + "stackLimit", + "failIfExtra", + "rejectDupKeys", + "allowSpecialFloats", + "skipBom", + }; + for (auto si = settings_.begin(); si != settings_.end(); ++si) { + auto key = si.name(); + if (valid_keys.count(key)) + continue; + if (invalid) + (*invalid)[key] = *si; + else + return false; + } + return invalid ? invalid->empty() : true; } - -std::string -Reader::getFormatedErrorMessages() const -{ - std::string formattedMessage; - for ( Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError ) - { - const ErrorInfo &error = *itError; - formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n"; - formattedMessage += " " + error.message_ + "\n"; - if ( error.extra_ ) - formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n"; - } - return formattedMessage; +Value& CharReaderBuilder::operator[](const String& key) { + return settings_[key]; } - - -std::istream& operator>>( std::istream &sin, Value &root ) -{ - Json::Reader reader; - bool ok = reader.parse(sin, root, true); - //JSON_ASSERT( ok ); - if (!ok) throw std::runtime_error(reader.getFormatedErrorMessages()); - return sin; +// static +void CharReaderBuilder::strictMode(Json::Value* settings) { + //! [CharReaderBuilderStrictMode] + (*settings)["allowComments"] = false; + (*settings)["allowTrailingCommas"] = false; + (*settings)["strictRoot"] = true; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = true; + (*settings)["rejectDupKeys"] = true; + (*settings)["allowSpecialFloats"] = false; + (*settings)["skipBom"] = true; + //! [CharReaderBuilderStrictMode] +} +// static +void CharReaderBuilder::setDefaults(Json::Value* settings) { + //! [CharReaderBuilderDefaults] + (*settings)["collectComments"] = true; + (*settings)["allowComments"] = true; + (*settings)["allowTrailingCommas"] = true; + (*settings)["strictRoot"] = false; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = false; + (*settings)["rejectDupKeys"] = false; + (*settings)["allowSpecialFloats"] = false; + (*settings)["skipBom"] = true; + //! [CharReaderBuilderDefaults] +} + +////////////////////////////////// +// global functions + +bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root, + String* errs) { + OStringStream ssin; + ssin << sin.rdbuf(); + String doc = ssin.str(); + char const* begin = doc.data(); + char const* end = begin + doc.size(); + // Note that we do not actually need a null-terminator. + CharReaderPtr const reader(fact.newCharReader()); + return reader->parse(begin, end, root, errs); +} + +IStream& operator>>(IStream& sin, Value& root) { + CharReaderBuilder b; + String errs; + bool ok = parseFromStream(b, sin, &root, &errs); + if (!ok) { + throwRuntimeError(errs); + } + return sin; } - } // namespace Json diff -Nru mstflint-4.17.0+1/ext_libs/json/json_tool.h mstflint-4.21.0+1/ext_libs/json/json_tool.h --- mstflint-4.17.0+1/ext_libs/json/json_tool.h 1970-01-01 00:00:00.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/json_tool.h 2022-07-31 09:33:57.000000000 +0000 @@ -0,0 +1,138 @@ +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED +#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include +#endif + +// Also support old flag NO_LOCALE_SUPPORT +#ifdef NO_LOCALE_SUPPORT +#define JSONCPP_NO_LOCALE_SUPPORT +#endif + +#ifndef JSONCPP_NO_LOCALE_SUPPORT +#include +#endif + +/* This header provides common string manipulation support, such as UTF-8, + * portable conversion from/to string... + * + * It is an internal header that must not be exposed. + */ + +namespace Json { +static inline char getDecimalPoint() { +#ifdef JSONCPP_NO_LOCALE_SUPPORT + return '\0'; +#else + struct lconv* lc = localeconv(); + return lc ? *(lc->decimal_point) : '\0'; +#endif +} + +/// Converts a unicode code-point to UTF-8. +static inline String codePointToUTF8(unsigned int cp) { + String result; + + // based on description from http://en.wikipedia.org/wiki/UTF-8 + + if (cp <= 0x7f) { + result.resize(1); + result[0] = static_cast(cp); + } else if (cp <= 0x7FF) { + result.resize(2); + result[1] = static_cast(0x80 | (0x3f & cp)); + result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); + } else if (cp <= 0xFFFF) { + result.resize(3); + result[2] = static_cast(0x80 | (0x3f & cp)); + result[1] = static_cast(0x80 | (0x3f & (cp >> 6))); + result[0] = static_cast(0xE0 | (0xf & (cp >> 12))); + } else if (cp <= 0x10FFFF) { + result.resize(4); + result[3] = static_cast(0x80 | (0x3f & cp)); + result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); + result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); + result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); + } + + return result; +} + +enum { + /// Constant that specify the size of the buffer that must be passed to + /// uintToString. + uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1 +}; + +// Defines a char buffer for use with uintToString(). +using UIntToStringBuffer = char[uintToStringBufferSize]; + +/** Converts an unsigned integer to string. + * @param value Unsigned integer to convert to string + * @param current Input/Output string buffer. + * Must have at least uintToStringBufferSize chars free. + */ +static inline void uintToString(LargestUInt value, char*& current) { + *--current = 0; + do { + *--current = static_cast(value % 10U + static_cast('0')); + value /= 10; + } while (value != 0); +} + +/** Change ',' to '.' everywhere in buffer. + * + * We had a sophisticated way, but it did not work in WinCE. + * @see https://github.com/open-source-parsers/jsoncpp/pull/9 + */ +template Iter fixNumericLocale(Iter begin, Iter end) { + for (; begin != end; ++begin) { + if (*begin == ',') { + *begin = '.'; + } + } + return begin; +} + +template void fixNumericLocaleInput(Iter begin, Iter end) { + char decimalPoint = getDecimalPoint(); + if (decimalPoint == '\0' || decimalPoint == '.') { + return; + } + for (; begin != end; ++begin) { + if (*begin == '.') { + *begin = decimalPoint; + } + } +} + +/** + * Return iterator that would be the new end of the range [begin,end), if we + * were to delete zeros in the end of string, but not the last zero before '.'. + */ +template +Iter fixZerosInTheEnd(Iter begin, Iter end, unsigned int precision) { + for (; begin != end; --end) { + if (*(end - 1) != '0') { + return end; + } + // Don't delete the last zero before the decimal point. + if (begin != (end - 1) && begin != (end - 2) && *(end - 2) == '.') { + if (precision) { + return end; + } + return end - 2; + } + } + return end; +} + +} // namespace Json + +#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED diff -Nru mstflint-4.17.0+1/ext_libs/json/json_value.cpp mstflint-4.21.0+1/ext_libs/json/json_value.cpp --- mstflint-4.17.0+1/ext_libs/json/json_value.cpp 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/json_value.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -1,168 +1,226 @@ -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE -#include +#if !defined(JSON_IS_AMALGAMATION) +#include #include #include -#include -#include -#include +#endif // if !defined(JSON_IS_AMALGAMATION) +#include #include -#ifdef JSON_USE_CPPTL -# include +#include +#include +#include +#include +#include +#include + +// Provide implementation equivalent of std::snprintf for older _MSC compilers +#if defined(_MSC_VER) && _MSC_VER < 1900 +#include +static int msvc_pre1900_c99_vsnprintf(char* outBuf, size_t size, + const char* format, va_list ap) { + int count = -1; + if (size != 0) + count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); + if (count == -1) + count = _vscprintf(format, ap); + return count; +} + +int JSON_API msvc_pre1900_c99_snprintf(char* outBuf, size_t size, + const char* format, ...) { + va_list ap; + va_start(ap, format); + const int count = msvc_pre1900_c99_vsnprintf(outBuf, size, format, ap); + va_end(ap); + return count; +} +#endif + +// Disable warning C4702 : unreachable code +#if defined(_MSC_VER) +#pragma warning(disable : 4702) #endif -#include // size_t -#ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR -# include "json_batchallocator.h" -#endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR - -#define JSON_ASSERT_UNREACHABLE assert( false ) -#define JSON_ASSERT( condition ) assert( condition ); // @todo <= change this into an exception throw -#define JSON_ASSERT_MESSAGE( condition, message ) if (!( condition )) throw std::runtime_error( message ); + +#define JSON_ASSERT_UNREACHABLE assert(false) namespace Json { +template +static std::unique_ptr cloneUnique(const std::unique_ptr& p) { + std::unique_ptr r; + if (p) { + r = std::unique_ptr(new T(*p)); + } + return r; +} -const Value Value::null; -const Int Value::minInt = Int( ~(UInt(-1)/2) ); -const Int Value::maxInt = Int( UInt(-1)/2 ); -const UInt Value::maxUInt = UInt(-1); - -// A "safe" implementation of strdup. Allow null pointer to be passed. -// Also avoid warning on msvc80. -// -//inline char *safeStringDup( const char *czstring ) -//{ -// if ( czstring ) -// { -// const size_t length = (unsigned int)( strlen(czstring) + 1 ); -// char *newString = static_cast( malloc( length ) ); -// memcpy( newString, czstring, length ); -// return newString; -// } -// return 0; -//} -// -//inline char *safeStringDup( const std::string &str ) -//{ -// if ( !str.empty() ) -// { -// const size_t length = str.length(); -// char *newString = static_cast( malloc( length + 1 ) ); -// memcpy( newString, str.c_str(), length ); -// newString[length] = 0; -// return newString; -// } -// return 0; -//} - -ValueAllocator::~ValueAllocator() -{ -} - -class DefaultValueAllocator : public ValueAllocator -{ -public: - virtual ~DefaultValueAllocator() - { - } - - virtual char *makeMemberName( const char *memberName ) - { - return duplicateStringValue( memberName ); - } - - virtual void releaseMemberName( char *memberName ) - { - releaseStringValue( memberName ); - } - - virtual char *duplicateStringValue( const char *value, - unsigned int length = unknown ) - { - //@todo invesgate this old optimization - //if ( !value || value[0] == 0 ) - // return 0; - - if ( length == unknown ) - length = (unsigned int)strlen(value); - char *newString = static_cast( malloc( length + 1 ) ); - memcpy( newString, value, length ); - newString[length] = 0; - return newString; - } - - virtual void releaseStringValue( char *value ) - { - if ( value ) - free( value ); - } -}; - -static ValueAllocator *&valueAllocator() -{ - static DefaultValueAllocator defaultAllocator; - static ValueAllocator *valueAllocator = &defaultAllocator; - return valueAllocator; -} - -static struct DummyValueAllocatorInitializer { - DummyValueAllocatorInitializer() - { - valueAllocator(); // ensure valueAllocator() statics are initialized before main(). - } -} dummyValueAllocatorInitializer; +// This is a walkaround to avoid the static initialization of Value::null. +// kNull must be word-aligned to avoid crashing on ARM. We use an alignment of +// 8 (instead of 4) as a bit of future-proofing. +#if defined(__ARMEL__) +#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) +#else +#define ALIGNAS(byte_alignment) +#endif +// static +Value const& Value::nullSingleton() { + static Value const nullStatic; + return nullStatic; +} +#if JSON_USE_NULLREF +// for backwards compatibility, we'll leave these global references around, but +// DO NOT use them in JSONCPP library code any more! +// static +Value const& Value::null = Value::nullSingleton(); -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ValueInternals... -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -#ifdef JSON_VALUE_USE_INTERNAL_MAP -# include "json_internalarray.inl" -# include "json_internalmap.inl" -#endif // JSON_VALUE_USE_INTERNAL_MAP +// static +Value const& Value::nullRef = Value::nullSingleton(); +#endif -# include "json_valueiterator.inl" +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +template +static inline bool InRange(double d, T min, U max) { + // The casts can lose precision, but we are looking only for + // an approximate range. Might fail on edge cases though. ~cdunn + return d >= static_cast(min) && d <= static_cast(max); +} +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +static inline double integerToDouble(Json::UInt64 value) { + return static_cast(Int64(value / 2)) * 2.0 + + static_cast(Int64(value & 1)); +} + +template static inline double integerToDouble(T value) { + return static_cast(value); +} +template +static inline bool InRange(double d, T min, U max) { + return d >= integerToDouble(min) && d <= integerToDouble(max); +} +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + +/** Duplicates the specified string value. + * @param value Pointer to the string to duplicate. Must be zero-terminated if + * length is "unknown". + * @param length Length of the value. if equals to unknown, then it will be + * computed using strlen(value). + * @return Pointer on the duplicate instance of string. + */ +static inline char* duplicateStringValue(const char* value, size_t length) { + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + if (length >= static_cast(Value::maxInt)) + length = Value::maxInt - 1; + + auto newString = static_cast(malloc(length + 1)); + if (newString == nullptr) { + throwRuntimeError("in Json::Value::duplicateStringValue(): " + "Failed to allocate string value buffer"); + } + memcpy(newString, value, length); + newString[length] = 0; + return newString; +} + +/* Record the length as a prefix. + */ +static inline char* duplicateAndPrefixStringValue(const char* value, + unsigned int length) { + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + JSON_ASSERT_MESSAGE(length <= static_cast(Value::maxInt) - + sizeof(unsigned) - 1U, + "in Json::Value::duplicateAndPrefixStringValue(): " + "length too big for prefixing"); + size_t actualLength = sizeof(length) + length + 1; + auto newString = static_cast(malloc(actualLength)); + if (newString == nullptr) { + throwRuntimeError("in Json::Value::duplicateAndPrefixStringValue(): " + "Failed to allocate string value buffer"); + } + *reinterpret_cast(newString) = length; + memcpy(newString + sizeof(unsigned), value, length); + newString[actualLength - 1U] = + 0; // to avoid buffer over-run accidents by users later + return newString; +} +inline static void decodePrefixedString(bool isPrefixed, char const* prefixed, + unsigned* length, char const** value) { + if (!isPrefixed) { + *length = static_cast(strlen(prefixed)); + *value = prefixed; + } else { + *length = *reinterpret_cast(prefixed); + *value = prefixed + sizeof(unsigned); + } +} +/** Free the string duplicated by + * duplicateStringValue()/duplicateAndPrefixStringValue(). + */ +#if JSONCPP_USING_SECURE_MEMORY +static inline void releasePrefixedStringValue(char* value) { + unsigned length = 0; + char const* valueDecoded; + decodePrefixedString(true, value, &length, &valueDecoded); + size_t const size = sizeof(unsigned) + length + 1U; + memset(value, 0, size); + free(value); +} +static inline void releaseStringValue(char* value, unsigned length) { + // length==0 => we allocated the strings memory + size_t size = (length == 0) ? strlen(value) : length; + memset(value, 0, size); + free(value); +} +#else // !JSONCPP_USING_SECURE_MEMORY +static inline void releasePrefixedStringValue(char* value) { free(value); } +static inline void releaseStringValue(char* value, unsigned) { free(value); } +#endif // JSONCPP_USING_SECURE_MEMORY + +} // namespace Json // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -// class Value::CommentInfo +// ValueInternals... // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// +#if !defined(JSON_IS_AMALGAMATION) +#include "json_valueiterator.inl" +#endif // if !defined(JSON_IS_AMALGAMATION) -Value::CommentInfo::CommentInfo() - : comment_( 0 ) -{ -} - -Value::CommentInfo::~CommentInfo() -{ - if ( comment_ ) - valueAllocator()->releaseStringValue( comment_ ); -} - +namespace Json { -void -Value::CommentInfo::setComment( const char *text ) -{ - if ( comment_ ) - valueAllocator()->releaseStringValue( comment_ ); - JSON_ASSERT( text ); - JSON_ASSERT_MESSAGE( text[0]=='\0' || text[0]=='/', "Comments must start with /"); - // It seems that /**/ style comments are acceptable as well. - comment_ = valueAllocator()->duplicateStringValue( text ); +#if JSON_USE_EXCEPTION +Exception::Exception(String msg) : msg_(std::move(msg)) {} +Exception::~Exception() noexcept = default; +char const* Exception::what() const noexcept { return msg_.c_str(); } +RuntimeError::RuntimeError(String const& msg) : Exception(msg) {} +LogicError::LogicError(String const& msg) : Exception(msg) {} +JSONCPP_NORETURN void throwRuntimeError(String const& msg) { + throw RuntimeError(msg); +} +JSONCPP_NORETURN void throwLogicError(String const& msg) { + throw LogicError(msg); +} +#else // !JSON_USE_EXCEPTION +JSONCPP_NORETURN void throwRuntimeError(String const& msg) { + std::cerr << msg << std::endl; + abort(); +} +JSONCPP_NORETURN void throwLogicError(String const& msg) { + std::cerr << msg << std::endl; + abort(); } - +#endif // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// @@ -171,93 +229,109 @@ // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -# ifndef JSON_VALUE_USE_INTERNAL_MAP -// Notes: index_ indicates if the string was allocated when +// Notes: policy_ indicates if the string was allocated when // a string is stored. -Value::CZString::CZString( int index ) - : cstr_( 0 ) - , index_( index ) -{ -} - -Value::CZString::CZString( const char *cstr, DuplicationPolicy allocate ) - : cstr_( allocate == duplicate ? valueAllocator()->makeMemberName(cstr) - : cstr ) - , index_( allocate ) -{ -} - -Value::CZString::CZString( const CZString &other ) -: cstr_( other.index_ != noDuplication && other.cstr_ != 0 - ? valueAllocator()->makeMemberName( other.cstr_ ) - : other.cstr_ ) - , index_( other.cstr_ ? (other.index_ == noDuplication ? noDuplication : duplicate) - : other.index_ ) -{ -} - -Value::CZString::~CZString() -{ - if ( cstr_ && index_ == duplicate ) - valueAllocator()->releaseMemberName( const_cast( cstr_ ) ); -} - -void -Value::CZString::swap( CZString &other ) -{ - std::swap( cstr_, other.cstr_ ); - std::swap( index_, other.index_ ); -} - -Value::CZString & -Value::CZString::operator =( const CZString &other ) -{ - CZString temp( other ); - swap( temp ); - return *this; -} +Value::CZString::CZString(ArrayIndex index) : cstr_(nullptr), index_(index) {} -bool -Value::CZString::operator<( const CZString &other ) const -{ - if ( cstr_ ) - return strcmp( cstr_, other.cstr_ ) < 0; - return index_ < other.index_; +Value::CZString::CZString(char const* str, unsigned length, + DuplicationPolicy allocate) + : cstr_(str) { + // allocate != duplicate + storage_.policy_ = allocate & 0x3; + storage_.length_ = length & 0x3FFFFFFF; +} + +Value::CZString::CZString(const CZString& other) { + cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != nullptr + ? duplicateStringValue(other.cstr_, other.storage_.length_) + : other.cstr_); + storage_.policy_ = + static_cast( + other.cstr_ + ? (static_cast(other.storage_.policy_) == + noDuplication + ? noDuplication + : duplicate) + : static_cast(other.storage_.policy_)) & + 3U; + storage_.length_ = other.storage_.length_; +} + +Value::CZString::CZString(CZString&& other) noexcept + : cstr_(other.cstr_), index_(other.index_) { + other.cstr_ = nullptr; +} + +Value::CZString::~CZString() { + if (cstr_ && storage_.policy_ == duplicate) { + releaseStringValue(const_cast(cstr_), + storage_.length_ + 1U); // +1 for null terminating + // character for sake of + // completeness but not actually + // necessary + } +} + +void Value::CZString::swap(CZString& other) { + std::swap(cstr_, other.cstr_); + std::swap(index_, other.index_); +} + +Value::CZString& Value::CZString::operator=(const CZString& other) { + cstr_ = other.cstr_; + index_ = other.index_; + return *this; +} + +Value::CZString& Value::CZString::operator=(CZString&& other) noexcept { + cstr_ = other.cstr_; + index_ = other.index_; + other.cstr_ = nullptr; + return *this; +} + +bool Value::CZString::operator<(const CZString& other) const { + if (!cstr_) + return index_ < other.index_; + // return strcmp(cstr_, other.cstr_) < 0; + // Assume both are strings. + unsigned this_len = this->storage_.length_; + unsigned other_len = other.storage_.length_; + unsigned min_len = std::min(this_len, other_len); + JSON_ASSERT(this->cstr_ && other.cstr_); + int comp = memcmp(this->cstr_, other.cstr_, min_len); + if (comp < 0) + return true; + if (comp > 0) + return false; + return (this_len < other_len); +} + +bool Value::CZString::operator==(const CZString& other) const { + if (!cstr_) + return index_ == other.index_; + // return strcmp(cstr_, other.cstr_) == 0; + // Assume both are strings. + unsigned this_len = this->storage_.length_; + unsigned other_len = other.storage_.length_; + if (this_len != other_len) + return false; + JSON_ASSERT(this->cstr_ && other.cstr_); + int comp = memcmp(this->cstr_, other.cstr_, this_len); + return comp == 0; +} + +ArrayIndex Value::CZString::index() const { return index_; } + +// const char* Value::CZString::c_str() const { return cstr_; } +const char* Value::CZString::data() const { return cstr_; } +unsigned Value::CZString::length() const { return storage_.length_; } +bool Value::CZString::isStaticString() const { + return storage_.policy_ == noDuplication; } -bool -Value::CZString::operator==( const CZString &other ) const -{ - if ( cstr_ ) - return strcmp( cstr_, other.cstr_ ) == 0; - return index_ == other.index_; -} - - -int -Value::CZString::index() const -{ - return index_; -} - - -const char * -Value::CZString::c_str() const -{ - return cstr_; -} - -bool -Value::CZString::isStaticString() const -{ - return index_ == noDuplication; -} - -#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP - - // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// @@ -270,1458 +344,1291 @@ * memset( this, 0, sizeof(Value) ) * This optimization is used in ValueInternalMap fast allocator. */ -Value::Value( ValueType type ) - : type_( type ) - , allocated_( 0 ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - switch ( type ) - { - case nullValue: - break; - case intValue: - case uintValue: - value_.int_ = 0; - break; - case realValue: - value_.real_ = 0.0; - break; - case stringValue: - value_.string_ = 0; - break; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - case objectValue: - value_.map_ = new ObjectValues(); - break; -#else - case arrayValue: - value_.array_ = arrayAllocator()->newArray(); - break; - case objectValue: - value_.map_ = mapAllocator()->newMap(); - break; -#endif - case booleanValue: - value_.bool_ = false; - break; - default: - JSON_ASSERT_UNREACHABLE; - } -} - - -Value::Value( Int value ) - : type_( intValue ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - value_.int_ = value; -} - - -Value::Value( UInt value ) - : type_( uintValue ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - value_.uint_ = value; -} - -Value::Value( double value ) - : type_( realValue ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - value_.real_ = value; -} - -Value::Value( const char *value ) - : type_( stringValue ) - , allocated_( true ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - value_.string_ = valueAllocator()->duplicateStringValue( value ); -} - - -Value::Value( const char *beginValue, - const char *endValue ) - : type_( stringValue ) - , allocated_( true ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - value_.string_ = valueAllocator()->duplicateStringValue( beginValue, - UInt(endValue - beginValue) ); -} - - -Value::Value( const std::string &value ) - : type_( stringValue ) - , allocated_( true ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - value_.string_ = valueAllocator()->duplicateStringValue( value.c_str(), - (unsigned int)value.length() ); - -} - -Value::Value( const StaticString &value ) - : type_( stringValue ) - , allocated_( false ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - value_.string_ = const_cast( value.c_str() ); -} - - -# ifdef JSON_USE_CPPTL -Value::Value( const CppTL::ConstString &value ) - : type_( stringValue ) - , allocated_( true ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - value_.string_ = valueAllocator()->duplicateStringValue( value, value.length() ); -} -# endif - -Value::Value( bool value ) - : type_( booleanValue ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - value_.bool_ = value; -} - - -Value::Value( const Value &other ) - : type_( other.type_ ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - switch ( type_ ) - { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - value_ = other.value_; - break; - case stringValue: - if ( other.value_.string_ ) - { - value_.string_ = valueAllocator()->duplicateStringValue( other.value_.string_ ); - allocated_ = true; - } - else - value_.string_ = 0; - break; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - case objectValue: - value_.map_ = new ObjectValues( *other.value_.map_ ); - break; -#else - case arrayValue: - value_.array_ = arrayAllocator()->newArrayCopy( *other.value_.array_ ); - break; - case objectValue: - value_.map_ = mapAllocator()->newMapCopy( *other.value_.map_ ); - break; -#endif - default: - JSON_ASSERT_UNREACHABLE; - } - if ( other.comments_ ) - { - comments_ = new CommentInfo[numberOfCommentPlacement]; - for ( int comment =0; comment < numberOfCommentPlacement; ++comment ) - { - const CommentInfo &otherComment = other.comments_[comment]; - if ( otherComment.comment_ ) - comments_[comment].setComment( otherComment.comment_ ); - } - } -} - - -Value::~Value() -{ - switch ( type_ ) - { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - break; - case stringValue: - if ( allocated_ ) - valueAllocator()->releaseStringValue( value_.string_ ); - break; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - case objectValue: - delete value_.map_; - break; -#else - case arrayValue: - arrayAllocator()->destructArray( value_.array_ ); - break; - case objectValue: - mapAllocator()->destructMap( value_.map_ ); - break; -#endif - default: - JSON_ASSERT_UNREACHABLE; - } - - if ( comments_ ) - delete[] comments_; -} - -Value & -Value::operator=( const Value &other ) -{ - Value temp( other ); - swap( temp ); - return *this; -} - -void -Value::swap( Value &other ) -{ - ValueType temp = type_; - type_ = other.type_; - other.type_ = temp; - std::swap( value_, other.value_ ); - int temp2 = allocated_; - allocated_ = other.allocated_; - other.allocated_ = temp2; -} - -ValueType -Value::type() const -{ - return type_; -} - - -int -Value::compare( const Value &other ) -{ - /* - int typeDelta = other.type_ - type_; - switch ( type_ ) - { - case nullValue: - - return other.type_ == type_; - case intValue: - if ( other.type_.isNumeric() - case uintValue: - case realValue: - case booleanValue: - break; - case stringValue, - break; - case arrayValue: - delete value_.array_; - break; - case objectValue: - delete value_.map_; - default: - JSON_ASSERT_UNREACHABLE; - } - */ - (void) other; - return 0; // unreachable -} - -bool -Value::operator <( const Value &other ) const -{ - int typeDelta = type_ - other.type_; - if ( typeDelta ) - return typeDelta < 0 ? true : false; - switch ( type_ ) - { - case nullValue: - return false; - case intValue: - return value_.int_ < other.value_.int_; - case uintValue: - return value_.uint_ < other.value_.uint_; - case realValue: - return value_.real_ < other.value_.real_; - case booleanValue: - return value_.bool_ < other.value_.bool_; - case stringValue: - return ( value_.string_ == 0 && other.value_.string_ ) - || ( other.value_.string_ - && value_.string_ - && strcmp( value_.string_, other.value_.string_ ) < 0 ); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - case objectValue: - { - int delta = int( value_.map_->size() - other.value_.map_->size() ); - if ( delta ) - return delta < 0; - return (*value_.map_) < (*other.value_.map_); - } -#else - case arrayValue: - return value_.array_->compare( *(other.value_.array_) ) < 0; - case objectValue: - return value_.map_->compare( *(other.value_.map_) ) < 0; -#endif - default: - JSON_ASSERT_UNREACHABLE; - } - return 0; // unreachable -} - -bool -Value::operator <=( const Value &other ) const -{ - return !(other > *this); -} - -bool -Value::operator >=( const Value &other ) const -{ - return !(*this < other); -} - -bool -Value::operator >( const Value &other ) const -{ - return other < *this; -} - -bool -Value::operator ==( const Value &other ) const -{ - //if ( type_ != other.type_ ) - // GCC 2.95.3 says: - // attempt to take address of bit-field structure member `Json::Value::type_' - // Beats me, but a temp solves the problem. - int temp = other.type_; - if ( type_ != temp ) - return false; - switch ( type_ ) - { - case nullValue: +Value::Value(ValueType type) { + static char const emptyString[] = ""; + initBasic(type); + switch (type) { + case nullValue: + break; + case intValue: + case uintValue: + value_.int_ = 0; + break; + case realValue: + value_.real_ = 0.0; + break; + case stringValue: + // allocated_ == false, so this is safe. + value_.string_ = const_cast(static_cast(emptyString)); + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(); + break; + case booleanValue: + value_.bool_ = false; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +Value::Value(Int value) { + initBasic(intValue); + value_.int_ = value; +} + +Value::Value(UInt value) { + initBasic(uintValue); + value_.uint_ = value; +} +#if defined(JSON_HAS_INT64) +Value::Value(Int64 value) { + initBasic(intValue); + value_.int_ = value; +} +Value::Value(UInt64 value) { + initBasic(uintValue); + value_.uint_ = value; +} +#endif // defined(JSON_HAS_INT64) + +Value::Value(double value) { + initBasic(realValue); + value_.real_ = value; +} + +Value::Value(const char* value) { + initBasic(stringValue, true); + JSON_ASSERT_MESSAGE(value != nullptr, + "Null Value Passed to Value Constructor"); + value_.string_ = duplicateAndPrefixStringValue( + value, static_cast(strlen(value))); +} + +Value::Value(const char* begin, const char* end) { + initBasic(stringValue, true); + value_.string_ = + duplicateAndPrefixStringValue(begin, static_cast(end - begin)); +} + +Value::Value(const String& value) { + initBasic(stringValue, true); + value_.string_ = duplicateAndPrefixStringValue( + value.data(), static_cast(value.length())); +} + +Value::Value(const StaticString& value) { + initBasic(stringValue); + value_.string_ = const_cast(value.c_str()); +} + +Value::Value(bool value) { + initBasic(booleanValue); + value_.bool_ = value; +} + +Value::Value(const Value& other) { + dupPayload(other); + dupMeta(other); +} + +Value::Value(Value&& other) noexcept { + initBasic(nullValue); + swap(other); +} + +Value::~Value() { + releasePayload(); + value_.uint_ = 0; +} + +Value& Value::operator=(const Value& other) { + Value(other).swap(*this); + return *this; +} + +Value& Value::operator=(Value&& other) noexcept { + other.swap(*this); + return *this; +} + +void Value::swapPayload(Value& other) { + std::swap(bits_, other.bits_); + std::swap(value_, other.value_); +} + +void Value::copyPayload(const Value& other) { + releasePayload(); + dupPayload(other); +} + +void Value::swap(Value& other) { + swapPayload(other); + std::swap(comments_, other.comments_); + std::swap(start_, other.start_); + std::swap(limit_, other.limit_); +} + +void Value::copy(const Value& other) { + copyPayload(other); + dupMeta(other); +} + +ValueType Value::type() const { + return static_cast(bits_.value_type_); +} + +int Value::compare(const Value& other) const { + if (*this < other) + return -1; + if (*this > other) + return 1; + return 0; +} + +bool Value::operator<(const Value& other) const { + int typeDelta = type() - other.type(); + if (typeDelta) + return typeDelta < 0; + switch (type()) { + case nullValue: + return false; + case intValue: + return value_.int_ < other.value_.int_; + case uintValue: + return value_.uint_ < other.value_.uint_; + case realValue: + return value_.real_ < other.value_.real_; + case booleanValue: + return value_.bool_ < other.value_.bool_; + case stringValue: { + if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) { + return other.value_.string_ != nullptr; + } + unsigned this_len; + unsigned other_len; + char const* this_str; + char const* other_str; + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len, + &other_str); + unsigned min_len = std::min(this_len, other_len); + JSON_ASSERT(this_str && other_str); + int comp = memcmp(this_str, other_str, min_len); + if (comp < 0) return true; - case intValue: - return value_.int_ == other.value_.int_; - case uintValue: - return value_.uint_ == other.value_.uint_; - case realValue: - return value_.real_ == other.value_.real_; - case booleanValue: - return value_.bool_ == other.value_.bool_; - case stringValue: - return ( value_.string_ == other.value_.string_ ) - || ( other.value_.string_ - && value_.string_ - && strcmp( value_.string_, other.value_.string_ ) == 0 ); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - case objectValue: - return value_.map_->size() == other.value_.map_->size() - && (*value_.map_) == (*other.value_.map_); -#else - case arrayValue: - return value_.array_->compare( *(other.value_.array_) ) == 0; - case objectValue: - return value_.map_->compare( *(other.value_.map_) ) == 0; -#endif - default: - JSON_ASSERT_UNREACHABLE; - } - return 0; // unreachable -} - -bool -Value::operator !=( const Value &other ) const -{ - return !( *this == other ); -} - -const char * -Value::asCString() const -{ - JSON_ASSERT( type_ == stringValue ); - return value_.string_; -} - - -std::string -Value::asString() const -{ - switch ( type_ ) - { - case nullValue: - return ""; - case stringValue: - return value_.string_ ? value_.string_ : ""; - case booleanValue: - return value_.bool_ ? "true" : "false"; - case intValue: - case uintValue: - case realValue: - case arrayValue: - case objectValue: - JSON_ASSERT_MESSAGE( false, "Type is not convertible to string" ); - default: - JSON_ASSERT_UNREACHABLE; - } - return ""; // unreachable -} - -# ifdef JSON_USE_CPPTL -CppTL::ConstString -Value::asConstString() const -{ - return CppTL::ConstString( asString().c_str() ); -} -# endif - -Value::Int -Value::asInt() const -{ - switch ( type_ ) - { - case nullValue: - return 0; - case intValue: - return value_.int_; - case uintValue: - JSON_ASSERT_MESSAGE( value_.uint_ < (unsigned)maxInt, "integer out of signed integer range" ); - return value_.uint_; - case realValue: - JSON_ASSERT_MESSAGE( value_.real_ >= minInt && value_.real_ <= maxInt, "Real out of signed integer range" ); - return Int( value_.real_ ); - case booleanValue: - return value_.bool_ ? 1 : 0; - case stringValue: - case arrayValue: - case objectValue: - JSON_ASSERT_MESSAGE( false, "Type is not convertible to int" ); - default: - JSON_ASSERT_UNREACHABLE; - } - return 0; // unreachable; -} - -Value::UInt -Value::asUInt() const -{ - switch ( type_ ) - { - case nullValue: - return 0; - case intValue: - JSON_ASSERT_MESSAGE( value_.int_ >= 0, "Negative integer can not be converted to unsigned integer" ); - return value_.int_; - case uintValue: - return value_.uint_; - case realValue: - JSON_ASSERT_MESSAGE( value_.real_ >= 0 && value_.real_ <= maxUInt, "Real out of unsigned integer range" ); - return UInt( value_.real_ ); - case booleanValue: - return value_.bool_ ? 1 : 0; - case stringValue: - case arrayValue: - case objectValue: - JSON_ASSERT_MESSAGE( false, "Type is not convertible to uint" ); - default: - JSON_ASSERT_UNREACHABLE; - } - return 0; // unreachable; -} - -double -Value::asDouble() const -{ - switch ( type_ ) - { - case nullValue: - return 0.0; - case intValue: - return value_.int_; - case uintValue: - return value_.uint_; - case realValue: - return value_.real_; - case booleanValue: - return value_.bool_ ? 1.0 : 0.0; - case stringValue: - case arrayValue: - case objectValue: - JSON_ASSERT_MESSAGE( false, "Type is not convertible to double" ); - default: - JSON_ASSERT_UNREACHABLE; - } - return 0; // unreachable; -} - -bool -Value::asBool() const -{ - switch ( type_ ) - { - case nullValue: + if (comp > 0) return false; - case intValue: - case uintValue: - return value_.int_ != 0; - case realValue: - return value_.real_ != 0.0; - case booleanValue: - return value_.bool_; - case stringValue: - return value_.string_ && value_.string_[0] != 0; - case arrayValue: - case objectValue: - return value_.map_->size() != 0; - default: - JSON_ASSERT_UNREACHABLE; - } - return false; // unreachable; -} - - -bool -Value::isConvertibleTo( ValueType other ) const -{ - switch ( type_ ) - { - case nullValue: - return true; - case intValue: - return ( other == nullValue && value_.int_ == 0 ) - || other == intValue - || ( other == uintValue && value_.int_ >= 0 ) - || other == realValue - || other == stringValue - || other == booleanValue; - case uintValue: - return ( other == nullValue && value_.uint_ == 0 ) - || ( other == intValue && value_.uint_ <= (unsigned)maxInt ) - || other == uintValue - || other == realValue - || other == stringValue - || other == booleanValue; - case realValue: - return ( other == nullValue && value_.real_ == 0.0 ) - || ( other == intValue && value_.real_ >= minInt && value_.real_ <= maxInt ) - || ( other == uintValue && value_.real_ >= 0 && value_.real_ <= maxUInt ) - || other == realValue - || other == stringValue - || other == booleanValue; - case booleanValue: - return ( other == nullValue && value_.bool_ == false ) - || other == intValue - || other == uintValue - || other == realValue - || other == stringValue - || other == booleanValue; - case stringValue: - return other == stringValue - || ( other == nullValue && (!value_.string_ || value_.string_[0] == 0) ); - case arrayValue: - return other == arrayValue - || ( other == nullValue && value_.map_->size() == 0 ); - case objectValue: - return other == objectValue - || ( other == nullValue && value_.map_->size() == 0 ); - default: - JSON_ASSERT_UNREACHABLE; - } - return false; // unreachable; -} - - -/// Number of values in array or object -Value::UInt -Value::size() const -{ - switch ( type_ ) - { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - case stringValue: - return 0; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: // size of the array is highest index + 1 - if ( !value_.map_->empty() ) - { - ObjectValues::const_iterator itLast = value_.map_->end(); - --itLast; - return (*itLast).first.index()+1; - } - return 0; - case objectValue: - return Int( value_.map_->size() ); -#else - case arrayValue: - return Int( value_.array_->size() ); - case objectValue: - return Int( value_.map_->size() ); -#endif - default: - JSON_ASSERT_UNREACHABLE; - } - return 0; // unreachable; -} - - -bool -Value::empty() const -{ - if ( isNull() || isArray() || isObject() ) - return size() == 0u; - else + return (this_len < other_len); + } + case arrayValue: + case objectValue: { + auto thisSize = value_.map_->size(); + auto otherSize = other.value_.map_->size(); + if (thisSize != otherSize) + return thisSize < otherSize; + return (*value_.map_) < (*other.value_.map_); + } + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator<=(const Value& other) const { return !(other < *this); } + +bool Value::operator>=(const Value& other) const { return !(*this < other); } + +bool Value::operator>(const Value& other) const { return other < *this; } + +bool Value::operator==(const Value& other) const { + if (type() != other.type()) + return false; + switch (type()) { + case nullValue: + return true; + case intValue: + return value_.int_ == other.value_.int_; + case uintValue: + return value_.uint_ == other.value_.uint_; + case realValue: + return value_.real_ == other.value_.real_; + case booleanValue: + return value_.bool_ == other.value_.bool_; + case stringValue: { + if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) { + return (value_.string_ == other.value_.string_); + } + unsigned this_len; + unsigned other_len; + char const* this_str; + char const* other_str; + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len, + &other_str); + if (this_len != other_len) return false; + JSON_ASSERT(this_str && other_str); + int comp = memcmp(this_str, other_str, this_len); + return comp == 0; + } + case arrayValue: + case objectValue: + return value_.map_->size() == other.value_.map_->size() && + (*value_.map_) == (*other.value_.map_); + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator!=(const Value& other) const { return !(*this == other); } + +const char* Value::asCString() const { + JSON_ASSERT_MESSAGE(type() == stringValue, + "in Json::Value::asCString(): requires stringValue"); + if (value_.string_ == nullptr) + return nullptr; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + return this_str; +} + +#if JSONCPP_USING_SECURE_MEMORY +unsigned Value::getCStringLength() const { + JSON_ASSERT_MESSAGE(type() == stringValue, + "in Json::Value::asCString(): requires stringValue"); + if (value_.string_ == 0) + return 0; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + return this_len; } - - -bool -Value::operator!() const -{ - return isNull(); -} - - -void -Value::clear() -{ - JSON_ASSERT( type_ == nullValue || type_ == arrayValue || type_ == objectValue ); - - switch ( type_ ) - { -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - case objectValue: - value_.map_->clear(); - break; -#else - case arrayValue: - value_.array_->clear(); - break; - case objectValue: - value_.map_->clear(); - break; -#endif - default: - break; - } -} - -void -Value::resize( UInt newSize ) -{ - JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); - if ( type_ == nullValue ) - *this = Value( arrayValue ); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - UInt oldSize = size(); - if ( newSize == 0 ) - clear(); - else if ( newSize > oldSize ) - (*this)[ newSize - 1 ]; - else - { - for ( UInt index = newSize; index < oldSize; ++index ) - value_.map_->erase( index ); - assert( size() == newSize ); - } -#else - value_.array_->resize( newSize ); #endif -} - -Value & -Value::operator[]( UInt index ) -{ - JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); - if ( type_ == nullValue ) - *this = Value( arrayValue ); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - CZString key( index ); - ObjectValues::iterator it = value_.map_->lower_bound( key ); - if ( it != value_.map_->end() && (*it).first == key ) - return (*it).second; - - ObjectValues::value_type defaultValue( key, null ); - it = value_.map_->insert( it, defaultValue ); - return (*it).second; +bool Value::getString(char const** begin, char const** end) const { + if (type() != stringValue) + return false; + if (value_.string_ == nullptr) + return false; + unsigned length; + decodePrefixedString(this->isAllocated(), this->value_.string_, &length, + begin); + *end = *begin + length; + return true; +} + +String Value::asString() const { + switch (type()) { + case nullValue: + return ""; + case stringValue: { + if (value_.string_ == nullptr) + return ""; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + return String(this_str, this_len); + } + case booleanValue: + return value_.bool_ ? "true" : "false"; + case intValue: + return valueToString(value_.int_); + case uintValue: + return valueToString(value_.uint_); + case realValue: + return valueToString(value_.real_); + default: + JSON_FAIL_MESSAGE("Type is not convertible to string"); + } +} + +Value::Int Value::asInt() const { + switch (type()) { + case intValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); + return Int(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range"); + return Int(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), + "double out of Int range"); + return Int(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int."); +} + +Value::UInt Value::asUInt() const { + switch (type()) { + case intValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); + return UInt(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range"); + return UInt(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), + "double out of UInt range"); + return UInt(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt."); +} + +#if defined(JSON_HAS_INT64) + +Value::Int64 Value::asInt64() const { + switch (type()) { + case intValue: + return Int64(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range"); + return Int64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), + "double out of Int64 range"); + return Int64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int64."); +} + +Value::UInt64 Value::asUInt64() const { + switch (type()) { + case intValue: + JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); + return UInt64(value_.int_); + case uintValue: + return UInt64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), + "double out of UInt64 range"); + return UInt64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt64."); +} +#endif // if defined(JSON_HAS_INT64) + +LargestInt Value::asLargestInt() const { +#if defined(JSON_NO_INT64) + return asInt(); #else - return value_.array_->resolveReference( index ); + return asInt64(); #endif } - -const Value & -Value::operator[]( UInt index ) const -{ - JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); - if ( type_ == nullValue ) - return null; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - CZString key( index ); - ObjectValues::const_iterator it = value_.map_->find( key ); - if ( it == value_.map_->end() ) - return null; - return (*it).second; +LargestUInt Value::asLargestUInt() const { +#if defined(JSON_NO_INT64) + return asUInt(); #else - Value *value = value_.array_->find( index ); - return value ? *value : null; + return asUInt64(); #endif } - -Value & -Value::operator[]( const char *key ) -{ - return resolveReference( key, false ); +double Value::asDouble() const { + switch (type()) { + case intValue: + return static_cast(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return integerToDouble(value_.uint_); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return value_.real_; + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0 : 0.0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to double."); +} + +float Value::asFloat() const { + switch (type()) { + case intValue: + return static_cast(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + // This can fail (silently?) if the value is bigger than MAX_FLOAT. + return static_cast(integerToDouble(value_.uint_)); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return static_cast(value_.real_); + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0F : 0.0F; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to float."); +} + +bool Value::asBool() const { + switch (type()) { + case booleanValue: + return value_.bool_; + case nullValue: + return false; + case intValue: + return value_.int_ != 0; + case uintValue: + return value_.uint_ != 0; + case realValue: { + // According to JavaScript language zero or NaN is regarded as false + const auto value_classification = std::fpclassify(value_.real_); + return value_classification != FP_ZERO && value_classification != FP_NAN; + } + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to bool."); +} + +bool Value::isConvertibleTo(ValueType other) const { + switch (other) { + case nullValue: + return (isNumeric() && asDouble() == 0.0) || + (type() == booleanValue && !value_.bool_) || + (type() == stringValue && asString().empty()) || + (type() == arrayValue && value_.map_->empty()) || + (type() == objectValue && value_.map_->empty()) || + type() == nullValue; + case intValue: + return isInt() || + (type() == realValue && InRange(value_.real_, minInt, maxInt)) || + type() == booleanValue || type() == nullValue; + case uintValue: + return isUInt() || + (type() == realValue && InRange(value_.real_, 0, maxUInt)) || + type() == booleanValue || type() == nullValue; + case realValue: + return isNumeric() || type() == booleanValue || type() == nullValue; + case booleanValue: + return isNumeric() || type() == booleanValue || type() == nullValue; + case stringValue: + return isNumeric() || type() == booleanValue || type() == stringValue || + type() == nullValue; + case arrayValue: + return type() == arrayValue || type() == nullValue; + case objectValue: + return type() == objectValue || type() == nullValue; + } + JSON_ASSERT_UNREACHABLE; + return false; } - -Value & -Value::resolveReference( const char *key, - bool isStatic ) -{ - JSON_ASSERT( type_ == nullValue || type_ == objectValue ); - if ( type_ == nullValue ) - *this = Value( objectValue ); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - CZString actualKey( key, isStatic ? CZString::noDuplication - : CZString::duplicateOnCopy ); - ObjectValues::iterator it = value_.map_->lower_bound( actualKey ); - if ( it != value_.map_->end() && (*it).first == actualKey ) - return (*it).second; - - ObjectValues::value_type defaultValue( actualKey, null ); - it = value_.map_->insert( it, defaultValue ); - Value &value = (*it).second; - return value; +/// Number of values in array or object +ArrayIndex Value::size() const { + switch (type()) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + case stringValue: + return 0; + case arrayValue: // size of the array is highest index + 1 + if (!value_.map_->empty()) { + ObjectValues::const_iterator itLast = value_.map_->end(); + --itLast; + return (*itLast).first.index() + 1; + } + return 0; + case objectValue: + return ArrayIndex(value_.map_->size()); + } + JSON_ASSERT_UNREACHABLE; + return 0; // unreachable; +} + +bool Value::empty() const { + if (isNull() || isArray() || isObject()) + return size() == 0U; + return false; +} + +Value::operator bool() const { return !isNull(); } + +void Value::clear() { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue || + type() == objectValue, + "in Json::Value::clear(): requires complex value"); + start_ = 0; + limit_ = 0; + switch (type()) { + case arrayValue: + case objectValue: + value_.map_->clear(); + break; + default: + break; + } +} + +void Value::resize(ArrayIndex newSize) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue, + "in Json::Value::resize(): requires arrayValue"); + if (type() == nullValue) + *this = Value(arrayValue); + ArrayIndex oldSize = size(); + if (newSize == 0) + clear(); + else if (newSize > oldSize) + for (ArrayIndex i = oldSize; i < newSize; ++i) + (*this)[i]; + else { + for (ArrayIndex index = newSize; index < oldSize; ++index) { + value_.map_->erase(index); + } + JSON_ASSERT(size() == newSize); + } +} + +Value& Value::operator[](ArrayIndex index) { + JSON_ASSERT_MESSAGE( + type() == nullValue || type() == arrayValue, + "in Json::Value::operator[](ArrayIndex): requires arrayValue"); + if (type() == nullValue) + *this = Value(arrayValue); + CZString key(index); + auto it = value_.map_->lower_bound(key); + if (it != value_.map_->end() && (*it).first == key) + return (*it).second; + + ObjectValues::value_type defaultValue(key, nullSingleton()); + it = value_.map_->insert(it, defaultValue); + return (*it).second; +} + +Value& Value::operator[](int index) { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value::operator[](int index): index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +const Value& Value::operator[](ArrayIndex index) const { + JSON_ASSERT_MESSAGE( + type() == nullValue || type() == arrayValue, + "in Json::Value::operator[](ArrayIndex)const: requires arrayValue"); + if (type() == nullValue) + return nullSingleton(); + CZString key(index); + ObjectValues::const_iterator it = value_.map_->find(key); + if (it == value_.map_->end()) + return nullSingleton(); + return (*it).second; +} + +const Value& Value::operator[](int index) const { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value::operator[](int index) const: index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +void Value::initBasic(ValueType type, bool allocated) { + setType(type); + setIsAllocated(allocated); + comments_ = Comments{}; + start_ = 0; + limit_ = 0; +} + +void Value::dupPayload(const Value& other) { + setType(other.type()); + setIsAllocated(false); + switch (type()) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + value_ = other.value_; + break; + case stringValue: + if (other.value_.string_ && other.isAllocated()) { + unsigned len; + char const* str; + decodePrefixedString(other.isAllocated(), other.value_.string_, &len, + &str); + value_.string_ = duplicateAndPrefixStringValue(str, len); + setIsAllocated(true); + } else { + value_.string_ = other.value_.string_; + } + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(*other.value_.map_); + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +void Value::releasePayload() { + switch (type()) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + if (isAllocated()) + releasePrefixedStringValue(value_.string_); + break; + case arrayValue: + case objectValue: + delete value_.map_; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +void Value::dupMeta(const Value& other) { + comments_ = other.comments_; + start_ = other.start_; + limit_ = other.limit_; +} + +// Access an object value by name, create a null member if it does not exist. +// @pre Type of '*this' is object or null. +// @param key is null-terminated. +Value& Value::resolveReference(const char* key) { + JSON_ASSERT_MESSAGE( + type() == nullValue || type() == objectValue, + "in Json::Value::resolveReference(): requires objectValue"); + if (type() == nullValue) + *this = Value(objectValue); + CZString actualKey(key, static_cast(strlen(key)), + CZString::noDuplication); // NOTE! + auto it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, nullSingleton()); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +} + +// @param key is not null-terminated. +Value& Value::resolveReference(char const* key, char const* end) { + JSON_ASSERT_MESSAGE( + type() == nullValue || type() == objectValue, + "in Json::Value::resolveReference(key, end): requires objectValue"); + if (type() == nullValue) + *this = Value(objectValue); + CZString actualKey(key, static_cast(end - key), + CZString::duplicateOnCopy); + auto it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, nullSingleton()); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +} + +Value Value::get(ArrayIndex index, const Value& defaultValue) const { + const Value* value = &((*this)[index]); + return value == &nullSingleton() ? defaultValue : *value; +} + +bool Value::isValidIndex(ArrayIndex index) const { return index < size(); } + +Value const* Value::find(char const* begin, char const* end) const { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, + "in Json::Value::find(begin, end): requires " + "objectValue or nullValue"); + if (type() == nullValue) + return nullptr; + CZString actualKey(begin, static_cast(end - begin), + CZString::noDuplication); + ObjectValues::const_iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) + return nullptr; + return &(*it).second; +} +Value* Value::demand(char const* begin, char const* end) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, + "in Json::Value::demand(begin, end): requires " + "objectValue or nullValue"); + return &resolveReference(begin, end); +} +const Value& Value::operator[](const char* key) const { + Value const* found = find(key, key + strlen(key)); + if (!found) + return nullSingleton(); + return *found; +} +Value const& Value::operator[](const String& key) const { + Value const* found = find(key.data(), key.data() + key.length()); + if (!found) + return nullSingleton(); + return *found; +} + +Value& Value::operator[](const char* key) { + return resolveReference(key, key + strlen(key)); +} + +Value& Value::operator[](const String& key) { + return resolveReference(key.data(), key.data() + key.length()); +} + +Value& Value::operator[](const StaticString& key) { + return resolveReference(key.c_str()); +} + +Value& Value::append(const Value& value) { return append(Value(value)); } + +Value& Value::append(Value&& value) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue, + "in Json::Value::append: requires arrayValue"); + if (type() == nullValue) { + *this = Value(arrayValue); + } + return this->value_.map_->emplace(size(), std::move(value)).first->second; +} + +bool Value::insert(ArrayIndex index, const Value& newValue) { + return insert(index, Value(newValue)); +} + +bool Value::insert(ArrayIndex index, Value&& newValue) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue, + "in Json::Value::insert: requires arrayValue"); + ArrayIndex length = size(); + if (index > length) { + return false; + } + for (ArrayIndex i = length; i > index; i--) { + (*this)[i] = std::move((*this)[i - 1]); + } + (*this)[index] = std::move(newValue); + return true; +} + +Value Value::get(char const* begin, char const* end, + Value const& defaultValue) const { + Value const* found = find(begin, end); + return !found ? defaultValue : *found; +} +Value Value::get(char const* key, Value const& defaultValue) const { + return get(key, key + strlen(key), defaultValue); +} +Value Value::get(String const& key, Value const& defaultValue) const { + return get(key.data(), key.data() + key.length(), defaultValue); +} + +bool Value::removeMember(const char* begin, const char* end, Value* removed) { + if (type() != objectValue) { + return false; + } + CZString actualKey(begin, static_cast(end - begin), + CZString::noDuplication); + auto it = value_.map_->find(actualKey); + if (it == value_.map_->end()) + return false; + if (removed) + *removed = std::move(it->second); + value_.map_->erase(it); + return true; +} +bool Value::removeMember(const char* key, Value* removed) { + return removeMember(key, key + strlen(key), removed); +} +bool Value::removeMember(String const& key, Value* removed) { + return removeMember(key.data(), key.data() + key.length(), removed); +} +void Value::removeMember(const char* key) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, + "in Json::Value::removeMember(): requires objectValue"); + if (type() == nullValue) + return; + + CZString actualKey(key, unsigned(strlen(key)), CZString::noDuplication); + value_.map_->erase(actualKey); +} +void Value::removeMember(const String& key) { removeMember(key.c_str()); } + +bool Value::removeIndex(ArrayIndex index, Value* removed) { + if (type() != arrayValue) { + return false; + } + CZString key(index); + auto it = value_.map_->find(key); + if (it == value_.map_->end()) { + return false; + } + if (removed) + *removed = it->second; + ArrayIndex oldSize = size(); + // shift left all items left, into the place of the "removed" + for (ArrayIndex i = index; i < (oldSize - 1); ++i) { + CZString keey(i); + (*value_.map_)[keey] = (*this)[i + 1]; + } + // erase the last one ("leftover") + CZString keyLast(oldSize - 1); + auto itLast = value_.map_->find(keyLast); + value_.map_->erase(itLast); + return true; +} + +bool Value::isMember(char const* begin, char const* end) const { + Value const* value = find(begin, end); + return nullptr != value; +} +bool Value::isMember(char const* key) const { + return isMember(key, key + strlen(key)); +} +bool Value::isMember(String const& key) const { + return isMember(key.data(), key.data() + key.length()); +} + +Value::Members Value::getMemberNames() const { + JSON_ASSERT_MESSAGE( + type() == nullValue || type() == objectValue, + "in Json::Value::getMemberNames(), value must be objectValue"); + if (type() == nullValue) + return Value::Members(); + Members members; + members.reserve(value_.map_->size()); + ObjectValues::const_iterator it = value_.map_->begin(); + ObjectValues::const_iterator itEnd = value_.map_->end(); + for (; it != itEnd; ++it) { + members.push_back(String((*it).first.data(), (*it).first.length())); + } + return members; +} + +static bool IsIntegral(double d) { + double integral_part; + return modf(d, &integral_part) == 0.0; +} + +bool Value::isNull() const { return type() == nullValue; } + +bool Value::isBool() const { return type() == booleanValue; } + +bool Value::isInt() const { + switch (type()) { + case intValue: +#if defined(JSON_HAS_INT64) + return value_.int_ >= minInt && value_.int_ <= maxInt; #else - return value_.map_->resolveReference( key, isStatic ); + return true; #endif -} - - -Value -Value::get( UInt index, - const Value &defaultValue ) const -{ - const Value *value = &((*this)[index]); - return value == &null ? defaultValue : *value; -} - - -bool -Value::isValidIndex( UInt index ) const -{ - return index < size(); -} - - - -const Value & -Value::operator[]( const char *key ) const -{ - JSON_ASSERT( type_ == nullValue || type_ == objectValue ); - if ( type_ == nullValue ) - return null; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - CZString actualKey( key, CZString::noDuplication ); - ObjectValues::const_iterator it = value_.map_->find( actualKey ); - if ( it == value_.map_->end() ) - return null; - return (*it).second; + case uintValue: + return value_.uint_ <= UInt(maxInt); + case realValue: + return value_.real_ >= minInt && value_.real_ <= maxInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool Value::isUInt() const { + switch (type()) { + case intValue: +#if defined(JSON_HAS_INT64) + return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); #else - const Value *value = value_.map_->find( key ); - return value ? *value : null; + return value_.int_ >= 0; #endif -} - - -Value & -Value::operator[]( const std::string &key ) -{ - return (*this)[ key.c_str() ]; -} - - -const Value & -Value::operator[]( const std::string &key ) const -{ - return (*this)[ key.c_str() ]; -} - -Value & -Value::operator[]( const StaticString &key ) -{ - return resolveReference( key, true ); -} - - -# ifdef JSON_USE_CPPTL -Value & -Value::operator[]( const CppTL::ConstString &key ) -{ - return (*this)[ key.c_str() ]; -} - - -const Value & -Value::operator[]( const CppTL::ConstString &key ) const -{ - return (*this)[ key.c_str() ]; -} -# endif - - -Value & -Value::append( const Value &value ) -{ - return (*this)[size()] = value; -} - - -Value -Value::get( const char *key, - const Value &defaultValue ) const -{ - const Value *value = &((*this)[key]); - return value == &null ? defaultValue : *value; -} - - -Value -Value::get( const std::string &key, - const Value &defaultValue ) const -{ - return get( key.c_str(), defaultValue ); -} - -Value -Value::removeMember( const char* key ) -{ - JSON_ASSERT( type_ == nullValue || type_ == objectValue ); - if ( type_ == nullValue ) - return null; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - CZString actualKey( key, CZString::noDuplication ); - ObjectValues::iterator it = value_.map_->find( actualKey ); - if ( it == value_.map_->end() ) - return null; - Value old(it->second); - value_.map_->erase(it); - return old; + case uintValue: +#if defined(JSON_HAS_INT64) + return value_.uint_ <= maxUInt; #else - Value *value = value_.map_->find( key ); - if (value){ - Value old(*value); - value_.map_.remove( key ); - return old; - } else { - return null; - } + return true; #endif + case realValue: + return value_.real_ >= 0 && value_.real_ <= maxUInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool Value::isInt64() const { +#if defined(JSON_HAS_INT64) + switch (type()) { + case intValue: + return true; + case uintValue: + return value_.uint_ <= UInt64(maxInt64); + case realValue: + // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a + // double, so double(maxInt64) will be rounded up to 2^63. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= double(minInt64) && + value_.real_ < double(maxInt64) && IsIntegral(value_.real_); + default: + break; + } +#endif // JSON_HAS_INT64 + return false; +} + +bool Value::isUInt64() const { +#if defined(JSON_HAS_INT64) + switch (type()) { + case intValue: + return value_.int_ >= 0; + case uintValue: + return true; + case realValue: + // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a + // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble && + IsIntegral(value_.real_); + default: + break; + } +#endif // JSON_HAS_INT64 + return false; +} + +bool Value::isIntegral() const { + switch (type()) { + case intValue: + case uintValue: + return true; + case realValue: +#if defined(JSON_HAS_INT64) + // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a + // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= double(minInt64) && + value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_); +#else + return value_.real_ >= minInt && value_.real_ <= maxUInt && + IsIntegral(value_.real_); +#endif // JSON_HAS_INT64 + default: + break; + } + return false; } -Value -Value::removeMember( const std::string &key ) -{ - return removeMember( key.c_str() ); +bool Value::isDouble() const { + return type() == intValue || type() == uintValue || type() == realValue; } -# ifdef JSON_USE_CPPTL -Value -Value::get( const CppTL::ConstString &key, - const Value &defaultValue ) const -{ - return get( key.c_str(), defaultValue ); -} -# endif +bool Value::isNumeric() const { return isDouble(); } -bool -Value::isMember( const char *key ) const -{ - const Value *value = &((*this)[key]); - return value != &null; -} +bool Value::isString() const { return type() == stringValue; } +bool Value::isArray() const { return type() == arrayValue; } -bool -Value::isMember( const std::string &key ) const -{ - return isMember( key.c_str() ); -} +bool Value::isObject() const { return type() == objectValue; } +Value::Comments::Comments(const Comments& that) + : ptr_{cloneUnique(that.ptr_)} {} -# ifdef JSON_USE_CPPTL -bool -Value::isMember( const CppTL::ConstString &key ) const -{ - return isMember( key.c_str() ); -} -#endif +Value::Comments::Comments(Comments&& that) noexcept + : ptr_{std::move(that.ptr_)} {} -Value::Members -Value::getMemberNames() const -{ - JSON_ASSERT( type_ == nullValue || type_ == objectValue ); - if ( type_ == nullValue ) - return Value::Members(); - Members members; - members.reserve( value_.map_->size() ); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - ObjectValues::const_iterator it = value_.map_->begin(); - ObjectValues::const_iterator itEnd = value_.map_->end(); - for ( ; it != itEnd; ++it ) - members.push_back( std::string( (*it).first.c_str() ) ); -#else - ValueInternalMap::IteratorState it; - ValueInternalMap::IteratorState itEnd; - value_.map_->makeBeginIterator( it ); - value_.map_->makeEndIterator( itEnd ); - for ( ; !ValueInternalMap::equals( it, itEnd ); ValueInternalMap::increment(it) ) - members.push_back( std::string( ValueInternalMap::key( it ) ) ); -#endif - return members; +Value::Comments& Value::Comments::operator=(const Comments& that) { + ptr_ = cloneUnique(that.ptr_); + return *this; } -// -//# ifdef JSON_USE_CPPTL -//EnumMemberNames -//Value::enumMemberNames() const -//{ -// if ( type_ == objectValue ) -// { -// return CppTL::Enum::any( CppTL::Enum::transform( -// CppTL::Enum::keys( *(value_.map_), CppTL::Type() ), -// MemberNamesTransform() ) ); -// } -// return EnumMemberNames(); -//} -// -// -//EnumValues -//Value::enumValues() const -//{ -// if ( type_ == objectValue || type_ == arrayValue ) -// return CppTL::Enum::anyValues( *(value_.map_), -// CppTL::Type() ); -// return EnumValues(); -//} -// -//# endif - -bool -Value::isNull() const -{ - return type_ == nullValue; +Value::Comments& Value::Comments::operator=(Comments&& that) noexcept { + ptr_ = std::move(that.ptr_); + return *this; } - -bool -Value::isBool() const -{ - return type_ == booleanValue; +bool Value::Comments::has(CommentPlacement slot) const { + return ptr_ && !(*ptr_)[slot].empty(); } - -bool -Value::isInt() const -{ - return type_ == intValue; +String Value::Comments::get(CommentPlacement slot) const { + if (!ptr_) + return {}; + return (*ptr_)[slot]; } - -bool -Value::isUInt() const -{ - return type_ == uintValue; +void Value::Comments::set(CommentPlacement slot, String comment) { + if (slot >= CommentPlacement::numberOfCommentPlacement) + return; + if (!ptr_) + ptr_ = std::unique_ptr(new Array()); + (*ptr_)[slot] = std::move(comment); } - -bool -Value::isIntegral() const -{ - return type_ == intValue - || type_ == uintValue - || type_ == booleanValue; +void Value::setComment(String comment, CommentPlacement placement) { + if (!comment.empty() && (comment.back() == '\n')) { + // Always discard trailing newline, to aid indentation. + comment.pop_back(); + } + JSON_ASSERT(!comment.empty()); + JSON_ASSERT_MESSAGE( + comment[0] == '\0' || comment[0] == '/', + "in Json::Value::setComment(): Comments must start with /"); + comments_.set(placement, std::move(comment)); } - -bool -Value::isDouble() const -{ - return type_ == realValue; +bool Value::hasComment(CommentPlacement placement) const { + return comments_.has(placement); } - -bool -Value::isNumeric() const -{ - return isIntegral() || isDouble(); +String Value::getComment(CommentPlacement placement) const { + return comments_.get(placement); } +void Value::setOffsetStart(ptrdiff_t start) { start_ = start; } -bool -Value::isString() const -{ - return type_ == stringValue; -} +void Value::setOffsetLimit(ptrdiff_t limit) { limit_ = limit; } +ptrdiff_t Value::getOffsetStart() const { return start_; } -bool -Value::isArray() const -{ - return type_ == nullValue || type_ == arrayValue; -} +ptrdiff_t Value::getOffsetLimit() const { return limit_; } +String Value::toStyledString() const { + StreamWriterBuilder builder; -bool -Value::isObject() const -{ - return type_ == nullValue || type_ == objectValue; -} + String out = this->hasComment(commentBefore) ? "\n" : ""; + out += Json::writeString(builder, *this); + out += '\n'; - -void -Value::setComment( const char *comment, - CommentPlacement placement ) -{ - if ( !comments_ ) - comments_ = new CommentInfo[numberOfCommentPlacement]; - comments_[placement].setComment( comment ); + return out; } - -void -Value::setComment( const std::string &comment, - CommentPlacement placement ) -{ - setComment( comment.c_str(), placement ); +Value::const_iterator Value::begin() const { + switch (type()) { + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->begin()); + break; + default: + break; + } + return {}; } - -bool -Value::hasComment( CommentPlacement placement ) const -{ - return comments_ != 0 && comments_[placement].comment_ != 0; +Value::const_iterator Value::end() const { + switch (type()) { + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->end()); + break; + default: + break; + } + return {}; } -std::string -Value::getComment( CommentPlacement placement ) const -{ - if ( hasComment(placement) ) - return comments_[placement].comment_; - return ""; -} - - -std::string -Value::toStyledString() const -{ - StyledWriter writer; - return writer.write( *this ); -} - - -Value::const_iterator -Value::begin() const -{ - switch ( type_ ) - { -#ifdef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - if ( value_.array_ ) - { - ValueInternalArray::IteratorState it; - value_.array_->makeBeginIterator( it ); - return const_iterator( it ); - } - break; - case objectValue: - if ( value_.map_ ) - { - ValueInternalMap::IteratorState it; - value_.map_->makeBeginIterator( it ); - return const_iterator( it ); - } - break; -#else - case arrayValue: - case objectValue: - if ( value_.map_ ) - return const_iterator( value_.map_->begin() ); - break; -#endif - default: - break; - } - return const_iterator(); -} - -Value::const_iterator -Value::end() const -{ - switch ( type_ ) - { -#ifdef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - if ( value_.array_ ) - { - ValueInternalArray::IteratorState it; - value_.array_->makeEndIterator( it ); - return const_iterator( it ); - } - break; - case objectValue: - if ( value_.map_ ) - { - ValueInternalMap::IteratorState it; - value_.map_->makeEndIterator( it ); - return const_iterator( it ); - } - break; -#else - case arrayValue: - case objectValue: - if ( value_.map_ ) - return const_iterator( value_.map_->end() ); - break; -#endif - default: - break; - } - return const_iterator(); +Value::iterator Value::begin() { + switch (type()) { + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->begin()); + break; + default: + break; + } + return iterator(); } - -Value::iterator -Value::begin() -{ - switch ( type_ ) - { -#ifdef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - if ( value_.array_ ) - { - ValueInternalArray::IteratorState it; - value_.array_->makeBeginIterator( it ); - return iterator( it ); - } - break; - case objectValue: - if ( value_.map_ ) - { - ValueInternalMap::IteratorState it; - value_.map_->makeBeginIterator( it ); - return iterator( it ); - } - break; -#else - case arrayValue: - case objectValue: - if ( value_.map_ ) - return iterator( value_.map_->begin() ); - break; -#endif - default: - break; - } - return iterator(); -} - -Value::iterator -Value::end() -{ - switch ( type_ ) - { -#ifdef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - if ( value_.array_ ) - { - ValueInternalArray::IteratorState it; - value_.array_->makeEndIterator( it ); - return iterator( it ); - } - break; - case objectValue: - if ( value_.map_ ) - { - ValueInternalMap::IteratorState it; - value_.map_->makeEndIterator( it ); - return iterator( it ); - } - break; -#else - case arrayValue: - case objectValue: - if ( value_.map_ ) - return iterator( value_.map_->end() ); - break; -#endif - default: - break; - } - return iterator(); +Value::iterator Value::end() { + switch (type()) { + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->end()); + break; + default: + break; + } + return iterator(); } - // class PathArgument // ////////////////////////////////////////////////////////////////// -PathArgument::PathArgument() - : kind_( kindNone ) -{ -} - - -PathArgument::PathArgument( Value::UInt index ) - : index_( index ) - , kind_( kindIndex ) -{ -} +PathArgument::PathArgument() = default; +PathArgument::PathArgument(ArrayIndex index) + : index_(index), kind_(kindIndex) {} -PathArgument::PathArgument( const char *key ) - : key_( key ) - , kind_( kindKey ) -{ -} +PathArgument::PathArgument(const char* key) : key_(key), kind_(kindKey) {} - -PathArgument::PathArgument( const std::string &key ) - : key_( key.c_str() ) - , kind_( kindKey ) -{ -} +PathArgument::PathArgument(String key) : key_(std::move(key)), kind_(kindKey) {} // class Path // ////////////////////////////////////////////////////////////////// -Path::Path( const std::string &path, - const PathArgument &a1, - const PathArgument &a2, - const PathArgument &a3, - const PathArgument &a4, - const PathArgument &a5 ) -{ - InArgs in; - in.push_back( &a1 ); - in.push_back( &a2 ); - in.push_back( &a3 ); - in.push_back( &a4 ); - in.push_back( &a5 ); - makePath( path, in ); -} - - -void -Path::makePath( const std::string &path, - const InArgs &in ) -{ - const char *current = path.c_str(); - const char *end = current + path.length(); - InArgs::const_iterator itInArg = in.begin(); - while ( current != end ) - { - if ( *current == '[' ) - { - ++current; - if ( *current == '%' ) - addPathInArg( path, in, itInArg, PathArgument::kindIndex ); - else - { - Value::UInt index = 0; - for ( ; current != end && *current >= '0' && *current <= '9'; ++current ) - index = index * 10 + Value::UInt(*current - '0'); - args_.push_back( index ); - } - if ( current == end || *current++ != ']' ) - invalidPath( path, int(current - path.c_str()) ); - } - else if ( *current == '%' ) - { - addPathInArg( path, in, itInArg, PathArgument::kindKey ); - ++current; - } - else if ( *current == '.' ) - { - ++current; - } - else - { - const char *beginName = current; - while ( current != end && !strchr( "[.", *current ) ) - ++current; - args_.push_back( std::string( beginName, current ) ); - } - } -} - - -void -Path::addPathInArg( const std::string &path, - const InArgs &in, - InArgs::const_iterator &itInArg, - PathArgument::Kind kind ) -{ - (void)path; - if ( itInArg == in.end() ) - { - // Error: missing argument %d - } - else if ( (*itInArg)->kind_ != kind ) - { - // Error: bad argument type - } - else - { - args_.push_back( **itInArg ); - } -} - - -void -Path::invalidPath( const std::string &path, - int location ) -{ - // Error: invalid path. - (void)path; - (void)location; +Path::Path(const String& path, const PathArgument& a1, const PathArgument& a2, + const PathArgument& a3, const PathArgument& a4, + const PathArgument& a5) { + InArgs in; + in.reserve(5); + in.push_back(&a1); + in.push_back(&a2); + in.push_back(&a3); + in.push_back(&a4); + in.push_back(&a5); + makePath(path, in); +} + +void Path::makePath(const String& path, const InArgs& in) { + const char* current = path.c_str(); + const char* end = current + path.length(); + auto itInArg = in.begin(); + while (current != end) { + if (*current == '[') { + ++current; + if (*current == '%') + addPathInArg(path, in, itInArg, PathArgument::kindIndex); + else { + ArrayIndex index = 0; + for (; current != end && *current >= '0' && *current <= '9'; ++current) + index = index * 10 + ArrayIndex(*current - '0'); + args_.push_back(index); + } + if (current == end || *++current != ']') + invalidPath(path, int(current - path.c_str())); + } else if (*current == '%') { + addPathInArg(path, in, itInArg, PathArgument::kindKey); + ++current; + } else if (*current == '.' || *current == ']') { + ++current; + } else { + const char* beginName = current; + while (current != end && !strchr("[.", *current)) + ++current; + args_.push_back(String(beginName, current)); + } + } +} + +void Path::addPathInArg(const String& /*path*/, const InArgs& in, + InArgs::const_iterator& itInArg, + PathArgument::Kind kind) { + if (itInArg == in.end()) { + // Error: missing argument %d + } else if ((*itInArg)->kind_ != kind) { + // Error: bad argument type + } else { + args_.push_back(**itInArg++); + } +} + +void Path::invalidPath(const String& /*path*/, int /*location*/) { + // Error: invalid path. +} + +const Value& Path::resolve(const Value& root) const { + const Value* node = &root; + for (const auto& arg : args_) { + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) { + // Error: unable to resolve path (array value expected at position... ) + return Value::nullSingleton(); + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: unable to resolve path (object value expected at position...) + return Value::nullSingleton(); + } + node = &((*node)[arg.key_]); + if (node == &Value::nullSingleton()) { + // Error: unable to resolve path (object has no member named '' at + // position...) + return Value::nullSingleton(); + } + } + } + return *node; +} + +Value Path::resolve(const Value& root, const Value& defaultValue) const { + const Value* node = &root; + for (const auto& arg : args_) { + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) + return defaultValue; + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) + return defaultValue; + node = &((*node)[arg.key_]); + if (node == &Value::nullSingleton()) + return defaultValue; + } + } + return *node; +} + +Value& Path::make(Value& root) const { + Value* node = &root; + for (const auto& arg : args_) { + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray()) { + // Error: node is not an array at position ... + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: node is not an object at position... + } + node = &((*node)[arg.key_]); + } + } + return *node; } - -const Value & -Path::resolve( const Value &root ) const -{ - const Value *node = &root; - for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) - { - const PathArgument &arg = *it; - if ( arg.kind_ == PathArgument::kindIndex ) - { - if ( !node->isArray() || node->isValidIndex( arg.index_ ) ) - { - // Error: unable to resolve path (array value expected at position... - } - node = &((*node)[arg.index_]); - } - else if ( arg.kind_ == PathArgument::kindKey ) - { - if ( !node->isObject() ) - { - // Error: unable to resolve path (object value expected at position...) - } - node = &((*node)[arg.key_]); - if ( node == &Value::null ) - { - // Error: unable to resolve path (object has no member named '' at position...) - } - } - } - return *node; -} - - -Value -Path::resolve( const Value &root, - const Value &defaultValue ) const -{ - const Value *node = &root; - for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) - { - const PathArgument &arg = *it; - if ( arg.kind_ == PathArgument::kindIndex ) - { - if ( !node->isArray() || node->isValidIndex( arg.index_ ) ) - return defaultValue; - node = &((*node)[arg.index_]); - } - else if ( arg.kind_ == PathArgument::kindKey ) - { - if ( !node->isObject() ) - return defaultValue; - node = &((*node)[arg.key_]); - if ( node == &Value::null ) - return defaultValue; - } - } - return *node; -} - - -Value & -Path::make( Value &root ) const -{ - Value *node = &root; - for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) - { - const PathArgument &arg = *it; - if ( arg.kind_ == PathArgument::kindIndex ) - { - if ( !node->isArray() ) - { - // Error: node is not an array at position ... - } - node = &((*node)[arg.index_]); - } - else if ( arg.kind_ == PathArgument::kindKey ) - { - if ( !node->isObject() ) - { - // Error: node is not an object at position... - } - node = &((*node)[arg.key_]); - } - } - return *node; -} - - } // namespace Json diff -Nru mstflint-4.17.0+1/ext_libs/json/json_valueiterator.inl mstflint-4.21.0+1/ext_libs/json/json_valueiterator.inl --- mstflint-4.17.0+1/ext_libs/json/json_valueiterator.inl 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/json_valueiterator.inl 2022-07-31 09:33:57.000000000 +0000 @@ -1,11 +1,11 @@ -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE // included by json_value.cpp -// everything is within Json namespace +namespace Json { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// @@ -15,201 +15,95 @@ // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -ValueIteratorBase::ValueIteratorBase() -#ifndef JSON_VALUE_USE_INTERNAL_MAP - : current_() - , isNull_( true ) -{ -} -#else - : isArray_( true ) - , isNull_( true ) -{ - iterator_.array_ = ValueInternalArray::IteratorState(); -} -#endif - - -#ifndef JSON_VALUE_USE_INTERNAL_MAP -ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator ¤t ) - : current_( current ) - , isNull_( false ) -{ -} -#else -ValueIteratorBase::ValueIteratorBase( const ValueInternalArray::IteratorState &state ) - : isArray_( true ) -{ - iterator_.array_ = state; -} - - -ValueIteratorBase::ValueIteratorBase( const ValueInternalMap::IteratorState &state ) - : isArray_( false ) -{ - iterator_.map_ = state; -} -#endif - -Value & -ValueIteratorBase::deref() const -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - return current_->second; -#else - if ( isArray_ ) - return ValueInternalArray::dereference( iterator_.array_ ); - return ValueInternalMap::value( iterator_.map_ ); -#endif -} - - -void -ValueIteratorBase::increment() -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - ++current_; -#else - if ( isArray_ ) - ValueInternalArray::increment( iterator_.array_ ); - ValueInternalMap::increment( iterator_.map_ ); -#endif -} - - -void -ValueIteratorBase::decrement() -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - --current_; -#else - if ( isArray_ ) - ValueInternalArray::decrement( iterator_.array_ ); - ValueInternalMap::decrement( iterator_.map_ ); -#endif -} - - -ValueIteratorBase::difference_type -ValueIteratorBase::computeDistance( const SelfType &other ) const -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP -# ifdef JSON_USE_CPPTL_SMALLMAP - return current_ - other.current_; -# else - // Iterator for null value are initialized using the default - // constructor, which initialize current_ to the default - // std::map::iterator. As begin() and end() are two instance - // of the default std::map::iterator, they can not be compared. - // To allow this, we handle this comparison specifically. - if ( isNull_ && other.isNull_ ) - { - return 0; - } - - - // Usage of std::distance is not portable (does not compile with Sun Studio 12 RogueWave STL, - // which is the one used by default). - // Using a portable hand-made version for non random iterator instead: - // return difference_type( std::distance( current_, other.current_ ) ); - difference_type myDistance = 0; - for ( Value::ObjectValues::iterator it = current_; it != other.current_; ++it ) - { - ++myDistance; - } - return myDistance; -# endif -#else - if ( isArray_ ) - return ValueInternalArray::distance( iterator_.array_, other.iterator_.array_ ); - return ValueInternalMap::distance( iterator_.map_, other.iterator_.map_ ); -#endif -} - - -bool -ValueIteratorBase::isEqual( const SelfType &other ) const -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - if ( isNull_ ) - { - return other.isNull_; - } - return current_ == other.current_; -#else - if ( isArray_ ) - return ValueInternalArray::equals( iterator_.array_, other.iterator_.array_ ); - return ValueInternalMap::equals( iterator_.map_, other.iterator_.map_ ); -#endif -} - - -void -ValueIteratorBase::copy( const SelfType &other ) -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - current_ = other.current_; -#else - if ( isArray_ ) - iterator_.array_ = other.iterator_.array_; - iterator_.map_ = other.iterator_.map_; -#endif -} - - -Value -ValueIteratorBase::key() const -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - const Value::CZString czstring = (*current_).first; - if ( czstring.c_str() ) - { - if ( czstring.isStaticString() ) - return Value( StaticString( czstring.c_str() ) ); - return Value( czstring.c_str() ); - } - return Value( czstring.index() ); -#else - if ( isArray_ ) - return Value( ValueInternalArray::indexOf( iterator_.array_ ) ); - bool isStatic; - const char *memberName = ValueInternalMap::key( iterator_.map_, isStatic ); - if ( isStatic ) - return Value( StaticString( memberName ) ); - return Value( memberName ); -#endif -} - - -UInt -ValueIteratorBase::index() const -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - const Value::CZString czstring = (*current_).first; - if ( !czstring.c_str() ) - return czstring.index(); - return Value::UInt( -1 ); -#else - if ( isArray_ ) - return Value::UInt( ValueInternalArray::indexOf( iterator_.array_ ) ); - return Value::UInt( -1 ); -#endif -} - - -const char * -ValueIteratorBase::memberName() const -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - const char *name = (*current_).first.c_str(); - return name ? name : ""; -#else - if ( !isArray_ ) - return ValueInternalMap::key( iterator_.map_ ); - return ""; -#endif -} +ValueIteratorBase::ValueIteratorBase() : current_() {} +ValueIteratorBase::ValueIteratorBase( + const Value::ObjectValues::iterator& current) + : current_(current), isNull_(false) {} + +Value& ValueIteratorBase::deref() { return current_->second; } +const Value& ValueIteratorBase::deref() const { return current_->second; } + +void ValueIteratorBase::increment() { ++current_; } + +void ValueIteratorBase::decrement() { --current_; } + +ValueIteratorBase::difference_type +ValueIteratorBase::computeDistance(const SelfType& other) const { + // Iterator for null value are initialized using the default + // constructor, which initialize current_ to the default + // std::map::iterator. As begin() and end() are two instance + // of the default std::map::iterator, they can not be compared. + // To allow this, we handle this comparison specifically. + if (isNull_ && other.isNull_) { + return 0; + } + + // Usage of std::distance is not portable (does not compile with Sun Studio 12 + // RogueWave STL, + // which is the one used by default). + // Using a portable hand-made version for non random iterator instead: + // return difference_type( std::distance( current_, other.current_ ) ); + difference_type myDistance = 0; + for (Value::ObjectValues::iterator it = current_; it != other.current_; + ++it) { + ++myDistance; + } + return myDistance; +} + +bool ValueIteratorBase::isEqual(const SelfType& other) const { + if (isNull_) { + return other.isNull_; + } + return current_ == other.current_; +} + +void ValueIteratorBase::copy(const SelfType& other) { + current_ = other.current_; + isNull_ = other.isNull_; +} + +Value ValueIteratorBase::key() const { + const Value::CZString czstring = (*current_).first; + if (czstring.data()) { + if (czstring.isStaticString()) + return Value(StaticString(czstring.data())); + return Value(czstring.data(), czstring.data() + czstring.length()); + } + return Value(czstring.index()); +} + +UInt ValueIteratorBase::index() const { + const Value::CZString czstring = (*current_).first; + if (!czstring.data()) + return czstring.index(); + return Value::UInt(-1); +} + +String ValueIteratorBase::name() const { + char const* keey; + char const* end; + keey = memberName(&end); + if (!keey) + return String(); + return String(keey, end); +} + +char const* ValueIteratorBase::memberName() const { + const char* cname = (*current_).first.data(); + return cname ? cname : ""; +} + +char const* ValueIteratorBase::memberName(char const** end) const { + const char* cname = (*current_).first.data(); + if (!cname) { + *end = nullptr; + return nullptr; + } + *end = cname + (*current_).first.length(); + return cname; +} // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// @@ -219,36 +113,21 @@ // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -ValueConstIterator::ValueConstIterator() -{ -} - - -#ifndef JSON_VALUE_USE_INTERNAL_MAP -ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator ¤t ) - : ValueIteratorBase( current ) -{ -} -#else -ValueConstIterator::ValueConstIterator( const ValueInternalArray::IteratorState &state ) - : ValueIteratorBase( state ) -{ -} - -ValueConstIterator::ValueConstIterator( const ValueInternalMap::IteratorState &state ) - : ValueIteratorBase( state ) -{ -} -#endif +ValueConstIterator::ValueConstIterator() = default; -ValueConstIterator & -ValueConstIterator::operator =( const ValueIteratorBase &other ) -{ - copy( other ); - return *this; +ValueConstIterator::ValueConstIterator( + const Value::ObjectValues::iterator& current) + : ValueIteratorBase(current) {} + +ValueConstIterator::ValueConstIterator(ValueIterator const& other) + : ValueIteratorBase(other) {} + +ValueConstIterator& ValueConstIterator:: +operator=(const ValueIteratorBase& other) { + copy(other); + return *this; } - // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// @@ -257,41 +136,21 @@ // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -ValueIterator::ValueIterator() -{ -} - +ValueIterator::ValueIterator() = default; -#ifndef JSON_VALUE_USE_INTERNAL_MAP -ValueIterator::ValueIterator( const Value::ObjectValues::iterator ¤t ) - : ValueIteratorBase( current ) -{ -} -#else -ValueIterator::ValueIterator( const ValueInternalArray::IteratorState &state ) - : ValueIteratorBase( state ) -{ -} +ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current) + : ValueIteratorBase(current) {} -ValueIterator::ValueIterator( const ValueInternalMap::IteratorState &state ) - : ValueIteratorBase( state ) -{ +ValueIterator::ValueIterator(const ValueConstIterator& other) + : ValueIteratorBase(other) { + throwRuntimeError("ConstIterator to Iterator should never be allowed."); } -#endif -ValueIterator::ValueIterator( const ValueConstIterator &other ) - : ValueIteratorBase( other ) -{ -} +ValueIterator::ValueIterator(const ValueIterator& other) = default; -ValueIterator::ValueIterator( const ValueIterator &other ) - : ValueIteratorBase( other ) -{ +ValueIterator& ValueIterator::operator=(const SelfType& other) { + copy(other); + return *this; } -ValueIterator & -ValueIterator::operator =( const SelfType &other ) -{ - copy( other ); - return *this; -} +} // namespace Json diff -Nru mstflint-4.17.0+1/ext_libs/json/json_writer.cpp mstflint-4.21.0+1/ext_libs/json/json_writer.cpp --- mstflint-4.17.0+1/ext_libs/json/json_writer.cpp 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/json_writer.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -1,841 +1,1259 @@ -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - +#if !defined(JSON_IS_AMALGAMATION) +#include "json_tool.h" #include -#include -#include -#include -#include -#include -#include +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include #include +#include +#include +#include +#include -#if _MSC_VER >= 1400 // VC++ 8.0 -#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. -#endif - -namespace Json { +#if __cplusplus >= 201103L +#include +#include -static bool isControlCharacter(char ch) -{ - return ch > 0 && ch <= 0x1F; -} - -static bool containsControlCharacter( const char* str ) -{ - while ( *str ) - { - if ( isControlCharacter( *(str++) ) ) - return true; - } - return false; -} -static void uintToString( unsigned int value, - char *¤t ) -{ - *--current = 0; - do - { - *--current = (value % 10) + '0'; - value /= 10; - } - while ( value != 0 ); -} - -std::string valueToString( Int value ) -{ - char buffer[32]; - char *current = buffer + sizeof(buffer); - bool isNegative = value < 0; - if ( isNegative ) - value = -value; - uintToString( UInt(value), current ); - if ( isNegative ) - *--current = '-'; - assert( current >= buffer ); - return current; -} +#if !defined(isnan) +#define isnan std::isnan +#endif +#if !defined(isfinite) +#define isfinite std::isfinite +#endif -std::string valueToString( UInt value ) -{ - char buffer[32]; - char *current = buffer + sizeof(buffer); - uintToString( value, current ); - assert( current >= buffer ); - return current; -} - -std::string valueToString( double value ) -{ - char buffer[32]; -#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning. - sprintf_s(buffer, sizeof(buffer), "%#.16g", value); #else - sprintf(buffer, "%#.16g", value); -#endif - char* ch = buffer + strlen(buffer) - 1; - if (*ch != '0') return buffer; // nothing to truncate, so save time - while(ch > buffer && *ch == '0'){ - --ch; - } - char* last_nonzero = ch; - while(ch >= buffer){ - switch(*ch){ - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - --ch; - continue; - case '.': - // Truncate zeroes to save bytes in output, but keep one. - *(last_nonzero+2) = '\0'; - return buffer; - default: - return buffer; - } - } - return buffer; -} - - -std::string valueToString( bool value ) -{ - return value ? "true" : "false"; -} - -std::string valueToQuotedString( const char *value ) -{ - // Not sure how to handle unicode... - if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value )) - return std::string("\"") + value + "\""; - // We have to walk value and escape any special characters. - // Appending to std::string is not efficient, but this should be rare. - // (Note: forward slashes are *not* rare, but I am not escaping them.) - unsigned maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL - std::string result; - result.reserve(maxsize); // to avoid lots of mallocs - result += "\""; - for (const char* c=value; *c != 0; ++c) - { - switch(*c) - { - case '\"': - result += "\\\""; - break; - case '\\': - result += "\\\\"; - break; - case '\b': - result += "\\b"; - break; - case '\f': - result += "\\f"; - break; - case '\n': - result += "\\n"; - break; - case '\r': - result += "\\r"; - break; - case '\t': - result += "\\t"; - break; - //case '/': - // Even though \/ is considered a legal escape in JSON, a bare - // slash is also legal, so I see no reason to escape it. - // (I hope I am not misunderstanding something. - // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); - result += oss.str(); - } - else - { - result += *c; - } - break; - } - } - result += "\""; - return result; -} +#include +#include -// Class Writer -// ////////////////////////////////////////////////////////////////// -Writer::~Writer() -{ -} +#if defined(_MSC_VER) +#if !defined(isnan) +#include +#define isnan _isnan +#endif +#if !defined(isfinite) +#include +#define isfinite _finite +#endif -// Class FastWriter -// ////////////////////////////////////////////////////////////////// +#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES) +#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 +#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES + +#endif //_MSC_VER + +#if defined(__sun) && defined(__SVR4) // Solaris +#if !defined(isfinite) +#include +#define isfinite finite +#endif +#endif -FastWriter::FastWriter() - : yamlCompatiblityEnabled_( false ) -{ -} +#if defined(__hpux) +#if !defined(isfinite) +#if defined(__ia64) && !defined(finite) +#define isfinite(x) \ + ((sizeof(x) == sizeof(float) ? _Isfinitef(x) : _IsFinite(x))) +#endif +#endif +#endif +#if !defined(isnan) +// IEEE standard states that NaN values will not compare to themselves +#define isnan(x) ((x) != (x)) +#endif -void -FastWriter::enableYAMLCompatibility() -{ - yamlCompatiblityEnabled_ = true; -} +#if !defined(__APPLE__) +#if !defined(isfinite) +#define isfinite finite +#endif +#endif +#endif +#if defined(_MSC_VER) +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif -std::string -FastWriter::write( const Value &root, bool noNewLine ) -{ - document_ = ""; - writeValue( root ); - if(!noNewLine) { - document_ += "\n"; - } - return document_; -} +namespace Json { +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) +using StreamWriterPtr = std::unique_ptr; +#else +using StreamWriterPtr = std::auto_ptr; +#endif -void -FastWriter::writeValue( const Value &value ) -{ - switch ( value.type() ) - { - case nullValue: - document_ += "null"; - break; - case intValue: - document_ += valueToString( value.asInt() ); - break; - case uintValue: - document_ += valueToString( value.asUInt() ); - break; - case realValue: - document_ += valueToString( value.asDouble() ); - break; - case stringValue: - document_ += valueToQuotedString( value.asCString() ); - break; - case booleanValue: - document_ += valueToString( value.asBool() ); - break; - case arrayValue: - { - document_ += "["; - int size = value.size(); - for ( int index =0; index < size; ++index ) - { - if ( index > 0 ) - document_ += ","; - writeValue( value[index] ); - } - document_ += "]"; +String valueToString(LargestInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + if (value == Value::minLargestInt) { + uintToString(LargestUInt(Value::maxLargestInt) + 1, current); + *--current = '-'; + } else if (value < 0) { + uintToString(LargestUInt(-value), current); + *--current = '-'; + } else { + uintToString(LargestUInt(value), current); + } + assert(current >= buffer); + return current; +} + +String valueToString(LargestUInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + uintToString(value, current); + assert(current >= buffer); + return current; +} + +#if defined(JSON_HAS_INT64) + +String valueToString(Int value) { return valueToString(LargestInt(value)); } + +String valueToString(UInt value) { return valueToString(LargestUInt(value)); } + +#endif // # if defined(JSON_HAS_INT64) + +namespace { +String valueToString(double value, bool useSpecialFloats, + unsigned int precision, PrecisionType precisionType) { + // Print into the buffer. We need not request the alternative representation + // that always has a decimal point because JSON doesn't distinguish the + // concepts of reals and integers. + if (!isfinite(value)) { + static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"}, + {"null", "-1e+9999", "1e+9999"}}; + return reps[useSpecialFloats ? 0 : 1] + [isnan(value) ? 0 : (value < 0) ? 1 : 2]; + } + + String buffer(size_t(36), '\0'); + while (true) { + int len = jsoncpp_snprintf( + &*buffer.begin(), buffer.size(), + (precisionType == PrecisionType::significantDigits) ? "%.*g" : "%.*f", + precision, value); + assert(len >= 0); + auto wouldPrint = static_cast(len); + if (wouldPrint >= buffer.size()) { + buffer.resize(wouldPrint + 1); + continue; + } + buffer.resize(wouldPrint); + break; + } + + buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end()); + + // try to ensure we preserve the fact that this was given to us as a double on + // input + if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) { + buffer += ".0"; + } + + // strip the zero padding from the right + if (precisionType == PrecisionType::decimalPlaces) { + buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end(), precision), + buffer.end()); + } + + return buffer; +} +} // namespace + +String valueToString(double value, unsigned int precision, + PrecisionType precisionType) { + return valueToString(value, false, precision, precisionType); +} + +String valueToString(bool value) { return value ? "true" : "false"; } + +static bool doesAnyCharRequireEscaping(char const* s, size_t n) { + assert(s || !n); + + return std::any_of(s, s + n, [](unsigned char c) { + return c == '\\' || c == '"' || c < 0x20 || c > 0x7F; + }); +} + +static unsigned int utf8ToCodepoint(const char*& s, const char* e) { + const unsigned int REPLACEMENT_CHARACTER = 0xFFFD; + + unsigned int firstByte = static_cast(*s); + + if (firstByte < 0x80) + return firstByte; + + if (firstByte < 0xE0) { + if (e - s < 2) + return REPLACEMENT_CHARACTER; + + unsigned int calculated = + ((firstByte & 0x1F) << 6) | (static_cast(s[1]) & 0x3F); + s += 1; + // oversized encoded characters are invalid + return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated; + } + + if (firstByte < 0xF0) { + if (e - s < 3) + return REPLACEMENT_CHARACTER; + + unsigned int calculated = ((firstByte & 0x0F) << 12) | + ((static_cast(s[1]) & 0x3F) << 6) | + (static_cast(s[2]) & 0x3F); + s += 2; + // surrogates aren't valid codepoints itself + // shouldn't be UTF-8 encoded + if (calculated >= 0xD800 && calculated <= 0xDFFF) + return REPLACEMENT_CHARACTER; + // oversized encoded characters are invalid + return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated; + } + + if (firstByte < 0xF8) { + if (e - s < 4) + return REPLACEMENT_CHARACTER; + + unsigned int calculated = ((firstByte & 0x07) << 18) | + ((static_cast(s[1]) & 0x3F) << 12) | + ((static_cast(s[2]) & 0x3F) << 6) | + (static_cast(s[3]) & 0x3F); + s += 3; + // oversized encoded characters are invalid + return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated; + } + + return REPLACEMENT_CHARACTER; +} + +static const char hex2[] = "000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f" + "505152535455565758595a5b5c5d5e5f" + "606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; + +static String toHex16Bit(unsigned int x) { + const unsigned int hi = (x >> 8) & 0xff; + const unsigned int lo = x & 0xff; + String result(4, ' '); + result[0] = hex2[2 * hi]; + result[1] = hex2[2 * hi + 1]; + result[2] = hex2[2 * lo]; + result[3] = hex2[2 * lo + 1]; + return result; +} + +static void appendRaw(String& result, unsigned ch) { + result += static_cast(ch); +} + +static void appendHex(String& result, unsigned ch) { + result.append("\\u").append(toHex16Bit(ch)); +} + +static String valueToQuotedStringN(const char* value, size_t length, + bool emitUTF8 = false) { + if (value == nullptr) + return ""; + + if (!doesAnyCharRequireEscaping(value, length)) + return String("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to String is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL + String result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + char const* end = value + length; + for (const char* c = value; c != end; ++c) { + switch (*c) { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + // case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something.) + // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); + if (codepoint < 0x20) { + appendHex(result, codepoint); + } else { + appendRaw(result, codepoint); + } + } else { + unsigned codepoint = utf8ToCodepoint(c, end); // modifies `c` + if (codepoint < 0x20) { + appendHex(result, codepoint); + } else if (codepoint < 0x80) { + appendRaw(result, codepoint); + } else if (codepoint < 0x10000) { + // Basic Multilingual Plane + appendHex(result, codepoint); + } else { + // Extended Unicode. Encode 20 bits as a surrogate pair. + codepoint -= 0x10000; + appendHex(result, 0xd800 + ((codepoint >> 10) & 0x3ff)); + appendHex(result, 0xdc00 + (codepoint & 0x3ff)); + } } - break; - case objectValue: - { - Value::Members members( value.getMemberNames() ); - document_ += "{"; - for ( Value::Members::iterator it = members.begin(); - it != members.end(); - ++it ) - { - const std::string &name = *it; - if ( it != members.begin() ) - document_ += ","; - document_ += valueToQuotedString( name.c_str() ); - document_ += yamlCompatiblityEnabled_ ? ": " - : ":"; - writeValue( value[name] ); - } - document_ += "}"; - } - break; - } + } break; + } + } + result += "\""; + return result; } - -// Class StyledWriter -// ////////////////////////////////////////////////////////////////// - -StyledWriter::StyledWriter() - : rightMargin_( 74 ) - , indentSize_( 3 ) -{ -} - - -std::string -StyledWriter::write( const Value &root, bool noNewLine) -{ - document_ = ""; - addChildValues_ = false; - indentString_ = ""; - writeCommentBeforeValue( root ); - writeValue( root ); - writeCommentAfterValueOnSameLine( root ); - if(!noNewLine) { - document_ += "\n"; - } - return document_; +String valueToQuotedString(const char* value) { + return valueToQuotedStringN(value, strlen(value)); } +// Class Writer +// ////////////////////////////////////////////////////////////////// +Writer::~Writer() = default; -void -StyledWriter::writeValue( const Value &value ) -{ - switch ( value.type() ) - { - case nullValue: - pushValue( "null" ); - break; - case intValue: - pushValue( valueToString( value.asInt() ) ); - break; - case uintValue: - pushValue( valueToString( value.asUInt() ) ); - break; - case realValue: - pushValue( valueToString( value.asDouble() ) ); - break; - case stringValue: - pushValue( valueToQuotedString( value.asCString() ) ); - break; - case booleanValue: - pushValue( valueToString( value.asBool() ) ); - break; - case arrayValue: - writeArrayValue( value); - break; - case objectValue: - { - Value::Members members( value.getMemberNames() ); - if ( members.empty() ) - pushValue( "{}" ); - else - { - writeWithIndent( "{" ); - indent(); - Value::Members::iterator it = members.begin(); - while ( true ) - { - const std::string &name = *it; - const Value &childValue = value[name]; - writeCommentBeforeValue( childValue ); - writeWithIndent( valueToQuotedString( name.c_str() ) ); - document_ += " : "; - writeValue( childValue ); - if ( ++it == members.end() ) - { - writeCommentAfterValueOnSameLine( childValue ); - break; - } - document_ += ","; - writeCommentAfterValueOnSameLine( childValue ); - } - unindent(); - writeWithIndent( "}" ); - } - } - break; - } -} +// Class FastWriter +// ////////////////////////////////////////////////////////////////// +FastWriter::FastWriter() -void -StyledWriter::writeArrayValue( const Value &value ) -{ - unsigned size = value.size(); - if ( size == 0 ) - pushValue( "[]" ); - else - { - bool isArrayMultiLine = isMultineArray( value ); - if ( isArrayMultiLine ) - { - writeWithIndent( "[" ); - indent(); - bool hasChildValue = !childValues_.empty(); - unsigned index =0; - while ( true ) - { - const Value &childValue = value[index]; - writeCommentBeforeValue( childValue ); - if ( hasChildValue ) - writeWithIndent( childValues_[index] ); - else - { - writeIndent(); - writeValue( childValue ); - } - if ( ++index == size ) - { - writeCommentAfterValueOnSameLine( childValue ); - break; - } - document_ += ","; - writeCommentAfterValueOnSameLine( childValue ); - } - unindent(); - writeWithIndent( "]" ); - } - else // output on a single line - { - assert( childValues_.size() == size ); - document_ += "[ "; - for ( unsigned index =0; index < size; ++index ) - { - if ( index > 0 ) - document_ += ", "; - document_ += childValues_[index]; - } - document_ += " ]"; - } - } -} - - -bool -StyledWriter::isMultineArray( const Value &value ) -{ - int size = value.size(); - bool isMultiLine = size*3 >= rightMargin_ ; - childValues_.clear(); - for ( int index =0; index < size && !isMultiLine; ++index ) - { - const Value &childValue = value[index]; - isMultiLine = isMultiLine || - ( (childValue.isArray() || childValue.isObject()) && - childValue.size() > 0 ); - } - if ( !isMultiLine ) // check if line length > max line length - { - childValues_.reserve( size ); - addChildValues_ = true; - int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' - for ( int index =0; index < size && !isMultiLine; ++index ) - { - writeValue( value[index] ); - lineLength += int( childValues_[index].length() ); - isMultiLine = isMultiLine && hasCommentForValue( value[index] ); - } - addChildValues_ = false; - isMultiLine = isMultiLine || lineLength >= rightMargin_; - } - return isMultiLine; -} - - -void -StyledWriter::pushValue( const std::string &value ) -{ - if ( addChildValues_ ) - childValues_.push_back( value ); - else - document_ += value; -} - - -void -StyledWriter::writeIndent() -{ - if ( !document_.empty() ) - { - char last = document_[document_.length()-1]; - if ( last == ' ' ) // already indented - return; - if ( last != '\n' ) // Comments may add new-line - document_ += '\n'; - } - document_ += indentString_; -} - - -void -StyledWriter::writeWithIndent( const std::string &value ) -{ - writeIndent(); - document_ += value; -} - - -void -StyledWriter::indent() -{ - indentString_ += std::string( indentSize_, ' ' ); -} - - -void -StyledWriter::unindent() -{ - assert( int(indentString_.size()) >= indentSize_ ); - indentString_.resize( indentString_.size() - indentSize_ ); -} - - -void -StyledWriter::writeCommentBeforeValue( const Value &root ) -{ - if ( !root.hasComment( commentBefore ) ) - return; - document_ += normalizeEOL( root.getComment( commentBefore ) ); - document_ += "\n"; -} + = default; +void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; } -void -StyledWriter::writeCommentAfterValueOnSameLine( const Value &root ) -{ - if ( root.hasComment( commentAfterOnSameLine ) ) - document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); - - if ( root.hasComment( commentAfter ) ) - { - document_ += "\n"; - document_ += normalizeEOL( root.getComment( commentAfter ) ); - document_ += "\n"; - } -} +void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; } +void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; } -bool -StyledWriter::hasCommentForValue( const Value &value ) -{ - return value.hasComment( commentBefore ) - || value.hasComment( commentAfterOnSameLine ) - || value.hasComment( commentAfter ); +String FastWriter::write(const Value& root) { + document_.clear(); + writeValue(root); + if (!omitEndingLineFeed_) + document_ += '\n'; + return document_; } - -std::string -StyledWriter::normalizeEOL( const std::string &text ) -{ - std::string normalized; - normalized.reserve( text.length() ); - const char *begin = text.c_str(); - const char *end = begin + text.length(); - const char *current = begin; - while ( current != end ) - { - char c = *current++; - if ( c == '\r' ) // mac or dos EOL - { - if ( *current == '\n' ) // convert dos EOL - ++current; - normalized += '\n'; - } - else // handle unix EOL & other char - normalized += c; - } - return normalized; +void FastWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + if (!dropNullPlaceholders_) + document_ += "null"; + break; + case intValue: + document_ += valueToString(value.asLargestInt()); + break; + case uintValue: + document_ += valueToString(value.asLargestUInt()); + break; + case realValue: + document_ += valueToString(value.asDouble()); + break; + case stringValue: { + // Is NULL possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) + document_ += valueToQuotedStringN(str, static_cast(end - str)); + break; + } + case booleanValue: + document_ += valueToString(value.asBool()); + break; + case arrayValue: { + document_ += '['; + ArrayIndex size = value.size(); + for (ArrayIndex index = 0; index < size; ++index) { + if (index > 0) + document_ += ','; + writeValue(value[index]); + } + document_ += ']'; + } break; + case objectValue: { + Value::Members members(value.getMemberNames()); + document_ += '{'; + for (auto it = members.begin(); it != members.end(); ++it) { + const String& name = *it; + if (it != members.begin()) + document_ += ','; + document_ += valueToQuotedStringN(name.data(), name.length()); + document_ += yamlCompatibilityEnabled_ ? ": " : ":"; + writeValue(value[name]); + } + document_ += '}'; + } break; + } } - -// Class StyledStreamWriter +// Class StyledWriter // ////////////////////////////////////////////////////////////////// -StyledStreamWriter::StyledStreamWriter( std::string indentation ) - : document_(NULL) - , rightMargin_( 74 ) - , indentation_( indentation ) -{ -} - +StyledWriter::StyledWriter() = default; -void -StyledStreamWriter::write( std::ostream &out, const Value &root, bool noNewLine) -{ - document_ = &out; - addChildValues_ = false; - indentString_ = ""; - writeCommentBeforeValue( root ); - writeValue( root ); - writeCommentAfterValueOnSameLine( root ); - if(!noNewLine) { - *document_ << "\n"; - } - document_ = NULL; // Forget the stream, for safety. +String StyledWriter::write(const Value& root) { + document_.clear(); + addChildValues_ = false; + indentString_.clear(); + writeCommentBeforeValue(root); + writeValue(root); + writeCommentAfterValueOnSameLine(root); + document_ += '\n'; + return document_; +} + +void StyledWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: { + // Is NULL possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) + pushValue(valueToQuotedStringN(str, static_cast(end - str))); + else + pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + auto it = members.begin(); + for (;;) { + const String& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + document_ += " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ','; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void StyledWriter::writeArrayValue(const Value& value) { + size_t size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultilineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + ArrayIndex index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + writeIndent(); + writeValue(childValue); + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ','; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + document_ += "[ "; + for (size_t index = 0; index < size; ++index) { + if (index > 0) + document_ += ", "; + document_ += childValues_[index]; + } + document_ += " ]"; + } + } +} + +bool StyledWriter::isMultilineArray(const Value& value) { + ArrayIndex const size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + !childValue.empty()); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += static_cast(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledWriter::pushValue(const String& value) { + if (addChildValues_) + childValues_.push_back(value); + else + document_ += value; +} + +void StyledWriter::writeIndent() { + if (!document_.empty()) { + char last = document_[document_.length() - 1]; + if (last == ' ') // already indented + return; + if (last != '\n') // Comments may add new-line + document_ += '\n'; + } + document_ += indentString_; } - -void -StyledStreamWriter::writeValue( const Value &value ) -{ - switch ( value.type() ) - { - case nullValue: - pushValue( "null" ); - break; - case intValue: - pushValue( valueToString( value.asInt() ) ); - break; - case uintValue: - pushValue( valueToString( value.asUInt() ) ); - break; - case realValue: - pushValue( valueToString( value.asDouble() ) ); - break; - case stringValue: - pushValue( valueToQuotedString( value.asCString() ) ); - break; - case booleanValue: - pushValue( valueToString( value.asBool() ) ); - break; - case arrayValue: - writeArrayValue( value); - break; - case objectValue: - { - Value::Members members( value.getMemberNames() ); - if ( members.empty() ) - pushValue( "{}" ); - else - { - writeWithIndent( "{" ); - indent(); - Value::Members::iterator it = members.begin(); - while ( true ) - { - const std::string &name = *it; - const Value &childValue = value[name]; - writeCommentBeforeValue( childValue ); - writeWithIndent( valueToQuotedString( name.c_str() ) ); - *document_ << " : "; - writeValue( childValue ); - if ( ++it == members.end() ) - { - writeCommentAfterValueOnSameLine( childValue ); - break; - } - *document_ << ","; - writeCommentAfterValueOnSameLine( childValue ); - } - unindent(); - writeWithIndent( "}" ); - } - } - break; - } +void StyledWriter::writeWithIndent(const String& value) { + writeIndent(); + document_ += value; } +void StyledWriter::indent() { indentString_ += String(indentSize_, ' '); } -void -StyledStreamWriter::writeArrayValue( const Value &value ) -{ - unsigned size = value.size(); - if ( size == 0 ) - pushValue( "[]" ); - else - { - bool isArrayMultiLine = isMultineArray( value ); - if ( isArrayMultiLine ) - { - writeWithIndent( "[" ); - indent(); - bool hasChildValue = !childValues_.empty(); - unsigned index =0; - while ( true ) - { - const Value &childValue = value[index]; - writeCommentBeforeValue( childValue ); - if ( hasChildValue ) - writeWithIndent( childValues_[index] ); - else - { - writeIndent(); - writeValue( childValue ); - } - if ( ++index == size ) - { - writeCommentAfterValueOnSameLine( childValue ); - break; - } - *document_ << ","; - writeCommentAfterValueOnSameLine( childValue ); - } - unindent(); - writeWithIndent( "]" ); - } - else // output on a single line - { - assert( childValues_.size() == size ); - *document_ << "[ "; - for ( unsigned index =0; index < size; ++index ) - { - if ( index > 0 ) - *document_ << ", "; - *document_ << childValues_[index]; - } - *document_ << " ]"; - } - } -} - - -bool -StyledStreamWriter::isMultineArray( const Value &value ) -{ - int size = value.size(); - bool isMultiLine = size*3 >= rightMargin_ ; - childValues_.clear(); - for ( int index =0; index < size && !isMultiLine; ++index ) - { - const Value &childValue = value[index]; - isMultiLine = isMultiLine || - ( (childValue.isArray() || childValue.isObject()) && - childValue.size() > 0 ); - } - if ( !isMultiLine ) // check if line length > max line length - { - childValues_.reserve( size ); - addChildValues_ = true; - int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' - for ( int index =0; index < size && !isMultiLine; ++index ) - { - writeValue( value[index] ); - lineLength += int( childValues_[index].length() ); - isMultiLine = isMultiLine && hasCommentForValue( value[index] ); - } - addChildValues_ = false; - isMultiLine = isMultiLine || lineLength >= rightMargin_; - } - return isMultiLine; -} - - -void -StyledStreamWriter::pushValue( const std::string &value ) -{ - if ( addChildValues_ ) - childValues_.push_back( value ); - else - *document_ << value; -} - - -void -StyledStreamWriter::writeIndent() -{ - /* - Some comments in this method would have been nice. ;-) - - if ( !document_.empty() ) - { - char last = document_[document_.length()-1]; - if ( last == ' ' ) // already indented - return; - if ( last != '\n' ) // Comments may add new-line - *document_ << '\n'; - } - */ - *document_ << '\n' << indentString_; -} - - -void -StyledStreamWriter::writeWithIndent( const std::string &value ) -{ - writeIndent(); - *document_ << value; -} - - -void -StyledStreamWriter::indent() -{ - indentString_ += indentation_; -} - - -void -StyledStreamWriter::unindent() -{ - assert( indentString_.size() >= indentation_.size() ); - indentString_.resize( indentString_.size() - indentation_.size() ); -} - - -void -StyledStreamWriter::writeCommentBeforeValue( const Value &root ) -{ - if ( !root.hasComment( commentBefore ) ) - return; - *document_ << normalizeEOL( root.getComment( commentBefore ) ); - *document_ << "\n"; +void StyledWriter::unindent() { + assert(indentString_.size() >= indentSize_); + indentString_.resize(indentString_.size() - indentSize_); } +void StyledWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; -void -StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root ) -{ - if ( root.hasComment( commentAfterOnSameLine ) ) - *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); - - if ( root.hasComment( commentAfter ) ) - { - *document_ << "\n"; - *document_ << normalizeEOL( root.getComment( commentAfter ) ); - *document_ << "\n"; - } + document_ += '\n'; + writeIndent(); + const String& comment = root.getComment(commentBefore); + String::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + document_ += *iter; + if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) + writeIndent(); + ++iter; + } + + // Comments are stripped of trailing newlines, so add one here + document_ += '\n'; } +void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + document_ += " " + root.getComment(commentAfterOnSameLine); -bool -StyledStreamWriter::hasCommentForValue( const Value &value ) -{ - return value.hasComment( commentBefore ) - || value.hasComment( commentAfterOnSameLine ) - || value.hasComment( commentAfter ); + if (root.hasComment(commentAfter)) { + document_ += '\n'; + document_ += root.getComment(commentAfter); + document_ += '\n'; + } } - -std::string -StyledStreamWriter::normalizeEOL( const std::string &text ) -{ - std::string normalized; - normalized.reserve( text.length() ); - const char *begin = text.c_str(); - const char *end = begin + text.length(); - const char *current = begin; - while ( current != end ) - { - char c = *current++; - if ( c == '\r' ) // mac or dos EOL - { - if ( *current == '\n' ) // convert dos EOL - ++current; - normalized += '\n'; - } - else // handle unix EOL & other char - normalized += c; - } - return normalized; +bool StyledWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); } +// Class StyledStreamWriter +// ////////////////////////////////////////////////////////////////// -std::ostream& operator<<( std::ostream &sout, const Value &root ) -{ - Json::StyledStreamWriter writer; - writer.write(sout, root); - return sout; +StyledStreamWriter::StyledStreamWriter(String indentation) + : document_(nullptr), indentation_(std::move(indentation)), + addChildValues_(), indented_(false) {} + +void StyledStreamWriter::write(OStream& out, const Value& root) { + document_ = &out; + addChildValues_ = false; + indentString_.clear(); + indented_ = true; + writeCommentBeforeValue(root); + if (!indented_) + writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *document_ << "\n"; + document_ = nullptr; // Forget the stream, for safety. +} + +void StyledStreamWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: { + // Is NULL possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) + pushValue(valueToQuotedStringN(str, static_cast(end - str))); + else + pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + auto it = members.begin(); + for (;;) { + const String& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + *document_ << " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void StyledStreamWriter::writeArrayValue(const Value& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultilineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + if (!indented_) + writeIndent(); + indented_ = true; + writeValue(childValue); + indented_ = false; + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *document_ << "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *document_ << ", "; + *document_ << childValues_[index]; + } + *document_ << " ]"; + } + } +} + +bool StyledStreamWriter::isMultilineArray(const Value& value) { + ArrayIndex const size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + !childValue.empty()); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += static_cast(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledStreamWriter::pushValue(const String& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *document_ << value; +} + +void StyledStreamWriter::writeIndent() { + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. + *document_ << '\n' << indentString_; +} + +void StyledStreamWriter::writeWithIndent(const String& value) { + if (!indented_) + writeIndent(); + *document_ << value; + indented_ = false; +} + +void StyledStreamWriter::indent() { indentString_ += indentation_; } + +void StyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) + writeIndent(); + const String& comment = root.getComment(commentBefore); + String::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *document_ << *iter; + if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would include newline + *document_ << indentString_; + ++iter; + } + indented_ = false; +} + +void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + *document_ << ' ' << root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *document_ << root.getComment(commentAfter); + } + indented_ = false; +} + +bool StyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +////////////////////////// +// BuiltStyledStreamWriter + +/// Scoped enums are not available until C++11. +struct CommentStyle { + /// Decide whether to write comments. + enum Enum { + None, ///< Drop all comments. + Most, ///< Recover odd behavior of previous versions (not implemented yet). + All ///< Keep all comments. + }; +}; + +struct BuiltStyledStreamWriter : public StreamWriter { + BuiltStyledStreamWriter(String indentation, CommentStyle::Enum cs, + String colonSymbol, String nullSymbol, + String endingLineFeedSymbol, bool useSpecialFloats, + bool emitUTF8, unsigned int precision, + PrecisionType precisionType); + int write(Value const& root, OStream* sout) override; + +private: + void writeValue(Value const& value); + void writeArrayValue(Value const& value); + bool isMultilineArray(Value const& value); + void pushValue(String const& value); + void writeIndent(); + void writeWithIndent(String const& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(Value const& root); + void writeCommentAfterValueOnSameLine(Value const& root); + static bool hasCommentForValue(const Value& value); + + using ChildValues = std::vector; + + ChildValues childValues_; + String indentString_; + unsigned int rightMargin_; + String indentation_; + CommentStyle::Enum cs_; + String colonSymbol_; + String nullSymbol_; + String endingLineFeedSymbol_; + bool addChildValues_ : 1; + bool indented_ : 1; + bool useSpecialFloats_ : 1; + bool emitUTF8_ : 1; + unsigned int precision_; + PrecisionType precisionType_; +}; +BuiltStyledStreamWriter::BuiltStyledStreamWriter( + String indentation, CommentStyle::Enum cs, String colonSymbol, + String nullSymbol, String endingLineFeedSymbol, bool useSpecialFloats, + bool emitUTF8, unsigned int precision, PrecisionType precisionType) + : rightMargin_(74), indentation_(std::move(indentation)), cs_(cs), + colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)), + endingLineFeedSymbol_(std::move(endingLineFeedSymbol)), + addChildValues_(false), indented_(false), + useSpecialFloats_(useSpecialFloats), emitUTF8_(emitUTF8), + precision_(precision), precisionType_(precisionType) {} +int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) { + sout_ = sout; + addChildValues_ = false; + indented_ = true; + indentString_.clear(); + writeCommentBeforeValue(root); + if (!indented_) + writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *sout_ << endingLineFeedSymbol_; + sout_ = nullptr; + return 0; +} +void BuiltStyledStreamWriter::writeValue(Value const& value) { + switch (value.type()) { + case nullValue: + pushValue(nullSymbol_); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_, + precisionType_)); + break; + case stringValue: { + // Is NULL is possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) + pushValue( + valueToQuotedStringN(str, static_cast(end - str), emitUTF8_)); + else + pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + auto it = members.begin(); + for (;;) { + String const& name = *it; + Value const& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent( + valueToQuotedStringN(name.data(), name.length(), emitUTF8_)); + *sout_ << colonSymbol_; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value); + if (isMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + Value const& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + if (!indented_) + writeIndent(); + indented_ = true; + writeValue(childValue); + indented_ = false; + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *sout_ << "["; + if (!indentation_.empty()) + *sout_ << " "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *sout_ << ((!indentation_.empty()) ? ", " : ","); + *sout_ << childValues_[index]; + } + if (!indentation_.empty()) + *sout_ << " "; + *sout_ << "]"; + } + } +} + +bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) { + ArrayIndex const size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { + Value const& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + !childValue.empty()); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += static_cast(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void BuiltStyledStreamWriter::pushValue(String const& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *sout_ << value; +} + +void BuiltStyledStreamWriter::writeIndent() { + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. + + if (!indentation_.empty()) { + // In this case, drop newlines too. + *sout_ << '\n' << indentString_; + } +} + +void BuiltStyledStreamWriter::writeWithIndent(String const& value) { + if (!indented_) + writeIndent(); + *sout_ << value; + indented_ = false; +} + +void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } + +void BuiltStyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { + if (cs_ == CommentStyle::None) + return; + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) + writeIndent(); + const String& comment = root.getComment(commentBefore); + String::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *sout_ << *iter; + if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would write extra newline + *sout_ << indentString_; + ++iter; + } + indented_ = false; +} + +void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine( + Value const& root) { + if (cs_ == CommentStyle::None) + return; + if (root.hasComment(commentAfterOnSameLine)) + *sout_ << " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *sout_ << root.getComment(commentAfter); + } +} + +// static +bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +/////////////// +// StreamWriter + +StreamWriter::StreamWriter() : sout_(nullptr) {} +StreamWriter::~StreamWriter() = default; +StreamWriter::Factory::~Factory() = default; +StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); } +StreamWriterBuilder::~StreamWriterBuilder() = default; +StreamWriter* StreamWriterBuilder::newStreamWriter() const { + const String indentation = settings_["indentation"].asString(); + const String cs_str = settings_["commentStyle"].asString(); + const String pt_str = settings_["precisionType"].asString(); + const bool eyc = settings_["enableYAMLCompatibility"].asBool(); + const bool dnp = settings_["dropNullPlaceholders"].asBool(); + const bool usf = settings_["useSpecialFloats"].asBool(); + const bool emitUTF8 = settings_["emitUTF8"].asBool(); + unsigned int pre = settings_["precision"].asUInt(); + CommentStyle::Enum cs = CommentStyle::All; + if (cs_str == "All") { + cs = CommentStyle::All; + } else if (cs_str == "None") { + cs = CommentStyle::None; + } else { + throwRuntimeError("commentStyle must be 'All' or 'None'"); + } + PrecisionType precisionType(significantDigits); + if (pt_str == "significant") { + precisionType = PrecisionType::significantDigits; + } else if (pt_str == "decimal") { + precisionType = PrecisionType::decimalPlaces; + } else { + throwRuntimeError("precisionType must be 'significant' or 'decimal'"); + } + String colonSymbol = " : "; + if (eyc) { + colonSymbol = ": "; + } else if (indentation.empty()) { + colonSymbol = ":"; + } + String nullSymbol = "null"; + if (dnp) { + nullSymbol.clear(); + } + if (pre > 17) + pre = 17; + String endingLineFeedSymbol; + return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol, + endingLineFeedSymbol, usf, emitUTF8, pre, + precisionType); +} + +bool StreamWriterBuilder::validate(Json::Value* invalid) const { + static const auto& valid_keys = *new std::set{ + "indentation", + "commentStyle", + "enableYAMLCompatibility", + "dropNullPlaceholders", + "useSpecialFloats", + "emitUTF8", + "precision", + "precisionType", + }; + for (auto si = settings_.begin(); si != settings_.end(); ++si) { + auto key = si.name(); + if (valid_keys.count(key)) + continue; + if (invalid) + (*invalid)[key] = *si; + else + return false; + } + return invalid ? invalid->empty() : true; +} + +Value& StreamWriterBuilder::operator[](const String& key) { + return settings_[key]; +} +// static +void StreamWriterBuilder::setDefaults(Json::Value* settings) { + //! [StreamWriterBuilderDefaults] + (*settings)["commentStyle"] = "All"; + (*settings)["indentation"] = "\t"; + (*settings)["enableYAMLCompatibility"] = false; + (*settings)["dropNullPlaceholders"] = false; + (*settings)["useSpecialFloats"] = false; + (*settings)["emitUTF8"] = false; + (*settings)["precision"] = 17; + (*settings)["precisionType"] = "significant"; + //! [StreamWriterBuilderDefaults] +} + +String writeString(StreamWriter::Factory const& factory, Value const& root) { + OStringStream sout; + StreamWriterPtr const writer(factory.newStreamWriter()); + writer->write(root, &sout); + return sout.str(); +} + +OStream& operator<<(OStream& sout, Value const& root) { + StreamWriterBuilder builder; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); + return sout; } - } // namespace Json diff -Nru mstflint-4.17.0+1/ext_libs/json/Makefile.am mstflint-4.21.0+1/ext_libs/json/Makefile.am --- mstflint-4.17.0+1/ext_libs/json/Makefile.am 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/Makefile.am 2022-07-31 09:33:57.000000000 +0000 @@ -1,5 +1,6 @@ #-- # Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved. +# Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU @@ -34,11 +35,11 @@ USER_DIR = ../ INCLUDES = -AM_CPPFLAGS = -W -Werror -g -MP -MD ${COMPILER_FPIC} -noinst_LTLIBRARIES = libjson.a -libjson_a_SOURCES = json_reader.cpp json_value.cpp json_batchallocator.h json_writer.cpp json/*.h +AM_CPPFLAGS = -W -Werror -g -MP -MD ${COMPILER_FPIC} -Wno-missing-field-initializers -Wno-unused-parameter +noinst_LTLIBRARIES = libjson.la +libjson_la_SOURCES = json_reader.cpp json_value.cpp json_tool.h json_writer.cpp json/*.h -libjson_a_DEPENDENCIES = -libjson_a_LIBADD = +libjson_la_DEPENDENCIES = +libjson_la_LIBADD = EXTRA_DIST = *.inl diff -Nru mstflint-4.17.0+1/ext_libs/json/Makefile.in mstflint-4.21.0+1/ext_libs/json/Makefile.in --- mstflint-4.17.0+1/ext_libs/json/Makefile.in 2021-06-29 08:51:08.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/json/Makefile.in 2022-07-31 09:34:41.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.3 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2020 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -16,6 +16,7 @@ #-- # Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved. +# Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU @@ -134,8 +135,8 @@ CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) -am_libjson_a_OBJECTS = json_reader.lo json_value.lo json_writer.lo -libjson_a_OBJECTS = $(am_libjson_a_OBJECTS) +am_libjson_la_OBJECTS = json_reader.lo json_value.lo json_writer.lo +libjson_la_OBJECTS = $(am_libjson_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent @@ -194,8 +195,8 @@ am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = -SOURCES = $(libjson_a_SOURCES) -DIST_SOURCES = $(libjson_a_SOURCES) +SOURCES = $(libjson_la_SOURCES) +DIST_SOURCES = $(libjson_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ @@ -374,11 +375,11 @@ top_srcdir = @top_srcdir@ USER_DIR = ../ INCLUDES = -AM_CPPFLAGS = -W -Werror -g -MP -MD ${COMPILER_FPIC} -noinst_LTLIBRARIES = libjson.a -libjson_a_SOURCES = json_reader.cpp json_value.cpp json_batchallocator.h json_writer.cpp json/*.h -libjson_a_DEPENDENCIES = -libjson_a_LIBADD = +AM_CPPFLAGS = -W -Werror -g -MP -MD ${COMPILER_FPIC} -Wno-missing-field-initializers -Wno-unused-parameter +noinst_LTLIBRARIES = libjson.la +libjson_la_SOURCES = json_reader.cpp json_value.cpp json_tool.h json_writer.cpp json/*.h +libjson_la_DEPENDENCIES = +libjson_la_LIBADD = EXTRA_DIST = *.inl all: all-am @@ -425,8 +426,8 @@ rm -f $${locs}; \ } -libjson.a: $(libjson_a_OBJECTS) $(libjson_a_DEPENDENCIES) $(EXTRA_libjson_a_DEPENDENCIES) - $(AM_V_CXXLD)$(CXXLINK) $(libjson_a_OBJECTS) $(libjson_a_LIBADD) $(LIBS) +libjson.la: $(libjson_la_OBJECTS) $(libjson_la_DEPENDENCIES) $(EXTRA_libjson_la_DEPENDENCIES) + $(AM_V_CXXLD)$(CXXLINK) $(libjson_la_OBJECTS) $(libjson_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) diff -Nru mstflint-4.17.0+1/ext_libs/Makefile.am mstflint-4.21.0+1/ext_libs/Makefile.am --- mstflint-4.17.0+1/ext_libs/Makefile.am 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/Makefile.am 2022-04-28 11:49:49.000000000 +0000 @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved. +# Copyright (c) 2004-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU diff -Nru mstflint-4.17.0+1/ext_libs/Makefile.in mstflint-4.21.0+1/ext_libs/Makefile.in --- mstflint-4.17.0+1/ext_libs/Makefile.in 2021-06-29 08:51:07.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/Makefile.in 2022-07-31 09:34:41.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.3 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2020 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -15,7 +15,7 @@ @SET_MAKE@ #-- -# Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved. +# Copyright (c) 2004-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU diff -Nru mstflint-4.17.0+1/ext_libs/minixz/Makefile.in mstflint-4.21.0+1/ext_libs/minixz/Makefile.in --- mstflint-4.17.0+1/ext_libs/minixz/Makefile.in 2021-06-29 08:51:08.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/minixz/Makefile.in 2022-07-31 09:34:41.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.3 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2020 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, diff -Nru mstflint-4.17.0+1/ext_libs/minixz/xz_config.h mstflint-4.21.0+1/ext_libs/minixz/xz_config.h --- mstflint-4.17.0+1/ext_libs/minixz/xz_config.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/minixz/xz_config.h 2022-07-31 09:33:57.000000000 +0000 @@ -65,7 +65,7 @@ #define memzero(buf, size) memset(buf, 0, size) #ifndef min -# define min(x, y) ((x) < (y) ? (x) : (y)) +#define min(x, y) ((x) < (y) ? (x) : (y)) #endif #define min_t(type, x, y) min(x, y) @@ -80,52 +80,45 @@ * so if you want to change it, you need to #undef it first. */ #ifndef __always_inline -# ifdef __GNUC__ -# define __always_inline \ - inline __attribute__((__always_inline__)) -# else -# define __always_inline inline -# endif +#ifdef __GNUC__ +#define __always_inline inline __attribute__((__always_inline__)) +#else +#define __always_inline inline +#endif #endif /* Inline functions to access unaligned unsigned 32-bit integers */ #ifndef get_unaligned_le32 -static inline uint32_t get_unaligned_le32(const uint8_t *buf) +static inline uint32_t get_unaligned_le32(const uint8_t* buf) { - return (uint32_t)buf[0] - | ((uint32_t)buf[1] << 8) - | ((uint32_t)buf[2] << 16) - | ((uint32_t)buf[3] << 24); + return (uint32_t)buf[0] | ((uint32_t)buf[1] << 8) | ((uint32_t)buf[2] << 16) | ((uint32_t)buf[3] << 24); } #endif #ifndef get_unaligned_be32 -static inline uint32_t get_unaligned_be32(const uint8_t *buf) +static inline uint32_t get_unaligned_be32(const uint8_t* buf) { - return (uint32_t)(buf[0] << 24) - | ((uint32_t)buf[1] << 16) - | ((uint32_t)buf[2] << 8) - | (uint32_t)buf[3]; + return (uint32_t)(buf[0] << 24) | ((uint32_t)buf[1] << 16) | ((uint32_t)buf[2] << 8) | (uint32_t)buf[3]; } #endif #ifndef put_unaligned_le32 -static inline void put_unaligned_le32(uint32_t val, uint8_t *buf) +static inline void put_unaligned_le32(uint32_t val, uint8_t* buf) { - buf[0] = (uint8_t)val; - buf[1] = (uint8_t)(val >> 8); - buf[2] = (uint8_t)(val >> 16); - buf[3] = (uint8_t)(val >> 24); + buf[0] = (uint8_t)val; + buf[1] = (uint8_t)(val >> 8); + buf[2] = (uint8_t)(val >> 16); + buf[3] = (uint8_t)(val >> 24); } #endif #ifndef put_unaligned_be32 -static inline void put_unaligned_be32(uint32_t val, uint8_t *buf) +static inline void put_unaligned_be32(uint32_t val, uint8_t* buf) { - buf[0] = (uint8_t)(val >> 24); - buf[1] = (uint8_t)(val >> 16); - buf[2] = (uint8_t)(val >> 8); - buf[3] = (uint8_t)val; + buf[0] = (uint8_t)(val >> 24); + buf[1] = (uint8_t)(val >> 16); + buf[2] = (uint8_t)(val >> 8); + buf[3] = (uint8_t)val; } #endif @@ -135,7 +128,7 @@ * could save a few bytes in code size. */ #ifndef get_le32 -# define get_le32 get_unaligned_le32 +#define get_le32 get_unaligned_le32 #endif #endif diff -Nru mstflint-4.17.0+1/ext_libs/minixz/xz_crc32.c mstflint-4.21.0+1/ext_libs/minixz/xz_crc32.c --- mstflint-4.17.0+1/ext_libs/minixz/xz_crc32.c 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/minixz/xz_crc32.c 2022-07-31 09:33:57.000000000 +0000 @@ -54,38 +54,40 @@ * See for details. */ #ifndef STATIC_RW_DATA -# define STATIC_RW_DATA static +#define STATIC_RW_DATA static #endif STATIC_RW_DATA uint32_t xz_crc32_table[256]; XZ_EXTERN void xz_crc32_init(void) { - const uint32_t poly = 0xEDB88320; + const uint32_t poly = 0xEDB88320; - uint32_t i; - uint32_t j; - uint32_t r; + uint32_t i; + uint32_t j; + uint32_t r; + + for (i = 0; i < 256; ++i) + { + r = i; + for (j = 0; j < 8; ++j) + r = (r >> 1) ^ (poly & ~((r & 1) - 1)); - for (i = 0; i < 256; ++i) { - r = i; - for (j = 0; j < 8; ++j) - r = (r >> 1) ^ (poly & ~((r & 1) - 1)); + xz_crc32_table[i] = r; + } - xz_crc32_table[i] = r; - } - - return; + return; } -XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc) +XZ_EXTERN uint32_t xz_crc32(const uint8_t* buf, size_t size, uint32_t crc) { - crc = ~crc; + crc = ~crc; - while (size != 0) { - crc = xz_crc32_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8); - --size; - } + while (size != 0) + { + crc = xz_crc32_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8); + --size; + } - return ~crc; + return ~crc; } diff -Nru mstflint-4.17.0+1/ext_libs/minixz/xz_dec_bcj.c mstflint-4.21.0+1/ext_libs/minixz/xz_dec_bcj.c --- mstflint-4.17.0+1/ext_libs/minixz/xz_dec_bcj.c 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/minixz/xz_dec_bcj.c 2022-07-31 09:33:57.000000000 +0000 @@ -49,63 +49,66 @@ */ #ifdef XZ_DEC_BCJ -struct xz_dec_bcj { - /* Type of the BCJ filter being used */ - enum { - BCJ_X86 = 4, /* x86 or x86-64 */ - BCJ_POWERPC = 5, /* Big endian only */ - BCJ_IA64 = 6, /* Big or little endian */ - BCJ_ARM = 7, /* Little endian only */ - BCJ_ARMTHUMB = 8, /* Little endian only */ - BCJ_SPARC = 9 /* Big or little endian */ - } type; - - /* - * Return value of the next filter in the chain. We need to preserve - * this information across calls, because we must not call the next - * filter anymore once it has returned XZ_STREAM_END. - */ - enum xz_ret ret; - - /* True if we are operating in single-call mode. */ - bool single_call; - - /* - * Absolute position relative to the beginning of the uncompressed - * data (in a single .xz Block). We care only about the lowest 32 - * bits so this doesn't need to be uint64_t even with big files. - */ - uint32_t pos; - - /* x86 filter state */ - uint32_t x86_prev_mask; - - /* Temporary space to hold the variables from struct xz_buf */ - uint8_t *out; - size_t out_pos; - size_t out_size; - - struct { - /* Amount of already filtered data in the beginning of buf */ - size_t filtered; - - /* Total amount of data currently stored in buf */ - size_t size; - - /* - * Buffer to hold a mix of filtered and unfiltered data. This - * needs to be big enough to hold Alignment + 2 * Look-ahead: - * - * Type Alignment Look-ahead - * x86 1 4 - * PowerPC 4 0 - * IA-64 16 0 - * ARM 4 0 - * ARM-Thumb 2 2 - * SPARC 4 0 - */ - uint8_t buf[16]; - } temp; +struct xz_dec_bcj +{ + /* Type of the BCJ filter being used */ + enum + { + BCJ_X86 = 4, /* x86 or x86-64 */ + BCJ_POWERPC = 5, /* Big endian only */ + BCJ_IA64 = 6, /* Big or little endian */ + BCJ_ARM = 7, /* Little endian only */ + BCJ_ARMTHUMB = 8, /* Little endian only */ + BCJ_SPARC = 9 /* Big or little endian */ + } type; + + /* + * Return value of the next filter in the chain. We need to preserve + * this information across calls, because we must not call the next + * filter anymore once it has returned XZ_STREAM_END. + */ + enum xz_ret ret; + + /* True if we are operating in single-call mode. */ + bool single_call; + + /* + * Absolute position relative to the beginning of the uncompressed + * data (in a single .xz Block). We care only about the lowest 32 + * bits so this doesn't need to be uint64_t even with big files. + */ + uint32_t pos; + + /* x86 filter state */ + uint32_t x86_prev_mask; + + /* Temporary space to hold the variables from struct xz_buf */ + uint8_t* out; + size_t out_pos; + size_t out_size; + + struct + { + /* Amount of already filtered data in the beginning of buf */ + size_t filtered; + + /* Total amount of data currently stored in buf */ + size_t size; + + /* + * Buffer to hold a mix of filtered and unfiltered data. This + * needs to be big enough to hold Alignment + 2 * Look-ahead: + * + * Type Alignment Look-ahead + * x86 1 4 + * PowerPC 4 0 + * IA-64 16 0 + * ARM 4 0 + * ARM-Thumb 2 2 + * SPARC 4 0 + */ + uint8_t buf[16]; + } temp; }; #ifdef XZ_DEC_X86 @@ -115,255 +118,261 @@ */ static inline int bcj_x86_test_msbyte(uint8_t b) { - return b == 0x00 || b == 0xFF; + return b == 0x00 || b == 0xFF; } -static size_t bcj_x86(struct xz_dec_bcj *s, uint8_t *buf, size_t size) +static size_t bcj_x86(struct xz_dec_bcj* s, uint8_t* buf, size_t size) { - static const bool mask_to_allowed_status[8] - = { true, true, true, false, true, false, false, false }; + static const bool mask_to_allowed_status[8] = {true, true, true, false, true, false, false, false}; + + static const uint8_t mask_to_bit_num[8] = {0, 1, 2, 2, 3, 3, 3, 3}; - static const uint8_t mask_to_bit_num[8] = { 0, 1, 2, 2, 3, 3, 3, 3 }; - - size_t i; - size_t prev_pos = (size_t)-1; - uint32_t prev_mask = s->x86_prev_mask; - uint32_t src; - uint32_t dest; - uint32_t j; - uint8_t b; - - if (size <= 4) - return 0; - - size -= 4; - for (i = 0; i < size; ++i) { - if ((buf[i] & 0xFE) != 0xE8) - continue; - - prev_pos = i - prev_pos; - if (prev_pos > 3) { - prev_mask = 0; - } else { - prev_mask = (prev_mask << (prev_pos - 1)) & 7; - if (prev_mask != 0) { - b = buf[i + 4 - mask_to_bit_num[prev_mask]]; - if (!mask_to_allowed_status[prev_mask] - || bcj_x86_test_msbyte(b)) { - prev_pos = i; - prev_mask = (prev_mask << 1) | 1; - continue; - } - } - } - - prev_pos = i; - - if (bcj_x86_test_msbyte(buf[i + 4])) { - src = get_unaligned_le32(buf + i + 1); - while (true) { - dest = src - (s->pos + (uint32_t)i + 5); - if (prev_mask == 0) - break; - - j = mask_to_bit_num[prev_mask] * 8; - b = (uint8_t)(dest >> (24 - j)); - if (!bcj_x86_test_msbyte(b)) - break; - - src = dest ^ (((uint32_t)1 << (32 - j)) - 1); - } - - dest &= 0x01FFFFFF; - dest |= (uint32_t)0 - (dest & 0x01000000); - put_unaligned_le32(dest, buf + i + 1); - i += 4; - } else { - prev_mask = (prev_mask << 1) | 1; - } - } - - prev_pos = i - prev_pos; - s->x86_prev_mask = prev_pos > 3 ? 0 : prev_mask << (prev_pos - 1); - return i; + size_t i; + size_t prev_pos = (size_t)-1; + uint32_t prev_mask = s->x86_prev_mask; + uint32_t src; + uint32_t dest; + uint32_t j; + uint8_t b; + + if (size <= 4) + return 0; + + size -= 4; + for (i = 0; i < size; ++i) + { + if ((buf[i] & 0xFE) != 0xE8) + continue; + + prev_pos = i - prev_pos; + if (prev_pos > 3) + { + prev_mask = 0; + } + else + { + prev_mask = (prev_mask << (prev_pos - 1)) & 7; + if (prev_mask != 0) + { + b = buf[i + 4 - mask_to_bit_num[prev_mask]]; + if (!mask_to_allowed_status[prev_mask] || bcj_x86_test_msbyte(b)) + { + prev_pos = i; + prev_mask = (prev_mask << 1) | 1; + continue; + } + } + } + + prev_pos = i; + + if (bcj_x86_test_msbyte(buf[i + 4])) + { + src = get_unaligned_le32(buf + i + 1); + while (true) + { + dest = src - (s->pos + (uint32_t)i + 5); + if (prev_mask == 0) + break; + + j = mask_to_bit_num[prev_mask] * 8; + b = (uint8_t)(dest >> (24 - j)); + if (!bcj_x86_test_msbyte(b)) + break; + + src = dest ^ (((uint32_t)1 << (32 - j)) - 1); + } + + dest &= 0x01FFFFFF; + dest |= (uint32_t)0 - (dest & 0x01000000); + put_unaligned_le32(dest, buf + i + 1); + i += 4; + } + else + { + prev_mask = (prev_mask << 1) | 1; + } + } + + prev_pos = i - prev_pos; + s->x86_prev_mask = prev_pos > 3 ? 0 : prev_mask << (prev_pos - 1); + return i; } #endif #ifdef XZ_DEC_POWERPC -static size_t bcj_powerpc(struct xz_dec_bcj *s, uint8_t *buf, size_t size) +static size_t bcj_powerpc(struct xz_dec_bcj* s, uint8_t* buf, size_t size) { - size_t i; - uint32_t instr; + size_t i; + uint32_t instr; - for (i = 0; i + 4 <= size; i += 4) { - instr = get_unaligned_be32(buf + i); - if ((instr & 0xFC000003) == 0x48000001) { - instr &= 0x03FFFFFC; - instr -= s->pos + (uint32_t)i; - instr &= 0x03FFFFFC; - instr |= 0x48000001; - put_unaligned_be32(instr, buf + i); - } - } + for (i = 0; i + 4 <= size; i += 4) + { + instr = get_unaligned_be32(buf + i); + if ((instr & 0xFC000003) == 0x48000001) + { + instr &= 0x03FFFFFC; + instr -= s->pos + (uint32_t)i; + instr &= 0x03FFFFFC; + instr |= 0x48000001; + put_unaligned_be32(instr, buf + i); + } + } - return i; + return i; } #endif #ifdef XZ_DEC_IA64 -static size_t bcj_ia64(struct xz_dec_bcj *s, uint8_t *buf, size_t size) +static size_t bcj_ia64(struct xz_dec_bcj* s, uint8_t* buf, size_t size) { - static const uint8_t branch_table[32] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 4, 4, 6, 6, 0, 0, 7, 7, - 4, 4, 0, 0, 4, 4, 0, 0 - }; - - /* - * The local variables take a little bit stack space, but it's less - * than what LZMA2 decoder takes, so it doesn't make sense to reduce - * stack usage here without doing that for the LZMA2 decoder too. - */ - - /* Loop counters */ - size_t i; - size_t j; - - /* Instruction slot (0, 1, or 2) in the 128-bit instruction word */ - uint32_t slot; - - /* Bitwise offset of the instruction indicated by slot */ - uint32_t bit_pos; - - /* bit_pos split into byte and bit parts */ - uint32_t byte_pos; - uint32_t bit_res; - - /* Address part of an instruction */ - uint32_t addr; - - /* Mask used to detect which instructions to convert */ - uint32_t mask; - - /* 41-bit instruction stored somewhere in the lowest 48 bits */ - uint64_t instr; - - /* Instruction normalized with bit_res for easier manipulation */ - uint64_t norm; - - for (i = 0; i + 16 <= size; i += 16) { - mask = branch_table[buf[i] & 0x1F]; - for (slot = 0, bit_pos = 5; slot < 3; ++slot, bit_pos += 41) { - if (((mask >> slot) & 1) == 0) - continue; - - byte_pos = bit_pos >> 3; - bit_res = bit_pos & 7; - instr = 0; - for (j = 0; j < 6; ++j) - instr |= (uint64_t)(buf[i + j + byte_pos]) - << (8 * j); - - norm = instr >> bit_res; - - if (((norm >> 37) & 0x0F) == 0x05 - && ((norm >> 9) & 0x07) == 0) { - addr = (norm >> 13) & 0x0FFFFF; - addr |= ((uint32_t)(norm >> 36) & 1) << 20; - addr <<= 4; - addr -= s->pos + (uint32_t)i; - addr >>= 4; - - norm &= ~((uint64_t)0x8FFFFF << 13); - norm |= (uint64_t)(addr & 0x0FFFFF) << 13; - norm |= (uint64_t)(addr & 0x100000) - << (36 - 20); - - instr &= (1 << bit_res) - 1; - instr |= norm << bit_res; - - for (j = 0; j < 6; j++) - buf[i + j + byte_pos] - = (uint8_t)(instr >> (8 * j)); - } - } - } + static const uint8_t branch_table[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 4, 6, 6, 0, 0, 7, 7, 4, 4, 0, 0, 4, 4, 0, 0}; + + /* + * The local variables take a little bit stack space, but it's less + * than what LZMA2 decoder takes, so it doesn't make sense to reduce + * stack usage here without doing that for the LZMA2 decoder too. + */ + + /* Loop counters */ + size_t i; + size_t j; + + /* Instruction slot (0, 1, or 2) in the 128-bit instruction word */ + uint32_t slot; + + /* Bitwise offset of the instruction indicated by slot */ + uint32_t bit_pos; + + /* bit_pos split into byte and bit parts */ + uint32_t byte_pos; + uint32_t bit_res; + + /* Address part of an instruction */ + uint32_t addr; + + /* Mask used to detect which instructions to convert */ + uint32_t mask; + + /* 41-bit instruction stored somewhere in the lowest 48 bits */ + uint64_t instr; + + /* Instruction normalized with bit_res for easier manipulation */ + uint64_t norm; + + for (i = 0; i + 16 <= size; i += 16) + { + mask = branch_table[buf[i] & 0x1F]; + for (slot = 0, bit_pos = 5; slot < 3; ++slot, bit_pos += 41) + { + if (((mask >> slot) & 1) == 0) + continue; + + byte_pos = bit_pos >> 3; + bit_res = bit_pos & 7; + instr = 0; + for (j = 0; j < 6; ++j) + instr |= (uint64_t)(buf[i + j + byte_pos]) << (8 * j); + + norm = instr >> bit_res; + + if (((norm >> 37) & 0x0F) == 0x05 && ((norm >> 9) & 0x07) == 0) + { + addr = (norm >> 13) & 0x0FFFFF; + addr |= ((uint32_t)(norm >> 36) & 1) << 20; + addr <<= 4; + addr -= s->pos + (uint32_t)i; + addr >>= 4; + + norm &= ~((uint64_t)0x8FFFFF << 13); + norm |= (uint64_t)(addr & 0x0FFFFF) << 13; + norm |= (uint64_t)(addr & 0x100000) << (36 - 20); + + instr &= (1 << bit_res) - 1; + instr |= norm << bit_res; + + for (j = 0; j < 6; j++) + buf[i + j + byte_pos] = (uint8_t)(instr >> (8 * j)); + } + } + } - return i; + return i; } #endif #ifdef XZ_DEC_ARM -static size_t bcj_arm(struct xz_dec_bcj *s, uint8_t *buf, size_t size) +static size_t bcj_arm(struct xz_dec_bcj* s, uint8_t* buf, size_t size) { - size_t i; - uint32_t addr; + size_t i; + uint32_t addr; - for (i = 0; i + 4 <= size; i += 4) { - if (buf[i + 3] == 0xEB) { - addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8) - | ((uint32_t)buf[i + 2] << 16); - addr <<= 2; - addr -= s->pos + (uint32_t)i + 8; - addr >>= 2; - buf[i] = (uint8_t)addr; - buf[i + 1] = (uint8_t)(addr >> 8); - buf[i + 2] = (uint8_t)(addr >> 16); - } - } + for (i = 0; i + 4 <= size; i += 4) + { + if (buf[i + 3] == 0xEB) + { + addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8) | ((uint32_t)buf[i + 2] << 16); + addr <<= 2; + addr -= s->pos + (uint32_t)i + 8; + addr >>= 2; + buf[i] = (uint8_t)addr; + buf[i + 1] = (uint8_t)(addr >> 8); + buf[i + 2] = (uint8_t)(addr >> 16); + } + } - return i; + return i; } #endif #ifdef XZ_DEC_ARMTHUMB -static size_t bcj_armthumb(struct xz_dec_bcj *s, uint8_t *buf, size_t size) +static size_t bcj_armthumb(struct xz_dec_bcj* s, uint8_t* buf, size_t size) { - size_t i; - uint32_t addr; + size_t i; + uint32_t addr; - for (i = 0; i + 4 <= size; i += 2) { - if ((buf[i + 1] & 0xF8) == 0xF0 - && (buf[i + 3] & 0xF8) == 0xF8) { - addr = (((uint32_t)buf[i + 1] & 0x07) << 19) - | ((uint32_t)buf[i] << 11) - | (((uint32_t)buf[i + 3] & 0x07) << 8) - | (uint32_t)buf[i + 2]; - addr <<= 1; - addr -= s->pos + (uint32_t)i + 4; - addr >>= 1; - buf[i + 1] = (uint8_t)(0xF0 | ((addr >> 19) & 0x07)); - buf[i] = (uint8_t)(addr >> 11); - buf[i + 3] = (uint8_t)(0xF8 | ((addr >> 8) & 0x07)); - buf[i + 2] = (uint8_t)addr; - i += 2; - } - } + for (i = 0; i + 4 <= size; i += 2) + { + if ((buf[i + 1] & 0xF8) == 0xF0 && (buf[i + 3] & 0xF8) == 0xF8) + { + addr = (((uint32_t)buf[i + 1] & 0x07) << 19) | ((uint32_t)buf[i] << 11) | + (((uint32_t)buf[i + 3] & 0x07) << 8) | (uint32_t)buf[i + 2]; + addr <<= 1; + addr -= s->pos + (uint32_t)i + 4; + addr >>= 1; + buf[i + 1] = (uint8_t)(0xF0 | ((addr >> 19) & 0x07)); + buf[i] = (uint8_t)(addr >> 11); + buf[i + 3] = (uint8_t)(0xF8 | ((addr >> 8) & 0x07)); + buf[i + 2] = (uint8_t)addr; + i += 2; + } + } - return i; + return i; } #endif #ifdef XZ_DEC_SPARC -static size_t bcj_sparc(struct xz_dec_bcj *s, uint8_t *buf, size_t size) +static size_t bcj_sparc(struct xz_dec_bcj* s, uint8_t* buf, size_t size) { - size_t i; - uint32_t instr; + size_t i; + uint32_t instr; - for (i = 0; i + 4 <= size; i += 4) { - instr = get_unaligned_be32(buf + i); - if ((instr >> 22) == 0x100 || (instr >> 22) == 0x1FF) { - instr <<= 2; - instr -= s->pos + (uint32_t)i; - instr >>= 2; - instr = ((uint32_t)0x40000000 - (instr & 0x400000)) - | 0x40000000 | (instr & 0x3FFFFF); - put_unaligned_be32(instr, buf + i); - } - } + for (i = 0; i + 4 <= size; i += 4) + { + instr = get_unaligned_be32(buf + i); + if ((instr >> 22) == 0x100 || (instr >> 22) == 0x1FF) + { + instr <<= 2; + instr -= s->pos + (uint32_t)i; + instr >>= 2; + instr = ((uint32_t)0x40000000 - (instr & 0x400000)) | 0x40000000 | (instr & 0x3FFFFF); + put_unaligned_be32(instr, buf + i); + } + } - return i; + return i; } #endif @@ -375,53 +384,53 @@ * pointers, which could be problematic in the kernel boot code, which must * avoid pointers to static data (at least on x86). */ -static void bcj_apply(struct xz_dec_bcj *s, - uint8_t *buf, size_t *pos, size_t size) +static void bcj_apply(struct xz_dec_bcj* s, uint8_t* buf, size_t* pos, size_t size) { - size_t filtered; + size_t filtered; - buf += *pos; - size -= *pos; + buf += *pos; + size -= *pos; - switch (s->type) { + switch (s->type) + { #ifdef XZ_DEC_X86 - case BCJ_X86: - filtered = bcj_x86(s, buf, size); - break; + case BCJ_X86: + filtered = bcj_x86(s, buf, size); + break; #endif #ifdef XZ_DEC_POWERPC - case BCJ_POWERPC: - filtered = bcj_powerpc(s, buf, size); - break; + case BCJ_POWERPC: + filtered = bcj_powerpc(s, buf, size); + break; #endif #ifdef XZ_DEC_IA64 - case BCJ_IA64: - filtered = bcj_ia64(s, buf, size); - break; + case BCJ_IA64: + filtered = bcj_ia64(s, buf, size); + break; #endif #ifdef XZ_DEC_ARM - case BCJ_ARM: - filtered = bcj_arm(s, buf, size); - break; + case BCJ_ARM: + filtered = bcj_arm(s, buf, size); + break; #endif #ifdef XZ_DEC_ARMTHUMB - case BCJ_ARMTHUMB: - filtered = bcj_armthumb(s, buf, size); - break; + case BCJ_ARMTHUMB: + filtered = bcj_armthumb(s, buf, size); + break; #endif #ifdef XZ_DEC_SPARC - case BCJ_SPARC: - filtered = bcj_sparc(s, buf, size); - break; -#endif - default: - /* Never reached but silence compiler warnings. */ - filtered = 0; - break; - } + case BCJ_SPARC: + filtered = bcj_sparc(s, buf, size); + break; +#endif + default: + /* Never reached but silence compiler warnings. */ + filtered = 0; + break; + } - *pos += filtered; - s->pos += filtered; + *pos += filtered; + s->pos += filtered; } /* @@ -429,17 +438,17 @@ * Move the remaining mixture of possibly filtered and unfiltered * data to the beginning of temp. */ -static void bcj_flush(struct xz_dec_bcj *s, struct xz_buf *b) +static void bcj_flush(struct xz_dec_bcj* s, struct xz_buf* b) { - size_t copy_size; + size_t copy_size; - copy_size = min_t(size_t, s->temp.filtered, b->out_size - b->out_pos); - memcpy(b->out + b->out_pos, s->temp.buf, copy_size); - b->out_pos += copy_size; + copy_size = min_t(size_t, s->temp.filtered, b->out_size - b->out_pos); + memcpy(b->out + b->out_pos, s->temp.buf, copy_size); + b->out_pos += copy_size; - s->temp.filtered -= copy_size; - s->temp.size -= copy_size; - memmove(s->temp.buf, s->temp.buf + copy_size, s->temp.size); + s->temp.filtered -= copy_size; + s->temp.size -= copy_size; + memmove(s->temp.buf, s->temp.buf + copy_size, s->temp.size); } /* @@ -447,161 +456,162 @@ * data in chunks of 1-16 bytes. To hide this issue, this function does * some buffering. */ -XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s, - struct xz_dec_lzma2 *lzma2, - struct xz_buf *b) -{ - size_t out_start; - - /* - * Flush pending already filtered data to the output buffer. Return - * immediatelly if we couldn't flush everything, or if the next - * filter in the chain had already returned XZ_STREAM_END. - */ - if (s->temp.filtered > 0) { - bcj_flush(s, b); - if (s->temp.filtered > 0) - return XZ_OK; - - if (s->ret == XZ_STREAM_END) - return XZ_STREAM_END; - } - - /* - * If we have more output space than what is currently pending in - * temp, copy the unfiltered data from temp to the output buffer - * and try to fill the output buffer by decoding more data from the - * next filter in the chain. Apply the BCJ filter on the new data - * in the output buffer. If everything cannot be filtered, copy it - * to temp and rewind the output buffer position accordingly. - * - * This needs to be always run when temp.size == 0 to handle a special - * case where the output buffer is full and the next filter has no - * more output coming but hasn't returned XZ_STREAM_END yet. - */ - if (s->temp.size < b->out_size - b->out_pos || s->temp.size == 0) { - out_start = b->out_pos; - memcpy(b->out + b->out_pos, s->temp.buf, s->temp.size); - b->out_pos += s->temp.size; - - s->ret = xz_dec_lzma2_run(lzma2, b); - if (s->ret != XZ_STREAM_END - && (s->ret != XZ_OK || s->single_call)) - return s->ret; - - bcj_apply(s, b->out, &out_start, b->out_pos); - - /* - * As an exception, if the next filter returned XZ_STREAM_END, - * we can do that too, since the last few bytes that remain - * unfiltered are meant to remain unfiltered. - */ - if (s->ret == XZ_STREAM_END) - return XZ_STREAM_END; - - s->temp.size = b->out_pos - out_start; - b->out_pos -= s->temp.size; - memcpy(s->temp.buf, b->out + b->out_pos, s->temp.size); - - /* - * If there wasn't enough input to the next filter to fill - * the output buffer with unfiltered data, there's no point - * to try decoding more data to temp. - */ - if (b->out_pos + s->temp.size < b->out_size) - return XZ_OK; - } - - /* - * We have unfiltered data in temp. If the output buffer isn't full - * yet, try to fill the temp buffer by decoding more data from the - * next filter. Apply the BCJ filter on temp. Then we hopefully can - * fill the actual output buffer by copying filtered data from temp. - * A mix of filtered and unfiltered data may be left in temp; it will - * be taken care on the next call to this function. - */ - if (b->out_pos < b->out_size) { - /* Make b->out{,_pos,_size} temporarily point to s->temp. */ - s->out = b->out; - s->out_pos = b->out_pos; - s->out_size = b->out_size; - b->out = s->temp.buf; - b->out_pos = s->temp.size; - b->out_size = sizeof(s->temp.buf); - - s->ret = xz_dec_lzma2_run(lzma2, b); - - s->temp.size = b->out_pos; - b->out = s->out; - b->out_pos = s->out_pos; - b->out_size = s->out_size; - - if (s->ret != XZ_OK && s->ret != XZ_STREAM_END) - return s->ret; - - bcj_apply(s, s->temp.buf, &s->temp.filtered, s->temp.size); - - /* - * If the next filter returned XZ_STREAM_END, we mark that - * everything is filtered, since the last unfiltered bytes - * of the stream are meant to be left as is. - */ - if (s->ret == XZ_STREAM_END) - s->temp.filtered = s->temp.size; - - bcj_flush(s, b); - if (s->temp.filtered > 0) - return XZ_OK; - } - - return s->ret; -} - -XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call) -{ - struct xz_dec_bcj *s = kmalloc(sizeof(*s), GFP_KERNEL); - if (s != NULL) - s->single_call = single_call; +XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj* s, struct xz_dec_lzma2* lzma2, struct xz_buf* b) +{ + size_t out_start; + + /* + * Flush pending already filtered data to the output buffer. Return + * immediatelly if we couldn't flush everything, or if the next + * filter in the chain had already returned XZ_STREAM_END. + */ + if (s->temp.filtered > 0) + { + bcj_flush(s, b); + if (s->temp.filtered > 0) + return XZ_OK; + + if (s->ret == XZ_STREAM_END) + return XZ_STREAM_END; + } + + /* + * If we have more output space than what is currently pending in + * temp, copy the unfiltered data from temp to the output buffer + * and try to fill the output buffer by decoding more data from the + * next filter in the chain. Apply the BCJ filter on the new data + * in the output buffer. If everything cannot be filtered, copy it + * to temp and rewind the output buffer position accordingly. + * + * This needs to be always run when temp.size == 0 to handle a special + * case where the output buffer is full and the next filter has no + * more output coming but hasn't returned XZ_STREAM_END yet. + */ + if (s->temp.size < b->out_size - b->out_pos || s->temp.size == 0) + { + out_start = b->out_pos; + memcpy(b->out + b->out_pos, s->temp.buf, s->temp.size); + b->out_pos += s->temp.size; + + s->ret = xz_dec_lzma2_run(lzma2, b); + if (s->ret != XZ_STREAM_END && (s->ret != XZ_OK || s->single_call)) + return s->ret; + + bcj_apply(s, b->out, &out_start, b->out_pos); + + /* + * As an exception, if the next filter returned XZ_STREAM_END, + * we can do that too, since the last few bytes that remain + * unfiltered are meant to remain unfiltered. + */ + if (s->ret == XZ_STREAM_END) + return XZ_STREAM_END; + + s->temp.size = b->out_pos - out_start; + b->out_pos -= s->temp.size; + memcpy(s->temp.buf, b->out + b->out_pos, s->temp.size); + + /* + * If there wasn't enough input to the next filter to fill + * the output buffer with unfiltered data, there's no point + * to try decoding more data to temp. + */ + if (b->out_pos + s->temp.size < b->out_size) + return XZ_OK; + } + + /* + * We have unfiltered data in temp. If the output buffer isn't full + * yet, try to fill the temp buffer by decoding more data from the + * next filter. Apply the BCJ filter on temp. Then we hopefully can + * fill the actual output buffer by copying filtered data from temp. + * A mix of filtered and unfiltered data may be left in temp; it will + * be taken care on the next call to this function. + */ + if (b->out_pos < b->out_size) + { + /* Make b->out{,_pos,_size} temporarily point to s->temp. */ + s->out = b->out; + s->out_pos = b->out_pos; + s->out_size = b->out_size; + b->out = s->temp.buf; + b->out_pos = s->temp.size; + b->out_size = sizeof(s->temp.buf); + + s->ret = xz_dec_lzma2_run(lzma2, b); + + s->temp.size = b->out_pos; + b->out = s->out; + b->out_pos = s->out_pos; + b->out_size = s->out_size; + + if (s->ret != XZ_OK && s->ret != XZ_STREAM_END) + return s->ret; + + bcj_apply(s, s->temp.buf, &s->temp.filtered, s->temp.size); + + /* + * If the next filter returned XZ_STREAM_END, we mark that + * everything is filtered, since the last unfiltered bytes + * of the stream are meant to be left as is. + */ + if (s->ret == XZ_STREAM_END) + s->temp.filtered = s->temp.size; + + bcj_flush(s, b); + if (s->temp.filtered > 0) + return XZ_OK; + } + + return s->ret; +} + +XZ_EXTERN struct xz_dec_bcj* xz_dec_bcj_create(bool single_call) +{ + struct xz_dec_bcj* s = kmalloc(sizeof(*s), GFP_KERNEL); + if (s != NULL) + s->single_call = single_call; - return s; + return s; } -XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id) +XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj* s, uint8_t id) { - switch (id) { + switch (id) + { #ifdef XZ_DEC_X86 - case BCJ_X86: + case BCJ_X86: #endif #ifdef XZ_DEC_POWERPC - case BCJ_POWERPC: + case BCJ_POWERPC: #endif #ifdef XZ_DEC_IA64 - case BCJ_IA64: + case BCJ_IA64: #endif #ifdef XZ_DEC_ARM - case BCJ_ARM: + case BCJ_ARM: #endif #ifdef XZ_DEC_ARMTHUMB - case BCJ_ARMTHUMB: + case BCJ_ARMTHUMB: #endif #ifdef XZ_DEC_SPARC - case BCJ_SPARC: + case BCJ_SPARC: #endif - break; + break; - default: - /* Unsupported Filter ID */ - return XZ_OPTIONS_ERROR; - } - - s->type = id; - s->ret = XZ_OK; - s->pos = 0; - s->x86_prev_mask = 0; - s->temp.filtered = 0; - s->temp.size = 0; + default: + /* Unsupported Filter ID */ + return XZ_OPTIONS_ERROR; + } + + s->type = id; + s->ret = XZ_OK; + s->pos = 0; + s->x86_prev_mask = 0; + s->temp.filtered = 0; + s->temp.size = 0; - return XZ_OK; + return XZ_OK; } #endif diff -Nru mstflint-4.17.0+1/ext_libs/minixz/xz_dec_lzma2.c mstflint-4.21.0+1/ext_libs/minixz/xz_dec_lzma2.c --- mstflint-4.17.0+1/ext_libs/minixz/xz_dec_lzma2.c 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/minixz/xz_dec_lzma2.c 2022-07-31 09:33:57.000000000 +0000 @@ -1,6 +1,7 @@ /* * Copyright (C) Jan 2006 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -74,238 +75,246 @@ * in which the dictionary variables address the actual output * buffer directly. */ -struct dictionary { - /* Beginning of the history buffer */ - uint8_t *buf; - - /* Old position in buf (before decoding more data) */ - size_t start; - - /* Position in buf */ - size_t pos; - - /* - * How full dictionary is. This is used to detect corrupt input that - * would read beyond the beginning of the uncompressed stream. - */ - size_t full; - - /* Write limit; we don't write to buf[limit] or later bytes. */ - size_t limit; - - /* - * End of the dictionary buffer. In multi-call mode, this is - * the same as the dictionary size. In single-call mode, this - * indicates the size of the output buffer. - */ - size_t end; - - /* - * Size of the dictionary as specified in Block Header. This is used - * together with "full" to detect corrupt input that would make us - * read beyond the beginning of the uncompressed stream. - */ - uint32_t size; - - /* - * Maximum allowed dictionary size in multi-call mode. - * This is ignored in single-call mode. - */ - uint32_t size_max; - - /* - * Amount of memory currently allocated for the dictionary. - * This is used only with XZ_DYNALLOC. (With XZ_PREALLOC, - * size_max is always the same as the allocated size.) - */ - uint32_t allocated; +struct dictionary +{ + /* Beginning of the history buffer */ + uint8_t* buf; + + /* Old position in buf (before decoding more data) */ + size_t start; + + /* Position in buf */ + size_t pos; + + /* + * How full dictionary is. This is used to detect corrupt input that + * would read beyond the beginning of the uncompressed stream. + */ + size_t full; + + /* Write limit; we don't write to buf[limit] or later bytes. */ + size_t limit; + + /* + * End of the dictionary buffer. In multi-call mode, this is + * the same as the dictionary size. In single-call mode, this + * indicates the size of the output buffer. + */ + size_t end; + + /* + * Size of the dictionary as specified in Block Header. This is used + * together with "full" to detect corrupt input that would make us + * read beyond the beginning of the uncompressed stream. + */ + uint32_t size; + + /* + * Maximum allowed dictionary size in multi-call mode. + * This is ignored in single-call mode. + */ + uint32_t size_max; + + /* + * Amount of memory currently allocated for the dictionary. + * This is used only with XZ_DYNALLOC. (With XZ_PREALLOC, + * size_max is always the same as the allocated size.) + */ + uint32_t allocated; - /* Operation mode */ - enum xz_mode mode; + /* Operation mode */ + enum xz_mode mode; }; /* Range decoder */ -struct rc_dec { - uint32_t range; - uint32_t code; - - /* - * Number of initializing bytes remaining to be read - * by rc_read_init(). - */ - uint32_t init_bytes_left; - - /* - * Buffer from which we read our input. It can be either - * temp.buf or the caller-provided input buffer. - */ - const uint8_t *in; - size_t in_pos; - size_t in_limit; +struct rc_dec +{ + uint32_t range; + uint32_t code; + + /* + * Number of initializing bytes remaining to be read + * by rc_read_init(). + */ + uint32_t init_bytes_left; + + /* + * Buffer from which we read our input. It can be either + * temp.buf or the caller-provided input buffer. + */ + const uint8_t* in; + size_t in_pos; + size_t in_limit; }; /* Probabilities for a length decoder. */ -struct lzma_len_dec { - /* Probability of match length being at least 10 */ - uint16_t choice; +struct lzma_len_dec +{ + /* Probability of match length being at least 10 */ + uint16_t choice; - /* Probability of match length being at least 18 */ - uint16_t choice2; + /* Probability of match length being at least 18 */ + uint16_t choice2; - /* Probabilities for match lengths 2-9 */ - uint16_t low[POS_STATES_MAX][LEN_LOW_SYMBOLS]; + /* Probabilities for match lengths 2-9 */ + uint16_t low[POS_STATES_MAX][LEN_LOW_SYMBOLS]; - /* Probabilities for match lengths 10-17 */ - uint16_t mid[POS_STATES_MAX][LEN_MID_SYMBOLS]; + /* Probabilities for match lengths 10-17 */ + uint16_t mid[POS_STATES_MAX][LEN_MID_SYMBOLS]; - /* Probabilities for match lengths 18-273 */ - uint16_t high[LEN_HIGH_SYMBOLS]; + /* Probabilities for match lengths 18-273 */ + uint16_t high[LEN_HIGH_SYMBOLS]; }; -struct lzma_dec { - /* Distances of latest four matches */ - uint32_t rep0; - uint32_t rep1; - uint32_t rep2; - uint32_t rep3; - - /* Types of the most recently seen LZMA symbols */ - enum lzma_state state; - - /* - * Length of a match. This is updated so that dict_repeat can - * be called again to finish repeating the whole match. - */ - uint32_t len; - - /* - * LZMA properties or related bit masks (number of literal - * context bits, a mask dervied from the number of literal - * position bits, and a mask dervied from the number - * position bits) - */ - uint32_t lc; - uint32_t literal_pos_mask; /* (1 << lp) - 1 */ - uint32_t pos_mask; /* (1 << pb) - 1 */ - - /* If 1, it's a match. Otherwise it's a single 8-bit literal. */ - uint16_t is_match[STATES][POS_STATES_MAX]; - - /* If 1, it's a repeated match. The distance is one of rep0 .. rep3. */ - uint16_t is_rep[STATES]; - - /* - * If 0, distance of a repeated match is rep0. - * Otherwise check is_rep1. - */ - uint16_t is_rep0[STATES]; - - /* - * If 0, distance of a repeated match is rep1. - * Otherwise check is_rep2. - */ - uint16_t is_rep1[STATES]; - - /* If 0, distance of a repeated match is rep2. Otherwise it is rep3. */ - uint16_t is_rep2[STATES]; - - /* - * If 1, the repeated match has length of one byte. Otherwise - * the length is decoded from rep_len_decoder. - */ - uint16_t is_rep0_long[STATES][POS_STATES_MAX]; - - /* - * Probability tree for the highest two bits of the match - * distance. There is a separate probability tree for match - * lengths of 2 (i.e. MATCH_LEN_MIN), 3, 4, and [5, 273]. - */ - uint16_t dist_slot[DIST_STATES][DIST_SLOTS]; - - /* - * Probility trees for additional bits for match distance - * when the distance is in the range [4, 127]. - */ - uint16_t dist_special[FULL_DISTANCES - DIST_MODEL_END]; - - /* - * Probability tree for the lowest four bits of a match - * distance that is equal to or greater than 128. - */ - uint16_t dist_align[ALIGN_SIZE]; +struct lzma_dec +{ + /* Distances of latest four matches */ + uint32_t rep0; + uint32_t rep1; + uint32_t rep2; + uint32_t rep3; + + /* Types of the most recently seen LZMA symbols */ + enum lzma_state state; + + /* + * Length of a match. This is updated so that dict_repeat can + * be called again to finish repeating the whole match. + */ + uint32_t len; + + /* + * LZMA properties or related bit masks (number of literal + * context bits, a mask dervied from the number of literal + * position bits, and a mask dervied from the number + * position bits) + */ + uint32_t lc; + uint32_t literal_pos_mask; /* (1 << lp) - 1 */ + uint32_t pos_mask; /* (1 << pb) - 1 */ + + /* If 1, it's a match. Otherwise it's a single 8-bit literal. */ + uint16_t is_match[STATES][POS_STATES_MAX]; + + /* If 1, it's a repeated match. The distance is one of rep0 .. rep3. */ + uint16_t is_rep[STATES]; + + /* + * If 0, distance of a repeated match is rep0. + * Otherwise check is_rep1. + */ + uint16_t is_rep0[STATES]; + + /* + * If 0, distance of a repeated match is rep1. + * Otherwise check is_rep2. + */ + uint16_t is_rep1[STATES]; + + /* If 0, distance of a repeated match is rep2. Otherwise it is rep3. */ + uint16_t is_rep2[STATES]; + + /* + * If 1, the repeated match has length of one byte. Otherwise + * the length is decoded from rep_len_decoder. + */ + uint16_t is_rep0_long[STATES][POS_STATES_MAX]; + + /* + * Probability tree for the highest two bits of the match + * distance. There is a separate probability tree for match + * lengths of 2 (i.e. MATCH_LEN_MIN), 3, 4, and [5, 273]. + */ + uint16_t dist_slot[DIST_STATES][DIST_SLOTS]; + + /* + * Probility trees for additional bits for match distance + * when the distance is in the range [4, 127]. + */ + uint16_t dist_special[FULL_DISTANCES - DIST_MODEL_END]; + + /* + * Probability tree for the lowest four bits of a match + * distance that is equal to or greater than 128. + */ + uint16_t dist_align[ALIGN_SIZE]; - /* Length of a normal match */ - struct lzma_len_dec match_len_dec; + /* Length of a normal match */ + struct lzma_len_dec match_len_dec; - /* Length of a repeated match */ - struct lzma_len_dec rep_len_dec; + /* Length of a repeated match */ + struct lzma_len_dec rep_len_dec; - /* Probabilities of literals */ - uint16_t literal[LITERAL_CODERS_MAX][LITERAL_CODER_SIZE]; + /* Probabilities of literals */ + uint16_t literal[LITERAL_CODERS_MAX][LITERAL_CODER_SIZE]; }; -struct lzma2_dec { - /* Position in xz_dec_lzma2_run(). */ - enum lzma2_seq { - SEQ_CONTROL, - SEQ_UNCOMPRESSED_1, - SEQ_UNCOMPRESSED_2, - SEQ_COMPRESSED_0, - SEQ_COMPRESSED_1, - SEQ_PROPERTIES, - SEQ_LZMA_PREPARE, - SEQ_LZMA_RUN, - SEQ_COPY - } sequence; - - /* Next position after decoding the compressed size of the chunk. */ - enum lzma2_seq next_sequence; - - /* Uncompressed size of LZMA chunk (2 MiB at maximum) */ - uint32_t uncompressed; - - /* - * Compressed size of LZMA chunk or compressed/uncompressed - * size of uncompressed chunk (64 KiB at maximum) - */ - uint32_t compressed; - - /* - * True if dictionary reset is needed. This is false before - * the first chunk (LZMA or uncompressed). - */ - bool need_dict_reset; - - /* - * True if new LZMA properties are needed. This is false - * before the first LZMA chunk. - */ - bool need_props; +struct lzma2_dec +{ + /* Position in xz_dec_lzma2_run(). */ + enum lzma2_seq + { + SEQ_CONTROL, + SEQ_UNCOMPRESSED_1, + SEQ_UNCOMPRESSED_2, + SEQ_COMPRESSED_0, + SEQ_COMPRESSED_1, + SEQ_PROPERTIES, + SEQ_LZMA_PREPARE, + SEQ_LZMA_RUN, + SEQ_COPY + } sequence; + + /* Next position after decoding the compressed size of the chunk. */ + enum lzma2_seq next_sequence; + + /* Uncompressed size of LZMA chunk (2 MiB at maximum) */ + uint32_t uncompressed; + + /* + * Compressed size of LZMA chunk or compressed/uncompressed + * size of uncompressed chunk (64 KiB at maximum) + */ + uint32_t compressed; + + /* + * True if dictionary reset is needed. This is false before + * the first chunk (LZMA or uncompressed). + */ + bool need_dict_reset; + + /* + * True if new LZMA properties are needed. This is false + * before the first LZMA chunk. + */ + bool need_props; }; -struct xz_dec_lzma2 { - /* - * The order below is important on x86 to reduce code size and - * it shouldn't hurt on other platforms. Everything up to and - * including lzma.pos_mask are in the first 128 bytes on x86-32, - * which allows using smaller instructions to access those - * variables. On x86-64, fewer variables fit into the first 128 - * bytes, but this is still the best order without sacrificing - * the readability by splitting the structures. - */ - struct rc_dec rc; - struct dictionary dict; - struct lzma2_dec lzma2; - struct lzma_dec lzma; - - /* - * Temporary buffer which holds small number of input bytes between - * decoder calls. See lzma2_lzma() for details. - */ - struct { - uint32_t size; - uint8_t buf[3 * LZMA_IN_REQUIRED]; - } temp; +struct xz_dec_lzma2 +{ + /* + * The order below is important on x86 to reduce code size and + * it shouldn't hurt on other platforms. Everything up to and + * including lzma.pos_mask are in the first 128 bytes on x86-32, + * which allows using smaller instructions to access those + * variables. On x86-64, fewer variables fit into the first 128 + * bytes, but this is still the best order without sacrificing + * the readability by splitting the structures. + */ + struct rc_dec rc; + struct dictionary dict; + struct lzma2_dec lzma2; + struct lzma_dec lzma; + + /* + * Temporary buffer which holds small number of input bytes between + * decoder calls. See lzma2_lzma() for details. + */ + struct + { + uint32_t size; + uint8_t buf[3 * LZMA_IN_REQUIRED]; + } temp; }; /************** @@ -316,32 +325,33 @@ * Reset the dictionary state. When in single-call mode, set up the beginning * of the dictionary to point to the actual output buffer. */ -static void dict_reset(struct dictionary *dict, struct xz_buf *b) +static void dict_reset(struct dictionary* dict, struct xz_buf* b) { - if (DEC_IS_SINGLE(dict->mode)) { - dict->buf = b->out + b->out_pos; - dict->end = b->out_size - b->out_pos; - } - - dict->start = 0; - dict->pos = 0; - dict->limit = 0; - dict->full = 0; + if (DEC_IS_SINGLE(dict->mode)) + { + dict->buf = b->out + b->out_pos; + dict->end = b->out_size - b->out_pos; + } + + dict->start = 0; + dict->pos = 0; + dict->limit = 0; + dict->full = 0; } /* Set dictionary write limit */ -static void dict_limit(struct dictionary *dict, size_t out_max) +static void dict_limit(struct dictionary* dict, size_t out_max) { - if (dict->end - dict->pos <= out_max) - dict->limit = dict->end; - else - dict->limit = dict->pos + out_max; + if (dict->end - dict->pos <= out_max) + dict->limit = dict->end; + else + dict->limit = dict->pos + out_max; } /* Return true if at least one byte can be written into the dictionary. */ -static inline bool dict_has_space(const struct dictionary *dict) +static inline bool dict_has_space(const struct dictionary* dict) { - return dict->pos < dict->limit; + return dict->pos < dict->limit; } /* @@ -350,25 +360,25 @@ * still empty. This special case is needed for single-call decoding to * avoid writing a '\0' to the end of the destination buffer. */ -static inline uint32_t dict_get(const struct dictionary *dict, uint32_t dist) +static inline uint32_t dict_get(const struct dictionary* dict, uint32_t dist) { - size_t offset = dict->pos - dist - 1; + size_t offset = dict->pos - dist - 1; - if (dist >= dict->pos) - offset += dict->end; + if (dist >= dict->pos) + offset += dict->end; - return dict->full > 0 ? dict->buf[offset] : 0; + return dict->full > 0 ? dict->buf[offset] : 0; } /* * Put one byte into the dictionary. It is assumed that there is space for it. */ -static inline void dict_put(struct dictionary *dict, uint8_t byte) +static inline void dict_put(struct dictionary* dict, uint8_t byte) { - dict->buf[dict->pos++] = byte; + dict->buf[dict->pos++] = byte; - if (dict->full < dict->pos) - dict->full = dict->pos; + if (dict->full < dict->pos) + dict->full = dict->pos; } /* @@ -376,69 +386,68 @@ * invalid, false is returned. On success, true is returned and *len is * updated to indicate how many bytes were left to be repeated. */ -static bool dict_repeat(struct dictionary *dict, uint32_t *len, uint32_t dist) +static bool dict_repeat(struct dictionary* dict, uint32_t* len, uint32_t dist) { - size_t back; - uint32_t left; + size_t back; + uint32_t left; - if (dist >= dict->full || dist >= dict->size) - return false; + if (dist >= dict->full || dist >= dict->size) + return false; - left = min_t(size_t, dict->limit - dict->pos, *len); - *len -= left; + left = min_t(size_t, dict->limit - dict->pos, *len); + *len -= left; - back = dict->pos - dist - 1; - if (dist >= dict->pos) - back += dict->end; + back = dict->pos - dist - 1; + if (dist >= dict->pos) + back += dict->end; - do { - dict->buf[dict->pos++] = dict->buf[back++]; - if (back == dict->end) - back = 0; - } while (--left > 0); + do + { + dict->buf[dict->pos++] = dict->buf[back++]; + if (back == dict->end) + back = 0; + } while (--left > 0); - if (dict->full < dict->pos) - dict->full = dict->pos; + if (dict->full < dict->pos) + dict->full = dict->pos; - return true; + return true; } /* Copy uncompressed data as is from input to dictionary and output buffers. */ -static void dict_uncompressed(struct dictionary *dict, struct xz_buf *b, - uint32_t *left) +static void dict_uncompressed(struct dictionary* dict, struct xz_buf* b, uint32_t* left) { - size_t copy_size; + size_t copy_size; - while (*left > 0 && b->in_pos < b->in_size - && b->out_pos < b->out_size) { - copy_size = min(b->in_size - b->in_pos, - b->out_size - b->out_pos); - if (copy_size > dict->end - dict->pos) - copy_size = dict->end - dict->pos; - if (copy_size > *left) - copy_size = *left; + while (*left > 0 && b->in_pos < b->in_size && b->out_pos < b->out_size) + { + copy_size = min(b->in_size - b->in_pos, b->out_size - b->out_pos); + if (copy_size > dict->end - dict->pos) + copy_size = dict->end - dict->pos; + if (copy_size > *left) + copy_size = *left; - *left -= copy_size; + *left -= copy_size; - memcpy(dict->buf + dict->pos, b->in + b->in_pos, copy_size); - dict->pos += copy_size; + memcpy(dict->buf + dict->pos, b->in + b->in_pos, copy_size); + dict->pos += copy_size; - if (dict->full < dict->pos) - dict->full = dict->pos; + if (dict->full < dict->pos) + dict->full = dict->pos; - if (DEC_IS_MULTI(dict->mode)) { - if (dict->pos == dict->end) - dict->pos = 0; + if (DEC_IS_MULTI(dict->mode)) + { + if (dict->pos == dict->end) + dict->pos = 0; - memcpy(b->out + b->out_pos, b->in + b->in_pos, - copy_size); - } + memcpy(b->out + b->out_pos, b->in + b->in_pos, copy_size); + } - dict->start = dict->pos; + dict->start = dict->pos; - b->out_pos += copy_size; - b->in_pos += copy_size; - } + b->out_pos += copy_size; + b->in_pos += copy_size; + } } /* @@ -446,21 +455,21 @@ * enough space in b->out. This is guaranteed because caller uses dict_limit() * before decoding data into the dictionary. */ -static uint32_t dict_flush(struct dictionary *dict, struct xz_buf *b) +static uint32_t dict_flush(struct dictionary* dict, struct xz_buf* b) { - size_t copy_size = dict->pos - dict->start; + size_t copy_size = dict->pos - dict->start; - if (DEC_IS_MULTI(dict->mode)) { - if (dict->pos == dict->end) - dict->pos = 0; + if (DEC_IS_MULTI(dict->mode)) + { + if (dict->pos == dict->end) + dict->pos = 0; - memcpy(b->out + b->out_pos, dict->buf + dict->start, - copy_size); - } + memcpy(b->out + b->out_pos, dict->buf + dict->start, copy_size); + } - dict->start = dict->pos; - b->out_pos += copy_size; - return copy_size; + dict->start = dict->pos; + b->out_pos += copy_size; + return copy_size; } /***************** @@ -468,52 +477,54 @@ *****************/ /* Reset the range decoder. */ -static void rc_reset(struct rc_dec *rc) +static void rc_reset(struct rc_dec* rc) { - rc->range = (uint32_t)-1; - rc->code = 0; - rc->init_bytes_left = RC_INIT_BYTES; + rc->range = (uint32_t)-1; + rc->code = 0; + rc->init_bytes_left = RC_INIT_BYTES; } /* * Read the first five initial bytes into rc->code if they haven't been * read already. (Yes, the first byte gets completely ignored.) */ -static bool rc_read_init(struct rc_dec *rc, struct xz_buf *b) +static bool rc_read_init(struct rc_dec* rc, struct xz_buf* b) { - while (rc->init_bytes_left > 0) { - if (b->in_pos == b->in_size) - return false; + while (rc->init_bytes_left > 0) + { + if (b->in_pos == b->in_size) + return false; + + rc->code = (rc->code << 8) + b->in[b->in_pos++]; + --rc->init_bytes_left; + } - rc->code = (rc->code << 8) + b->in[b->in_pos++]; - --rc->init_bytes_left; - } - - return true; + return true; } /* Return true if there may not be enough input for the next decoding loop. */ -static inline bool rc_limit_exceeded(const struct rc_dec *rc) +static inline bool rc_limit_exceeded(const struct rc_dec* rc) { - return rc->in_pos > rc->in_limit; + return rc->in_pos > rc->in_limit; } /* * Return true if it is possible (from point of view of range decoder) that * we have reached the end of the LZMA chunk. */ -static inline bool rc_is_finished(const struct rc_dec *rc) +static inline bool rc_is_finished(const struct rc_dec* rc) { - return rc->code == 0; + return rc->code == 0; } /* Read the next input byte if needed. */ -static __always_inline void rc_normalize(struct rc_dec *rc) +static __always_inline void rc_normalize(struct rc_dec* rc) { - if (rc->range < RC_TOP_VALUE) { - rc->range <<= RC_SHIFT_BITS; - rc->code = (rc->code << RC_SHIFT_BITS) + rc->in[rc->in_pos++]; - } + if (rc->range < RC_TOP_VALUE) + { + rc->range <<= RC_SHIFT_BITS; + rc->code = (rc->code << RC_SHIFT_BITS) + rc->in[rc->in_pos++]; + } } /* @@ -527,74 +538,80 @@ * of the code generated by GCC 3.x decreases 10-15 %. (GCC 4.3 doesn't care, * and it generates 10-20 % faster code than GCC 3.x from this file anyway.) */ -static __always_inline int rc_bit(struct rc_dec *rc, uint16_t *prob) +static __always_inline int rc_bit(struct rc_dec* rc, uint16_t* prob) { - uint32_t bound; - int bit; + uint32_t bound; + int bit; - rc_normalize(rc); - bound = (rc->range >> RC_BIT_MODEL_TOTAL_BITS) * *prob; - if (rc->code < bound) { - rc->range = bound; - *prob += (RC_BIT_MODEL_TOTAL - *prob) >> RC_MOVE_BITS; - bit = 0; - } else { - rc->range -= bound; - rc->code -= bound; - *prob -= *prob >> RC_MOVE_BITS; - bit = 1; - } + rc_normalize(rc); + bound = (rc->range >> RC_BIT_MODEL_TOTAL_BITS) * *prob; + if (rc->code < bound) + { + rc->range = bound; + *prob += (RC_BIT_MODEL_TOTAL - *prob) >> RC_MOVE_BITS; + bit = 0; + } + else + { + rc->range -= bound; + rc->code -= bound; + *prob -= *prob >> RC_MOVE_BITS; + bit = 1; + } - return bit; + return bit; } /* Decode a bittree starting from the most significant bit. */ -static __always_inline uint32_t rc_bittree(struct rc_dec *rc, - uint16_t *probs, uint32_t limit) +static __always_inline uint32_t rc_bittree(struct rc_dec* rc, uint16_t* probs, uint32_t limit) { - uint32_t symbol = 1; + uint32_t symbol = 1; - do { - if (rc_bit(rc, &probs[symbol])) - symbol = (symbol << 1) + 1; - else - symbol <<= 1; - } while (symbol < limit); + do + { + if (rc_bit(rc, &probs[symbol])) + symbol = (symbol << 1) + 1; + else + symbol <<= 1; + } while (symbol < limit); - return symbol; + return symbol; } /* Decode a bittree starting from the least significant bit. */ -static __always_inline void rc_bittree_reverse(struct rc_dec *rc, - uint16_t *probs, - uint32_t *dest, uint32_t limit) -{ - uint32_t symbol = 1; - uint32_t i = 0; - - do { - if (rc_bit(rc, &probs[symbol])) { - symbol = (symbol << 1) + 1; - *dest += 1 << i; - } else { - symbol <<= 1; - } - } while (++i < limit); +static __always_inline void rc_bittree_reverse(struct rc_dec* rc, uint16_t* probs, uint32_t* dest, uint32_t limit) +{ + uint32_t symbol = 1; + uint32_t i = 0; + + do + { + if (rc_bit(rc, &probs[symbol])) + { + symbol = (symbol << 1) + 1; + *dest += 1 << i; + } + else + { + symbol <<= 1; + } + } while (++i < limit); } /* Decode direct bits (fixed fifty-fifty probability) */ -static inline void rc_direct(struct rc_dec *rc, uint32_t *dest, uint32_t limit) +static inline void rc_direct(struct rc_dec* rc, uint32_t* dest, uint32_t limit) { - uint32_t mask; + uint32_t mask; - do { - rc_normalize(rc); - rc->range >>= 1; - rc->code -= rc->range; - mask = (uint32_t)0 - (rc->code >> 31); - rc->code += rc->range & mask; - *dest = (*dest << 1) + (mask + 1); - } while (--limit > 0); + do + { + rc_normalize(rc); + rc->range >>= 1; + rc->code -= rc->range; + mask = (uint32_t)0 - (rc->code >> 31); + rc->code += rc->range & mask; + *dest = (*dest << 1) + (mask + 1); + } while (--limit > 0); } /******** @@ -602,226 +619,252 @@ ********/ /* Get pointer to literal coder probability array. */ -static uint16_t *lzma_literal_probs(struct xz_dec_lzma2 *s) +static uint16_t* lzma_literal_probs(struct xz_dec_lzma2* s) { - uint32_t prev_byte = dict_get(&s->dict, 0); - uint32_t low = prev_byte >> (8 - s->lzma.lc); - uint32_t high = (s->dict.pos & s->lzma.literal_pos_mask) << s->lzma.lc; - return s->lzma.literal[low + high]; + uint32_t prev_byte = dict_get(&s->dict, 0); + uint32_t low = prev_byte >> (8 - s->lzma.lc); + uint32_t high = (s->dict.pos & s->lzma.literal_pos_mask) << s->lzma.lc; + return s->lzma.literal[low + high]; } /* Decode a literal (one 8-bit byte) */ -static void lzma_literal(struct xz_dec_lzma2 *s) +static void lzma_literal(struct xz_dec_lzma2* s) { - uint16_t *probs; - uint32_t symbol; - uint32_t match_byte; - uint32_t match_bit; - uint32_t offset; - uint32_t i; - - probs = lzma_literal_probs(s); - - if (lzma_state_is_literal(s->lzma.state)) { - symbol = rc_bittree(&s->rc, probs, 0x100); - } else { - symbol = 1; - match_byte = dict_get(&s->dict, s->lzma.rep0) << 1; - offset = 0x100; - - do { - match_bit = match_byte & offset; - match_byte <<= 1; - i = offset + match_bit + symbol; - - if (rc_bit(&s->rc, &probs[i])) { - symbol = (symbol << 1) + 1; - offset &= match_bit; - } else { - symbol <<= 1; - offset &= ~match_bit; - } - } while (symbol < 0x100); - } + uint16_t* probs; + uint32_t symbol; + uint32_t match_byte; + uint32_t match_bit; + uint32_t offset; + uint32_t i; + + probs = lzma_literal_probs(s); + + if (lzma_state_is_literal(s->lzma.state)) + { + symbol = rc_bittree(&s->rc, probs, 0x100); + } + else + { + symbol = 1; + match_byte = dict_get(&s->dict, s->lzma.rep0) << 1; + offset = 0x100; + + do + { + match_bit = match_byte & offset; + match_byte <<= 1; + i = offset + match_bit + symbol; + + if (rc_bit(&s->rc, &probs[i])) + { + symbol = (symbol << 1) + 1; + offset &= match_bit; + } + else + { + symbol <<= 1; + offset &= ~match_bit; + } + } while (symbol < 0x100); + } - dict_put(&s->dict, (uint8_t)symbol); - lzma_state_literal(&s->lzma.state); + dict_put(&s->dict, (uint8_t)symbol); + lzma_state_literal(&s->lzma.state); } /* Decode the length of the match into s->lzma.len. */ -static void lzma_len(struct xz_dec_lzma2 *s, struct lzma_len_dec *l, - uint32_t pos_state) +static void lzma_len(struct xz_dec_lzma2* s, struct lzma_len_dec* l, uint32_t pos_state) { - uint16_t *probs; - uint32_t limit; + uint16_t* probs; + uint32_t limit; - if (!rc_bit(&s->rc, &l->choice)) { - probs = l->low[pos_state]; - limit = LEN_LOW_SYMBOLS; - s->lzma.len = MATCH_LEN_MIN; - } else { - if (!rc_bit(&s->rc, &l->choice2)) { - probs = l->mid[pos_state]; - limit = LEN_MID_SYMBOLS; - s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS; - } else { - probs = l->high; - limit = LEN_HIGH_SYMBOLS; - s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS - + LEN_MID_SYMBOLS; - } - } + if (!rc_bit(&s->rc, &l->choice)) + { + probs = l->low[pos_state]; + limit = LEN_LOW_SYMBOLS; + s->lzma.len = MATCH_LEN_MIN; + } + else + { + if (!rc_bit(&s->rc, &l->choice2)) + { + probs = l->mid[pos_state]; + limit = LEN_MID_SYMBOLS; + s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS; + } + else + { + probs = l->high; + limit = LEN_HIGH_SYMBOLS; + s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS + LEN_MID_SYMBOLS; + } + } - s->lzma.len += rc_bittree(&s->rc, probs, limit) - limit; + s->lzma.len += rc_bittree(&s->rc, probs, limit) - limit; } /* Decode a match. The distance will be stored in s->lzma.rep0. */ -static void lzma_match(struct xz_dec_lzma2 *s, uint32_t pos_state) +static void lzma_match(struct xz_dec_lzma2* s, uint32_t pos_state) { - uint16_t *probs; - uint32_t dist_slot; - uint32_t limit; - - lzma_state_match(&s->lzma.state); - - s->lzma.rep3 = s->lzma.rep2; - s->lzma.rep2 = s->lzma.rep1; - s->lzma.rep1 = s->lzma.rep0; - - lzma_len(s, &s->lzma.match_len_dec, pos_state); - - probs = s->lzma.dist_slot[lzma_get_dist_state(s->lzma.len)]; - dist_slot = rc_bittree(&s->rc, probs, DIST_SLOTS) - DIST_SLOTS; - - if (dist_slot < DIST_MODEL_START) { - s->lzma.rep0 = dist_slot; - } else { - limit = (dist_slot >> 1) - 1; - s->lzma.rep0 = 2 + (dist_slot & 1); - - if (dist_slot < DIST_MODEL_END) { - s->lzma.rep0 <<= limit; - probs = s->lzma.dist_special + s->lzma.rep0 - - dist_slot - 1; - rc_bittree_reverse(&s->rc, probs, - &s->lzma.rep0, limit); - } else { - rc_direct(&s->rc, &s->lzma.rep0, limit - ALIGN_BITS); - s->lzma.rep0 <<= ALIGN_BITS; - rc_bittree_reverse(&s->rc, s->lzma.dist_align, - &s->lzma.rep0, ALIGN_BITS); - } - } + uint16_t* probs; + uint32_t dist_slot; + uint32_t limit; + + lzma_state_match(&s->lzma.state); + + s->lzma.rep3 = s->lzma.rep2; + s->lzma.rep2 = s->lzma.rep1; + s->lzma.rep1 = s->lzma.rep0; + + lzma_len(s, &s->lzma.match_len_dec, pos_state); + + probs = s->lzma.dist_slot[lzma_get_dist_state(s->lzma.len)]; + dist_slot = rc_bittree(&s->rc, probs, DIST_SLOTS) - DIST_SLOTS; + + if (dist_slot < DIST_MODEL_START) + { + s->lzma.rep0 = dist_slot; + } + else + { + limit = (dist_slot >> 1) - 1; + s->lzma.rep0 = 2 + (dist_slot & 1); + + if (dist_slot < DIST_MODEL_END) + { + s->lzma.rep0 <<= limit; + probs = s->lzma.dist_special + s->lzma.rep0 - dist_slot - 1; + rc_bittree_reverse(&s->rc, probs, &s->lzma.rep0, limit); + } + else + { + rc_direct(&s->rc, &s->lzma.rep0, limit - ALIGN_BITS); + s->lzma.rep0 <<= ALIGN_BITS; + rc_bittree_reverse(&s->rc, s->lzma.dist_align, &s->lzma.rep0, ALIGN_BITS); + } + } } /* * Decode a repeated match. The distance is one of the four most recently * seen matches. The distance will be stored in s->lzma.rep0. */ -static void lzma_rep_match(struct xz_dec_lzma2 *s, uint32_t pos_state) +static void lzma_rep_match(struct xz_dec_lzma2* s, uint32_t pos_state) { - uint32_t tmp; + uint32_t tmp; - if (!rc_bit(&s->rc, &s->lzma.is_rep0[s->lzma.state])) { - if (!rc_bit(&s->rc, &s->lzma.is_rep0_long[ - s->lzma.state][pos_state])) { - lzma_state_short_rep(&s->lzma.state); - s->lzma.len = 1; - return; - } - } else { - if (!rc_bit(&s->rc, &s->lzma.is_rep1[s->lzma.state])) { - tmp = s->lzma.rep1; - } else { - if (!rc_bit(&s->rc, &s->lzma.is_rep2[s->lzma.state])) { - tmp = s->lzma.rep2; - } else { - tmp = s->lzma.rep3; - s->lzma.rep3 = s->lzma.rep2; - } - - s->lzma.rep2 = s->lzma.rep1; - } - - s->lzma.rep1 = s->lzma.rep0; - s->lzma.rep0 = tmp; - } + if (!rc_bit(&s->rc, &s->lzma.is_rep0[s->lzma.state])) + { + if (!rc_bit(&s->rc, &s->lzma.is_rep0_long[s->lzma.state][pos_state])) + { + lzma_state_short_rep(&s->lzma.state); + s->lzma.len = 1; + return; + } + } + else + { + if (!rc_bit(&s->rc, &s->lzma.is_rep1[s->lzma.state])) + { + tmp = s->lzma.rep1; + } + else + { + if (!rc_bit(&s->rc, &s->lzma.is_rep2[s->lzma.state])) + { + tmp = s->lzma.rep2; + } + else + { + tmp = s->lzma.rep3; + s->lzma.rep3 = s->lzma.rep2; + } + + s->lzma.rep2 = s->lzma.rep1; + } + + s->lzma.rep1 = s->lzma.rep0; + s->lzma.rep0 = tmp; + } - lzma_state_long_rep(&s->lzma.state); - lzma_len(s, &s->lzma.rep_len_dec, pos_state); + lzma_state_long_rep(&s->lzma.state); + lzma_len(s, &s->lzma.rep_len_dec, pos_state); } /* LZMA decoder core */ -static bool lzma_main(struct xz_dec_lzma2 *s) +static bool lzma_main(struct xz_dec_lzma2* s) { - uint32_t pos_state; + uint32_t pos_state; - /* - * If the dictionary was reached during the previous call, try to - * finish the possibly pending repeat in the dictionary. - */ - if (dict_has_space(&s->dict) && s->lzma.len > 0) - dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0); - - /* - * Decode more LZMA symbols. One iteration may consume up to - * LZMA_IN_REQUIRED - 1 bytes. - */ - while (dict_has_space(&s->dict) && !rc_limit_exceeded(&s->rc)) { - pos_state = s->dict.pos & s->lzma.pos_mask; - - if (!rc_bit(&s->rc, &s->lzma.is_match[ - s->lzma.state][pos_state])) { - lzma_literal(s); - } else { - if (rc_bit(&s->rc, &s->lzma.is_rep[s->lzma.state])) - lzma_rep_match(s, pos_state); - else - lzma_match(s, pos_state); - - if (!dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0)) - return false; - } - } - - /* - * Having the range decoder always normalized when we are outside - * this function makes it easier to correctly handle end of the chunk. - */ - rc_normalize(&s->rc); + /* + * If the dictionary was reached during the previous call, try to + * finish the possibly pending repeat in the dictionary. + */ + if (dict_has_space(&s->dict) && s->lzma.len > 0) + dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0); + + /* + * Decode more LZMA symbols. One iteration may consume up to + * LZMA_IN_REQUIRED - 1 bytes. + */ + while (dict_has_space(&s->dict) && !rc_limit_exceeded(&s->rc)) + { + pos_state = s->dict.pos & s->lzma.pos_mask; + + if (!rc_bit(&s->rc, &s->lzma.is_match[s->lzma.state][pos_state])) + { + lzma_literal(s); + } + else + { + if (rc_bit(&s->rc, &s->lzma.is_rep[s->lzma.state])) + lzma_rep_match(s, pos_state); + else + lzma_match(s, pos_state); + + if (!dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0)) + return false; + } + } + + /* + * Having the range decoder always normalized when we are outside + * this function makes it easier to correctly handle end of the chunk. + */ + rc_normalize(&s->rc); - return true; + return true; } /* * Reset the LZMA decoder and range decoder state. Dictionary is nore reset * here, because LZMA state may be reset without resetting the dictionary. */ -static void lzma_reset(struct xz_dec_lzma2 *s) +static void lzma_reset(struct xz_dec_lzma2* s) { - uint16_t *probs; - size_t i; + uint16_t* probs; + size_t i; - s->lzma.state = STATE_LIT_LIT; - s->lzma.rep0 = 0; - s->lzma.rep1 = 0; - s->lzma.rep2 = 0; - s->lzma.rep3 = 0; - - /* - * All probabilities are initialized to the same value. This hack - * makes the code smaller by avoiding a separate loop for each - * probability array. - * - * This could be optimized so that only that part of literal - * probabilities that are actually required. In the common case - * we would write 12 KiB less. - */ - probs = s->lzma.is_match[0]; - for (i = 0; i < PROBS_TOTAL; ++i) - probs[i] = RC_BIT_MODEL_TOTAL / 2; + s->lzma.state = STATE_LIT_LIT; + s->lzma.rep0 = 0; + s->lzma.rep1 = 0; + s->lzma.rep2 = 0; + s->lzma.rep3 = 0; + + /* + * All probabilities are initialized to the same value. This hack + * makes the code smaller by avoiding a separate loop for each + * probability array. + * + * This could be optimized so that only that part of literal + * probabilities that are actually required. In the common case + * we would write 12 KiB less. + */ + probs = s->lzma.is_match[0]; + for (i = 0; i < PROBS_TOTAL; ++i) + probs[i] = RC_BIT_MODEL_TOTAL / 2; - rc_reset(&s->rc); + rc_reset(&s->rc); } /* @@ -829,35 +872,37 @@ * from the decoded lp and pb values. On success, the LZMA decoder state is * reset and true is returned. */ -static bool lzma_props(struct xz_dec_lzma2 *s, uint8_t props) +static bool lzma_props(struct xz_dec_lzma2* s, uint8_t props) { - if (props > (4 * 5 + 4) * 9 + 8) - return false; + if (props > (4 * 5 + 4) * 9 + 8) + return false; - s->lzma.pos_mask = 0; - while (props >= 9 * 5) { - props -= 9 * 5; - ++s->lzma.pos_mask; - } + s->lzma.pos_mask = 0; + while (props >= 9 * 5) + { + props -= 9 * 5; + ++s->lzma.pos_mask; + } - s->lzma.pos_mask = (1 << s->lzma.pos_mask) - 1; + s->lzma.pos_mask = (1 << s->lzma.pos_mask) - 1; - s->lzma.literal_pos_mask = 0; - while (props >= 9) { - props -= 9; - ++s->lzma.literal_pos_mask; - } + s->lzma.literal_pos_mask = 0; + while (props >= 9) + { + props -= 9; + ++s->lzma.literal_pos_mask; + } - s->lzma.lc = props; + s->lzma.lc = props; - if (s->lzma.lc + s->lzma.literal_pos_mask > 4) - return false; + if (s->lzma.lc + s->lzma.literal_pos_mask > 4) + return false; - s->lzma.literal_pos_mask = (1 << s->lzma.literal_pos_mask) - 1; + s->lzma.literal_pos_mask = (1 << s->lzma.literal_pos_mask) - 1; - lzma_reset(s); + lzma_reset(s); - return true; + return true; } /********* @@ -876,331 +921,342 @@ * function. We decode a few bytes from the temporary buffer so that we can * continue decoding from the caller-supplied input buffer again. */ -static bool lzma2_lzma(struct xz_dec_lzma2 *s, struct xz_buf *b) +static bool lzma2_lzma(struct xz_dec_lzma2* s, struct xz_buf* b) { - size_t in_avail; - uint32_t tmp; + size_t in_avail; + uint32_t tmp; - in_avail = b->in_size - b->in_pos; - if (s->temp.size > 0 || s->lzma2.compressed == 0) { - tmp = 2 * LZMA_IN_REQUIRED - s->temp.size; - if (tmp > s->lzma2.compressed - s->temp.size) - tmp = s->lzma2.compressed - s->temp.size; - if (tmp > in_avail) - tmp = in_avail; - - memcpy(s->temp.buf + s->temp.size, b->in + b->in_pos, tmp); - - if (s->temp.size + tmp == s->lzma2.compressed) { - memzero(s->temp.buf + s->temp.size + tmp, - sizeof(s->temp.buf) - - s->temp.size - tmp); - s->rc.in_limit = s->temp.size + tmp; - } else if (s->temp.size + tmp < LZMA_IN_REQUIRED) { - s->temp.size += tmp; - b->in_pos += tmp; - return true; - } else { - s->rc.in_limit = s->temp.size + tmp - LZMA_IN_REQUIRED; - } - - s->rc.in = s->temp.buf; - s->rc.in_pos = 0; - - if (!lzma_main(s) || s->rc.in_pos > s->temp.size + tmp) - return false; - - s->lzma2.compressed -= s->rc.in_pos; - - if (s->rc.in_pos < s->temp.size) { - s->temp.size -= s->rc.in_pos; - memmove(s->temp.buf, s->temp.buf + s->rc.in_pos, - s->temp.size); - return true; - } - - b->in_pos += s->rc.in_pos - s->temp.size; - s->temp.size = 0; - } - - in_avail = b->in_size - b->in_pos; - if (in_avail >= LZMA_IN_REQUIRED) { - s->rc.in = b->in; - s->rc.in_pos = b->in_pos; - - if (in_avail >= s->lzma2.compressed + LZMA_IN_REQUIRED) - s->rc.in_limit = b->in_pos + s->lzma2.compressed; - else - s->rc.in_limit = b->in_size - LZMA_IN_REQUIRED; - - if (!lzma_main(s)) - return false; - - in_avail = s->rc.in_pos - b->in_pos; - if (in_avail > s->lzma2.compressed) - return false; - - s->lzma2.compressed -= in_avail; - b->in_pos = s->rc.in_pos; - } - - in_avail = b->in_size - b->in_pos; - if (in_avail < LZMA_IN_REQUIRED) { - if (in_avail > s->lzma2.compressed) - in_avail = s->lzma2.compressed; - - memcpy(s->temp.buf, b->in + b->in_pos, in_avail); - s->temp.size = in_avail; - b->in_pos += in_avail; - } + in_avail = b->in_size - b->in_pos; + if (s->temp.size > 0 || s->lzma2.compressed == 0) + { + tmp = 2 * LZMA_IN_REQUIRED - s->temp.size; + if (tmp > s->lzma2.compressed - s->temp.size) + tmp = s->lzma2.compressed - s->temp.size; + if (tmp > in_avail) + tmp = in_avail; + + memcpy(s->temp.buf + s->temp.size, b->in + b->in_pos, tmp); + + if (s->temp.size + tmp == s->lzma2.compressed) + { + memzero(s->temp.buf + s->temp.size + tmp, sizeof(s->temp.buf) - s->temp.size - tmp); + s->rc.in_limit = s->temp.size + tmp; + } + else if (s->temp.size + tmp < LZMA_IN_REQUIRED) + { + s->temp.size += tmp; + b->in_pos += tmp; + return true; + } + else + { + s->rc.in_limit = s->temp.size + tmp - LZMA_IN_REQUIRED; + } + + s->rc.in = s->temp.buf; + s->rc.in_pos = 0; + + if (!lzma_main(s) || s->rc.in_pos > s->temp.size + tmp) + return false; + + s->lzma2.compressed -= s->rc.in_pos; + + if (s->rc.in_pos < s->temp.size) + { + s->temp.size -= s->rc.in_pos; + memmove(s->temp.buf, s->temp.buf + s->rc.in_pos, s->temp.size); + return true; + } + + b->in_pos += s->rc.in_pos - s->temp.size; + s->temp.size = 0; + } + + in_avail = b->in_size - b->in_pos; + if (in_avail >= LZMA_IN_REQUIRED) + { + s->rc.in = b->in; + s->rc.in_pos = b->in_pos; + + if (in_avail >= s->lzma2.compressed + LZMA_IN_REQUIRED) + s->rc.in_limit = b->in_pos + s->lzma2.compressed; + else + s->rc.in_limit = b->in_size - LZMA_IN_REQUIRED; + + if (!lzma_main(s)) + return false; + + in_avail = s->rc.in_pos - b->in_pos; + if (in_avail > s->lzma2.compressed) + return false; + + s->lzma2.compressed -= in_avail; + b->in_pos = s->rc.in_pos; + } + + in_avail = b->in_size - b->in_pos; + if (in_avail < LZMA_IN_REQUIRED) + { + if (in_avail > s->lzma2.compressed) + in_avail = s->lzma2.compressed; + + memcpy(s->temp.buf, b->in + b->in_pos, in_avail); + s->temp.size = in_avail; + b->in_pos += in_avail; + } - return true; + return true; } /* * Take care of the LZMA2 control layer, and forward the job of actual LZMA * decoding or copying of uncompressed chunks to other functions. */ -XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s, - struct xz_buf *b) +XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2* s, struct xz_buf* b) { - uint32_t tmp; + uint32_t tmp; - while (b->in_pos < b->in_size || s->lzma2.sequence == SEQ_LZMA_RUN) { - switch (s->lzma2.sequence) { - case SEQ_CONTROL: - /* - * LZMA2 control byte - * - * Exact values: - * 0x00 End marker - * 0x01 Dictionary reset followed by - * an uncompressed chunk - * 0x02 Uncompressed chunk (no dictionary reset) - * - * Highest three bits (s->control & 0xE0): - * 0xE0 Dictionary reset, new properties and state - * reset, followed by LZMA compressed chunk - * 0xC0 New properties and state reset, followed - * by LZMA compressed chunk (no dictionary - * reset) - * 0xA0 State reset using old properties, - * followed by LZMA compressed chunk (no - * dictionary reset) - * 0x80 LZMA chunk (no dictionary or state reset) - * - * For LZMA compressed chunks, the lowest five bits - * (s->control & 1F) are the highest bits of the - * uncompressed size (bits 16-20). - * - * A new LZMA2 stream must begin with a dictionary - * reset. The first LZMA chunk must set new - * properties and reset the LZMA state. - * - * Values that don't match anything described above - * are invalid and we return XZ_DATA_ERROR. - */ - tmp = b->in[b->in_pos++]; - - if (tmp == 0x00) - return XZ_STREAM_END; - - if (tmp >= 0xE0 || tmp == 0x01) { - s->lzma2.need_props = true; - s->lzma2.need_dict_reset = false; - dict_reset(&s->dict, b); - } else if (s->lzma2.need_dict_reset) { - return XZ_DATA_ERROR; - } - - if (tmp >= 0x80) { - s->lzma2.uncompressed = (tmp & 0x1F) << 16; - s->lzma2.sequence = SEQ_UNCOMPRESSED_1; - - if (tmp >= 0xC0) { - /* - * When there are new properties, - * state reset is done at - * SEQ_PROPERTIES. - */ - s->lzma2.need_props = false; - s->lzma2.next_sequence - = SEQ_PROPERTIES; - - } else if (s->lzma2.need_props) { - return XZ_DATA_ERROR; - - } else { - s->lzma2.next_sequence - = SEQ_LZMA_PREPARE; - if (tmp >= 0xA0) - lzma_reset(s); - } - } else { - if (tmp > 0x02) - return XZ_DATA_ERROR; - - s->lzma2.sequence = SEQ_COMPRESSED_0; - s->lzma2.next_sequence = SEQ_COPY; - } - - break; - - case SEQ_UNCOMPRESSED_1: - s->lzma2.uncompressed - += (uint32_t)b->in[b->in_pos++] << 8; - s->lzma2.sequence = SEQ_UNCOMPRESSED_2; - break; - - case SEQ_UNCOMPRESSED_2: - s->lzma2.uncompressed - += (uint32_t)b->in[b->in_pos++] + 1; - s->lzma2.sequence = SEQ_COMPRESSED_0; - break; - - case SEQ_COMPRESSED_0: - s->lzma2.compressed - = (uint32_t)b->in[b->in_pos++] << 8; - s->lzma2.sequence = SEQ_COMPRESSED_1; - break; - - case SEQ_COMPRESSED_1: - s->lzma2.compressed - += (uint32_t)b->in[b->in_pos++] + 1; - s->lzma2.sequence = s->lzma2.next_sequence; - break; - - case SEQ_PROPERTIES: - if (!lzma_props(s, b->in[b->in_pos++])) - return XZ_DATA_ERROR; - - s->lzma2.sequence = SEQ_LZMA_PREPARE; - /* fallthrough */ - - case SEQ_LZMA_PREPARE: - if (s->lzma2.compressed < RC_INIT_BYTES) - return XZ_DATA_ERROR; - - if (!rc_read_init(&s->rc, b)) - return XZ_OK; - - s->lzma2.compressed -= RC_INIT_BYTES; - s->lzma2.sequence = SEQ_LZMA_RUN; - /* fallthrough */ - - case SEQ_LZMA_RUN: - /* - * Set dictionary limit to indicate how much we want - * to be encoded at maximum. Decode new data into the - * dictionary. Flush the new data from dictionary to - * b->out. Check if we finished decoding this chunk. - * In case the dictionary got full but we didn't fill - * the output buffer yet, we may run this loop - * multiple times without changing s->lzma2.sequence. - */ - dict_limit(&s->dict, min_t(size_t, - b->out_size - b->out_pos, - s->lzma2.uncompressed)); - if (!lzma2_lzma(s, b)) - return XZ_DATA_ERROR; - - s->lzma2.uncompressed -= dict_flush(&s->dict, b); - - if (s->lzma2.uncompressed == 0) { - if (s->lzma2.compressed > 0 || s->lzma.len > 0 - || !rc_is_finished(&s->rc)) - return XZ_DATA_ERROR; - - rc_reset(&s->rc); - s->lzma2.sequence = SEQ_CONTROL; - - } else if (b->out_pos == b->out_size - || (b->in_pos == b->in_size - && s->temp.size - < s->lzma2.compressed)) { - return XZ_OK; - } - - break; - - case SEQ_COPY: - dict_uncompressed(&s->dict, b, &s->lzma2.compressed); - if (s->lzma2.compressed > 0) - return XZ_OK; - - s->lzma2.sequence = SEQ_CONTROL; - break; - } - } - - return XZ_OK; -} - -XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode, - uint32_t dict_max) -{ - struct xz_dec_lzma2 *s = kmalloc(sizeof(*s), GFP_KERNEL); - if (s == NULL) - return NULL; - - s->dict.mode = mode; - s->dict.size_max = dict_max; - - if (DEC_IS_PREALLOC(mode)) { - s->dict.buf = vmalloc(dict_max); - if (s->dict.buf == NULL) { - kfree(s); - return NULL; - } - } else if (DEC_IS_DYNALLOC(mode)) { - s->dict.buf = NULL; - s->dict.allocated = 0; - } - - return s; -} - -XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, uint8_t props) -{ - /* This limits dictionary size to 3 GiB to keep parsing simpler. */ - if (props > 39) - return XZ_OPTIONS_ERROR; - - s->dict.size = 2 + (props & 1); - s->dict.size <<= (props >> 1) + 11; - - if (DEC_IS_MULTI(s->dict.mode)) { - if (s->dict.size > s->dict.size_max) - return XZ_MEMLIMIT_ERROR; - - s->dict.end = s->dict.size; - - if (DEC_IS_DYNALLOC(s->dict.mode)) { - if (s->dict.allocated < s->dict.size) { - vfree(s->dict.buf); - s->dict.buf = vmalloc(s->dict.size); - if (s->dict.buf == NULL) { - s->dict.allocated = 0; - return XZ_MEM_ERROR; - } - } - } - } - - s->lzma.len = 0; - - s->lzma2.sequence = SEQ_CONTROL; - s->lzma2.need_dict_reset = true; - - s->temp.size = 0; - - return XZ_OK; + while (b->in_pos < b->in_size || s->lzma2.sequence == SEQ_LZMA_RUN) + { + switch (s->lzma2.sequence) + { + case SEQ_CONTROL: + /* + * LZMA2 control byte + * + * Exact values: + * 0x00 End marker + * 0x01 Dictionary reset followed by + * an uncompressed chunk + * 0x02 Uncompressed chunk (no dictionary reset) + * + * Highest three bits (s->control & 0xE0): + * 0xE0 Dictionary reset, new properties and state + * reset, followed by LZMA compressed chunk + * 0xC0 New properties and state reset, followed + * by LZMA compressed chunk (no dictionary + * reset) + * 0xA0 State reset using old properties, + * followed by LZMA compressed chunk (no + * dictionary reset) + * 0x80 LZMA chunk (no dictionary or state reset) + * + * For LZMA compressed chunks, the lowest five bits + * (s->control & 1F) are the highest bits of the + * uncompressed size (bits 16-20). + * + * A new LZMA2 stream must begin with a dictionary + * reset. The first LZMA chunk must set new + * properties and reset the LZMA state. + * + * Values that don't match anything described above + * are invalid and we return XZ_DATA_ERROR. + */ + tmp = b->in[b->in_pos++]; + + if (tmp == 0x00) + return XZ_STREAM_END; + + if (tmp >= 0xE0 || tmp == 0x01) + { + s->lzma2.need_props = true; + s->lzma2.need_dict_reset = false; + dict_reset(&s->dict, b); + } + else if (s->lzma2.need_dict_reset) + { + return XZ_DATA_ERROR; + } + + if (tmp >= 0x80) + { + s->lzma2.uncompressed = (tmp & 0x1F) << 16; + s->lzma2.sequence = SEQ_UNCOMPRESSED_1; + + if (tmp >= 0xC0) + { + /* + * When there are new properties, + * state reset is done at + * SEQ_PROPERTIES. + */ + s->lzma2.need_props = false; + s->lzma2.next_sequence = SEQ_PROPERTIES; + } + else if (s->lzma2.need_props) + { + return XZ_DATA_ERROR; + } + else + { + s->lzma2.next_sequence = SEQ_LZMA_PREPARE; + if (tmp >= 0xA0) + lzma_reset(s); + } + } + else + { + if (tmp > 0x02) + return XZ_DATA_ERROR; + + s->lzma2.sequence = SEQ_COMPRESSED_0; + s->lzma2.next_sequence = SEQ_COPY; + } + + break; + + case SEQ_UNCOMPRESSED_1: + s->lzma2.uncompressed += (uint32_t)b->in[b->in_pos++] << 8; + s->lzma2.sequence = SEQ_UNCOMPRESSED_2; + break; + + case SEQ_UNCOMPRESSED_2: + s->lzma2.uncompressed += (uint32_t)b->in[b->in_pos++] + 1; + s->lzma2.sequence = SEQ_COMPRESSED_0; + break; + + case SEQ_COMPRESSED_0: + s->lzma2.compressed = (uint32_t)b->in[b->in_pos++] << 8; + s->lzma2.sequence = SEQ_COMPRESSED_1; + break; + + case SEQ_COMPRESSED_1: + s->lzma2.compressed += (uint32_t)b->in[b->in_pos++] + 1; + s->lzma2.sequence = s->lzma2.next_sequence; + break; + + case SEQ_PROPERTIES: + if (!lzma_props(s, b->in[b->in_pos++])) + return XZ_DATA_ERROR; + + s->lzma2.sequence = SEQ_LZMA_PREPARE; + + case SEQ_LZMA_PREPARE: + if (s->lzma2.compressed < RC_INIT_BYTES) + return XZ_DATA_ERROR; + + if (!rc_read_init(&s->rc, b)) + return XZ_OK; + + s->lzma2.compressed -= RC_INIT_BYTES; + s->lzma2.sequence = SEQ_LZMA_RUN; + + case SEQ_LZMA_RUN: + /* + * Set dictionary limit to indicate how much we want + * to be encoded at maximum. Decode new data into the + * dictionary. Flush the new data from dictionary to + * b->out. Check if we finished decoding this chunk. + * In case the dictionary got full but we didn't fill + * the output buffer yet, we may run this loop + * multiple times without changing s->lzma2.sequence. + */ + dict_limit(&s->dict, min_t(size_t, b->out_size - b->out_pos, s->lzma2.uncompressed)); + if (!lzma2_lzma(s, b)) + return XZ_DATA_ERROR; + + s->lzma2.uncompressed -= dict_flush(&s->dict, b); + + if (s->lzma2.uncompressed == 0) + { + if (s->lzma2.compressed > 0 || s->lzma.len > 0 || !rc_is_finished(&s->rc)) + return XZ_DATA_ERROR; + + rc_reset(&s->rc); + s->lzma2.sequence = SEQ_CONTROL; + } + else if (b->out_pos == b->out_size || (b->in_pos == b->in_size && s->temp.size < s->lzma2.compressed)) + { + return XZ_OK; + } + + break; + + case SEQ_COPY: + dict_uncompressed(&s->dict, b, &s->lzma2.compressed); + if (s->lzma2.compressed > 0) + return XZ_OK; + + s->lzma2.sequence = SEQ_CONTROL; + break; + } + } + + return XZ_OK; +} + +XZ_EXTERN struct xz_dec_lzma2* xz_dec_lzma2_create(enum xz_mode mode, uint32_t dict_max) +{ + struct xz_dec_lzma2* s = kmalloc(sizeof(*s), GFP_KERNEL); + if (s == NULL) + return NULL; + + s->dict.mode = mode; + s->dict.size_max = dict_max; + + if (DEC_IS_PREALLOC(mode)) + { + s->dict.buf = vmalloc(dict_max); + if (s->dict.buf == NULL) + { + kfree(s); + return NULL; + } + } + else if (DEC_IS_DYNALLOC(mode)) + { + s->dict.buf = NULL; + s->dict.allocated = 0; + } + + return s; +} + +XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2* s, uint8_t props) +{ + /* This limits dictionary size to 3 GiB to keep parsing simpler. */ + if (props > 39) + return XZ_OPTIONS_ERROR; + + s->dict.size = 2 + (props & 1); + s->dict.size <<= (props >> 1) + 11; + + if (DEC_IS_MULTI(s->dict.mode)) + { + if (s->dict.size > s->dict.size_max) + return XZ_MEMLIMIT_ERROR; + + s->dict.end = s->dict.size; + + if (DEC_IS_DYNALLOC(s->dict.mode)) + { + if (s->dict.allocated < s->dict.size) + { + vfree(s->dict.buf); + s->dict.buf = vmalloc(s->dict.size); + if (s->dict.buf == NULL) + { + s->dict.allocated = 0; + return XZ_MEM_ERROR; + } + } + } + } + + s->lzma.len = 0; + + s->lzma2.sequence = SEQ_CONTROL; + s->lzma2.need_dict_reset = true; + + s->temp.size = 0; + + return XZ_OK; } -XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s) +XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2* s) { - if (DEC_IS_MULTI(s->dict.mode)) - vfree(s->dict.buf); + if (DEC_IS_MULTI(s->dict.mode)) + vfree(s->dict.buf); - kfree(s); + kfree(s); } diff -Nru mstflint-4.17.0+1/ext_libs/minixz/xz_dec_stream.c mstflint-4.21.0+1/ext_libs/minixz/xz_dec_stream.c --- mstflint-4.17.0+1/ext_libs/minixz/xz_dec_stream.c 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/minixz/xz_dec_stream.c 2022-07-31 09:33:57.000000000 +0000 @@ -1,6 +1,7 @@ /* * Copyright (C) Jan 2006 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -44,141 +45,142 @@ #include "xz_stream.h" /* Hash used to validate the Index field */ -struct xz_dec_hash { - vli_type unpadded; - vli_type uncompressed; - uint32_t crc32; +struct xz_dec_hash +{ + vli_type unpadded; + vli_type uncompressed; + uint32_t crc32; }; -struct xz_dec { - /* Position in dec_main() */ - enum { - SEQ_STREAM_HEADER, - SEQ_BLOCK_START, - SEQ_BLOCK_HEADER, - SEQ_BLOCK_UNCOMPRESS, - SEQ_BLOCK_PADDING, - SEQ_BLOCK_CHECK, - SEQ_INDEX, - SEQ_INDEX_PADDING, - SEQ_INDEX_CRC32, - SEQ_STREAM_FOOTER - } sequence; - - /* Position in variable-length integers and Check fields */ - uint32_t pos; - - /* Variable-length integer decoded by dec_vli() */ - vli_type vli; - - /* Saved in_pos and out_pos */ - size_t in_start; - size_t out_start; - - /* CRC32 value in Block or Index */ - uint32_t crc32; - - /* Type of the integrity check calculated from uncompressed data */ - enum xz_check check_type; - - /* Operation mode */ - enum xz_mode mode; - - /* - * True if the next call to xz_dec_run() is allowed to return - * XZ_BUF_ERROR. - */ - bool allow_buf_error; - - /* Information stored in Block Header */ - struct { - /* - * Value stored in the Compressed Size field, or - * VLI_UNKNOWN if Compressed Size is not present. - */ - vli_type compressed; - - /* - * Value stored in the Uncompressed Size field, or - * VLI_UNKNOWN if Uncompressed Size is not present. - */ - vli_type uncompressed; - - /* Size of the Block Header field */ - uint32_t size; - } block_header; - - /* Information collected when decoding Blocks */ - struct { - /* Observed compressed size of the current Block */ - vli_type compressed; - - /* Observed uncompressed size of the current Block */ - vli_type uncompressed; - - /* Number of Blocks decoded so far */ - vli_type count; - - /* - * Hash calculated from the Block sizes. This is used to - * validate the Index field. - */ - struct xz_dec_hash hash; - } block; - - /* Variables needed when verifying the Index field */ - struct { - /* Position in dec_index() */ - enum { - SEQ_INDEX_COUNT, - SEQ_INDEX_UNPADDED, - SEQ_INDEX_UNCOMPRESSED - } sequence; - - /* Size of the Index in bytes */ - vli_type size; - - /* Number of Records (matches block.count in valid files) */ - vli_type count; - - /* - * Hash calculated from the Records (matches block.hash in - * valid files). - */ - struct xz_dec_hash hash; - } index; - - /* - * Temporary buffer needed to hold Stream Header, Block Header, - * and Stream Footer. The Block Header is the biggest (1 KiB) - * so we reserve space according to that. buf[] has to be aligned - * to a multiple of four bytes; the size_t variables before it - * should guarantee this. - */ - struct { - size_t pos; - size_t size; - uint8_t buf[1024]; - } temp; +struct xz_dec +{ + /* Position in dec_main() */ + enum + { + SEQ_STREAM_HEADER, + SEQ_BLOCK_START, + SEQ_BLOCK_HEADER, + SEQ_BLOCK_UNCOMPRESS, + SEQ_BLOCK_PADDING, + SEQ_BLOCK_CHECK, + SEQ_INDEX, + SEQ_INDEX_PADDING, + SEQ_INDEX_CRC32, + SEQ_STREAM_FOOTER + } sequence; + + /* Position in variable-length integers and Check fields */ + uint32_t pos; + + /* Variable-length integer decoded by dec_vli() */ + vli_type vli; + + /* Saved in_pos and out_pos */ + size_t in_start; + size_t out_start; + + /* CRC32 value in Block or Index */ + uint32_t crc32; + + /* Type of the integrity check calculated from uncompressed data */ + enum xz_check check_type; + + /* Operation mode */ + enum xz_mode mode; + + /* + * True if the next call to xz_dec_run() is allowed to return + * XZ_BUF_ERROR. + */ + bool allow_buf_error; + + /* Information stored in Block Header */ + struct + { + /* + * Value stored in the Compressed Size field, or + * VLI_UNKNOWN if Compressed Size is not present. + */ + vli_type compressed; + + /* + * Value stored in the Uncompressed Size field, or + * VLI_UNKNOWN if Uncompressed Size is not present. + */ + vli_type uncompressed; + + /* Size of the Block Header field */ + uint32_t size; + } block_header; + + /* Information collected when decoding Blocks */ + struct + { + /* Observed compressed size of the current Block */ + vli_type compressed; + + /* Observed uncompressed size of the current Block */ + vli_type uncompressed; + + /* Number of Blocks decoded so far */ + vli_type count; + + /* + * Hash calculated from the Block sizes. This is used to + * validate the Index field. + */ + struct xz_dec_hash hash; + } block; + + /* Variables needed when verifying the Index field */ + struct + { + /* Position in dec_index() */ + enum + { + SEQ_INDEX_COUNT, + SEQ_INDEX_UNPADDED, + SEQ_INDEX_UNCOMPRESSED + } sequence; + + /* Size of the Index in bytes */ + vli_type size; + + /* Number of Records (matches block.count in valid files) */ + vli_type count; + + /* + * Hash calculated from the Records (matches block.hash in + * valid files). + */ + struct xz_dec_hash hash; + } index; + + /* + * Temporary buffer needed to hold Stream Header, Block Header, + * and Stream Footer. The Block Header is the biggest (1 KiB) + * so we reserve space according to that. buf[] has to be aligned + * to a multiple of four bytes; the size_t variables before it + * should guarantee this. + */ + struct + { + size_t pos; + size_t size; + uint8_t buf[1024]; + } temp; - struct xz_dec_lzma2 *lzma2; + struct xz_dec_lzma2* lzma2; #ifdef XZ_DEC_BCJ - struct xz_dec_bcj *bcj; - bool bcj_active; + struct xz_dec_bcj* bcj; + bool bcj_active; #endif }; #ifdef XZ_DEC_ANY_CHECK /* Sizes of the Check field with different Check IDs */ -static const uint8_t check_sizes[16] = { - 0, - 4, 4, 4, - 8, 8, 8, - 16, 16, 16, - 32, 32, 32, - 64, 64, 64 -}; +static const uint8_t check_sizes[16] = {0, 4, 4, 4, 8, 8, 8, 16, 16, 16, 32, 32, 32, 64, 64, 64}; #endif /* @@ -187,53 +189,54 @@ * to copy into s->temp.buf. Return true once s->temp.pos has reached * s->temp.size. */ -static bool fill_temp(struct xz_dec *s, struct xz_buf *b) +static bool fill_temp(struct xz_dec* s, struct xz_buf* b) { - size_t copy_size = min_t(size_t, - b->in_size - b->in_pos, s->temp.size - s->temp.pos); + size_t copy_size = min_t(size_t, b->in_size - b->in_pos, s->temp.size - s->temp.pos); - memcpy(s->temp.buf + s->temp.pos, b->in + b->in_pos, copy_size); - b->in_pos += copy_size; - s->temp.pos += copy_size; + memcpy(s->temp.buf + s->temp.pos, b->in + b->in_pos, copy_size); + b->in_pos += copy_size; + s->temp.pos += copy_size; - if (s->temp.pos == s->temp.size) { - s->temp.pos = 0; - return true; - } + if (s->temp.pos == s->temp.size) + { + s->temp.pos = 0; + return true; + } - return false; + return false; } /* Decode a variable-length integer (little-endian base-128 encoding) */ -static enum xz_ret dec_vli(struct xz_dec *s, const uint8_t *in, - size_t *in_pos, size_t in_size) +static enum xz_ret dec_vli(struct xz_dec* s, const uint8_t* in, size_t* in_pos, size_t in_size) { - uint8_t byte; + uint8_t byte; - if (s->pos == 0) - s->vli = 0; + if (s->pos == 0) + s->vli = 0; - while (*in_pos < in_size) { - byte = in[*in_pos]; - ++*in_pos; + while (*in_pos < in_size) + { + byte = in[*in_pos]; + ++*in_pos; - s->vli |= (vli_type)(byte & 0x7F) << s->pos; + s->vli |= (vli_type)(byte & 0x7F) << s->pos; - if ((byte & 0x80) == 0) { - /* Don't allow non-minimal encodings. */ - if (byte == 0 && s->pos != 0) - return XZ_DATA_ERROR; + if ((byte & 0x80) == 0) + { + /* Don't allow non-minimal encodings. */ + if (byte == 0 && s->pos != 0) + return XZ_DATA_ERROR; - s->pos = 0; - return XZ_STREAM_END; - } + s->pos = 0; + return XZ_STREAM_END; + } - s->pos += 7; - if (s->pos == 7 * VLI_BYTES_MAX) - return XZ_DATA_ERROR; - } + s->pos += 7; + if (s->pos == 7 * VLI_BYTES_MAX) + return XZ_DATA_ERROR; + } - return XZ_OK; + return XZ_OK; } /* @@ -248,74 +251,65 @@ * the sizes possibly stored in the Block Header. Update the hash and * Block count, which are later used to validate the Index field. */ -static enum xz_ret dec_block(struct xz_dec *s, struct xz_buf *b) +static enum xz_ret dec_block(struct xz_dec* s, struct xz_buf* b) { - enum xz_ret ret; + enum xz_ret ret; - s->in_start = b->in_pos; - s->out_start = b->out_pos; + s->in_start = b->in_pos; + s->out_start = b->out_pos; #ifdef XZ_DEC_BCJ - if (s->bcj_active) - ret = xz_dec_bcj_run(s->bcj, s->lzma2, b); - else -#endif - ret = xz_dec_lzma2_run(s->lzma2, b); - - s->block.compressed += b->in_pos - s->in_start; - s->block.uncompressed += b->out_pos - s->out_start; - - /* - * There is no need to separately check for VLI_UNKNOWN, since - * the observed sizes are always smaller than VLI_UNKNOWN. - */ - if (s->block.compressed > s->block_header.compressed - || s->block.uncompressed - > s->block_header.uncompressed) - return XZ_DATA_ERROR; - - if (s->check_type == XZ_CHECK_CRC32) - s->crc32 = xz_crc32(b->out + s->out_start, - b->out_pos - s->out_start, s->crc32); - - if (ret == XZ_STREAM_END) { - if (s->block_header.compressed != VLI_UNKNOWN - && s->block_header.compressed - != s->block.compressed) - return XZ_DATA_ERROR; - - if (s->block_header.uncompressed != VLI_UNKNOWN - && s->block_header.uncompressed - != s->block.uncompressed) - return XZ_DATA_ERROR; + if (s->bcj_active) + ret = xz_dec_bcj_run(s->bcj, s->lzma2, b); + else +#endif + ret = xz_dec_lzma2_run(s->lzma2, b); + + s->block.compressed += b->in_pos - s->in_start; + s->block.uncompressed += b->out_pos - s->out_start; + + /* + * There is no need to separately check for VLI_UNKNOWN, since + * the observed sizes are always smaller than VLI_UNKNOWN. + */ + if (s->block.compressed > s->block_header.compressed || s->block.uncompressed > s->block_header.uncompressed) + return XZ_DATA_ERROR; + + if (s->check_type == XZ_CHECK_CRC32) + s->crc32 = xz_crc32(b->out + s->out_start, b->out_pos - s->out_start, s->crc32); + + if (ret == XZ_STREAM_END) + { + if (s->block_header.compressed != VLI_UNKNOWN && s->block_header.compressed != s->block.compressed) + return XZ_DATA_ERROR; + + if (s->block_header.uncompressed != VLI_UNKNOWN && s->block_header.uncompressed != s->block.uncompressed) + return XZ_DATA_ERROR; - s->block.hash.unpadded += s->block_header.size - + s->block.compressed; + s->block.hash.unpadded += s->block_header.size + s->block.compressed; #ifdef XZ_DEC_ANY_CHECK - s->block.hash.unpadded += check_sizes[s->check_type]; + s->block.hash.unpadded += check_sizes[s->check_type]; #else - if (s->check_type == XZ_CHECK_CRC32) - s->block.hash.unpadded += 4; + if (s->check_type == XZ_CHECK_CRC32) + s->block.hash.unpadded += 4; #endif - s->block.hash.uncompressed += s->block.uncompressed; - s->block.hash.crc32 = xz_crc32( - (const uint8_t *)&s->block.hash, - sizeof(s->block.hash), s->block.hash.crc32); + s->block.hash.uncompressed += s->block.uncompressed; + s->block.hash.crc32 = xz_crc32((const uint8_t*)&s->block.hash, sizeof(s->block.hash), s->block.hash.crc32); - ++s->block.count; - } + ++s->block.count; + } - return ret; + return ret; } /* Update the Index size and the CRC32 value. */ -static void index_update(struct xz_dec *s, const struct xz_buf *b) +static void index_update(struct xz_dec* s, const struct xz_buf* b) { - size_t in_used = b->in_pos - s->in_start; - s->index.size += in_used; - s->crc32 = xz_crc32(b->in + s->in_start, in_used, s->crc32); + size_t in_used = b->in_pos - s->in_start; + s->index.size += in_used; + s->crc32 = xz_crc32(b->in + s->in_start, in_used, s->crc32); } /* @@ -326,73 +320,75 @@ * This can return XZ_OK (more input needed), XZ_STREAM_END (everything * successfully decoded), or XZ_DATA_ERROR (input is corrupt). */ -static enum xz_ret dec_index(struct xz_dec *s, struct xz_buf *b) +static enum xz_ret dec_index(struct xz_dec* s, struct xz_buf* b) { - enum xz_ret ret; + enum xz_ret ret; - do { - ret = dec_vli(s, b->in, &b->in_pos, b->in_size); - if (ret != XZ_STREAM_END) { - index_update(s, b); - return ret; - } - - switch (s->index.sequence) { - case SEQ_INDEX_COUNT: - s->index.count = s->vli; - - /* - * Validate that the Number of Records field - * indicates the same number of Records as - * there were Blocks in the Stream. - */ - if (s->index.count != s->block.count) - return XZ_DATA_ERROR; - - s->index.sequence = SEQ_INDEX_UNPADDED; - break; - - case SEQ_INDEX_UNPADDED: - s->index.hash.unpadded += s->vli; - s->index.sequence = SEQ_INDEX_UNCOMPRESSED; - break; - - case SEQ_INDEX_UNCOMPRESSED: - s->index.hash.uncompressed += s->vli; - s->index.hash.crc32 = xz_crc32( - (const uint8_t *)&s->index.hash, - sizeof(s->index.hash), - s->index.hash.crc32); - --s->index.count; - s->index.sequence = SEQ_INDEX_UNPADDED; - break; - } - } while (s->index.count > 0); + do + { + ret = dec_vli(s, b->in, &b->in_pos, b->in_size); + if (ret != XZ_STREAM_END) + { + index_update(s, b); + return ret; + } + + switch (s->index.sequence) + { + case SEQ_INDEX_COUNT: + s->index.count = s->vli; + + /* + * Validate that the Number of Records field + * indicates the same number of Records as + * there were Blocks in the Stream. + */ + if (s->index.count != s->block.count) + return XZ_DATA_ERROR; + + s->index.sequence = SEQ_INDEX_UNPADDED; + break; + + case SEQ_INDEX_UNPADDED: + s->index.hash.unpadded += s->vli; + s->index.sequence = SEQ_INDEX_UNCOMPRESSED; + break; + + case SEQ_INDEX_UNCOMPRESSED: + s->index.hash.uncompressed += s->vli; + s->index.hash.crc32 = + xz_crc32((const uint8_t*)&s->index.hash, sizeof(s->index.hash), s->index.hash.crc32); + --s->index.count; + s->index.sequence = SEQ_INDEX_UNPADDED; + break; + } + } while (s->index.count > 0); - return XZ_STREAM_END; + return XZ_STREAM_END; } /* * Validate that the next four input bytes match the value of s->crc32. * s->pos must be zero when starting to validate the first byte. */ -static enum xz_ret crc32_validate(struct xz_dec *s, struct xz_buf *b) +static enum xz_ret crc32_validate(struct xz_dec* s, struct xz_buf* b) { - do { - if (b->in_pos == b->in_size) - return XZ_OK; + do + { + if (b->in_pos == b->in_size) + return XZ_OK; - if (((s->crc32 >> s->pos) & 0xFF) != b->in[b->in_pos++]) - return XZ_DATA_ERROR; + if (((s->crc32 >> s->pos) & 0xFF) != b->in[b->in_pos++]) + return XZ_DATA_ERROR; - s->pos += 8; + s->pos += 8; - } while (s->pos < 32); + } while (s->pos < 32); - s->crc32 = 0; - s->pos = 0; + s->crc32 = 0; + s->pos = 0; - return XZ_STREAM_END; + return XZ_STREAM_END; } #ifdef XZ_DEC_ANY_CHECK @@ -400,351 +396,352 @@ * Skip over the Check field when the Check ID is not supported. * Returns true once the whole Check field has been skipped over. */ -static bool check_skip(struct xz_dec *s, struct xz_buf *b) +static bool check_skip(struct xz_dec* s, struct xz_buf* b) { - while (s->pos < check_sizes[s->check_type]) { - if (b->in_pos == b->in_size) - return false; + while (s->pos < check_sizes[s->check_type]) + { + if (b->in_pos == b->in_size) + return false; - ++b->in_pos; - ++s->pos; - } + ++b->in_pos; + ++s->pos; + } - s->pos = 0; + s->pos = 0; - return true; + return true; } #endif /* Decode the Stream Header field (the first 12 bytes of the .xz Stream). */ -static enum xz_ret dec_stream_header(struct xz_dec *s) +static enum xz_ret dec_stream_header(struct xz_dec* s) { - if (!memeq(s->temp.buf, HEADER_MAGIC, HEADER_MAGIC_SIZE)) - return XZ_FORMAT_ERROR; + if (!memeq(s->temp.buf, HEADER_MAGIC, HEADER_MAGIC_SIZE)) + return XZ_FORMAT_ERROR; - if (xz_crc32(s->temp.buf + HEADER_MAGIC_SIZE, 2, 0) - != get_le32(s->temp.buf + HEADER_MAGIC_SIZE + 2)) - return XZ_DATA_ERROR; - - if (s->temp.buf[HEADER_MAGIC_SIZE] != 0) - return XZ_OPTIONS_ERROR; - - /* - * Of integrity checks, we support only none (Check ID = 0) and - * CRC32 (Check ID = 1). However, if XZ_DEC_ANY_CHECK is defined, - * we will accept other check types too, but then the check won't - * be verified and a warning (XZ_UNSUPPORTED_CHECK) will be given. - */ - s->check_type = s->temp.buf[HEADER_MAGIC_SIZE + 1]; + if (xz_crc32(s->temp.buf + HEADER_MAGIC_SIZE, 2, 0) != get_le32(s->temp.buf + HEADER_MAGIC_SIZE + 2)) + return XZ_DATA_ERROR; + + if (s->temp.buf[HEADER_MAGIC_SIZE] != 0) + return XZ_OPTIONS_ERROR; + + /* + * Of integrity checks, we support only none (Check ID = 0) and + * CRC32 (Check ID = 1). However, if XZ_DEC_ANY_CHECK is defined, + * we will accept other check types too, but then the check won't + * be verified and a warning (XZ_UNSUPPORTED_CHECK) will be given. + */ + s->check_type = s->temp.buf[HEADER_MAGIC_SIZE + 1]; #ifdef XZ_DEC_ANY_CHECK - if (s->check_type > XZ_CHECK_MAX) - return XZ_OPTIONS_ERROR; + if (s->check_type > XZ_CHECK_MAX) + return XZ_OPTIONS_ERROR; - if (s->check_type > XZ_CHECK_CRC32) - return XZ_UNSUPPORTED_CHECK; + if (s->check_type > XZ_CHECK_CRC32) + return XZ_UNSUPPORTED_CHECK; #else - if (s->check_type > XZ_CHECK_CRC32) - return XZ_OPTIONS_ERROR; + if (s->check_type > XZ_CHECK_CRC32) + return XZ_OPTIONS_ERROR; #endif - return XZ_OK; + return XZ_OK; } /* Decode the Stream Footer field (the last 12 bytes of the .xz Stream) */ -static enum xz_ret dec_stream_footer(struct xz_dec *s) +static enum xz_ret dec_stream_footer(struct xz_dec* s) { - if (!memeq(s->temp.buf + 10, FOOTER_MAGIC, FOOTER_MAGIC_SIZE)) - return XZ_DATA_ERROR; + if (!memeq(s->temp.buf + 10, FOOTER_MAGIC, FOOTER_MAGIC_SIZE)) + return XZ_DATA_ERROR; - if (xz_crc32(s->temp.buf + 4, 6, 0) != get_le32(s->temp.buf)) - return XZ_DATA_ERROR; + if (xz_crc32(s->temp.buf + 4, 6, 0) != get_le32(s->temp.buf)) + return XZ_DATA_ERROR; - /* - * Validate Backward Size. Note that we never added the size of the - * Index CRC32 field to s->index.size, thus we use s->index.size / 4 - * instead of s->index.size / 4 - 1. - */ - if ((s->index.size >> 2) != get_le32(s->temp.buf + 4)) - return XZ_DATA_ERROR; - - if (s->temp.buf[8] != 0 || s->temp.buf[9] != s->check_type) - return XZ_DATA_ERROR; - - /* - * Use XZ_STREAM_END instead of XZ_OK to be more convenient - * for the caller. - */ - return XZ_STREAM_END; + /* + * Validate Backward Size. Note that we never added the size of the + * Index CRC32 field to s->index.size, thus we use s->index.size / 4 + * instead of s->index.size / 4 - 1. + */ + if ((s->index.size >> 2) != get_le32(s->temp.buf + 4)) + return XZ_DATA_ERROR; + + if (s->temp.buf[8] != 0 || s->temp.buf[9] != s->check_type) + return XZ_DATA_ERROR; + + /* + * Use XZ_STREAM_END instead of XZ_OK to be more convenient + * for the caller. + */ + return XZ_STREAM_END; } /* Decode the Block Header and initialize the filter chain. */ -static enum xz_ret dec_block_header(struct xz_dec *s) +static enum xz_ret dec_block_header(struct xz_dec* s) { - enum xz_ret ret; + enum xz_ret ret; - /* - * Validate the CRC32. We know that the temp buffer is at least - * eight bytes so this is safe. - */ - s->temp.size -= 4; - if (xz_crc32(s->temp.buf, s->temp.size, 0) - != get_le32(s->temp.buf + s->temp.size)) - return XZ_DATA_ERROR; - - s->temp.pos = 2; - - /* - * Catch unsupported Block Flags. We support only one or two filters - * in the chain, so we catch that with the same test. - */ + /* + * Validate the CRC32. We know that the temp buffer is at least + * eight bytes so this is safe. + */ + s->temp.size -= 4; + if (xz_crc32(s->temp.buf, s->temp.size, 0) != get_le32(s->temp.buf + s->temp.size)) + return XZ_DATA_ERROR; + + s->temp.pos = 2; + + /* + * Catch unsupported Block Flags. We support only one or two filters + * in the chain, so we catch that with the same test. + */ #ifdef XZ_DEC_BCJ - if (s->temp.buf[1] & 0x3E) + if (s->temp.buf[1] & 0x3E) #else - if (s->temp.buf[1] & 0x3F) + if (s->temp.buf[1] & 0x3F) #endif - return XZ_OPTIONS_ERROR; + return XZ_OPTIONS_ERROR; - /* Compressed Size */ - if (s->temp.buf[1] & 0x40) { - if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size) - != XZ_STREAM_END) - return XZ_DATA_ERROR; - - s->block_header.compressed = s->vli; - } else { - s->block_header.compressed = VLI_UNKNOWN; - } - - /* Uncompressed Size */ - if (s->temp.buf[1] & 0x80) { - if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size) - != XZ_STREAM_END) - return XZ_DATA_ERROR; - - s->block_header.uncompressed = s->vli; - } else { - s->block_header.uncompressed = VLI_UNKNOWN; - } + /* Compressed Size */ + if (s->temp.buf[1] & 0x40) + { + if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size) != XZ_STREAM_END) + return XZ_DATA_ERROR; + + s->block_header.compressed = s->vli; + } + else + { + s->block_header.compressed = VLI_UNKNOWN; + } + + /* Uncompressed Size */ + if (s->temp.buf[1] & 0x80) + { + if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size) != XZ_STREAM_END) + return XZ_DATA_ERROR; + + s->block_header.uncompressed = s->vli; + } + else + { + s->block_header.uncompressed = VLI_UNKNOWN; + } #ifdef XZ_DEC_BCJ - /* If there are two filters, the first one must be a BCJ filter. */ - s->bcj_active = s->temp.buf[1] & 0x01; - if (s->bcj_active) { - if (s->temp.size - s->temp.pos < 2) - return XZ_OPTIONS_ERROR; - - ret = xz_dec_bcj_reset(s->bcj, s->temp.buf[s->temp.pos++]); - if (ret != XZ_OK) - return ret; - - /* - * We don't support custom start offset, - * so Size of Properties must be zero. - */ - if (s->temp.buf[s->temp.pos++] != 0x00) - return XZ_OPTIONS_ERROR; - } -#endif - - /* Valid Filter Flags always take at least two bytes. */ - if (s->temp.size - s->temp.pos < 2) - return XZ_DATA_ERROR; - - /* Filter ID = LZMA2 */ - if (s->temp.buf[s->temp.pos++] != 0x21) - return XZ_OPTIONS_ERROR; - - /* Size of Properties = 1-byte Filter Properties */ - if (s->temp.buf[s->temp.pos++] != 0x01) - return XZ_OPTIONS_ERROR; - - /* Filter Properties contains LZMA2 dictionary size. */ - if (s->temp.size - s->temp.pos < 1) - return XZ_DATA_ERROR; - - ret = xz_dec_lzma2_reset(s->lzma2, s->temp.buf[s->temp.pos++]); - if (ret != XZ_OK) - return ret; - - /* The rest must be Header Padding. */ - while (s->temp.pos < s->temp.size) - if (s->temp.buf[s->temp.pos++] != 0x00) - return XZ_OPTIONS_ERROR; - - s->temp.pos = 0; - s->block.compressed = 0; - s->block.uncompressed = 0; - - return XZ_OK; -} - -static enum xz_ret dec_main(struct xz_dec *s, struct xz_buf *b) -{ - enum xz_ret ret; - - /* - * Store the start position for the case when we are in the middle - * of the Index field. - */ - s->in_start = b->in_pos; - - while (true) { - switch (s->sequence) { - case SEQ_STREAM_HEADER: - /* - * Stream Header is copied to s->temp, and then - * decoded from there. This way if the caller - * gives us only little input at a time, we can - * still keep the Stream Header decoding code - * simple. Similar approach is used in many places - * in this file. - */ - if (!fill_temp(s, b)) - return XZ_OK; - - /* - * If dec_stream_header() returns - * XZ_UNSUPPORTED_CHECK, it is still possible - * to continue decoding if working in multi-call - * mode. Thus, update s->sequence before calling - * dec_stream_header(). - */ - s->sequence = SEQ_BLOCK_START; - - ret = dec_stream_header(s); - if (ret != XZ_OK) - return ret; - /* fallthrough */ - - case SEQ_BLOCK_START: - /* We need one byte of input to continue. */ - if (b->in_pos == b->in_size) - return XZ_OK; - - /* See if this is the beginning of the Index field. */ - if (b->in[b->in_pos] == 0) { - s->in_start = b->in_pos++; - s->sequence = SEQ_INDEX; - break; - } - - /* - * Calculate the size of the Block Header and - * prepare to decode it. - */ - s->block_header.size - = ((uint32_t)b->in[b->in_pos] + 1) * 4; - - s->temp.size = s->block_header.size; - s->temp.pos = 0; - s->sequence = SEQ_BLOCK_HEADER; - /* fallthrough */ - - case SEQ_BLOCK_HEADER: - if (!fill_temp(s, b)) - return XZ_OK; - - ret = dec_block_header(s); - if (ret != XZ_OK) - return ret; - - s->sequence = SEQ_BLOCK_UNCOMPRESS; - /* fallthrough */ - - case SEQ_BLOCK_UNCOMPRESS: - ret = dec_block(s, b); - if (ret != XZ_STREAM_END) - return ret; - - s->sequence = SEQ_BLOCK_PADDING; - /* fallthrough */ - - case SEQ_BLOCK_PADDING: - /* - * Size of Compressed Data + Block Padding - * must be a multiple of four. We don't need - * s->block.compressed for anything else - * anymore, so we use it here to test the size - * of the Block Padding field. - */ - while (s->block.compressed & 3) { - if (b->in_pos == b->in_size) - return XZ_OK; - - if (b->in[b->in_pos++] != 0) - return XZ_DATA_ERROR; - - ++s->block.compressed; - } - - s->sequence = SEQ_BLOCK_CHECK; - /* fallthrough */ - - case SEQ_BLOCK_CHECK: - if (s->check_type == XZ_CHECK_CRC32) { - ret = crc32_validate(s, b); - if (ret != XZ_STREAM_END) - return ret; - } + /* If there are two filters, the first one must be a BCJ filter. */ + s->bcj_active = s->temp.buf[1] & 0x01; + if (s->bcj_active) + { + if (s->temp.size - s->temp.pos < 2) + return XZ_OPTIONS_ERROR; + + ret = xz_dec_bcj_reset(s->bcj, s->temp.buf[s->temp.pos++]); + if (ret != XZ_OK) + return ret; + + /* + * We don't support custom start offset, + * so Size of Properties must be zero. + */ + if (s->temp.buf[s->temp.pos++] != 0x00) + return XZ_OPTIONS_ERROR; + } +#endif + + /* Valid Filter Flags always take at least two bytes. */ + if (s->temp.size - s->temp.pos < 2) + return XZ_DATA_ERROR; + + /* Filter ID = LZMA2 */ + if (s->temp.buf[s->temp.pos++] != 0x21) + return XZ_OPTIONS_ERROR; + + /* Size of Properties = 1-byte Filter Properties */ + if (s->temp.buf[s->temp.pos++] != 0x01) + return XZ_OPTIONS_ERROR; + + /* Filter Properties contains LZMA2 dictionary size. */ + if (s->temp.size - s->temp.pos < 1) + return XZ_DATA_ERROR; + + ret = xz_dec_lzma2_reset(s->lzma2, s->temp.buf[s->temp.pos++]); + if (ret != XZ_OK) + return ret; + + /* The rest must be Header Padding. */ + while (s->temp.pos < s->temp.size) + if (s->temp.buf[s->temp.pos++] != 0x00) + return XZ_OPTIONS_ERROR; + + s->temp.pos = 0; + s->block.compressed = 0; + s->block.uncompressed = 0; + + return XZ_OK; +} + +static enum xz_ret dec_main(struct xz_dec* s, struct xz_buf* b) +{ + enum xz_ret ret; + + /* + * Store the start position for the case when we are in the middle + * of the Index field. + */ + s->in_start = b->in_pos; + + while (true) + { + switch (s->sequence) + { + case SEQ_STREAM_HEADER: + /* + * Stream Header is copied to s->temp, and then + * decoded from there. This way if the caller + * gives us only little input at a time, we can + * still keep the Stream Header decoding code + * simple. Similar approach is used in many places + * in this file. + */ + if (!fill_temp(s, b)) + return XZ_OK; + + /* + * If dec_stream_header() returns + * XZ_UNSUPPORTED_CHECK, it is still possible + * to continue decoding if working in multi-call + * mode. Thus, update s->sequence before calling + * dec_stream_header(). + */ + s->sequence = SEQ_BLOCK_START; + + ret = dec_stream_header(s); + if (ret != XZ_OK) + return ret; + + case SEQ_BLOCK_START: + /* We need one byte of input to continue. */ + if (b->in_pos == b->in_size) + return XZ_OK; + + /* See if this is the beginning of the Index field. */ + if (b->in[b->in_pos] == 0) + { + s->in_start = b->in_pos++; + s->sequence = SEQ_INDEX; + break; + } + + /* + * Calculate the size of the Block Header and + * prepare to decode it. + */ + s->block_header.size = ((uint32_t)b->in[b->in_pos] + 1) * 4; + + s->temp.size = s->block_header.size; + s->temp.pos = 0; + s->sequence = SEQ_BLOCK_HEADER; + + case SEQ_BLOCK_HEADER: + if (!fill_temp(s, b)) + return XZ_OK; + + ret = dec_block_header(s); + if (ret != XZ_OK) + return ret; + + s->sequence = SEQ_BLOCK_UNCOMPRESS; + + case SEQ_BLOCK_UNCOMPRESS: + ret = dec_block(s, b); + if (ret != XZ_STREAM_END) + return ret; + + s->sequence = SEQ_BLOCK_PADDING; + + case SEQ_BLOCK_PADDING: + /* + * Size of Compressed Data + Block Padding + * must be a multiple of four. We don't need + * s->block.compressed for anything else + * anymore, so we use it here to test the size + * of the Block Padding field. + */ + while (s->block.compressed & 3) + { + if (b->in_pos == b->in_size) + return XZ_OK; + + if (b->in[b->in_pos++] != 0) + return XZ_DATA_ERROR; + + ++s->block.compressed; + } + + s->sequence = SEQ_BLOCK_CHECK; + + case SEQ_BLOCK_CHECK: + if (s->check_type == XZ_CHECK_CRC32) + { + ret = crc32_validate(s, b); + if (ret != XZ_STREAM_END) + return ret; + } #ifdef XZ_DEC_ANY_CHECK - else if (!check_skip(s, b)) { - return XZ_OK; - } -#endif - - s->sequence = SEQ_BLOCK_START; - break; - - case SEQ_INDEX: - ret = dec_index(s, b); - if (ret != XZ_STREAM_END) - return ret; - - s->sequence = SEQ_INDEX_PADDING; - /* fallthrough */ - - case SEQ_INDEX_PADDING: - while ((s->index.size + (b->in_pos - s->in_start)) - & 3) { - if (b->in_pos == b->in_size) { - index_update(s, b); - return XZ_OK; - } - - if (b->in[b->in_pos++] != 0) - return XZ_DATA_ERROR; - } - - /* Finish the CRC32 value and Index size. */ - index_update(s, b); - - /* Compare the hashes to validate the Index field. */ - if (!memeq(&s->block.hash, &s->index.hash, - sizeof(s->block.hash))) - return XZ_DATA_ERROR; - - s->sequence = SEQ_INDEX_CRC32; - /* fallthrough */ - - case SEQ_INDEX_CRC32: - ret = crc32_validate(s, b); - if (ret != XZ_STREAM_END) - return ret; - - s->temp.size = STREAM_HEADER_SIZE; - s->sequence = SEQ_STREAM_FOOTER; - /* fallthrough */ - - case SEQ_STREAM_FOOTER: - if (!fill_temp(s, b)) - return XZ_OK; - - return dec_stream_footer(s); - } - } + else if (!check_skip(s, b)) + { + return XZ_OK; + } +#endif + + s->sequence = SEQ_BLOCK_START; + break; + + case SEQ_INDEX: + ret = dec_index(s, b); + if (ret != XZ_STREAM_END) + return ret; + + s->sequence = SEQ_INDEX_PADDING; + + case SEQ_INDEX_PADDING: + while ((s->index.size + (b->in_pos - s->in_start)) & 3) + { + if (b->in_pos == b->in_size) + { + index_update(s, b); + return XZ_OK; + } + + if (b->in[b->in_pos++] != 0) + return XZ_DATA_ERROR; + } + + /* Finish the CRC32 value and Index size. */ + index_update(s, b); + + /* Compare the hashes to validate the Index field. */ + if (!memeq(&s->block.hash, &s->index.hash, sizeof(s->block.hash))) + return XZ_DATA_ERROR; + + s->sequence = SEQ_INDEX_CRC32; - /* Never reached */ + case SEQ_INDEX_CRC32: + ret = crc32_validate(s, b); + if (ret != XZ_STREAM_END) + return ret; + + s->temp.size = STREAM_HEADER_SIZE; + s->sequence = SEQ_STREAM_FOOTER; + + case SEQ_STREAM_FOOTER: + if (!fill_temp(s, b)) + return XZ_OK; + + return dec_stream_footer(s); + } + } + + /* Never reached */ } /* @@ -772,91 +769,95 @@ * actually succeeds (that's the price to pay of using the output buffer as * the workspace). */ -XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b) -{ - size_t in_start; - size_t out_start; - enum xz_ret ret; - - if (DEC_IS_SINGLE(s->mode)) - xz_dec_reset(s); - - in_start = b->in_pos; - out_start = b->out_pos; - ret = dec_main(s, b); - - if (DEC_IS_SINGLE(s->mode)) { - if (ret == XZ_OK) - ret = b->in_pos == b->in_size - ? XZ_DATA_ERROR : XZ_BUF_ERROR; - - if (ret != XZ_STREAM_END) { - b->in_pos = in_start; - b->out_pos = out_start; - } - - } else if (ret == XZ_OK && in_start == b->in_pos - && out_start == b->out_pos) { - if (s->allow_buf_error) - ret = XZ_BUF_ERROR; - - s->allow_buf_error = true; - } else { - s->allow_buf_error = false; - } - - return ret; -} - -XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max) +XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec* s, struct xz_buf* b) { - struct xz_dec *s = kmalloc(sizeof(*s), GFP_KERNEL); - if (s == NULL) - return NULL; + size_t in_start; + size_t out_start; + enum xz_ret ret; + + if (DEC_IS_SINGLE(s->mode)) + xz_dec_reset(s); + + in_start = b->in_pos; + out_start = b->out_pos; + ret = dec_main(s, b); + + if (DEC_IS_SINGLE(s->mode)) + { + if (ret == XZ_OK) + ret = b->in_pos == b->in_size ? XZ_DATA_ERROR : XZ_BUF_ERROR; + + if (ret != XZ_STREAM_END) + { + b->in_pos = in_start; + b->out_pos = out_start; + } + } + else if (ret == XZ_OK && in_start == b->in_pos && out_start == b->out_pos) + { + if (s->allow_buf_error) + ret = XZ_BUF_ERROR; + + s->allow_buf_error = true; + } + else + { + s->allow_buf_error = false; + } + + return ret; +} + +XZ_EXTERN struct xz_dec* xz_dec_init(enum xz_mode mode, uint32_t dict_max) +{ + struct xz_dec* s = kmalloc(sizeof(*s), GFP_KERNEL); + if (s == NULL) + return NULL; - s->mode = mode; + s->mode = mode; #ifdef XZ_DEC_BCJ - s->bcj = xz_dec_bcj_create(DEC_IS_SINGLE(mode)); - if (s->bcj == NULL) - goto error_bcj; + s->bcj = xz_dec_bcj_create(DEC_IS_SINGLE(mode)); + if (s->bcj == NULL) + goto error_bcj; #endif - s->lzma2 = xz_dec_lzma2_create(mode, dict_max); - if (s->lzma2 == NULL) - goto error_lzma2; + s->lzma2 = xz_dec_lzma2_create(mode, dict_max); + if (s->lzma2 == NULL) + goto error_lzma2; - xz_dec_reset(s); - return s; + xz_dec_reset(s); + return s; error_lzma2: #ifdef XZ_DEC_BCJ - xz_dec_bcj_end(s->bcj); + xz_dec_bcj_end(s->bcj); error_bcj: #endif - kfree(s); - return NULL; + kfree(s); + return NULL; } -XZ_EXTERN void xz_dec_reset(struct xz_dec *s) +XZ_EXTERN void xz_dec_reset(struct xz_dec* s) { - s->sequence = SEQ_STREAM_HEADER; - s->allow_buf_error = false; - s->pos = 0; - s->crc32 = 0; - memzero(&s->block, sizeof(s->block)); - memzero(&s->index, sizeof(s->index)); - s->temp.pos = 0; - s->temp.size = STREAM_HEADER_SIZE; + s->sequence = SEQ_STREAM_HEADER; + s->allow_buf_error = false; + s->pos = 0; + s->crc32 = 0; + memzero(&s->block, sizeof(s->block)); + memzero(&s->index, sizeof(s->index)); + s->temp.pos = 0; + s->temp.size = STREAM_HEADER_SIZE; } -XZ_EXTERN void xz_dec_end(struct xz_dec *s) +XZ_EXTERN void xz_dec_end(struct xz_dec* s) { - if (s != NULL) { - xz_dec_lzma2_end(s->lzma2); + if (s != NULL) + { + xz_dec_lzma2_end(s->lzma2); #ifdef XZ_DEC_BCJ - xz_dec_bcj_end(s->bcj); + xz_dec_bcj_end(s->bcj); #endif - kfree(s); - } + kfree(s); + } } diff -Nru mstflint-4.17.0+1/ext_libs/minixz/xz.h mstflint-4.21.0+1/ext_libs/minixz/xz.h --- mstflint-4.17.0+1/ext_libs/minixz/xz.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/minixz/xz.h 2022-07-31 09:33:57.000000000 +0000 @@ -42,230 +42,234 @@ #define XZ_H #ifdef __KERNEL__ -# include -# include +#include +#include #else -# include -# include +#include +#include #endif #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif /* In Linux, this is used to make extern functions static when needed. */ #ifndef XZ_EXTERN -# define XZ_EXTERN extern +#define XZ_EXTERN extern #endif -/** - * enum xz_mode - Operation mode - * - * @XZ_SINGLE: Single-call mode. This uses less RAM than - * than multi-call modes, because the LZMA2 - * dictionary doesn't need to be allocated as - * part of the decoder state. All required data - * structures are allocated at initialization, - * so xz_dec_run() cannot return XZ_MEM_ERROR. - * @XZ_PREALLOC: Multi-call mode with preallocated LZMA2 - * dictionary buffer. All data structures are - * allocated at initialization, so xz_dec_run() - * cannot return XZ_MEM_ERROR. - * @XZ_DYNALLOC: Multi-call mode. The LZMA2 dictionary is - * allocated once the required size has been - * parsed from the stream headers. If the - * allocation fails, xz_dec_run() will return - * XZ_MEM_ERROR. - * - * It is possible to enable support only for a subset of the above - * modes at compile time by defining XZ_DEC_SINGLE, XZ_DEC_PREALLOC, - * or XZ_DEC_DYNALLOC. The xz_dec kernel module is always compiled - * with support for all operation modes, but the preboot code may - * be built with fewer features to minimize code size. - */ -enum xz_mode { - XZ_SINGLE, - XZ_PREALLOC, - XZ_DYNALLOC -}; - -/** - * enum xz_ret - Return codes - * @XZ_OK: Everything is OK so far. More input or more - * output space is required to continue. This - * return code is possible only in multi-call mode - * (XZ_PREALLOC or XZ_DYNALLOC). - * @XZ_STREAM_END: Operation finished successfully. - * @XZ_UNSUPPORTED_CHECK: Integrity check type is not supported. Decoding - * is still possible in multi-call mode by simply - * calling xz_dec_run() again. - * Note that this return value is used only if - * XZ_DEC_ANY_CHECK was defined at build time, - * which is not used in the kernel. Unsupported - * check types return XZ_OPTIONS_ERROR if - * XZ_DEC_ANY_CHECK was not defined at build time. - * @XZ_MEM_ERROR: Allocating memory failed. This return code is - * possible only if the decoder was initialized - * with XZ_DYNALLOC. The amount of memory that was - * tried to be allocated was no more than the - * dict_max argument given to xz_dec_init(). - * @XZ_MEMLIMIT_ERROR: A bigger LZMA2 dictionary would be needed than - * allowed by the dict_max argument given to - * xz_dec_init(). This return value is possible - * only in multi-call mode (XZ_PREALLOC or - * XZ_DYNALLOC); the single-call mode (XZ_SINGLE) - * ignores the dict_max argument. - * @XZ_FORMAT_ERROR: File format was not recognized (wrong magic - * bytes). - * @XZ_OPTIONS_ERROR: This implementation doesn't support the requested - * compression options. In the decoder this means - * that the header CRC32 matches, but the header - * itself specifies something that we don't support. - * @XZ_DATA_ERROR: Compressed data is corrupt. - * @XZ_BUF_ERROR: Cannot make any progress. Details are slightly - * different between multi-call and single-call - * mode; more information below. - * - * In multi-call mode, XZ_BUF_ERROR is returned when two consecutive calls - * to XZ code cannot consume any input and cannot produce any new output. - * This happens when there is no new input available, or the output buffer - * is full while at least one output byte is still pending. Assuming your - * code is not buggy, you can get this error only when decoding a compressed - * stream that is truncated or otherwise corrupt. - * - * In single-call mode, XZ_BUF_ERROR is returned only when the output buffer - * is too small or the compressed input is corrupt in a way that makes the - * decoder produce more output than the caller expected. When it is - * (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR - * is used instead of XZ_BUF_ERROR. - */ -enum xz_ret { - XZ_OK, - XZ_STREAM_END, - XZ_UNSUPPORTED_CHECK, - XZ_MEM_ERROR, - XZ_MEMLIMIT_ERROR, - XZ_FORMAT_ERROR, - XZ_OPTIONS_ERROR, - XZ_DATA_ERROR, - XZ_BUF_ERROR -}; - -/** - * struct xz_buf - Passing input and output buffers to XZ code - * @in: Beginning of the input buffer. This may be NULL if and only - * if in_pos is equal to in_size. - * @in_pos: Current position in the input buffer. This must not exceed - * in_size. - * @in_size: Size of the input buffer - * @out: Beginning of the output buffer. This may be NULL if and only - * if out_pos is equal to out_size. - * @out_pos: Current position in the output buffer. This must not exceed - * out_size. - * @out_size: Size of the output buffer - * - * Only the contents of the output buffer from out[out_pos] onward, and - * the variables in_pos and out_pos are modified by the XZ code. - */ -struct xz_buf { - const uint8_t *in; - size_t in_pos; - size_t in_size; - - uint8_t *out; - size_t out_pos; - size_t out_size; -}; - -/** - * struct xz_dec - Opaque type to hold the XZ decoder state - */ -struct xz_dec; - -/** - * xz_dec_init() - Allocate and initialize a XZ decoder state - * @mode: Operation mode - * @dict_max: Maximum size of the LZMA2 dictionary (history buffer) for - * multi-call decoding. This is ignored in single-call mode - * (mode == XZ_SINGLE). LZMA2 dictionary is always 2^n bytes - * or 2^n + 2^(n-1) bytes (the latter sizes are less common - * in practice), so other values for dict_max don't make sense. - * In the kernel, dictionary sizes of 64 KiB, 128 KiB, 256 KiB, - * 512 KiB, and 1 MiB are probably the only reasonable values, - * except for kernel and initramfs images where a bigger - * dictionary can be fine and useful. - * - * Single-call mode (XZ_SINGLE): xz_dec_run() decodes the whole stream at - * once. The caller must provide enough output space or the decoding will - * fail. The output space is used as the dictionary buffer, which is why - * there is no need to allocate the dictionary as part of the decoder's - * internal state. - * - * Because the output buffer is used as the workspace, streams encoded using - * a big dictionary are not a problem in single-call mode. It is enough that - * the output buffer is big enough to hold the actual uncompressed data; it - * can be smaller than the dictionary size stored in the stream headers. - * - * Multi-call mode with preallocated dictionary (XZ_PREALLOC): dict_max bytes - * of memory is preallocated for the LZMA2 dictionary. This way there is no - * risk that xz_dec_run() could run out of memory, since xz_dec_run() will - * never allocate any memory. Instead, if the preallocated dictionary is too - * small for decoding the given input stream, xz_dec_run() will return - * XZ_MEMLIMIT_ERROR. Thus, it is important to know what kind of data will be - * decoded to avoid allocating excessive amount of memory for the dictionary. - * - * Multi-call mode with dynamically allocated dictionary (XZ_DYNALLOC): - * dict_max specifies the maximum allowed dictionary size that xz_dec_run() - * may allocate once it has parsed the dictionary size from the stream - * headers. This way excessive allocations can be avoided while still - * limiting the maximum memory usage to a sane value to prevent running the - * system out of memory when decompressing streams from untrusted sources. - * - * On success, xz_dec_init() returns a pointer to struct xz_dec, which is - * ready to be used with xz_dec_run(). If memory allocation fails, - * xz_dec_init() returns NULL. - */ -XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max); - -/** - * xz_dec_run() - Run the XZ decoder - * @s: Decoder state allocated using xz_dec_init() - * @b: Input and output buffers - * - * The possible return values depend on build options and operation mode. - * See enum xz_ret for details. - * - * Note that if an error occurs in single-call mode (return value is not - * XZ_STREAM_END), b->in_pos and b->out_pos are not modified and the - * contents of the output buffer from b->out[b->out_pos] onward are - * undefined. This is true even after XZ_BUF_ERROR, because with some filter - * chains, there may be a second pass over the output buffer, and this pass - * cannot be properly done if the output buffer is truncated. Thus, you - * cannot give the single-call decoder a too small buffer and then expect to - * get that amount valid data from the beginning of the stream. You must use - * the multi-call decoder if you don't want to uncompress the whole stream. - */ -XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b); - -/** - * xz_dec_reset() - Reset an already allocated decoder state - * @s: Decoder state allocated using xz_dec_init() - * - * This function can be used to reset the multi-call decoder state without - * freeing and reallocating memory with xz_dec_end() and xz_dec_init(). - * - * In single-call mode, xz_dec_reset() is always called in the beginning of - * xz_dec_run(). Thus, explicit call to xz_dec_reset() is useful only in - * multi-call mode. - */ -XZ_EXTERN void xz_dec_reset(struct xz_dec *s); - -/** - * xz_dec_end() - Free the memory allocated for the decoder state - * @s: Decoder state allocated using xz_dec_init(). If s is NULL, - * this function does nothing. - */ -XZ_EXTERN void xz_dec_end(struct xz_dec *s); + /** + * enum xz_mode - Operation mode + * + * @XZ_SINGLE: Single-call mode. This uses less RAM than + * than multi-call modes, because the LZMA2 + * dictionary doesn't need to be allocated as + * part of the decoder state. All required data + * structures are allocated at initialization, + * so xz_dec_run() cannot return XZ_MEM_ERROR. + * @XZ_PREALLOC: Multi-call mode with preallocated LZMA2 + * dictionary buffer. All data structures are + * allocated at initialization, so xz_dec_run() + * cannot return XZ_MEM_ERROR. + * @XZ_DYNALLOC: Multi-call mode. The LZMA2 dictionary is + * allocated once the required size has been + * parsed from the stream headers. If the + * allocation fails, xz_dec_run() will return + * XZ_MEM_ERROR. + * + * It is possible to enable support only for a subset of the above + * modes at compile time by defining XZ_DEC_SINGLE, XZ_DEC_PREALLOC, + * or XZ_DEC_DYNALLOC. The xz_dec kernel module is always compiled + * with support for all operation modes, but the preboot code may + * be built with fewer features to minimize code size. + */ + enum xz_mode + { + XZ_SINGLE, + XZ_PREALLOC, + XZ_DYNALLOC + }; + + /** + * enum xz_ret - Return codes + * @XZ_OK: Everything is OK so far. More input or more + * output space is required to continue. This + * return code is possible only in multi-call mode + * (XZ_PREALLOC or XZ_DYNALLOC). + * @XZ_STREAM_END: Operation finished successfully. + * @XZ_UNSUPPORTED_CHECK: Integrity check type is not supported. Decoding + * is still possible in multi-call mode by simply + * calling xz_dec_run() again. + * Note that this return value is used only if + * XZ_DEC_ANY_CHECK was defined at build time, + * which is not used in the kernel. Unsupported + * check types return XZ_OPTIONS_ERROR if + * XZ_DEC_ANY_CHECK was not defined at build time. + * @XZ_MEM_ERROR: Allocating memory failed. This return code is + * possible only if the decoder was initialized + * with XZ_DYNALLOC. The amount of memory that was + * tried to be allocated was no more than the + * dict_max argument given to xz_dec_init(). + * @XZ_MEMLIMIT_ERROR: A bigger LZMA2 dictionary would be needed than + * allowed by the dict_max argument given to + * xz_dec_init(). This return value is possible + * only in multi-call mode (XZ_PREALLOC or + * XZ_DYNALLOC); the single-call mode (XZ_SINGLE) + * ignores the dict_max argument. + * @XZ_FORMAT_ERROR: File format was not recognized (wrong magic + * bytes). + * @XZ_OPTIONS_ERROR: This implementation doesn't support the requested + * compression options. In the decoder this means + * that the header CRC32 matches, but the header + * itself specifies something that we don't support. + * @XZ_DATA_ERROR: Compressed data is corrupt. + * @XZ_BUF_ERROR: Cannot make any progress. Details are slightly + * different between multi-call and single-call + * mode; more information below. + * + * In multi-call mode, XZ_BUF_ERROR is returned when two consecutive calls + * to XZ code cannot consume any input and cannot produce any new output. + * This happens when there is no new input available, or the output buffer + * is full while at least one output byte is still pending. Assuming your + * code is not buggy, you can get this error only when decoding a compressed + * stream that is truncated or otherwise corrupt. + * + * In single-call mode, XZ_BUF_ERROR is returned only when the output buffer + * is too small or the compressed input is corrupt in a way that makes the + * decoder produce more output than the caller expected. When it is + * (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR + * is used instead of XZ_BUF_ERROR. + */ + enum xz_ret + { + XZ_OK, + XZ_STREAM_END, + XZ_UNSUPPORTED_CHECK, + XZ_MEM_ERROR, + XZ_MEMLIMIT_ERROR, + XZ_FORMAT_ERROR, + XZ_OPTIONS_ERROR, + XZ_DATA_ERROR, + XZ_BUF_ERROR + }; + + /** + * struct xz_buf - Passing input and output buffers to XZ code + * @in: Beginning of the input buffer. This may be NULL if and only + * if in_pos is equal to in_size. + * @in_pos: Current position in the input buffer. This must not exceed + * in_size. + * @in_size: Size of the input buffer + * @out: Beginning of the output buffer. This may be NULL if and only + * if out_pos is equal to out_size. + * @out_pos: Current position in the output buffer. This must not exceed + * out_size. + * @out_size: Size of the output buffer + * + * Only the contents of the output buffer from out[out_pos] onward, and + * the variables in_pos and out_pos are modified by the XZ code. + */ + struct xz_buf + { + const uint8_t* in; + size_t in_pos; + size_t in_size; + + uint8_t* out; + size_t out_pos; + size_t out_size; + }; + + /** + * struct xz_dec - Opaque type to hold the XZ decoder state + */ + struct xz_dec; + + /** + * xz_dec_init() - Allocate and initialize a XZ decoder state + * @mode: Operation mode + * @dict_max: Maximum size of the LZMA2 dictionary (history buffer) for + * multi-call decoding. This is ignored in single-call mode + * (mode == XZ_SINGLE). LZMA2 dictionary is always 2^n bytes + * or 2^n + 2^(n-1) bytes (the latter sizes are less common + * in practice), so other values for dict_max don't make sense. + * In the kernel, dictionary sizes of 64 KiB, 128 KiB, 256 KiB, + * 512 KiB, and 1 MiB are probably the only reasonable values, + * except for kernel and initramfs images where a bigger + * dictionary can be fine and useful. + * + * Single-call mode (XZ_SINGLE): xz_dec_run() decodes the whole stream at + * once. The caller must provide enough output space or the decoding will + * fail. The output space is used as the dictionary buffer, which is why + * there is no need to allocate the dictionary as part of the decoder's + * internal state. + * + * Because the output buffer is used as the workspace, streams encoded using + * a big dictionary are not a problem in single-call mode. It is enough that + * the output buffer is big enough to hold the actual uncompressed data; it + * can be smaller than the dictionary size stored in the stream headers. + * + * Multi-call mode with preallocated dictionary (XZ_PREALLOC): dict_max bytes + * of memory is preallocated for the LZMA2 dictionary. This way there is no + * risk that xz_dec_run() could run out of memory, since xz_dec_run() will + * never allocate any memory. Instead, if the preallocated dictionary is too + * small for decoding the given input stream, xz_dec_run() will return + * XZ_MEMLIMIT_ERROR. Thus, it is important to know what kind of data will be + * decoded to avoid allocating excessive amount of memory for the dictionary. + * + * Multi-call mode with dynamically allocated dictionary (XZ_DYNALLOC): + * dict_max specifies the maximum allowed dictionary size that xz_dec_run() + * may allocate once it has parsed the dictionary size from the stream + * headers. This way excessive allocations can be avoided while still + * limiting the maximum memory usage to a sane value to prevent running the + * system out of memory when decompressing streams from untrusted sources. + * + * On success, xz_dec_init() returns a pointer to struct xz_dec, which is + * ready to be used with xz_dec_run(). If memory allocation fails, + * xz_dec_init() returns NULL. + */ + XZ_EXTERN struct xz_dec* xz_dec_init(enum xz_mode mode, uint32_t dict_max); + + /** + * xz_dec_run() - Run the XZ decoder + * @s: Decoder state allocated using xz_dec_init() + * @b: Input and output buffers + * + * The possible return values depend on build options and operation mode. + * See enum xz_ret for details. + * + * Note that if an error occurs in single-call mode (return value is not + * XZ_STREAM_END), b->in_pos and b->out_pos are not modified and the + * contents of the output buffer from b->out[b->out_pos] onward are + * undefined. This is true even after XZ_BUF_ERROR, because with some filter + * chains, there may be a second pass over the output buffer, and this pass + * cannot be properly done if the output buffer is truncated. Thus, you + * cannot give the single-call decoder a too small buffer and then expect to + * get that amount valid data from the beginning of the stream. You must use + * the multi-call decoder if you don't want to uncompress the whole stream. + */ + XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec* s, struct xz_buf* b); + + /** + * xz_dec_reset() - Reset an already allocated decoder state + * @s: Decoder state allocated using xz_dec_init() + * + * This function can be used to reset the multi-call decoder state without + * freeing and reallocating memory with xz_dec_end() and xz_dec_init(). + * + * In single-call mode, xz_dec_reset() is always called in the beginning of + * xz_dec_run(). Thus, explicit call to xz_dec_reset() is useful only in + * multi-call mode. + */ + XZ_EXTERN void xz_dec_reset(struct xz_dec* s); + + /** + * xz_dec_end() - Free the memory allocated for the decoder state + * @s: Decoder state allocated using xz_dec_init(). If s is NULL, + * this function does nothing. + */ + XZ_EXTERN void xz_dec_end(struct xz_dec* s); /* * Standalone build (userspace build or in-kernel build for boot time use) @@ -274,26 +278,26 @@ * care about the functions below. */ #ifndef XZ_INTERNAL_CRC32 -# ifdef __KERNEL__ -# define XZ_INTERNAL_CRC32 0 -# else -# define XZ_INTERNAL_CRC32 1 -# endif +#ifdef __KERNEL__ +#define XZ_INTERNAL_CRC32 0 +#else +#define XZ_INTERNAL_CRC32 1 +#endif #endif #if XZ_INTERNAL_CRC32 -/* - * This must be called before any other xz_* function to initialize - * the CRC32 lookup table. - */ -XZ_EXTERN void xz_crc32_init(void); - -/* - * Update CRC32 value using the polynomial from IEEE-802.3. To start a new - * calculation, the third argument must be zero. To continue the calculation, - * the previously returned value is passed as the third argument. - */ -XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc); + /* + * This must be called before any other xz_* function to initialize + * the CRC32 lookup table. + */ + XZ_EXTERN void xz_crc32_init(void); + + /* + * Update CRC32 value using the polynomial from IEEE-802.3. To start a new + * calculation, the third argument must be zero. To continue the calculation, + * the previously returned value is passed as the third argument. + */ + XZ_EXTERN uint32_t xz_crc32(const uint8_t* buf, size_t size, uint32_t crc); #endif #ifdef __cplusplus diff -Nru mstflint-4.17.0+1/ext_libs/minixz/xz_lzma2.h mstflint-4.21.0+1/ext_libs/minixz/xz_lzma2.h --- mstflint-4.17.0+1/ext_libs/minixz/xz_lzma2.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/minixz/xz_lzma2.h 2022-07-31 09:33:57.000000000 +0000 @@ -72,19 +72,20 @@ * The symbol names are in from STATE_oldest_older_previous. REP means * either short or long repeated match, and NONLIT means any non-literal. */ -enum lzma_state { - STATE_LIT_LIT, - STATE_MATCH_LIT_LIT, - STATE_REP_LIT_LIT, - STATE_SHORTREP_LIT_LIT, - STATE_MATCH_LIT, - STATE_REP_LIT, - STATE_SHORTREP_LIT, - STATE_LIT_MATCH, - STATE_LIT_LONGREP, - STATE_LIT_SHORTREP, - STATE_NONLIT_MATCH, - STATE_NONLIT_REP +enum lzma_state +{ + STATE_LIT_LIT, + STATE_MATCH_LIT_LIT, + STATE_REP_LIT_LIT, + STATE_SHORTREP_LIT_LIT, + STATE_MATCH_LIT, + STATE_REP_LIT, + STATE_SHORTREP_LIT, + STATE_LIT_MATCH, + STATE_LIT_LONGREP, + STATE_LIT_SHORTREP, + STATE_NONLIT_MATCH, + STATE_NONLIT_REP }; /* Total number of states */ @@ -94,38 +95,38 @@ #define LIT_STATES 7 /* Indicate that the latest symbol was a literal. */ -static inline void lzma_state_literal(enum lzma_state *state) +static inline void lzma_state_literal(enum lzma_state* state) { - if (*state <= STATE_SHORTREP_LIT_LIT) - *state = STATE_LIT_LIT; - else if (*state <= STATE_LIT_SHORTREP) - *state -= 3; - else - *state -= 6; + if (*state <= STATE_SHORTREP_LIT_LIT) + *state = STATE_LIT_LIT; + else if (*state <= STATE_LIT_SHORTREP) + *state -= 3; + else + *state -= 6; } /* Indicate that the latest symbol was a match. */ -static inline void lzma_state_match(enum lzma_state *state) +static inline void lzma_state_match(enum lzma_state* state) { - *state = *state < LIT_STATES ? STATE_LIT_MATCH : STATE_NONLIT_MATCH; + *state = *state < LIT_STATES ? STATE_LIT_MATCH : STATE_NONLIT_MATCH; } /* Indicate that the latest state was a long repeated match. */ -static inline void lzma_state_long_rep(enum lzma_state *state) +static inline void lzma_state_long_rep(enum lzma_state* state) { - *state = *state < LIT_STATES ? STATE_LIT_LONGREP : STATE_NONLIT_REP; + *state = *state < LIT_STATES ? STATE_LIT_LONGREP : STATE_NONLIT_REP; } /* Indicate that the latest symbol was a short match. */ -static inline void lzma_state_short_rep(enum lzma_state *state) +static inline void lzma_state_short_rep(enum lzma_state* state) { - *state = *state < LIT_STATES ? STATE_LIT_SHORTREP : STATE_NONLIT_REP; + *state = *state < LIT_STATES ? STATE_LIT_SHORTREP : STATE_NONLIT_REP; } /* Test if the previous symbol was a literal. */ static inline bool lzma_state_is_literal(enum lzma_state state) { - return state < LIT_STATES; + return state < LIT_STATES; } /* Each literal coder is divided in three sections: @@ -179,8 +180,7 @@ */ static inline uint32_t lzma_get_dist_state(uint32_t len) { - return len < DIST_STATES + MATCH_LEN_MIN - ? len - MATCH_LEN_MIN : DIST_STATES - 1; + return len < DIST_STATES + MATCH_LEN_MIN ? len - MATCH_LEN_MIN : DIST_STATES - 1; } /* diff -Nru mstflint-4.17.0+1/ext_libs/minixz/xz_private.h mstflint-4.21.0+1/ext_libs/minixz/xz_private.h --- mstflint-4.17.0+1/ext_libs/minixz/xz_private.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/minixz/xz_private.h 2022-07-31 09:33:57.000000000 +0000 @@ -43,51 +43,50 @@ #define XZ_PRIVATE_H #ifdef __KERNEL__ -# include -# include -# include - /* XZ_PREBOOT may be defined only via decompress_unxz.c. */ -# ifndef XZ_PREBOOT -# include -# include -# include -# ifdef CONFIG_XZ_DEC_X86 -# define XZ_DEC_X86 -# endif -# ifdef CONFIG_XZ_DEC_POWERPC -# define XZ_DEC_POWERPC -# endif -# ifdef CONFIG_XZ_DEC_IA64 -# define XZ_DEC_IA64 -# endif -# ifdef CONFIG_XZ_DEC_ARM -# define XZ_DEC_ARM -# endif -# ifdef CONFIG_XZ_DEC_ARMTHUMB -# define XZ_DEC_ARMTHUMB -# endif -# ifdef CONFIG_XZ_DEC_SPARC -# define XZ_DEC_SPARC -# endif -# define memeq(a, b, size) (memcmp(a, b, size) == 0) -# define memzero(buf, size) memset(buf, 0, size) -# endif -# define get_le32(p) le32_to_cpup((const uint32_t *)(p)) +#include +#include +#include +/* XZ_PREBOOT may be defined only via decompress_unxz.c. */ +#ifndef XZ_PREBOOT +#include +#include +#include +#ifdef CONFIG_XZ_DEC_X86 +#define XZ_DEC_X86 +#endif +#ifdef CONFIG_XZ_DEC_POWERPC +#define XZ_DEC_POWERPC +#endif +#ifdef CONFIG_XZ_DEC_IA64 +#define XZ_DEC_IA64 +#endif +#ifdef CONFIG_XZ_DEC_ARM +#define XZ_DEC_ARM +#endif +#ifdef CONFIG_XZ_DEC_ARMTHUMB +#define XZ_DEC_ARMTHUMB +#endif +#ifdef CONFIG_XZ_DEC_SPARC +#define XZ_DEC_SPARC +#endif +#define memeq(a, b, size) (memcmp(a, b, size) == 0) +#define memzero(buf, size) memset(buf, 0, size) +#endif +#define get_le32(p) le32_to_cpup((const uint32_t*)(p)) #else - /* - * For userspace builds, use a separate header to define the required - * macros and functions. This makes it easier to adapt the code into - * different environments and avoids clutter in the Linux kernel tree. - */ -# include +/* + * For userspace builds, use a separate header to define the required + * macros and functions. This makes it easier to adapt the code into + * different environments and avoids clutter in the Linux kernel tree. + */ +#include #endif /* If no specific decoding mode is requested, enable support for all modes. */ -#if !defined(XZ_DEC_SINGLE) && !defined(XZ_DEC_PREALLOC) \ - && !defined(XZ_DEC_DYNALLOC) -# define XZ_DEC_SINGLE -# define XZ_DEC_PREALLOC -# define XZ_DEC_DYNALLOC +#if !defined(XZ_DEC_SINGLE) && !defined(XZ_DEC_PREALLOC) && !defined(XZ_DEC_DYNALLOC) +#define XZ_DEC_SINGLE +#define XZ_DEC_PREALLOC +#define XZ_DEC_DYNALLOC #endif /* @@ -96,29 +95,29 @@ * false at compile time and thus allow the compiler to omit unneeded code. */ #ifdef XZ_DEC_SINGLE -# define DEC_IS_SINGLE(mode) ((mode) == XZ_SINGLE) +#define DEC_IS_SINGLE(mode) ((mode) == XZ_SINGLE) #else -# define DEC_IS_SINGLE(mode) (false) +#define DEC_IS_SINGLE(mode) (false) #endif #ifdef XZ_DEC_PREALLOC -# define DEC_IS_PREALLOC(mode) ((mode) == XZ_PREALLOC) +#define DEC_IS_PREALLOC(mode) ((mode) == XZ_PREALLOC) #else -# define DEC_IS_PREALLOC(mode) (false) +#define DEC_IS_PREALLOC(mode) (false) #endif #ifdef XZ_DEC_DYNALLOC -# define DEC_IS_DYNALLOC(mode) ((mode) == XZ_DYNALLOC) +#define DEC_IS_DYNALLOC(mode) ((mode) == XZ_DYNALLOC) #else -# define DEC_IS_DYNALLOC(mode) (false) +#define DEC_IS_DYNALLOC(mode) (false) #endif #if !defined(XZ_DEC_SINGLE) -# define DEC_IS_MULTI(mode) (true) +#define DEC_IS_MULTI(mode) (true) #elif defined(XZ_DEC_PREALLOC) || defined(XZ_DEC_DYNALLOC) -# define DEC_IS_MULTI(mode) ((mode) != XZ_SINGLE) +#define DEC_IS_MULTI(mode) ((mode) != XZ_SINGLE) #else -# define DEC_IS_MULTI(mode) (false) +#define DEC_IS_MULTI(mode) (false) #endif /* @@ -126,20 +125,17 @@ * XZ_DEC_BCJ is used to enable generic support for BCJ decoders. */ #ifndef XZ_DEC_BCJ -# if defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) \ - || defined(XZ_DEC_IA64) || defined(XZ_DEC_ARM) \ - || defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) \ - || defined(XZ_DEC_SPARC) -# define XZ_DEC_BCJ -# endif +#if defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) || defined(XZ_DEC_IA64) || defined(XZ_DEC_ARM) || \ + defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) || defined(XZ_DEC_SPARC) +#define XZ_DEC_BCJ +#endif #endif /* * Allocate memory for LZMA2 decoder. xz_dec_lzma2_reset() must be used * before calling xz_dec_lzma2_run(). */ -XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode, - uint32_t dict_max); +XZ_EXTERN struct xz_dec_lzma2* xz_dec_lzma2_create(enum xz_mode mode, uint32_t dict_max); /* * Decode the LZMA2 properties (one byte) and reset the decoder. Return @@ -147,22 +143,20 @@ * big enough, and XZ_OPTIONS_ERROR if props indicates something that this * decoder doesn't support. */ -XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, - uint8_t props); +XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2* s, uint8_t props); /* Decode raw LZMA2 stream from b->in to b->out. */ -XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s, - struct xz_buf *b); +XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2* s, struct xz_buf* b); /* Free the memory allocated for the LZMA2 decoder. */ -XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s); +XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2* s); #ifdef XZ_DEC_BCJ /* * Allocate memory for BCJ decoders. xz_dec_bcj_reset() must be used before * calling xz_dec_bcj_run(). */ -XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call); +XZ_EXTERN struct xz_dec_bcj* xz_dec_bcj_create(bool single_call); /* * Decode the Filter ID of a BCJ filter. This implementation doesn't @@ -170,16 +164,14 @@ * is needed. Returns XZ_OK if the given Filter ID is supported. * Otherwise XZ_OPTIONS_ERROR is returned. */ -XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id); +XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj* s, uint8_t id); /* * Decode raw BCJ + LZMA2 stream. This must be used only if there actually is * a BCJ filter in the chain. If the chain has only LZMA2, xz_dec_lzma2_run() * must be called directly. */ -XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s, - struct xz_dec_lzma2 *lzma2, - struct xz_buf *b); +XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj* s, struct xz_dec_lzma2* lzma2, struct xz_buf* b); /* Free the memory allocated for the BCJ filters. */ #define xz_dec_bcj_end(s) kfree(s) diff -Nru mstflint-4.17.0+1/ext_libs/minixz/xz_stream.h mstflint-4.21.0+1/ext_libs/minixz/xz_stream.h --- mstflint-4.17.0+1/ext_libs/minixz/xz_stream.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/minixz/xz_stream.h 2022-07-31 09:33:57.000000000 +0000 @@ -42,10 +42,9 @@ #define XZ_STREAM_H #if defined(__KERNEL__) && !XZ_INTERNAL_CRC32 -# include -# undef crc32 -# define xz_crc32(buf, size, crc) \ - (~crc32_le(~(uint32_t)(crc), buf, size)) +#include +#undef crc32 +#define xz_crc32(buf, size, crc) (~crc32_le(~(uint32_t)(crc), buf, size)) #endif /* @@ -80,11 +79,12 @@ #define VLI_BYTES_MAX (sizeof(vli_type) * 8 / 7) /* Integrity Check types */ -enum xz_check { - XZ_CHECK_NONE = 0, - XZ_CHECK_CRC32 = 1, - XZ_CHECK_CRC64 = 4, - XZ_CHECK_SHA256 = 10 +enum xz_check +{ + XZ_CHECK_NONE = 0, + XZ_CHECK_CRC32 = 1, + XZ_CHECK_CRC64 = 4, + XZ_CHECK_SHA256 = 10 }; /* Maximum possible Check ID */ diff -Nru mstflint-4.17.0+1/ext_libs/muparser/Makefile.am mstflint-4.21.0+1/ext_libs/muparser/Makefile.am --- mstflint-4.17.0+1/ext_libs/muparser/Makefile.am 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/Makefile.am 2022-04-28 11:50:03.000000000 +0000 @@ -29,9 +29,11 @@ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. #-- + # Makefile.am -- Process this file with automake to produce Makefile.in -AM_CXXFLAGS = -W -Wno-error -g -MP -MD ${COMPILER_FPIC} -Wno-deprecated-declarations +AM_CFLAGS = -w -g -MP -MD ${COMPILER_FPIC} +AM_CXXFLAGS = -w -g -MP -MD ${COMPILER_FPIC} noinst_LIBRARIES = libmuparser.a noinst_HEADERS = muParser.h muParserBase.h muParserBytecode.h muParserCallback.h muParserDef.h muParserError.h muParserFixes.h muParserStack.h muParserToken.h muParserTokenReader.h muParserTemplateMagic.h diff -Nru mstflint-4.17.0+1/ext_libs/muparser/Makefile.in mstflint-4.21.0+1/ext_libs/muparser/Makefile.in --- mstflint-4.17.0+1/ext_libs/muparser/Makefile.in 2021-06-29 08:51:08.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/Makefile.in 2022-07-31 09:34:41.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.3 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2020 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -45,6 +45,7 @@ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. #-- + # Makefile.am -- Process this file with automake to produce Makefile.in @@ -386,7 +387,8 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ -AM_CXXFLAGS = -W -Wno-error -g -MP -MD ${COMPILER_FPIC} -Wno-deprecated-declarations +AM_CFLAGS = -w -g -MP -MD ${COMPILER_FPIC} +AM_CXXFLAGS = -w -g -MP -MD ${COMPILER_FPIC} noinst_LIBRARIES = libmuparser.a noinst_HEADERS = muParser.h muParserBase.h muParserBytecode.h muParserCallback.h muParserDef.h muParserError.h muParserFixes.h muParserStack.h muParserToken.h muParserTokenReader.h muParserTemplateMagic.h libmuparser_a_SOURCES = \ diff -Nru mstflint-4.17.0+1/ext_libs/muparser/muParserBase.cpp mstflint-4.21.0+1/ext_libs/muparser/muParserBase.cpp --- mstflint-4.17.0+1/ext_libs/muparser/muParserBase.cpp 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/muParserBase.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ /* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2011 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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. */ #include "muParserBase.h" @@ -37,7 +37,7 @@ #include #ifdef MUP_USE_OPENMP - #include +#include #endif using namespace std; @@ -48,128 +48,121 @@ namespace mu { - std::locale ParserBase::s_locale = std::locale(std::locale::classic(), new change_dec_sep('.')); +std::locale ParserBase::s_locale = std::locale(std::locale::classic(), new change_dec_sep('.')); - bool ParserBase::g_DbgDumpCmdCode = false; - bool ParserBase::g_DbgDumpStack = false; +bool ParserBase::g_DbgDumpCmdCode = false; +bool ParserBase::g_DbgDumpStack = false; - //------------------------------------------------------------------------------ - /** \brief Identifiers for built in binary operators. +//------------------------------------------------------------------------------ +/** \brief Identifiers for built in binary operators. - When defining custom binary operators with #AddOprt(...) make sure not to choose - names conflicting with these definitions. - */ - const char_type* ParserBase::c_DefaultOprt[] = - { - _T("<="), _T(">="), _T("!="), - _T("=="), _T("<"), _T(">"), - _T("+"), _T("-"), _T("*"), - _T("/"), _T("^"), _T("&&"), - _T("||"), _T("="), _T("("), - _T(")"), _T("?"), _T(":"), 0 - }; - - //------------------------------------------------------------------------------ - /** \brief Constructor. - \param a_szFormula the formula to interpret. - \throw ParserException if a_szFormula is null. - */ - ParserBase::ParserBase() - :m_pParseFormula(&ParserBase::ParseString) - ,m_vRPN() - ,m_vStringBuf() - ,m_pTokenReader() - ,m_FunDef() - ,m_PostOprtDef() - ,m_InfixOprtDef() - ,m_OprtDef() - ,m_ConstDef() - ,m_StrVarDef() - ,m_VarDef() - ,m_bBuiltInOp(true) - ,m_sNameChars() - ,m_sOprtChars() - ,m_sInfixOprtChars() - ,m_nIfElseCounter(0) - ,m_vStackBuffer() - ,m_nFinalResultIdx(0) - { + When defining custom binary operators with #AddOprt(...) make sure not to choose + names conflicting with these definitions. +*/ +const char_type* ParserBase::c_DefaultOprt[] = {_T("<="), _T(">="), _T("!="), _T("=="), _T("<"), _T(">"), _T("+"), + _T("-"), _T("*"), _T("/"), _T("^"), _T("&&"), _T("||"), _T("="), + _T("("), _T(")"), _T("?"), _T(":"), 0}; + +//------------------------------------------------------------------------------ +/** \brief Constructor. + \param a_szFormula the formula to interpret. + \throw ParserException if a_szFormula is null. +*/ +ParserBase::ParserBase() : + m_pParseFormula(&ParserBase::ParseString), + m_vRPN(), + m_vStringBuf(), + m_pTokenReader(), + m_FunDef(), + m_PostOprtDef(), + m_InfixOprtDef(), + m_OprtDef(), + m_ConstDef(), + m_StrVarDef(), + m_VarDef(), + m_bBuiltInOp(true), + m_sNameChars(), + m_sOprtChars(), + m_sInfixOprtChars(), + m_nIfElseCounter(0), + m_vStackBuffer(), + m_nFinalResultIdx(0) +{ InitTokenReader(); - } +} - //--------------------------------------------------------------------------- - /** \brief Copy constructor. +//--------------------------------------------------------------------------- +/** \brief Copy constructor. - The parser can be safely copy constructed but the bytecode is reset during - copy construction. - */ - ParserBase::ParserBase(const ParserBase &a_Parser) - :m_pParseFormula(&ParserBase::ParseString) - ,m_vRPN() - ,m_vStringBuf() - ,m_pTokenReader() - ,m_FunDef() - ,m_PostOprtDef() - ,m_InfixOprtDef() - ,m_OprtDef() - ,m_ConstDef() - ,m_StrVarDef() - ,m_VarDef() - ,m_bBuiltInOp(true) - ,m_sNameChars() - ,m_sOprtChars() - ,m_sInfixOprtChars() - ,m_nIfElseCounter(0) - { + The parser can be safely copy constructed but the bytecode is reset during + copy construction. +*/ +ParserBase::ParserBase(const ParserBase& a_Parser) : + m_pParseFormula(&ParserBase::ParseString), + m_vRPN(), + m_vStringBuf(), + m_pTokenReader(), + m_FunDef(), + m_PostOprtDef(), + m_InfixOprtDef(), + m_OprtDef(), + m_ConstDef(), + m_StrVarDef(), + m_VarDef(), + m_bBuiltInOp(true), + m_sNameChars(), + m_sOprtChars(), + m_sInfixOprtChars(), + m_nIfElseCounter(0) +{ m_pTokenReader.reset(new token_reader_type(this)); Assign(a_Parser); - } +} - //--------------------------------------------------------------------------- - ParserBase::~ParserBase() - {} - - //--------------------------------------------------------------------------- - /** \brief Assignment operator. - - Implemented by calling Assign(a_Parser). Self assignment is suppressed. - \param a_Parser Object to copy to this. - \return *this - \throw nothrow - */ - ParserBase& ParserBase::operator=(const ParserBase &a_Parser) - { +//--------------------------------------------------------------------------- +ParserBase::~ParserBase() {} + +//--------------------------------------------------------------------------- +/** \brief Assignment operator. + + Implemented by calling Assign(a_Parser). Self assignment is suppressed. + \param a_Parser Object to copy to this. + \return *this + \throw nothrow +*/ +ParserBase& ParserBase::operator=(const ParserBase& a_Parser) +{ Assign(a_Parser); return *this; - } +} - //--------------------------------------------------------------------------- - /** \brief Copy state of a parser object to this. +//--------------------------------------------------------------------------- +/** \brief Copy state of a parser object to this. - Clears Variables and Functions of this parser. - Copies the states of all internal variables. - Resets parse function to string parse mode. - - \param a_Parser the source object. - */ - void ParserBase::Assign(const ParserBase &a_Parser) - { - if (&a_Parser==this) - return; + Clears Variables and Functions of this parser. + Copies the states of all internal variables. + Resets parse function to string parse mode. + + \param a_Parser the source object. +*/ +void ParserBase::Assign(const ParserBase& a_Parser) +{ + if (&a_Parser == this) + return; // Don't copy bytecode instead cause the parser to create new bytecode // by resetting the parse function. ReInit(); - m_ConstDef = a_Parser.m_ConstDef; // Copy user define constants - m_VarDef = a_Parser.m_VarDef; // Copy user defined variables - m_bBuiltInOp = a_Parser.m_bBuiltInOp; - m_vStringBuf = a_Parser.m_vStringBuf; - m_vStackBuffer = a_Parser.m_vStackBuffer; + m_ConstDef = a_Parser.m_ConstDef; // Copy user define constants + m_VarDef = a_Parser.m_VarDef; // Copy user defined variables + m_bBuiltInOp = a_Parser.m_bBuiltInOp; + m_vStringBuf = a_Parser.m_vStringBuf; + m_vStackBuffer = a_Parser.m_vStackBuffer; m_nFinalResultIdx = a_Parser.m_nFinalResultIdx; - m_StrVarDef = a_Parser.m_StrVarDef; - m_vStringVarBuf = a_Parser.m_vStringVarBuf; - m_nIfElseCounter = a_Parser.m_nIfElseCounter; + m_StrVarDef = a_Parser.m_StrVarDef; + m_vStringVarBuf = a_Parser.m_vStringVarBuf; + m_nIfElseCounter = a_Parser.m_nIfElseCounter; m_pTokenReader.reset(a_Parser.m_pTokenReader->Clone(this)); // Copy function and operator callbacks @@ -181,624 +174,634 @@ m_sNameChars = a_Parser.m_sNameChars; m_sOprtChars = a_Parser.m_sOprtChars; m_sInfixOprtChars = a_Parser.m_sInfixOprtChars; - } +} - //--------------------------------------------------------------------------- - /** \brief Set the decimal separator. - \param cDecSep Decimal separator as a character value. - \sa SetThousandsSep - - By default muparser uses the "C" locale. The decimal separator of this - locale is overwritten by the one provided here. - */ - void ParserBase::SetDecSep(char_type cDecSep) - { - char_type cThousandsSep = std::use_facet< change_dec_sep >(s_locale).thousands_sep(); +//--------------------------------------------------------------------------- +/** \brief Set the decimal separator. + \param cDecSep Decimal separator as a character value. + \sa SetThousandsSep + + By default muparser uses the "C" locale. The decimal separator of this + locale is overwritten by the one provided here. +*/ +void ParserBase::SetDecSep(char_type cDecSep) +{ + char_type cThousandsSep = std::use_facet >(s_locale).thousands_sep(); s_locale = std::locale(std::locale("C"), new change_dec_sep(cDecSep, cThousandsSep)); - } - - //--------------------------------------------------------------------------- - /** \brief Sets the thousands operator. - \param cThousandsSep The thousands separator as a character - \sa SetDecSep - - By default muparser uses the "C" locale. The thousands separator of this - locale is overwritten by the one provided here. - */ - void ParserBase::SetThousandsSep(char_type cThousandsSep) - { - char_type cDecSep = std::use_facet< change_dec_sep >(s_locale).decimal_point(); +} + +//--------------------------------------------------------------------------- +/** \brief Sets the thousands operator. + \param cThousandsSep The thousands separator as a character + \sa SetDecSep + + By default muparser uses the "C" locale. The thousands separator of this + locale is overwritten by the one provided here. +*/ +void ParserBase::SetThousandsSep(char_type cThousandsSep) +{ + char_type cDecSep = std::use_facet >(s_locale).decimal_point(); s_locale = std::locale(std::locale("C"), new change_dec_sep(cDecSep, cThousandsSep)); - } +} - //--------------------------------------------------------------------------- - /** \brief Resets the locale. +//--------------------------------------------------------------------------- +/** \brief Resets the locale. - The default locale used "." as decimal separator, no thousands separator and - "," as function argument separator. - */ - void ParserBase::ResetLocale() - { + The default locale used "." as decimal separator, no thousands separator and + "," as function argument separator. +*/ +void ParserBase::ResetLocale() +{ s_locale = std::locale(std::locale("C"), new change_dec_sep('.')); SetArgSep(','); - } +} - //--------------------------------------------------------------------------- - /** \brief Initialize the token reader. +//--------------------------------------------------------------------------- +/** \brief Initialize the token reader. - Create new token reader object and submit pointers to function, operator, - constant and variable definitions. + Create new token reader object and submit pointers to function, operator, + constant and variable definitions. - \post m_pTokenReader.get()!=0 - \throw nothrow - */ - void ParserBase::InitTokenReader() - { + \post m_pTokenReader.get()!=0 + \throw nothrow +*/ +void ParserBase::InitTokenReader() +{ m_pTokenReader.reset(new token_reader_type(this)); - } +} - //--------------------------------------------------------------------------- - /** \brief Reset parser to string parsing mode and clear internal buffers. +//--------------------------------------------------------------------------- +/** \brief Reset parser to string parsing mode and clear internal buffers. - Clear bytecode, reset the token reader. - \throw nothrow - */ - void ParserBase::ReInit() const - { + Clear bytecode, reset the token reader. + \throw nothrow +*/ +void ParserBase::ReInit() const +{ m_pParseFormula = &ParserBase::ParseString; m_vStringBuf.clear(); m_vRPN.clear(); m_pTokenReader->ReInit(); m_nIfElseCounter = 0; - } +} - //--------------------------------------------------------------------------- - void ParserBase::OnDetectVar(string_type * /*pExpr*/, int & /*nStart*/, int & /*nEnd*/) - {} - - //--------------------------------------------------------------------------- - /** \brief Returns the version of muparser. - \param eInfo A flag indicating whether the full version info should be - returned or not. - - Format is as follows: "MAJOR.MINOR (COMPILER_FLAGS)" The COMPILER_FLAGS - are returned only if eInfo==pviFULL. - */ - string_type ParserBase::GetVersion(EParserVersionInfo eInfo) const - { +//--------------------------------------------------------------------------- +void ParserBase::OnDetectVar(string_type* /*pExpr*/, int& /*nStart*/, int& /*nEnd*/) {} + +//--------------------------------------------------------------------------- +/** \brief Returns the version of muparser. + \param eInfo A flag indicating whether the full version info should be + returned or not. + + Format is as follows: "MAJOR.MINOR (COMPILER_FLAGS)" The COMPILER_FLAGS + are returned only if eInfo==pviFULL. +*/ +string_type ParserBase::GetVersion(EParserVersionInfo eInfo) const +{ stringstream_type ss; ss << MUP_VERSION; - if (eInfo==pviFULL) + if (eInfo == pviFULL) { - ss << _T(" (") << MUP_VERSION_DATE; - ss << std::dec << _T("; ") << sizeof(void*)*8 << _T("BIT"); + ss << _T(" (") << MUP_VERSION_DATE; + ss << std::dec << _T("; ") << sizeof(void*) * 8 << _T("BIT"); #ifdef _DEBUG - ss << _T("; DEBUG"); -#else - ss << _T("; RELEASE"); + ss << _T("; DEBUG"); +#else + ss << _T("; RELEASE"); #endif #ifdef _UNICODE - ss << _T("; UNICODE"); + ss << _T("; UNICODE"); +#else +#ifdef _MBCS + ss << _T("; MBCS"); #else - #ifdef _MBCS - ss << _T("; MBCS"); - #else - ss << _T("; ASCII"); - #endif + ss << _T("; ASCII"); +#endif #endif #ifdef MUP_USE_OPENMP - ss << _T("; OPENMP"); + ss << _T("; OPENMP"); //#else // ss << _T("; NO_OPENMP"); #endif #if defined(MUP_MATH_EXCEPTIONS) - ss << _T("; MATHEXC"); + ss << _T("; MATHEXC"); //#else // ss << _T("; NO_MATHEXC"); #endif - ss << _T(")"); + ss << _T(")"); } return ss.str(); - } +} + +//--------------------------------------------------------------------------- +/** \brief Add a value parsing function. - //--------------------------------------------------------------------------- - /** \brief Add a value parsing function. - - When parsing an expression muParser tries to detect values in the expression - string using different valident callbacks. Thus it's possible to parse - for hex values, binary values and floating point values. - */ - void ParserBase::AddValIdent(identfun_type a_pCallback) - { + When parsing an expression muParser tries to detect values in the expression + string using different valident callbacks. Thus it's possible to parse + for hex values, binary values and floating point values. +*/ +void ParserBase::AddValIdent(identfun_type a_pCallback) +{ m_pTokenReader->AddValIdent(a_pCallback); - } +} + +//--------------------------------------------------------------------------- +/** \brief Set a function that can create variable pointer for unknown expression variables. + \param a_pFactory A pointer to the variable factory. + \param pUserData A user defined context pointer. +*/ +void ParserBase::SetVarFactory(facfun_type a_pFactory, void* pUserData) +{ + m_pTokenReader->SetVarCreator(a_pFactory, pUserData); +} - //--------------------------------------------------------------------------- - /** \brief Set a function that can create variable pointer for unknown expression variables. - \param a_pFactory A pointer to the variable factory. - \param pUserData A user defined context pointer. - */ - void ParserBase::SetVarFactory(facfun_type a_pFactory, void *pUserData) - { - m_pTokenReader->SetVarCreator(a_pFactory, pUserData); - } - - //--------------------------------------------------------------------------- - /** \brief Add a function or operator callback to the parser. */ - void ParserBase::AddCallback( const string_type &a_strName, - const ParserCallback &a_Callback, - funmap_type &a_Storage, - const char_type *a_szCharSet ) - { - if (a_Callback.GetAddr()==0) +//--------------------------------------------------------------------------- +/** \brief Add a function or operator callback to the parser. */ +void ParserBase::AddCallback(const string_type& a_strName, + const ParserCallback& a_Callback, + funmap_type& a_Storage, + const char_type* a_szCharSet) +{ + if (a_Callback.GetAddr() == 0) Error(ecINVALID_FUN_PTR); - const funmap_type *pFunMap = &a_Storage; + const funmap_type* pFunMap = &a_Storage; // Check for conflicting operator or function names - if ( pFunMap!=&m_FunDef && m_FunDef.find(a_strName)!=m_FunDef.end() ) - Error(ecNAME_CONFLICT, -1, a_strName); + if (pFunMap != &m_FunDef && m_FunDef.find(a_strName) != m_FunDef.end()) + Error(ecNAME_CONFLICT, -1, a_strName); - if ( pFunMap!=&m_PostOprtDef && m_PostOprtDef.find(a_strName)!=m_PostOprtDef.end() ) - Error(ecNAME_CONFLICT, -1, a_strName); + if (pFunMap != &m_PostOprtDef && m_PostOprtDef.find(a_strName) != m_PostOprtDef.end()) + Error(ecNAME_CONFLICT, -1, a_strName); - if ( pFunMap!=&m_InfixOprtDef && pFunMap!=&m_OprtDef && m_InfixOprtDef.find(a_strName)!=m_InfixOprtDef.end() ) - Error(ecNAME_CONFLICT, -1, a_strName); + if (pFunMap != &m_InfixOprtDef && pFunMap != &m_OprtDef && m_InfixOprtDef.find(a_strName) != m_InfixOprtDef.end()) + Error(ecNAME_CONFLICT, -1, a_strName); - if ( pFunMap!=&m_InfixOprtDef && pFunMap!=&m_OprtDef && m_OprtDef.find(a_strName)!=m_OprtDef.end() ) - Error(ecNAME_CONFLICT, -1, a_strName); + if (pFunMap != &m_InfixOprtDef && pFunMap != &m_OprtDef && m_OprtDef.find(a_strName) != m_OprtDef.end()) + Error(ecNAME_CONFLICT, -1, a_strName); CheckOprt(a_strName, a_Callback, a_szCharSet); a_Storage[a_strName] = a_Callback; ReInit(); - } +} + +//--------------------------------------------------------------------------- +/** \brief Check if a name contains invalid characters. + + \throw ParserException if the name contains invalid characters. +*/ +void ParserBase::CheckOprt(const string_type& a_sName, + const ParserCallback& a_Callback, + const string_type& a_szCharSet) const +{ + if (!a_sName.length() || (a_sName.find_first_not_of(a_szCharSet) != string_type::npos) || + (a_sName[0] >= '0' && a_sName[0] <= '9')) + { + switch (a_Callback.GetCode()) + { + case cmOPRT_POSTFIX: + Error(ecINVALID_POSTFIX_IDENT, -1, a_sName); + case cmOPRT_INFIX: + Error(ecINVALID_INFIX_IDENT, -1, a_sName); + default: + Error(ecINVALID_NAME, -1, a_sName); + } + } +} + +//--------------------------------------------------------------------------- +/** \brief Check if a name contains invalid characters. + + \throw ParserException if the name contains invalid characters. +*/ +void ParserBase::CheckName(const string_type& a_sName, const string_type& a_szCharSet) const +{ + if (!a_sName.length() || (a_sName.find_first_not_of(a_szCharSet) != string_type::npos) || + (a_sName[0] >= '0' && a_sName[0] <= '9')) + { + Error(ecINVALID_NAME); + } +} - //--------------------------------------------------------------------------- - /** \brief Check if a name contains invalid characters. +//--------------------------------------------------------------------------- +/** \brief Set the formula. + \param a_strFormula Formula as string_type + \throw ParserException in case of syntax errors. - \throw ParserException if the name contains invalid characters. - */ - void ParserBase::CheckOprt(const string_type &a_sName, - const ParserCallback &a_Callback, - const string_type &a_szCharSet) const - { - if ( !a_sName.length() || - (a_sName.find_first_not_of(a_szCharSet)!=string_type::npos) || - (a_sName[0]>='0' && a_sName[0]<='9')) - { - switch(a_Callback.GetCode()) - { - case cmOPRT_POSTFIX: Error(ecINVALID_POSTFIX_IDENT, -1, a_sName); - /* fallthrough */ // Not reached - case cmOPRT_INFIX: Error(ecINVALID_INFIX_IDENT, -1, a_sName); - /* fallthrough */ // Not reached - default: Error(ecINVALID_NAME, -1, a_sName); - } - } - } - - //--------------------------------------------------------------------------- - /** \brief Check if a name contains invalid characters. - - \throw ParserException if the name contains invalid characters. - */ - void ParserBase::CheckName(const string_type &a_sName, - const string_type &a_szCharSet) const - { - if ( !a_sName.length() || - (a_sName.find_first_not_of(a_szCharSet)!=string_type::npos) || - (a_sName[0]>='0' && a_sName[0]<='9')) - { - Error(ecINVALID_NAME); - } - } - - //--------------------------------------------------------------------------- - /** \brief Set the formula. - \param a_strFormula Formula as string_type - \throw ParserException in case of syntax errors. - - Triggers first time calculation thus the creation of the bytecode and - scanning of used variables. - */ - void ParserBase::SetExpr(const string_type &a_sExpr) - { + Triggers first time calculation thus the creation of the bytecode and + scanning of used variables. +*/ +void ParserBase::SetExpr(const string_type& a_sExpr) +{ // Check locale compatibility std::locale loc; - if (m_pTokenReader->GetArgSep()==std::use_facet >(loc).decimal_point()) - Error(ecLOCALE); + if (m_pTokenReader->GetArgSep() == std::use_facet >(loc).decimal_point()) + Error(ecLOCALE); // 20060222: Bugfix for Borland-Kylix: // adding a space to the expression will keep Borlands KYLIX from going wild - // when calling tellg on a stringstream created from the expression after + // when calling tellg on a stringstream created from the expression after // reading a value at the end of an expression. (mu::Parser::IsVal function) // (tellg returns -1 otherwise causing the parser to ignore the value) - string_type sBuf(a_sExpr + _T(" ") ); + string_type sBuf(a_sExpr + _T(" ")); m_pTokenReader->SetFormula(sBuf); ReInit(); - } +} + +//--------------------------------------------------------------------------- +/** \brief Get the default symbols used for the built in operators. + \sa c_DefaultOprt +*/ +const char_type** ParserBase::GetOprtDef() const +{ + return (const char_type**)(&c_DefaultOprt[0]); +} - //--------------------------------------------------------------------------- - /** \brief Get the default symbols used for the built in operators. - \sa c_DefaultOprt - */ - const char_type** ParserBase::GetOprtDef() const - { - return (const char_type **)(&c_DefaultOprt[0]); - } - - //--------------------------------------------------------------------------- - /** \brief Define the set of valid characters to be used in names of - functions, variables, constants. - */ - void ParserBase::DefineNameChars(const char_type *a_szCharset) - { +//--------------------------------------------------------------------------- +/** \brief Define the set of valid characters to be used in names of + functions, variables, constants. +*/ +void ParserBase::DefineNameChars(const char_type* a_szCharset) +{ m_sNameChars = a_szCharset; - } +} - //--------------------------------------------------------------------------- - /** \brief Define the set of valid characters to be used in names of - binary operators and postfix operators. - */ - void ParserBase::DefineOprtChars(const char_type *a_szCharset) - { +//--------------------------------------------------------------------------- +/** \brief Define the set of valid characters to be used in names of + binary operators and postfix operators. +*/ +void ParserBase::DefineOprtChars(const char_type* a_szCharset) +{ m_sOprtChars = a_szCharset; - } +} - //--------------------------------------------------------------------------- - /** \brief Define the set of valid characters to be used in names of - infix operators. - */ - void ParserBase::DefineInfixOprtChars(const char_type *a_szCharset) - { +//--------------------------------------------------------------------------- +/** \brief Define the set of valid characters to be used in names of + infix operators. +*/ +void ParserBase::DefineInfixOprtChars(const char_type* a_szCharset) +{ m_sInfixOprtChars = a_szCharset; - } +} - //--------------------------------------------------------------------------- - /** \brief Virtual function that defines the characters allowed in name identifiers. - \sa #ValidOprtChars, #ValidPrefixOprtChars - */ - const char_type* ParserBase::ValidNameChars() const - { +//--------------------------------------------------------------------------- +/** \brief Virtual function that defines the characters allowed in name identifiers. + \sa #ValidOprtChars, #ValidPrefixOprtChars +*/ +const char_type* ParserBase::ValidNameChars() const +{ assert(m_sNameChars.size()); return m_sNameChars.c_str(); - } +} - //--------------------------------------------------------------------------- - /** \brief Virtual function that defines the characters allowed in operator definitions. - \sa #ValidNameChars, #ValidPrefixOprtChars - */ - const char_type* ParserBase::ValidOprtChars() const - { +//--------------------------------------------------------------------------- +/** \brief Virtual function that defines the characters allowed in operator definitions. + \sa #ValidNameChars, #ValidPrefixOprtChars +*/ +const char_type* ParserBase::ValidOprtChars() const +{ assert(m_sOprtChars.size()); return m_sOprtChars.c_str(); - } +} - //--------------------------------------------------------------------------- - /** \brief Virtual function that defines the characters allowed in infix operator definitions. - \sa #ValidNameChars, #ValidOprtChars - */ - const char_type* ParserBase::ValidInfixOprtChars() const - { +//--------------------------------------------------------------------------- +/** \brief Virtual function that defines the characters allowed in infix operator definitions. + \sa #ValidNameChars, #ValidOprtChars +*/ +const char_type* ParserBase::ValidInfixOprtChars() const +{ assert(m_sInfixOprtChars.size()); return m_sInfixOprtChars.c_str(); - } +} - //--------------------------------------------------------------------------- - /** \brief Add a user defined operator. - \post Will reset the Parser to string parsing mode. - */ - void ParserBase::DefinePostfixOprt(const string_type &a_sName, - fun_type1 a_pFun, - bool a_bAllowOpt) - { - AddCallback(a_sName, - ParserCallback(a_pFun, a_bAllowOpt, prPOSTFIX, cmOPRT_POSTFIX), - m_PostOprtDef, - ValidOprtChars() ); - } - - //--------------------------------------------------------------------------- - /** \brief Initialize user defined functions. - - Calls the virtual functions InitFun(), InitConst() and InitOprt(). - */ - void ParserBase::Init() - { +//--------------------------------------------------------------------------- +/** \brief Add a user defined operator. + \post Will reset the Parser to string parsing mode. +*/ +void ParserBase::DefinePostfixOprt(const string_type& a_sName, fun_type1 a_pFun, bool a_bAllowOpt) +{ + AddCallback( + a_sName, ParserCallback(a_pFun, a_bAllowOpt, prPOSTFIX, cmOPRT_POSTFIX), m_PostOprtDef, ValidOprtChars()); +} + +//--------------------------------------------------------------------------- +/** \brief Initialize user defined functions. + + Calls the virtual functions InitFun(), InitConst() and InitOprt(). +*/ +void ParserBase::Init() +{ InitCharSets(); InitFun(); InitConst(); InitOprt(); - } +} + +//--------------------------------------------------------------------------- +/** \brief Add a user defined operator. + \post Will reset the Parser to string parsing mode. + \param [in] a_sName operator Identifier + \param [in] a_pFun Operator callback function + \param [in] a_iPrec Operator Precedence (default=prSIGN) + \param [in] a_bAllowOpt True if operator is volatile (default=false) + \sa EPrec +*/ +void ParserBase::DefineInfixOprt(const string_type& a_sName, fun_type1 a_pFun, int a_iPrec, bool a_bAllowOpt) +{ + AddCallback( + a_sName, ParserCallback(a_pFun, a_bAllowOpt, a_iPrec, cmOPRT_INFIX), m_InfixOprtDef, ValidInfixOprtChars()); +} + +//--------------------------------------------------------------------------- +/** \brief Define a binary operator. + \param [in] a_sName The identifier of the operator. + \param [in] a_pFun Pointer to the callback function. + \param [in] a_iPrec Precedence of the operator. + \param [in] a_eAssociativity The associativity of the operator. + \param [in] a_bAllowOpt If this is true the operator may be optimized away. - //--------------------------------------------------------------------------- - /** \brief Add a user defined operator. - \post Will reset the Parser to string parsing mode. - \param [in] a_sName operator Identifier - \param [in] a_pFun Operator callback function - \param [in] a_iPrec Operator Precedence (default=prSIGN) - \param [in] a_bAllowOpt True if operator is volatile (default=false) - \sa EPrec - */ - void ParserBase::DefineInfixOprt(const string_type &a_sName, - fun_type1 a_pFun, - int a_iPrec, - bool a_bAllowOpt) - { - AddCallback(a_sName, - ParserCallback(a_pFun, a_bAllowOpt, a_iPrec, cmOPRT_INFIX), - m_InfixOprtDef, - ValidInfixOprtChars() ); - } - - - //--------------------------------------------------------------------------- - /** \brief Define a binary operator. - \param [in] a_sName The identifier of the operator. - \param [in] a_pFun Pointer to the callback function. - \param [in] a_iPrec Precedence of the operator. - \param [in] a_eAssociativity The associativity of the operator. - \param [in] a_bAllowOpt If this is true the operator may be optimized away. - - Adds a new Binary operator to the parser instance. - */ - void ParserBase::DefineOprt( const string_type &a_sName, - fun_type2 a_pFun, - unsigned a_iPrec, - EOprtAssociativity a_eAssociativity, - bool a_bAllowOpt ) - { + Adds a new Binary operator the the parser instance. +*/ +void ParserBase::DefineOprt(const string_type& a_sName, + fun_type2 a_pFun, + unsigned a_iPrec, + EOprtAssociativity a_eAssociativity, + bool a_bAllowOpt) +{ // Check for conflicts with built in operator names - for (int i=0; m_bBuiltInOp && iIgnoreUndefVar(true); - CreateRPN(); // try to create bytecode, but don't use it for any further calculations since it - // may contain references to nonexisting variables. - m_pParseFormula = &ParserBase::ParseString; - m_pTokenReader->IgnoreUndefVar(false); - } - catch(exception_type & /*e*/) - { - // Make sure to stay in string parse mode, dont call ReInit() - // because it deletes the array with the used variables - m_pParseFormula = &ParserBase::ParseString; - m_pTokenReader->IgnoreUndefVar(false); - throw; + m_pTokenReader->IgnoreUndefVar(true); + CreateRPN(); // try to create bytecode, but don't use it for any further calculations since it + // may contain references to nonexisting variables. + m_pParseFormula = &ParserBase::ParseString; + m_pTokenReader->IgnoreUndefVar(false); + } + catch (exception_type& /*e*/) + { + // Make sure to stay in string parse mode, dont call ReInit() + // because it deletes the array with the used variables + m_pParseFormula = &ParserBase::ParseString; + m_pTokenReader->IgnoreUndefVar(false); + throw; } - + return m_pTokenReader->GetUsedVar(); - } +} - //--------------------------------------------------------------------------- - /** \brief Return a map containing the used variables only. */ - const varmap_type& ParserBase::GetVar() const - { +//--------------------------------------------------------------------------- +/** \brief Return a map containing the used variables only. */ +const varmap_type& ParserBase::GetVar() const +{ return m_VarDef; - } +} - //--------------------------------------------------------------------------- - /** \brief Return a map containing all parser constants. */ - const valmap_type& ParserBase::GetConst() const - { +//--------------------------------------------------------------------------- +/** \brief Return a map containing all parser constants. */ +const valmap_type& ParserBase::GetConst() const +{ return m_ConstDef; - } +} - //--------------------------------------------------------------------------- - /** \brief Return prototypes of all parser functions. - \return #m_FunDef - \sa FunProt - \throw nothrow - - The return type is a map of the public type #funmap_type containing the prototype - definitions for all numerical parser functions. String functions are not part of - this map. The Prototype definition is encapsulated in objects of the class FunProt - one per parser function each associated with function names via a map construct. - */ - const funmap_type& ParserBase::GetFunDef() const - { +//--------------------------------------------------------------------------- +/** \brief Return prototypes of all parser functions. + \return #m_FunDef + \sa FunProt + \throw nothrow + + The return type is a map of the public type #funmap_type containing the prototype + definitions for all numerical parser functions. String functions are not part of + this map. The Prototype definition is encapsulated in objects of the class FunProt + one per parser function each associated with function names via a map construct. +*/ +const funmap_type& ParserBase::GetFunDef() const +{ return m_FunDef; - } +} - //--------------------------------------------------------------------------- - /** \brief Retrieve the formula. */ - const string_type& ParserBase::GetExpr() const - { +//--------------------------------------------------------------------------- +/** \brief Retrieve the formula. */ +const string_type& ParserBase::GetExpr() const +{ return m_pTokenReader->GetExpr(); - } +} - //--------------------------------------------------------------------------- - /** \brief Execute a function that takes a single string argument. - \param a_FunTok Function token. - \throw exception_type If the function token is not a string function - */ - ParserBase::token_type ParserBase::ApplyStrFunc(const token_type &a_FunTok, - const std::vector &a_vArg) const - { - if (a_vArg.back().GetCode()!=cmSTRING) - Error(ecSTRING_EXPECTED, m_pTokenReader->GetPos(), a_FunTok.GetAsString()); +//--------------------------------------------------------------------------- +/** \brief Execute a function that takes a single string argument. + \param a_FunTok Function token. + \throw exception_type If the function token is not a string function +*/ +ParserBase::token_type ParserBase::ApplyStrFunc(const token_type& a_FunTok, const std::vector& a_vArg) const +{ + if (a_vArg.back().GetCode() != cmSTRING) + Error(ecSTRING_EXPECTED, m_pTokenReader->GetPos(), a_FunTok.GetAsString()); - token_type valTok; + token_type valTok; generic_fun_type pFunc = a_FunTok.GetFuncAddr(); assert(pFunc); try { - // Check function arguments; write dummy value into valtok to represent the result - switch(a_FunTok.GetArgCount()) - { - case 0: valTok.SetVal(1); a_vArg[0].GetAsString(); break; - case 1: valTok.SetVal(1); a_vArg[1].GetAsString(); a_vArg[0].GetVal(); break; - case 2: valTok.SetVal(1); a_vArg[2].GetAsString(); a_vArg[1].GetVal(); a_vArg[0].GetVal(); break; - default: Error(ecINTERNAL_ERROR); - } + // Check function arguments; write dummy value into valtok to represent the result + switch (a_FunTok.GetArgCount()) + { + case 0: + valTok.SetVal(1); + a_vArg[0].GetAsString(); + break; + case 1: + valTok.SetVal(1); + a_vArg[1].GetAsString(); + a_vArg[0].GetVal(); + break; + case 2: + valTok.SetVal(1); + a_vArg[2].GetAsString(); + a_vArg[1].GetVal(); + a_vArg[0].GetVal(); + break; + default: + Error(ecINTERNAL_ERROR); + } } - catch(ParserError& ) + catch (ParserError&) { - Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), a_FunTok.GetAsString()); + Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), a_FunTok.GetAsString()); } // string functions won't be optimized m_vRPN.AddStrFun(pFunc, a_FunTok.GetArgCount(), a_vArg.back().GetIdx()); - + // Push dummy value representing the function result to the stack return valTok; - } +} - //--------------------------------------------------------------------------- - /** \brief Apply a function token. - \param iArgCount Number of Arguments actually gathered used only for multiarg functions. - \post The result is pushed to the value stack - \post The function token is removed from the stack - \throw exception_type if Argument count does not match function requirements. - */ - void ParserBase::ApplyFunc( ParserStack &a_stOpt, - ParserStack &a_stVal, - int a_iArgCount) const - { +//--------------------------------------------------------------------------- +/** \brief Apply a function token. + \param iArgCount Number of Arguments actually gathered used only for multiarg functions. + \post The result is pushed to the value stack + \post The function token is removed from the stack + \throw exception_type if Argument count does not match function requirements. +*/ +void ParserBase::ApplyFunc(ParserStack& a_stOpt, ParserStack& a_stVal, int a_iArgCount) const +{ assert(m_pTokenReader.get()); // Operator stack empty or does not contain tokens with callback functions - if (a_stOpt.empty() || a_stOpt.top().GetFuncAddr()==0 ) - return; + if (a_stOpt.empty() || a_stOpt.top().GetFuncAddr() == 0) + return; token_type funTok = a_stOpt.pop(); assert(funTok.GetFuncAddr()); @@ -806,975 +809,1183 @@ // Binary operators must rely on their internal operator number // since counting of operators relies on commas for function arguments // binary operators do not have commas in their expression - int iArgCount = (funTok.GetCode()==cmOPRT_BIN) ? funTok.GetArgCount() : a_iArgCount; + int iArgCount = (funTok.GetCode() == cmOPRT_BIN) ? funTok.GetArgCount() : a_iArgCount; - // determine how many parameters the function needs. To remember iArgCount includes the + // determine how many parameters the function needs. To remember iArgCount includes the // string parameter whilst GetArgCount() counts only numeric parameters. - int iArgRequired = funTok.GetArgCount() + ((funTok.GetType()==tpSTR) ? 1 : 0); + int iArgRequired = funTok.GetArgCount() + ((funTok.GetType() == tpSTR) ? 1 : 0); // Thats the number of numerical parameters - int iArgNumerical = iArgCount - ((funTok.GetType()==tpSTR) ? 1 : 0); + int iArgNumerical = iArgCount - ((funTok.GetType() == tpSTR) ? 1 : 0); - if (funTok.GetCode()==cmFUNC_STR && iArgCount-iArgNumerical>1) - Error(ecINTERNAL_ERROR); + if (funTok.GetCode() == cmFUNC_STR && iArgCount - iArgNumerical > 1) + Error(ecINTERNAL_ERROR); - if (funTok.GetArgCount()>=0 && iArgCount>iArgRequired) - Error(ecTOO_MANY_PARAMS, m_pTokenReader->GetPos()-1, funTok.GetAsString()); + if (funTok.GetArgCount() >= 0 && iArgCount > iArgRequired) + Error(ecTOO_MANY_PARAMS, m_pTokenReader->GetPos() - 1, funTok.GetAsString()); - if (funTok.GetCode()!=cmOPRT_BIN && iArgCountGetPos()-1, funTok.GetAsString()); + if (funTok.GetCode() != cmOPRT_BIN && iArgCount < iArgRequired) + Error(ecTOO_FEW_PARAMS, m_pTokenReader->GetPos() - 1, funTok.GetAsString()); - if (funTok.GetCode()==cmFUNC_STR && iArgCount>iArgRequired ) - Error(ecTOO_MANY_PARAMS, m_pTokenReader->GetPos()-1, funTok.GetAsString()); + if (funTok.GetCode() == cmFUNC_STR && iArgCount > iArgRequired) + Error(ecTOO_MANY_PARAMS, m_pTokenReader->GetPos() - 1, funTok.GetAsString()); // Collect the numeric function arguments from the value stack and store them // in a vector - std::vector stArg; - for (int i=0; i stArg; + for (int i = 0; i < iArgNumerical; ++i) { - stArg.push_back( a_stVal.pop() ); - if ( stArg.back().GetType()==tpSTR && funTok.GetType()!=tpSTR ) - Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), funTok.GetAsString()); + stArg.push_back(a_stVal.pop()); + if (stArg.back().GetType() == tpSTR && funTok.GetType() != tpSTR) + Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), funTok.GetAsString()); } - switch((int)funTok.GetCode()) + switch ((int)funTok.GetCode()) { - case cmFUNC_STR: - stArg.push_back(a_stVal.pop()); - - if ( stArg.back().GetType()==tpSTR && funTok.GetType()!=tpSTR ) - Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), funTok.GetAsString()); + case cmFUNC_STR: + stArg.push_back(a_stVal.pop()); + + if (stArg.back().GetType() == tpSTR && funTok.GetType() != tpSTR) + Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), funTok.GetAsString()); - ApplyStrFunc(funTok, stArg); - break; + ApplyStrFunc(funTok, stArg); + break; - case cmFUNC_BULK: - m_vRPN.AddBulkFun(funTok.GetFuncAddr(), (int)stArg.size()); - break; - - case cmOPRT_BIN: - case cmOPRT_POSTFIX: - case cmOPRT_INFIX: - case cmFUNC: - if (funTok.GetArgCount()==-1 && iArgCount==0) - Error(ecTOO_FEW_PARAMS, m_pTokenReader->GetPos(), funTok.GetAsString()); + case cmFUNC_BULK: + m_vRPN.AddBulkFun(funTok.GetFuncAddr(), (int)stArg.size()); + break; - m_vRPN.AddFun(funTok.GetFuncAddr(), (funTok.GetArgCount()==-1) ? -iArgNumerical : iArgNumerical); - break; + case cmOPRT_BIN: + case cmOPRT_POSTFIX: + case cmOPRT_INFIX: + case cmFUNC: + if (funTok.GetArgCount() == -1 && iArgCount == 0) + Error(ecTOO_FEW_PARAMS, m_pTokenReader->GetPos(), funTok.GetAsString()); + + m_vRPN.AddFun(funTok.GetFuncAddr(), (funTok.GetArgCount() == -1) ? -iArgNumerical : iArgNumerical); + break; } // Push dummy value representing the function result to the stack token_type token; - token.SetVal(1); + token.SetVal(1); a_stVal.push(token); - } +} - //--------------------------------------------------------------------------- - void ParserBase::ApplyIfElse(ParserStack &a_stOpt, - ParserStack &a_stVal) const - { +//--------------------------------------------------------------------------- +void ParserBase::ApplyIfElse(ParserStack& a_stOpt, ParserStack& a_stVal) const +{ // Check if there is an if Else clause to be calculated - while (a_stOpt.size() && a_stOpt.top().GetCode()==cmELSE) + while (a_stOpt.size() && a_stOpt.top().GetCode() == cmELSE) { - token_type opElse = a_stOpt.pop(); - MUP_ASSERT(a_stOpt.size()>0); + token_type opElse = a_stOpt.pop(); + MUP_ASSERT(a_stOpt.size() > 0); - // Take the value associated with the else branch from the value stack - token_type vVal2 = a_stVal.pop(); + // Take the value associated with the else branch from the value stack + token_type vVal2 = a_stVal.pop(); - MUP_ASSERT(a_stOpt.size()>0); - MUP_ASSERT(a_stVal.size()>=2); + MUP_ASSERT(a_stOpt.size() > 0); + MUP_ASSERT(a_stVal.size() >= 2); - // it then else is a ternary operator Pop all three values from the value s - // tack and just return the right value - token_type vVal1 = a_stVal.pop(); - token_type vExpr = a_stVal.pop(); + // it then else is a ternary operator Pop all three values from the value s + // tack and just return the right value + token_type vVal1 = a_stVal.pop(); + token_type vExpr = a_stVal.pop(); - a_stVal.push( (vExpr.GetVal()!=0) ? vVal1 : vVal2); + a_stVal.push((vExpr.GetVal() != 0) ? vVal1 : vVal2); - token_type opIf = a_stOpt.pop(); - MUP_ASSERT(opElse.GetCode()==cmELSE); - MUP_ASSERT(opIf.GetCode()==cmIF); + token_type opIf = a_stOpt.pop(); + MUP_ASSERT(opElse.GetCode() == cmELSE); + MUP_ASSERT(opIf.GetCode() == cmIF); - m_vRPN.AddIfElse(cmENDIF); + m_vRPN.AddIfElse(cmENDIF); } // while pending if-else-clause found - } +} - //--------------------------------------------------------------------------- - /** \brief Performs the necessary steps to write code for - the execution of binary operators into the bytecode. - */ - void ParserBase::ApplyBinOprt(ParserStack &a_stOpt, - ParserStack &a_stVal) const - { +//--------------------------------------------------------------------------- +/** \brief Performs the necessary steps to write code for + the execution of binary operators into the bytecode. +*/ +void ParserBase::ApplyBinOprt(ParserStack& a_stOpt, ParserStack& a_stVal) const +{ // is it a user defined binary operator? - if (a_stOpt.top().GetCode()==cmOPRT_BIN) + if (a_stOpt.top().GetCode() == cmOPRT_BIN) { - ApplyFunc(a_stOpt, a_stVal, 2); + ApplyFunc(a_stOpt, a_stVal, 2); } else { - MUP_ASSERT(a_stVal.size()>=2); - token_type valTok1 = a_stVal.pop(), - valTok2 = a_stVal.pop(), - optTok = a_stOpt.pop(), - resTok; - - if ( valTok1.GetType()!=valTok2.GetType() || - (valTok1.GetType()==tpSTR && valTok2.GetType()==tpSTR) ) - Error(ecOPRT_TYPE_CONFLICT, m_pTokenReader->GetPos(), optTok.GetAsString()); - - if (optTok.GetCode()==cmASSIGN) - { - if (valTok2.GetCode()!=cmVAR) - Error(ecUNEXPECTED_OPERATOR, -1, _T("=")); - - m_vRPN.AddAssignOp(valTok2.GetVar()); - } - else - m_vRPN.AddOp(optTok.GetCode()); - - resTok.SetVal(1); - a_stVal.push(resTok); - } - } - - //--------------------------------------------------------------------------- - /** \brief Apply a binary operator. - \param a_stOpt The operator stack - \param a_stVal The value stack - */ - void ParserBase::ApplyRemainingOprt(ParserStack &stOpt, - ParserStack &stVal) const - { - while (stOpt.size() && - stOpt.top().GetCode() != cmBO && - stOpt.top().GetCode() != cmIF) - { - token_type tok = stOpt.top(); - switch (tok.GetCode()) - { - case cmOPRT_INFIX: - case cmOPRT_BIN: - case cmLE: - case cmGE: - case cmNEQ: - case cmEQ: - case cmLT: - case cmGT: - case cmADD: - case cmSUB: - case cmMUL: - case cmDIV: - case cmPOW: - case cmLAND: - case cmLOR: - case cmASSIGN: - if (stOpt.top().GetCode()==cmOPRT_INFIX) - ApplyFunc(stOpt, stVal, 1); - else - ApplyBinOprt(stOpt, stVal); - break; - - case cmELSE: - ApplyIfElse(stOpt, stVal); - break; - - default: - Error(ecINTERNAL_ERROR); - } - } - } - - //--------------------------------------------------------------------------- - /** \brief Parse the command code. - \sa ParseString(...) - - Command code contains precalculated stack positions of the values and the - associated operators. The Stack is filled beginning from index one the - value at index zero is not used at all. - */ - value_type ParserBase::ParseCmdCode() const - { + MUP_ASSERT(a_stVal.size() >= 2); + token_type valTok1 = a_stVal.pop(), valTok2 = a_stVal.pop(), optTok = a_stOpt.pop(), resTok; + + if (valTok1.GetType() != valTok2.GetType() || (valTok1.GetType() == tpSTR && valTok2.GetType() == tpSTR)) + Error(ecOPRT_TYPE_CONFLICT, m_pTokenReader->GetPos(), optTok.GetAsString()); + + if (optTok.GetCode() == cmASSIGN) + { + if (valTok2.GetCode() != cmVAR) + Error(ecUNEXPECTED_OPERATOR, -1, _T("=")); + + m_vRPN.AddAssignOp(valTok2.GetVar()); + } + else + m_vRPN.AddOp(optTok.GetCode()); + + resTok.SetVal(1); + a_stVal.push(resTok); + } +} + +//--------------------------------------------------------------------------- +/** \brief Apply a binary operator. + \param a_stOpt The operator stack + \param a_stVal The value stack +*/ +void ParserBase::ApplyRemainingOprt(ParserStack& stOpt, ParserStack& stVal) const +{ + while (stOpt.size() && stOpt.top().GetCode() != cmBO && stOpt.top().GetCode() != cmIF) + { + token_type tok = stOpt.top(); + switch (tok.GetCode()) + { + case cmOPRT_INFIX: + case cmOPRT_BIN: + case cmLE: + case cmGE: + case cmNEQ: + case cmEQ: + case cmLT: + case cmGT: + case cmADD: + case cmSUB: + case cmMUL: + case cmDIV: + case cmPOW: + case cmLAND: + case cmLOR: + case cmASSIGN: + if (stOpt.top().GetCode() == cmOPRT_INFIX) + ApplyFunc(stOpt, stVal, 1); + else + ApplyBinOprt(stOpt, stVal); + break; + + case cmELSE: + ApplyIfElse(stOpt, stVal); + break; + + default: + Error(ecINTERNAL_ERROR); + } + } +} + +//--------------------------------------------------------------------------- +/** \brief Parse the command code. + \sa ParseString(...) + + Command code contains precalculated stack positions of the values and the + associated operators. The Stack is filled beginning from index one the + value at index zero is not used at all. +*/ +value_type ParserBase::ParseCmdCode() const +{ return ParseCmdCodeBulk(0, 0); - } +} - //--------------------------------------------------------------------------- - /** \brief Evaluate the RPN. - \param nOffset The offset added to variable addresses (for bulk mode) - \param nThreadID OpenMP Thread id of the calling thread - */ - value_type ParserBase::ParseCmdCodeBulk(int nOffset, int nThreadID) const - { - assert(nThreadID<=s_MaxNumOpenMPThreads); +//--------------------------------------------------------------------------- +/** \brief Evaluate the RPN. + \param nOffset The offset added to variable addresses (for bulk mode) + \param nThreadID OpenMP Thread id of the calling thread +*/ +value_type ParserBase::ParseCmdCodeBulk(int nOffset, int nThreadID) const +{ + assert(nThreadID <= s_MaxNumOpenMPThreads); - // Note: The check for nOffset==0 and nThreadID here is not necessary but + // Note: The check for nOffset==0 and nThreadID here is not necessary but // brings a minor performance gain when not in bulk mode. - value_type *Stack = ((nOffset==0) && (nThreadID==0)) ? &m_vStackBuffer[0] : &m_vStackBuffer[nThreadID * (m_vStackBuffer.size() / s_MaxNumOpenMPThreads)]; + value_type* Stack = ((nOffset == 0) && (nThreadID == 0)) ? + &m_vStackBuffer[0] : + &m_vStackBuffer[nThreadID * (m_vStackBuffer.size() / s_MaxNumOpenMPThreads)]; value_type buf; int sidx(0); - for (const SToken *pTok = m_vRPN.GetBase(); pTok->Cmd!=cmEND ; ++pTok) + for (const SToken* pTok = m_vRPN.GetBase(); pTok->Cmd != cmEND; ++pTok) { - switch (pTok->Cmd) - { - // built in binary operators - case cmLE: --sidx; Stack[sidx] = Stack[sidx] <= Stack[sidx+1]; continue; - case cmGE: --sidx; Stack[sidx] = Stack[sidx] >= Stack[sidx+1]; continue; - case cmNEQ: --sidx; Stack[sidx] = Stack[sidx] != Stack[sidx+1]; continue; - case cmEQ: --sidx; Stack[sidx] = Stack[sidx] == Stack[sidx+1]; continue; - case cmLT: --sidx; Stack[sidx] = Stack[sidx] < Stack[sidx+1]; continue; - case cmGT: --sidx; Stack[sidx] = Stack[sidx] > Stack[sidx+1]; continue; - case cmADD: --sidx; Stack[sidx] += Stack[1+sidx]; continue; - case cmSUB: --sidx; Stack[sidx] -= Stack[1+sidx]; continue; - case cmMUL: --sidx; Stack[sidx] *= Stack[1+sidx]; continue; - case cmDIV: --sidx; + switch (pTok->Cmd) + { + // built in binary operators + case cmLE: + --sidx; + Stack[sidx] = Stack[sidx] <= Stack[sidx + 1]; + continue; + case cmGE: + --sidx; + Stack[sidx] = Stack[sidx] >= Stack[sidx + 1]; + continue; + case cmNEQ: + --sidx; + Stack[sidx] = Stack[sidx] != Stack[sidx + 1]; + continue; + case cmEQ: + --sidx; + Stack[sidx] = Stack[sidx] == Stack[sidx + 1]; + continue; + case cmLT: + --sidx; + Stack[sidx] = Stack[sidx] < Stack[sidx + 1]; + continue; + case cmGT: + --sidx; + Stack[sidx] = Stack[sidx] > Stack[sidx + 1]; + continue; + case cmADD: + --sidx; + Stack[sidx] += Stack[1 + sidx]; + continue; + case cmSUB: + --sidx; + Stack[sidx] -= Stack[1 + sidx]; + continue; + case cmMUL: + --sidx; + Stack[sidx] *= Stack[1 + sidx]; + continue; + case cmDIV: + --sidx; - #if defined(MUP_MATH_EXCEPTIONS) - if (Stack[1+sidx]==0) +#if defined(MUP_MATH_EXCEPTIONS) + if (Stack[1 + sidx] == 0) Error(ecDIV_BY_ZERO); - #endif - Stack[sidx] /= Stack[1+sidx]; - continue; - - case cmPOW: - --sidx; Stack[sidx] = MathImpl::Pow(Stack[sidx], Stack[1+sidx]); - continue; - - case cmLAND: --sidx; Stack[sidx] = Stack[sidx] && Stack[sidx+1]; continue; - case cmLOR: --sidx; Stack[sidx] = Stack[sidx] || Stack[sidx+1]; continue; - - case cmASSIGN: - // Bugfix for Bulkmode: - // for details see: - // https://groups.google.com/forum/embed/?place=forum/muparser-dev&showsearch=true&showpopout=true&showtabs=false&parenturl=http://muparser.beltoforion.de/mup_forum.html&afterlogin&pli=1#!topic/muparser-dev/szgatgoHTws - --sidx; Stack[sidx] = *(pTok->Oprt.ptr + nOffset) = Stack[sidx + 1]; continue; - // original code: - //--sidx; Stack[sidx] = *pTok->Oprt.ptr = Stack[sidx+1]; continue; - - //case cmBO: // unused, listed for compiler optimization purposes - //case cmBC: - // MUP_FAIL(INVALID_CODE_IN_BYTECODE); - // continue; - - case cmIF: - if (Stack[sidx--]==0) - pTok += pTok->Oprt.offset; - continue; - - case cmELSE: - pTok += pTok->Oprt.offset; - continue; - - case cmENDIF: - continue; - - //case cmARG_SEP: - // MUP_FAIL(INVALID_CODE_IN_BYTECODE); - // continue; - - // value and variable tokens - case cmVAR: Stack[++sidx] = *(pTok->Val.ptr + nOffset); continue; - case cmVAL: Stack[++sidx] = pTok->Val.data2; continue; - - case cmVARPOW2: buf = *(pTok->Val.ptr + nOffset); - Stack[++sidx] = buf*buf; - continue; - - case cmVARPOW3: buf = *(pTok->Val.ptr + nOffset); - Stack[++sidx] = buf*buf*buf; - continue; - - case cmVARPOW4: buf = *(pTok->Val.ptr + nOffset); - Stack[++sidx] = buf*buf*buf*buf; - continue; - - case cmVARMUL: Stack[++sidx] = *(pTok->Val.ptr + nOffset) * pTok->Val.data + pTok->Val.data2; - continue; +#endif + Stack[sidx] /= Stack[1 + sidx]; + continue; - // Next is treatment of numeric functions - case cmFUNC: - { - int iArgCount = pTok->Fun.argc; + case cmPOW: + --sidx; + Stack[sidx] = MathImpl::Pow(Stack[sidx], Stack[1 + sidx]); + continue; - // switch according to argument count - switch(iArgCount) - { - case 0: sidx += 1; Stack[sidx] = (*(fun_type0)pTok->Fun.ptr)(); continue; - case 1: Stack[sidx] = (*(fun_type1)pTok->Fun.ptr)(Stack[sidx]); continue; - case 2: sidx -= 1; Stack[sidx] = (*(fun_type2)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1]); continue; - case 3: sidx -= 2; Stack[sidx] = (*(fun_type3)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2]); continue; - case 4: sidx -= 3; Stack[sidx] = (*(fun_type4)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3]); continue; - case 5: sidx -= 4; Stack[sidx] = (*(fun_type5)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4]); continue; - case 6: sidx -= 5; Stack[sidx] = (*(fun_type6)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5]); continue; - case 7: sidx -= 6; Stack[sidx] = (*(fun_type7)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6]); continue; - case 8: sidx -= 7; Stack[sidx] = (*(fun_type8)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6], Stack[sidx+7]); continue; - case 9: sidx -= 8; Stack[sidx] = (*(fun_type9)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6], Stack[sidx+7], Stack[sidx+8]); continue; - case 10:sidx -= 9; Stack[sidx] = (*(fun_type10)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6], Stack[sidx+7], Stack[sidx+8], Stack[sidx+9]); continue; - default: - if (iArgCount>0) // function with variable arguments store the number as a negative value - Error(ecINTERNAL_ERROR, 1); + case cmLAND: + --sidx; + Stack[sidx] = Stack[sidx] && Stack[sidx + 1]; + continue; + case cmLOR: + --sidx; + Stack[sidx] = Stack[sidx] || Stack[sidx + 1]; + continue; + + case cmASSIGN: + // Bugfix for Bulkmode: + // for details see: + // https://groups.google.com/forum/embed/?place=forum/muparser-dev&showsearch=true&showpopout=true&showtabs=false&parenturl=http://muparser.beltoforion.de/mup_forum.html&afterlogin&pli=1#!topic/muparser-dev/szgatgoHTws + --sidx; + Stack[sidx] = *(pTok->Oprt.ptr + nOffset) = Stack[sidx + 1]; + continue; + // original code: + //--sidx; Stack[sidx] = *pTok->Oprt.ptr = Stack[sidx+1]; continue; + + // case cmBO: // unused, listed for compiler optimization purposes + // case cmBC: + // MUP_FAIL(INVALID_CODE_IN_BYTECODE); + // continue; + + case cmIF: + if (Stack[sidx--] == 0) + pTok += pTok->Oprt.offset; + continue; + + case cmELSE: + pTok += pTok->Oprt.offset; + continue; + + case cmENDIF: + continue; + + // case cmARG_SEP: + // MUP_FAIL(INVALID_CODE_IN_BYTECODE); + // continue; + + // value and variable tokens + case cmVAR: + Stack[++sidx] = *(pTok->Val.ptr + nOffset); + continue; + case cmVAL: + Stack[++sidx] = pTok->Val.data2; + continue; - sidx -= -iArgCount - 1; - Stack[sidx] =(*(multfun_type)pTok->Fun.ptr)(&Stack[sidx], -iArgCount); + case cmVARPOW2: + buf = *(pTok->Val.ptr + nOffset); + Stack[++sidx] = buf * buf; continue; - } + + case cmVARPOW3: + buf = *(pTok->Val.ptr + nOffset); + Stack[++sidx] = buf * buf * buf; + continue; + + case cmVARPOW4: + buf = *(pTok->Val.ptr + nOffset); + Stack[++sidx] = buf * buf * buf * buf; + continue; + + case cmVARMUL: + Stack[++sidx] = *(pTok->Val.ptr + nOffset) * pTok->Val.data + pTok->Val.data2; + continue; + + // Next is treatment of numeric functions + case cmFUNC: + { + int iArgCount = pTok->Fun.argc; + + // switch according to argument count + switch (iArgCount) + { + case 0: + sidx += 1; + Stack[sidx] = (*(fun_type0)pTok->Fun.ptr)(); + continue; + case 1: + Stack[sidx] = (*(fun_type1)pTok->Fun.ptr)(Stack[sidx]); + continue; + case 2: + sidx -= 1; + Stack[sidx] = (*(fun_type2)pTok->Fun.ptr)(Stack[sidx], Stack[sidx + 1]); + continue; + case 3: + sidx -= 2; + Stack[sidx] = (*(fun_type3)pTok->Fun.ptr)(Stack[sidx], Stack[sidx + 1], Stack[sidx + 2]); + continue; + case 4: + sidx -= 3; + Stack[sidx] = + (*(fun_type4)pTok->Fun.ptr)(Stack[sidx], Stack[sidx + 1], Stack[sidx + 2], Stack[sidx + 3]); + continue; + case 5: + sidx -= 4; + Stack[sidx] = (*(fun_type5)pTok->Fun.ptr)( + Stack[sidx], Stack[sidx + 1], Stack[sidx + 2], Stack[sidx + 3], Stack[sidx + 4]); + continue; + case 6: + sidx -= 5; + Stack[sidx] = (*(fun_type6)pTok->Fun.ptr)(Stack[sidx], + Stack[sidx + 1], + Stack[sidx + 2], + Stack[sidx + 3], + Stack[sidx + 4], + Stack[sidx + 5]); + continue; + case 7: + sidx -= 6; + Stack[sidx] = (*(fun_type7)pTok->Fun.ptr)(Stack[sidx], + Stack[sidx + 1], + Stack[sidx + 2], + Stack[sidx + 3], + Stack[sidx + 4], + Stack[sidx + 5], + Stack[sidx + 6]); + continue; + case 8: + sidx -= 7; + Stack[sidx] = (*(fun_type8)pTok->Fun.ptr)(Stack[sidx], + Stack[sidx + 1], + Stack[sidx + 2], + Stack[sidx + 3], + Stack[sidx + 4], + Stack[sidx + 5], + Stack[sidx + 6], + Stack[sidx + 7]); + continue; + case 9: + sidx -= 8; + Stack[sidx] = (*(fun_type9)pTok->Fun.ptr)(Stack[sidx], + Stack[sidx + 1], + Stack[sidx + 2], + Stack[sidx + 3], + Stack[sidx + 4], + Stack[sidx + 5], + Stack[sidx + 6], + Stack[sidx + 7], + Stack[sidx + 8]); + continue; + case 10: + sidx -= 9; + Stack[sidx] = (*(fun_type10)pTok->Fun.ptr)(Stack[sidx], + Stack[sidx + 1], + Stack[sidx + 2], + Stack[sidx + 3], + Stack[sidx + 4], + Stack[sidx + 5], + Stack[sidx + 6], + Stack[sidx + 7], + Stack[sidx + 8], + Stack[sidx + 9]); + continue; + default: + if (iArgCount > 0) // function with variable arguments store the number as a negative value + Error(ecINTERNAL_ERROR, 1); + + sidx -= -iArgCount - 1; + Stack[sidx] = (*(multfun_type)pTok->Fun.ptr)(&Stack[sidx], -iArgCount); + continue; + } } - // Next is treatment of string functions - case cmFUNC_STR: + // Next is treatment of string functions + case cmFUNC_STR: { - sidx -= pTok->Fun.argc -1; + sidx -= pTok->Fun.argc - 1; + + // The index of the string argument in the string table + int iIdxStack = pTok->Fun.idx; + MUP_ASSERT(iIdxStack >= 0 && iIdxStack < (int)m_vStringBuf.size()); - // The index of the string argument in the string table - int iIdxStack = pTok->Fun.idx; - MUP_ASSERT( iIdxStack>=0 && iIdxStack<(int)m_vStringBuf.size() ); - - switch(pTok->Fun.argc) // switch according to argument count - { - case 0: Stack[sidx] = (*(strfun_type1)pTok->Fun.ptr)(m_vStringBuf[iIdxStack].c_str()); continue; - case 1: Stack[sidx] = (*(strfun_type2)pTok->Fun.ptr)(m_vStringBuf[iIdxStack].c_str(), Stack[sidx]); continue; - case 2: Stack[sidx] = (*(strfun_type3)pTok->Fun.ptr)(m_vStringBuf[iIdxStack].c_str(), Stack[sidx], Stack[sidx+1]); continue; - } + switch (pTok->Fun.argc) // switch according to argument count + { + case 0: + Stack[sidx] = (*(strfun_type1)pTok->Fun.ptr)(m_vStringBuf[iIdxStack].c_str()); + continue; + case 1: + Stack[sidx] = (*(strfun_type2)pTok->Fun.ptr)(m_vStringBuf[iIdxStack].c_str(), Stack[sidx]); + continue; + case 2: + Stack[sidx] = + (*(strfun_type3)pTok->Fun.ptr)(m_vStringBuf[iIdxStack].c_str(), Stack[sidx], Stack[sidx + 1]); + continue; + } - continue; + continue; } - case cmFUNC_BULK: - { + case cmFUNC_BULK: + { int iArgCount = pTok->Fun.argc; // switch according to argument count - switch(iArgCount) + switch (iArgCount) { - case 0: sidx += 1; Stack[sidx] = (*(bulkfun_type0 )pTok->Fun.ptr)(nOffset, nThreadID); continue; - case 1: Stack[sidx] = (*(bulkfun_type1 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx]); continue; - case 2: sidx -= 1; Stack[sidx] = (*(bulkfun_type2 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1]); continue; - case 3: sidx -= 2; Stack[sidx] = (*(bulkfun_type3 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2]); continue; - case 4: sidx -= 3; Stack[sidx] = (*(bulkfun_type4 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3]); continue; - case 5: sidx -= 4; Stack[sidx] = (*(bulkfun_type5 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4]); continue; - case 6: sidx -= 5; Stack[sidx] = (*(bulkfun_type6 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5]); continue; - case 7: sidx -= 6; Stack[sidx] = (*(bulkfun_type7 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6]); continue; - case 8: sidx -= 7; Stack[sidx] = (*(bulkfun_type8 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6], Stack[sidx+7]); continue; - case 9: sidx -= 8; Stack[sidx] = (*(bulkfun_type9 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6], Stack[sidx+7], Stack[sidx+8]); continue; - case 10:sidx -= 9; Stack[sidx] = (*(bulkfun_type10)pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6], Stack[sidx+7], Stack[sidx+8], Stack[sidx+9]); continue; - default: - Error(ecINTERNAL_ERROR, 2); - continue; + case 0: + sidx += 1; + Stack[sidx] = (*(bulkfun_type0)pTok->Fun.ptr)(nOffset, nThreadID); + continue; + case 1: + Stack[sidx] = (*(bulkfun_type1)pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx]); + continue; + case 2: + sidx -= 1; + Stack[sidx] = (*(bulkfun_type2)pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx + 1]); + continue; + case 3: + sidx -= 2; + Stack[sidx] = (*(bulkfun_type3)pTok->Fun.ptr)( + nOffset, nThreadID, Stack[sidx], Stack[sidx + 1], Stack[sidx + 2]); + continue; + case 4: + sidx -= 3; + Stack[sidx] = (*(bulkfun_type4)pTok->Fun.ptr)( + nOffset, nThreadID, Stack[sidx], Stack[sidx + 1], Stack[sidx + 2], Stack[sidx + 3]); + continue; + case 5: + sidx -= 4; + Stack[sidx] = (*(bulkfun_type5)pTok->Fun.ptr)(nOffset, + nThreadID, + Stack[sidx], + Stack[sidx + 1], + Stack[sidx + 2], + Stack[sidx + 3], + Stack[sidx + 4]); + continue; + case 6: + sidx -= 5; + Stack[sidx] = (*(bulkfun_type6)pTok->Fun.ptr)(nOffset, + nThreadID, + Stack[sidx], + Stack[sidx + 1], + Stack[sidx + 2], + Stack[sidx + 3], + Stack[sidx + 4], + Stack[sidx + 5]); + continue; + case 7: + sidx -= 6; + Stack[sidx] = (*(bulkfun_type7)pTok->Fun.ptr)(nOffset, + nThreadID, + Stack[sidx], + Stack[sidx + 1], + Stack[sidx + 2], + Stack[sidx + 3], + Stack[sidx + 4], + Stack[sidx + 5], + Stack[sidx + 6]); + continue; + case 8: + sidx -= 7; + Stack[sidx] = (*(bulkfun_type8)pTok->Fun.ptr)(nOffset, + nThreadID, + Stack[sidx], + Stack[sidx + 1], + Stack[sidx + 2], + Stack[sidx + 3], + Stack[sidx + 4], + Stack[sidx + 5], + Stack[sidx + 6], + Stack[sidx + 7]); + continue; + case 9: + sidx -= 8; + Stack[sidx] = (*(bulkfun_type9)pTok->Fun.ptr)(nOffset, + nThreadID, + Stack[sidx], + Stack[sidx + 1], + Stack[sidx + 2], + Stack[sidx + 3], + Stack[sidx + 4], + Stack[sidx + 5], + Stack[sidx + 6], + Stack[sidx + 7], + Stack[sidx + 8]); + continue; + case 10: + sidx -= 9; + Stack[sidx] = (*(bulkfun_type10)pTok->Fun.ptr)(nOffset, + nThreadID, + Stack[sidx], + Stack[sidx + 1], + Stack[sidx + 2], + Stack[sidx + 3], + Stack[sidx + 4], + Stack[sidx + 5], + Stack[sidx + 6], + Stack[sidx + 7], + Stack[sidx + 8], + Stack[sidx + 9]); + continue; + default: + Error(ecINTERNAL_ERROR, 2); + continue; } - } + } - default: - Error(ecINTERNAL_ERROR, 3); - return 0; - } // switch CmdCode - } // for all bytecode tokens - - return Stack[m_nFinalResultIdx]; - } - - //--------------------------------------------------------------------------- - void ParserBase::CreateRPN() const - { + default: + Error(ecINTERNAL_ERROR, 3); + return 0; + } // switch CmdCode + } // for all bytecode tokens + + return Stack[m_nFinalResultIdx]; +} + +//--------------------------------------------------------------------------- +void ParserBase::CreateRPN() const +{ if (!m_pTokenReader->GetExpr().length()) - Error(ecUNEXPECTED_EOF, 0); + Error(ecUNEXPECTED_EOF, 0); ParserStack stOpt, stVal; ParserStack stArgCount; - token_type opta, opt; // for storing operators - token_type val, tval; // for storing value + token_type opta, opt; // for storing operators + token_type val, tval; // for storing value ReInit(); - + // The outermost counter counts the number of separated items // such as in "a=10,b=20,c=c+a" stArgCount.push(1); - - for(;;) + + for (;;) { - opt = m_pTokenReader->ReadNextToken(); + opt = m_pTokenReader->ReadNextToken(); - switch (opt.GetCode()) - { - // - // Next three are different kind of value entries - // - case cmSTRING: - opt.SetIdx((int)m_vStringBuf.size()); // Assign buffer index to token + switch (opt.GetCode()) + { + // + // Next three are different kind of value entries + // + case cmSTRING: + opt.SetIdx((int)m_vStringBuf.size()); // Assign buffer index to token stVal.push(opt); - m_vStringBuf.push_back(opt.GetAsString()); // Store string in internal buffer + m_vStringBuf.push_back(opt.GetAsString()); // Store string in internal buffer break; - - case cmVAR: + + case cmVAR: stVal.push(opt); - m_vRPN.AddVar( static_cast(opt.GetVar()) ); + m_vRPN.AddVar(static_cast(opt.GetVar())); break; - case cmVAL: - stVal.push(opt); - m_vRPN.AddVal( opt.GetVal() ); + case cmVAL: + stVal.push(opt); + m_vRPN.AddVal(opt.GetVal()); break; - case cmELSE: + case cmELSE: m_nIfElseCounter--; - if (m_nIfElseCounter<0) - Error(ecMISPLACED_COLON, m_pTokenReader->GetPos()); + if (m_nIfElseCounter < 0) + Error(ecMISPLACED_COLON, m_pTokenReader->GetPos()); ApplyRemainingOprt(stOpt, stVal); m_vRPN.AddIfElse(cmELSE); stOpt.push(opt); break; - - case cmARG_SEP: + case cmARG_SEP: if (stArgCount.empty()) - Error(ecUNEXPECTED_ARG_SEP, m_pTokenReader->GetPos()); + Error(ecUNEXPECTED_ARG_SEP, m_pTokenReader->GetPos()); ++stArgCount.top(); - /* fallthrough */ + // fallthrough intentional (no break!) - case cmEND: + case cmEND: ApplyRemainingOprt(stOpt, stVal); break; - case cmBC: - { - // The argument count for parameterless functions is zero - // by default an opening bracket sets parameter count to 1 - // in preparation of arguments to come. If the last token - // was an opening bracket we know better... - if (opta.GetCode()==cmBO) + case cmBC: + { + // The argument count for parameterless functions is zero + // by default an opening bracket sets parameter count to 1 + // in preparation of arguments to come. If the last token + // was an opening bracket we know better... + if (opta.GetCode() == cmBO) --stArgCount.top(); - - ApplyRemainingOprt(stOpt, stVal); - // Check if the bracket content has been evaluated completely - if (stOpt.size() && stOpt.top().GetCode()==cmBO) - { + ApplyRemainingOprt(stOpt, stVal); + + // Check if the bracket content has been evaluated completely + if (stOpt.size() && stOpt.top().GetCode() == cmBO) + { // if opt is ")" and opta is "(" the bracket has been evaluated, now its time to check // if there is either a function or a sign pending // neither the opening nor the closing bracket will be pushed back to // the operator stack - // Check if a function is standing in front of the opening bracket, + // Check if a function is standing in front of the opening bracket, // if yes evaluate it afterwards check for infix operators assert(stArgCount.size()); int iArgCount = stArgCount.pop(); - + stOpt.pop(); // Take opening bracket from stack - if (iArgCount>1 && ( stOpt.size()==0 || - (stOpt.top().GetCode()!=cmFUNC && - stOpt.top().GetCode()!=cmFUNC_BULK && - stOpt.top().GetCode()!=cmFUNC_STR) ) ) - Error(ecUNEXPECTED_ARG, m_pTokenReader->GetPos()); - + if (iArgCount > 1 && (stOpt.size() == 0 || + (stOpt.top().GetCode() != cmFUNC && stOpt.top().GetCode() != cmFUNC_BULK && + stOpt.top().GetCode() != cmFUNC_STR))) + Error(ecUNEXPECTED_ARG, m_pTokenReader->GetPos()); + // The opening bracket was popped from the stack now check if there // was a function before this bracket - if (stOpt.size() && - stOpt.top().GetCode()!=cmOPRT_INFIX && - stOpt.top().GetCode()!=cmOPRT_BIN && - stOpt.top().GetFuncAddr()!=0) + if (stOpt.size() && stOpt.top().GetCode() != cmOPRT_INFIX && stOpt.top().GetCode() != cmOPRT_BIN && + stOpt.top().GetFuncAddr() != 0) { - ApplyFunc(stOpt, stVal, iArgCount); + ApplyFunc(stOpt, stVal, iArgCount); } - } - } // if bracket content is evaluated - break; + } + } // if bracket content is evaluated + break; - // - // Next are the binary operator entries - // - //case cmAND: // built in binary operators - //case cmOR: - //case cmXOR: - case cmIF: + // + // Next are the binary operator entries + // + // case cmAND: // built in binary operators + // case cmOR: + // case cmXOR: + case cmIF: m_nIfElseCounter++; - /* fallthrough */ + // fallthrough intentional (no break!) - case cmLAND: - case cmLOR: - case cmLT: - case cmGT: - case cmLE: - case cmGE: - case cmNEQ: - case cmEQ: - case cmADD: - case cmSUB: - case cmMUL: - case cmDIV: - case cmPOW: - case cmASSIGN: - case cmOPRT_BIN: - - // A binary operator (user defined or built in) has been found. - while ( stOpt.size() && - stOpt.top().GetCode() != cmBO && - stOpt.top().GetCode() != cmELSE && - stOpt.top().GetCode() != cmIF) + case cmLAND: + case cmLOR: + case cmLT: + case cmGT: + case cmLE: + case cmGE: + case cmNEQ: + case cmEQ: + case cmADD: + case cmSUB: + case cmMUL: + case cmDIV: + case cmPOW: + case cmASSIGN: + case cmOPRT_BIN: + + // A binary operator (user defined or built in) has been found. + while (stOpt.size() && stOpt.top().GetCode() != cmBO && stOpt.top().GetCode() != cmELSE && + stOpt.top().GetCode() != cmIF) { - int nPrec1 = GetOprtPrecedence(stOpt.top()), - nPrec2 = GetOprtPrecedence(opt); - - if (stOpt.top().GetCode()==opt.GetCode()) - { + int nPrec1 = GetOprtPrecedence(stOpt.top()), nPrec2 = GetOprtPrecedence(opt); - // Deal with operator associativity - EOprtAssociativity eOprtAsct = GetOprtAssociativity(opt); - if ( (eOprtAsct==oaRIGHT && (nPrec1 <= nPrec2)) || - (eOprtAsct==oaLEFT && (nPrec1 < nPrec2)) ) + if (stOpt.top().GetCode() == opt.GetCode()) { - break; + // Deal with operator associativity + EOprtAssociativity eOprtAsct = GetOprtAssociativity(opt); + if ((eOprtAsct == oaRIGHT && (nPrec1 <= nPrec2)) || (eOprtAsct == oaLEFT && (nPrec1 < nPrec2))) + { + break; + } } - } - else if (nPrec1 < nPrec2) - { - // In case the operators are not equal the precedence decides alone... - break; - } - - if (stOpt.top().GetCode()==cmOPRT_INFIX) - ApplyFunc(stOpt, stVal, 1); - else - ApplyBinOprt(stOpt, stVal); + else if (nPrec1 < nPrec2) + { + // In case the operators are not equal the precedence decides alone... + break; + } + + if (stOpt.top().GetCode() == cmOPRT_INFIX) + ApplyFunc(stOpt, stVal, 1); + else + ApplyBinOprt(stOpt, stVal); } // while ( ... ) - if (opt.GetCode()==cmIF) - m_vRPN.AddIfElse(opt.GetCode()); + if (opt.GetCode() == cmIF) + m_vRPN.AddIfElse(opt.GetCode()); - // The operator can't be evaluated right now, push back to the operator stack + // The operator can't be evaluated right now, push back to the operator stack stOpt.push(opt); break; - // - // Last section contains functions and operators implicitly mapped to functions - // - case cmBO: + // + // Last section contains functions and operators implicitly mapped to functions + // + case cmBO: stArgCount.push(1); stOpt.push(opt); break; - case cmOPRT_INFIX: - case cmFUNC: - case cmFUNC_BULK: - case cmFUNC_STR: + case cmOPRT_INFIX: + case cmFUNC: + case cmFUNC_BULK: + case cmFUNC_STR: stOpt.push(opt); break; - case cmOPRT_POSTFIX: + case cmOPRT_POSTFIX: stOpt.push(opt); - ApplyFunc(stOpt, stVal, 1); // this is the postfix operator + ApplyFunc(stOpt, stVal, 1); // this is the postfix operator break; - default: Error(ecINTERNAL_ERROR, 3); - } // end of switch operator-token + default: + Error(ecINTERNAL_ERROR, 3); + } // end of switch operator-token - opta = opt; + opta = opt; - if ( opt.GetCode() == cmEND ) - { - m_vRPN.Finalize(); - break; - } - - if (ParserBase::g_DbgDumpStack) - { - StackDump(stVal, stOpt); - m_vRPN.AsciiDump(); - } + if (opt.GetCode() == cmEND) + { + m_vRPN.Finalize(); + break; + } + + if (ParserBase::g_DbgDumpStack) + { + StackDump(stVal, stOpt); + m_vRPN.AsciiDump(); + } } // while (true) if (ParserBase::g_DbgDumpCmdCode) - m_vRPN.AsciiDump(); + m_vRPN.AsciiDump(); - if (m_nIfElseCounter>0) - Error(ecMISSING_ELSE_CLAUSE); + if (m_nIfElseCounter > 0) + Error(ecMISSING_ELSE_CLAUSE); // get the last value (= final result) from the stack - MUP_ASSERT(stArgCount.size()==1); + MUP_ASSERT(stArgCount.size() == 1); m_nFinalResultIdx = stArgCount.top(); - if (m_nFinalResultIdx==0) - Error(ecINTERNAL_ERROR, 9); + if (m_nFinalResultIdx == 0) + Error(ecINTERNAL_ERROR, 9); - if (stVal.size()==0) - Error(ecEMPTY_EXPRESSION); + if (stVal.size() == 0) + Error(ecEMPTY_EXPRESSION); - if (stVal.top().GetType()!=tpDBL) - Error(ecSTR_RESULT); + if (stVal.top().GetType() != tpDBL) + Error(ecSTR_RESULT); m_vStackBuffer.resize(m_vRPN.GetMaxStackSize() * s_MaxNumOpenMPThreads); - } +} - //--------------------------------------------------------------------------- - /** \brief One of the two main parse functions. - \sa ParseCmdCode(...) - - Parse expression from input string. Perform syntax checking and create - bytecode. After parsing the string and creating the bytecode the function - pointer #m_pParseFormula will be changed to the second parse routine the - uses bytecode instead of string parsing. - */ - value_type ParserBase::ParseString() const - { +//--------------------------------------------------------------------------- +/** \brief One of the two main parse functions. + \sa ParseCmdCode(...) + + Parse expression from input string. Perform syntax checking and create + bytecode. After parsing the string and creating the bytecode the function + pointer #m_pParseFormula will be changed to the second parse routine the + uses bytecode instead of string parsing. +*/ +value_type ParserBase::ParseString() const +{ try { - CreateRPN(); - m_pParseFormula = &ParserBase::ParseCmdCode; - return (this->*m_pParseFormula)(); - } - catch(ParserError &exc) - { - exc.SetFormula(m_pTokenReader->GetExpr()); - throw; - } - } - - //--------------------------------------------------------------------------- - /** \brief Create an error containing the parse error position. - - This function will create an Parser Exception object containing the error text and - its position. - - \param a_iErrc [in] The error code of type #EErrorCodes. - \param a_iPos [in] The position where the error was detected. - \param a_strTok [in] The token string representation associated with the error. - \throw ParserException always throws thats the only purpose of this function. - */ - void ParserBase::Error(EErrorCodes a_iErrc, int a_iPos, const string_type &a_sTok) const - { + CreateRPN(); + m_pParseFormula = &ParserBase::ParseCmdCode; + return (this->*m_pParseFormula)(); + } + catch (ParserError& exc) + { + exc.SetFormula(m_pTokenReader->GetExpr()); + throw; + } +} + +//--------------------------------------------------------------------------- +/** \brief Create an error containing the parse error position. + + This function will create an Parser Exception object containing the error text and + its position. + + \param a_iErrc [in] The error code of type #EErrorCodes. + \param a_iPos [in] The position where the error was detected. + \param a_strTok [in] The token string representation associated with the error. + \throw ParserException always throws thats the only purpose of this function. +*/ +void ParserBase::Error(EErrorCodes a_iErrc, int a_iPos, const string_type& a_sTok) const +{ throw exception_type(a_iErrc, a_sTok, m_pTokenReader->GetExpr(), a_iPos); - } +} + +//------------------------------------------------------------------------------ +/** \brief Clear all user defined variables. + \throw nothrow - //------------------------------------------------------------------------------ - /** \brief Clear all user defined variables. - \throw nothrow - - Resets the parser to string parsing mode by calling #ReInit. - */ - void ParserBase::ClearVar() - { + Resets the parser to string parsing mode by calling #ReInit. +*/ +void ParserBase::ClearVar() +{ m_VarDef.clear(); ReInit(); - } +} + +//------------------------------------------------------------------------------ +/** \brief Remove a variable from internal storage. + \throw nothrow - //------------------------------------------------------------------------------ - /** \brief Remove a variable from internal storage. - \throw nothrow - - Removes a variable if it exists. If the Variable does not exist nothing will be done. - */ - void ParserBase::RemoveVar(const string_type &a_strVarName) - { + Removes a variable if it exists. If the Variable does not exist nothing will be done. +*/ +void ParserBase::RemoveVar(const string_type& a_strVarName) +{ varmap_type::iterator item = m_VarDef.find(a_strVarName); - if (item!=m_VarDef.end()) + if (item != m_VarDef.end()) { - m_VarDef.erase(item); - ReInit(); + m_VarDef.erase(item); + ReInit(); } - } +} - //------------------------------------------------------------------------------ - /** \brief Clear all functions. - \post Resets the parser to string parsing mode. - \throw nothrow - */ - void ParserBase::ClearFun() - { +//------------------------------------------------------------------------------ +/** \brief Clear all functions. + \post Resets the parser to string parsing mode. + \throw nothrow +*/ +void ParserBase::ClearFun() +{ m_FunDef.clear(); ReInit(); - } +} - //------------------------------------------------------------------------------ - /** \brief Clear all user defined constants. +//------------------------------------------------------------------------------ +/** \brief Clear all user defined constants. - Both numeric and string constants will be removed from the internal storage. - \post Resets the parser to string parsing mode. - \throw nothrow - */ - void ParserBase::ClearConst() - { + Both numeric and string constants will be removed from the internal storage. + \post Resets the parser to string parsing mode. + \throw nothrow +*/ +void ParserBase::ClearConst() +{ m_ConstDef.clear(); m_StrVarDef.clear(); ReInit(); - } +} - //------------------------------------------------------------------------------ - /** \brief Clear all user defined postfix operators. - \post Resets the parser to string parsing mode. - \throw nothrow - */ - void ParserBase::ClearPostfixOprt() - { +//------------------------------------------------------------------------------ +/** \brief Clear all user defined postfix operators. + \post Resets the parser to string parsing mode. + \throw nothrow +*/ +void ParserBase::ClearPostfixOprt() +{ m_PostOprtDef.clear(); ReInit(); - } +} - //------------------------------------------------------------------------------ - /** \brief Clear all user defined binary operators. - \post Resets the parser to string parsing mode. - \throw nothrow - */ - void ParserBase::ClearOprt() - { +//------------------------------------------------------------------------------ +/** \brief Clear all user defined binary operators. + \post Resets the parser to string parsing mode. + \throw nothrow +*/ +void ParserBase::ClearOprt() +{ m_OprtDef.clear(); ReInit(); - } +} - //------------------------------------------------------------------------------ - /** \brief Clear the user defined Prefix operators. - \post Resets the parser to string parser mode. - \throw nothrow - */ - void ParserBase::ClearInfixOprt() - { +//------------------------------------------------------------------------------ +/** \brief Clear the user defined Prefix operators. + \post Resets the parser to string parser mode. + \throw nothrow +*/ +void ParserBase::ClearInfixOprt() +{ m_InfixOprtDef.clear(); ReInit(); - } +} - //------------------------------------------------------------------------------ - /** \brief Enable or disable the formula optimization feature. - \post Resets the parser to string parser mode. - \throw nothrow - */ - void ParserBase::EnableOptimizer(bool a_bIsOn) - { +//------------------------------------------------------------------------------ +/** \brief Enable or disable the formula optimization feature. + \post Resets the parser to string parser mode. + \throw nothrow +*/ +void ParserBase::EnableOptimizer(bool a_bIsOn) +{ m_vRPN.EnableOptimizer(a_bIsOn); ReInit(); - } +} - //--------------------------------------------------------------------------- - /** \brief Enable the dumping of bytecode and stack content on the console. - \param bDumpCmd Flag to enable dumping of the current bytecode to the console. - \param bDumpStack Flag to enable dumping of the stack content is written to the console. - - This function is for debug purposes only! - */ - void ParserBase::EnableDebugDump(bool bDumpCmd, bool bDumpStack) - { +//--------------------------------------------------------------------------- +/** \brief Enable the dumping of bytecode and stack content on the console. + \param bDumpCmd Flag to enable dumping of the current bytecode to the console. + \param bDumpStack Flag to enable dumping of the stack content is written to the console. + + This function is for debug purposes only! +*/ +void ParserBase::EnableDebugDump(bool bDumpCmd, bool bDumpStack) +{ ParserBase::g_DbgDumpCmdCode = bDumpCmd; - ParserBase::g_DbgDumpStack = bDumpStack; - } + ParserBase::g_DbgDumpStack = bDumpStack; +} - //------------------------------------------------------------------------------ - /** \brief Enable or disable the built in binary operators. - \throw nothrow - \sa m_bBuiltInOp, ReInit() - - If you disable the built in binary operators there will be no binary operators - defined. Thus you must add them manually one by one. It is not possible to - disable built in operators selectively. This function will Reinitialize the - parser by calling ReInit(). - */ - void ParserBase::EnableBuiltInOprt(bool a_bIsOn) - { +//------------------------------------------------------------------------------ +/** \brief Enable or disable the built in binary operators. + \throw nothrow + \sa m_bBuiltInOp, ReInit() + + If you disable the built in binary operators there will be no binary operators + defined. Thus you must add them manually one by one. It is not possible to + disable built in operators selectively. This function will Reinitialize the + parser by calling ReInit(). +*/ +void ParserBase::EnableBuiltInOprt(bool a_bIsOn) +{ m_bBuiltInOp = a_bIsOn; ReInit(); - } +} - //------------------------------------------------------------------------------ - /** \brief Query status of built in variables. - \return #m_bBuiltInOp; true if built in operators are enabled. - \throw nothrow - */ - bool ParserBase::HasBuiltInOprt() const - { +//------------------------------------------------------------------------------ +/** \brief Query status of built in variables. + \return #m_bBuiltInOp; true if built in operators are enabled. + \throw nothrow +*/ +bool ParserBase::HasBuiltInOprt() const +{ return m_bBuiltInOp; - } +} - //------------------------------------------------------------------------------ - /** \brief Get the argument separator character. - */ - char_type ParserBase::GetArgSep() const - { +//------------------------------------------------------------------------------ +/** \brief Get the argument separator character. + */ +char_type ParserBase::GetArgSep() const +{ return m_pTokenReader->GetArgSep(); - } +} - //------------------------------------------------------------------------------ - /** \brief Set argument separator. - \param cArgSep the argument separator character. - */ - void ParserBase::SetArgSep(char_type cArgSep) - { +//------------------------------------------------------------------------------ +/** \brief Set argument separator. + \param cArgSep the argument separator character. +*/ +void ParserBase::SetArgSep(char_type cArgSep) +{ m_pTokenReader->SetArgSep(cArgSep); - } +} - //------------------------------------------------------------------------------ - /** \brief Dump stack content. +//------------------------------------------------------------------------------ +/** \brief Dump stack content. - This function is used for debugging only. - */ - void ParserBase::StackDump(const ParserStack &a_stVal, - const ParserStack &a_stOprt) const - { - ParserStack stOprt(a_stOprt), - stVal(a_stVal); + This function is used for debugging only. +*/ +void ParserBase::StackDump(const ParserStack& a_stVal, const ParserStack& a_stOprt) const +{ + ParserStack stOprt(a_stOprt), stVal(a_stVal); mu::console() << _T("\nValue stack:\n"); - while ( !stVal.empty() ) + while (!stVal.empty()) { - token_type val = stVal.pop(); - if (val.GetType()==tpSTR) - mu::console() << _T(" \"") << val.GetAsString() << _T("\" "); - else - mu::console() << _T(" ") << val.GetVal() << _T(" "); + token_type val = stVal.pop(); + if (val.GetType() == tpSTR) + mu::console() << _T(" \"") << val.GetAsString() << _T("\" "); + else + mu::console() << _T(" ") << val.GetVal() << _T(" "); } mu::console() << "\nOperator stack:\n"; - while ( !stOprt.empty() ) + while (!stOprt.empty()) { - if (stOprt.top().GetCode()<=cmASSIGN) - { - mu::console() << _T("OPRT_INTRNL \"") - << ParserBase::c_DefaultOprt[stOprt.top().GetCode()] - << _T("\" \n"); - } - else - { - switch(stOprt.top().GetCode()) + if (stOprt.top().GetCode() <= cmASSIGN) { - case cmVAR: mu::console() << _T("VAR\n"); break; - case cmVAL: mu::console() << _T("VAL\n"); break; - case cmFUNC: mu::console() << _T("FUNC \"") - << stOprt.top().GetAsString() - << _T("\"\n"); break; - case cmFUNC_BULK: mu::console() << _T("FUNC_BULK \"") - << stOprt.top().GetAsString() - << _T("\"\n"); break; - case cmOPRT_INFIX: mu::console() << _T("OPRT_INFIX \"") - << stOprt.top().GetAsString() - << _T("\"\n"); break; - case cmOPRT_BIN: mu::console() << _T("OPRT_BIN \"") - << stOprt.top().GetAsString() - << _T("\"\n"); break; - case cmFUNC_STR: mu::console() << _T("FUNC_STR\n"); break; - case cmEND: mu::console() << _T("END\n"); break; - case cmUNKNOWN: mu::console() << _T("UNKNOWN\n"); break; - case cmBO: mu::console() << _T("BRACKET \"(\"\n"); break; - case cmBC: mu::console() << _T("BRACKET \")\"\n"); break; - case cmIF: mu::console() << _T("IF\n"); break; - case cmELSE: mu::console() << _T("ELSE\n"); break; - case cmENDIF: mu::console() << _T("ENDIF\n"); break; - default: mu::console() << stOprt.top().GetCode() << _T(" "); break; + mu::console() << _T("OPRT_INTRNL \"") << ParserBase::c_DefaultOprt[stOprt.top().GetCode()] << _T("\" \n"); + } + else + { + switch (stOprt.top().GetCode()) + { + case cmVAR: + mu::console() << _T("VAR\n"); + break; + case cmVAL: + mu::console() << _T("VAL\n"); + break; + case cmFUNC: + mu::console() << _T("FUNC \"") << stOprt.top().GetAsString() << _T("\"\n"); + break; + case cmFUNC_BULK: + mu::console() << _T("FUNC_BULK \"") << stOprt.top().GetAsString() << _T("\"\n"); + break; + case cmOPRT_INFIX: + mu::console() << _T("OPRT_INFIX \"") << stOprt.top().GetAsString() << _T("\"\n"); + break; + case cmOPRT_BIN: + mu::console() << _T("OPRT_BIN \"") << stOprt.top().GetAsString() << _T("\"\n"); + break; + case cmFUNC_STR: + mu::console() << _T("FUNC_STR\n"); + break; + case cmEND: + mu::console() << _T("END\n"); + break; + case cmUNKNOWN: + mu::console() << _T("UNKNOWN\n"); + break; + case cmBO: + mu::console() << _T("BRACKET \"(\"\n"); + break; + case cmBC: + mu::console() << _T("BRACKET \")\"\n"); + break; + case cmIF: + mu::console() << _T("IF\n"); + break; + case cmELSE: + mu::console() << _T("ELSE\n"); + break; + case cmENDIF: + mu::console() << _T("ENDIF\n"); + break; + default: + mu::console() << stOprt.top().GetCode() << _T(" "); + break; + } } - } - stOprt.pop(); + stOprt.pop(); } mu::console() << dec << endl; - } +} + +//------------------------------------------------------------------------------ +/** \brief Evaluate an expression containing comma separated subexpressions + \param [out] nStackSize The total number of results available + \return Pointer to the array containing all expression results - //------------------------------------------------------------------------------ - /** \brief Evaluate an expression containing comma separated subexpressions - \param [out] nStackSize The total number of results available - \return Pointer to the array containing all expression results - - This member function can be used to retrieve all results of an expression - made up of multiple comma separated subexpressions (i.e. "x+y,sin(x),cos(y)") - */ - value_type* ParserBase::Eval(int &nStackSize) const - { - (this->*m_pParseFormula)(); + This member function can be used to retrieve all results of an expression + made up of multiple comma separated subexpressions (i.e. "x+y,sin(x),cos(y)") +*/ +value_type* ParserBase::Eval(int& nStackSize) const +{ + (this->*m_pParseFormula)(); nStackSize = m_nFinalResultIdx; // (for historic reasons the stack starts at position 1) return &m_vStackBuffer[1]; - } +} + +//--------------------------------------------------------------------------- +/** \brief Return the number of results on the calculation stack. - //--------------------------------------------------------------------------- - /** \brief Return the number of results on the calculation stack. - - If the expression contains comma separated subexpressions (i.e. "sin(y), x+y"). - There may be more than one return value. This function returns the number of - available results. - */ - int ParserBase::GetNumResults() const - { + If the expression contains comma separated subexpressions (i.e. "sin(y), x+y"). + There may be more than one return value. This function returns the number of + available results. +*/ +int ParserBase::GetNumResults() const +{ return m_nFinalResultIdx; - } +} - //--------------------------------------------------------------------------- - /** \brief Calculate the result. +//--------------------------------------------------------------------------- +/** \brief Calculate the result. - A note on const correctness: - I consider it important that Calc is a const function. - Due to caching operations Calc changes only the state of internal variables with one exception - m_UsedVar this is reset during string parsing and accessible from the outside. Instead of making - Calc non const GetUsedVar is non const because it explicitly calls Eval() forcing this update. - - \pre A formula must be set. - \pre Variables must have been set (if needed) - - \sa #m_pParseFormula - \return The evaluation result - \throw ParseException if no Formula is set or in case of any other error related to the formula. - */ - value_type ParserBase::Eval() const - { - return (this->*m_pParseFormula)(); - } - - //--------------------------------------------------------------------------- - void ParserBase::Eval(value_type *results, int nBulkSize) - { -/* Commented because it is making a unit test impossible - - // Parallelization does not make sense for fewer than 10000 computations - // due to thread creation overhead. If the bulk size is below 2000 - // computation is refused. - if (nBulkSize<2000) - { - throw ParserError(ecUNREASONABLE_NUMBER_OF_COMPUTATIONS); - } + A note on const correctness: + I consider it important that Calc is a const function. + Due to caching operations Calc changes only the state of internal variables with one exception + m_UsedVar this is reset during string parsing and accessible from the outside. Instead of making + Calc non const GetUsedVar is non const because it explicitly calls Eval() forcing this update. + + \pre A formula must be set. + \pre Variables must have been set (if needed) + + \sa #m_pParseFormula + \return The evaluation result + \throw ParseException if no Formula is set or in case of any other error related to the formula. */ +value_type ParserBase::Eval() const +{ + return (this->*m_pParseFormula)(); +} + +//--------------------------------------------------------------------------- +void ParserBase::Eval(value_type* results, int nBulkSize) +{ + /* Commented because it is making a unit test impossible + + // Parallelization does not make sense for fewer than 10000 computations + // due to thread creation overhead. If the bulk size is below 2000 + // computation is refused. + if (nBulkSize<2000) + { + throw ParserError(ecUNREASONABLE_NUMBER_OF_COMPUTATIONS); + } + */ CreateRPN(); int i = 0; #ifdef MUP_USE_OPENMP -//#define DEBUG_OMP_STUFF - #ifdef DEBUG_OMP_STUFF - int *pThread = new int[nBulkSize]; - int *pIdx = new int[nBulkSize]; - #endif + //#define DEBUG_OMP_STUFF +#ifdef DEBUG_OMP_STUFF + int* pThread = new int[nBulkSize]; + int* pIdx = new int[nBulkSize]; +#endif int nMaxThreads = std::min(omp_get_max_threads(), s_MaxNumOpenMPThreads); - int nThreadID = 0, ct = 0; + int nThreadID = 0, ct = 0; omp_set_num_threads(nMaxThreads); - #pragma omp parallel for schedule(static, nBulkSize/nMaxThreads) private(nThreadID) - for (i=0; i \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2013 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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 MU_PARSER_BASE_H #define MU_PARSER_BASE_H @@ -41,7 +41,6 @@ #include "muParserBytecode.h" #include "muParserError.h" - namespace mu { /** \file @@ -52,26 +51,25 @@ /** \brief Mathematical expressions parser (base parser engine). \author (C) 2013 Ingo Berg - This is the implementation of a bytecode based mathematical expressions parser. - The formula will be parsed from string and converted into a bytecode. + This is the implementation of a bytecode based mathematical expressions parser. + The formula will be parsed from string and converted into a bytecode. Future calculations will be done with the bytecode instead the formula string - resulting in a significant performance increase. - Complementary to a set of internally implemented functions the parser is able to handle - user defined functions and variables. + resulting in a significant performance increase. + Complementary to a set of internally implemented functions the parser is able to handle + user defined functions and variables. */ -class ParserBase +class ParserBase { -friend class ParserTokenReader; + friend class ParserTokenReader; private: + /** \brief Typedef for the parse functions. - /** \brief Typedef for the parse functions. - The parse function do the actual work. The parser exchanges - the function pointer to the parser function depending on + the function pointer to the parser function depending on which state it is in. (i.e. bytecode parser vs. string parser) */ - typedef value_type (ParserBase::*ParseFunction)() const; + typedef value_type (ParserBase::*ParseFunction)() const; /** \brief Type used for storing an array of values. */ typedef std::vector valbuf_type; @@ -81,70 +79,70 @@ /** \brief Typedef for the token reader. */ typedef ParserTokenReader token_reader_type; - + /** \brief Type used for parser tokens. */ typedef ParserToken token_type; /** \brief Maximum number of threads spawned by OpenMP when using the bulk mode. */ static const int s_MaxNumOpenMPThreads = 16; - public: +public: + /** \brief Type of the error class. - /** \brief Type of the error class. - Included for backwards compatibility. */ typedef ParserError exception_type; static void EnableDebugDump(bool bDumpCmd, bool bDumpStack); - ParserBase(); - ParserBase(const ParserBase &a_Parser); - ParserBase& operator=(const ParserBase &a_Parser); + ParserBase(); + ParserBase(const ParserBase& a_Parser); + ParserBase& operator=(const ParserBase& a_Parser); virtual ~ParserBase(); - - value_type Eval() const; - value_type* Eval(int &nStackSize) const; - void Eval(value_type *results, int nBulkSize); + + value_type Eval() const; + value_type* Eval(int& nStackSize) const; + void Eval(value_type* results, int nBulkSize); int GetNumResults() const; - void SetExpr(const string_type &a_sExpr); - void SetVarFactory(facfun_type a_pFactory, void *pUserData = NULL); + void SetExpr(const string_type& a_sExpr); + void SetVarFactory(facfun_type a_pFactory, void* pUserData = NULL); void SetDecSep(char_type cDecSep); void SetThousandsSep(char_type cThousandsSep = 0); void ResetLocale(); - void EnableOptimizer(bool a_bIsOn=true); - void EnableBuiltInOprt(bool a_bIsOn=true); + void EnableOptimizer(bool a_bIsOn = true); + void EnableBuiltInOprt(bool a_bIsOn = true); bool HasBuiltInOprt() const; void AddValIdent(identfun_type a_pCallback); - /** \fn void mu::ParserBase::DefineFun(const string_type &a_strName, fun_type0 a_pFun, bool a_bAllowOpt = true) + /** \fn void mu::ParserBase::DefineFun(const string_type &a_strName, fun_type0 a_pFun, bool a_bAllowOpt = true) \brief Define a parser function without arguments. \param a_strName Name of the function \param a_pFun Pointer to the callback function \param a_bAllowOpt A flag indicating this function may be optimized */ template - void DefineFun(const string_type &a_strName, T a_pFun, bool a_bAllowOpt = true) + void DefineFun(const string_type& a_strName, T a_pFun, bool a_bAllowOpt = true) { - AddCallback( a_strName, ParserCallback(a_pFun, a_bAllowOpt), m_FunDef, ValidNameChars() ); + AddCallback(a_strName, ParserCallback(a_pFun, a_bAllowOpt), m_FunDef, ValidNameChars()); } - void DefineOprt(const string_type &a_strName, - fun_type2 a_pFun, - unsigned a_iPri=0, + void DefineOprt(const string_type& a_strName, + fun_type2 a_pFun, + unsigned a_iPri = 0, EOprtAssociativity a_eAssociativity = oaLEFT, bool a_bAllowOpt = false); - void DefineConst(const string_type &a_sName, value_type a_fVal); - void DefineStrConst(const string_type &a_sName, const string_type &a_strVal); - void DefineVar(const string_type &a_sName, value_type *a_fVar); - void DefinePostfixOprt(const string_type &a_strFun, fun_type1 a_pOprt, bool a_bAllowOpt=true); - void DefineInfixOprt(const string_type &a_strName, fun_type1 a_pOprt, int a_iPrec=prINFIX, bool a_bAllowOpt=true); + void DefineConst(const string_type& a_sName, value_type a_fVal); + void DefineStrConst(const string_type& a_sName, const string_type& a_strVal); + void DefineVar(const string_type& a_sName, value_type* a_fVar); + void DefinePostfixOprt(const string_type& a_strFun, fun_type1 a_pOprt, bool a_bAllowOpt = true); + void + DefineInfixOprt(const string_type& a_strName, fun_type1 a_pOprt, int a_iPrec = prINFIX, bool a_bAllowOpt = true); // Clear user defined variables, constants or functions void ClearVar(); @@ -153,8 +151,8 @@ void ClearInfixOprt(); void ClearPostfixOprt(); void ClearOprt(); - - void RemoveVar(const string_type &a_strVarName); + + void RemoveVar(const string_type& a_strVarName); const varmap_type& GetUsedVar() const; const varmap_type& GetVar() const; const valmap_type& GetConst() const; @@ -162,10 +160,10 @@ const funmap_type& GetFunDef() const; string_type GetVersion(EParserVersionInfo eInfo = pviFULL) const; - const char_type ** GetOprtDef() const; - void DefineNameChars(const char_type *a_szCharset); - void DefineOprtChars(const char_type *a_szCharset); - void DefineInfixOprtChars(const char_type *a_szCharset); + const char_type** GetOprtDef() const; + void DefineNameChars(const char_type* a_szCharset); + void DefineOprtChars(const char_type* a_szCharset); + void DefineInfixOprtChars(const char_type* a_szCharset); const char_type* ValidNameChars() const; const char_type* ValidOprtChars() const; @@ -173,24 +171,23 @@ void SetArgSep(char_type cArgSep); char_type GetArgSep() const; - - void Error(EErrorCodes a_iErrc, - int a_iPos = (int)mu::string_type::npos, - const string_type &a_strTok = string_type() ) const; - protected: - + void Error(EErrorCodes a_iErrc, + int a_iPos = (int)mu::string_type::npos, + const string_type& a_strTok = string_type()) const; + +protected: void Init(); virtual void InitCharSets() = 0; virtual void InitFun() = 0; virtual void InitConst() = 0; - virtual void InitOprt() = 0; + virtual void InitOprt() = 0; - virtual void OnDetectVar(string_type *pExpr, int &nStart, int &nEnd); + virtual void OnDetectVar(string_type* pExpr, int& nStart, int& nEnd); - static const char_type *c_DefaultOprt[]; - static std::locale s_locale; ///< The locale used by the parser + static const char_type* c_DefaultOprt[]; + static std::locale s_locale; ///< The locale used by the parser static bool g_DbgDumpCmdCode; static bool g_DbgDumpStack; @@ -199,112 +196,91 @@ class change_dec_sep : public std::numpunct { public: - - explicit change_dec_sep(char_type cDecSep, char_type cThousandsSep = 0, int nGroup = 3) - :std::numpunct() - ,m_nGroup(nGroup) - ,m_cDecPoint(cDecSep) - ,m_cThousandsSep(cThousandsSep) - {} - + explicit change_dec_sep(char_type cDecSep, char_type cThousandsSep = 0, int nGroup = 3) : + std::numpunct(), m_nGroup(nGroup), m_cDecPoint(cDecSep), m_cThousandsSep(cThousandsSep) + { + } + protected: - - virtual char_type do_decimal_point() const - { - return m_cDecPoint; - } - - virtual char_type do_thousands_sep() const - { - return m_cThousandsSep; - } - - virtual std::string do_grouping() const - { - // fix for issue 4: https://code.google.com/p/muparser/issues/detail?id=4 - // courtesy of Jens Bartsch - // original code: - // return std::string(1, (char)m_nGroup); - // new code: - return std::string(1, (char)(m_cThousandsSep > 0 ? m_nGroup : CHAR_MAX)); - } + virtual char_type do_decimal_point() const { return m_cDecPoint; } - private: + virtual char_type do_thousands_sep() const { return m_cThousandsSep; } - int m_nGroup; - char_type m_cDecPoint; - char_type m_cThousandsSep; - }; + virtual std::string do_grouping() const + { + // fix for issue 4: https://code.google.com/p/muparser/issues/detail?id=4 + // courtesy of Jens Bartsch + // original code: + // return std::string(1, (char)m_nGroup); + // new code: + return std::string(1, (char)(m_cThousandsSep > 0 ? m_nGroup : CHAR_MAX)); + } - private: + private: + int m_nGroup; + char_type m_cDecPoint; + char_type m_cThousandsSep; + }; - void Assign(const ParserBase &a_Parser); +private: + void Assign(const ParserBase& a_Parser); void InitTokenReader(); void ReInit() const; - void AddCallback( const string_type &a_strName, - const ParserCallback &a_Callback, - funmap_type &a_Storage, - const char_type *a_szCharSet ); - - void ApplyRemainingOprt(ParserStack &a_stOpt, - ParserStack &a_stVal) const; - void ApplyBinOprt(ParserStack &a_stOpt, - ParserStack &a_stVal) const; - - void ApplyIfElse(ParserStack &a_stOpt, - ParserStack &a_stVal) const; - - void ApplyFunc(ParserStack &a_stOpt, - ParserStack &a_stVal, - int iArgCount) const; + void AddCallback(const string_type& a_strName, + const ParserCallback& a_Callback, + funmap_type& a_Storage, + const char_type* a_szCharSet); + + void ApplyRemainingOprt(ParserStack& a_stOpt, ParserStack& a_stVal) const; + void ApplyBinOprt(ParserStack& a_stOpt, ParserStack& a_stVal) const; + + void ApplyIfElse(ParserStack& a_stOpt, ParserStack& a_stVal) const; - token_type ApplyStrFunc(const token_type &a_FunTok, - const std::vector &a_vArg) const; + void ApplyFunc(ParserStack& a_stOpt, ParserStack& a_stVal, int iArgCount) const; - int GetOprtPrecedence(const token_type &a_Tok) const; - EOprtAssociativity GetOprtAssociativity(const token_type &a_Tok) const; + token_type ApplyStrFunc(const token_type& a_FunTok, const std::vector& a_vArg) const; + + int GetOprtPrecedence(const token_type& a_Tok) const; + EOprtAssociativity GetOprtAssociativity(const token_type& a_Tok) const; void CreateRPN() const; - value_type ParseString() const; + value_type ParseString() const; value_type ParseCmdCode() const; value_type ParseCmdCodeBulk(int nOffset, int nThreadID) const; - void CheckName(const string_type &a_strName, const string_type &a_CharSet) const; - void CheckOprt(const string_type &a_sName, - const ParserCallback &a_Callback, - const string_type &a_szCharSet) const; + void CheckName(const string_type& a_strName, const string_type& a_CharSet) const; + void CheckOprt(const string_type& a_sName, const ParserCallback& a_Callback, const string_type& a_szCharSet) const; + + void StackDump(const ParserStack& a_stVal, const ParserStack& a_stOprt) const; - void StackDump(const ParserStack &a_stVal, - const ParserStack &a_stOprt) const; + /** \brief Pointer to the parser function. - /** \brief Pointer to the parser function. - Eval() calls the function whose address is stored there. */ - mutable ParseFunction m_pParseFormula; - mutable ParserByteCode m_vRPN; ///< The Bytecode class. - mutable stringbuf_type m_vStringBuf; ///< String buffer, used for storing string function arguments - stringbuf_type m_vStringVarBuf; + mutable ParseFunction m_pParseFormula; + mutable ParserByteCode m_vRPN; ///< The Bytecode class. + mutable stringbuf_type m_vStringBuf; ///< String buffer, used for storing string function arguments + stringbuf_type m_vStringVarBuf; std::auto_ptr m_pTokenReader; ///< Managed pointer to the token reader object. - funmap_type m_FunDef; ///< Map of function names and pointers. - funmap_type m_PostOprtDef; ///< Postfix operator callbacks - funmap_type m_InfixOprtDef; ///< unary infix operator. - funmap_type m_OprtDef; ///< Binary operator callbacks - valmap_type m_ConstDef; ///< user constants. - strmap_type m_StrVarDef; ///< user defined string constants - varmap_type m_VarDef; ///< user defind variables. + funmap_type m_FunDef; ///< Map of function names and pointers. + funmap_type m_PostOprtDef; ///< Postfix operator callbacks + funmap_type m_InfixOprtDef; ///< unary infix operator. + funmap_type m_OprtDef; ///< Binary operator callbacks + valmap_type m_ConstDef; ///< user constants. + strmap_type m_StrVarDef; ///< user defined string constants + varmap_type m_VarDef; ///< user defind variables. - bool m_bBuiltInOp; ///< Flag that can be used for switching built in operators on and off + bool m_bBuiltInOp; ///< Flag that can be used for switching built in operators on and off string_type m_sNameChars; ///< Charset for names string_type m_sOprtChars; ///< Charset for postfix/ binary operator tokens string_type m_sInfixOprtChars; ///< Charset for infix operator tokens - - mutable int m_nIfElseCounter; ///< Internal counter for keeping track of nested if-then-else clauses + + mutable int m_nIfElseCounter; ///< Internal counter for keeping track of nested if-then-else clauses // items merely used for caching state information mutable valbuf_type m_vStackBuffer; ///< This is merely a buffer used for the stack in the cmd parsing routine @@ -314,4 +290,3 @@ } // namespace mu #endif - diff -Nru mstflint-4.17.0+1/ext_libs/muparser/muParserBytecode.cpp mstflint-4.21.0+1/ext_libs/muparser/muParserBytecode.cpp --- mstflint-4.17.0+1/ext_libs/muparser/muParserBytecode.cpp 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/muParserBytecode.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ /* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2011 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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. */ #include "muParserBytecode.h" @@ -38,334 +38,370 @@ #include "muParserStack.h" #include "muParserTemplateMagic.h" - namespace mu { - //--------------------------------------------------------------------------- - /** \brief Bytecode default constructor. */ - ParserByteCode::ParserByteCode() - :m_iStackPos(0) - ,m_iMaxStackSize(0) - ,m_vRPN() - ,m_bEnableOptimizer(true) - { +//--------------------------------------------------------------------------- +/** \brief Bytecode default constructor. */ +ParserByteCode::ParserByteCode() : m_iStackPos(0), m_iMaxStackSize(0), m_vRPN(), m_bEnableOptimizer(true) +{ m_vRPN.reserve(50); - } +} - //--------------------------------------------------------------------------- - /** \brief Copy constructor. - - Implemented in Terms of Assign(const ParserByteCode &a_ByteCode) - */ - ParserByteCode::ParserByteCode(const ParserByteCode &a_ByteCode) - { +//--------------------------------------------------------------------------- +/** \brief Copy constructor. + + Implemented in Terms of Assign(const ParserByteCode &a_ByteCode) +*/ +ParserByteCode::ParserByteCode(const ParserByteCode& a_ByteCode) +{ Assign(a_ByteCode); - } +} + +//--------------------------------------------------------------------------- +/** \brief Assignment operator. - //--------------------------------------------------------------------------- - /** \brief Assignment operator. - - Implemented in Terms of Assign(const ParserByteCode &a_ByteCode) - */ - ParserByteCode& ParserByteCode::operator=(const ParserByteCode &a_ByteCode) - { + Implemented in Terms of Assign(const ParserByteCode &a_ByteCode) +*/ +ParserByteCode& ParserByteCode::operator=(const ParserByteCode& a_ByteCode) +{ Assign(a_ByteCode); return *this; - } +} - //--------------------------------------------------------------------------- - void ParserByteCode::EnableOptimizer(bool bStat) - { +//--------------------------------------------------------------------------- +void ParserByteCode::EnableOptimizer(bool bStat) +{ m_bEnableOptimizer = bStat; - } +} + +//--------------------------------------------------------------------------- +/** \brief Copy state of another object to this. - //--------------------------------------------------------------------------- - /** \brief Copy state of another object to this. - - \throw nowthrow - */ - void ParserByteCode::Assign(const ParserByteCode &a_ByteCode) - { - if (this==&a_ByteCode) - return; + \throw nowthrow +*/ +void ParserByteCode::Assign(const ParserByteCode& a_ByteCode) +{ + if (this == &a_ByteCode) + return; m_iStackPos = a_ByteCode.m_iStackPos; m_vRPN = a_ByteCode.m_vRPN; m_iMaxStackSize = a_ByteCode.m_iMaxStackSize; - m_bEnableOptimizer = a_ByteCode.m_bEnableOptimizer; - } + m_bEnableOptimizer = a_ByteCode.m_bEnableOptimizer; +} - //--------------------------------------------------------------------------- - /** \brief Add a Variable pointer to bytecode. - \param a_pVar Pointer to be added. - \throw nothrow - */ - void ParserByteCode::AddVar(value_type *a_pVar) - { +//--------------------------------------------------------------------------- +/** \brief Add a Variable pointer to bytecode. + \param a_pVar Pointer to be added. + \throw nothrow +*/ +void ParserByteCode::AddVar(value_type* a_pVar) +{ ++m_iStackPos; m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); // optimization does not apply SToken tok; - tok.Cmd = cmVAR; - tok.Val.ptr = a_pVar; - tok.Val.data = 1; + tok.Cmd = cmVAR; + tok.Val.ptr = a_pVar; + tok.Val.data = 1; tok.Val.data2 = 0; m_vRPN.push_back(tok); - } +} + +//--------------------------------------------------------------------------- +/** \brief Add a Variable pointer to bytecode. - //--------------------------------------------------------------------------- - /** \brief Add a Variable pointer to bytecode. + Value entries in byte code consist of: +
    +
  • value array position of the value
  • +
  • the operator code according to ParserToken::cmVAL
  • +
  • the value stored in #mc_iSizeVal number of bytecode entries.
  • +
- Value entries in byte code consist of: -
    -
  • value array position of the value
  • -
  • the operator code according to ParserToken::cmVAL
  • -
  • the value stored in #mc_iSizeVal number of bytecode entries.
  • -
- - \param a_pVal Value to be added. - \throw nothrow - */ - void ParserByteCode::AddVal(value_type a_fVal) - { + \param a_pVal Value to be added. + \throw nothrow +*/ +void ParserByteCode::AddVal(value_type a_fVal) +{ ++m_iStackPos; m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); // If optimization does not apply SToken tok; tok.Cmd = cmVAL; - tok.Val.ptr = NULL; - tok.Val.data = 0; + tok.Val.ptr = NULL; + tok.Val.data = 0; tok.Val.data2 = a_fVal; m_vRPN.push_back(tok); - } +} - //--------------------------------------------------------------------------- - void ParserByteCode::ConstantFolding(ECmdCode a_Oprt) - { +//--------------------------------------------------------------------------- +void ParserByteCode::ConstantFolding(ECmdCode a_Oprt) +{ std::size_t sz = m_vRPN.size(); - value_type &x = m_vRPN[sz-2].Val.data2, - &y = m_vRPN[sz-1].Val.data2; + value_type &x = m_vRPN[sz - 2].Val.data2, &y = m_vRPN[sz - 1].Val.data2; switch (a_Oprt) { - case cmLAND: x = (int)x && (int)y; m_vRPN.pop_back(); break; - case cmLOR: x = (int)x || (int)y; m_vRPN.pop_back(); break; - case cmLT: x = x < y; m_vRPN.pop_back(); break; - case cmGT: x = x > y; m_vRPN.pop_back(); break; - case cmLE: x = x <= y; m_vRPN.pop_back(); break; - case cmGE: x = x >= y; m_vRPN.pop_back(); break; - case cmNEQ: x = x != y; m_vRPN.pop_back(); break; - case cmEQ: x = x == y; m_vRPN.pop_back(); break; - case cmADD: x = x + y; m_vRPN.pop_back(); break; - case cmSUB: x = x - y; m_vRPN.pop_back(); break; - case cmMUL: x = x * y; m_vRPN.pop_back(); break; - case cmDIV: + case cmLAND: + x = (int)x && (int)y; + m_vRPN.pop_back(); + break; + case cmLOR: + x = (int)x || (int)y; + m_vRPN.pop_back(); + break; + case cmLT: + x = x < y; + m_vRPN.pop_back(); + break; + case cmGT: + x = x > y; + m_vRPN.pop_back(); + break; + case cmLE: + x = x <= y; + m_vRPN.pop_back(); + break; + case cmGE: + x = x >= y; + m_vRPN.pop_back(); + break; + case cmNEQ: + x = x != y; + m_vRPN.pop_back(); + break; + case cmEQ: + x = x == y; + m_vRPN.pop_back(); + break; + case cmADD: + x = x + y; + m_vRPN.pop_back(); + break; + case cmSUB: + x = x - y; + m_vRPN.pop_back(); + break; + case cmMUL: + x = x * y; + m_vRPN.pop_back(); + break; + case cmDIV: #if defined(MUP_MATH_EXCEPTIONS) - if (y==0) - throw ParserError(ecDIV_BY_ZERO, _T("0")); + if (y == 0) + throw ParserError(ecDIV_BY_ZERO, _T("0")); #endif - x = x / y; - m_vRPN.pop_back(); - break; + x = x / y; + m_vRPN.pop_back(); + break; - case cmPOW: x = MathImpl::Pow(x, y); - m_vRPN.pop_back(); - break; + case cmPOW: + x = MathImpl::Pow(x, y); + m_vRPN.pop_back(); + break; - default: - break; + default: + break; } // switch opcode - } +} - //--------------------------------------------------------------------------- - /** \brief Add an operator identifier to bytecode. - - Operator entries in byte code consist of: -
    -
  • value array position of the result
  • -
  • the operator code according to ParserToken::ECmdCode
  • -
- - \sa ParserToken::ECmdCode - */ - void ParserByteCode::AddOp(ECmdCode a_Oprt) - { +//--------------------------------------------------------------------------- +/** \brief Add an operator identifier to bytecode. + + Operator entries in byte code consist of: +
    +
  • value array position of the result
  • +
  • the operator code according to ParserToken::ECmdCode
  • +
+ + \sa ParserToken::ECmdCode +*/ +void ParserByteCode::AddOp(ECmdCode a_Oprt) +{ bool bOptimized = false; if (m_bEnableOptimizer) { - std::size_t sz = m_vRPN.size(); + std::size_t sz = m_vRPN.size(); - // Check for foldable constants like: - // cmVAL cmVAL cmADD - // where cmADD can stand fopr any binary operator applied to - // two constant values. - if (sz>=2 && m_vRPN[sz-2].Cmd == cmVAL && m_vRPN[sz-1].Cmd == cmVAL) - { - ConstantFolding(a_Oprt); - bOptimized = true; - } - else - { - switch((int)a_Oprt) + // Check for foldable constants like: + // cmVAL cmVAL cmADD + // where cmADD can stand fopr any binary operator applied to + // two constant values. + if (sz >= 2 && m_vRPN[sz - 2].Cmd == cmVAL && m_vRPN[sz - 1].Cmd == cmVAL) { - case cmPOW: - // Optimization for polynomials of low order - if (m_vRPN[sz-2].Cmd == cmVAR && m_vRPN[sz-1].Cmd == cmVAL) - { - if (m_vRPN[sz-1].Val.data2==2) - m_vRPN[sz-2].Cmd = cmVARPOW2; - else if (m_vRPN[sz-1].Val.data2==3) - m_vRPN[sz-2].Cmd = cmVARPOW3; - else if (m_vRPN[sz-1].Val.data2==4) - m_vRPN[sz-2].Cmd = cmVARPOW4; - else - break; - - m_vRPN.pop_back(); - bOptimized = true; - } - break; - - case cmSUB: - case cmADD: - // Simple optimization based on pattern recognition for a shitload of different - // bytecode combinations of addition/subtraction - if ( (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVAL) || - (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVAR) || - (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVARMUL) || - (m_vRPN[sz-1].Cmd == cmVARMUL && m_vRPN[sz-2].Cmd == cmVAL) || - (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVAR && m_vRPN[sz-2].Val.ptr == m_vRPN[sz-1].Val.ptr) || - (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVARMUL && m_vRPN[sz-2].Val.ptr == m_vRPN[sz-1].Val.ptr) || - (m_vRPN[sz-1].Cmd == cmVARMUL && m_vRPN[sz-2].Cmd == cmVAR && m_vRPN[sz-2].Val.ptr == m_vRPN[sz-1].Val.ptr) || - (m_vRPN[sz-1].Cmd == cmVARMUL && m_vRPN[sz-2].Cmd == cmVARMUL && m_vRPN[sz-2].Val.ptr == m_vRPN[sz-1].Val.ptr) ) - { - assert( (m_vRPN[sz-2].Val.ptr==NULL && m_vRPN[sz-1].Val.ptr!=NULL) || - (m_vRPN[sz-2].Val.ptr!=NULL && m_vRPN[sz-1].Val.ptr==NULL) || - (m_vRPN[sz-2].Val.ptr == m_vRPN[sz-1].Val.ptr) ); - - m_vRPN[sz-2].Cmd = cmVARMUL; - m_vRPN[sz-2].Val.ptr = (value_type*)((long long)(m_vRPN[sz-2].Val.ptr) | (long long)(m_vRPN[sz-1].Val.ptr)); // variable - m_vRPN[sz-2].Val.data2 += ((a_Oprt==cmSUB) ? -1 : 1) * m_vRPN[sz-1].Val.data2; // offset - m_vRPN[sz-2].Val.data += ((a_Oprt==cmSUB) ? -1 : 1) * m_vRPN[sz-1].Val.data; // multiplicand - m_vRPN.pop_back(); - bOptimized = true; - } - break; - - case cmMUL: - if ( (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVAL) || - (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVAR) ) - { - m_vRPN[sz-2].Cmd = cmVARMUL; - m_vRPN[sz-2].Val.ptr = (value_type*)((long long)(m_vRPN[sz-2].Val.ptr) | (long long)(m_vRPN[sz-1].Val.ptr)); - m_vRPN[sz-2].Val.data = m_vRPN[sz-2].Val.data2 + m_vRPN[sz-1].Val.data2; - m_vRPN[sz-2].Val.data2 = 0; - m_vRPN.pop_back(); - bOptimized = true; - } - else if ( (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVARMUL) || - (m_vRPN[sz-1].Cmd == cmVARMUL && m_vRPN[sz-2].Cmd == cmVAL) ) - { - // Optimization: 2*(3*b+1) or (3*b+1)*2 -> 6*b+2 - m_vRPN[sz-2].Cmd = cmVARMUL; - m_vRPN[sz-2].Val.ptr = (value_type*)((long long)(m_vRPN[sz-2].Val.ptr) | (long long)(m_vRPN[sz-1].Val.ptr)); - if (m_vRPN[sz-1].Cmd == cmVAL) - { - m_vRPN[sz-2].Val.data *= m_vRPN[sz-1].Val.data2; - m_vRPN[sz-2].Val.data2 *= m_vRPN[sz-1].Val.data2; - } - else - { - m_vRPN[sz-2].Val.data = m_vRPN[sz-1].Val.data * m_vRPN[sz-2].Val.data2; - m_vRPN[sz-2].Val.data2 = m_vRPN[sz-1].Val.data2 * m_vRPN[sz-2].Val.data2; - } - m_vRPN.pop_back(); - bOptimized = true; - } - else if (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVAR && - m_vRPN[sz-1].Val.ptr == m_vRPN[sz-2].Val.ptr) - { - // Optimization: a*a -> a^2 - m_vRPN[sz-2].Cmd = cmVARPOW2; - m_vRPN.pop_back(); - bOptimized = true; - } - break; + ConstantFolding(a_Oprt); + bOptimized = true; + } + else + { + switch ((int)a_Oprt) + { + case cmPOW: + // Optimization for polynomials of low order + if (m_vRPN[sz - 2].Cmd == cmVAR && m_vRPN[sz - 1].Cmd == cmVAL) + { + if (m_vRPN[sz - 1].Val.data2 == 2) + m_vRPN[sz - 2].Cmd = cmVARPOW2; + else if (m_vRPN[sz - 1].Val.data2 == 3) + m_vRPN[sz - 2].Cmd = cmVARPOW3; + else if (m_vRPN[sz - 1].Val.data2 == 4) + m_vRPN[sz - 2].Cmd = cmVARPOW4; + else + break; + + m_vRPN.pop_back(); + bOptimized = true; + } + break; - case cmDIV: - if (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVARMUL && m_vRPN[sz-1].Val.data2!=0) - { - // Optimization: 4*a/2 -> 2*a - m_vRPN[sz-2].Val.data /= m_vRPN[sz-1].Val.data2; - m_vRPN[sz-2].Val.data2 /= m_vRPN[sz-1].Val.data2; - m_vRPN.pop_back(); - bOptimized = true; - } - break; - - } // switch a_Oprt - } + case cmSUB: + case cmADD: + // Simple optimization based on pattern recognition for a shitload of different + // bytecode combinations of addition/subtraction + if ((m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVAL) || + (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVAR) || + (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVARMUL) || + (m_vRPN[sz - 1].Cmd == cmVARMUL && m_vRPN[sz - 2].Cmd == cmVAL) || + (m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVAR && + m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr) || + (m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVARMUL && + m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr) || + (m_vRPN[sz - 1].Cmd == cmVARMUL && m_vRPN[sz - 2].Cmd == cmVAR && + m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr) || + (m_vRPN[sz - 1].Cmd == cmVARMUL && m_vRPN[sz - 2].Cmd == cmVARMUL && + m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr)) + { + assert((m_vRPN[sz - 2].Val.ptr == NULL && m_vRPN[sz - 1].Val.ptr != NULL) || + (m_vRPN[sz - 2].Val.ptr != NULL && m_vRPN[sz - 1].Val.ptr == NULL) || + (m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr)); + + m_vRPN[sz - 2].Cmd = cmVARMUL; + m_vRPN[sz - 2].Val.ptr = (value_type*)((long long)(m_vRPN[sz - 2].Val.ptr) | + (long long)(m_vRPN[sz - 1].Val.ptr)); // variable + m_vRPN[sz - 2].Val.data2 += ((a_Oprt == cmSUB) ? -1 : 1) * m_vRPN[sz - 1].Val.data2; // offset + m_vRPN[sz - 2].Val.data += + ((a_Oprt == cmSUB) ? -1 : 1) * m_vRPN[sz - 1].Val.data; // multiplicand + m_vRPN.pop_back(); + bOptimized = true; + } + break; + + case cmMUL: + if ((m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVAL) || + (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVAR)) + { + m_vRPN[sz - 2].Cmd = cmVARMUL; + m_vRPN[sz - 2].Val.ptr = + (value_type*)((long long)(m_vRPN[sz - 2].Val.ptr) | (long long)(m_vRPN[sz - 1].Val.ptr)); + m_vRPN[sz - 2].Val.data = m_vRPN[sz - 2].Val.data2 + m_vRPN[sz - 1].Val.data2; + m_vRPN[sz - 2].Val.data2 = 0; + m_vRPN.pop_back(); + bOptimized = true; + } + else if ((m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVARMUL) || + (m_vRPN[sz - 1].Cmd == cmVARMUL && m_vRPN[sz - 2].Cmd == cmVAL)) + { + // Optimization: 2*(3*b+1) or (3*b+1)*2 -> 6*b+2 + m_vRPN[sz - 2].Cmd = cmVARMUL; + m_vRPN[sz - 2].Val.ptr = + (value_type*)((long long)(m_vRPN[sz - 2].Val.ptr) | (long long)(m_vRPN[sz - 1].Val.ptr)); + if (m_vRPN[sz - 1].Cmd == cmVAL) + { + m_vRPN[sz - 2].Val.data *= m_vRPN[sz - 1].Val.data2; + m_vRPN[sz - 2].Val.data2 *= m_vRPN[sz - 1].Val.data2; + } + else + { + m_vRPN[sz - 2].Val.data = m_vRPN[sz - 1].Val.data * m_vRPN[sz - 2].Val.data2; + m_vRPN[sz - 2].Val.data2 = m_vRPN[sz - 1].Val.data2 * m_vRPN[sz - 2].Val.data2; + } + m_vRPN.pop_back(); + bOptimized = true; + } + else if (m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVAR && + m_vRPN[sz - 1].Val.ptr == m_vRPN[sz - 2].Val.ptr) + { + // Optimization: a*a -> a^2 + m_vRPN[sz - 2].Cmd = cmVARPOW2; + m_vRPN.pop_back(); + bOptimized = true; + } + break; + + case cmDIV: + if (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVARMUL && m_vRPN[sz - 1].Val.data2 != 0) + { + // Optimization: 4*a/2 -> 2*a + m_vRPN[sz - 2].Val.data /= m_vRPN[sz - 1].Val.data2; + m_vRPN[sz - 2].Val.data2 /= m_vRPN[sz - 1].Val.data2; + m_vRPN.pop_back(); + bOptimized = true; + } + break; + + } // switch a_Oprt + } } // If optimization can't be applied just write the value if (!bOptimized) { - --m_iStackPos; - SToken tok; - tok.Cmd = a_Oprt; - m_vRPN.push_back(tok); + --m_iStackPos; + SToken tok; + tok.Cmd = a_Oprt; + m_vRPN.push_back(tok); } - } +} - //--------------------------------------------------------------------------- - void ParserByteCode::AddIfElse(ECmdCode a_Oprt) - { +//--------------------------------------------------------------------------- +void ParserByteCode::AddIfElse(ECmdCode a_Oprt) +{ SToken tok; tok.Cmd = a_Oprt; m_vRPN.push_back(tok); - } +} + +//--------------------------------------------------------------------------- +/** \brief Add an assignment operator - //--------------------------------------------------------------------------- - /** \brief Add an assignment operator - - Operator entries in byte code consist of: -
    -
  • cmASSIGN code
  • -
  • the pointer of the destination variable
  • -
- - \sa ParserToken::ECmdCode - */ - void ParserByteCode::AddAssignOp(value_type *a_pVar) - { + Operator entries in byte code consist of: +
    +
  • cmASSIGN code
  • +
  • the pointer of the destination variable
  • +
+ + \sa ParserToken::ECmdCode +*/ +void ParserByteCode::AddAssignOp(value_type* a_pVar) +{ --m_iStackPos; SToken tok; tok.Cmd = cmASSIGN; tok.Oprt.ptr = a_pVar; m_vRPN.push_back(tok); - } +} - //--------------------------------------------------------------------------- - /** \brief Add function to bytecode. +//--------------------------------------------------------------------------- +/** \brief Add function to bytecode. - \param a_iArgc Number of arguments, negative numbers indicate multiarg functions. - \param a_pFun Pointer to function callback. - */ - void ParserByteCode::AddFun(generic_fun_type a_pFun, int a_iArgc) - { - if (a_iArgc>=0) + \param a_iArgc Number of arguments, negative numbers indicate multiarg functions. + \param a_pFun Pointer to function callback. +*/ +void ParserByteCode::AddFun(generic_fun_type a_pFun, int a_iArgc) +{ + if (a_iArgc >= 0) { - m_iStackPos = m_iStackPos - a_iArgc + 1; + m_iStackPos = m_iStackPos - a_iArgc + 1; } else { - // function with unlimited number of arguments - m_iStackPos = m_iStackPos + a_iArgc + 1; + // function with unlimited number of arguments + m_iStackPos = m_iStackPos + a_iArgc + 1; } m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); @@ -374,17 +410,17 @@ tok.Fun.argc = a_iArgc; tok.Fun.ptr = a_pFun; m_vRPN.push_back(tok); - } +} - //--------------------------------------------------------------------------- - /** \brief Add a bulk function to bytecode. +//--------------------------------------------------------------------------- +/** \brief Add a bulk function to bytecode. - \param a_iArgc Number of arguments, negative numbers indicate multiarg functions. - \param a_pFun Pointer to function callback. - */ - void ParserByteCode::AddBulkFun(generic_fun_type a_pFun, int a_iArgc) - { - m_iStackPos = m_iStackPos - a_iArgc + 1; + \param a_iArgc Number of arguments, negative numbers indicate multiarg functions. + \param a_pFun Pointer to function callback. +*/ +void ParserByteCode::AddBulkFun(generic_fun_type a_pFun, int a_iArgc) +{ + m_iStackPos = m_iStackPos - a_iArgc + 1; m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); SToken tok; @@ -392,18 +428,18 @@ tok.Fun.argc = a_iArgc; tok.Fun.ptr = a_pFun; m_vRPN.push_back(tok); - } +} - //--------------------------------------------------------------------------- - /** \brief Add Strung function entry to the parser bytecode. - \throw nothrow - - A string function entry consists of the stack position of the return value, - followed by a cmSTRFUNC code, the function pointer and an index into the - string buffer maintained by the parser. - */ - void ParserByteCode::AddStrFun(generic_fun_type a_pFun, int a_iArgc, int a_iIdx) - { +//--------------------------------------------------------------------------- +/** \brief Add Strung function entry to the parser bytecode. + \throw nothrow + + A string function entry consists of the stack position of the return value, + followed by a cmSTRFUNC code, the function pointer and an index into the + string buffer maintained by the parser. +*/ +void ParserByteCode::AddStrFun(generic_fun_type a_pFun, int a_iArgc, int a_iIdx) +{ m_iStackPos = m_iStackPos - a_iArgc + 1; SToken tok; @@ -414,175 +450,213 @@ m_vRPN.push_back(tok); m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); - } +} + +//--------------------------------------------------------------------------- +/** \brief Add end marker to bytecode. - //--------------------------------------------------------------------------- - /** \brief Add end marker to bytecode. - - \throw nothrow - */ - void ParserByteCode::Finalize() - { + \throw nothrow +*/ +void ParserByteCode::Finalize() +{ SToken tok; tok.Cmd = cmEND; m_vRPN.push_back(tok); - rpn_type(m_vRPN).swap(m_vRPN); // shrink bytecode vector to fit + rpn_type(m_vRPN).swap(m_vRPN); // shrink bytecode vector to fit // Determine the if-then-else jump offsets ParserStack stIf, stElse; int idx; - for (int i=0; i<(int)m_vRPN.size(); ++i) + for (int i = 0; i < (int)m_vRPN.size(); ++i) { - switch(m_vRPN[i].Cmd) - { - case cmIF: - stIf.push(i); - break; + switch (m_vRPN[i].Cmd) + { + case cmIF: + stIf.push(i); + break; - case cmELSE: - stElse.push(i); - idx = stIf.pop(); - m_vRPN[idx].Oprt.offset = i - idx; - break; - - case cmENDIF: - idx = stElse.pop(); - m_vRPN[idx].Oprt.offset = i - idx; - break; + case cmELSE: + stElse.push(i); + idx = stIf.pop(); + m_vRPN[idx].Oprt.offset = i - idx; + break; - default: - break; - } + case cmENDIF: + idx = stElse.pop(); + m_vRPN[idx].Oprt.offset = i - idx; + break; + + default: + break; + } } - } +} - //--------------------------------------------------------------------------- - const SToken* ParserByteCode::GetBase() const - { - if (m_vRPN.size()==0) - throw ParserError(ecINTERNAL_ERROR); +//--------------------------------------------------------------------------- +const SToken* ParserByteCode::GetBase() const +{ + if (m_vRPN.size() == 0) + throw ParserError(ecINTERNAL_ERROR); else - return &m_vRPN[0]; - } + return &m_vRPN[0]; +} + +//--------------------------------------------------------------------------- +std::size_t ParserByteCode::GetMaxStackSize() const +{ + return m_iMaxStackSize + 1; +} - //--------------------------------------------------------------------------- - std::size_t ParserByteCode::GetMaxStackSize() const - { - return m_iMaxStackSize+1; - } - - //--------------------------------------------------------------------------- - /** \brief Returns the number of entries in the bytecode. */ - std::size_t ParserByteCode::GetSize() const - { +//--------------------------------------------------------------------------- +/** \brief Returns the number of entries in the bytecode. */ +std::size_t ParserByteCode::GetSize() const +{ return m_vRPN.size(); - } +} + +//--------------------------------------------------------------------------- +/** \brief Delete the bytecode. - //--------------------------------------------------------------------------- - /** \brief Delete the bytecode. - - \throw nothrow - - The name of this function is a violation of my own coding guidelines - but this way it's more in line with the STL functions thus more - intuitive. - */ - void ParserByteCode::clear() - { + \throw nothrow + + The name of this function is a violation of my own coding guidelines + but this way it's more in line with the STL functions thus more + intuitive. +*/ +void ParserByteCode::clear() +{ m_vRPN.clear(); m_iStackPos = 0; m_iMaxStackSize = 0; - } +} - //--------------------------------------------------------------------------- - /** \brief Dump bytecode (for debugging only!). */ - void ParserByteCode::AsciiDump() - { - if (!m_vRPN.size()) +//--------------------------------------------------------------------------- +/** \brief Dump bytecode (for debugging only!). */ +void ParserByteCode::AsciiDump() +{ + if (!m_vRPN.size()) { - mu::console() << _T("No bytecode available\n"); - return; + mu::console() << _T("No bytecode available\n"); + return; } mu::console() << _T("Number of RPN tokens:") << (int)m_vRPN.size() << _T("\n"); - for (std::size_t i=0; i \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2004-2013 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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 MU_PARSER_BYTECODE_H #define MU_PARSER_BYTECODE_H @@ -38,56 +38,53 @@ \brief Definition of the parser bytecode class. */ - namespace mu { - struct SToken - { +struct SToken +{ ECmdCode Cmd; int StackPos; union { - struct //SValData - { - value_type *ptr; - value_type data; - value_type data2; - } Val; - - struct //SFunData - { - // Note: generic_fun_type is merely a placeholder. The real type could be - // anything between gun_type1 and fun_type9. I can't use a void - // pointer due to constraints in the ANSI standard which allows - // data pointers and function pointers to differ in size. - generic_fun_type ptr; - int argc; - int idx; - } Fun; - - struct //SOprtData - { - value_type *ptr; - int offset; - } Oprt; + struct // SValData + { + value_type* ptr; + value_type data; + value_type data2; + } Val; + + struct // SFunData + { + // Note: generic_fun_type is merely a placeholder. The real type could be + // anything between gun_type1 and fun_type9. I can't use a void + // pointer due to constraints in the ANSI standard which allows + // data pointers and function pointers to differ in size. + generic_fun_type ptr; + int argc; + int idx; + } Fun; + + struct // SOprtData + { + value_type* ptr; + int offset; + } Oprt; }; - }; - - - /** \brief Bytecode implementation of the Math Parser. - - The bytecode contains the formula converted to revers polish notation stored in a continious - memory area. Associated with this data are operator codes, variable pointers, constant - values and function pointers. Those are necessary in order to calculate the result. - All those data items will be casted to the underlying datatype of the bytecode. +}; + +/** \brief Bytecode implementation of the Math Parser. + +The bytecode contains the formula converted to revers polish notation stored in a continious +memory area. Associated with this data are operator codes, variable pointers, constant +values and function pointers. Those are necessary in order to calculate the result. +All those data items will be casted to the underlying datatype of the bytecode. - \author (C) 2004-2013 Ingo Berg +\author (C) 2004-2013 Ingo Berg */ class ParserByteCode { private: - /** \brief Token type for internal use only. */ typedef ParserToken token_type; @@ -99,26 +96,25 @@ /** \brief Maximum size needed for the stack. */ std::size_t m_iMaxStackSize; - + /** \brief The actual rpn storage. */ - rpn_type m_vRPN; + rpn_type m_vRPN; bool m_bEnableOptimizer; void ConstantFolding(ECmdCode a_Oprt); public: - ParserByteCode(); - ParserByteCode(const ParserByteCode &a_ByteCode); - ParserByteCode& operator=(const ParserByteCode &a_ByteCode); - void Assign(const ParserByteCode &a_ByteCode); + ParserByteCode(const ParserByteCode& a_ByteCode); + ParserByteCode& operator=(const ParserByteCode& a_ByteCode); + void Assign(const ParserByteCode& a_ByteCode); - void AddVar(value_type *a_pVar); + void AddVar(value_type* a_pVar); void AddVal(value_type a_fVal); void AddOp(ECmdCode a_Oprt); void AddIfElse(ECmdCode a_Oprt); - void AddAssignOp(value_type *a_pVar); + void AddAssignOp(value_type* a_pVar); void AddFun(generic_fun_type a_pFun, int a_iArgc); void AddBulkFun(generic_fun_type a_pFun, int a_iArgc); void AddStrFun(generic_fun_type a_pFun, int a_iArgc, int a_iIdx); @@ -137,5 +133,3 @@ } // namespace mu #endif - - diff -Nru mstflint-4.17.0+1/ext_libs/muparser/muParserCallback.cpp mstflint-4.21.0+1/ext_libs/muparser/muParserCallback.cpp --- mstflint-4.17.0+1/ext_libs/muparser/muParserCallback.cpp 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/muParserCallback.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ /* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2004-2011 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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. */ #include "muParserCallback.h" @@ -29,435 +29,440 @@ \brief Implementation of the parser callback class. */ - namespace mu { - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(fun_type0 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(0) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(fun_type1 a_pFun, bool a_bAllowOpti, int a_iPrec, ECmdCode a_iCode) - :m_pFun((void*)a_pFun) - ,m_iArgc(1) - ,m_iPri(a_iPrec) - ,m_eOprtAsct(oaNONE) - ,m_iCode(a_iCode) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - /** \brief Constructor for constructing function callbacks taking two arguments. - \throw nothrow - */ - ParserCallback::ParserCallback(fun_type2 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(2) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - /** \brief Constructor for constructing binary operator callbacks. - \param a_pFun Pointer to a static function taking two arguments - \param a_bAllowOpti A flag indicating this function can be optimized - \param a_iPrec The operator precedence - \param a_eOprtAsct The operators associativity - \throw nothrow - */ - ParserCallback::ParserCallback(fun_type2 a_pFun, - bool a_bAllowOpti, - int a_iPrec, - EOprtAssociativity a_eOprtAsct) - :m_pFun((void*)a_pFun) - ,m_iArgc(2) - ,m_iPri(a_iPrec) - ,m_eOprtAsct(a_eOprtAsct) - ,m_iCode(cmOPRT_BIN) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(fun_type3 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(3) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(fun_type4 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(4) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(fun_type5 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(5) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(fun_type6 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(6) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(fun_type7 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(7) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(fun_type8 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(8) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(fun_type9 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(9) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(fun_type10 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(10) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(bulkfun_type0 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(0) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(bulkfun_type1 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(1) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - /** \brief Constructor for constructing function callbacks taking two arguments. - \throw nothrow - */ - ParserCallback::ParserCallback(bulkfun_type2 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(2) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(bulkfun_type3 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(3) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(bulkfun_type4 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(4) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(bulkfun_type5 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(5) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(bulkfun_type6 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(6) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(bulkfun_type7 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(7) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(bulkfun_type8 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(8) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(bulkfun_type9 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(9) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(bulkfun_type10 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(10) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(multfun_type a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(-1) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(strfun_type1 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(0) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_STR) - ,m_iType(tpSTR) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(strfun_type2 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(1) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_STR) - ,m_iType(tpSTR) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(strfun_type3 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(2) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_STR) - ,m_iType(tpSTR) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - /** \brief Default constructor. - \throw nothrow - */ - ParserCallback::ParserCallback() - :m_pFun(0) - ,m_iArgc(0) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmUNKNOWN) - ,m_iType(tpVOID) - ,m_bAllowOpti(0) - {} - - - //--------------------------------------------------------------------------- - /** \brief Copy constructor. - \throw nothrow - */ - ParserCallback::ParserCallback(const ParserCallback &ref) - { - m_pFun = ref.m_pFun; - m_iArgc = ref.m_iArgc; +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(fun_type0 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(0), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(fun_type1 a_pFun, bool a_bAllowOpti, int a_iPrec, ECmdCode a_iCode) : + m_pFun((void*)a_pFun), + m_iArgc(1), + m_iPri(a_iPrec), + m_eOprtAsct(oaNONE), + m_iCode(a_iCode), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +/** \brief Constructor for constructing function callbacks taking two arguments. + \throw nothrow +*/ +ParserCallback::ParserCallback(fun_type2 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(2), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +/** \brief Constructor for constructing binary operator callbacks. + \param a_pFun Pointer to a static function taking two arguments + \param a_bAllowOpti A flag indicating this function can be optimized + \param a_iPrec The operator precedence + \param a_eOprtAsct The operators associativity + \throw nothrow +*/ +ParserCallback::ParserCallback(fun_type2 a_pFun, bool a_bAllowOpti, int a_iPrec, EOprtAssociativity a_eOprtAsct) : + m_pFun((void*)a_pFun), + m_iArgc(2), + m_iPri(a_iPrec), + m_eOprtAsct(a_eOprtAsct), + m_iCode(cmOPRT_BIN), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(fun_type3 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(3), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(fun_type4 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(4), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(fun_type5 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(5), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(fun_type6 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(6), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(fun_type7 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(7), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(fun_type8 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(8), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(fun_type9 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(9), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(fun_type10 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(10), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(bulkfun_type0 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(0), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC_BULK), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(bulkfun_type1 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(1), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC_BULK), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +/** \brief Constructor for constructing function callbacks taking two arguments. + \throw nothrow +*/ +ParserCallback::ParserCallback(bulkfun_type2 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(2), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC_BULK), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(bulkfun_type3 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(3), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC_BULK), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(bulkfun_type4 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(4), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC_BULK), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(bulkfun_type5 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(5), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC_BULK), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(bulkfun_type6 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(6), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC_BULK), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(bulkfun_type7 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(7), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC_BULK), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(bulkfun_type8 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(8), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC_BULK), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(bulkfun_type9 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(9), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC_BULK), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(bulkfun_type10 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(10), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC_BULK), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(multfun_type a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(-1), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC), + m_iType(tpDBL), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(strfun_type1 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(0), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC_STR), + m_iType(tpSTR), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(strfun_type2 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(1), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC_STR), + m_iType(tpSTR), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +ParserCallback::ParserCallback(strfun_type3 a_pFun, bool a_bAllowOpti) : + m_pFun((void*)a_pFun), + m_iArgc(2), + m_iPri(-1), + m_eOprtAsct(oaNONE), + m_iCode(cmFUNC_STR), + m_iType(tpSTR), + m_bAllowOpti(a_bAllowOpti) +{ +} + +//--------------------------------------------------------------------------- +/** \brief Default constructor. + \throw nothrow +*/ +ParserCallback::ParserCallback() : + m_pFun(0), m_iArgc(0), m_iPri(-1), m_eOprtAsct(oaNONE), m_iCode(cmUNKNOWN), m_iType(tpVOID), m_bAllowOpti(0) +{ +} + +//--------------------------------------------------------------------------- +/** \brief Copy constructor. + \throw nothrow +*/ +ParserCallback::ParserCallback(const ParserCallback& ref) +{ + m_pFun = ref.m_pFun; + m_iArgc = ref.m_iArgc; m_bAllowOpti = ref.m_bAllowOpti; - m_iCode = ref.m_iCode; - m_iType = ref.m_iType; - m_iPri = ref.m_iPri; - m_eOprtAsct = ref.m_eOprtAsct; - } - - //--------------------------------------------------------------------------- - /** \brief Clone this instance and return a pointer to the new instance. */ - ParserCallback* ParserCallback::Clone() const - { + m_iCode = ref.m_iCode; + m_iType = ref.m_iType; + m_iPri = ref.m_iPri; + m_eOprtAsct = ref.m_eOprtAsct; +} + +//--------------------------------------------------------------------------- +/** \brief Clone this instance and return a pointer to the new instance. */ +ParserCallback* ParserCallback::Clone() const +{ return new ParserCallback(*this); - } +} + +//--------------------------------------------------------------------------- +/** \brief Return tru if the function is conservative. + + Conservative functions return always the same result for the same argument. + \throw nothrow +*/ +bool ParserCallback::IsOptimizable() const +{ + return m_bAllowOpti; +} + +//--------------------------------------------------------------------------- +/** \brief Get the callback address for the parser function. - //--------------------------------------------------------------------------- - /** \brief Return tru if the function is conservative. + The type of the address is void. It needs to be recasted according to the + argument number to the right type. + + \throw nothrow + \return #pFun +*/ +void* ParserCallback::GetAddr() const +{ + return m_pFun; +} + +//--------------------------------------------------------------------------- +/** \brief Return the callback code. */ +ECmdCode ParserCallback::GetCode() const +{ + return m_iCode; +} - Conservative functions return always the same result for the same argument. - \throw nothrow - */ - bool ParserCallback::IsOptimizable() const - { - return m_bAllowOpti; - } - - //--------------------------------------------------------------------------- - /** \brief Get the callback address for the parser function. - - The type of the address is void. It needs to be recasted according to the - argument number to the right type. - - \throw nothrow - \return #pFun - */ - void* ParserCallback::GetAddr() const - { - return m_pFun; - } - - //--------------------------------------------------------------------------- - /** \brief Return the callback code. */ - ECmdCode ParserCallback::GetCode() const - { - return m_iCode; - } - - //--------------------------------------------------------------------------- - ETypeCode ParserCallback::GetType() const - { - return m_iType; - } - - - //--------------------------------------------------------------------------- - /** \brief Return the operator precedence. - \throw nothrown - - Only valid if the callback token is an operator token (binary or infix). - */ - int ParserCallback::GetPri() const - { - return m_iPri; - } - - //--------------------------------------------------------------------------- - /** \brief Return the operators associativity. - \throw nothrown - - Only valid if the callback token is a binary operator token. - */ - EOprtAssociativity ParserCallback::GetAssociativity() const - { +//--------------------------------------------------------------------------- +ETypeCode ParserCallback::GetType() const +{ + return m_iType; +} + +//--------------------------------------------------------------------------- +/** \brief Return the operator precedence. + \throw nothrown + + Only valid if the callback token is an operator token (binary or infix). +*/ +int ParserCallback::GetPri() const +{ + return m_iPri; +} + +//--------------------------------------------------------------------------- +/** \brief Return the operators associativity. + \throw nothrown + + Only valid if the callback token is a binary operator token. +*/ +EOprtAssociativity ParserCallback::GetAssociativity() const +{ return m_eOprtAsct; - } +} - //--------------------------------------------------------------------------- - /** \brief Returns the number of function Arguments. */ - int ParserCallback::GetArgc() const - { - return m_iArgc; - } +//--------------------------------------------------------------------------- +/** \brief Returns the number of function Arguments. */ +int ParserCallback::GetArgc() const +{ + return m_iArgc; +} } // namespace mu diff -Nru mstflint-4.17.0+1/ext_libs/muparser/muParserCallback.h mstflint-4.21.0+1/ext_libs/muparser/muParserCallback.h --- mstflint-4.17.0+1/ext_libs/muparser/muParserCallback.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/muParserCallback.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ /* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2004-2011 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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 MU_PARSER_CALLBACK_H @@ -34,45 +34,44 @@ namespace mu { - /** \brief Encapsulation of prototypes for a numerical parser function. Encapsulates the prototyp for numerical parser functions. The class stores the number of arguments for parser functions as well as additional flags indication the function is non optimizeable. - The pointer to the callback function pointer is stored as void* + The pointer to the callback function pointer is stored as void* and needs to be casted according to the argument count. Negative argument counts indicate a parser function with a variable number - of arguments. + of arguments. \author (C) 2004-2011 Ingo Berg */ class ParserCallback { public: - ParserCallback(fun_type0 a_pFun, bool a_bAllowOpti); - ParserCallback(fun_type1 a_pFun, bool a_bAllowOpti, int a_iPrec = -1, ECmdCode a_iCode=cmFUNC); - ParserCallback(fun_type2 a_pFun, bool a_bAllowOpti, int a_iPrec, EOprtAssociativity a_eAssociativity); - ParserCallback(fun_type2 a_pFun, bool a_bAllowOpti); - ParserCallback(fun_type3 a_pFun, bool a_bAllowOpti); - ParserCallback(fun_type4 a_pFun, bool a_bAllowOpti); - ParserCallback(fun_type5 a_pFun, bool a_bAllowOpti); - ParserCallback(fun_type6 a_pFun, bool a_bAllowOpti); - ParserCallback(fun_type7 a_pFun, bool a_bAllowOpti); - ParserCallback(fun_type8 a_pFun, bool a_bAllowOpti); - ParserCallback(fun_type9 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type0 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type1 a_pFun, bool a_bAllowOpti, int a_iPrec = -1, ECmdCode a_iCode = cmFUNC); + ParserCallback(fun_type2 a_pFun, bool a_bAllowOpti, int a_iPrec, EOprtAssociativity a_eAssociativity); + ParserCallback(fun_type2 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type3 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type4 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type5 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type6 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type7 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type8 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type9 a_pFun, bool a_bAllowOpti); ParserCallback(fun_type10 a_pFun, bool a_bAllowOpti); - ParserCallback(bulkfun_type0 a_pFun, bool a_bAllowOpti); - ParserCallback(bulkfun_type1 a_pFun, bool a_bAllowOpti); - ParserCallback(bulkfun_type2 a_pFun, bool a_bAllowOpti); - ParserCallback(bulkfun_type3 a_pFun, bool a_bAllowOpti); - ParserCallback(bulkfun_type4 a_pFun, bool a_bAllowOpti); - ParserCallback(bulkfun_type5 a_pFun, bool a_bAllowOpti); - ParserCallback(bulkfun_type6 a_pFun, bool a_bAllowOpti); - ParserCallback(bulkfun_type7 a_pFun, bool a_bAllowOpti); - ParserCallback(bulkfun_type8 a_pFun, bool a_bAllowOpti); - ParserCallback(bulkfun_type9 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type0 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type1 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type2 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type3 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type4 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type5 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type6 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type7 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type8 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type9 a_pFun, bool a_bAllowOpti); ParserCallback(bulkfun_type10 a_pFun, bool a_bAllowOpti); ParserCallback(multfun_type a_pFun, bool a_bAllowOpti); @@ -80,39 +79,38 @@ ParserCallback(strfun_type2 a_pFun, bool a_bAllowOpti); ParserCallback(strfun_type3 a_pFun, bool a_bAllowOpti); ParserCallback(); - ParserCallback(const ParserCallback &a_Fun); - + ParserCallback(const ParserCallback& a_Fun); + ParserCallback* Clone() const; - bool IsOptimizable() const; + bool IsOptimizable() const; void* GetAddr() const; - ECmdCode GetCode() const; + ECmdCode GetCode() const; ETypeCode GetType() const; - int GetPri() const; + int GetPri() const; EOprtAssociativity GetAssociativity() const; int GetArgc() const; private: - void *m_pFun; ///< Pointer to the callback function, casted to void - + void* m_pFun; ///< Pointer to the callback function, casted to void + /** \brief Number of numeric function arguments - + This number is negative for functions with variable number of arguments. in this cases they represent the actual number of arguments found. */ - int m_iArgc; - int m_iPri; ///< Valid only for binary and infix operators; Operator precedence. - EOprtAssociativity m_eOprtAsct; ///< Operator associativity; Valid only for binary operators - ECmdCode m_iCode; + int m_iArgc; + int m_iPri; ///< Valid only for binary and infix operators; Operator precedence. + EOprtAssociativity m_eOprtAsct; ///< Operator associativity; Valid only for binary operators + ECmdCode m_iCode; ETypeCode m_iType; - bool m_bAllowOpti; ///< Flag indication optimizeability + bool m_bAllowOpti; ///< Flag indication optimizeability }; //------------------------------------------------------------------------------ /** \brief Container for Callback objects. */ -typedef std::map funmap_type; +typedef std::map funmap_type; } // namespace mu #endif - diff -Nru mstflint-4.17.0+1/ext_libs/muparser/muParser.cpp mstflint-4.21.0+1/ext_libs/muparser/muParser.cpp --- mstflint-4.17.0+1/ext_libs/muparser/muParser.cpp 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/muParser.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -1,27 +1,27 @@ -/* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2013 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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. */ #include "muParser.h" #include "muParserTemplateMagic.h" @@ -32,10 +32,10 @@ #include /** \brief Pi (what else?). */ -#define PARSER_CONST_PI 3.141592653589793238462643 +#define PARSER_CONST_PI 3.141592653589793238462643 /** \brief The Eulerian number. */ -#define PARSER_CONST_E 2.718281828459045235360287 +#define PARSER_CONST_E 2.718281828459045235360287 using namespace std; @@ -43,299 +43,344 @@ \brief Implementation of the standard floating point parser. */ - - /** \brief Namespace for mathematical applications. */ namespace mu { +//--------------------------------------------------------------------------- +// Trigonometric function +value_type Parser::Sin(value_type v) +{ + return MathImpl::Sin(v); +} +value_type Parser::Cos(value_type v) +{ + return MathImpl::Cos(v); +} +value_type Parser::Tan(value_type v) +{ + return MathImpl::Tan(v); +} +value_type Parser::ASin(value_type v) +{ + return MathImpl::ASin(v); +} +value_type Parser::ACos(value_type v) +{ + return MathImpl::ACos(v); +} +value_type Parser::ATan(value_type v) +{ + return MathImpl::ATan(v); +} +value_type Parser::ATan2(value_type v1, value_type v2) +{ + return MathImpl::ATan2(v1, v2); +} +value_type Parser::Sinh(value_type v) +{ + return MathImpl::Sinh(v); +} +value_type Parser::Cosh(value_type v) +{ + return MathImpl::Cosh(v); +} +value_type Parser::Tanh(value_type v) +{ + return MathImpl::Tanh(v); +} +value_type Parser::ASinh(value_type v) +{ + return MathImpl::ASinh(v); +} +value_type Parser::ACosh(value_type v) +{ + return MathImpl::ACosh(v); +} +value_type Parser::ATanh(value_type v) +{ + return MathImpl::ATanh(v); +} +//--------------------------------------------------------------------------- +// Logarithm functions - //--------------------------------------------------------------------------- - // Trigonometric function - value_type Parser::Sin(value_type v) { return MathImpl::Sin(v); } - value_type Parser::Cos(value_type v) { return MathImpl::Cos(v); } - value_type Parser::Tan(value_type v) { return MathImpl::Tan(v); } - value_type Parser::ASin(value_type v) { return MathImpl::ASin(v); } - value_type Parser::ACos(value_type v) { return MathImpl::ACos(v); } - value_type Parser::ATan(value_type v) { return MathImpl::ATan(v); } - value_type Parser::ATan2(value_type v1, value_type v2) { return MathImpl::ATan2(v1, v2); } - value_type Parser::Sinh(value_type v) { return MathImpl::Sinh(v); } - value_type Parser::Cosh(value_type v) { return MathImpl::Cosh(v); } - value_type Parser::Tanh(value_type v) { return MathImpl::Tanh(v); } - value_type Parser::ASinh(value_type v) { return MathImpl::ASinh(v); } - value_type Parser::ACosh(value_type v) { return MathImpl::ACosh(v); } - value_type Parser::ATanh(value_type v) { return MathImpl::ATanh(v); } - - //--------------------------------------------------------------------------- - // Logarithm functions - - // Logarithm base 2 - value_type Parser::Log2(value_type v) - { - #ifdef MUP_MATH_EXCEPTIONS - if (v<=0) - throw ParserError(ecDOMAIN_ERROR, _T("Log2")); - #endif - - return MathImpl::Log2(v); - } - - // Logarithm base 10 - value_type Parser::Log10(value_type v) - { - #ifdef MUP_MATH_EXCEPTIONS - if (v<=0) - throw ParserError(ecDOMAIN_ERROR, _T("Log10")); - #endif +// Logarithm base 2 +value_type Parser::Log2(value_type v) +{ +#ifdef MUP_MATH_EXCEPTIONS + if (v <= 0) + throw ParserError(ecDOMAIN_ERROR, _T("Log2")); +#endif - return MathImpl::Log10(v); - } + return MathImpl::Log2(v); +} + +// Logarithm base 10 +value_type Parser::Log10(value_type v) +{ +#ifdef MUP_MATH_EXCEPTIONS + if (v <= 0) + throw ParserError(ecDOMAIN_ERROR, _T("Log10")); +#endif + + return MathImpl::Log10(v); +} // Logarithm base e (natural logarithm) - value_type Parser::Ln(value_type v) - { - #ifdef MUP_MATH_EXCEPTIONS - if (v<=0) - throw ParserError(ecDOMAIN_ERROR, _T("Ln")); - #endif - - return MathImpl::Log(v); - } - - //--------------------------------------------------------------------------- - // misc - value_type Parser::Exp(value_type v) { return MathImpl::Exp(v); } - value_type Parser::Abs(value_type v) { return MathImpl::Abs(v); } - value_type Parser::Sqrt(value_type v) - { - #ifdef MUP_MATH_EXCEPTIONS - if (v<0) - throw ParserError(ecDOMAIN_ERROR, _T("sqrt")); - #endif - - return MathImpl::Sqrt(v); - } - value_type Parser::Rint(value_type v) { return MathImpl::Rint(v); } - value_type Parser::Sign(value_type v) { return MathImpl::Sign(v); } - - //--------------------------------------------------------------------------- - /** \brief Callback for the unary minus operator. - \param v The value to negate - \return -v - */ - value_type Parser::UnaryMinus(value_type v) - { - return -v; - } - - //--------------------------------------------------------------------------- - /** \brief Callback for the unary minus operator. - \param v The value to negate - \return -v - */ - value_type Parser::UnaryPlus(value_type v) - { - return v; - } - - //--------------------------------------------------------------------------- - /** \brief Callback for adding multiple values. - \param [in] a_afArg Vector with the function arguments - \param [in] a_iArgc The size of a_afArg - */ - value_type Parser::Sum(const value_type *a_afArg, int a_iArgc) - { - if (!a_iArgc) - throw exception_type(_T("too few arguments for function sum.")); +value_type Parser::Ln(value_type v) +{ +#ifdef MUP_MATH_EXCEPTIONS + if (v <= 0) + throw ParserError(ecDOMAIN_ERROR, _T("Ln")); +#endif + + return MathImpl::Log(v); +} + +//--------------------------------------------------------------------------- +// misc +value_type Parser::Exp(value_type v) +{ + return MathImpl::Exp(v); +} +value_type Parser::Abs(value_type v) +{ + return MathImpl::Abs(v); +} +value_type Parser::Sqrt(value_type v) +{ +#ifdef MUP_MATH_EXCEPTIONS + if (v < 0) + throw ParserError(ecDOMAIN_ERROR, _T("sqrt")); +#endif + + return MathImpl::Sqrt(v); +} +value_type Parser::Rint(value_type v) +{ + return MathImpl::Rint(v); +} +value_type Parser::Sign(value_type v) +{ + return MathImpl::Sign(v); +} - value_type fRes=0; - for (int i=0; i> fVal; stringstream_type::pos_type iEnd = stream.tellg(); // Position after reading - if (iEnd==(stringstream_type::pos_type)-1) - return 0; + if (iEnd == (stringstream_type::pos_type)-1) + return 0; *a_iPos += (int)iEnd; *a_fVal = fVal; return 1; - } - +} - //--------------------------------------------------------------------------- - /** \brief Constructor. +//--------------------------------------------------------------------------- +/** \brief Constructor. - Call ParserBase class constructor and trigger Function, Operator and Constant initialization. - */ - Parser::Parser() - :ParserBase() - { + Call ParserBase class constructor and trigger Function, Operator and Constant initialization. +*/ +Parser::Parser() : ParserBase() +{ AddValIdent(IsVal); InitCharSets(); InitFun(); InitConst(); InitOprt(); - } +} - //--------------------------------------------------------------------------- - /** \brief Define the character sets. - \sa DefineNameChars, DefineOprtChars, DefineInfixOprtChars - - This function is used for initializing the default character sets that define - the characters to be useable in function and variable names and operators. - */ - void Parser::InitCharSets() - { - DefineNameChars( _T("0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") ); - DefineOprtChars( _T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-*^/?<>=#!$%&|~'_{}") ); - DefineInfixOprtChars( _T("/+-*^?<>=#!$%&|~'_") ); - } - - //--------------------------------------------------------------------------- - /** \brief Initialize the default functions. */ - void Parser::InitFun() - { +//--------------------------------------------------------------------------- +/** \brief Define the character sets. + \sa DefineNameChars, DefineOprtChars, DefineInfixOprtChars + + This function is used for initializing the default character sets that define + the characters to be useable in function and variable names and operators. +*/ +void Parser::InitCharSets() +{ + DefineNameChars(_T("0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")); + DefineOprtChars(_T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-*^/?<>=#!$%&|~'_{}")); + DefineInfixOprtChars(_T("/+-*^?<>=#!$%&|~'_")); +} + +//--------------------------------------------------------------------------- +/** \brief Initialize the default functions. */ +void Parser::InitFun() +{ if (mu::TypeInfo::IsInteger()) { - // When setting MUP_BASETYPE to an integer type - // Place functions for dealing with integer values here - // ... - // ... - // ... + // When setting MUP_BASETYPE to an integer type + // Place functions for dealing with integer values here + // ... + // ... + // ... } else { - // trigonometric functions - DefineFun(_T("sin"), Sin); - DefineFun(_T("cos"), Cos); - DefineFun(_T("tan"), Tan); - // arcus functions - DefineFun(_T("asin"), ASin); - DefineFun(_T("acos"), ACos); - DefineFun(_T("atan"), ATan); - DefineFun(_T("atan2"), ATan2); - // hyperbolic functions - DefineFun(_T("sinh"), Sinh); - DefineFun(_T("cosh"), Cosh); - DefineFun(_T("tanh"), Tanh); - // arcus hyperbolic functions - DefineFun(_T("asinh"), ASinh); - DefineFun(_T("acosh"), ACosh); - DefineFun(_T("atanh"), ATanh); - // Logarithm functions - DefineFun(_T("log2"), Log2); - DefineFun(_T("log10"), Log10); - DefineFun(_T("log"), Ln); - DefineFun(_T("ln"), Ln); - // misc - DefineFun(_T("exp"), Exp); - DefineFun(_T("sqrt"), Sqrt); - DefineFun(_T("sign"), Sign); - DefineFun(_T("rint"), Rint); - DefineFun(_T("abs"), Abs); - // Functions with variable number of arguments - DefineFun(_T("sum"), Sum); - DefineFun(_T("avg"), Avg); - DefineFun(_T("min"), Min); - DefineFun(_T("max"), Max); + // trigonometric functions + DefineFun(_T("sin"), Sin); + DefineFun(_T("cos"), Cos); + DefineFun(_T("tan"), Tan); + // arcus functions + DefineFun(_T("asin"), ASin); + DefineFun(_T("acos"), ACos); + DefineFun(_T("atan"), ATan); + DefineFun(_T("atan2"), ATan2); + // hyperbolic functions + DefineFun(_T("sinh"), Sinh); + DefineFun(_T("cosh"), Cosh); + DefineFun(_T("tanh"), Tanh); + // arcus hyperbolic functions + DefineFun(_T("asinh"), ASinh); + DefineFun(_T("acosh"), ACosh); + DefineFun(_T("atanh"), ATanh); + // Logarithm functions + DefineFun(_T("log2"), Log2); + DefineFun(_T("log10"), Log10); + DefineFun(_T("log"), Ln); + DefineFun(_T("ln"), Ln); + // misc + DefineFun(_T("exp"), Exp); + DefineFun(_T("sqrt"), Sqrt); + DefineFun(_T("sign"), Sign); + DefineFun(_T("rint"), Rint); + DefineFun(_T("abs"), Abs); + // Functions with variable number of arguments + DefineFun(_T("sum"), Sum); + DefineFun(_T("avg"), Avg); + DefineFun(_T("min"), Min); + DefineFun(_T("max"), Max); } - } +} - //--------------------------------------------------------------------------- - /** \brief Initialize constants. - - By default the parser recognizes two constants. Pi ("pi") and the Eulerian - number ("_e"). - */ - void Parser::InitConst() - { +//--------------------------------------------------------------------------- +/** \brief Initialize constants. + + By default the parser recognizes two constants. Pi ("pi") and the Eulerian + number ("_e"). +*/ +void Parser::InitConst() +{ DefineConst(_T("_pi"), (value_type)PARSER_CONST_PI); DefineConst(_T("_e"), (value_type)PARSER_CONST_E); - } +} + +//--------------------------------------------------------------------------- +/** \brief Initialize operators. - //--------------------------------------------------------------------------- - /** \brief Initialize operators. - - By default only the unary minus operator is added. - */ - void Parser::InitOprt() - { + By default only the unary minus operator is added. +*/ +void Parser::InitOprt() +{ DefineInfixOprt(_T("-"), UnaryMinus); DefineInfixOprt(_T("+"), UnaryPlus); - } +} - //--------------------------------------------------------------------------- - void Parser::OnDetectVar(string_type * /*pExpr*/, int & /*nStart*/, int & /*nEnd*/) - { +//--------------------------------------------------------------------------- +void Parser::OnDetectVar(string_type* /*pExpr*/, int& /*nStart*/, int& /*nEnd*/) +{ // this is just sample code to illustrate modifying variable names on the fly. // I'm not sure anyone really needs such a feature... /* @@ -343,7 +388,7 @@ string sVar(pExpr->begin()+nStart, pExpr->begin()+nEnd); string sRepl = std::string("_") + sVar + "_"; - + int nOrigVarEnd = nEnd; cout << "variable detected!\n"; cout << " Expr: " << *pExpr << "\n"; @@ -356,42 +401,41 @@ pExpr->replace(pExpr->begin()+nStart, pExpr->begin()+nOrigVarEnd, sRepl); cout << " New expr: " << *pExpr << "\n"; */ - } +} - //--------------------------------------------------------------------------- - /** \brief Numerically differentiate with regard to a variable. - \param [in] a_Var Pointer to the differentiation variable. - \param [in] a_fPos Position at which the differentiation should take place. - \param [in] a_fEpsilon Epsilon used for the numerical differentiation. - - Numerical differentiation uses a 5 point operator yielding a 4th order - formula. The default value for epsilon is 0.00074 which is - numeric_limits::epsilon() ^ (1/5) as suggested in the muparser - forum: - - http://sourceforge.net/forum/forum.php?thread_id=1994611&forum_id=462843 - */ - value_type Parser::Diff(value_type *a_Var, - value_type a_fPos, - value_type a_fEpsilon) const - { - value_type fRes(0), - fBuf(*a_Var), - f[4] = {0,0,0,0}, - fEpsilon(a_fEpsilon); +//--------------------------------------------------------------------------- +/** \brief Numerically differentiate with regard to a variable. + \param [in] a_Var Pointer to the differentiation variable. + \param [in] a_fPos Position at which the differentiation should take place. + \param [in] a_fEpsilon Epsilon used for the numerical differentiation. + + Numerical differentiation uses a 5 point operator yielding a 4th order + formula. The default value for epsilon is 0.00074 which is + numeric_limits::epsilon() ^ (1/5) as suggested in the muparser + forum: + + http://sourceforge.net/forum/forum.php?thread_id=1994611&forum_id=462843 +*/ +value_type Parser::Diff(value_type* a_Var, value_type a_fPos, value_type a_fEpsilon) const +{ + value_type fRes(0), fBuf(*a_Var), f[4] = {0, 0, 0, 0}, fEpsilon(a_fEpsilon); // Backwards compatible calculation of epsilon inc case the user doesn't provide // his own epsilon - if (fEpsilon==0) - fEpsilon = (a_fPos==0) ? (value_type)1e-10 : (value_type)1e-7 * a_fPos; + if (fEpsilon == 0) + fEpsilon = (a_fPos == 0) ? (value_type)1e-10 : (value_type)1e-7 * a_fPos; - *a_Var = a_fPos+2 * fEpsilon; f[0] = Eval(); - *a_Var = a_fPos+1 * fEpsilon; f[1] = Eval(); - *a_Var = a_fPos-1 * fEpsilon; f[2] = Eval(); - *a_Var = a_fPos-2 * fEpsilon; f[3] = Eval(); + *a_Var = a_fPos + 2 * fEpsilon; + f[0] = Eval(); + *a_Var = a_fPos + 1 * fEpsilon; + f[1] = Eval(); + *a_Var = a_fPos - 1 * fEpsilon; + f[2] = Eval(); + *a_Var = a_fPos - 2 * fEpsilon; + f[3] = Eval(); *a_Var = fBuf; // restore variable - fRes = (-f[0] + 8*f[1] - 8*f[2] + f[3]) / (12*fEpsilon); + fRes = (-f[0] + 8 * f[1] - 8 * f[2] + f[3]) / (12 * fEpsilon); return fRes; - } +} } // namespace mu diff -Nru mstflint-4.17.0+1/ext_libs/muparser/muParserDef.h mstflint-4.21.0+1/ext_libs/muparser/muParserDef.h --- mstflint-4.17.0+1/ext_libs/muparser/muParserDef.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/muParserDef.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ /* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2014 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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 MUP_DEF_H #define MUP_DEF_H @@ -51,128 +51,125 @@ */ #define MUP_BASETYPE double -/** \brief Activate this option in order to compile with OpenMP support. +/** \brief Activate this option in order to compile with OpenMP support. - OpenMP is used only in the bulk mode it may increase the performance a bit. + OpenMP is used only in the bulk mode it may increase the performance a bit. */ //#define MUP_USE_OPENMP #if defined(_UNICODE) - /** \brief Definition of the basic parser string type. */ - #define MUP_STRING_TYPE std::wstring +/** \brief Definition of the basic parser string type. */ +#define MUP_STRING_TYPE std::wstring - #if !defined(_T) - #define _T(x) L##x - #endif // not defined _T +#if !defined(_T) +#define _T(x) L##x +#endif // not defined _T #else - #ifndef _T - #define _T(x) x - #endif - - /** \brief Definition of the basic parser string type. */ - #define MUP_STRING_TYPE std::string +#ifndef _T +#define _T(x) x +#endif + +/** \brief Definition of the basic parser string type. */ +#define MUP_STRING_TYPE std::string #endif #if defined(_DEBUG) - /** \brief Debug macro to force an abortion of the programm with a certain message. - */ - #define MUP_FAIL(MSG) \ - { \ - bool MSG=false; \ - assert(MSG); \ - } - - /** \brief An assertion that does not kill the program. - - This macro is neutralised in UNICODE builds. It's - too difficult to translate. - */ - #define MUP_ASSERT(COND) \ - if (!(COND)) \ - { \ - stringstream_type ss; \ - ss << _T("Assertion \"") _T(#COND) _T("\" failed: ") \ - << __FILE__ << _T(" line ") \ - << __LINE__ << _T("."); \ - throw ParserError( ss.str() ); \ - } +/** \brief Debug macro to force an abortion of the programm with a certain message. + */ +#define MUP_FAIL(MSG) \ + { \ + bool MSG = false; \ + assert(MSG); \ + } + +/** \brief An assertion that does not kill the program. + + This macro is neutralised in UNICODE builds. It's + too difficult to translate. +*/ +#define MUP_ASSERT(COND) \ + if (!(COND)) \ + { \ + stringstream_type ss; \ + ss << _T("Assertion \"") _T(#COND) _T("\" failed: ") << __FILE__ << _T(" line ") << __LINE__ << _T("."); \ + throw ParserError(ss.str()); \ + } #else - #define MUP_FAIL(MSG) - #define MUP_ASSERT(COND) +#define MUP_FAIL(MSG) +#define MUP_ASSERT(COND) #endif - namespace mu { #if defined(_UNICODE) - //------------------------------------------------------------------------------ - /** \brief Encapsulate wcout. */ - inline std::wostream& console() - { +//------------------------------------------------------------------------------ +/** \brief Encapsulate wcout. */ +inline std::wostream& console() +{ return std::wcout; - } +} - /** \brief Encapsulate cin. */ - inline std::wistream& console_in() - { +/** \brief Encapsulate cin. */ +inline std::wistream& console_in() +{ return std::wcin; - } +} #else - /** \brief Encapsulate cout. - - Used for supporting UNICODE more easily. - */ - inline std::ostream& console() - { +/** \brief Encapsulate cout. + + Used for supporting UNICODE more easily. +*/ +inline std::ostream& console() +{ return std::cout; - } +} - /** \brief Encapsulate cin. +/** \brief Encapsulate cin. - Used for supporting UNICODE more easily. - */ - inline std::istream& console_in() - { + Used for supporting UNICODE more easily. +*/ +inline std::istream& console_in() +{ return std::cin; - } +} #endif - //------------------------------------------------------------------------------ - /** \brief Bytecode values. +//------------------------------------------------------------------------------ +/** \brief Bytecode values. - \attention The order of the operator entries must match the order in ParserBase::c_DefaultOprt! - */ - enum ECmdCode - { + \attention The order of the operator entries must match the order in ParserBase::c_DefaultOprt! +*/ +enum ECmdCode +{ // The following are codes for built in binary operators // apart from built in operators the user has the opportunity to // add user defined operators. - cmLE = 0, ///< Operator item: less or equal - cmGE = 1, ///< Operator item: greater or equal - cmNEQ = 2, ///< Operator item: not equal - cmEQ = 3, ///< Operator item: equals - cmLT = 4, ///< Operator item: less than - cmGT = 5, ///< Operator item: greater than - cmADD = 6, ///< Operator item: add - cmSUB = 7, ///< Operator item: subtract - cmMUL = 8, ///< Operator item: multiply - cmDIV = 9, ///< Operator item: division - cmPOW = 10, ///< Operator item: y to the power of ... - cmLAND = 11, - cmLOR = 12, - cmASSIGN = 13, ///< Operator item: Assignment operator - cmBO = 14, ///< Operator item: opening bracket - cmBC = 15, ///< Operator item: closing bracket - cmIF = 16, ///< For use in the ternary if-then-else operator - cmELSE = 17, ///< For use in the ternary if-then-else operator - cmENDIF = 18, ///< For use in the ternary if-then-else operator - cmARG_SEP = 19, ///< function argument separator - cmVAR = 20, ///< variable item - cmVAL = 21, ///< value item + cmLE = 0, ///< Operator item: less or equal + cmGE = 1, ///< Operator item: greater or equal + cmNEQ = 2, ///< Operator item: not equal + cmEQ = 3, ///< Operator item: equals + cmLT = 4, ///< Operator item: less than + cmGT = 5, ///< Operator item: greater than + cmADD = 6, ///< Operator item: add + cmSUB = 7, ///< Operator item: subtract + cmMUL = 8, ///< Operator item: multiply + cmDIV = 9, ///< Operator item: division + cmPOW = 10, ///< Operator item: y to the power of ... + cmLAND = 11, + cmLOR = 12, + cmASSIGN = 13, ///< Operator item: Assignment operator + cmBO = 14, ///< Operator item: opening bracket + cmBC = 15, ///< Operator item: closing bracket + cmIF = 16, ///< For use in the ternary if-then-else operator + cmELSE = 17, ///< For use in the ternary if-then-else operator + cmENDIF = 18, ///< For use in the ternary if-then-else operator + cmARG_SEP = 19, ///< function argument separator + cmVAR = 20, ///< variable item + cmVAL = 21, ///< value item // For optimization purposes cmVARPOW2, @@ -182,187 +179,226 @@ cmPOW2, // operators and functions - cmFUNC, ///< Code for a generic function item - cmFUNC_STR, ///< Code for a function with a string parameter - cmFUNC_BULK, ///< Special callbacks for Bulk mode with an additional parameter for the bulk index - cmSTRING, ///< Code for a string token - cmOPRT_BIN, ///< user defined binary operator - cmOPRT_POSTFIX, ///< code for postfix operators - cmOPRT_INFIX, ///< code for infix operators - cmEND, ///< end of formula - cmUNKNOWN ///< uninitialized item - }; - - //------------------------------------------------------------------------------ - /** \brief Types internally used by the parser. - */ - enum ETypeCode - { - tpSTR = 0, ///< String type (Function arguments and constants only, no string variables) - tpDBL = 1, ///< Floating point variables - tpVOID = 2 ///< Undefined type. - }; - - //------------------------------------------------------------------------------ - enum EParserVersionInfo - { + cmFUNC, ///< Code for a generic function item + cmFUNC_STR, ///< Code for a function with a string parameter + cmFUNC_BULK, ///< Special callbacks for Bulk mode with an additional parameter for the bulk index + cmSTRING, ///< Code for a string token + cmOPRT_BIN, ///< user defined binary operator + cmOPRT_POSTFIX, ///< code for postfix operators + cmOPRT_INFIX, ///< code for infix operators + cmEND, ///< end of formula + cmUNKNOWN ///< uninitialized item +}; + +//------------------------------------------------------------------------------ +/** \brief Types internally used by the parser. + */ +enum ETypeCode +{ + tpSTR = 0, ///< String type (Function arguments and constants only, no string variables) + tpDBL = 1, ///< Floating point variables + tpVOID = 2 ///< Undefined type. +}; + +//------------------------------------------------------------------------------ +enum EParserVersionInfo +{ pviBRIEF, pviFULL - }; +}; - //------------------------------------------------------------------------------ - /** \brief Parser operator precedence values. */ - enum EOprtAssociativity - { - oaLEFT = 0, +//------------------------------------------------------------------------------ +/** \brief Parser operator precedence values. */ +enum EOprtAssociativity +{ + oaLEFT = 0, oaRIGHT = 1, - oaNONE = 2 - }; + oaNONE = 2 +}; - //------------------------------------------------------------------------------ - /** \brief Parser operator precedence values. */ - enum EOprtPrecedence - { +//------------------------------------------------------------------------------ +/** \brief Parser operator precedence values. */ +enum EOprtPrecedence +{ // binary operators - prLOR = 1, - prLAND = 2, - prLOGIC = 3, ///< logic operators - prCMP = 4, ///< comparsion operators - prADD_SUB = 5, ///< addition - prMUL_DIV = 6, ///< multiplication/division - prPOW = 7, ///< power operator priority (highest) + prLOR = 1, + prLAND = 2, + prLOGIC = 3, ///< logic operators + prCMP = 4, ///< comparsion operators + prADD_SUB = 5, ///< addition + prMUL_DIV = 6, ///< multiplication/division + prPOW = 7, ///< power operator priority (highest) // infix operators - prINFIX = 6, ///< Signs have a higher priority than ADD_SUB, but lower than power operator - prPOSTFIX = 6 ///< Postfix operator priority (currently unused) - }; - - //------------------------------------------------------------------------------ - // basic types - - /** \brief The numeric datatype used by the parser. - - Normally this is a floating point type either single or double precision. - */ - typedef MUP_BASETYPE value_type; - - /** \brief The stringtype used by the parser. - - Depends on wether UNICODE is used or not. - */ - typedef MUP_STRING_TYPE string_type; - - /** \brief The character type used by the parser. - - Depends on wether UNICODE is used or not. - */ - typedef string_type::value_type char_type; - - /** \brief Typedef for easily using stringstream that respect the parser stringtype. */ - typedef std::basic_stringstream, - std::allocator > stringstream_type; + prINFIX = 6, ///< Signs have a higher priority than ADD_SUB, but lower than power operator + prPOSTFIX = 6 ///< Postfix operator priority (currently unused) +}; - // Data container types +//------------------------------------------------------------------------------ +// basic types - /** \brief Type used for storing variables. */ - typedef std::map varmap_type; - - /** \brief Type used for storing constants. */ - typedef std::map valmap_type; - - /** \brief Type for assigning a string name to an index in the internal string table. */ - typedef std::map strmap_type; +/** \brief The numeric datatype used by the parser. - // Parser callbacks - - /** \brief Callback type used for functions without arguments. */ - typedef value_type (*generic_fun_type)(); - - /** \brief Callback type used for functions without arguments. */ - typedef value_type (*fun_type0)(); - - /** \brief Callback type used for functions with a single arguments. */ - typedef value_type (*fun_type1)(value_type); - - /** \brief Callback type used for functions with two arguments. */ - typedef value_type (*fun_type2)(value_type, value_type); - - /** \brief Callback type used for functions with three arguments. */ - typedef value_type (*fun_type3)(value_type, value_type, value_type); - - /** \brief Callback type used for functions with four arguments. */ - typedef value_type (*fun_type4)(value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*fun_type5)(value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*fun_type6)(value_type, value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*fun_type7)(value_type, value_type, value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*fun_type8)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*fun_type9)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*fun_type10)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions without arguments. */ - typedef value_type (*bulkfun_type0)(int, int); - - /** \brief Callback type used for functions with a single arguments. */ - typedef value_type (*bulkfun_type1)(int, int, value_type); - - /** \brief Callback type used for functions with two arguments. */ - typedef value_type (*bulkfun_type2)(int, int, value_type, value_type); - - /** \brief Callback type used for functions with three arguments. */ - typedef value_type (*bulkfun_type3)(int, int, value_type, value_type, value_type); - - /** \brief Callback type used for functions with four arguments. */ - typedef value_type (*bulkfun_type4)(int, int, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*bulkfun_type5)(int, int, value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*bulkfun_type6)(int, int, value_type, value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*bulkfun_type7)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*bulkfun_type8)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*bulkfun_type9)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + Normally this is a floating point type either single or double precision. +*/ +typedef MUP_BASETYPE value_type; - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*bulkfun_type10)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); +/** \brief The stringtype used by the parser. - /** \brief Callback type used for functions with a variable argument list. */ - typedef value_type (*multfun_type)(const value_type*, int); + Depends on wether UNICODE is used or not. +*/ +typedef MUP_STRING_TYPE string_type; - /** \brief Callback type used for functions taking a string as an argument. */ - typedef value_type (*strfun_type1)(const char_type*); +/** \brief The character type used by the parser. - /** \brief Callback type used for functions taking a string and a value as arguments. */ - typedef value_type (*strfun_type2)(const char_type*, value_type); + Depends on wether UNICODE is used or not. +*/ +typedef string_type::value_type char_type; - /** \brief Callback type used for functions taking a string and two values as arguments. */ - typedef value_type (*strfun_type3)(const char_type*, value_type, value_type); +/** \brief Typedef for easily using stringstream that respect the parser stringtype. */ +typedef std::basic_stringstream, std::allocator > stringstream_type; - /** \brief Callback used for functions that identify values in a string. */ - typedef int (*identfun_type)(const char_type *sExpr, int *nPos, value_type *fVal); +// Data container types - /** \brief Callback used for variable creation factory functions. */ - typedef value_type* (*facfun_type)(const char_type*, void*); -} // end of namespace +/** \brief Type used for storing variables. */ +typedef std::map varmap_type; + +/** \brief Type used for storing constants. */ +typedef std::map valmap_type; + +/** \brief Type for assigning a string name to an index in the internal string table. */ +typedef std::map strmap_type; + +// Parser callbacks + +/** \brief Callback type used for functions without arguments. */ +typedef value_type (*generic_fun_type)(); + +/** \brief Callback type used for functions without arguments. */ +typedef value_type (*fun_type0)(); + +/** \brief Callback type used for functions with a single arguments. */ +typedef value_type (*fun_type1)(value_type); + +/** \brief Callback type used for functions with two arguments. */ +typedef value_type (*fun_type2)(value_type, value_type); + +/** \brief Callback type used for functions with three arguments. */ +typedef value_type (*fun_type3)(value_type, value_type, value_type); + +/** \brief Callback type used for functions with four arguments. */ +typedef value_type (*fun_type4)(value_type, value_type, value_type, value_type); + +/** \brief Callback type used for functions with five arguments. */ +typedef value_type (*fun_type5)(value_type, value_type, value_type, value_type, value_type); + +/** \brief Callback type used for functions with five arguments. */ +typedef value_type (*fun_type6)(value_type, value_type, value_type, value_type, value_type, value_type); + +/** \brief Callback type used for functions with five arguments. */ +typedef value_type (*fun_type7)(value_type, value_type, value_type, value_type, value_type, value_type, value_type); + +/** \brief Callback type used for functions with five arguments. */ +typedef value_type ( + *fun_type8)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + +/** \brief Callback type used for functions with five arguments. */ +typedef value_type ( + *fun_type9)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + +/** \brief Callback type used for functions with five arguments. */ +typedef value_type (*fun_type10)(value_type, + value_type, + value_type, + value_type, + value_type, + value_type, + value_type, + value_type, + value_type, + value_type); + +/** \brief Callback type used for functions without arguments. */ +typedef value_type (*bulkfun_type0)(int, int); + +/** \brief Callback type used for functions with a single arguments. */ +typedef value_type (*bulkfun_type1)(int, int, value_type); + +/** \brief Callback type used for functions with two arguments. */ +typedef value_type (*bulkfun_type2)(int, int, value_type, value_type); + +/** \brief Callback type used for functions with three arguments. */ +typedef value_type (*bulkfun_type3)(int, int, value_type, value_type, value_type); + +/** \brief Callback type used for functions with four arguments. */ +typedef value_type (*bulkfun_type4)(int, int, value_type, value_type, value_type, value_type); + +/** \brief Callback type used for functions with five arguments. */ +typedef value_type (*bulkfun_type5)(int, int, value_type, value_type, value_type, value_type, value_type); + +/** \brief Callback type used for functions with five arguments. */ +typedef value_type (*bulkfun_type6)(int, int, value_type, value_type, value_type, value_type, value_type, value_type); + +/** \brief Callback type used for functions with five arguments. */ +typedef value_type ( + *bulkfun_type7)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + +/** \brief Callback type used for functions with five arguments. */ +typedef value_type (*bulkfun_type8)(int, + int, + value_type, + value_type, + value_type, + value_type, + value_type, + value_type, + value_type, + value_type); + +/** \brief Callback type used for functions with five arguments. */ +typedef value_type (*bulkfun_type9)(int, + int, + value_type, + value_type, + value_type, + value_type, + value_type, + value_type, + value_type, + value_type, + value_type); + +/** \brief Callback type used for functions with five arguments. */ +typedef value_type (*bulkfun_type10)(int, + int, + value_type, + value_type, + value_type, + value_type, + value_type, + value_type, + value_type, + value_type, + value_type, + value_type); + +/** \brief Callback type used for functions with a variable argument list. */ +typedef value_type (*multfun_type)(const value_type*, int); + +/** \brief Callback type used for functions taking a string as an argument. */ +typedef value_type (*strfun_type1)(const char_type*); + +/** \brief Callback type used for functions taking a string and a value as arguments. */ +typedef value_type (*strfun_type2)(const char_type*, value_type); + +/** \brief Callback type used for functions taking a string and two values as arguments. */ +typedef value_type (*strfun_type3)(const char_type*, value_type, value_type); + +/** \brief Callback used for functions that identify values in a string. */ +typedef int (*identfun_type)(const char_type* sExpr, int* nPos, value_type* fVal); + +/** \brief Callback used for variable creation factory functions. */ +typedef value_type* (*facfun_type)(const char_type*, void*); +} // namespace mu #endif - diff -Nru mstflint-4.17.0+1/ext_libs/muparser/muParserDLL.cpp mstflint-4.21.0+1/ext_libs/muparser/muParserDLL.cpp --- mstflint-4.17.0+1/ext_libs/muparser/muParserDLL.cpp 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/muParserDLL.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -22,7 +22,7 @@ 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(MUPARSER_DLL) +#if defined(MUPARSER_DLL) #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN @@ -34,29 +34,27 @@ #include "muParserInt.h" #include "muParserError.h" - -#define MU_TRY \ - try \ - { - -#define MU_CATCH \ - } \ - catch (muError_t &e) \ - { \ - ParserTag *pTag = static_cast(a_hParser); \ - pTag->exc = e; \ - pTag->bError = true; \ -if (pTag->errHandler) \ - (pTag->errHandler)(a_hParser); \ - } \ - catch (...) \ - { \ - ParserTag *pTag = static_cast(a_hParser); \ - pTag->exc = muError_t(mu::ecINTERNAL_ERROR); \ - pTag->bError = true; \ -if (pTag->errHandler) \ - (pTag->errHandler)(a_hParser); \ - } +#define MU_TRY \ + try \ + { +#define MU_CATCH \ + } \ + catch (muError_t & e) \ + { \ + ParserTag* pTag = static_cast(a_hParser); \ + pTag->exc = e; \ + pTag->bError = true; \ + if (pTag->errHandler) \ + (pTag->errHandler)(a_hParser); \ + } \ + catch (...) \ + { \ + ParserTag* pTag = static_cast(a_hParser); \ + pTag->exc = muError_t(mu::ecINTERNAL_ERROR); \ + pTag->bError = true; \ + if (pTag->errHandler) \ + (pTag->errHandler)(a_hParser); \ + } /** \file \brief This file contains the implementation of the DLL interface of muparser. @@ -73,28 +71,26 @@ class ParserTag { public: - ParserTag(int nType) - :pParser((nType == muBASETYPE_FLOAT) ? (mu::ParserBase*)new mu::Parser() : - (nType == muBASETYPE_INT) ? (mu::ParserBase*)new mu::ParserInt() : NULL) - , exc() - , errHandler(NULL) - , bError(false) - , m_nParserType(nType) - {} - - ~ParserTag() + ParserTag(int nType) : + pParser((nType == muBASETYPE_FLOAT) ? (mu::ParserBase*)new mu::Parser() : + (nType == muBASETYPE_INT) ? (mu::ParserBase*)new mu::ParserInt() : NULL), + exc(), + errHandler(NULL), + bError(false), + m_nParserType(nType) { - delete pParser; } - mu::ParserBase *pParser; + ~ParserTag() { delete pParser; } + + mu::ParserBase* pParser; mu::ParserBase::exception_type exc; muErrorHandler_t errHandler; bool bError; private: - ParserTag(const ParserTag &ref); - ParserTag& operator=(const ParserTag &ref); + ParserTag(const ParserTag& ref); + ParserTag& operator=(const ParserTag& ref); int m_nParserType; }; @@ -125,19 +121,17 @@ #if defined(_WIN32) #define _CRT_SECURE_NO_DEPRECATE -BOOL APIENTRY DllMain(HANDLE /*hModule*/, - DWORD ul_reason_for_call, - LPVOID /*lpReserved*/) +BOOL APIENTRY DllMain(HANDLE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/) { switch (ul_reason_for_call) { - case DLL_PROCESS_ATTACH: - break; + case DLL_PROCESS_ATTACH: + break; - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; } return TRUE; @@ -153,34 +147,37 @@ // //--------------------------------------------------------------------------- -API_EXPORT(void) mupSetVarFactory(muParserHandle_t a_hParser, muFacFun_t a_pFactory, void *pUserData) +API_EXPORT(void) mupSetVarFactory(muParserHandle_t a_hParser, muFacFun_t a_pFactory, void* pUserData) { MU_TRY - muParser_t* p(AsParser(a_hParser)); + muParser_t* p(AsParser(a_hParser)); p->SetVarFactory(a_pFactory, pUserData); MU_CATCH } //--------------------------------------------------------------------------- /** \brief Create a new Parser instance and return its handle. -*/ + */ API_EXPORT(muParserHandle_t) mupCreate(int nBaseType) { switch (nBaseType) { - case muBASETYPE_FLOAT: return (void*)(new ParserTag(muBASETYPE_FLOAT)); - case muBASETYPE_INT: return (void*)(new ParserTag(muBASETYPE_INT)); - default: return NULL; + case muBASETYPE_FLOAT: + return (void*)(new ParserTag(muBASETYPE_FLOAT)); + case muBASETYPE_INT: + return (void*)(new ParserTag(muBASETYPE_INT)); + default: + return NULL; } } //--------------------------------------------------------------------------- /** \brief Release the parser instance related with a parser handle. -*/ + */ API_EXPORT(void) mupRelease(muParserHandle_t a_hParser) { MU_TRY - ParserTag* p = static_cast(a_hParser); + ParserTag* p = static_cast(a_hParser); delete p; MU_CATCH } @@ -189,7 +186,7 @@ API_EXPORT(const muChar_t*) mupGetVersion(muParserHandle_t a_hParser) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); #ifndef _UNICODE sprintf(s_tmpOutBuf, "%s", p->GetVersion().c_str()); @@ -200,40 +197,40 @@ return s_tmpOutBuf; MU_CATCH - return _T(""); + return _T(""); } //--------------------------------------------------------------------------- /** \brief Evaluate the expression. -*/ + */ API_EXPORT(muFloat_t) mupEval(muParserHandle_t a_hParser) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); return p->Eval(); MU_CATCH - return 0; + return 0; } //--------------------------------------------------------------------------- -API_EXPORT(muFloat_t*) mupEvalMulti(muParserHandle_t a_hParser, int *nNum) +API_EXPORT(muFloat_t*) mupEvalMulti(muParserHandle_t a_hParser, int* nNum) { MU_TRY - assert(nNum != NULL); + assert(nNum != NULL); muParser_t* const p(AsParser(a_hParser)); return p->Eval(*nNum); MU_CATCH - return 0; + return 0; } //--------------------------------------------------------------------------- -API_EXPORT(void) mupEvalBulk(muParserHandle_t a_hParser, muFloat_t *a_res, int nSize) +API_EXPORT(void) mupEvalBulk(muParserHandle_t a_hParser, muFloat_t* a_res, int nSize) { MU_TRY - muParser_t* p(AsParser(a_hParser)); + muParser_t* p(AsParser(a_hParser)); p->Eval(a_res, nSize); MU_CATCH } @@ -242,7 +239,7 @@ API_EXPORT(void) mupSetExpr(muParserHandle_t a_hParser, const muChar_t* a_szExpr) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->SetExpr(a_szExpr); MU_CATCH } @@ -251,7 +248,7 @@ API_EXPORT(void) mupRemoveVar(muParserHandle_t a_hParser, const muChar_t* a_szName) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->RemoveVar(a_szName); MU_CATCH } @@ -263,7 +260,7 @@ API_EXPORT(void) mupClearVar(muParserHandle_t a_hParser) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->ClearVar(); MU_CATCH } @@ -275,7 +272,7 @@ API_EXPORT(void) mupClearConst(muParserHandle_t a_hParser) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->ClearConst(); MU_CATCH } @@ -287,7 +284,7 @@ API_EXPORT(void) mupClearOprt(muParserHandle_t a_hParser) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->ClearOprt(); MU_CATCH } @@ -296,367 +293,304 @@ API_EXPORT(void) mupClearFun(muParserHandle_t a_hParser) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->ClearFun(); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineFun0(muParserHandle_t a_hParser, - const muChar_t* a_szName, - muFun0_t a_pFun, - muBool_t a_bAllowOpt) +API_EXPORT(void) +mupDefineFun0(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun0_t a_pFun, muBool_t a_bAllowOpt) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineFun1(muParserHandle_t a_hParser, - const muChar_t* a_szName, - muFun1_t a_pFun, - muBool_t a_bAllowOpt) +API_EXPORT(void) +mupDefineFun1(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun1_t a_pFun, muBool_t a_bAllowOpt) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineFun2(muParserHandle_t a_hParser, - const muChar_t* a_szName, - muFun2_t a_pFun, - muBool_t a_bAllowOpt) +API_EXPORT(void) +mupDefineFun2(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun2_t a_pFun, muBool_t a_bAllowOpt) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineFun3(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muFun3_t a_pFun, - muBool_t a_bAllowOpt) +API_EXPORT(void) +mupDefineFun3(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun3_t a_pFun, muBool_t a_bAllowOpt) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineFun4(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muFun4_t a_pFun, - muBool_t a_bAllowOpt) +API_EXPORT(void) +mupDefineFun4(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun4_t a_pFun, muBool_t a_bAllowOpt) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineFun5(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muFun5_t a_pFun, - muBool_t a_bAllowOpt) +API_EXPORT(void) +mupDefineFun5(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun5_t a_pFun, muBool_t a_bAllowOpt) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineFun6(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muFun6_t a_pFun, - muBool_t a_bAllowOpt) +API_EXPORT(void) +mupDefineFun6(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun6_t a_pFun, muBool_t a_bAllowOpt) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineFun7(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muFun7_t a_pFun, - muBool_t a_bAllowOpt) +API_EXPORT(void) +mupDefineFun7(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun7_t a_pFun, muBool_t a_bAllowOpt) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineFun8(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muFun8_t a_pFun, - muBool_t a_bAllowOpt) +API_EXPORT(void) +mupDefineFun8(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun8_t a_pFun, muBool_t a_bAllowOpt) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineFun9(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muFun9_t a_pFun, - muBool_t a_bAllowOpt) +API_EXPORT(void) +mupDefineFun9(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun9_t a_pFun, muBool_t a_bAllowOpt) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineFun10(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muFun10_t a_pFun, - muBool_t a_bAllowOpt) +API_EXPORT(void) +mupDefineFun10(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun10_t a_pFun, muBool_t a_bAllowOpt) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineBulkFun0(muParserHandle_t a_hParser, - const muChar_t* a_szName, - muBulkFun0_t a_pFun) +API_EXPORT(void) mupDefineBulkFun0(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun0_t a_pFun) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, false); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineBulkFun1(muParserHandle_t a_hParser, - const muChar_t* a_szName, - muBulkFun1_t a_pFun) +API_EXPORT(void) mupDefineBulkFun1(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun1_t a_pFun) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, false); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineBulkFun2(muParserHandle_t a_hParser, - const muChar_t* a_szName, - muBulkFun2_t a_pFun) +API_EXPORT(void) mupDefineBulkFun2(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun2_t a_pFun) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, false); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineBulkFun3(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muBulkFun3_t a_pFun) +API_EXPORT(void) mupDefineBulkFun3(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun3_t a_pFun) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, false); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineBulkFun4(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muBulkFun4_t a_pFun) +API_EXPORT(void) mupDefineBulkFun4(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun4_t a_pFun) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, false); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineBulkFun5(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muBulkFun5_t a_pFun) +API_EXPORT(void) mupDefineBulkFun5(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun5_t a_pFun) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, false); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineBulkFun6(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muBulkFun6_t a_pFun) +API_EXPORT(void) mupDefineBulkFun6(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun6_t a_pFun) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, false); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineBulkFun7(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muBulkFun7_t a_pFun) +API_EXPORT(void) mupDefineBulkFun7(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun7_t a_pFun) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, false); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineBulkFun8(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muBulkFun8_t a_pFun) +API_EXPORT(void) mupDefineBulkFun8(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun8_t a_pFun) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, false); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineBulkFun9(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muBulkFun9_t a_pFun) +API_EXPORT(void) mupDefineBulkFun9(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun9_t a_pFun) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, false); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineBulkFun10(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muBulkFun10_t a_pFun) +API_EXPORT(void) mupDefineBulkFun10(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun10_t a_pFun) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, false); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineStrFun1(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muStrFun1_t a_pFun) +API_EXPORT(void) mupDefineStrFun1(muParserHandle_t a_hParser, const muChar_t* a_szName, muStrFun1_t a_pFun) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, false); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineStrFun2(muParserHandle_t a_hParser, - const muChar_t* a_szName, - muStrFun2_t a_pFun) +API_EXPORT(void) mupDefineStrFun2(muParserHandle_t a_hParser, const muChar_t* a_szName, muStrFun2_t a_pFun) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, false); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineStrFun3(muParserHandle_t a_hParser, - const muChar_t* a_szName, - muStrFun3_t a_pFun) +API_EXPORT(void) mupDefineStrFun3(muParserHandle_t a_hParser, const muChar_t* a_szName, muStrFun3_t a_pFun) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, false); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineMultFun(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muMultFun_t a_pFun, - muBool_t a_bAllowOpt) +API_EXPORT(void) +mupDefineMultFun(muParserHandle_t a_hParser, const muChar_t* a_szName, muMultFun_t a_pFun, muBool_t a_bAllowOpt) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineFun(a_szName, a_pFun, a_bAllowOpt != 0); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineOprt(muParserHandle_t a_hParser, - const muChar_t* a_szName, - muFun2_t a_pFun, - muInt_t a_nPrec, - muInt_t a_nOprtAsct, - muBool_t a_bAllowOpt) +API_EXPORT(void) +mupDefineOprt(muParserHandle_t a_hParser, + const muChar_t* a_szName, + muFun2_t a_pFun, + muInt_t a_nPrec, + muInt_t a_nOprtAsct, + muBool_t a_bAllowOpt) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); - p->DefineOprt(a_szName, - a_pFun, - a_nPrec, - (mu::EOprtAssociativity)a_nOprtAsct, - a_bAllowOpt != 0); + muParser_t* const p(AsParser(a_hParser)); + p->DefineOprt(a_szName, a_pFun, a_nPrec, (mu::EOprtAssociativity)a_nOprtAsct, a_bAllowOpt != 0); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineVar(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muFloat_t *a_pVar) +API_EXPORT(void) mupDefineVar(muParserHandle_t a_hParser, const muChar_t* a_szName, muFloat_t* a_pVar) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineVar(a_szName, a_pVar); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineBulkVar(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muFloat_t *a_pVar) +API_EXPORT(void) mupDefineBulkVar(muParserHandle_t a_hParser, const muChar_t* a_szName, muFloat_t* a_pVar) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineVar(a_szName, a_pVar); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineConst(muParserHandle_t a_hParser, - const muChar_t *a_szName, - muFloat_t a_fVal) +API_EXPORT(void) mupDefineConst(muParserHandle_t a_hParser, const muChar_t* a_szName, muFloat_t a_fVal) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineConst(a_szName, a_fVal); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineStrConst(muParserHandle_t a_hParser, - const muChar_t *a_szName, - const muChar_t *a_szVal) +API_EXPORT(void) mupDefineStrConst(muParserHandle_t a_hParser, const muChar_t* a_szName, const muChar_t* a_szVal) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineStrConst(a_szName, a_szVal); MU_CATCH } @@ -665,7 +599,7 @@ API_EXPORT(const muChar_t*) mupGetExpr(muParserHandle_t a_hParser) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); // C# explodes when pMsg is returned directly. For some reason it can't access // the memory where the message lies directly. @@ -679,53 +613,46 @@ MU_CATCH - return _T(""); + return _T(""); } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefinePostfixOprt(muParserHandle_t a_hParser, - const muChar_t* a_szName, - muFun1_t a_pOprt, - muBool_t a_bAllowOpt) +API_EXPORT(void) +mupDefinePostfixOprt(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun1_t a_pOprt, muBool_t a_bAllowOpt) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefinePostfixOprt(a_szName, a_pOprt, a_bAllowOpt != 0); MU_CATCH } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineInfixOprt(muParserHandle_t a_hParser, - const muChar_t* a_szName, - muFun1_t a_pOprt, - muBool_t a_bAllowOpt) +API_EXPORT(void) +mupDefineInfixOprt(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun1_t a_pOprt, muBool_t a_bAllowOpt) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->DefineInfixOprt(a_szName, a_pOprt, a_bAllowOpt != 0); MU_CATCH } // Define character sets for identifiers //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineNameChars(muParserHandle_t a_hParser, - const muChar_t* a_szCharset) +API_EXPORT(void) mupDefineNameChars(muParserHandle_t a_hParser, const muChar_t* a_szCharset) { muParser_t* const p(AsParser(a_hParser)); p->DefineNameChars(a_szCharset); } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineOprtChars(muParserHandle_t a_hParser, - const muChar_t* a_szCharset) +API_EXPORT(void) mupDefineOprtChars(muParserHandle_t a_hParser, const muChar_t* a_szCharset) { muParser_t* const p(AsParser(a_hParser)); p->DefineOprtChars(a_szCharset); } //--------------------------------------------------------------------------- -API_EXPORT(void) mupDefineInfixOprtChars(muParserHandle_t a_hParser, - const muChar_t *a_szCharset) +API_EXPORT(void) mupDefineInfixOprtChars(muParserHandle_t a_hParser, const muChar_t* a_szCharset) { muParser_t* const p(AsParser(a_hParser)); p->DefineInfixOprtChars(a_szCharset); @@ -740,12 +667,12 @@ API_EXPORT(int) mupGetVarNum(muParserHandle_t a_hParser) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); const mu::varmap_type VarMap = p->GetVar(); return (int)VarMap.size(); MU_CATCH - return 0; // never reached + return 0; // never reached } //--------------------------------------------------------------------------- @@ -764,17 +691,14 @@ During the calculation user defined callback functions present in the expression will be called, this is unavoidable. */ -API_EXPORT(void) mupGetVar(muParserHandle_t a_hParser, - unsigned a_iVar, - const muChar_t **a_szName, - muFloat_t **a_pVar) +API_EXPORT(void) mupGetVar(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_szName, muFloat_t** a_pVar) { // A static buffer is needed for the name since i cant return the // pointer from the map. - static muChar_t szName[1024]; + static muChar_t szName[1024]; MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); const mu::varmap_type VarMap = p->GetVar(); if (a_iVar >= VarMap.size()) @@ -795,7 +719,7 @@ wcsncpy(szName, item->first.c_str(), sizeof(szName)); #endif - szName[sizeof(szName)-1] = 0; + szName[sizeof(szName) - 1] = 0; *a_szName = &szName[0]; *a_pVar = item->second; @@ -803,7 +727,7 @@ MU_CATCH - *a_szName = 0; + *a_szName = 0; *a_pVar = 0; } @@ -816,12 +740,12 @@ API_EXPORT(int) mupGetExprVarNum(muParserHandle_t a_hParser) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); const mu::varmap_type VarMap = p->GetUsedVar(); return (int)VarMap.size(); MU_CATCH - return 0; // never reached + return 0; // never reached } //--------------------------------------------------------------------------- @@ -841,17 +765,15 @@ \param a_pVar [out] Pointer to the variable. \throw nothrow */ -API_EXPORT(void) mupGetExprVar(muParserHandle_t a_hParser, - unsigned a_iVar, - const muChar_t **a_szName, - muFloat_t **a_pVar) +API_EXPORT(void) +mupGetExprVar(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_szName, muFloat_t** a_pVar) { // A static buffer is needed for the name since i cant return the // pointer from the map. - static muChar_t szName[1024]; + static muChar_t szName[1024]; MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); const mu::varmap_type VarMap = p->GetUsedVar(); if (a_iVar >= VarMap.size()) @@ -872,7 +794,7 @@ wcsncpy(szName, item->first.c_str(), sizeof(szName)); #endif - szName[sizeof(szName)-1] = 0; + szName[sizeof(szName) - 1] = 0; *a_szName = &szName[0]; *a_pVar = item->second; @@ -880,7 +802,7 @@ MU_CATCH - *a_szName = 0; + *a_szName = 0; *a_pVar = 0; } @@ -889,19 +811,19 @@ API_EXPORT(int) mupGetConstNum(muParserHandle_t a_hParser) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); const mu::valmap_type ValMap = p->GetConst(); return (int)ValMap.size(); MU_CATCH - return 0; // never reached + return 0; // never reached } //----------------------------------------------------------------------------------------------------- API_EXPORT(void) mupSetArgSep(muParserHandle_t a_hParser, const muChar_t cArgSep) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->SetArgSep(cArgSep); MU_CATCH } @@ -910,7 +832,7 @@ API_EXPORT(void) mupResetLocale(muParserHandle_t a_hParser) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->ResetLocale(); MU_CATCH } @@ -919,7 +841,7 @@ API_EXPORT(void) mupSetDecSep(muParserHandle_t a_hParser, const muChar_t cDecSep) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->SetDecSep(cDecSep); MU_CATCH } @@ -928,7 +850,7 @@ API_EXPORT(void) mupSetThousandsSep(muParserHandle_t a_hParser, const muChar_t cThousandsSep) { MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); p->SetThousandsSep(cThousandsSep); MU_CATCH } @@ -940,17 +862,14 @@ \param a_pszName [out] pointer to a null terminated string with the constant name \param [out] The constant value */ -API_EXPORT(void) mupGetConst(muParserHandle_t a_hParser, - unsigned a_iVar, - const muChar_t **a_pszName, - muFloat_t *a_fVal) +API_EXPORT(void) mupGetConst(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_pszName, muFloat_t* a_fVal) { // A static buffer is needed for the name since i cant return the // pointer from the map. static muChar_t szName[1024]; MU_TRY - muParser_t* const p(AsParser(a_hParser)); + muParser_t* const p(AsParser(a_hParser)); const mu::valmap_type ValMap = p->GetConst(); if (a_iVar >= ValMap.size()) @@ -971,7 +890,7 @@ wcsncpy(szName, item->first.c_str(), sizeof(szName)); #endif - szName[sizeof(szName)-1] = 0; + szName[sizeof(szName) - 1] = 0; *a_pszName = &szName[0]; *a_fVal = item->second; @@ -979,18 +898,17 @@ MU_CATCH - *a_pszName = 0; + *a_pszName = 0; *a_fVal = 0; } //--------------------------------------------------------------------------- /** \brief Add a custom value recognition function. -*/ -API_EXPORT(void) mupAddValIdent(muParserHandle_t a_hParser, - muIdentFun_t a_pFun) + */ +API_EXPORT(void) mupAddValIdent(muParserHandle_t a_hParser, muIdentFun_t a_pFun) { MU_TRY - muParser_t* p(AsParser(a_hParser)); + muParser_t* p(AsParser(a_hParser)); p->AddValIdent(a_pFun); MU_CATCH } @@ -1010,7 +928,7 @@ //--------------------------------------------------------------------------- /** \brief Reset the internal error flag. -*/ + */ API_EXPORT(void) mupErrorReset(muParserHandle_t a_hParser) { AsParserTag(a_hParser)->bError = false; @@ -1024,11 +942,11 @@ //--------------------------------------------------------------------------- /** \brief Return the message associated with the last error. -*/ + */ API_EXPORT(const muChar_t*) mupGetErrorMsg(muParserHandle_t a_hParser) { ParserTag* const p(AsParserTag(a_hParser)); - const muChar_t *pMsg = p->exc.GetMsg().c_str(); + const muChar_t* pMsg = p->exc.GetMsg().c_str(); // C# explodes when pMsg is returned directly. For some reason it can't access // the memory where the message lies directly. @@ -1043,11 +961,11 @@ //--------------------------------------------------------------------------- /** \brief Return the message associated with the last error. -*/ + */ API_EXPORT(const muChar_t*) mupGetErrorToken(muParserHandle_t a_hParser) { ParserTag* const p(AsParserTag(a_hParser)); - const muChar_t *pToken = p->exc.GetToken().c_str(); + const muChar_t* pToken = p->exc.GetToken().c_str(); // C# explodes when pMsg is returned directly. For some reason it can't access // the memory where the message lies directly. @@ -1062,7 +980,7 @@ //--------------------------------------------------------------------------- /** \brief Return the code associated with the last error. -*/ + */ API_EXPORT(int) mupGetErrorCode(muParserHandle_t a_hParser) { return AsParserTag(a_hParser)->exc.GetCode(); @@ -1076,7 +994,7 @@ } ////----------------------------------------------------------------------------------------------------- -//API_EXPORT(const muChar_t*) mupGetErrorExpr(muParserHandle_t a_hParser) +// API_EXPORT(const muChar_t*) mupGetErrorExpr(muParserHandle_t a_hParser) //{ // return AsParserTag(a_hParser)->exc.GetExpr().c_str(); //} @@ -1088,9 +1006,9 @@ } //----------------------------------------------------------------------------------------------------- -API_EXPORT(void) mupReleaseVar(muFloat_t *ptr) +API_EXPORT(void) mupReleaseVar(muFloat_t* ptr) { delete ptr; } -#endif // MUPARSER_DLL +#endif // MUPARSER_DLL diff -Nru mstflint-4.17.0+1/ext_libs/muparser/muParserDLL.h mstflint-4.21.0+1/ext_libs/muparser/muParserDLL.h --- mstflint-4.17.0+1/ext_libs/muparser/muParserDLL.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/muParserDLL.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,238 +1,270 @@ /* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2011 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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 MU_PARSER_DLL_H #define MU_PARSER_DLL_H #if defined(WIN32) || defined(_WIN32) - #ifdef MUPARSERLIB_EXPORTS - #define API_EXPORT(TYPE) __declspec(dllexport) TYPE __cdecl - #else - #define API_EXPORT(TYPE) __declspec(dllimport) TYPE __cdecl - #endif +#ifdef MUPARSERLIB_EXPORTS +#define API_EXPORT(TYPE) __declspec(dllexport) TYPE __cdecl #else - #define API_EXPORT(TYPE) TYPE +#define API_EXPORT(TYPE) __declspec(dllimport) TYPE __cdecl +#endif +#else +#define API_EXPORT(TYPE) TYPE #endif - #ifdef __cplusplus extern "C" { #endif -/** \file - \brief This file contains the DLL interface of muparser. -*/ + /** \file + \brief This file contains the DLL interface of muparser. + */ -// Basic types -typedef void* muParserHandle_t; // parser handle + // Basic types + typedef void* muParserHandle_t; // parser handle #ifndef _UNICODE - typedef char muChar_t; // character type + typedef char muChar_t; // character type #else - typedef wchar_t muChar_t; // character type +typedef wchar_t muChar_t; // character type #endif -typedef int muBool_t; // boolean type -typedef int muInt_t; // integer type -typedef double muFloat_t; // floating point type - -// function types for calculation -typedef muFloat_t (*muFun0_t )(); -typedef muFloat_t (*muFun1_t )(muFloat_t); -typedef muFloat_t (*muFun2_t )(muFloat_t, muFloat_t); -typedef muFloat_t (*muFun3_t )(muFloat_t, muFloat_t, muFloat_t); -typedef muFloat_t (*muFun4_t )(muFloat_t, muFloat_t, muFloat_t, muFloat_t); -typedef muFloat_t (*muFun5_t )(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); -typedef muFloat_t (*muFun6_t )(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); -typedef muFloat_t (*muFun7_t )(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); -typedef muFloat_t (*muFun8_t )(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); -typedef muFloat_t (*muFun9_t )(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); -typedef muFloat_t (*muFun10_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); - -// Function prototypes for bulkmode functions -typedef muFloat_t (*muBulkFun0_t )(int, int); -typedef muFloat_t (*muBulkFun1_t )(int, int, muFloat_t); -typedef muFloat_t (*muBulkFun2_t )(int, int, muFloat_t, muFloat_t); -typedef muFloat_t (*muBulkFun3_t )(int, int, muFloat_t, muFloat_t, muFloat_t); -typedef muFloat_t (*muBulkFun4_t )(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t); -typedef muFloat_t (*muBulkFun5_t )(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); -typedef muFloat_t (*muBulkFun6_t )(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); -typedef muFloat_t (*muBulkFun7_t )(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); -typedef muFloat_t (*muBulkFun8_t )(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); -typedef muFloat_t (*muBulkFun9_t )(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); -typedef muFloat_t (*muBulkFun10_t)(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); - -typedef muFloat_t (*muMultFun_t)(const muFloat_t*, muInt_t); -typedef muFloat_t (*muStrFun1_t)(const muChar_t*); -typedef muFloat_t (*muStrFun2_t)(const muChar_t*, muFloat_t); -typedef muFloat_t (*muStrFun3_t)(const muChar_t*, muFloat_t, muFloat_t); - -// Functions for parser management -typedef void (*muErrorHandler_t)(muParserHandle_t a_hParser); // [optional] callback to an error handler -typedef muFloat_t* (*muFacFun_t)(const muChar_t*, void*); // [optional] callback for creating new variables -typedef muInt_t (*muIdentFun_t)(const muChar_t*, muInt_t*, muFloat_t*); // [optional] value identification callbacks - -//----------------------------------------------------------------------------------------------------- -// Constants -static const int muOPRT_ASCT_LEFT = 0; -static const int muOPRT_ASCT_RIGHT = 1; - -static const int muBASETYPE_FLOAT = 0; -static const int muBASETYPE_INT = 1; - -//----------------------------------------------------------------------------------------------------- -// -// -// muParser C compatible bindings -// -// -//----------------------------------------------------------------------------------------------------- - - -// Basic operations / initialization -API_EXPORT(muParserHandle_t) mupCreate(int nBaseType); -API_EXPORT(void) mupRelease(muParserHandle_t a_hParser); -API_EXPORT(const muChar_t*) mupGetExpr(muParserHandle_t a_hParser); -API_EXPORT(void) mupSetExpr(muParserHandle_t a_hParser, const muChar_t *a_szExpr); -API_EXPORT(void) mupSetVarFactory(muParserHandle_t a_hParser, muFacFun_t a_pFactory, void* pUserData); -API_EXPORT(const muChar_t*) mupGetVersion(muParserHandle_t a_hParser); -API_EXPORT(muFloat_t) mupEval(muParserHandle_t a_hParser); -API_EXPORT(muFloat_t*) mupEvalMulti(muParserHandle_t a_hParser, int *nNum); -API_EXPORT(void) mupEvalBulk(muParserHandle_t a_hParser, muFloat_t *a_fResult, int nSize); - -// Defining callbacks / variables / constants -API_EXPORT(void) mupDefineFun0(muParserHandle_t a_hParser, const muChar_t *a_szName, muFun0_t a_pFun, muBool_t a_bOptimize); -API_EXPORT(void) mupDefineFun1(muParserHandle_t a_hParser, const muChar_t *a_szName, muFun1_t a_pFun, muBool_t a_bOptimize); -API_EXPORT(void) mupDefineFun2(muParserHandle_t a_hParser, const muChar_t *a_szName, muFun2_t a_pFun, muBool_t a_bOptimize); -API_EXPORT(void) mupDefineFun3(muParserHandle_t a_hParser, const muChar_t *a_szName, muFun3_t a_pFun, muBool_t a_bOptimize); -API_EXPORT(void) mupDefineFun4(muParserHandle_t a_hParser, const muChar_t *a_szName, muFun4_t a_pFun, muBool_t a_bOptimize); -API_EXPORT(void) mupDefineFun5(muParserHandle_t a_hParser, const muChar_t *a_szName, muFun5_t a_pFun, muBool_t a_bOptimize); -API_EXPORT(void) mupDefineFun6(muParserHandle_t a_hParser, const muChar_t *a_szName, muFun6_t a_pFun, muBool_t a_bOptimize); -API_EXPORT(void) mupDefineFun7(muParserHandle_t a_hParser, const muChar_t *a_szName, muFun7_t a_pFun, muBool_t a_bOptimize); -API_EXPORT(void) mupDefineFun8(muParserHandle_t a_hParser, const muChar_t *a_szName, muFun8_t a_pFun, muBool_t a_bOptimize); -API_EXPORT(void) mupDefineFun9(muParserHandle_t a_hParser, const muChar_t *a_szName, muFun9_t a_pFun, muBool_t a_bOptimize); -API_EXPORT(void) mupDefineFun10(muParserHandle_t a_hParser, const muChar_t *a_szName, muFun10_t a_pFun, muBool_t a_bOptimize); - -// Defining bulkmode functions -API_EXPORT(void) mupDefineBulkFun0(muParserHandle_t a_hParser, const muChar_t *a_szName, muBulkFun0_t a_pFun); -API_EXPORT(void) mupDefineBulkFun1(muParserHandle_t a_hParser, const muChar_t *a_szName, muBulkFun1_t a_pFun); -API_EXPORT(void) mupDefineBulkFun2(muParserHandle_t a_hParser, const muChar_t *a_szName, muBulkFun2_t a_pFun); -API_EXPORT(void) mupDefineBulkFun3(muParserHandle_t a_hParser, const muChar_t *a_szName, muBulkFun3_t a_pFun); -API_EXPORT(void) mupDefineBulkFun4(muParserHandle_t a_hParser, const muChar_t *a_szName, muBulkFun4_t a_pFun); -API_EXPORT(void) mupDefineBulkFun5(muParserHandle_t a_hParser, const muChar_t *a_szName, muBulkFun5_t a_pFun); -API_EXPORT(void) mupDefineBulkFun6(muParserHandle_t a_hParser, const muChar_t *a_szName, muBulkFun6_t a_pFun); -API_EXPORT(void) mupDefineBulkFun7(muParserHandle_t a_hParser, const muChar_t *a_szName, muBulkFun7_t a_pFun); -API_EXPORT(void) mupDefineBulkFun8(muParserHandle_t a_hParser, const muChar_t *a_szName, muBulkFun8_t a_pFun); -API_EXPORT(void) mupDefineBulkFun9(muParserHandle_t a_hParser, const muChar_t *a_szName, muBulkFun9_t a_pFun); -API_EXPORT(void) mupDefineBulkFun10(muParserHandle_t a_hParser, const muChar_t *a_szName, muBulkFun10_t a_pFun); - -// string functions -API_EXPORT(void) mupDefineStrFun1(muParserHandle_t a_hParser, const muChar_t *a_szName, muStrFun1_t a_pFun); -API_EXPORT(void) mupDefineStrFun2(muParserHandle_t a_hParser, const muChar_t *a_szName, muStrFun2_t a_pFun); -API_EXPORT(void) mupDefineStrFun3(muParserHandle_t a_hParser, const muChar_t *a_szName, muStrFun3_t a_pFun); - -API_EXPORT(void) mupDefineMultFun( muParserHandle_t a_hParser, - const muChar_t* a_szName, - muMultFun_t a_pFun, - muBool_t a_bOptimize); - -API_EXPORT(void) mupDefineOprt( muParserHandle_t a_hParser, - const muChar_t* a_szName, - muFun2_t a_pFun, - muInt_t a_nPrec, - muInt_t a_nOprtAsct, - muBool_t a_bOptimize); - -API_EXPORT(void) mupDefineConst( muParserHandle_t a_hParser, - const muChar_t* a_szName, - muFloat_t a_fVal ); - -API_EXPORT(void) mupDefineStrConst( muParserHandle_t a_hParser, - const muChar_t* a_szName, - const muChar_t *a_sVal ); - -API_EXPORT(void) mupDefineVar( muParserHandle_t a_hParser, - const muChar_t* a_szName, - muFloat_t *a_fVar); - -API_EXPORT(void) mupDefineBulkVar( muParserHandle_t a_hParser, - const muChar_t* a_szName, - muFloat_t *a_fVar); - -API_EXPORT(void) mupDefinePostfixOprt( muParserHandle_t a_hParser, - const muChar_t* a_szName, - muFun1_t a_pOprt, - muBool_t a_bOptimize); - - -API_EXPORT(void) mupDefineInfixOprt( muParserHandle_t a_hParser, - const muChar_t* a_szName, - muFun1_t a_pOprt, - muBool_t a_bOptimize); - -// Define character sets for identifiers -API_EXPORT(void) mupDefineNameChars(muParserHandle_t a_hParser, const muChar_t* a_szCharset); -API_EXPORT(void) mupDefineOprtChars(muParserHandle_t a_hParser, const muChar_t* a_szCharset); -API_EXPORT(void) mupDefineInfixOprtChars(muParserHandle_t a_hParser, const muChar_t* a_szCharset); - -// Remove all / single variables -API_EXPORT(void) mupRemoveVar(muParserHandle_t a_hParser, const muChar_t* a_szName); -API_EXPORT(void) mupClearVar(muParserHandle_t a_hParser); -API_EXPORT(void) mupClearConst(muParserHandle_t a_hParser); -API_EXPORT(void) mupClearOprt(muParserHandle_t a_hParser); -API_EXPORT(void) mupClearFun(muParserHandle_t a_hParser); - -// Querying variables / expression variables / constants -API_EXPORT(int) mupGetExprVarNum(muParserHandle_t a_hParser); -API_EXPORT(int) mupGetVarNum(muParserHandle_t a_hParser); -API_EXPORT(int) mupGetConstNum(muParserHandle_t a_hParser); -API_EXPORT(void) mupGetExprVar(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_pszName, muFloat_t** a_pVar); -API_EXPORT(void) mupGetVar(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_pszName, muFloat_t** a_pVar); -API_EXPORT(void) mupGetConst(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_pszName, muFloat_t* a_pVar); -API_EXPORT(void) mupSetArgSep(muParserHandle_t a_hParser, const muChar_t cArgSep); -API_EXPORT(void) mupSetDecSep(muParserHandle_t a_hParser, const muChar_t cArgSep); -API_EXPORT(void) mupSetThousandsSep(muParserHandle_t a_hParser, const muChar_t cArgSep); -API_EXPORT(void) mupResetLocale(muParserHandle_t a_hParser); - -// Add value recognition callbacks -API_EXPORT(void) mupAddValIdent(muParserHandle_t a_hParser, muIdentFun_t); - -// Error handling -API_EXPORT(muBool_t) mupError(muParserHandle_t a_hParser); -API_EXPORT(void) mupErrorReset(muParserHandle_t a_hParser); -API_EXPORT(void) mupSetErrorHandler(muParserHandle_t a_hParser, muErrorHandler_t a_pErrHandler); -API_EXPORT(const muChar_t*) mupGetErrorMsg(muParserHandle_t a_hParser); -API_EXPORT(muInt_t) mupGetErrorCode(muParserHandle_t a_hParser); -API_EXPORT(muInt_t) mupGetErrorPos(muParserHandle_t a_hParser); -API_EXPORT(const muChar_t*) mupGetErrorToken(muParserHandle_t a_hParser); -//API_EXPORT(const muChar_t*) mupGetErrorExpr(muParserHandle_t a_hParser); - -// This is used for .NET only. It creates a new variable allowing the dll to -// manage the variable rather than the .NET garbage collector. -API_EXPORT(muFloat_t*) mupCreateVar(); -API_EXPORT(void) mupReleaseVar(muFloat_t*); + typedef int muBool_t; // boolean type + typedef int muInt_t; // integer type + typedef double muFloat_t; // floating point type + + // function types for calculation + typedef muFloat_t (*muFun0_t)(); + typedef muFloat_t (*muFun1_t)(muFloat_t); + typedef muFloat_t (*muFun2_t)(muFloat_t, muFloat_t); + typedef muFloat_t (*muFun3_t)(muFloat_t, muFloat_t, muFloat_t); + typedef muFloat_t (*muFun4_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t); + typedef muFloat_t (*muFun5_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); + typedef muFloat_t (*muFun6_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); + typedef muFloat_t (*muFun7_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); + typedef muFloat_t ( + *muFun8_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); + typedef muFloat_t ( + *muFun9_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); + typedef muFloat_t (*muFun10_t)(muFloat_t, + muFloat_t, + muFloat_t, + muFloat_t, + muFloat_t, + muFloat_t, + muFloat_t, + muFloat_t, + muFloat_t, + muFloat_t); + + // Function prototypes for bulkmode functions + typedef muFloat_t (*muBulkFun0_t)(int, int); + typedef muFloat_t (*muBulkFun1_t)(int, int, muFloat_t); + typedef muFloat_t (*muBulkFun2_t)(int, int, muFloat_t, muFloat_t); + typedef muFloat_t (*muBulkFun3_t)(int, int, muFloat_t, muFloat_t, muFloat_t); + typedef muFloat_t (*muBulkFun4_t)(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t); + typedef muFloat_t (*muBulkFun5_t)(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); + typedef muFloat_t (*muBulkFun6_t)(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); + typedef muFloat_t ( + *muBulkFun7_t)(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); + typedef muFloat_t ( + *muBulkFun8_t)(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t); + typedef muFloat_t (*muBulkFun9_t)(int, + int, + muFloat_t, + muFloat_t, + muFloat_t, + muFloat_t, + muFloat_t, + muFloat_t, + muFloat_t, + muFloat_t, + muFloat_t); + typedef muFloat_t (*muBulkFun10_t)(int, + int, + muFloat_t, + muFloat_t, + muFloat_t, + muFloat_t, + muFloat_t, + muFloat_t, + muFloat_t, + muFloat_t, + muFloat_t, + muFloat_t); + + typedef muFloat_t (*muMultFun_t)(const muFloat_t*, muInt_t); + typedef muFloat_t (*muStrFun1_t)(const muChar_t*); + typedef muFloat_t (*muStrFun2_t)(const muChar_t*, muFloat_t); + typedef muFloat_t (*muStrFun3_t)(const muChar_t*, muFloat_t, muFloat_t); + + // Functions for parser management + typedef void (*muErrorHandler_t)(muParserHandle_t a_hParser); // [optional] callback to an error handler + typedef muFloat_t* (*muFacFun_t)(const muChar_t*, void*); // [optional] callback for creating new variables + typedef muInt_t (*muIdentFun_t)(const muChar_t*, muInt_t*, muFloat_t*); // [optional] value identification callbacks + + //----------------------------------------------------------------------------------------------------- + // Constants + static const int muOPRT_ASCT_LEFT = 0; + static const int muOPRT_ASCT_RIGHT = 1; + + static const int muBASETYPE_FLOAT = 0; + static const int muBASETYPE_INT = 1; + + //----------------------------------------------------------------------------------------------------- + // + // + // muParser C compatible bindings + // + // + //----------------------------------------------------------------------------------------------------- + + // Basic operations / initialization + API_EXPORT(muParserHandle_t) mupCreate(int nBaseType); + API_EXPORT(void) mupRelease(muParserHandle_t a_hParser); + API_EXPORT(const muChar_t*) mupGetExpr(muParserHandle_t a_hParser); + API_EXPORT(void) mupSetExpr(muParserHandle_t a_hParser, const muChar_t* a_szExpr); + API_EXPORT(void) mupSetVarFactory(muParserHandle_t a_hParser, muFacFun_t a_pFactory, void* pUserData); + API_EXPORT(const muChar_t*) mupGetVersion(muParserHandle_t a_hParser); + API_EXPORT(muFloat_t) mupEval(muParserHandle_t a_hParser); + API_EXPORT(muFloat_t*) mupEvalMulti(muParserHandle_t a_hParser, int* nNum); + API_EXPORT(void) mupEvalBulk(muParserHandle_t a_hParser, muFloat_t* a_fResult, int nSize); + + // Defining callbacks / variables / constants + API_EXPORT(void) + mupDefineFun0(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun0_t a_pFun, muBool_t a_bOptimize); + API_EXPORT(void) + mupDefineFun1(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun1_t a_pFun, muBool_t a_bOptimize); + API_EXPORT(void) + mupDefineFun2(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun2_t a_pFun, muBool_t a_bOptimize); + API_EXPORT(void) + mupDefineFun3(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun3_t a_pFun, muBool_t a_bOptimize); + API_EXPORT(void) + mupDefineFun4(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun4_t a_pFun, muBool_t a_bOptimize); + API_EXPORT(void) + mupDefineFun5(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun5_t a_pFun, muBool_t a_bOptimize); + API_EXPORT(void) + mupDefineFun6(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun6_t a_pFun, muBool_t a_bOptimize); + API_EXPORT(void) + mupDefineFun7(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun7_t a_pFun, muBool_t a_bOptimize); + API_EXPORT(void) + mupDefineFun8(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun8_t a_pFun, muBool_t a_bOptimize); + API_EXPORT(void) + mupDefineFun9(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun9_t a_pFun, muBool_t a_bOptimize); + API_EXPORT(void) + mupDefineFun10(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun10_t a_pFun, muBool_t a_bOptimize); + + // Defining bulkmode functions + API_EXPORT(void) mupDefineBulkFun0(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun0_t a_pFun); + API_EXPORT(void) mupDefineBulkFun1(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun1_t a_pFun); + API_EXPORT(void) mupDefineBulkFun2(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun2_t a_pFun); + API_EXPORT(void) mupDefineBulkFun3(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun3_t a_pFun); + API_EXPORT(void) mupDefineBulkFun4(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun4_t a_pFun); + API_EXPORT(void) mupDefineBulkFun5(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun5_t a_pFun); + API_EXPORT(void) mupDefineBulkFun6(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun6_t a_pFun); + API_EXPORT(void) mupDefineBulkFun7(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun7_t a_pFun); + API_EXPORT(void) mupDefineBulkFun8(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun8_t a_pFun); + API_EXPORT(void) mupDefineBulkFun9(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun9_t a_pFun); + API_EXPORT(void) mupDefineBulkFun10(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun10_t a_pFun); + + // string functions + API_EXPORT(void) mupDefineStrFun1(muParserHandle_t a_hParser, const muChar_t* a_szName, muStrFun1_t a_pFun); + API_EXPORT(void) mupDefineStrFun2(muParserHandle_t a_hParser, const muChar_t* a_szName, muStrFun2_t a_pFun); + API_EXPORT(void) mupDefineStrFun3(muParserHandle_t a_hParser, const muChar_t* a_szName, muStrFun3_t a_pFun); + + API_EXPORT(void) + mupDefineMultFun(muParserHandle_t a_hParser, const muChar_t* a_szName, muMultFun_t a_pFun, muBool_t a_bOptimize); + + API_EXPORT(void) + mupDefineOprt(muParserHandle_t a_hParser, + const muChar_t* a_szName, + muFun2_t a_pFun, + muInt_t a_nPrec, + muInt_t a_nOprtAsct, + muBool_t a_bOptimize); + + API_EXPORT(void) mupDefineConst(muParserHandle_t a_hParser, const muChar_t* a_szName, muFloat_t a_fVal); + + API_EXPORT(void) mupDefineStrConst(muParserHandle_t a_hParser, const muChar_t* a_szName, const muChar_t* a_sVal); + + API_EXPORT(void) mupDefineVar(muParserHandle_t a_hParser, const muChar_t* a_szName, muFloat_t* a_fVar); + + API_EXPORT(void) mupDefineBulkVar(muParserHandle_t a_hParser, const muChar_t* a_szName, muFloat_t* a_fVar); + + API_EXPORT(void) + mupDefinePostfixOprt(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun1_t a_pOprt, muBool_t a_bOptimize); + + API_EXPORT(void) + mupDefineInfixOprt(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun1_t a_pOprt, muBool_t a_bOptimize); + + // Define character sets for identifiers + API_EXPORT(void) mupDefineNameChars(muParserHandle_t a_hParser, const muChar_t* a_szCharset); + API_EXPORT(void) mupDefineOprtChars(muParserHandle_t a_hParser, const muChar_t* a_szCharset); + API_EXPORT(void) mupDefineInfixOprtChars(muParserHandle_t a_hParser, const muChar_t* a_szCharset); + + // Remove all / single variables + API_EXPORT(void) mupRemoveVar(muParserHandle_t a_hParser, const muChar_t* a_szName); + API_EXPORT(void) mupClearVar(muParserHandle_t a_hParser); + API_EXPORT(void) mupClearConst(muParserHandle_t a_hParser); + API_EXPORT(void) mupClearOprt(muParserHandle_t a_hParser); + API_EXPORT(void) mupClearFun(muParserHandle_t a_hParser); + + // Querying variables / expression variables / constants + API_EXPORT(int) mupGetExprVarNum(muParserHandle_t a_hParser); + API_EXPORT(int) mupGetVarNum(muParserHandle_t a_hParser); + API_EXPORT(int) mupGetConstNum(muParserHandle_t a_hParser); + API_EXPORT(void) + mupGetExprVar(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_pszName, muFloat_t** a_pVar); + API_EXPORT(void) + mupGetVar(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_pszName, muFloat_t** a_pVar); + API_EXPORT(void) + mupGetConst(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_pszName, muFloat_t* a_pVar); + API_EXPORT(void) mupSetArgSep(muParserHandle_t a_hParser, const muChar_t cArgSep); + API_EXPORT(void) mupSetDecSep(muParserHandle_t a_hParser, const muChar_t cArgSep); + API_EXPORT(void) mupSetThousandsSep(muParserHandle_t a_hParser, const muChar_t cArgSep); + API_EXPORT(void) mupResetLocale(muParserHandle_t a_hParser); + + // Add value recognition callbacks + API_EXPORT(void) mupAddValIdent(muParserHandle_t a_hParser, muIdentFun_t); + + // Error handling + API_EXPORT(muBool_t) mupError(muParserHandle_t a_hParser); + API_EXPORT(void) mupErrorReset(muParserHandle_t a_hParser); + API_EXPORT(void) mupSetErrorHandler(muParserHandle_t a_hParser, muErrorHandler_t a_pErrHandler); + API_EXPORT(const muChar_t*) mupGetErrorMsg(muParserHandle_t a_hParser); + API_EXPORT(muInt_t) mupGetErrorCode(muParserHandle_t a_hParser); + API_EXPORT(muInt_t) mupGetErrorPos(muParserHandle_t a_hParser); + API_EXPORT(const muChar_t*) mupGetErrorToken(muParserHandle_t a_hParser); + // API_EXPORT(const muChar_t*) mupGetErrorExpr(muParserHandle_t a_hParser); + + // This is used for .NET only. It creates a new variable allowing the dll to + // manage the variable rather than the .NET garbage collector. + API_EXPORT(muFloat_t*) mupCreateVar(); + API_EXPORT(void) mupReleaseVar(muFloat_t*); #ifdef __cplusplus } diff -Nru mstflint-4.17.0+1/ext_libs/muparser/muParserError.cpp mstflint-4.21.0+1/ext_libs/muparser/muParserError.cpp --- mstflint-4.17.0+1/ext_libs/muparser/muParserError.cpp 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/muParserError.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -1,244 +1,217 @@ /* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2011 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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. */ #include "muParserError.h" - namespace mu { - const ParserErrorMsg ParserErrorMsg::m_Instance; +const ParserErrorMsg ParserErrorMsg::m_Instance; - //------------------------------------------------------------------------------ - const ParserErrorMsg& ParserErrorMsg::Instance() - { +//------------------------------------------------------------------------------ +const ParserErrorMsg& ParserErrorMsg::Instance() +{ return m_Instance; - } +} + +//------------------------------------------------------------------------------ +string_type ParserErrorMsg::operator[](unsigned a_iIdx) const +{ + return (a_iIdx < m_vErrMsg.size()) ? m_vErrMsg[a_iIdx] : string_type(); +} + +//--------------------------------------------------------------------------- +ParserErrorMsg::~ParserErrorMsg() {} - //------------------------------------------------------------------------------ - string_type ParserErrorMsg::operator[](unsigned a_iIdx) const - { - return (a_iIdx \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2004-2011 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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 MU_PARSER_ERROR_H @@ -35,92 +35,91 @@ #include "muParserDef.h" -/** \file +/** \file \brief This file defines the error class used by the parser. */ namespace mu { - /** \brief Error codes. */ enum EErrorCodes { - // Formula syntax errors - ecUNEXPECTED_OPERATOR = 0, ///< Unexpected binary operator found - ecUNASSIGNABLE_TOKEN = 1, ///< Token cant be identified. - ecUNEXPECTED_EOF = 2, ///< Unexpected end of formula. (Example: "2+sin(") - ecUNEXPECTED_ARG_SEP = 3, ///< An unexpected comma has been found. (Example: "1,23") - ecUNEXPECTED_ARG = 4, ///< An unexpected argument has been found - ecUNEXPECTED_VAL = 5, ///< An unexpected value token has been found - ecUNEXPECTED_VAR = 6, ///< An unexpected variable token has been found - ecUNEXPECTED_PARENS = 7, ///< Unexpected Parenthesis, opening or closing - ecUNEXPECTED_STR = 8, ///< A string has been found at an inapropriate position - ecSTRING_EXPECTED = 9, ///< A string function has been called with a different type of argument - ecVAL_EXPECTED = 10, ///< A numerical function has been called with a non value type of argument - ecMISSING_PARENS = 11, ///< Missing parens. (Example: "3*sin(3") - ecUNEXPECTED_FUN = 12, ///< Unexpected function found. (Example: "sin(8)cos(9)") - ecUNTERMINATED_STRING = 13, ///< unterminated string constant. (Example: "3*valueof("hello)") - ecTOO_MANY_PARAMS = 14, ///< Too many function parameters - ecTOO_FEW_PARAMS = 15, ///< Too few function parameters. (Example: "ite(1<2,2)") - ecOPRT_TYPE_CONFLICT = 16, ///< binary operators may only be applied to value items of the same type - ecSTR_RESULT = 17, ///< result is a string - - // Invalid Parser input Parameters - ecINVALID_NAME = 18, ///< Invalid function, variable or constant name. - ecINVALID_BINOP_IDENT = 19, ///< Invalid binary operator identifier - ecINVALID_INFIX_IDENT = 20, ///< Invalid function, variable or constant name. - ecINVALID_POSTFIX_IDENT = 21, ///< Invalid function, variable or constant name. - - ecBUILTIN_OVERLOAD = 22, ///< Trying to overload builtin operator - ecINVALID_FUN_PTR = 23, ///< Invalid callback function pointer - ecINVALID_VAR_PTR = 24, ///< Invalid variable pointer - ecEMPTY_EXPRESSION = 25, ///< The Expression is empty - ecNAME_CONFLICT = 26, ///< Name conflict - ecOPT_PRI = 27, ///< Invalid operator priority - // - ecDOMAIN_ERROR = 28, ///< catch division by zero, sqrt(-1), log(0) (currently unused) - ecDIV_BY_ZERO = 29, ///< Division by zero (currently unused) - ecGENERIC = 30, ///< Generic error - ecLOCALE = 31, ///< Conflict with current locale - - ecUNEXPECTED_CONDITIONAL = 32, - ecMISSING_ELSE_CLAUSE = 33, - ecMISPLACED_COLON = 34, - - ecUNREASONABLE_NUMBER_OF_COMPUTATIONS = 35, - - // internal errors - ecINTERNAL_ERROR = 36, ///< Internal error of any kind. - - // The last two are special entries - ecCOUNT, ///< This is no error code, It just stores just the total number of error codes - ecUNDEFINED = -1 ///< Undefined message, placeholder to detect unassigned error messages + // Formula syntax errors + ecUNEXPECTED_OPERATOR = 0, ///< Unexpected binary operator found + ecUNASSIGNABLE_TOKEN = 1, ///< Token cant be identified. + ecUNEXPECTED_EOF = 2, ///< Unexpected end of formula. (Example: "2+sin(") + ecUNEXPECTED_ARG_SEP = 3, ///< An unexpected comma has been found. (Example: "1,23") + ecUNEXPECTED_ARG = 4, ///< An unexpected argument has been found + ecUNEXPECTED_VAL = 5, ///< An unexpected value token has been found + ecUNEXPECTED_VAR = 6, ///< An unexpected variable token has been found + ecUNEXPECTED_PARENS = 7, ///< Unexpected Parenthesis, opening or closing + ecUNEXPECTED_STR = 8, ///< A string has been found at an inapropriate position + ecSTRING_EXPECTED = 9, ///< A string function has been called with a different type of argument + ecVAL_EXPECTED = 10, ///< A numerical function has been called with a non value type of argument + ecMISSING_PARENS = 11, ///< Missing parens. (Example: "3*sin(3") + ecUNEXPECTED_FUN = 12, ///< Unexpected function found. (Example: "sin(8)cos(9)") + ecUNTERMINATED_STRING = 13, ///< unterminated string constant. (Example: "3*valueof("hello)") + ecTOO_MANY_PARAMS = 14, ///< Too many function parameters + ecTOO_FEW_PARAMS = 15, ///< Too few function parameters. (Example: "ite(1<2,2)") + ecOPRT_TYPE_CONFLICT = 16, ///< binary operators may only be applied to value items of the same type + ecSTR_RESULT = 17, ///< result is a string + + // Invalid Parser input Parameters + ecINVALID_NAME = 18, ///< Invalid function, variable or constant name. + ecINVALID_BINOP_IDENT = 19, ///< Invalid binary operator identifier + ecINVALID_INFIX_IDENT = 20, ///< Invalid function, variable or constant name. + ecINVALID_POSTFIX_IDENT = 21, ///< Invalid function, variable or constant name. + + ecBUILTIN_OVERLOAD = 22, ///< Trying to overload builtin operator + ecINVALID_FUN_PTR = 23, ///< Invalid callback function pointer + ecINVALID_VAR_PTR = 24, ///< Invalid variable pointer + ecEMPTY_EXPRESSION = 25, ///< The Expression is empty + ecNAME_CONFLICT = 26, ///< Name conflict + ecOPT_PRI = 27, ///< Invalid operator priority + // + ecDOMAIN_ERROR = 28, ///< catch division by zero, sqrt(-1), log(0) (currently unused) + ecDIV_BY_ZERO = 29, ///< Division by zero (currently unused) + ecGENERIC = 30, ///< Generic error + ecLOCALE = 31, ///< Conflict with current locale + + ecUNEXPECTED_CONDITIONAL = 32, + ecMISSING_ELSE_CLAUSE = 33, + ecMISPLACED_COLON = 34, + + ecUNREASONABLE_NUMBER_OF_COMPUTATIONS = 35, + + // internal errors + ecINTERNAL_ERROR = 36, ///< Internal error of any kind. + + // The last two are special entries + ecCOUNT, ///< This is no error code, It just stores just the total number of error codes + ecUNDEFINED = -1 ///< Undefined message, placeholder to detect unassigned error messages }; //--------------------------------------------------------------------------- /** \brief A class that handles the error messages. -*/ + */ class ParserErrorMsg { public: typedef ParserErrorMsg self_type; - ParserErrorMsg& operator=(const ParserErrorMsg &); + ParserErrorMsg& operator=(const ParserErrorMsg&); ParserErrorMsg(const ParserErrorMsg&); ParserErrorMsg(); - ~ParserErrorMsg(); + ~ParserErrorMsg(); static const ParserErrorMsg& Instance(); string_type operator[](unsigned a_iIdx) const; private: - std::vector m_vErrMsg; ///< A vector with the predefined error messages - static const self_type m_Instance; ///< The instance pointer + std::vector m_vErrMsg; ///< A vector with the predefined error messages + static const self_type m_Instance; ///< The instance pointer }; //--------------------------------------------------------------------------- -/** \brief Error class of the parser. +/** \brief Error class of the parser. \author Ingo Berg Part of the math parser package. @@ -128,33 +127,25 @@ class ParserError { private: - /** \brief Replace all ocuurences of a substring with another string. */ - void ReplaceSubString( string_type &strSource, - const string_type &strFind, - const string_type &strReplaceWith); + void ReplaceSubString(string_type& strSource, const string_type& strFind, const string_type& strReplaceWith); void Reset(); public: - ParserError(); explicit ParserError(EErrorCodes a_iErrc); - explicit ParserError(const string_type &sMsg); - ParserError( EErrorCodes a_iErrc, - const string_type &sTok, - const string_type &sFormula = string_type(), - int a_iPos = -1); - ParserError( EErrorCodes a_iErrc, - int a_iPos, - const string_type &sTok); - ParserError( const char_type *a_szMsg, - int a_iPos = -1, - const string_type &sTok = string_type()); - ParserError(const ParserError &a_Obj); - ParserError& operator=(const ParserError &a_Obj); - ~ParserError(); + explicit ParserError(const string_type& sMsg); + ParserError(EErrorCodes a_iErrc, + const string_type& sTok, + const string_type& sFormula = string_type(), + int a_iPos = -1); + ParserError(EErrorCodes a_iErrc, int a_iPos, const string_type& sTok); + ParserError(const char_type* a_szMsg, int a_iPos = -1, const string_type& sTok = string_type()); + ParserError(const ParserError& a_Obj); + ParserError& operator=(const ParserError& a_Obj); + ~ParserError(); - void SetFormula(const string_type &a_strFormula); + void SetFormula(const string_type& a_strFormula); const string_type& GetExpr() const; const string_type& GetMsg() const; int GetPos() const; @@ -165,12 +156,11 @@ string_type m_strMsg; ///< The message string string_type m_strFormula; ///< Formula string string_type m_strTok; ///< Token related with the error - int m_iPos; ///< Formula position related to the error + int m_iPos; ///< Formula position related to the error EErrorCodes m_iErrc; ///< Error code - const ParserErrorMsg &m_ErrMsg; -}; + const ParserErrorMsg& m_ErrMsg; +}; } // namespace mu #endif - diff -Nru mstflint-4.17.0+1/ext_libs/muparser/muParserFixes.h mstflint-4.21.0+1/ext_libs/muparser/muParserFixes.h --- mstflint-4.17.0+1/ext_libs/muparser/muParserFixes.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/muParserFixes.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ /* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2013 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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 MU_PARSER_FIXES_H @@ -45,18 +45,16 @@ // remark #981: operands are evaluated in unspecified order // disabled -> completely pointless if the functions do not have side effects // -#pragma warning(disable:981) +#pragma warning(disable : 981) // remark #383: value copied to temporary, reference to temporary used -#pragma warning(disable:383) +#pragma warning(disable : 383) // remark #1572: floating-point equality and inequality comparisons are unreliable // disabled -> everyone knows it, the parser passes this problem // deliberately to the user -#pragma warning(disable:1572) +#pragma warning(disable : 1572) #endif #endif // include guard - - diff -Nru mstflint-4.17.0+1/ext_libs/muparser/muParser.h mstflint-4.21.0+1/ext_libs/muparser/muParser.h --- mstflint-4.17.0+1/ext_libs/muparser/muParser.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/muParser.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ /* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2013 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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 MU_PARSER_H #define MU_PARSER_H @@ -38,78 +38,73 @@ namespace mu { - /** \brief Mathematical expressions parser. - - Standard implementation of the mathematical expressions parser. - Can be used as a reference implementation for subclassing the parser. - - - (C) 2011 Ingo Berg
- muparser(at)beltoforion.de -
- */ - /* final */ class Parser : public ParserBase - { - public: +/** \brief Mathematical expressions parser. + Standard implementation of the mathematical expressions parser. + Can be used as a reference implementation for subclassing the parser. + + + (C) 2011 Ingo Berg
+ muparser(at)beltoforion.de +
+*/ +/* final */ class Parser : public ParserBase +{ +public: Parser(); virtual void InitCharSets(); virtual void InitFun(); virtual void InitConst(); virtual void InitOprt(); - virtual void OnDetectVar(string_type *pExpr, int &nStart, int &nEnd); + virtual void OnDetectVar(string_type* pExpr, int& nStart, int& nEnd); - value_type Diff(value_type *a_Var, - value_type a_fPos, - value_type a_fEpsilon = 0) const; - - protected: + value_type Diff(value_type* a_Var, value_type a_fPos, value_type a_fEpsilon = 0) const; +protected: // Trigonometric functions - static value_type Sin(value_type); - static value_type Cos(value_type); - static value_type Tan(value_type); - static value_type Tan2(value_type, value_type); + static value_type Sin(value_type); + static value_type Cos(value_type); + static value_type Tan(value_type); + static value_type Tan2(value_type, value_type); // arcus functions - static value_type ASin(value_type); - static value_type ACos(value_type); - static value_type ATan(value_type); - static value_type ATan2(value_type, value_type); + static value_type ASin(value_type); + static value_type ACos(value_type); + static value_type ATan(value_type); + static value_type ATan2(value_type, value_type); // hyperbolic functions - static value_type Sinh(value_type); - static value_type Cosh(value_type); - static value_type Tanh(value_type); + static value_type Sinh(value_type); + static value_type Cosh(value_type); + static value_type Tanh(value_type); // arcus hyperbolic functions - static value_type ASinh(value_type); - static value_type ACosh(value_type); - static value_type ATanh(value_type); + static value_type ASinh(value_type); + static value_type ACosh(value_type); + static value_type ATanh(value_type); // Logarithm functions - static value_type Log2(value_type); // Logarithm Base 2 - static value_type Log10(value_type); // Logarithm Base 10 - static value_type Ln(value_type); // Logarithm Base e (natural logarithm) + static value_type Log2(value_type); // Logarithm Base 2 + static value_type Log10(value_type); // Logarithm Base 10 + static value_type Ln(value_type); // Logarithm Base e (natural logarithm) // misc - static value_type Exp(value_type); - static value_type Abs(value_type); - static value_type Sqrt(value_type); - static value_type Rint(value_type); - static value_type Sign(value_type); + static value_type Exp(value_type); + static value_type Abs(value_type); + static value_type Sqrt(value_type); + static value_type Rint(value_type); + static value_type Sign(value_type); // Prefix operators // !!! Unary Minus is a MUST if you want to use negative signs !!! - static value_type UnaryMinus(value_type); - static value_type UnaryPlus(value_type); + static value_type UnaryMinus(value_type); + static value_type UnaryPlus(value_type); // Functions with variable number of arguments - static value_type Sum(const value_type*, int); // sum - static value_type Avg(const value_type*, int); // mean value - static value_type Min(const value_type*, int); // minimum - static value_type Max(const value_type*, int); // maximum + static value_type Sum(const value_type*, int); // sum + static value_type Avg(const value_type*, int); // mean value + static value_type Min(const value_type*, int); // minimum + static value_type Max(const value_type*, int); // maximum - static int IsVal(const char_type* a_szExpr, int *a_iPos, value_type *a_fVal); - }; + static int IsVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal); +}; } // namespace mu #endif - diff -Nru mstflint-4.17.0+1/ext_libs/muparser/muParserInt.cpp mstflint-4.21.0+1/ext_libs/muparser/muParserInt.cpp --- mstflint-4.17.0+1/ext_libs/muparser/muParserInt.cpp 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/muParserInt.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ /* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2011 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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. */ #include "muParserInt.h" @@ -38,243 +38,299 @@ /** \brief Namespace for mathematical applications. */ namespace mu { -value_type ParserInt::Abs(value_type v) { return (value_type)Round(fabs((double)v)); } -value_type ParserInt::Sign(value_type v) { return (Round(v)<0) ? -1 : (Round(v)>0) ? 1 : 0; } -value_type ParserInt::Ite(value_type v1, - value_type v2, - value_type v3) { return (Round(v1)==1) ? Round(v2) : Round(v3); } -value_type ParserInt::Add(value_type v1, value_type v2) { return Round(v1) + Round(v2); } -value_type ParserInt::Sub(value_type v1, value_type v2) { return Round(v1) - Round(v2); } -value_type ParserInt::Mul(value_type v1, value_type v2) { return Round(v1) * Round(v2); } -value_type ParserInt::Div(value_type v1, value_type v2) { return Round(v1) / Round(v2); } -value_type ParserInt::Mod(value_type v1, value_type v2) { return Round(v1) % Round(v2); } -value_type ParserInt::Shr(value_type v1, value_type v2) { return Round(v1) >> Round(v2); } -value_type ParserInt::Shl(value_type v1, value_type v2) { return Round(v1) << Round(v2); } -value_type ParserInt::LogAnd(value_type v1, value_type v2) { return Round(v1) & Round(v2); } -value_type ParserInt::LogOr(value_type v1, value_type v2) { return Round(v1) | Round(v2); } -value_type ParserInt::And(value_type v1, value_type v2) { return Round(v1) && Round(v2); } -value_type ParserInt::Or(value_type v1, value_type v2) { return Round(v1) || Round(v2); } -value_type ParserInt::Less(value_type v1, value_type v2) { return Round(v1) < Round(v2); } -value_type ParserInt::Greater(value_type v1, value_type v2) { return Round(v1) > Round(v2); } -value_type ParserInt::LessEq(value_type v1, value_type v2) { return Round(v1) <= Round(v2); } -value_type ParserInt::GreaterEq(value_type v1, value_type v2) { return Round(v1) >= Round(v2); } -value_type ParserInt::Equal(value_type v1, value_type v2) { return Round(v1) == Round(v2); } -value_type ParserInt::NotEqual(value_type v1, value_type v2) { return Round(v1) != Round(v2); } -value_type ParserInt::Not(value_type v) { return !Round(v); } - -value_type ParserInt::Pow(value_type v1, value_type v2) -{ - return std::pow((double)Round(v1), (double)Round(v2)); +value_type ParserInt::Abs(value_type v) +{ + return (value_type)Round(fabs((double)v)); +} +value_type ParserInt::Sign(value_type v) +{ + return (Round(v) < 0) ? -1 : (Round(v) > 0) ? 1 : 0; +} +value_type ParserInt::Ite(value_type v1, value_type v2, value_type v3) +{ + return (Round(v1) == 1) ? Round(v2) : Round(v3); +} +value_type ParserInt::Add(value_type v1, value_type v2) +{ + return Round(v1) + Round(v2); +} +value_type ParserInt::Sub(value_type v1, value_type v2) +{ + return Round(v1) - Round(v2); +} +value_type ParserInt::Mul(value_type v1, value_type v2) +{ + return Round(v1) * Round(v2); +} +value_type ParserInt::Div(value_type v1, value_type v2) +{ + return Round(v1) / Round(v2); +} +value_type ParserInt::Mod(value_type v1, value_type v2) +{ + return Round(v1) % Round(v2); +} +value_type ParserInt::Shr(value_type v1, value_type v2) +{ + return Round(v1) >> Round(v2); +} +value_type ParserInt::Shl(value_type v1, value_type v2) +{ + return Round(v1) << Round(v2); +} +value_type ParserInt::LogAnd(value_type v1, value_type v2) +{ + return Round(v1) & Round(v2); +} +value_type ParserInt::LogOr(value_type v1, value_type v2) +{ + return Round(v1) | Round(v2); +} +value_type ParserInt::And(value_type v1, value_type v2) +{ + return Round(v1) && Round(v2); +} +value_type ParserInt::Or(value_type v1, value_type v2) +{ + return Round(v1) || Round(v2); +} +value_type ParserInt::Less(value_type v1, value_type v2) +{ + return Round(v1) < Round(v2); +} +value_type ParserInt::Greater(value_type v1, value_type v2) +{ + return Round(v1) > Round(v2); +} +value_type ParserInt::LessEq(value_type v1, value_type v2) +{ + return Round(v1) <= Round(v2); +} +value_type ParserInt::GreaterEq(value_type v1, value_type v2) +{ + return Round(v1) >= Round(v2); +} +value_type ParserInt::Equal(value_type v1, value_type v2) +{ + return Round(v1) == Round(v2); +} +value_type ParserInt::NotEqual(value_type v1, value_type v2) +{ + return Round(v1) != Round(v2); +} +value_type ParserInt::Not(value_type v) +{ + return !Round(v); +} + +value_type ParserInt::Pow(value_type v1, value_type v2) +{ + return std::pow((double)Round(v1), (double)Round(v2)); } //--------------------------------------------------------------------------- // Unary operator Callbacks: Infix operators -value_type ParserInt::UnaryMinus(value_type v) -{ - return -Round(v); +value_type ParserInt::UnaryMinus(value_type v) +{ + return -Round(v); } //--------------------------------------------------------------------------- value_type ParserInt::Sum(const value_type* a_afArg, int a_iArgc) -{ - if (!a_iArgc) - throw ParserError(_T("too few arguments for function sum.")); - - value_type fRes=0; - for (int i=0; i> iVal; - if (stream.fail()) - return 0; - - stringstream_type::pos_type iEnd = stream.tellg(); // Position after reading - if (stream.fail()) - iEnd = stream.str().length(); + stream >> iVal; + if (stream.fail()) + return 0; - if (iEnd==(stringstream_type::pos_type)-1) - return 0; + stringstream_type::pos_type iEnd = stream.tellg(); // Position after reading + if (stream.fail()) + iEnd = stream.str().length(); - *a_iPos += (int)iEnd; - *a_fVal = (value_type)iVal; - return 1; + if (iEnd == (stringstream_type::pos_type)-1) + return 0; + + *a_iPos += (int)iEnd; + *a_fVal = (value_type)iVal; + return 1; } //--------------------------------------------------------------------------- -/** \brief Check a given position in the expression for the presence of - a hex value. +/** \brief Check a given position in the expression for the presence of + a hex value. \param a_szExpr Pointer to the expression string - \param [in/out] a_iPos Pointer to an integer value holding the current parsing + \param [in/out] a_iPos Pointer to an integer value holding the current parsing position in the expression. \param [out] a_fVal Pointer to the position where the detected value shall be stored. Hey values must be prefixed with "0x" in order to be detected properly. */ -int ParserInt::IsHexVal(const char_type *a_szExpr, int *a_iPos, value_type *a_fVal) +int ParserInt::IsHexVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal) { - if (a_szExpr[1]==0 || (a_szExpr[0]!='0' || a_szExpr[1]!='x') ) - return 0; + if (a_szExpr[1] == 0 || (a_szExpr[0] != '0' || a_szExpr[1] != 'x')) + return 0; - unsigned iVal(0); + unsigned iVal(0); - // New code based on streams for UNICODE compliance: - stringstream_type::pos_type nPos(0); - stringstream_type ss(a_szExpr + 2); - ss >> std::hex >> iVal; - nPos = ss.tellg(); + // New code based on streams for UNICODE compliance: + stringstream_type::pos_type nPos(0); + stringstream_type ss(a_szExpr + 2); + ss >> std::hex >> iVal; + nPos = ss.tellg(); - if (nPos==(stringstream_type::pos_type)0) - return 1; + if (nPos == (stringstream_type::pos_type)0) + return 1; - *a_iPos += (int)(2 + nPos); - *a_fVal = (value_type)iVal; - return 1; + *a_iPos += (int)(2 + nPos); + *a_fVal = (value_type)iVal; + return 1; } //--------------------------------------------------------------------------- -int ParserInt::IsBinVal(const char_type *a_szExpr, int *a_iPos, value_type *a_fVal) +int ParserInt::IsBinVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal) { - if (a_szExpr[0]!='#') - return 0; + if (a_szExpr[0] != '#') + return 0; - unsigned iVal(0), - iBits(sizeof(iVal)*8), - i(0); + unsigned iVal(0), iBits(sizeof(iVal) * 8), i(0); - for (i=0; (a_szExpr[i+1]=='0' || a_szExpr[i+1]=='1') && i> (iBits-i) ); - *a_iPos += i+1; + *a_fVal = (unsigned)(iVal >> (iBits - i)); + *a_iPos += i + 1; - return 1; + return 1; } //--------------------------------------------------------------------------- -/** \brief Constructor. +/** \brief Constructor. Call ParserBase class constructor and trigger Function, Operator and Constant initialization. */ -ParserInt::ParserInt() - :ParserBase() +ParserInt::ParserInt() : ParserBase() { - AddValIdent(IsVal); // lowest priority - AddValIdent(IsBinVal); - AddValIdent(IsHexVal); // highest priority + AddValIdent(IsVal); // lowest priority + AddValIdent(IsBinVal); + AddValIdent(IsHexVal); // highest priority - InitCharSets(); - InitFun(); - InitOprt(); + InitCharSets(); + InitFun(); + InitOprt(); } //--------------------------------------------------------------------------- -void ParserInt::InitConst() -{ -} +void ParserInt::InitConst() {} //--------------------------------------------------------------------------- void ParserInt::InitCharSets() { - DefineNameChars( _T("0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") ); - DefineOprtChars( _T("+-*^/?<>=!%&|~'_") ); - DefineInfixOprtChars( _T("/+-*^?<>=!%&|~'_") ); + DefineNameChars(_T("0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")); + DefineOprtChars(_T("+-*^/?<>=!%&|~'_")); + DefineInfixOprtChars(_T("/+-*^?<>=!%&|~'_")); } //--------------------------------------------------------------------------- /** \brief Initialize the default functions. */ void ParserInt::InitFun() { - DefineFun( _T("sign"), Sign); - DefineFun( _T("abs"), Abs); - DefineFun( _T("if"), Ite); - DefineFun( _T("sum"), Sum); - DefineFun( _T("min"), Min); - DefineFun( _T("max"), Max); + DefineFun(_T("sign"), Sign); + DefineFun(_T("abs"), Abs); + DefineFun(_T("if"), Ite); + DefineFun(_T("sum"), Sum); + DefineFun(_T("min"), Min); + DefineFun(_T("max"), Max); } //--------------------------------------------------------------------------- /** \brief Initialize operators. */ void ParserInt::InitOprt() { - // disable all built in operators, not all of them useful for integer numbers - // (they don't do rounding of values) - EnableBuiltInOprt(false); - - // Disable all built in operators, they wont work with integer numbers - // since they are designed for floating point numbers - DefineInfixOprt( _T("-"), UnaryMinus); - DefineInfixOprt( _T("!"), Not); - - DefineOprt( _T("&"), LogAnd, prLOGIC); - DefineOprt( _T("|"), LogOr, prLOGIC); - DefineOprt( _T("&&"), And, prLOGIC); - DefineOprt( _T("||"), Or, prLOGIC); - - DefineOprt( _T("<"), Less, prCMP); - DefineOprt( _T(">"), Greater, prCMP); - DefineOprt( _T("<="), LessEq, prCMP); - DefineOprt( _T(">="), GreaterEq, prCMP); - DefineOprt( _T("=="), Equal, prCMP); - DefineOprt( _T("!="), NotEqual, prCMP); - - DefineOprt( _T("+"), Add, prADD_SUB); - DefineOprt( _T("-"), Sub, prADD_SUB); - - DefineOprt( _T("*"), Mul, prMUL_DIV); - DefineOprt( _T("/"), Div, prMUL_DIV); - DefineOprt( _T("%"), Mod, prMUL_DIV); - - DefineOprt( _T("^"), Pow, prPOW, oaRIGHT); - DefineOprt( _T(">>"), Shr, prMUL_DIV+1); - DefineOprt( _T("<<"), Shl, prMUL_DIV+1); + // disable all built in operators, not all of them useful for integer numbers + // (they don't do rounding of values) + EnableBuiltInOprt(false); + + // Disable all built in operators, they wont work with integer numbers + // since they are designed for floating point numbers + DefineInfixOprt(_T("-"), UnaryMinus); + DefineInfixOprt(_T("!"), Not); + + DefineOprt(_T("&"), LogAnd, prLOGIC); + DefineOprt(_T("|"), LogOr, prLOGIC); + DefineOprt(_T("&&"), And, prLOGIC); + DefineOprt(_T("||"), Or, prLOGIC); + + DefineOprt(_T("<"), Less, prCMP); + DefineOprt(_T(">"), Greater, prCMP); + DefineOprt(_T("<="), LessEq, prCMP); + DefineOprt(_T(">="), GreaterEq, prCMP); + DefineOprt(_T("=="), Equal, prCMP); + DefineOprt(_T("!="), NotEqual, prCMP); + + DefineOprt(_T("+"), Add, prADD_SUB); + DefineOprt(_T("-"), Sub, prADD_SUB); + + DefineOprt(_T("*"), Mul, prMUL_DIV); + DefineOprt(_T("/"), Div, prMUL_DIV); + DefineOprt(_T("%"), Mod, prMUL_DIV); + + DefineOprt(_T("^"), Pow, prPOW, oaRIGHT); + DefineOprt(_T(">>"), Shr, prMUL_DIV + 1); + DefineOprt(_T("<<"), Shl, prMUL_DIV + 1); } } // namespace mu diff -Nru mstflint-4.17.0+1/ext_libs/muparser/muParserInt.h mstflint-4.21.0+1/ext_libs/muparser/muParserInt.h --- mstflint-4.17.0+1/ext_libs/muparser/muParserInt.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/muParserInt.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ /* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2004-2013 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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 MU_PARSER_INT_H @@ -29,100 +29,86 @@ #include "muParserBase.h" #include - /** \file \brief Definition of a parser using integer value. */ - namespace mu { - /** \brief Mathematical expressions parser. - - This version of the parser handles only integer numbers. It disables the built in operators thus it is + + This version of the parser handles only integer numbers. It disables the built in operators thus it is slower than muParser. Integer values are stored in the double value_type and converted if needed. */ class ParserInt : public ParserBase { private: - static int Round(value_type v) { return (int)(v + ((v>=0) ? 0.5 : -0.5) ); }; - - static value_type Abs(value_type); - static value_type Sign(value_type); - static value_type Ite(value_type, value_type, value_type); + static int Round(value_type v) { return (int)(v + ((v >= 0) ? 0.5 : -0.5)); }; + + static value_type Abs(value_type); + static value_type Sign(value_type); + static value_type Ite(value_type, value_type, value_type); // !! The unary Minus is a MUST, otherwise you cant use negative signs !! - static value_type UnaryMinus(value_type); + static value_type UnaryMinus(value_type); // Functions with variable number of arguments - static value_type Sum(const value_type* a_afArg, int a_iArgc); // sum - static value_type Min(const value_type* a_afArg, int a_iArgc); // minimum - static value_type Max(const value_type* a_afArg, int a_iArgc); // maximum + static value_type Sum(const value_type* a_afArg, int a_iArgc); // sum + static value_type Min(const value_type* a_afArg, int a_iArgc); // minimum + static value_type Max(const value_type* a_afArg, int a_iArgc); // maximum // binary operator callbacks - static value_type Add(value_type v1, value_type v2); - static value_type Sub(value_type v1, value_type v2); - static value_type Mul(value_type v1, value_type v2); - static value_type Div(value_type v1, value_type v2); - static value_type Mod(value_type v1, value_type v2); - static value_type Pow(value_type v1, value_type v2); - static value_type Shr(value_type v1, value_type v2); - static value_type Shl(value_type v1, value_type v2); - static value_type LogAnd(value_type v1, value_type v2); - static value_type LogOr(value_type v1, value_type v2); - static value_type And(value_type v1, value_type v2); - static value_type Or(value_type v1, value_type v2); - static value_type Xor(value_type v1, value_type v2); - static value_type Less(value_type v1, value_type v2); - static value_type Greater(value_type v1, value_type v2); - static value_type LessEq(value_type v1, value_type v2); - static value_type GreaterEq(value_type v1, value_type v2); - static value_type Equal(value_type v1, value_type v2); - static value_type NotEqual(value_type v1, value_type v2); - static value_type Not(value_type v1); - - static int IsHexVal(const char_type* a_szExpr, int *a_iPos, value_type *a_iVal); - static int IsBinVal(const char_type* a_szExpr, int *a_iPos, value_type *a_iVal); - static int IsVal (const char_type* a_szExpr, int *a_iPos, value_type *a_iVal); + static value_type Add(value_type v1, value_type v2); + static value_type Sub(value_type v1, value_type v2); + static value_type Mul(value_type v1, value_type v2); + static value_type Div(value_type v1, value_type v2); + static value_type Mod(value_type v1, value_type v2); + static value_type Pow(value_type v1, value_type v2); + static value_type Shr(value_type v1, value_type v2); + static value_type Shl(value_type v1, value_type v2); + static value_type LogAnd(value_type v1, value_type v2); + static value_type LogOr(value_type v1, value_type v2); + static value_type And(value_type v1, value_type v2); + static value_type Or(value_type v1, value_type v2); + static value_type Xor(value_type v1, value_type v2); + static value_type Less(value_type v1, value_type v2); + static value_type Greater(value_type v1, value_type v2); + static value_type LessEq(value_type v1, value_type v2); + static value_type GreaterEq(value_type v1, value_type v2); + static value_type Equal(value_type v1, value_type v2); + static value_type NotEqual(value_type v1, value_type v2); + static value_type Not(value_type v1); + + static int IsHexVal(const char_type* a_szExpr, int* a_iPos, value_type* a_iVal); + static int IsBinVal(const char_type* a_szExpr, int* a_iPos, value_type* a_iVal); + static int IsVal(const char_type* a_szExpr, int* a_iPos, value_type* a_iVal); /** \brief A facet class used to change decimal and thousands separator. */ template class change_dec_sep : public std::numpunct { public: - - explicit change_dec_sep(char_type cDecSep, char_type cThousandsSep = 0, int nGroup = 3) - :std::numpunct() - ,m_cDecPoint(cDecSep) - ,m_cThousandsSep(cThousandsSep) - ,m_nGroup(nGroup) - {} - + explicit change_dec_sep(char_type cDecSep, char_type cThousandsSep = 0, int nGroup = 3) : + std::numpunct(), m_cDecPoint(cDecSep), m_cThousandsSep(cThousandsSep), m_nGroup(nGroup) + { + } + protected: - - virtual char_type do_decimal_point() const - { - return m_cDecPoint; - } - - virtual char_type do_thousands_sep() const - { - return m_cThousandsSep; - } - - virtual std::string do_grouping() const - { - // fix for issue 4: https://code.google.com/p/muparser/issues/detail?id=4 - // courtesy of Jens Bartsch - // original code: - // return std::string(1, (char)m_nGroup); - // new code: - return std::string(1, (char)(m_cThousandsSep > 0 ? m_nGroup : CHAR_MAX)); - } + virtual char_type do_decimal_point() const { return m_cDecPoint; } - private: + virtual char_type do_thousands_sep() const { return m_cThousandsSep; } + + virtual std::string do_grouping() const + { + // fix for issue 4: https://code.google.com/p/muparser/issues/detail?id=4 + // courtesy of Jens Bartsch + // original code: + // return std::string(1, (char)m_nGroup); + // new code: + return std::string(1, (char)(m_cThousandsSep > 0 ? m_nGroup : CHAR_MAX)); + } - int m_nGroup; - char_type m_cDecPoint; - char_type m_cThousandsSep; + private: + int m_nGroup; + char_type m_cDecPoint; + char_type m_cThousandsSep; }; public: @@ -137,4 +123,3 @@ } // namespace mu #endif - diff -Nru mstflint-4.17.0+1/ext_libs/muparser/muParserStack.h mstflint-4.21.0+1/ext_libs/muparser/muParserStack.h --- mstflint-4.17.0+1/ext_libs/muparser/muParserStack.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/muParserStack.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ /* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2004-2011 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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 MU_PARSER_STACK_H @@ -34,92 +34,74 @@ #include "muParserError.h" #include "muParserToken.h" -/** \file +/** \file \brief This file defines the stack used by muparser. */ namespace mu { +/** \brief Parser stack implementation. - /** \brief Parser stack implementation. + Stack implementation based on a std::stack. The behaviour of pop() had been + slightly changed in order to get an error code if the stack is empty. + The stack is used within the Parser both as a value stack and as an operator stack. - Stack implementation based on a std::stack. The behaviour of pop() had been - slightly changed in order to get an error code if the stack is empty. - The stack is used within the Parser both as a value stack and as an operator stack. - - \author (C) 2004-2011 Ingo Berg - */ - template - class ParserStack - { - private: - - /** \brief Type of the underlying stack implementation. */ - typedef std::stack > impl_type; - - impl_type m_Stack; ///< This is the actual stack. - - public: - - //--------------------------------------------------------------------------- - ParserStack() - :m_Stack() - {} - - //--------------------------------------------------------------------------- - virtual ~ParserStack() - {} - - //--------------------------------------------------------------------------- - /** \brief Pop a value from the stack. - - Unlike the standard implementation this function will return the value that - is going to be taken from the stack. - - \throw ParserException in case the stack is empty. - \sa pop(int &a_iErrc) - */ - TValueType pop() - { + \author (C) 2004-2011 Ingo Berg +*/ +template +class ParserStack +{ +private: + /** \brief Type of the underlying stack implementation. */ + typedef std::stack > impl_type; + + impl_type m_Stack; ///< This is the actual stack. + +public: + //--------------------------------------------------------------------------- + ParserStack() : m_Stack() {} + + //--------------------------------------------------------------------------- + virtual ~ParserStack() {} + + //--------------------------------------------------------------------------- + /** \brief Pop a value from the stack. + + Unlike the standard implementation this function will return the value that + is going to be taken from the stack. + + \throw ParserException in case the stack is empty. + \sa pop(int &a_iErrc) + */ + TValueType pop() + { if (empty()) - throw ParserError( _T("stack is empty.") ); + throw ParserError(_T("stack is empty.")); TValueType el = top(); m_Stack.pop(); return el; - } + } + + /** \brief Push an object into the stack. + + \param a_Val object to push into the stack. + \throw nothrow + */ + void push(const TValueType& a_Val) { m_Stack.push(a_Val); } + + /** \brief Return the number of stored elements. */ + unsigned size() const { return (unsigned)m_Stack.size(); } + + /** \brief Returns true if stack is empty false otherwise. */ + bool empty() const { return m_Stack.empty(); } - /** \brief Push an object into the stack. + /** \brief Return reference to the top object in the stack. - \param a_Val object to push into the stack. - \throw nothrow - */ - void push(const TValueType& a_Val) - { - m_Stack.push(a_Val); - } - - /** \brief Return the number of stored elements. */ - unsigned size() const - { - return (unsigned)m_Stack.size(); - } - - /** \brief Returns true if stack is empty false otherwise. */ - bool empty() const - { - return m_Stack.empty(); - } - - /** \brief Return reference to the top object in the stack. - - The top object is the one pushed most recently. - */ - TValueType& top() - { - return m_Stack.top(); - } - }; -} // namespace MathUtils + The top object is the one pushed most recently. + */ + TValueType& top() { return m_Stack.top(); } +}; +} // namespace mu #endif diff -Nru mstflint-4.17.0+1/ext_libs/muparser/muParserTemplateMagic.h mstflint-4.21.0+1/ext_libs/muparser/muParserTemplateMagic.h --- mstflint-4.17.0+1/ext_libs/muparser/muParserTemplateMagic.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/muParserTemplateMagic.h 2022-07-31 09:33:57.000000000 +0000 @@ -4,110 +4,108 @@ #include #include "muParserError.h" - namespace mu { - //----------------------------------------------------------------------------------------------- - // - // Compile time type detection - // - //----------------------------------------------------------------------------------------------- - - /** \brief A class singling out integer types at compile time using - template meta programming. - */ - template - struct TypeInfo - { +//----------------------------------------------------------------------------------------------- +// +// Compile time type detection +// +//----------------------------------------------------------------------------------------------- + +/** \brief A class singling out integer types at compile time using + template meta programming. +*/ +template +struct TypeInfo +{ static bool IsInteger() { return false; } - }; +}; + +template<> +struct TypeInfo +{ + static bool IsInteger() { return true; } +}; + +template<> +struct TypeInfo +{ + static bool IsInteger() { return true; } +}; + +template<> +struct TypeInfo +{ + static bool IsInteger() { return true; } +}; + +template<> +struct TypeInfo +{ + static bool IsInteger() { return true; } +}; - template<> - struct TypeInfo - { - static bool IsInteger() { return true; } - }; - - template<> - struct TypeInfo - { - static bool IsInteger() { return true; } - }; - - template<> - struct TypeInfo - { - static bool IsInteger() { return true; } - }; - - template<> - struct TypeInfo - { - static bool IsInteger() { return true; } - }; - - template<> - struct TypeInfo - { - static bool IsInteger() { return true; } - }; - - template<> - struct TypeInfo - { - static bool IsInteger() { return true; } - }; - - template<> - struct TypeInfo - { - static bool IsInteger() { return true; } - }; - - template<> - struct TypeInfo - { - static bool IsInteger() { return true; } - }; - - - //----------------------------------------------------------------------------------------------- - // - // Standard math functions with dummy overload for integer types - // - //----------------------------------------------------------------------------------------------- - - /** \brief A template class for providing wrappers for essential math functions. - - This template is spezialized for several types in order to provide a unified interface - for parser internal math function calls regardless of the data type. - */ - template - struct MathImpl - { - static T Sin(T v) { return sin(v); } - static T Cos(T v) { return cos(v); } - static T Tan(T v) { return tan(v); } - static T ASin(T v) { return asin(v); } - static T ACos(T v) { return acos(v); } - static T ATan(T v) { return atan(v); } +template<> +struct TypeInfo +{ + static bool IsInteger() { return true; } +}; + +template<> +struct TypeInfo +{ + static bool IsInteger() { return true; } +}; + +template<> +struct TypeInfo +{ + static bool IsInteger() { return true; } +}; + +template<> +struct TypeInfo +{ + static bool IsInteger() { return true; } +}; + +//----------------------------------------------------------------------------------------------- +// +// Standard math functions with dummy overload for integer types +// +//----------------------------------------------------------------------------------------------- + +/** \brief A template class for providing wrappers for essential math functions. + + This template is spezialized for several types in order to provide a unified interface + for parser internal math function calls regardless of the data type. +*/ +template +struct MathImpl +{ + static T Sin(T v) { return sin(v); } + static T Cos(T v) { return cos(v); } + static T Tan(T v) { return tan(v); } + static T ASin(T v) { return asin(v); } + static T ACos(T v) { return acos(v); } + static T ATan(T v) { return atan(v); } static T ATan2(T v1, T v2) { return atan2(v1, v2); } - static T Sinh(T v) { return sinh(v); } - static T Cosh(T v) { return cosh(v); } - static T Tanh(T v) { return tanh(v); } + static T Sinh(T v) { return sinh(v); } + static T Cosh(T v) { return cosh(v); } + static T Tanh(T v) { return tanh(v); } static T ASinh(T v) { return log(v + sqrt(v * v + 1)); } static T ACosh(T v) { return log(v + sqrt(v * v - 1)); } static T ATanh(T v) { return ((T)0.5 * log((1 + v) / (1 - v))); } - static T Log(T v) { return log(v); } - static T Log2(T v) { return log(v)/log((T)2); } // Logarithm base 2 - static T Log10(T v) { return log10(v); } // Logarithm base 10 - static T Exp(T v) { return exp(v); } - static T Abs(T v) { return (v>=0) ? v : -v; } - static T Sqrt(T v) { return sqrt(v); } - static T Rint(T v) { return floor(v + (T)0.5); } - static T Sign(T v) { return (T)((v<0) ? -1 : (v>0) ? 1 : 0); } + static T Log(T v) { return log(v); } + static T Log2(T v) { return log(v) / log((T)2); } // Logarithm base 2 + static T Log10(T v) { return log10(v); } // Logarithm base 10 + static T Exp(T v) { return exp(v); } + static T Abs(T v) { return (v >= 0) ? v : -v; } + static T Sqrt(T v) { return sqrt(v); } + static T Rint(T v) { return floor(v + (T)0.5); } + static T Sign(T v) { return (T)((v < 0) ? -1 : (v > 0) ? 1 : 0); } static T Pow(T v1, T v2) { return std::pow(v1, v2); } - }; -} +}; +} // namespace mu #endif diff -Nru mstflint-4.17.0+1/ext_libs/muparser/muParserTest.cpp mstflint-4.21.0+1/ext_libs/muparser/muParserTest.cpp --- mstflint-4.17.0+1/ext_libs/muparser/muParserTest.cpp 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/muParserTest.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ /* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2013 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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. */ #include "muParserTest.h" @@ -28,10 +28,10 @@ #include #include #include -#include +#include -#define PARSER_CONST_PI 3.141592653589793238462643 -#define PARSER_CONST_E 2.718281828459045235360287 +#define PARSER_CONST_PI 3.141592653589793238462643 +#define PARSER_CONST_E 2.718281828459045235360287 using namespace std; @@ -41,1177 +41,1170 @@ namespace mu { - namespace Test - { - int ParserTester::c_iCount = 0; - - //--------------------------------------------------------------------------------------------- - ParserTester::ParserTester() - :m_vTestFun() - { - AddTest(&ParserTester::TestNames); - AddTest(&ParserTester::TestSyntax); - AddTest(&ParserTester::TestPostFix); - AddTest(&ParserTester::TestInfixOprt); - AddTest(&ParserTester::TestVarConst); - AddTest(&ParserTester::TestMultiArg); - AddTest(&ParserTester::TestExpression); - AddTest(&ParserTester::TestIfThenElse); - AddTest(&ParserTester::TestInterface); - AddTest(&ParserTester::TestBinOprt); - AddTest(&ParserTester::TestException); - AddTest(&ParserTester::TestStrArg); - AddTest(&ParserTester::TestBulkMode); +namespace Test +{ +int ParserTester::c_iCount = 0; - ParserTester::c_iCount = 0; - } +//--------------------------------------------------------------------------------------------- +ParserTester::ParserTester() : m_vTestFun() +{ + AddTest(&ParserTester::TestNames); + AddTest(&ParserTester::TestSyntax); + AddTest(&ParserTester::TestPostFix); + AddTest(&ParserTester::TestInfixOprt); + AddTest(&ParserTester::TestVarConst); + AddTest(&ParserTester::TestMultiArg); + AddTest(&ParserTester::TestExpression); + AddTest(&ParserTester::TestIfThenElse); + AddTest(&ParserTester::TestInterface); + AddTest(&ParserTester::TestBinOprt); + AddTest(&ParserTester::TestException); + AddTest(&ParserTester::TestStrArg); + AddTest(&ParserTester::TestBulkMode); - //--------------------------------------------------------------------------------------------- - int ParserTester::IsHexVal(const char_type *a_szExpr, int *a_iPos, value_type *a_fVal) - { - if (a_szExpr[1]==0 || (a_szExpr[0]!='0' || a_szExpr[1]!='x') ) + ParserTester::c_iCount = 0; +} + +//--------------------------------------------------------------------------------------------- +int ParserTester::IsHexVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal) +{ + if (a_szExpr[1] == 0 || (a_szExpr[0] != '0' || a_szExpr[1] != 'x')) return 0; - unsigned iVal(0); + unsigned iVal(0); - // New code based on streams for UNICODE compliance: - stringstream_type::pos_type nPos(0); - stringstream_type ss(a_szExpr + 2); - ss >> std::hex >> iVal; - nPos = ss.tellg(); + // New code based on streams for UNICODE compliance: + stringstream_type::pos_type nPos(0); + stringstream_type ss(a_szExpr + 2); + ss >> std::hex >> iVal; + nPos = ss.tellg(); - if (nPos==(stringstream_type::pos_type)0) + if (nPos == (stringstream_type::pos_type)0) return 1; - *a_iPos += (int)(2 + nPos); - *a_fVal = (value_type)iVal; - return 1; - } - - //--------------------------------------------------------------------------------------------- - int ParserTester::TestInterface() - { - int iStat = 0; - mu::console() << _T("testing member functions..."); - - // Test RemoveVar - value_type afVal[3] = {1,2,3}; - Parser p; - - try - { - p.DefineVar( _T("a"), &afVal[0]); - p.DefineVar( _T("b"), &afVal[1]); - p.DefineVar( _T("c"), &afVal[2]); - p.SetExpr( _T("a+b+c") ); + *a_iPos += (int)(2 + nPos); + *a_fVal = (value_type)iVal; + return 1; +} + +//--------------------------------------------------------------------------------------------- +int ParserTester::TestInterface() +{ + int iStat = 0; + mu::console() << _T("testing member functions..."); + + // Test RemoveVar + value_type afVal[3] = {1, 2, 3}; + Parser p; + + try + { + p.DefineVar(_T("a"), &afVal[0]); + p.DefineVar(_T("b"), &afVal[1]); + p.DefineVar(_T("c"), &afVal[2]); + p.SetExpr(_T("a+b+c")); p.Eval(); - } - catch(...) - { - iStat += 1; // this is not supposed to happen - } - - try - { - p.RemoveVar( _T("c") ); + } + catch (...) + { + iStat += 1; // this is not supposed to happen + } + + try + { + p.RemoveVar(_T("c")); p.Eval(); - iStat += 1; // not supposed to reach this, nonexisting variable "c" deleted... - } - catch(...) - { + iStat += 1; // not supposed to reach this, nonexisting variable "c" deleted... + } + catch (...) + { // failure is expected... - } + } - if (iStat==0) + if (iStat == 0) mu::console() << _T("passed") << endl; - else + else mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - return iStat; - } + return iStat; +} - //--------------------------------------------------------------------------------------------- - int ParserTester::TestStrArg() - { - int iStat = 0; - mu::console() << _T("testing string arguments..."); - - iStat += EqnTest(_T("valueof(\"\")"), 123, true); // empty string arguments caused a crash - iStat += EqnTest(_T("valueof(\"aaa\")+valueof(\"bbb\") "), 246, true); - iStat += EqnTest(_T("2*(valueof(\"aaa\")-23)+valueof(\"bbb\")"), 323, true); - // use in expressions with variables - iStat += EqnTest(_T("a*(atof(\"10\")-b)"), 8, true); - iStat += EqnTest(_T("a-(atof(\"10\")*b)"), -19, true); - // string + numeric arguments - iStat += EqnTest(_T("strfun1(\"100\")"), 100, true); - iStat += EqnTest(_T("strfun2(\"100\",1)"), 101, true); - iStat += EqnTest(_T("strfun3(\"99\",1,2)"), 102, true); - // string constants - iStat += EqnTest(_T("atof(str1)+atof(str2)"), 3.33, true); +//--------------------------------------------------------------------------------------------- +int ParserTester::TestStrArg() +{ + int iStat = 0; + mu::console() << _T("testing string arguments..."); - if (iStat==0) + iStat += EqnTest(_T("valueof(\"\")"), 123, true); // empty string arguments caused a crash + iStat += EqnTest(_T("valueof(\"aaa\")+valueof(\"bbb\") "), 246, true); + iStat += EqnTest(_T("2*(valueof(\"aaa\")-23)+valueof(\"bbb\")"), 323, true); + // use in expressions with variables + iStat += EqnTest(_T("a*(atof(\"10\")-b)"), 8, true); + iStat += EqnTest(_T("a-(atof(\"10\")*b)"), -19, true); + // string + numeric arguments + iStat += EqnTest(_T("strfun1(\"100\")"), 100, true); + iStat += EqnTest(_T("strfun2(\"100\",1)"), 101, true); + iStat += EqnTest(_T("strfun3(\"99\",1,2)"), 102, true); + // string constants + iStat += EqnTest(_T("atof(str1)+atof(str2)"), 3.33, true); + + if (iStat == 0) mu::console() << _T("passed") << endl; - else + else mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - return iStat; - } - - //--------------------------------------------------------------------------------------------- - int ParserTester::TestBulkMode() - { - int iStat = 0; - mu::console() << _T("testing bulkmode..."); + return iStat; +} -#define EQN_TEST_BULK(EXPR, R1, R2, R3, R4, PASS) \ - { \ - double res[] = { R1, R2, R3, R4 }; \ - iStat += EqnTestBulk(_T(EXPR), res, (PASS)); \ - } +//--------------------------------------------------------------------------------------------- +int ParserTester::TestBulkMode() +{ + int iStat = 0; + mu::console() << _T("testing bulkmode..."); - // Bulk Variables for the test: - // a: 1,2,3,4 - // b: 2,2,2,2 - // c: 3,3,3,3 - // d: 5,4,3,2 - EQN_TEST_BULK("a", 1, 1, 1, 1, false) - EQN_TEST_BULK("a", 1, 2, 3, 4, true) - EQN_TEST_BULK("b=a", 1, 2, 3, 4, true) - EQN_TEST_BULK("b=a, b*10", 10, 20, 30, 40, true) - EQN_TEST_BULK("b=a, b*10, a", 1, 2, 3, 4, true) - EQN_TEST_BULK("a+b", 3, 4, 5, 6, true) - EQN_TEST_BULK("c*(a+b)", 9, 12, 15, 18, true) +#define EQN_TEST_BULK(EXPR, R1, R2, R3, R4, PASS) \ + { \ + double res[] = {R1, R2, R3, R4}; \ + iStat += EqnTestBulk(_T(EXPR), res, (PASS)); \ + } + + // Bulk Variables for the test: + // a: 1,2,3,4 + // b: 2,2,2,2 + // c: 3,3,3,3 + // d: 5,4,3,2 + EQN_TEST_BULK("a", 1, 1, 1, 1, false) + EQN_TEST_BULK("a", 1, 2, 3, 4, true) + EQN_TEST_BULK("b=a", 1, 2, 3, 4, true) + EQN_TEST_BULK("b=a, b*10", 10, 20, 30, 40, true) + EQN_TEST_BULK("b=a, b*10, a", 1, 2, 3, 4, true) + EQN_TEST_BULK("a+b", 3, 4, 5, 6, true) + EQN_TEST_BULK("c*(a+b)", 9, 12, 15, 18, true) #undef EQN_TEST_BULK - if (iStat == 0) - mu::console() << _T("passed") << endl; - else - mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - - return iStat; - } - - //--------------------------------------------------------------------------------------------- - int ParserTester::TestBinOprt() - { - int iStat = 0; - mu::console() << _T("testing binary operators..."); - - // built in operators - // xor operator - - iStat += EqnTest(_T("a++b"), 3, true); - iStat += EqnTest(_T("a ++ b"), 3, true); - iStat += EqnTest(_T("1++2"), 3, true); - iStat += EqnTest(_T("1 ++ 2"), 3, true); - iStat += EqnTest(_T("a add b"), 3, true); - iStat += EqnTest(_T("1 add 2"), 3, true); - iStat += EqnTest(_T("aa"), 1, true); - iStat += EqnTest(_T("a>a"), 0, true); - iStat += EqnTest(_T("aa"), 0, true); - iStat += EqnTest(_T("a<=a"), 1, true); - iStat += EqnTest(_T("a<=b"), 1, true); - iStat += EqnTest(_T("b<=a"), 0, true); - iStat += EqnTest(_T("a>=a"), 1, true); - iStat += EqnTest(_T("b>=a"), 1, true); - iStat += EqnTest(_T("a>=b"), 0, true); - - // Test logical operators, especially if user defined "&" and the internal "&&" collide - iStat += EqnTest(_T("1 && 1"), 1, true); - iStat += EqnTest(_T("1 && 0"), 0, true); - iStat += EqnTest(_T("(aa)"), 1, true); - iStat += EqnTest(_T("(ab)"), 0, true); - //iStat += EqnTest(_T("12 and 255"), 12, true); - //iStat += EqnTest(_T("12 and 0"), 0, true); - iStat += EqnTest(_T("12 & 255"), 12, true); - iStat += EqnTest(_T("12 & 0"), 0, true); - iStat += EqnTest(_T("12&255"), 12, true); - iStat += EqnTest(_T("12&0"), 0, true); - - // Assignment operator - iStat += EqnTest(_T("a = b"), 2, true); - iStat += EqnTest(_T("a = sin(b)"), 0.909297, true); - iStat += EqnTest(_T("a = 1+sin(b)"), 1.909297, true); - iStat += EqnTest(_T("(a=b)*2"), 4, true); - iStat += EqnTest(_T("2*(a=b)"), 4, true); - iStat += EqnTest(_T("2*(a=b+1)"), 6, true); - iStat += EqnTest(_T("(a=b+1)*2"), 6, true); - iStat += EqnTest(_T("a=c, a*10"), 30, true); - - iStat += EqnTest(_T("2^2^3"), 256, true); - iStat += EqnTest(_T("1/2/3"), 1.0/6.0, true); - - // reference: http://www.wolframalpha.com/input/?i=3%2B4*2%2F%281-5%29^2^3 - iStat += EqnTest(_T("3+4*2/(1-5)^2^3"), 3.0001220703125, true); - - // Test user defined binary operators - iStat += EqnTestInt(_T("1 | 2"), 3, true); - iStat += EqnTestInt(_T("1 || 2"), 1, true); - iStat += EqnTestInt(_T("123 & 456"), 72, true); - iStat += EqnTestInt(_T("(123 & 456) % 10"), 2, true); - iStat += EqnTestInt(_T("1 && 0"), 0, true); - iStat += EqnTestInt(_T("123 && 456"), 1, true); - iStat += EqnTestInt(_T("1 << 3"), 8, true); - iStat += EqnTestInt(_T("8 >> 3"), 1, true); - iStat += EqnTestInt(_T("9 / 4"), 2, true); - iStat += EqnTestInt(_T("9 % 4"), 1, true); - iStat += EqnTestInt(_T("if(5%2,1,0)"), 1, true); - iStat += EqnTestInt(_T("if(4%2,1,0)"), 0, true); - iStat += EqnTestInt(_T("-10+1"), -9, true); - iStat += EqnTestInt(_T("1+2*3"), 7, true); - iStat += EqnTestInt(_T("const1 != const2"), 1, true); - iStat += EqnTestInt(_T("const1 != const2"), 0, false); - iStat += EqnTestInt(_T("const1 == const2"), 0, true); - iStat += EqnTestInt(_T("const1 == 1"), 1, true); - iStat += EqnTestInt(_T("10*(const1 == 1)"), 10, true); - iStat += EqnTestInt(_T("2*(const1 | const2)"), 6, true); - iStat += EqnTestInt(_T("2*(const1 | const2)"), 7, false); - iStat += EqnTestInt(_T("const1 < const2"), 1, true); - iStat += EqnTestInt(_T("const2 > const1"), 1, true); - iStat += EqnTestInt(_T("const1 <= 1"), 1, true); - iStat += EqnTestInt(_T("const2 >= 2"), 1, true); - iStat += EqnTestInt(_T("2*(const1 + const2)"), 6, true); - iStat += EqnTestInt(_T("2*(const1 - const2)"), -2, true); - iStat += EqnTestInt(_T("a != b"), 1, true); - iStat += EqnTestInt(_T("a != b"), 0, false); - iStat += EqnTestInt(_T("a == b"), 0, true); - iStat += EqnTestInt(_T("a == 1"), 1, true); - iStat += EqnTestInt(_T("10*(a == 1)"), 10, true); - iStat += EqnTestInt(_T("2*(a | b)"), 6, true); - iStat += EqnTestInt(_T("2*(a | b)"), 7, false); - iStat += EqnTestInt(_T("a < b"), 1, true); - iStat += EqnTestInt(_T("b > a"), 1, true); - iStat += EqnTestInt(_T("a <= 1"), 1, true); - iStat += EqnTestInt(_T("b >= 2"), 1, true); - iStat += EqnTestInt(_T("2*(a + b)"), 6, true); - iStat += EqnTestInt(_T("2*(a - b)"), -2, true); - iStat += EqnTestInt(_T("a + (a << b)"), 5, true); - iStat += EqnTestInt(_T("-2^2"), -4, true); - iStat += EqnTestInt(_T("3--a"), 4, true); - iStat += EqnTestInt(_T("3+-3^2"), -6, true); - - // Test reading of hex values: - iStat += EqnTestInt(_T("0xff"), 255, true); - iStat += EqnTestInt(_T("10+0xff"), 265, true); - iStat += EqnTestInt(_T("0xff+10"), 265, true); - iStat += EqnTestInt(_T("10*0xff"), 2550, true); - iStat += EqnTestInt(_T("0xff*10"), 2550, true); - iStat += EqnTestInt(_T("10+0xff+1"), 266, true); - iStat += EqnTestInt(_T("1+0xff+10"), 266, true); - -// incorrect: '^' is yor here, not power -// iStat += EqnTestInt("-(1+2)^2", -9, true); -// iStat += EqnTestInt("-1^3", -1, true); - - // Test precedence - // a=1, b=2, c=3 - iStat += EqnTestInt(_T("a + b * c"), 7, true); - iStat += EqnTestInt(_T("a * b + c"), 5, true); - iStat += EqnTestInt(_T("a10"), 0, true); - iStat += EqnTestInt(_T("aa"), 1, true); + iStat += EqnTest(_T("a>a"), 0, true); + iStat += EqnTest(_T("aa"), 0, true); + iStat += EqnTest(_T("a<=a"), 1, true); + iStat += EqnTest(_T("a<=b"), 1, true); + iStat += EqnTest(_T("b<=a"), 0, true); + iStat += EqnTest(_T("a>=a"), 1, true); + iStat += EqnTest(_T("b>=a"), 1, true); + iStat += EqnTest(_T("a>=b"), 0, true); + + // Test logical operators, especially if user defined "&" and the internal "&&" collide + iStat += EqnTest(_T("1 && 1"), 1, true); + iStat += EqnTest(_T("1 && 0"), 0, true); + iStat += EqnTest(_T("(aa)"), 1, true); + iStat += EqnTest(_T("(ab)"), 0, true); + // iStat += EqnTest(_T("12 and 255"), 12, true); + // iStat += EqnTest(_T("12 and 0"), 0, true); + iStat += EqnTest(_T("12 & 255"), 12, true); + iStat += EqnTest(_T("12 & 0"), 0, true); + iStat += EqnTest(_T("12&255"), 12, true); + iStat += EqnTest(_T("12&0"), 0, true); + + // Assignment operator + iStat += EqnTest(_T("a = b"), 2, true); + iStat += EqnTest(_T("a = sin(b)"), 0.909297, true); + iStat += EqnTest(_T("a = 1+sin(b)"), 1.909297, true); + iStat += EqnTest(_T("(a=b)*2"), 4, true); + iStat += EqnTest(_T("2*(a=b)"), 4, true); + iStat += EqnTest(_T("2*(a=b+1)"), 6, true); + iStat += EqnTest(_T("(a=b+1)*2"), 6, true); + iStat += EqnTest(_T("a=c, a*10"), 30, true); + + iStat += EqnTest(_T("2^2^3"), 256, true); + iStat += EqnTest(_T("1/2/3"), 1.0 / 6.0, true); + + // reference: http://www.wolframalpha.com/input/?i=3%2B4*2%2F%281-5%29^2^3 + iStat += EqnTest(_T("3+4*2/(1-5)^2^3"), 3.0001220703125, true); + + // Test user defined binary operators + iStat += EqnTestInt(_T("1 | 2"), 3, true); + iStat += EqnTestInt(_T("1 || 2"), 1, true); + iStat += EqnTestInt(_T("123 & 456"), 72, true); + iStat += EqnTestInt(_T("(123 & 456) % 10"), 2, true); + iStat += EqnTestInt(_T("1 && 0"), 0, true); + iStat += EqnTestInt(_T("123 && 456"), 1, true); + iStat += EqnTestInt(_T("1 << 3"), 8, true); + iStat += EqnTestInt(_T("8 >> 3"), 1, true); + iStat += EqnTestInt(_T("9 / 4"), 2, true); + iStat += EqnTestInt(_T("9 % 4"), 1, true); + iStat += EqnTestInt(_T("if(5%2,1,0)"), 1, true); + iStat += EqnTestInt(_T("if(4%2,1,0)"), 0, true); + iStat += EqnTestInt(_T("-10+1"), -9, true); + iStat += EqnTestInt(_T("1+2*3"), 7, true); + iStat += EqnTestInt(_T("const1 != const2"), 1, true); + iStat += EqnTestInt(_T("const1 != const2"), 0, false); + iStat += EqnTestInt(_T("const1 == const2"), 0, true); + iStat += EqnTestInt(_T("const1 == 1"), 1, true); + iStat += EqnTestInt(_T("10*(const1 == 1)"), 10, true); + iStat += EqnTestInt(_T("2*(const1 | const2)"), 6, true); + iStat += EqnTestInt(_T("2*(const1 | const2)"), 7, false); + iStat += EqnTestInt(_T("const1 < const2"), 1, true); + iStat += EqnTestInt(_T("const2 > const1"), 1, true); + iStat += EqnTestInt(_T("const1 <= 1"), 1, true); + iStat += EqnTestInt(_T("const2 >= 2"), 1, true); + iStat += EqnTestInt(_T("2*(const1 + const2)"), 6, true); + iStat += EqnTestInt(_T("2*(const1 - const2)"), -2, true); + iStat += EqnTestInt(_T("a != b"), 1, true); + iStat += EqnTestInt(_T("a != b"), 0, false); + iStat += EqnTestInt(_T("a == b"), 0, true); + iStat += EqnTestInt(_T("a == 1"), 1, true); + iStat += EqnTestInt(_T("10*(a == 1)"), 10, true); + iStat += EqnTestInt(_T("2*(a | b)"), 6, true); + iStat += EqnTestInt(_T("2*(a | b)"), 7, false); + iStat += EqnTestInt(_T("a < b"), 1, true); + iStat += EqnTestInt(_T("b > a"), 1, true); + iStat += EqnTestInt(_T("a <= 1"), 1, true); + iStat += EqnTestInt(_T("b >= 2"), 1, true); + iStat += EqnTestInt(_T("2*(a + b)"), 6, true); + iStat += EqnTestInt(_T("2*(a - b)"), -2, true); + iStat += EqnTestInt(_T("a + (a << b)"), 5, true); + iStat += EqnTestInt(_T("-2^2"), -4, true); + iStat += EqnTestInt(_T("3--a"), 4, true); + iStat += EqnTestInt(_T("3+-3^2"), -6, true); + + // Test reading of hex values: + iStat += EqnTestInt(_T("0xff"), 255, true); + iStat += EqnTestInt(_T("10+0xff"), 265, true); + iStat += EqnTestInt(_T("0xff+10"), 265, true); + iStat += EqnTestInt(_T("10*0xff"), 2550, true); + iStat += EqnTestInt(_T("0xff*10"), 2550, true); + iStat += EqnTestInt(_T("10+0xff+1"), 266, true); + iStat += EqnTestInt(_T("1+0xff+10"), 266, true); + + // incorrect: '^' is yor here, not power + // iStat += EqnTestInt("-(1+2)^2", -9, true); + // iStat += EqnTestInt("-1^3", -1, true); + + // Test precedence + // a=1, b=2, c=3 + iStat += EqnTestInt(_T("a + b * c"), 7, true); + iStat += EqnTestInt(_T("a * b + c"), 5, true); + iStat += EqnTestInt(_T("a10"), 0, true); + iStat += EqnTestInt(_T("a"), f1of1) - PARSER_THROWCHECK(PostfixOprt, true, _T("?<"), f1of1) - PARSER_THROWCHECK(PostfixOprt, true, _T("**"), f1of1) - PARSER_THROWCHECK(PostfixOprt, true, _T("xor"), f1of1) - PARSER_THROWCHECK(PostfixOprt, true, _T("and"), f1of1) - PARSER_THROWCHECK(PostfixOprt, true, _T("or"), f1of1) - PARSER_THROWCHECK(PostfixOprt, true, _T("not"), f1of1) - PARSER_THROWCHECK(PostfixOprt, true, _T("!"), f1of1) - // Binary operator - // The following must fail with builtin operators activated - // p.EnableBuiltInOp(true); -> this is the default - p.ClearPostfixOprt(); - PARSER_THROWCHECK(Oprt, false, _T("+"), f1of2) - PARSER_THROWCHECK(Oprt, false, _T("-"), f1of2) - PARSER_THROWCHECK(Oprt, false, _T("*"), f1of2) - PARSER_THROWCHECK(Oprt, false, _T("/"), f1of2) - PARSER_THROWCHECK(Oprt, false, _T("^"), f1of2) - PARSER_THROWCHECK(Oprt, false, _T("&&"), f1of2) - PARSER_THROWCHECK(Oprt, false, _T("||"), f1of2) - // without activated built in operators it should work - p.EnableBuiltInOprt(false); - PARSER_THROWCHECK(Oprt, true, _T("+"), f1of2) - PARSER_THROWCHECK(Oprt, true, _T("-"), f1of2) - PARSER_THROWCHECK(Oprt, true, _T("*"), f1of2) - PARSER_THROWCHECK(Oprt, true, _T("/"), f1of2) - PARSER_THROWCHECK(Oprt, true, _T("^"), f1of2) - PARSER_THROWCHECK(Oprt, true, _T("&&"), f1of2) - PARSER_THROWCHECK(Oprt, true, _T("||"), f1of2) - #undef PARSER_THROWCHECK +//--------------------------------------------------------------------------------------------- +/** \brief Check muParser name restriction enforcement. */ +int ParserTester::TestNames() +{ + int iStat = 0, iErr = 0; + + mu::console() << "testing name restriction enforcement..."; + + Parser p; - if (iStat==0) +#define PARSER_THROWCHECK(DOMAIN, FAIL, EXPR, ARG) \ + iErr = 0; \ + ParserTester::c_iCount++; \ + try \ + { \ + p.Define##DOMAIN(EXPR, ARG); \ + } \ + catch (Parser::exception_type&) \ + { \ + iErr = (FAIL == false) ? 0 : 1; \ + } \ + iStat += iErr; + + // constant names + PARSER_THROWCHECK(Const, false, _T("0a"), 1) + PARSER_THROWCHECK(Const, false, _T("9a"), 1) + PARSER_THROWCHECK(Const, false, _T("+a"), 1) + PARSER_THROWCHECK(Const, false, _T("-a"), 1) + PARSER_THROWCHECK(Const, false, _T("a-"), 1) + PARSER_THROWCHECK(Const, false, _T("a*"), 1) + PARSER_THROWCHECK(Const, false, _T("a?"), 1) + PARSER_THROWCHECK(Const, true, _T("a"), 1) + PARSER_THROWCHECK(Const, true, _T("a_min"), 1) + PARSER_THROWCHECK(Const, true, _T("a_min0"), 1) + PARSER_THROWCHECK(Const, true, _T("a_min9"), 1) + // variable names + value_type a; + p.ClearConst(); + PARSER_THROWCHECK(Var, false, _T("123abc"), &a) + PARSER_THROWCHECK(Var, false, _T("9a"), &a) + PARSER_THROWCHECK(Var, false, _T("0a"), &a) + PARSER_THROWCHECK(Var, false, _T("+a"), &a) + PARSER_THROWCHECK(Var, false, _T("-a"), &a) + PARSER_THROWCHECK(Var, false, _T("?a"), &a) + PARSER_THROWCHECK(Var, false, _T("!a"), &a) + PARSER_THROWCHECK(Var, false, _T("a+"), &a) + PARSER_THROWCHECK(Var, false, _T("a-"), &a) + PARSER_THROWCHECK(Var, false, _T("a*"), &a) + PARSER_THROWCHECK(Var, false, _T("a?"), &a) + PARSER_THROWCHECK(Var, true, _T("a"), &a) + PARSER_THROWCHECK(Var, true, _T("a_min"), &a) + PARSER_THROWCHECK(Var, true, _T("a_min0"), &a) + PARSER_THROWCHECK(Var, true, _T("a_min9"), &a) + PARSER_THROWCHECK(Var, false, _T("a_min9"), 0) + // Postfix operators + // fail + PARSER_THROWCHECK(PostfixOprt, false, _T("(k"), f1of1) + PARSER_THROWCHECK(PostfixOprt, false, _T("9+"), f1of1) + PARSER_THROWCHECK(PostfixOprt, false, _T("+"), 0) + // pass + PARSER_THROWCHECK(PostfixOprt, true, _T("-a"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("?a"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("_"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("#"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("&&"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("||"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("&"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("|"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("++"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("--"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("?>"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("?<"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("**"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("xor"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("and"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("or"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("not"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("!"), f1of1) + // Binary operator + // The following must fail with builtin operators activated + // p.EnableBuiltInOp(true); -> this is the default + p.ClearPostfixOprt(); + PARSER_THROWCHECK(Oprt, false, _T("+"), f1of2) + PARSER_THROWCHECK(Oprt, false, _T("-"), f1of2) + PARSER_THROWCHECK(Oprt, false, _T("*"), f1of2) + PARSER_THROWCHECK(Oprt, false, _T("/"), f1of2) + PARSER_THROWCHECK(Oprt, false, _T("^"), f1of2) + PARSER_THROWCHECK(Oprt, false, _T("&&"), f1of2) + PARSER_THROWCHECK(Oprt, false, _T("||"), f1of2) + // without activated built in operators it should work + p.EnableBuiltInOprt(false); + PARSER_THROWCHECK(Oprt, true, _T("+"), f1of2) + PARSER_THROWCHECK(Oprt, true, _T("-"), f1of2) + PARSER_THROWCHECK(Oprt, true, _T("*"), f1of2) + PARSER_THROWCHECK(Oprt, true, _T("/"), f1of2) + PARSER_THROWCHECK(Oprt, true, _T("^"), f1of2) + PARSER_THROWCHECK(Oprt, true, _T("&&"), f1of2) + PARSER_THROWCHECK(Oprt, true, _T("||"), f1of2) +#undef PARSER_THROWCHECK + + if (iStat == 0) mu::console() << _T("passed") << endl; - else + else mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - return iStat; - } + return iStat; +} - //--------------------------------------------------------------------------- - int ParserTester::TestSyntax() - { - int iStat = 0; - mu::console() << _T("testing syntax engine..."); +//--------------------------------------------------------------------------- +int ParserTester::TestSyntax() +{ + int iStat = 0; + mu::console() << _T("testing syntax engine..."); - iStat += ThrowTest(_T("1,"), ecUNEXPECTED_EOF); // incomplete hex definition - iStat += ThrowTest(_T("a,"), ecUNEXPECTED_EOF); // incomplete hex definition - iStat += ThrowTest(_T("sin(8),"), ecUNEXPECTED_EOF); // incomplete hex definition - iStat += ThrowTest(_T("(sin(8)),"), ecUNEXPECTED_EOF); // incomplete hex definition - iStat += ThrowTest(_T("a{m},"), ecUNEXPECTED_EOF); // incomplete hex definition - - iStat += EqnTest(_T("(1+ 2*a)"), 3, true); // Spaces within formula - iStat += EqnTest(_T("sqrt((4))"), 2, true); // Multiple brackets - iStat += EqnTest(_T("sqrt((2)+2)"), 2, true);// Multiple brackets - iStat += EqnTest(_T("sqrt(2+(2))"), 2, true);// Multiple brackets - iStat += EqnTest(_T("sqrt(a+(3))"), 2, true);// Multiple brackets - iStat += EqnTest(_T("sqrt((3)+a)"), 2, true);// Multiple brackets - iStat += EqnTest(_T("order(1,2)"), 1, true); // May not cause name collision with operator "or" - iStat += EqnTest(_T("(2+"), 0, false); // missing closing bracket - iStat += EqnTest(_T("2++4"), 0, false); // unexpected operator - iStat += EqnTest(_T("2+-4"), 0, false); // unexpected operator - iStat += EqnTest(_T("(2+)"), 0, false); // unexpected closing bracket - iStat += EqnTest(_T("--2"), 0, false); // double sign - iStat += EqnTest(_T("ksdfj"), 0, false); // unknown token - iStat += EqnTest(_T("()"), 0, false); // empty bracket without a function - iStat += EqnTest(_T("5+()"), 0, false); // empty bracket without a function - iStat += EqnTest(_T("sin(cos)"), 0, false); // unexpected function - iStat += EqnTest(_T("5t6"), 0, false); // unknown token - iStat += EqnTest(_T("5 t 6"), 0, false); // unknown token - iStat += EqnTest(_T("8*"), 0, false); // unexpected end of formula - iStat += EqnTest(_T(",3"), 0, false); // unexpected comma - iStat += EqnTest(_T("3,5"), 0, false); // unexpected comma - iStat += EqnTest(_T("sin(8,8)"), 0, false); // too many function args - iStat += EqnTest(_T("(7,8)"), 0, false); // too many function args - iStat += EqnTest(_T("sin)"), 0, false); // unexpected closing bracket - iStat += EqnTest(_T("a)"), 0, false); // unexpected closing bracket - iStat += EqnTest(_T("pi)"), 0, false); // unexpected closing bracket - iStat += EqnTest(_T("sin(())"), 0, false); // unexpected closing bracket - iStat += EqnTest(_T("sin()"), 0, false); // unexpected closing bracket + iStat += ThrowTest(_T("1,"), ecUNEXPECTED_EOF); // incomplete hex definition + iStat += ThrowTest(_T("a,"), ecUNEXPECTED_EOF); // incomplete hex definition + iStat += ThrowTest(_T("sin(8),"), ecUNEXPECTED_EOF); // incomplete hex definition + iStat += ThrowTest(_T("(sin(8)),"), ecUNEXPECTED_EOF); // incomplete hex definition + iStat += ThrowTest(_T("a{m},"), ecUNEXPECTED_EOF); // incomplete hex definition + + iStat += EqnTest(_T("(1+ 2*a)"), 3, true); // Spaces within formula + iStat += EqnTest(_T("sqrt((4))"), 2, true); // Multiple brackets + iStat += EqnTest(_T("sqrt((2)+2)"), 2, true); // Multiple brackets + iStat += EqnTest(_T("sqrt(2+(2))"), 2, true); // Multiple brackets + iStat += EqnTest(_T("sqrt(a+(3))"), 2, true); // Multiple brackets + iStat += EqnTest(_T("sqrt((3)+a)"), 2, true); // Multiple brackets + iStat += EqnTest(_T("order(1,2)"), 1, true); // May not cause name collision with operator "or" + iStat += EqnTest(_T("(2+"), 0, false); // missing closing bracket + iStat += EqnTest(_T("2++4"), 0, false); // unexpected operator + iStat += EqnTest(_T("2+-4"), 0, false); // unexpected operator + iStat += EqnTest(_T("(2+)"), 0, false); // unexpected closing bracket + iStat += EqnTest(_T("--2"), 0, false); // double sign + iStat += EqnTest(_T("ksdfj"), 0, false); // unknown token + iStat += EqnTest(_T("()"), 0, false); // empty bracket without a function + iStat += EqnTest(_T("5+()"), 0, false); // empty bracket without a function + iStat += EqnTest(_T("sin(cos)"), 0, false); // unexpected function + iStat += EqnTest(_T("5t6"), 0, false); // unknown token + iStat += EqnTest(_T("5 t 6"), 0, false); // unknown token + iStat += EqnTest(_T("8*"), 0, false); // unexpected end of formula + iStat += EqnTest(_T(",3"), 0, false); // unexpected comma + iStat += EqnTest(_T("3,5"), 0, false); // unexpected comma + iStat += EqnTest(_T("sin(8,8)"), 0, false); // too many function args + iStat += EqnTest(_T("(7,8)"), 0, false); // too many function args + iStat += EqnTest(_T("sin)"), 0, false); // unexpected closing bracket + iStat += EqnTest(_T("a)"), 0, false); // unexpected closing bracket + iStat += EqnTest(_T("pi)"), 0, false); // unexpected closing bracket + iStat += EqnTest(_T("sin(())"), 0, false); // unexpected closing bracket + iStat += EqnTest(_T("sin()"), 0, false); // unexpected closing bracket - if (iStat==0) + if (iStat == 0) mu::console() << _T("passed") << endl; - else + else mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - return iStat; - } + return iStat; +} - //--------------------------------------------------------------------------- - int ParserTester::TestVarConst() - { - int iStat = 0; - mu::console() << _T("testing variable/constant detection..."); +//--------------------------------------------------------------------------- +int ParserTester::TestVarConst() +{ + int iStat = 0; + mu::console() << _T("testing variable/constant detection..."); + + // Test if the result changes when a variable changes + iStat += EqnTestWithVarChange(_T("a"), 1, 1, 2, 2); + iStat += EqnTestWithVarChange(_T("2*a"), 2, 4, 3, 6); + + // distinguish constants with same basename + iStat += EqnTest(_T("const"), 1, true); + iStat += EqnTest(_T("const1"), 2, true); + iStat += EqnTest(_T("const2"), 3, true); + iStat += EqnTest(_T("2*const"), 2, true); + iStat += EqnTest(_T("2*const1"), 4, true); + iStat += EqnTest(_T("2*const2"), 6, true); + iStat += EqnTest(_T("2*const+1"), 3, true); + iStat += EqnTest(_T("2*const1+1"), 5, true); + iStat += EqnTest(_T("2*const2+1"), 7, true); + iStat += EqnTest(_T("const"), 0, false); + iStat += EqnTest(_T("const1"), 0, false); + iStat += EqnTest(_T("const2"), 0, false); + + // distinguish variables with same basename + iStat += EqnTest(_T("a"), 1, true); + iStat += EqnTest(_T("aa"), 2, true); + iStat += EqnTest(_T("2*a"), 2, true); + iStat += EqnTest(_T("2*aa"), 4, true); + iStat += EqnTest(_T("2*a-1"), 1, true); + iStat += EqnTest(_T("2*aa-1"), 3, true); + + // custom value recognition + iStat += EqnTest(_T("0xff"), 255, true); + iStat += EqnTest(_T("0x97 + 0xff"), 406, true); - // Test if the result changes when a variable changes - iStat += EqnTestWithVarChange( _T("a"), 1, 1, 2, 2 ); - iStat += EqnTestWithVarChange( _T("2*a"), 2, 4, 3, 6 ); - - // distinguish constants with same basename - iStat += EqnTest( _T("const"), 1, true); - iStat += EqnTest( _T("const1"), 2, true); - iStat += EqnTest( _T("const2"), 3, true); - iStat += EqnTest( _T("2*const"), 2, true); - iStat += EqnTest( _T("2*const1"), 4, true); - iStat += EqnTest( _T("2*const2"), 6, true); - iStat += EqnTest( _T("2*const+1"), 3, true); - iStat += EqnTest( _T("2*const1+1"), 5, true); - iStat += EqnTest( _T("2*const2+1"), 7, true); - iStat += EqnTest( _T("const"), 0, false); - iStat += EqnTest( _T("const1"), 0, false); - iStat += EqnTest( _T("const2"), 0, false); - - // distinguish variables with same basename - iStat += EqnTest( _T("a"), 1, true); - iStat += EqnTest( _T("aa"), 2, true); - iStat += EqnTest( _T("2*a"), 2, true); - iStat += EqnTest( _T("2*aa"), 4, true); - iStat += EqnTest( _T("2*a-1"), 1, true); - iStat += EqnTest( _T("2*aa-1"), 3, true); - - // custom value recognition - iStat += EqnTest( _T("0xff"), 255, true); - iStat += EqnTest( _T("0x97 + 0xff"), 406, true); - - // Finally test querying of used variables - try - { + // Finally test querying of used variables + try + { int idx; mu::Parser p; - mu::value_type vVarVal[] = { 1, 2, 3, 4, 5}; - p.DefineVar( _T("a"), &vVarVal[0]); - p.DefineVar( _T("b"), &vVarVal[1]); - p.DefineVar( _T("c"), &vVarVal[2]); - p.DefineVar( _T("d"), &vVarVal[3]); - p.DefineVar( _T("e"), &vVarVal[4]); + mu::value_type vVarVal[] = {1, 2, 3, 4, 5}; + p.DefineVar(_T("a"), &vVarVal[0]); + p.DefineVar(_T("b"), &vVarVal[1]); + p.DefineVar(_T("c"), &vVarVal[2]); + p.DefineVar(_T("d"), &vVarVal[3]); + p.DefineVar(_T("e"), &vVarVal[4]); // Test lookup of defined variables // 4 used variables - p.SetExpr( _T("a+b+c+d") ); + p.SetExpr(_T("a+b+c+d")); mu::varmap_type UsedVar = p.GetUsedVar(); int iCount = (int)UsedVar.size(); - if (iCount!=4) - throw false; - - // the next check will fail if the parser + if (iCount != 4) + throw false; + + // the next check will fail if the parser // erroneously creates new variables internally - if (p.GetVar().size()!=5) - throw false; + if (p.GetVar().size() != 5) + throw false; mu::varmap_type::const_iterator item = UsedVar.begin(); - for (idx=0; item!=UsedVar.end(); ++item) + for (idx = 0; item != UsedVar.end(); ++item) { - if (&vVarVal[idx++]!=item->second) - throw false; + if (&vVarVal[idx++] != item->second) + throw false; } // Test lookup of undefined variables - p.SetExpr( _T("undef1+undef2+undef3") ); + p.SetExpr(_T("undef1+undef2+undef3")); UsedVar = p.GetUsedVar(); iCount = (int)UsedVar.size(); - if (iCount!=3) - throw false; + if (iCount != 3) + throw false; - // the next check will fail if the parser + // the next check will fail if the parser // erroneously creates new variables internally - if (p.GetVar().size()!=5) - throw false; + if (p.GetVar().size() != 5) + throw false; - for (item = UsedVar.begin(); item!=UsedVar.end(); ++item) + for (item = UsedVar.begin(); item != UsedVar.end(); ++item) { - if (item->second!=0) - throw false; // all pointers to undefined variables must be null + if (item->second != 0) + throw false; // all pointers to undefined variables must be null } // 1 used variables - p.SetExpr( _T("a+b") ); + p.SetExpr(_T("a+b")); UsedVar = p.GetUsedVar(); iCount = (int)UsedVar.size(); - if (iCount!=2) throw false; + if (iCount != 2) + throw false; item = UsedVar.begin(); - for (idx=0; item!=UsedVar.end(); ++item) - if (&vVarVal[idx++]!=item->second) throw false; - - } - catch(...) - { + for (idx = 0; item != UsedVar.end(); ++item) + if (&vVarVal[idx++] != item->second) + throw false; + } + catch (...) + { iStat += 1; - } + } - if (iStat==0) + if (iStat == 0) mu::console() << _T("passed") << endl; - else + else mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - return iStat; - } + return iStat; +} - //--------------------------------------------------------------------------- - int ParserTester::TestMultiArg() - { - int iStat = 0; - mu::console() << _T("testing multiarg functions..."); - - // Compound expressions - iStat += EqnTest( _T("1,2,3"), 3, true); - iStat += EqnTest( _T("a,b,c"), 3, true); - iStat += EqnTest( _T("a=10,b=20,c=a*b"), 200, true); - iStat += EqnTest( _T("1,\n2,\n3"), 3, true); - iStat += EqnTest( _T("a,\nb,\nc"), 3, true); - iStat += EqnTest( _T("a=10,\nb=20,\nc=a*b"), 200, true); - iStat += EqnTest( _T("1,\r\n2,\r\n3"), 3, true); - iStat += EqnTest( _T("a,\r\nb,\r\nc"), 3, true); - iStat += EqnTest( _T("a=10,\r\nb=20,\r\nc=a*b"), 200, true); - - // picking the right argument - iStat += EqnTest( _T("f1of1(1)"), 1, true); - iStat += EqnTest( _T("f1of2(1, 2)"), 1, true); - iStat += EqnTest( _T("f2of2(1, 2)"), 2, true); - iStat += EqnTest( _T("f1of3(1, 2, 3)"), 1, true); - iStat += EqnTest( _T("f2of3(1, 2, 3)"), 2, true); - iStat += EqnTest( _T("f3of3(1, 2, 3)"), 3, true); - iStat += EqnTest( _T("f1of4(1, 2, 3, 4)"), 1, true); - iStat += EqnTest( _T("f2of4(1, 2, 3, 4)"), 2, true); - iStat += EqnTest( _T("f3of4(1, 2, 3, 4)"), 3, true); - iStat += EqnTest( _T("f4of4(1, 2, 3, 4)"), 4, true); - iStat += EqnTest( _T("f1of5(1, 2, 3, 4, 5)"), 1, true); - iStat += EqnTest( _T("f2of5(1, 2, 3, 4, 5)"), 2, true); - iStat += EqnTest( _T("f3of5(1, 2, 3, 4, 5)"), 3, true); - iStat += EqnTest( _T("f4of5(1, 2, 3, 4, 5)"), 4, true); - iStat += EqnTest( _T("f5of5(1, 2, 3, 4, 5)"), 5, true); - // Too few arguments / Too many arguments - iStat += EqnTest( _T("1+ping()"), 11, true); - iStat += EqnTest( _T("ping()+1"), 11, true); - iStat += EqnTest( _T("2*ping()"), 20, true); - iStat += EqnTest( _T("ping()*2"), 20, true); - iStat += EqnTest( _T("ping(1,2)"), 0, false); - iStat += EqnTest( _T("1+ping(1,2)"), 0, false); - iStat += EqnTest( _T("f1of1(1,2)"), 0, false); - iStat += EqnTest( _T("f1of1()"), 0, false); - iStat += EqnTest( _T("f1of2(1, 2, 3)"), 0, false); - iStat += EqnTest( _T("f1of2(1)"), 0, false); - iStat += EqnTest( _T("f1of3(1, 2, 3, 4)"), 0, false); - iStat += EqnTest( _T("f1of3(1)"), 0, false); - iStat += EqnTest( _T("f1of4(1, 2, 3, 4, 5)"), 0, false); - iStat += EqnTest( _T("f1of4(1)"), 0, false); - iStat += EqnTest( _T("(1,2,3)"), 0, false); - iStat += EqnTest( _T("1,2,3"), 0, false); - iStat += EqnTest( _T("(1*a,2,3)"), 0, false); - iStat += EqnTest( _T("1,2*a,3"), 0, false); - - // correct calculation of arguments - iStat += EqnTest( _T("min(a, 1)"), 1, true); - iStat += EqnTest( _T("min(3*2, 1)"), 1, true); - iStat += EqnTest( _T("min(3*2, 1)"), 6, false); - iStat += EqnTest( _T("firstArg(2,3,4)"), 2, true); - iStat += EqnTest( _T("lastArg(2,3,4)"), 4, true); - iStat += EqnTest( _T("min(3*a+1, 1)"), 1, true); - iStat += EqnTest( _T("max(3*a+1, 1)"), 4, true); - iStat += EqnTest( _T("max(3*a+1, 1)*2"), 8, true); - iStat += EqnTest( _T("2*max(3*a+1, 1)+2"), 10, true); - - // functions with Variable argument count - iStat += EqnTest( _T("sum(a)"), 1, true); - iStat += EqnTest( _T("sum(1,2,3)"), 6, true); - iStat += EqnTest( _T("sum(a,b,c)"), 6, true); - iStat += EqnTest( _T("sum(1,-max(1,2),3)*2"), 4, true); - iStat += EqnTest( _T("2*sum(1,2,3)"), 12, true); - iStat += EqnTest( _T("2*sum(1,2,3)+2"), 14, true); - iStat += EqnTest( _T("2*sum(-1,2,3)+2"), 10, true); - iStat += EqnTest( _T("2*sum(-1,2,-(-a))+2"), 6, true); - iStat += EqnTest( _T("2*sum(-1,10,-a)+2"), 18, true); - iStat += EqnTest( _T("2*sum(1,2,3)*2"), 24, true); - iStat += EqnTest( _T("sum(1,-max(1,2),3)*2"), 4, true); - iStat += EqnTest( _T("sum(1*3, 4, a+2)"), 10, true); - iStat += EqnTest( _T("sum(1*3, 2*sum(1,2,2), a+2)"), 16, true); - iStat += EqnTest( _T("sum(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2)"), 24, true); - - // some failures - iStat += EqnTest( _T("sum()"), 0, false); - iStat += EqnTest( _T("sum(,)"), 0, false); - iStat += EqnTest( _T("sum(1,2,)"), 0, false); - iStat += EqnTest( _T("sum(,1,2)"), 0, false); +//--------------------------------------------------------------------------- +int ParserTester::TestMultiArg() +{ + int iStat = 0; + mu::console() << _T("testing multiarg functions..."); + + // Compound expressions + iStat += EqnTest(_T("1,2,3"), 3, true); + iStat += EqnTest(_T("a,b,c"), 3, true); + iStat += EqnTest(_T("a=10,b=20,c=a*b"), 200, true); + iStat += EqnTest(_T("1,\n2,\n3"), 3, true); + iStat += EqnTest(_T("a,\nb,\nc"), 3, true); + iStat += EqnTest(_T("a=10,\nb=20,\nc=a*b"), 200, true); + iStat += EqnTest(_T("1,\r\n2,\r\n3"), 3, true); + iStat += EqnTest(_T("a,\r\nb,\r\nc"), 3, true); + iStat += EqnTest(_T("a=10,\r\nb=20,\r\nc=a*b"), 200, true); + + // picking the right argument + iStat += EqnTest(_T("f1of1(1)"), 1, true); + iStat += EqnTest(_T("f1of2(1, 2)"), 1, true); + iStat += EqnTest(_T("f2of2(1, 2)"), 2, true); + iStat += EqnTest(_T("f1of3(1, 2, 3)"), 1, true); + iStat += EqnTest(_T("f2of3(1, 2, 3)"), 2, true); + iStat += EqnTest(_T("f3of3(1, 2, 3)"), 3, true); + iStat += EqnTest(_T("f1of4(1, 2, 3, 4)"), 1, true); + iStat += EqnTest(_T("f2of4(1, 2, 3, 4)"), 2, true); + iStat += EqnTest(_T("f3of4(1, 2, 3, 4)"), 3, true); + iStat += EqnTest(_T("f4of4(1, 2, 3, 4)"), 4, true); + iStat += EqnTest(_T("f1of5(1, 2, 3, 4, 5)"), 1, true); + iStat += EqnTest(_T("f2of5(1, 2, 3, 4, 5)"), 2, true); + iStat += EqnTest(_T("f3of5(1, 2, 3, 4, 5)"), 3, true); + iStat += EqnTest(_T("f4of5(1, 2, 3, 4, 5)"), 4, true); + iStat += EqnTest(_T("f5of5(1, 2, 3, 4, 5)"), 5, true); + // Too few arguments / Too many arguments + iStat += EqnTest(_T("1+ping()"), 11, true); + iStat += EqnTest(_T("ping()+1"), 11, true); + iStat += EqnTest(_T("2*ping()"), 20, true); + iStat += EqnTest(_T("ping()*2"), 20, true); + iStat += EqnTest(_T("ping(1,2)"), 0, false); + iStat += EqnTest(_T("1+ping(1,2)"), 0, false); + iStat += EqnTest(_T("f1of1(1,2)"), 0, false); + iStat += EqnTest(_T("f1of1()"), 0, false); + iStat += EqnTest(_T("f1of2(1, 2, 3)"), 0, false); + iStat += EqnTest(_T("f1of2(1)"), 0, false); + iStat += EqnTest(_T("f1of3(1, 2, 3, 4)"), 0, false); + iStat += EqnTest(_T("f1of3(1)"), 0, false); + iStat += EqnTest(_T("f1of4(1, 2, 3, 4, 5)"), 0, false); + iStat += EqnTest(_T("f1of4(1)"), 0, false); + iStat += EqnTest(_T("(1,2,3)"), 0, false); + iStat += EqnTest(_T("1,2,3"), 0, false); + iStat += EqnTest(_T("(1*a,2,3)"), 0, false); + iStat += EqnTest(_T("1,2*a,3"), 0, false); + + // correct calculation of arguments + iStat += EqnTest(_T("min(a, 1)"), 1, true); + iStat += EqnTest(_T("min(3*2, 1)"), 1, true); + iStat += EqnTest(_T("min(3*2, 1)"), 6, false); + iStat += EqnTest(_T("firstArg(2,3,4)"), 2, true); + iStat += EqnTest(_T("lastArg(2,3,4)"), 4, true); + iStat += EqnTest(_T("min(3*a+1, 1)"), 1, true); + iStat += EqnTest(_T("max(3*a+1, 1)"), 4, true); + iStat += EqnTest(_T("max(3*a+1, 1)*2"), 8, true); + iStat += EqnTest(_T("2*max(3*a+1, 1)+2"), 10, true); + + // functions with Variable argument count + iStat += EqnTest(_T("sum(a)"), 1, true); + iStat += EqnTest(_T("sum(1,2,3)"), 6, true); + iStat += EqnTest(_T("sum(a,b,c)"), 6, true); + iStat += EqnTest(_T("sum(1,-max(1,2),3)*2"), 4, true); + iStat += EqnTest(_T("2*sum(1,2,3)"), 12, true); + iStat += EqnTest(_T("2*sum(1,2,3)+2"), 14, true); + iStat += EqnTest(_T("2*sum(-1,2,3)+2"), 10, true); + iStat += EqnTest(_T("2*sum(-1,2,-(-a))+2"), 6, true); + iStat += EqnTest(_T("2*sum(-1,10,-a)+2"), 18, true); + iStat += EqnTest(_T("2*sum(1,2,3)*2"), 24, true); + iStat += EqnTest(_T("sum(1,-max(1,2),3)*2"), 4, true); + iStat += EqnTest(_T("sum(1*3, 4, a+2)"), 10, true); + iStat += EqnTest(_T("sum(1*3, 2*sum(1,2,2), a+2)"), 16, true); + iStat += EqnTest(_T("sum(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2)"), 24, true); + + // some failures + iStat += EqnTest(_T("sum()"), 0, false); + iStat += EqnTest(_T("sum(,)"), 0, false); + iStat += EqnTest(_T("sum(1,2,)"), 0, false); + iStat += EqnTest(_T("sum(,1,2)"), 0, false); - if (iStat==0) + if (iStat == 0) mu::console() << _T("passed") << endl; - else + else mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - - return iStat; - } + return iStat; +} - //--------------------------------------------------------------------------- - int ParserTester::TestInfixOprt() - { - int iStat(0); - mu::console() << "testing infix operators..."; - - iStat += EqnTest( _T("+1"), +1, true); - iStat += EqnTest( _T("-(+1)"), -1, true); - iStat += EqnTest( _T("-(+1)*2"), -2, true); - iStat += EqnTest( _T("-(+2)*sqrt(4)"), -4, true); - iStat += EqnTest( _T("3-+a"), 2, true); - iStat += EqnTest( _T("+1*3"), 3, true); - - iStat += EqnTest( _T("-1"), -1, true); - iStat += EqnTest( _T("-(-1)"), 1, true); - iStat += EqnTest( _T("-(-1)*2"), 2, true); - iStat += EqnTest( _T("-(-2)*sqrt(4)"), 4, true); - iStat += EqnTest( _T("-_pi"), -PARSER_CONST_PI, true); - iStat += EqnTest( _T("-a"), -1, true); - iStat += EqnTest( _T("-(a)"), -1, true); - iStat += EqnTest( _T("-(-a)"), 1, true); - iStat += EqnTest( _T("-(-a)*2"), 2, true); - iStat += EqnTest( _T("-(8)"), -8, true); - iStat += EqnTest( _T("-8"), -8, true); - iStat += EqnTest( _T("-(2+1)"), -3, true); - iStat += EqnTest( _T("-(f1of1(1+2*3)+1*2)"), -9, true); - iStat += EqnTest( _T("-(-f1of1(1+2*3)+1*2)"), 5, true); - iStat += EqnTest( _T("-sin(8)"), -0.989358, true); - iStat += EqnTest( _T("3-(-a)"), 4, true); - iStat += EqnTest( _T("3--a"), 4, true); - iStat += EqnTest( _T("-1*3"), -3, true); - - // Postfix / infix priorities - iStat += EqnTest( _T("~2#"), 8, true); - iStat += EqnTest( _T("~f1of1(2)#"), 8, true); - iStat += EqnTest( _T("~(b)#"), 8, true); - iStat += EqnTest( _T("(~b)#"), 12, true); - iStat += EqnTest( _T("~(2#)"), 8, true); - iStat += EqnTest( _T("~(f1of1(2)#)"), 8, true); - // - iStat += EqnTest( _T("-2^2"),-4, true); - iStat += EqnTest( _T("-(a+b)^2"),-9, true); - iStat += EqnTest( _T("(-3)^2"),9, true); - iStat += EqnTest( _T("-(-2^2)"),4, true); - iStat += EqnTest( _T("3+-3^2"),-6, true); - // The following assumes use of sqr as postfix operator ("§") together - // with a sign operator of low priority: - iStat += EqnTest( _T("-2'"), -4, true); - iStat += EqnTest( _T("-(1+1)'"),-4, true); - iStat += EqnTest( _T("2+-(1+1)'"),-2, true); - iStat += EqnTest( _T("2+-2'"), -2, true); - // This is the classic behaviour of the infix sign operator (here: "$") which is - // now deprecated: - iStat += EqnTest( _T("$2^2"),4, true); - iStat += EqnTest( _T("$(a+b)^2"),9, true); - iStat += EqnTest( _T("($3)^2"),9, true); - iStat += EqnTest( _T("$($2^2)"),-4, true); - iStat += EqnTest( _T("3+$3^2"),12, true); - - // infix operators sharing the first few characters - iStat += EqnTest( _T("~ 123"), 123+2, true); - iStat += EqnTest( _T("~~ 123"), 123+2, true); +//--------------------------------------------------------------------------- +int ParserTester::TestInfixOprt() +{ + int iStat(0); + mu::console() << "testing infix operators..."; + + iStat += EqnTest(_T("+1"), +1, true); + iStat += EqnTest(_T("-(+1)"), -1, true); + iStat += EqnTest(_T("-(+1)*2"), -2, true); + iStat += EqnTest(_T("-(+2)*sqrt(4)"), -4, true); + iStat += EqnTest(_T("3-+a"), 2, true); + iStat += EqnTest(_T("+1*3"), 3, true); + + iStat += EqnTest(_T("-1"), -1, true); + iStat += EqnTest(_T("-(-1)"), 1, true); + iStat += EqnTest(_T("-(-1)*2"), 2, true); + iStat += EqnTest(_T("-(-2)*sqrt(4)"), 4, true); + iStat += EqnTest(_T("-_pi"), -PARSER_CONST_PI, true); + iStat += EqnTest(_T("-a"), -1, true); + iStat += EqnTest(_T("-(a)"), -1, true); + iStat += EqnTest(_T("-(-a)"), 1, true); + iStat += EqnTest(_T("-(-a)*2"), 2, true); + iStat += EqnTest(_T("-(8)"), -8, true); + iStat += EqnTest(_T("-8"), -8, true); + iStat += EqnTest(_T("-(2+1)"), -3, true); + iStat += EqnTest(_T("-(f1of1(1+2*3)+1*2)"), -9, true); + iStat += EqnTest(_T("-(-f1of1(1+2*3)+1*2)"), 5, true); + iStat += EqnTest(_T("-sin(8)"), -0.989358, true); + iStat += EqnTest(_T("3-(-a)"), 4, true); + iStat += EqnTest(_T("3--a"), 4, true); + iStat += EqnTest(_T("-1*3"), -3, true); + + // Postfix / infix priorities + iStat += EqnTest(_T("~2#"), 8, true); + iStat += EqnTest(_T("~f1of1(2)#"), 8, true); + iStat += EqnTest(_T("~(b)#"), 8, true); + iStat += EqnTest(_T("(~b)#"), 12, true); + iStat += EqnTest(_T("~(2#)"), 8, true); + iStat += EqnTest(_T("~(f1of1(2)#)"), 8, true); + // + iStat += EqnTest(_T("-2^2"), -4, true); + iStat += EqnTest(_T("-(a+b)^2"), -9, true); + iStat += EqnTest(_T("(-3)^2"), 9, true); + iStat += EqnTest(_T("-(-2^2)"), 4, true); + iStat += EqnTest(_T("3+-3^2"), -6, true); + // The following assumes use of sqr as postfix operator ("§") together + // with a sign operator of low priority: + iStat += EqnTest(_T("-2'"), -4, true); + iStat += EqnTest(_T("-(1+1)'"), -4, true); + iStat += EqnTest(_T("2+-(1+1)'"), -2, true); + iStat += EqnTest(_T("2+-2'"), -2, true); + // This is the classic behaviour of the infix sign operator (here: "$") which is + // now deprecated: + iStat += EqnTest(_T("$2^2"), 4, true); + iStat += EqnTest(_T("$(a+b)^2"), 9, true); + iStat += EqnTest(_T("($3)^2"), 9, true); + iStat += EqnTest(_T("$($2^2)"), -4, true); + iStat += EqnTest(_T("3+$3^2"), 12, true); + + // infix operators sharing the first few characters + iStat += EqnTest(_T("~ 123"), 123 + 2, true); + iStat += EqnTest(_T("~~ 123"), 123 + 2, true); - if (iStat==0) + if (iStat == 0) mu::console() << _T("passed") << endl; - else + else mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - return iStat; - } + return iStat; +} +//--------------------------------------------------------------------------- +int ParserTester::TestPostFix() +{ + int iStat = 0; + mu::console() << _T("testing postfix operators..."); - //--------------------------------------------------------------------------- - int ParserTester::TestPostFix() - { - int iStat = 0; - mu::console() << _T("testing postfix operators..."); - - // application - iStat += EqnTest( _T("3{m}+5"), 5.003, true); - iStat += EqnTest( _T("1000{m}"), 1, true); - iStat += EqnTest( _T("1000 {m}"), 1, true); - iStat += EqnTest( _T("(a){m}"), 1e-3, true); - iStat += EqnTest( _T("a{m}"), 1e-3, true); - iStat += EqnTest( _T("a {m}"), 1e-3, true); - iStat += EqnTest( _T("-(a){m}"), -1e-3, true); - iStat += EqnTest( _T("-2{m}"), -2e-3, true); - iStat += EqnTest( _T("-2 {m}"), -2e-3, true); - iStat += EqnTest( _T("f1of1(1000){m}"), 1, true); - iStat += EqnTest( _T("-f1of1(1000){m}"), -1, true); - iStat += EqnTest( _T("-f1of1(-1000){m}"), 1, true); - iStat += EqnTest( _T("f4of4(0,0,0,1000){m}"), 1, true); - iStat += EqnTest( _T("2+(a*1000){m}"), 3, true); - - // can postfix operators "m" und "meg" be told apart properly? - iStat += EqnTest( _T("2*3000meg+2"), 2*3e9+2, true); - - // some incorrect results - iStat += EqnTest( _T("1000{m}"), 0.1, false); - iStat += EqnTest( _T("(a){m}"), 2, false); - // failure due to syntax checking - iStat += ThrowTest(_T("0x"), ecUNASSIGNABLE_TOKEN); // incomplete hex definition - iStat += ThrowTest(_T("3+"), ecUNEXPECTED_EOF); - iStat += ThrowTest( _T("4 + {m}"), ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( _T("{m}4"), ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( _T("sin({m})"), ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( _T("{m} {m}"), ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( _T("{m}(8)"), ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( _T("4,{m}"), ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( _T("-{m}"), ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( _T("2(-{m})"), ecUNEXPECTED_PARENS); - iStat += ThrowTest( _T("2({m})"), ecUNEXPECTED_PARENS); - - iStat += ThrowTest( _T("multi*1.0"), ecUNASSIGNABLE_TOKEN); + // application + iStat += EqnTest(_T("3{m}+5"), 5.003, true); + iStat += EqnTest(_T("1000{m}"), 1, true); + iStat += EqnTest(_T("1000 {m}"), 1, true); + iStat += EqnTest(_T("(a){m}"), 1e-3, true); + iStat += EqnTest(_T("a{m}"), 1e-3, true); + iStat += EqnTest(_T("a {m}"), 1e-3, true); + iStat += EqnTest(_T("-(a){m}"), -1e-3, true); + iStat += EqnTest(_T("-2{m}"), -2e-3, true); + iStat += EqnTest(_T("-2 {m}"), -2e-3, true); + iStat += EqnTest(_T("f1of1(1000){m}"), 1, true); + iStat += EqnTest(_T("-f1of1(1000){m}"), -1, true); + iStat += EqnTest(_T("-f1of1(-1000){m}"), 1, true); + iStat += EqnTest(_T("f4of4(0,0,0,1000){m}"), 1, true); + iStat += EqnTest(_T("2+(a*1000){m}"), 3, true); + + // can postfix operators "m" und "meg" be told apart properly? + iStat += EqnTest(_T("2*3000meg+2"), 2 * 3e9 + 2, true); + + // some incorrect results + iStat += EqnTest(_T("1000{m}"), 0.1, false); + iStat += EqnTest(_T("(a){m}"), 2, false); + // failure due to syntax checking + iStat += ThrowTest(_T("0x"), ecUNASSIGNABLE_TOKEN); // incomplete hex definition + iStat += ThrowTest(_T("3+"), ecUNEXPECTED_EOF); + iStat += ThrowTest(_T("4 + {m}"), ecUNASSIGNABLE_TOKEN); + iStat += ThrowTest(_T("{m}4"), ecUNASSIGNABLE_TOKEN); + iStat += ThrowTest(_T("sin({m})"), ecUNASSIGNABLE_TOKEN); + iStat += ThrowTest(_T("{m} {m}"), ecUNASSIGNABLE_TOKEN); + iStat += ThrowTest(_T("{m}(8)"), ecUNASSIGNABLE_TOKEN); + iStat += ThrowTest(_T("4,{m}"), ecUNASSIGNABLE_TOKEN); + iStat += ThrowTest(_T("-{m}"), ecUNASSIGNABLE_TOKEN); + iStat += ThrowTest(_T("2(-{m})"), ecUNEXPECTED_PARENS); + iStat += ThrowTest(_T("2({m})"), ecUNEXPECTED_PARENS); + + iStat += ThrowTest(_T("multi*1.0"), ecUNASSIGNABLE_TOKEN); - if (iStat==0) + if (iStat == 0) mu::console() << _T("passed") << endl; - else + else mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - return iStat; - } + return iStat; +} - //--------------------------------------------------------------------------- - int ParserTester::TestExpression() - { - int iStat = 0; - mu::console() << _T("testing expression samples..."); +//--------------------------------------------------------------------------- +int ParserTester::TestExpression() +{ + int iStat = 0; + mu::console() << _T("testing expression samples..."); + + value_type b = 2; - value_type b = 2; - - // Optimization - iStat += EqnTest( _T("2*b*5"), 20, true); - iStat += EqnTest( _T("2*b*5 + 4*b"), 28, true); - iStat += EqnTest( _T("2*a/3"), 2.0/3.0, true); - - // Addition auf cmVARMUL - iStat += EqnTest( _T("3+b"), b+3, true); - iStat += EqnTest( _T("b+3"), b+3, true); - iStat += EqnTest( _T("b*3+2"), b*3+2, true); - iStat += EqnTest( _T("3*b+2"), b*3+2, true); - iStat += EqnTest( _T("2+b*3"), b*3+2, true); - iStat += EqnTest( _T("2+3*b"), b*3+2, true); - iStat += EqnTest( _T("b+3*b"), b+3*b, true); - iStat += EqnTest( _T("3*b+b"), b+3*b, true); - - iStat += EqnTest( _T("2+b*3+b"), 2+b*3+b, true); - iStat += EqnTest( _T("b+2+b*3"), b+2+b*3, true); - - iStat += EqnTest( _T("(2*b+1)*4"), (2*b+1)*4, true); - iStat += EqnTest( _T("4*(2*b+1)"), (2*b+1)*4, true); - - // operator precedences - iStat += EqnTest( _T("1+2-3*4/5^6"), 2.99923, true); - iStat += EqnTest( _T("1^2/3*4-5+6"), 2.33333333, true); - iStat += EqnTest( _T("1+2*3"), 7, true); - iStat += EqnTest( _T("1+2*3"), 7, true); - iStat += EqnTest( _T("(1+2)*3"), 9, true); - iStat += EqnTest( _T("(1+2)*(-3)"), -9, true); - iStat += EqnTest( _T("2/4"), 0.5, true); - - iStat += EqnTest( _T("exp(ln(7))"), 7, true); - iStat += EqnTest( _T("e^ln(7)"), 7, true); - iStat += EqnTest( _T("e^(ln(7))"), 7, true); - iStat += EqnTest( _T("(e^(ln(7)))"), 7, true); - iStat += EqnTest( _T("1-(e^(ln(7)))"), -6, true); - iStat += EqnTest( _T("2*(e^(ln(7)))"), 14, true); - iStat += EqnTest( _T("10^log(5)"), pow(10.0, log(5.0)), true); - iStat += EqnTest( _T("10^log10(5)"), 5, true); - iStat += EqnTest( _T("2^log2(4)"), 4, true); - iStat += EqnTest( _T("-(sin(0)+1)"), -1, true); - iStat += EqnTest( _T("-(2^1.1)"), -2.14354692, true); - - iStat += EqnTest( _T("(cos(2.41)/b)"), -0.372056, true); - iStat += EqnTest( _T("(1*(2*(3*(4*(5*(6*(a+b)))))))"), 2160, true); - iStat += EqnTest( _T("(1*(2*(3*(4*(5*(6*(7*(a+b))))))))"), 15120, true); - iStat += EqnTest( _T("(a/((((b+(((e*(((((pi*((((3.45*((pi+a)+pi))+b)+b)*a))+0.68)+e)+a)/a))+a)+b))+b)*a)-pi))"), 0.00377999, true); - - // long formula (Reference: Matlab) - iStat += EqnTest( - _T("(((-9))-e/(((((((pi-(((-7)+(-3)/4/e))))/(((-5))-2)-((pi+(-0))*(sqrt((e+e))*(-8))*(((-pi)+(-pi)-(-9)*(6*5))") - _T("/(-e)-e))/2)/((((sqrt(2/(-e)+6)-(4-2))+((5/(-2))/(1*(-pi)+3))/8)*pi*((pi/((-2)/(-6)*1*(-1))*(-6)+(-e)))))/") - _T("((e+(-2)+(-e)*((((-3)*9+(-e)))+(-9)))))))-((((e-7+(((5/pi-(3/1+pi)))))/e)/(-5))/(sqrt((((((1+(-7))))+((((-") - _T("e)*(-e)))-8))*(-5)/((-e)))*(-6)-((((((-2)-(-9)-(-e)-1)/3))))/(sqrt((8+(e-((-6))+(9*(-9))))*(((3+2-8))*(7+6") - _T("+(-5))+((0/(-e)*(-pi))+7)))+(((((-e)/e/e)+((-6)*5)*e+(3+(-5)/pi))))+pi))/sqrt((((9))+((((pi))-8+2))+pi))/e") - _T("*4)*((-5)/(((-pi))*(sqrt(e)))))-(((((((-e)*(e)-pi))/4+(pi)*(-9)))))))+(-pi)"), -12.23016549, true); - - // long formula (Reference: Matlab) - iStat += EqnTest( - _T("(atan(sin((((((((((((((((pi/cos((a/((((0.53-b)-pi)*e)/b))))+2.51)+a)-0.54)/0.98)+b)*b)+e)/a)+b)+a)+b)+pi)/e") - _T(")+a)))*2.77)"), -2.16995656, true); - - // long formula (Reference: Matlab) - iStat += EqnTest( _T("1+2-3*4/5^6*(2*(1-5+(3*7^9)*(4+6*7-3)))+12"), -7995810.09926, true); - - if (iStat==0) - mu::console() << _T("passed") << endl; - else + // Optimization + iStat += EqnTest(_T("2*b*5"), 20, true); + iStat += EqnTest(_T("2*b*5 + 4*b"), 28, true); + iStat += EqnTest(_T("2*a/3"), 2.0 / 3.0, true); + + // Addition auf cmVARMUL + iStat += EqnTest(_T("3+b"), b + 3, true); + iStat += EqnTest(_T("b+3"), b + 3, true); + iStat += EqnTest(_T("b*3+2"), b * 3 + 2, true); + iStat += EqnTest(_T("3*b+2"), b * 3 + 2, true); + iStat += EqnTest(_T("2+b*3"), b * 3 + 2, true); + iStat += EqnTest(_T("2+3*b"), b * 3 + 2, true); + iStat += EqnTest(_T("b+3*b"), b + 3 * b, true); + iStat += EqnTest(_T("3*b+b"), b + 3 * b, true); + + iStat += EqnTest(_T("2+b*3+b"), 2 + b * 3 + b, true); + iStat += EqnTest(_T("b+2+b*3"), b + 2 + b * 3, true); + + iStat += EqnTest(_T("(2*b+1)*4"), (2 * b + 1) * 4, true); + iStat += EqnTest(_T("4*(2*b+1)"), (2 * b + 1) * 4, true); + + // operator precedences + iStat += EqnTest(_T("1+2-3*4/5^6"), 2.99923, true); + iStat += EqnTest(_T("1^2/3*4-5+6"), 2.33333333, true); + iStat += EqnTest(_T("1+2*3"), 7, true); + iStat += EqnTest(_T("1+2*3"), 7, true); + iStat += EqnTest(_T("(1+2)*3"), 9, true); + iStat += EqnTest(_T("(1+2)*(-3)"), -9, true); + iStat += EqnTest(_T("2/4"), 0.5, true); + + iStat += EqnTest(_T("exp(ln(7))"), 7, true); + iStat += EqnTest(_T("e^ln(7)"), 7, true); + iStat += EqnTest(_T("e^(ln(7))"), 7, true); + iStat += EqnTest(_T("(e^(ln(7)))"), 7, true); + iStat += EqnTest(_T("1-(e^(ln(7)))"), -6, true); + iStat += EqnTest(_T("2*(e^(ln(7)))"), 14, true); + iStat += EqnTest(_T("10^log(5)"), pow(10.0, log(5.0)), true); + iStat += EqnTest(_T("10^log10(5)"), 5, true); + iStat += EqnTest(_T("2^log2(4)"), 4, true); + iStat += EqnTest(_T("-(sin(0)+1)"), -1, true); + iStat += EqnTest(_T("-(2^1.1)"), -2.14354692, true); + + iStat += EqnTest(_T("(cos(2.41)/b)"), -0.372056, true); + iStat += EqnTest(_T("(1*(2*(3*(4*(5*(6*(a+b)))))))"), 2160, true); + iStat += EqnTest(_T("(1*(2*(3*(4*(5*(6*(7*(a+b))))))))"), 15120, true); + iStat += EqnTest(_T("(a/((((b+(((e*(((((pi*((((3.45*((pi+a)+pi))+b)+b)*a))+0.68)+e)+a)/a))+a)+b))+b)*a)-pi))"), + 0.00377999, true); + + // long formula (Reference: Matlab) + iStat += EqnTest( + _T("(((-9))-e/(((((((pi-(((-7)+(-3)/4/e))))/(((-5))-2)-((pi+(-0))*(sqrt((e+e))*(-8))*(((-pi)+(-pi)-(-9)*(6*5))") + _T("/(-e)-e))/2)/((((sqrt(2/(-e)+6)-(4-2))+((5/(-2))/(1*(-pi)+3))/8)*pi*((pi/((-2)/(-6)*1*(-1))*(-6)+(-e)))))/") + _T("((e+(-2)+(-e)*((((-3)*9+(-e)))+(-9)))))))-((((e-7+(((5/pi-(3/1+pi)))))/e)/(-5))/(sqrt((((((1+(-7))))+((((-") + _T("e)*(-e)))-8))*(-5)/((-e)))*(-6)-((((((-2)-(-9)-(-e)-1)/3))))/(sqrt((8+(e-((-6))+(9*(-9))))*(((3+2-8))*(7+6") + _T("+(-5))+((0/(-e)*(-pi))+7)))+(((((-e)/e/e)+((-6)*5)*e+(3+(-5)/pi))))+pi))/sqrt((((9))+((((pi))-8+2))+pi))/e") + _T("*4)*((-5)/(((-pi))*(sqrt(e)))))-(((((((-e)*(e)-pi))/4+(pi)*(-9)))))))+(-pi)"), + -12.23016549, true); + + // long formula (Reference: Matlab) + iStat += EqnTest( + _T("(atan(sin((((((((((((((((pi/cos((a/((((0.53-b)-pi)*e)/b))))+2.51)+a)-0.54)/0.98)+b)*b)+e)/a)+b)+a)+b)+pi)/e") + _T(")+a)))*2.77)"), + -2.16995656, true); + + // long formula (Reference: Matlab) + iStat += EqnTest(_T("1+2-3*4/5^6*(2*(1-5+(3*7^9)*(4+6*7-3)))+12"), -7995810.09926, true); + + if (iStat == 0) + mu::console() << _T("passed") << endl; + else mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - return iStat; - } + return iStat; +} +//--------------------------------------------------------------------------- +int ParserTester::TestIfThenElse() +{ + int iStat = 0; + mu::console() << _T("testing if-then-else operator..."); + // Test error detection + iStat += ThrowTest(_T(":3"), ecUNEXPECTED_CONDITIONAL); + iStat += ThrowTest(_T("? 1 : 2"), ecUNEXPECTED_CONDITIONAL); + iStat += ThrowTest(_T("(ab) ? 10 : 11"), 11, true); + iStat += EqnTest(_T("(ab) ? c : d"), -2, true); + + iStat += EqnTest(_T("(a>b) ? 1 : 0"), 0, true); + iStat += EqnTest(_T("((a>b) ? 1 : 0) ? 1 : 2"), 2, true); + iStat += EqnTest(_T("((a>b) ? 1 : 0) ? 1 : sum((a>b) ? 1 : 2)"), 2, true); + iStat += EqnTest(_T("((a>b) ? 0 : 1) ? 1 : sum((a>b) ? 1 : 2)"), 1, true); + + iStat += EqnTest(_T("sum((a>b) ? 1 : 2)"), 2, true); + iStat += EqnTest(_T("sum((1) ? 1 : 2)"), 1, true); + iStat += EqnTest(_T("sum((a>b) ? 1 : 2, 100)"), 102, true); + iStat += EqnTest(_T("sum((1) ? 1 : 2, 100)"), 101, true); + iStat += EqnTest(_T("sum(3, (a>b) ? 3 : 10)"), 13, true); + iStat += EqnTest(_T("sum(3, (ab) ? 3 : 10)"), 130, true); + iStat += EqnTest(_T("10*sum(3, (ab) ? 3 : 10)*10"), 130, true); + iStat += EqnTest(_T("sum(3, (ab) ? sum(3, (ab) ? sum(3, (ab) ? sum(3, (ab)&&(a2)&&(1<2) ? 128 : 255"), 255, true); + iStat += EqnTest(_T("((1<2)&&(1<2)) ? 128 : 255"), 128, true); + iStat += EqnTest(_T("((1>2)&&(1<2)) ? 128 : 255"), 255, true); + iStat += EqnTest(_T("((ab)&&(a0 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64"), 255, true); + iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 :(1>0 ? 32 : 64)"), 255, true); + iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64"), 128, true); + iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 :(1>2 ? 32 : 64)"), 128, true); + iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64"), 32, true); + iStat += EqnTest(_T("1>2 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64"), 64, true); + iStat += EqnTest(_T("1>0 ? 50 : 1>0 ? 128 : 255"), 50, true); + iStat += EqnTest(_T("1>0 ? 50 : (1>0 ? 128 : 255)"), 50, true); + iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 : 50"), 128, true); + iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 1>2 ? 64 : 16"), 32, true); + iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 :(1>2 ? 64 : 16)"), 32, true); + iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : 1>0 ? 32 :1>2 ? 64 : 16"), 255, true); + iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : (1>0 ? 32 :1>2 ? 64 : 16)"), 255, true); + iStat += EqnTest(_T("1 ? 0 ? 128 : 255 : 1 ? 32 : 64"), 255, true); + + // assignment operators + iStat += EqnTest(_T("a= 0 ? 128 : 255, a"), 255, true); + iStat += EqnTest(_T("a=((a>b)&&(ab) ? 10 : 11"), 11, true); - iStat += EqnTest(_T("(ab) ? c : d"), -2, true); - - iStat += EqnTest(_T("(a>b) ? 1 : 0"), 0, true); - iStat += EqnTest(_T("((a>b) ? 1 : 0) ? 1 : 2"), 2, true); - iStat += EqnTest(_T("((a>b) ? 1 : 0) ? 1 : sum((a>b) ? 1 : 2)"), 2, true); - iStat += EqnTest(_T("((a>b) ? 0 : 1) ? 1 : sum((a>b) ? 1 : 2)"), 1, true); - - iStat += EqnTest(_T("sum((a>b) ? 1 : 2)"), 2, true); - iStat += EqnTest(_T("sum((1) ? 1 : 2)"), 1, true); - iStat += EqnTest(_T("sum((a>b) ? 1 : 2, 100)"), 102, true); - iStat += EqnTest(_T("sum((1) ? 1 : 2, 100)"), 101, true); - iStat += EqnTest(_T("sum(3, (a>b) ? 3 : 10)"), 13, true); - iStat += EqnTest(_T("sum(3, (ab) ? 3 : 10)"), 130, true); - iStat += EqnTest(_T("10*sum(3, (ab) ? 3 : 10)*10"), 130, true); - iStat += EqnTest(_T("sum(3, (ab) ? sum(3, (ab) ? sum(3, (ab) ? sum(3, (ab)&&(a2)&&(1<2) ? 128 : 255"), 255, true); - iStat += EqnTest(_T("((1<2)&&(1<2)) ? 128 : 255"), 128, true); - iStat += EqnTest(_T("((1>2)&&(1<2)) ? 128 : 255"), 255, true); - iStat += EqnTest(_T("((ab)&&(a0 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64"), 255, true); - iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 :(1>0 ? 32 : 64)"), 255, true); - iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64"), 128, true); - iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 :(1>2 ? 32 : 64)"), 128, true); - iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64"), 32, true); - iStat += EqnTest(_T("1>2 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64"), 64, true); - iStat += EqnTest(_T("1>0 ? 50 : 1>0 ? 128 : 255"), 50, true); - iStat += EqnTest(_T("1>0 ? 50 : (1>0 ? 128 : 255)"), 50, true); - iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 : 50"), 128, true); - iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 1>2 ? 64 : 16"), 32, true); - iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 :(1>2 ? 64 : 16)"), 32, true); - iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : 1>0 ? 32 :1>2 ? 64 : 16"), 255, true); - iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : (1>0 ? 32 :1>2 ? 64 : 16)"), 255, true); - iStat += EqnTest(_T("1 ? 0 ? 128 : 255 : 1 ? 32 : 64"), 255, true); - - // assignment operators - iStat += EqnTest(_T("a= 0 ? 128 : 255, a"), 255, true); - iStat += EqnTest(_T("a=((a>b)&&(a - // this is now legal, for reference see: - // https://sourceforge.net/forum/message.php?msg_id=7411373 - // iStat += ThrowTest( _T("sin=9"), ecUNEXPECTED_OPERATOR); - //
- - iStat += ThrowTest( _T("(8)=5"), ecUNEXPECTED_OPERATOR); - iStat += ThrowTest( _T("(a)=5"), ecUNEXPECTED_OPERATOR); - iStat += ThrowTest( _T("a=\"tttt\""), ecOPRT_TYPE_CONFLICT); + // functions without parameter + iStat += ThrowTest(_T("3+ping(2)"), ecTOO_MANY_PARAMS); + iStat += ThrowTest(_T("3+ping(a+2)"), ecTOO_MANY_PARAMS); + iStat += ThrowTest(_T("3+ping(sin(a)+2)"), ecTOO_MANY_PARAMS); + iStat += ThrowTest(_T("3+ping(1+sin(a))"), ecTOO_MANY_PARAMS); + + // String function related + iStat += ThrowTest(_T("valueof(\"xxx\")"), 999, false); + iStat += ThrowTest(_T("valueof()"), ecUNEXPECTED_PARENS); + iStat += ThrowTest(_T("1+valueof(\"abc\""), ecMISSING_PARENS); + iStat += ThrowTest(_T("valueof(\"abc\""), ecMISSING_PARENS); + iStat += ThrowTest(_T("valueof(\"abc"), ecUNTERMINATED_STRING); + iStat += ThrowTest(_T("valueof(\"abc\",3)"), ecTOO_MANY_PARAMS); + iStat += ThrowTest(_T("valueof(3)"), ecSTRING_EXPECTED); + iStat += ThrowTest(_T("sin(\"abc\")"), ecVAL_EXPECTED); + iStat += ThrowTest(_T("valueof(\"\\\"abc\\\"\")"), 999, false); + iStat += ThrowTest(_T("\"hello world\""), ecSTR_RESULT); + iStat += ThrowTest(_T("(\"hello world\")"), ecSTR_RESULT); + iStat += ThrowTest(_T("\"abcd\"+100"), ecOPRT_TYPE_CONFLICT); + iStat += ThrowTest(_T("\"a\"+\"b\""), ecOPRT_TYPE_CONFLICT); + iStat += ThrowTest(_T("strfun1(\"100\",3)"), ecTOO_MANY_PARAMS); + iStat += ThrowTest(_T("strfun2(\"100\",3,5)"), ecTOO_MANY_PARAMS); + iStat += ThrowTest(_T("strfun3(\"100\",3,5,6)"), ecTOO_MANY_PARAMS); + iStat += ThrowTest(_T("strfun2(\"100\")"), ecTOO_FEW_PARAMS); + iStat += ThrowTest(_T("strfun3(\"100\",6)"), ecTOO_FEW_PARAMS); + iStat += ThrowTest(_T("strfun2(1,1)"), ecSTRING_EXPECTED); + iStat += ThrowTest(_T("strfun2(a,1)"), ecSTRING_EXPECTED); + iStat += ThrowTest(_T("strfun2(1,1,1)"), ecTOO_MANY_PARAMS); + iStat += ThrowTest(_T("strfun2(a,1,1)"), ecTOO_MANY_PARAMS); + iStat += ThrowTest(_T("strfun3(1,2,3)"), ecSTRING_EXPECTED); + iStat += ThrowTest(_T("strfun3(1, \"100\",3)"), ecSTRING_EXPECTED); + iStat += ThrowTest(_T("strfun3(\"1\", \"100\",3)"), ecVAL_EXPECTED); + iStat += ThrowTest(_T("strfun3(\"1\", 3, \"100\")"), ecVAL_EXPECTED); + iStat += ThrowTest(_T("strfun3(\"1\", \"100\", \"100\", \"100\")"), ecTOO_MANY_PARAMS); + + // assignment operator + iStat += ThrowTest(_T("3=4"), ecUNEXPECTED_OPERATOR); + iStat += ThrowTest(_T("sin(8)=4"), ecUNEXPECTED_OPERATOR); + iStat += ThrowTest(_T("\"test\"=a"), ecUNEXPECTED_OPERATOR); + + // + // this is now legal, for reference see: + // https://sourceforge.net/forum/message.php?msg_id=7411373 + // iStat += ThrowTest( _T("sin=9"), ecUNEXPECTED_OPERATOR); + // + + iStat += ThrowTest(_T("(8)=5"), ecUNEXPECTED_OPERATOR); + iStat += ThrowTest(_T("(a)=5"), ecUNEXPECTED_OPERATOR); + iStat += ThrowTest(_T("a=\"tttt\""), ecOPRT_TYPE_CONFLICT); - if (iStat==0) + if (iStat == 0) mu::console() << _T("passed") << endl; - else + else mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - return iStat; - } + return iStat; +} +//--------------------------------------------------------------------------- +void ParserTester::AddTest(testfun_type a_pFun) +{ + m_vTestFun.push_back(a_pFun); +} - //--------------------------------------------------------------------------- - void ParserTester::AddTest(testfun_type a_pFun) +//--------------------------------------------------------------------------- +void ParserTester::Run() +{ + int iStat = 0; + try { - m_vTestFun.push_back(a_pFun); + for (int i = 0; i < (int)m_vTestFun.size(); ++i) + iStat += (this->*m_vTestFun[i])(); } - - //--------------------------------------------------------------------------- - void ParserTester::Run() + catch (Parser::exception_type& e) { - int iStat = 0; - try - { - for (int i=0; i<(int)m_vTestFun.size(); ++i) - iStat += (this->*m_vTestFun[i])(); - } - catch(Parser::exception_type &e) - { mu::console() << "\n" << e.GetMsg() << endl; mu::console() << e.GetToken() << endl; Abort(); - } - catch(std::exception &e) - { + } + catch (std::exception& e) + { mu::console() << e.what() << endl; Abort(); - } - catch(...) - { + } + catch (...) + { mu::console() << "Internal error"; Abort(); - } + } - if (iStat==0) - { - mu::console() << "Test passed (" << ParserTester::c_iCount << " expressions)" << endl; - } - else - { - mu::console() << "Test failed with " << iStat - << " errors (" << ParserTester::c_iCount - << " expressions)" << endl; - } - ParserTester::c_iCount = 0; + if (iStat == 0) + { + mu::console() << "Test passed (" << ParserTester::c_iCount << " expressions)" << endl; } + else + { + mu::console() << "Test failed with " << iStat << " errors (" << ParserTester::c_iCount << " expressions)" + << endl; + } + ParserTester::c_iCount = 0; +} +//--------------------------------------------------------------------------- +int ParserTester::ThrowTest(const string_type& a_str, int a_iErrc, bool a_bFail) +{ + ParserTester::c_iCount++; - //--------------------------------------------------------------------------- - int ParserTester::ThrowTest(const string_type &a_str, int a_iErrc, bool a_bFail) - { - ParserTester::c_iCount++; - - try - { - value_type fVal[] = {1,1,1}; + try + { + value_type fVal[] = {1, 1, 1}; Parser p; - p.DefineVar( _T("a"), &fVal[0]); - p.DefineVar( _T("b"), &fVal[1]); - p.DefineVar( _T("c"), &fVal[2]); - p.DefinePostfixOprt( _T("{m}"), Milli); - p.DefinePostfixOprt( _T("m"), Milli); - p.DefineFun( _T("ping"), Ping); - p.DefineFun( _T("valueof"), ValueOf); - p.DefineFun( _T("strfun1"), StrFun1); - p.DefineFun( _T("strfun2"), StrFun2); - p.DefineFun( _T("strfun3"), StrFun3); + p.DefineVar(_T("a"), &fVal[0]); + p.DefineVar(_T("b"), &fVal[1]); + p.DefineVar(_T("c"), &fVal[2]); + p.DefinePostfixOprt(_T("{m}"), Milli); + p.DefinePostfixOprt(_T("m"), Milli); + p.DefineFun(_T("ping"), Ping); + p.DefineFun(_T("valueof"), ValueOf); + p.DefineFun(_T("strfun1"), StrFun1); + p.DefineFun(_T("strfun2"), StrFun2); + p.DefineFun(_T("strfun3"), StrFun3); p.SetExpr(a_str); p.Eval(); - } - catch(ParserError &e) - { + } + catch (ParserError& e) + { // output the formula in case of an failed test - if (a_bFail==false || (a_bFail==true && a_iErrc!=e.GetCode()) ) + if (a_bFail == false || (a_bFail == true && a_iErrc != e.GetCode())) { - mu::console() << _T("\n ") - << _T("Expression: ") << a_str - << _T(" Code:") << e.GetCode() << _T("(") << e.GetMsg() << _T(")") - << _T(" Expected:") << a_iErrc; + mu::console() << _T("\n ") + << _T("Expression: ") << a_str << _T(" Code:") << e.GetCode() << _T("(") << e.GetMsg() + << _T(")") + << _T(" Expected:") << a_iErrc; } - return (a_iErrc==e.GetCode()) ? 0 : 1; - } + return (a_iErrc == e.GetCode()) ? 0 : 1; + } + + // if a_bFail==false no exception is expected + bool bRet((a_bFail == false) ? 0 : 1); + if (bRet == 1) + { + mu::console() << _T("\n ") + << _T("Expression: ") << a_str << _T(" did evaluate; Expected error:") << a_iErrc; + } + + return bRet; +} + +//--------------------------------------------------------------------------- +/** \brief Evaluate a tet expression. + + \return 1 in case of a failure, 0 otherwise. +*/ +int ParserTester::EqnTestWithVarChange(const string_type& a_str, + double a_fVar1, + double a_fRes1, + double a_fVar2, + double a_fRes2) +{ + ParserTester::c_iCount++; + + try + { + value_type fVal[2] = {-999, -999}; // should be equal - // if a_bFail==false no exception is expected - bool bRet((a_bFail==false) ? 0 : 1); - if (bRet==1) - { - mu::console() << _T("\n ") - << _T("Expression: ") << a_str - << _T(" did evaluate; Expected error:") << a_iErrc; - } - - return bRet; - } - - //--------------------------------------------------------------------------- - /** \brief Evaluate a tet expression. - - \return 1 in case of a failure, 0 otherwise. - */ - int ParserTester::EqnTestWithVarChange(const string_type &a_str, - double a_fVar1, - double a_fRes1, - double a_fVar2, - double a_fRes2) - { - ParserTester::c_iCount++; - - try - { - value_type fVal[2] = {-999, -999 }; // should be equal - - Parser p; + Parser p; value_type var = 0; // variable - p.DefineVar( _T("a"), &var); + p.DefineVar(_T("a"), &var); p.SetExpr(a_str); var = a_fVar1; @@ -1219,334 +1212,329 @@ var = a_fVar2; fVal[1] = p.Eval(); - - if ( fabs(a_fRes1-fVal[0]) > 0.0000000001) - throw std::runtime_error("incorrect result (first pass)"); - - if ( fabs(a_fRes2-fVal[1]) > 0.0000000001) - throw std::runtime_error("incorrect result (second pass)"); - } - catch(Parser::exception_type &e) - { + + if (fabs(a_fRes1 - fVal[0]) > 0.0000000001) + throw std::runtime_error("incorrect result (first pass)"); + + if (fabs(a_fRes2 - fVal[1]) > 0.0000000001) + throw std::runtime_error("incorrect result (second pass)"); + } + catch (Parser::exception_type& e) + { mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.GetMsg() << _T(")"); return 1; - } - catch(std::exception &e) - { + } + catch (std::exception& e) + { mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.what() << _T(")"); - return 1; // always return a failure since this exception is not expected - } - catch(...) - { - mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)"); - return 1; // exceptions other than ParserException are not allowed - } - - return 0; - } - - //--------------------------------------------------------------------------- - /** \brief Evaluate a tet expression. - - \return 1 in case of a failure, 0 otherwise. - */ - int ParserTester::EqnTest(const string_type &a_str, double a_fRes, bool a_fPass) - { - ParserTester::c_iCount++; - int iRet(0); - value_type fVal[5] = {-999, -998, -997, -996, -995}; // initially should be different + return 1; // always return a failure since this exception is not expected + } + catch (...) + { + mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)"); + return 1; // exceptions other than ParserException are not allowed + } - try - { + return 0; +} + +//--------------------------------------------------------------------------- +/** \brief Evaluate a tet expression. + + \return 1 in case of a failure, 0 otherwise. +*/ +int ParserTester::EqnTest(const string_type& a_str, double a_fRes, bool a_fPass) +{ + ParserTester::c_iCount++; + int iRet(0); + value_type fVal[5] = {-999, -998, -997, -996, -995}; // initially should be different + + try + { std::auto_ptr p1; - Parser p2, p3; // three parser objects - // they will be used for testing copy and assignment operators + Parser p2, p3; // three parser objects + // they will be used for testing copy and assignment operators // p1 is a pointer since i'm going to delete it in order to test if // parsers after copy construction still refer to members of it. // !! If this is the case this function will crash !! - - p1.reset(new mu::Parser()); + + p1.reset(new mu::Parser()); // Add constants - p1->DefineConst( _T("pi"), (value_type)PARSER_CONST_PI); - p1->DefineConst( _T("e"), (value_type)PARSER_CONST_E); - p1->DefineConst( _T("const"), 1); - p1->DefineConst( _T("const1"), 2); - p1->DefineConst( _T("const2"), 3); + p1->DefineConst(_T("pi"), (value_type)PARSER_CONST_PI); + p1->DefineConst(_T("e"), (value_type)PARSER_CONST_E); + p1->DefineConst(_T("const"), 1); + p1->DefineConst(_T("const1"), 2); + p1->DefineConst(_T("const2"), 3); // string constants - p1->DefineStrConst( _T("str1"), _T("1.11")); - p1->DefineStrConst( _T("str2"), _T("2.22")); + p1->DefineStrConst(_T("str1"), _T("1.11")); + p1->DefineStrConst(_T("str2"), _T("2.22")); // variables - value_type vVarVal[] = { 1, 2, 3, -2}; - p1->DefineVar( _T("a"), &vVarVal[0]); - p1->DefineVar( _T("aa"), &vVarVal[1]); - p1->DefineVar( _T("b"), &vVarVal[1]); - p1->DefineVar( _T("c"), &vVarVal[2]); - p1->DefineVar( _T("d"), &vVarVal[3]); - + value_type vVarVal[] = {1, 2, 3, -2}; + p1->DefineVar(_T("a"), &vVarVal[0]); + p1->DefineVar(_T("aa"), &vVarVal[1]); + p1->DefineVar(_T("b"), &vVarVal[1]); + p1->DefineVar(_T("c"), &vVarVal[2]); + p1->DefineVar(_T("d"), &vVarVal[3]); + // custom value ident functions - p1->AddValIdent(&ParserTester::IsHexVal); + p1->AddValIdent(&ParserTester::IsHexVal); // functions - p1->DefineFun( _T("ping"), Ping); - p1->DefineFun( _T("f1of1"), f1of1); // one parameter - p1->DefineFun( _T("f1of2"), f1of2); // two parameter - p1->DefineFun( _T("f2of2"), f2of2); - p1->DefineFun( _T("f1of3"), f1of3); // three parameter - p1->DefineFun( _T("f2of3"), f2of3); - p1->DefineFun( _T("f3of3"), f3of3); - p1->DefineFun( _T("f1of4"), f1of4); // four parameter - p1->DefineFun( _T("f2of4"), f2of4); - p1->DefineFun( _T("f3of4"), f3of4); - p1->DefineFun( _T("f4of4"), f4of4); - p1->DefineFun( _T("f1of5"), f1of5); // five parameter - p1->DefineFun( _T("f2of5"), f2of5); - p1->DefineFun( _T("f3of5"), f3of5); - p1->DefineFun( _T("f4of5"), f4of5); - p1->DefineFun( _T("f5of5"), f5of5); + p1->DefineFun(_T("ping"), Ping); + p1->DefineFun(_T("f1of1"), f1of1); // one parameter + p1->DefineFun(_T("f1of2"), f1of2); // two parameter + p1->DefineFun(_T("f2of2"), f2of2); + p1->DefineFun(_T("f1of3"), f1of3); // three parameter + p1->DefineFun(_T("f2of3"), f2of3); + p1->DefineFun(_T("f3of3"), f3of3); + p1->DefineFun(_T("f1of4"), f1of4); // four parameter + p1->DefineFun(_T("f2of4"), f2of4); + p1->DefineFun(_T("f3of4"), f3of4); + p1->DefineFun(_T("f4of4"), f4of4); + p1->DefineFun(_T("f1of5"), f1of5); // five parameter + p1->DefineFun(_T("f2of5"), f2of5); + p1->DefineFun(_T("f3of5"), f3of5); + p1->DefineFun(_T("f4of5"), f4of5); + p1->DefineFun(_T("f5of5"), f5of5); // binary operators - p1->DefineOprt( _T("add"), add, 0); - p1->DefineOprt( _T("++"), add, 0); - p1->DefineOprt( _T("&"), land, prLAND); + p1->DefineOprt(_T("add"), add, 0); + p1->DefineOprt(_T("++"), add, 0); + p1->DefineOprt(_T("&"), land, prLAND); // sample functions - p1->DefineFun( _T("min"), Min); - p1->DefineFun( _T("max"), Max); - p1->DefineFun( _T("sum"), Sum); - p1->DefineFun( _T("valueof"), ValueOf); - p1->DefineFun( _T("atof"), StrToFloat); - p1->DefineFun( _T("strfun1"), StrFun1); - p1->DefineFun( _T("strfun2"), StrFun2); - p1->DefineFun( _T("strfun3"), StrFun3); - p1->DefineFun( _T("lastArg"), LastArg); - p1->DefineFun( _T("firstArg"), FirstArg); - p1->DefineFun( _T("order"), FirstArg); + p1->DefineFun(_T("min"), Min); + p1->DefineFun(_T("max"), Max); + p1->DefineFun(_T("sum"), Sum); + p1->DefineFun(_T("valueof"), ValueOf); + p1->DefineFun(_T("atof"), StrToFloat); + p1->DefineFun(_T("strfun1"), StrFun1); + p1->DefineFun(_T("strfun2"), StrFun2); + p1->DefineFun(_T("strfun3"), StrFun3); + p1->DefineFun(_T("lastArg"), LastArg); + p1->DefineFun(_T("firstArg"), FirstArg); + p1->DefineFun(_T("order"), FirstArg); // infix / postfix operator - // Note: Identifiers used here do not have any meaning + // Note: Identifiers used here do not have any meaning // they are mere placeholders to test certain features. - p1->DefineInfixOprt( _T("$"), sign, prPOW+1); // sign with high priority - p1->DefineInfixOprt( _T("~"), plus2); // high priority - p1->DefineInfixOprt( _T("~~"), plus2); - p1->DefinePostfixOprt( _T("{m}"), Milli); - p1->DefinePostfixOprt( _T("{M}"), Mega); - p1->DefinePostfixOprt( _T("m"), Milli); - p1->DefinePostfixOprt( _T("meg"), Mega); - p1->DefinePostfixOprt( _T("#"), times3); - p1->DefinePostfixOprt( _T("'"), sqr); + p1->DefineInfixOprt(_T("$"), sign, prPOW + 1); // sign with high priority + p1->DefineInfixOprt(_T("~"), plus2); // high priority + p1->DefineInfixOprt(_T("~~"), plus2); + p1->DefinePostfixOprt(_T("{m}"), Milli); + p1->DefinePostfixOprt(_T("{M}"), Mega); + p1->DefinePostfixOprt(_T("m"), Milli); + p1->DefinePostfixOprt(_T("meg"), Mega); + p1->DefinePostfixOprt(_T("#"), times3); + p1->DefinePostfixOprt(_T("'"), sqr); p1->SetExpr(a_str); // Test bytecode integrity // String parsing and bytecode parsing must yield the same result fVal[0] = p1->Eval(); // result from stringparsing fVal[1] = p1->Eval(); // result from bytecode - if (fVal[0]!=fVal[1]) - throw Parser::exception_type( _T("Bytecode / string parsing mismatch.") ); + if (fVal[0] != fVal[1]) + throw Parser::exception_type(_T("Bytecode / string parsing mismatch.")); // Test copy and assignment operators try { - // Test copy constructor - std::vector vParser; - vParser.push_back(*(p1.get())); - mu::Parser p2 = vParser[0]; // take parser from vector - - // destroy the originals from p2 - vParser.clear(); // delete the vector - p1.reset(0); - - fVal[2] = p2.Eval(); - - // Test assignment operator - // additionally disable Optimizer this time - mu::Parser p3; - p3 = p2; - p3.EnableOptimizer(false); - fVal[3] = p3.Eval(); - - // Test Eval function for multiple return values - // use p2 since it has the optimizer enabled! - int nNum; - value_type *v = p2.Eval(nNum); - fVal[4] = v[nNum-1]; + // Test copy constructor + std::vector vParser; + vParser.push_back(*(p1.get())); + mu::Parser p2 = vParser[0]; // take parser from vector + + // destroy the originals from p2 + vParser.clear(); // delete the vector + p1.reset(0); + + fVal[2] = p2.Eval(); + + // Test assignment operator + // additionally disable Optimizer this time + mu::Parser p3; + p3 = p2; + p3.EnableOptimizer(false); + fVal[3] = p3.Eval(); + + // Test Eval function for multiple return values + // use p2 since it has the optimizer enabled! + int nNum; + value_type* v = p2.Eval(nNum); + fVal[4] = v[nNum - 1]; } - catch(std::exception &e) + catch (std::exception& e) { - mu::console() << _T("\n ") << e.what() << _T("\n"); + mu::console() << _T("\n ") << e.what() << _T("\n"); } // limited floating point accuracy requires the following test bool bCloseEnough(true); - for (unsigned i=0; i::has_infinity) - #pragma warning(pop) - { - bCloseEnough &= (fabs(fVal[i]) != numeric_limits::infinity()); - } - } +// The tests equations never result in infinity, if they do thats a bug. +// reference: +// http://sourceforge.net/projects/muparser/forums/forum/462843/topic/5037825 +#pragma warning(push) +#pragma warning(disable : 4127) + if (std::numeric_limits::has_infinity) +#pragma warning(pop) + { + bCloseEnough &= (fabs(fVal[i]) != numeric_limits::infinity()); + } + } iRet = ((bCloseEnough && a_fPass) || (!bCloseEnough && !a_fPass)) ? 0 : 1; - - - if (iRet==1) + + if (iRet == 1) { - mu::console() << _T("\n fail: ") << a_str.c_str() - << _T(" (incorrect result; expected: ") << a_fRes - << _T(" ;calculated: ") << fVal[0] << _T(",") - << fVal[1] << _T(",") - << fVal[2] << _T(",") - << fVal[3] << _T(",") - << fVal[4] << _T(")."); + mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (incorrect result; expected: ") << a_fRes + << _T(" ;calculated: ") << fVal[0] << _T(",") << fVal[1] << _T(",") << fVal[2] << _T(",") + << fVal[3] << _T(",") << fVal[4] << _T(")."); } - } - catch(Parser::exception_type &e) - { + } + catch (Parser::exception_type& e) + { if (a_fPass) { - if (fVal[0]!=fVal[2] && fVal[0]!=-999 && fVal[1]!=-998) - mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (copy construction)"); - else - mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.GetMsg() << _T(")"); - return 1; + if (fVal[0] != fVal[2] && fVal[0] != -999 && fVal[1] != -998) + mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (copy construction)"); + else + mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.GetMsg() << _T(")"); + return 1; } - } - catch(std::exception &e) - { + } + catch (std::exception& e) + { mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.what() << _T(")"); - return 1; // always return a failure since this exception is not expected - } - catch(...) - { - mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)"); - return 1; // exceptions other than ParserException are not allowed - } - - return iRet; + return 1; // always return a failure since this exception is not expected } - - //--------------------------------------------------------------------------- - int ParserTester::EqnTestInt(const string_type &a_str, double a_fRes, bool a_fPass) + catch (...) { - ParserTester::c_iCount++; + mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)"); + return 1; // exceptions other than ParserException are not allowed + } + + return iRet; +} + +//--------------------------------------------------------------------------- +int ParserTester::EqnTestInt(const string_type& a_str, double a_fRes, bool a_fPass) +{ + ParserTester::c_iCount++; - value_type vVarVal[] = {1, 2, 3}; // variable values - int iRet(0); + value_type vVarVal[] = {1, 2, 3}; // variable values + int iRet(0); - try - { - value_type fVal[2] = {-99, -999}; // results: initially should be different + try + { + value_type fVal[2] = {-99, -999}; // results: initially should be different ParserInt p; - p.DefineConst( _T("const1"), 1); - p.DefineConst( _T("const2"), 2); - p.DefineVar( _T("a"), &vVarVal[0]); - p.DefineVar( _T("b"), &vVarVal[1]); - p.DefineVar( _T("c"), &vVarVal[2]); + p.DefineConst(_T("const1"), 1); + p.DefineConst(_T("const2"), 2); + p.DefineVar(_T("a"), &vVarVal[0]); + p.DefineVar(_T("b"), &vVarVal[1]); + p.DefineVar(_T("c"), &vVarVal[2]); p.SetExpr(a_str); fVal[0] = p.Eval(); // result from stringparsing fVal[1] = p.Eval(); // result from bytecode - if (fVal[0]!=fVal[1]) - throw Parser::exception_type( _T("Bytecode corrupt.") ); + if (fVal[0] != fVal[1]) + throw Parser::exception_type(_T("Bytecode corrupt.")); - iRet = ( (a_fRes==fVal[0] && a_fPass) || - (a_fRes!=fVal[0] && !a_fPass) ) ? 0 : 1; - if (iRet==1) + iRet = ((a_fRes == fVal[0] && a_fPass) || (a_fRes != fVal[0] && !a_fPass)) ? 0 : 1; + if (iRet == 1) { - mu::console() << _T("\n fail: ") << a_str.c_str() - << _T(" (incorrect result; expected: ") << a_fRes - << _T(" ;calculated: ") << fVal[0]<< _T(")."); + mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (incorrect result; expected: ") << a_fRes + << _T(" ;calculated: ") << fVal[0] << _T(")."); } - } - catch(Parser::exception_type &e) - { + } + catch (Parser::exception_type& e) + { if (a_fPass) { - mu::console() << _T("\n fail: ") << e.GetExpr() << _T(" : ") << e.GetMsg(); - iRet = 1; + mu::console() << _T("\n fail: ") << e.GetExpr() << _T(" : ") << e.GetMsg(); + iRet = 1; } - } - catch(...) - { - mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)"); - iRet = 1; // exceptions other than ParserException are not allowed - } - - return iRet; - } - - //--------------------------------------------------------------------------- - /** \brief Test an expression in Bulk Mode. */ - int ParserTester::EqnTestBulk(const string_type &a_str, double a_fRes[4], bool a_fPass) - { - ParserTester::c_iCount++; - - // Define Bulk Variables - int nBulkSize = 4; - value_type vVariableA[] = { 1, 2, 3, 4 }; // variable values - value_type vVariableB[] = { 2, 2, 2, 2 }; // variable values - value_type vVariableC[] = { 3, 3, 3, 3 }; // variable values - value_type vResults[] = { 0, 0, 0, 0 }; // variable values - int iRet(0); + } + catch (...) + { + mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)"); + iRet = 1; // exceptions other than ParserException are not allowed + } - try - { - Parser p; - p.DefineConst(_T("const1"), 1); - p.DefineConst(_T("const2"), 2); - p.DefineVar(_T("a"), vVariableA); - p.DefineVar(_T("b"), vVariableB); - p.DefineVar(_T("c"), vVariableC); + return iRet; +} - p.SetExpr(a_str); - p.Eval(vResults, nBulkSize); +//--------------------------------------------------------------------------- +/** \brief Test an expression in Bulk Mode. */ +int ParserTester::EqnTestBulk(const string_type& a_str, double a_fRes[4], bool a_fPass) +{ + ParserTester::c_iCount++; - bool bCloseEnough(true); - for (int i = 0; i < nBulkSize; ++i) - { - bCloseEnough &= (fabs(a_fRes[i] - vResults[i]) <= fabs(a_fRes[i] * 0.00001)); - } + // Define Bulk Variables + int nBulkSize = 4; + value_type vVariableA[] = {1, 2, 3, 4}; // variable values + value_type vVariableB[] = {2, 2, 2, 2}; // variable values + value_type vVariableC[] = {3, 3, 3, 3}; // variable values + value_type vResults[] = {0, 0, 0, 0}; // variable values + int iRet(0); - iRet = ((bCloseEnough && a_fPass) || (!bCloseEnough && !a_fPass)) ? 0 : 1; - if (iRet == 1) - { - mu::console() << _T("\n fail: ") << a_str.c_str() - << _T(" (incorrect result; expected: {") << a_fRes[0] << _T(",") << a_fRes[1] << _T(",") << a_fRes[2] << _T(",") << a_fRes[3] << _T("}") - << _T(" ;calculated: ") << vResults[0] << _T(",") << vResults[1] << _T(",") << vResults[2] << _T(",") << vResults[3] << _T("}"); - } + try + { + Parser p; + p.DefineConst(_T("const1"), 1); + p.DefineConst(_T("const2"), 2); + p.DefineVar(_T("a"), vVariableA); + p.DefineVar(_T("b"), vVariableB); + p.DefineVar(_T("c"), vVariableC); + + p.SetExpr(a_str); + p.Eval(vResults, nBulkSize); + + bool bCloseEnough(true); + for (int i = 0; i < nBulkSize; ++i) + { + bCloseEnough &= (fabs(a_fRes[i] - vResults[i]) <= fabs(a_fRes[i] * 0.00001)); } - catch (Parser::exception_type &e) + + iRet = ((bCloseEnough && a_fPass) || (!bCloseEnough && !a_fPass)) ? 0 : 1; + if (iRet == 1) { - if (a_fPass) - { - mu::console() << _T("\n fail: ") << e.GetExpr() << _T(" : ") << e.GetMsg(); - iRet = 1; - } + mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (incorrect result; expected: {") << a_fRes[0] + << _T(",") << a_fRes[1] << _T(",") << a_fRes[2] << _T(",") << a_fRes[3] << _T("}") + << _T(" ;calculated: ") << vResults[0] << _T(",") << vResults[1] << _T(",") << vResults[2] + << _T(",") << vResults[3] << _T("}"); } - catch (...) + } + catch (Parser::exception_type& e) + { + if (a_fPass) { - mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)"); - iRet = 1; // exceptions other than ParserException are not allowed + mu::console() << _T("\n fail: ") << e.GetExpr() << _T(" : ") << e.GetMsg(); + iRet = 1; } - - return iRet; } - - //--------------------------------------------------------------------------- - /** \brief Internal error in test class Test is going to be aborted. */ - void ParserTester::Abort() const + catch (...) { - mu::console() << _T("Test failed (internal error in test class)") << endl; - while (!getchar()); - exit(-1); + mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)"); + iRet = 1; // exceptions other than ParserException are not allowed } - } // namespace test + + return iRet; +} + +//--------------------------------------------------------------------------- +/** \brief Internal error in test class Test is going to be aborted. */ +void ParserTester::Abort() const +{ + mu::console() << _T("Test failed (internal error in test class)") << endl; + while (!getchar()) + ; + exit(-1); +} +} // namespace Test } // namespace mu diff -Nru mstflint-4.17.0+1/ext_libs/muparser/muParserTest.h mstflint-4.21.0+1/ext_libs/muparser/muParserTest.h --- mstflint-4.17.0+1/ext_libs/muparser/muParserTest.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/muParserTest.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ /* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2013 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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 MU_PARSER_TEST_H @@ -38,177 +38,162 @@ namespace mu { - /** \brief Namespace for test cases. */ - namespace Test - { - //------------------------------------------------------------------------------ - /** \brief Test cases for unit testing. - - (C) 2004-2011 Ingo Berg - */ - class ParserTester // final - { - private: - static int c_iCount; - - // Multiarg callbacks - static value_type f1of1(value_type v) { return v;}; - - static value_type f1of2(value_type v, value_type ) {return v;}; - static value_type f2of2(value_type , value_type v) {return v;}; - - static value_type f1of3(value_type v, value_type , value_type ) {return v;}; - static value_type f2of3(value_type , value_type v, value_type ) {return v;}; - static value_type f3of3(value_type , value_type , value_type v) {return v;}; - - static value_type f1of4(value_type v, value_type, value_type , value_type ) {return v;} - static value_type f2of4(value_type , value_type v, value_type , value_type ) {return v;} - static value_type f3of4(value_type , value_type, value_type v, value_type ) {return v;} - static value_type f4of4(value_type , value_type, value_type , value_type v) {return v;} - - static value_type f1of5(value_type v, value_type, value_type , value_type , value_type ) { return v; } - static value_type f2of5(value_type , value_type v, value_type , value_type , value_type ) { return v; } - static value_type f3of5(value_type , value_type, value_type v, value_type , value_type ) { return v; } - static value_type f4of5(value_type , value_type, value_type , value_type v, value_type ) { return v; } - static value_type f5of5(value_type , value_type, value_type , value_type , value_type v) { return v; } - - static value_type Min(value_type a_fVal1, value_type a_fVal2) { return (a_fVal1a_fVal2) ? a_fVal1 : a_fVal2; } - - static value_type plus2(value_type v1) { return v1+2; } - static value_type times3(value_type v1) { return v1*3; } - static value_type sqr(value_type v1) { return v1*v1; } - static value_type sign(value_type v) { return -v; } - static value_type add(value_type v1, value_type v2) { return v1+v2; } - static value_type land(value_type v1, value_type v2) { return (int)v1 & (int)v2; } - - - static value_type FirstArg(const value_type* a_afArg, int a_iArgc) - { - if (!a_iArgc) - throw mu::Parser::exception_type( _T("too few arguments for function FirstArg.") ); - - return a_afArg[0]; - } - - static value_type LastArg(const value_type* a_afArg, int a_iArgc) - { - if (!a_iArgc) - throw mu::Parser::exception_type( _T("too few arguments for function LastArg.") ); - - return a_afArg[a_iArgc-1]; - } - - static value_type Sum(const value_type* a_afArg, int a_iArgc) - { - if (!a_iArgc) - throw mu::Parser::exception_type( _T("too few arguments for function sum.") ); - - value_type fRes=0; - for (int i=0; i> val; - return (value_type)val; - } - - static value_type StrFun2(const char_type* v1, value_type v2) - { - int val(0); - stringstream_type(v1) >> val; - return (value_type)(val + v2); - } - - static value_type StrFun3(const char_type* v1, value_type v2, value_type v3) - { - int val(0); - stringstream_type(v1) >> val; - return val + v2 + v3; - } - - static value_type StrToFloat(const char_type* a_szMsg) - { - value_type val(0); - stringstream_type(a_szMsg) >> val; - return val; - } - - // postfix operator callback - static value_type Mega(value_type a_fVal) { return a_fVal * (value_type)1e6; } - static value_type Micro(value_type a_fVal) { return a_fVal * (value_type)1e-6; } - static value_type Milli(value_type a_fVal) { return a_fVal / (value_type)1e3; } - - // Custom value recognition - static int IsHexVal(const char_type *a_szExpr, int *a_iPos, value_type *a_fVal); - - int TestNames(); - int TestSyntax(); - int TestMultiArg(); - int TestPostFix(); - int TestExpression(); - int TestInfixOprt(); - int TestBinOprt(); - int TestVarConst(); - int TestInterface(); - int TestException(); - int TestStrArg(); - int TestIfThenElse(); - int TestBulkMode(); - - void Abort() const; - - public: - typedef int (ParserTester::*testfun_type)(); - - ParserTester(); - void Run(); - - private: - std::vector m_vTestFun; - void AddTest(testfun_type a_pFun); - - // Test Double Parser - int EqnTest(const string_type& a_str, double a_fRes, bool a_fPass); - int EqnTestWithVarChange(const string_type& a_str, - double a_fRes1, - double a_fVar1, - double a_fRes2, - double a_fVar2); - int ThrowTest(const string_type& a_str, int a_iErrc, bool a_bFail = true); - - // Test Int Parser - int EqnTestInt(const string_type& a_str, double a_fRes, bool a_fPass); - - // Test Bulkmode - int EqnTestBulk(const string_type& a_str, double a_fRes[4], bool a_fPass); - }; - } // namespace Test -} // namespace mu +/** \brief Namespace for test cases. */ +namespace Test +{ +//------------------------------------------------------------------------------ +/** \brief Test cases for unit testing. -#endif + (C) 2004-2011 Ingo Berg +*/ +class ParserTester // final +{ +private: + static int c_iCount; + + // Multiarg callbacks + static value_type f1of1(value_type v) { return v; }; + + static value_type f1of2(value_type v, value_type) { return v; }; + static value_type f2of2(value_type, value_type v) { return v; }; + + static value_type f1of3(value_type v, value_type, value_type) { return v; }; + static value_type f2of3(value_type, value_type v, value_type) { return v; }; + static value_type f3of3(value_type, value_type, value_type v) { return v; }; + + static value_type f1of4(value_type v, value_type, value_type, value_type) { return v; } + static value_type f2of4(value_type, value_type v, value_type, value_type) { return v; } + static value_type f3of4(value_type, value_type, value_type v, value_type) { return v; } + static value_type f4of4(value_type, value_type, value_type, value_type v) { return v; } + + static value_type f1of5(value_type v, value_type, value_type, value_type, value_type) { return v; } + static value_type f2of5(value_type, value_type v, value_type, value_type, value_type) { return v; } + static value_type f3of5(value_type, value_type, value_type v, value_type, value_type) { return v; } + static value_type f4of5(value_type, value_type, value_type, value_type v, value_type) { return v; } + static value_type f5of5(value_type, value_type, value_type, value_type, value_type v) { return v; } + + static value_type Min(value_type a_fVal1, value_type a_fVal2) { return (a_fVal1 < a_fVal2) ? a_fVal1 : a_fVal2; } + static value_type Max(value_type a_fVal1, value_type a_fVal2) { return (a_fVal1 > a_fVal2) ? a_fVal1 : a_fVal2; } + + static value_type plus2(value_type v1) { return v1 + 2; } + static value_type times3(value_type v1) { return v1 * 3; } + static value_type sqr(value_type v1) { return v1 * v1; } + static value_type sign(value_type v) { return -v; } + static value_type add(value_type v1, value_type v2) { return v1 + v2; } + static value_type land(value_type v1, value_type v2) { return (int)v1 & (int)v2; } + + static value_type FirstArg(const value_type* a_afArg, int a_iArgc) + { + if (!a_iArgc) + throw mu::Parser::exception_type(_T("too few arguments for function FirstArg.")); + + return a_afArg[0]; + } + + static value_type LastArg(const value_type* a_afArg, int a_iArgc) + { + if (!a_iArgc) + throw mu::Parser::exception_type(_T("too few arguments for function LastArg.")); + + return a_afArg[a_iArgc - 1]; + } + + static value_type Sum(const value_type* a_afArg, int a_iArgc) + { + if (!a_iArgc) + throw mu::Parser::exception_type(_T("too few arguments for function sum.")); + value_type fRes = 0; + for (int i = 0; i < a_iArgc; ++i) + fRes += a_afArg[i]; + return fRes; + } + static value_type Rnd(value_type v) { return (value_type)(1 + (v * std::rand() / (RAND_MAX + 1.0))); } + + static value_type RndWithString(const char_type*) + { + return (value_type)(1 + (1000.0f * std::rand() / (RAND_MAX + 1.0))); + } + + static value_type Ping() { return 10; } + + static value_type ValueOf(const char_type*) { return 123; } + + static value_type StrFun1(const char_type* v1) + { + int val(0); + stringstream_type(v1) >> val; + return (value_type)val; + } + + static value_type StrFun2(const char_type* v1, value_type v2) + { + int val(0); + stringstream_type(v1) >> val; + return (value_type)(val + v2); + } + + static value_type StrFun3(const char_type* v1, value_type v2, value_type v3) + { + int val(0); + stringstream_type(v1) >> val; + return val + v2 + v3; + } + + static value_type StrToFloat(const char_type* a_szMsg) + { + value_type val(0); + stringstream_type(a_szMsg) >> val; + return val; + } + + // postfix operator callback + static value_type Mega(value_type a_fVal) { return a_fVal * (value_type)1e6; } + static value_type Micro(value_type a_fVal) { return a_fVal * (value_type)1e-6; } + static value_type Milli(value_type a_fVal) { return a_fVal / (value_type)1e3; } + + // Custom value recognition + static int IsHexVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal); + + int TestNames(); + int TestSyntax(); + int TestMultiArg(); + int TestPostFix(); + int TestExpression(); + int TestInfixOprt(); + int TestBinOprt(); + int TestVarConst(); + int TestInterface(); + int TestException(); + int TestStrArg(); + int TestIfThenElse(); + int TestBulkMode(); + + void Abort() const; + +public: + typedef int (ParserTester::*testfun_type)(); + + ParserTester(); + void Run(); + +private: + std::vector m_vTestFun; + void AddTest(testfun_type a_pFun); + + // Test Double Parser + int EqnTest(const string_type& a_str, double a_fRes, bool a_fPass); + int EqnTestWithVarChange(const string_type& a_str, double a_fRes1, double a_fVar1, double a_fRes2, double a_fVar2); + int ThrowTest(const string_type& a_str, int a_iErrc, bool a_bFail = true); + + // Test Int Parser + int EqnTestInt(const string_type& a_str, double a_fRes, bool a_fPass); + + // Test Bulkmode + int EqnTestBulk(const string_type& a_str, double a_fRes[4], bool a_fPass); +}; +} // namespace Test +} // namespace mu + +#endif diff -Nru mstflint-4.17.0+1/ext_libs/muparser/muParserToken.h mstflint-4.21.0+1/ext_libs/muparser/muParserToken.h --- mstflint-4.17.0+1/ext_libs/muparser/muParserToken.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/muParserToken.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ /* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2004-2013 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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 MU_PARSER_TOKEN_H @@ -41,89 +41,78 @@ namespace mu { - /** \brief Encapsulation of the data for a single formula token. +/** \brief Encapsulation of the data for a single formula token. - Formula token implementation. Part of the Math Parser Package. - Formula tokens can be either one of the following: -
    -
  • value
  • -
  • variable
  • -
  • function with numerical arguments
  • -
  • functions with a string as argument
  • -
  • prefix operators
  • -
  • infix operators
  • -
  • binary operator
  • -
- - \author (C) 2004-2013 Ingo Berg - */ - template - class ParserToken - { - private: - - ECmdCode m_iCode; ///< Type of the token; The token type is a constant of type #ECmdCode. - ETypeCode m_iType; - void *m_pTok; ///< Stores Token pointer; not applicable for all tokens - int m_iIdx; ///< An otional index to an external buffer storing the token data - TString m_strTok; ///< Token string - TString m_strVal; ///< Value for string variables - value_type m_fVal; ///< the value - std::auto_ptr m_pCallback; - - public: - - //--------------------------------------------------------------------------- - /** \brief Constructor (default). - - Sets token to an neutral state of type cmUNKNOWN. - \throw nothrow - \sa ECmdCode - */ - ParserToken() - :m_iCode(cmUNKNOWN) - ,m_iType(tpVOID) - ,m_pTok(0) - ,m_iIdx(-1) - ,m_strTok() - ,m_strVal() - ,m_fVal(0) - ,m_pCallback() - {} - - //------------------------------------------------------------------------------ - /** \brief Create token from another one. - - Implemented by calling Assign(...) - \throw nothrow - \post m_iType==cmUNKNOWN - \sa #Assign - */ - ParserToken(const ParserToken &a_Tok) - { - Assign(a_Tok); - } - - //------------------------------------------------------------------------------ - /** \brief Assignement operator. - - Copy token state from another token and return this. - Implemented by calling Assign(...). - \throw nothrow - */ - ParserToken& operator=(const ParserToken &a_Tok) - { + Formula token implementation. Part of the Math Parser Package. + Formula tokens can be either one of the following: +
    +
  • value
  • +
  • variable
  • +
  • function with numerical arguments
  • +
  • functions with a string as argument
  • +
  • prefix operators
  • +
  • infix operators
  • +
  • binary operator
  • +
+ + \author (C) 2004-2013 Ingo Berg +*/ +template +class ParserToken +{ +private: + ECmdCode m_iCode; ///< Type of the token; The token type is a constant of type #ECmdCode. + ETypeCode m_iType; + void* m_pTok; ///< Stores Token pointer; not applicable for all tokens + int m_iIdx; ///< An otional index to an external buffer storing the token data + TString m_strTok; ///< Token string + TString m_strVal; ///< Value for string variables + value_type m_fVal; ///< the value + std::auto_ptr m_pCallback; + +public: + //--------------------------------------------------------------------------- + /** \brief Constructor (default). + + Sets token to an neutral state of type cmUNKNOWN. + \throw nothrow + \sa ECmdCode + */ + ParserToken() : + m_iCode(cmUNKNOWN), m_iType(tpVOID), m_pTok(0), m_iIdx(-1), m_strTok(), m_strVal(), m_fVal(0), m_pCallback() + { + } + + //------------------------------------------------------------------------------ + /** \brief Create token from another one. + + Implemented by calling Assign(...) + \throw nothrow + \post m_iType==cmUNKNOWN + \sa #Assign + */ + ParserToken(const ParserToken& a_Tok) { Assign(a_Tok); } + + //------------------------------------------------------------------------------ + /** \brief Assignement operator. + + Copy token state from another token and return this. + Implemented by calling Assign(...). + \throw nothrow + */ + ParserToken& operator=(const ParserToken& a_Tok) + { Assign(a_Tok); return *this; - } + } + + //------------------------------------------------------------------------------ + /** \brief Copy token information from argument. - //------------------------------------------------------------------------------ - /** \brief Copy token information from argument. - - \throw nothrow - */ - void Assign(const ParserToken &a_Tok) - { + \throw nothrow + */ + void Assign(const ParserToken& a_Tok) + { m_iCode = a_Tok.m_iCode; m_pTok = a_Tok.m_pTok; m_strTok = a_Tok.m_strTok; @@ -131,27 +120,27 @@ m_strVal = a_Tok.m_strVal; m_iType = a_Tok.m_iType; m_fVal = a_Tok.m_fVal; - // create new callback object if a_Tok has one + // create new callback object if a_Tok has one m_pCallback.reset(a_Tok.m_pCallback.get() ? a_Tok.m_pCallback->Clone() : 0); - } + } - //------------------------------------------------------------------------------ - /** \brief Assign a token type. + //------------------------------------------------------------------------------ + /** \brief Assign a token type. - Token may not be of type value, variable or function. Those have seperate set functions. + Token may not be of type value, variable or function. Those have seperate set functions. - \pre [assert] a_iType!=cmVAR - \pre [assert] a_iType!=cmVAL - \pre [assert] a_iType!=cmFUNC - \post m_fVal = 0 - \post m_pTok = 0 - */ - ParserToken& Set(ECmdCode a_iType, const TString &a_strTok=TString()) - { + \pre [assert] a_iType!=cmVAR + \pre [assert] a_iType!=cmVAL + \pre [assert] a_iType!=cmFUNC + \post m_fVal = 0 + \post m_pTok = 0 + */ + ParserToken& Set(ECmdCode a_iType, const TString& a_strTok = TString()) + { // The following types cant be set this way, they have special Set functions - assert(a_iType!=cmVAR); - assert(a_iType!=cmVAL); - assert(a_iType!=cmFUNC); + assert(a_iType != cmVAR); + assert(a_iType != cmVAL); + assert(a_iType != cmFUNC); m_iCode = a_iType; m_iType = tpVOID; @@ -160,12 +149,12 @@ m_iIdx = -1; return *this; - } + } - //------------------------------------------------------------------------------ - /** \brief Set Callback type. */ - ParserToken& Set(const ParserCallback &a_pCallback, const TString &a_sTok) - { + //------------------------------------------------------------------------------ + /** \brief Set Callback type. */ + ParserToken& Set(const ParserCallback& a_pCallback, const TString& a_sTok) + { assert(a_pCallback.GetAddr()); m_iCode = a_pCallback.GetCode(); @@ -175,38 +164,38 @@ m_pTok = 0; m_iIdx = -1; - + return *this; - } + } + + //------------------------------------------------------------------------------ + /** \brief Make this token a value token. - //------------------------------------------------------------------------------ - /** \brief Make this token a value token. - - Member variables not necessary for value tokens will be invalidated. - \throw nothrow - */ - ParserToken& SetVal(TBase a_fVal, const TString &a_strTok=TString()) - { + Member variables not necessary for value tokens will be invalidated. + \throw nothrow + */ + ParserToken& SetVal(TBase a_fVal, const TString& a_strTok = TString()) + { m_iCode = cmVAL; m_iType = tpDBL; m_fVal = a_fVal; m_strTok = a_strTok; m_iIdx = -1; - + m_pTok = 0; m_pCallback.reset(0); return *this; - } + } + + //------------------------------------------------------------------------------ + /** \brief make this token a variable token. - //------------------------------------------------------------------------------ - /** \brief make this token a variable token. - - Member variables not necessary for variable tokens will be invalidated. - \throw nothrow - */ - ParserToken& SetVar(TBase *a_pVar, const TString &a_strTok) - { + Member variables not necessary for variable tokens will be invalidated. + \throw nothrow + */ + ParserToken& SetVar(TBase* a_pVar, const TString& a_strTok) + { m_iCode = cmVAR; m_iType = tpDBL; m_strTok = a_strTok; @@ -214,16 +203,16 @@ m_pTok = (void*)a_pVar; m_pCallback.reset(0); return *this; - } + } - //------------------------------------------------------------------------------ - /** \brief Make this token a variable token. - - Member variables not necessary for variable tokens will be invalidated. - \throw nothrow - */ - ParserToken& SetString(const TString &a_strTok, std::size_t a_iSize) - { + //------------------------------------------------------------------------------ + /** \brief Make this token a variable token. + + Member variables not necessary for variable tokens will be invalidated. + \throw nothrow + */ + ParserToken& SetString(const TString& a_strTok, std::size_t a_iSize) + { m_iCode = cmSTRING; m_iType = tpSTR; m_strTok = a_strTok; @@ -232,170 +221,167 @@ m_pTok = 0; m_pCallback.reset(0); return *this; - } + } + + //------------------------------------------------------------------------------ + /** \brief Set an index associated with the token related data. + + In cmSTRFUNC - This is the index to a string table in the main parser. + \param a_iIdx The index the string function result will take in the bytecode parser. + \throw exception_type if #a_iIdx<0 or #m_iType!=cmSTRING + */ + void SetIdx(int a_iIdx) + { + if (m_iCode != cmSTRING || a_iIdx < 0) + throw ParserError(ecINTERNAL_ERROR); - //------------------------------------------------------------------------------ - /** \brief Set an index associated with the token related data. - - In cmSTRFUNC - This is the index to a string table in the main parser. - \param a_iIdx The index the string function result will take in the bytecode parser. - \throw exception_type if #a_iIdx<0 or #m_iType!=cmSTRING - */ - void SetIdx(int a_iIdx) - { - if (m_iCode!=cmSTRING || a_iIdx<0) - throw ParserError(ecINTERNAL_ERROR); - m_iIdx = a_iIdx; - } + } + + //------------------------------------------------------------------------------ + /** \brief Return Index associated with the token related data. + + In cmSTRFUNC - This is the index to a string table in the main parser. - //------------------------------------------------------------------------------ - /** \brief Return Index associated with the token related data. - - In cmSTRFUNC - This is the index to a string table in the main parser. - - \throw exception_type if #m_iIdx<0 or #m_iType!=cmSTRING - \return The index the result will take in the Bytecode calculatin array (#m_iIdx). - */ - int GetIdx() const - { - if (m_iIdx<0 || m_iCode!=cmSTRING ) - throw ParserError(ecINTERNAL_ERROR); + \throw exception_type if #m_iIdx<0 or #m_iType!=cmSTRING + \return The index the result will take in the Bytecode calculatin array (#m_iIdx). + */ + int GetIdx() const + { + if (m_iIdx < 0 || m_iCode != cmSTRING) + throw ParserError(ecINTERNAL_ERROR); return m_iIdx; - } + } - //------------------------------------------------------------------------------ - /** \brief Return the token type. - - \return #m_iType - \throw nothrow - */ - ECmdCode GetCode() const - { + //------------------------------------------------------------------------------ + /** \brief Return the token type. + + \return #m_iType + \throw nothrow + */ + ECmdCode GetCode() const + { if (m_pCallback.get()) { - return m_pCallback->GetCode(); + return m_pCallback->GetCode(); } else { - return m_iCode; + return m_iCode; } - } + } - //------------------------------------------------------------------------------ - ETypeCode GetType() const - { + //------------------------------------------------------------------------------ + ETypeCode GetType() const + { if (m_pCallback.get()) { - return m_pCallback->GetType(); + return m_pCallback->GetType(); } else { - return m_iType; + return m_iType; } - } - - //------------------------------------------------------------------------------ - int GetPri() const - { - if ( !m_pCallback.get()) - throw ParserError(ecINTERNAL_ERROR); - - if ( m_pCallback->GetCode()!=cmOPRT_BIN && m_pCallback->GetCode()!=cmOPRT_INFIX) - throw ParserError(ecINTERNAL_ERROR); + } + + //------------------------------------------------------------------------------ + int GetPri() const + { + if (!m_pCallback.get()) + throw ParserError(ecINTERNAL_ERROR); + + if (m_pCallback->GetCode() != cmOPRT_BIN && m_pCallback->GetCode() != cmOPRT_INFIX) + throw ParserError(ecINTERNAL_ERROR); return m_pCallback->GetPri(); - } + } - //------------------------------------------------------------------------------ - EOprtAssociativity GetAssociativity() const - { - if (m_pCallback.get()==NULL || m_pCallback->GetCode()!=cmOPRT_BIN) - throw ParserError(ecINTERNAL_ERROR); + //------------------------------------------------------------------------------ + EOprtAssociativity GetAssociativity() const + { + if (m_pCallback.get() == NULL || m_pCallback->GetCode() != cmOPRT_BIN) + throw ParserError(ecINTERNAL_ERROR); return m_pCallback->GetAssociativity(); - } + } - //------------------------------------------------------------------------------ - /** \brief Return the address of the callback function assoziated with - function and operator tokens. - - \return The pointer stored in #m_pTok. - \throw exception_type if token type is non of: -
    -
  • cmFUNC
  • -
  • cmSTRFUNC
  • -
  • cmPOSTOP
  • -
  • cmINFIXOP
  • -
  • cmOPRT_BIN
  • -
- \sa ECmdCode - */ - generic_fun_type GetFuncAddr() const - { - return (m_pCallback.get()) ? (generic_fun_type)m_pCallback->GetAddr() : 0; - } - - //------------------------------------------------------------------------------ - /** \biref Get value of the token. - - Only applicable to variable and value tokens. - \throw exception_type if token is no value/variable token. - */ - TBase GetVal() const - { + //------------------------------------------------------------------------------ + /** \brief Return the address of the callback function assoziated with + function and operator tokens. + + \return The pointer stored in #m_pTok. + \throw exception_type if token type is non of: +
    +
  • cmFUNC
  • +
  • cmSTRFUNC
  • +
  • cmPOSTOP
  • +
  • cmINFIXOP
  • +
  • cmOPRT_BIN
  • +
+ \sa ECmdCode + */ + generic_fun_type GetFuncAddr() const { return (m_pCallback.get()) ? (generic_fun_type)m_pCallback->GetAddr() : 0; } + + //------------------------------------------------------------------------------ + /** \biref Get value of the token. + + Only applicable to variable and value tokens. + \throw exception_type if token is no value/variable token. + */ + TBase GetVal() const + { switch (m_iCode) { - case cmVAL: return m_fVal; - case cmVAR: return *((TBase*)m_pTok); - default: throw ParserError(ecVAL_EXPECTED); + case cmVAL: + return m_fVal; + case cmVAR: + return *((TBase*)m_pTok); + default: + throw ParserError(ecVAL_EXPECTED); } - } + } - //------------------------------------------------------------------------------ - /** \brief Get address of a variable token. + //------------------------------------------------------------------------------ + /** \brief Get address of a variable token. - Valid only if m_iType==CmdVar. - \throw exception_type if token is no variable token. - */ - TBase* GetVar() const - { - if (m_iCode!=cmVAR) - throw ParserError(ecINTERNAL_ERROR); + Valid only if m_iType==CmdVar. + \throw exception_type if token is no variable token. + */ + TBase* GetVar() const + { + if (m_iCode != cmVAR) + throw ParserError(ecINTERNAL_ERROR); return (TBase*)m_pTok; - } + } - //------------------------------------------------------------------------------ - /** \brief Return the number of function arguments. + //------------------------------------------------------------------------------ + /** \brief Return the number of function arguments. - Valid only if m_iType==CmdFUNC. - */ - int GetArgCount() const - { + Valid only if m_iType==CmdFUNC. + */ + int GetArgCount() const + { assert(m_pCallback.get()); if (!m_pCallback->GetAddr()) - throw ParserError(ecINTERNAL_ERROR); + throw ParserError(ecINTERNAL_ERROR); return m_pCallback->GetArgc(); - } + } + + //------------------------------------------------------------------------------ + /** \brief Return the token identifier. - //------------------------------------------------------------------------------ - /** \brief Return the token identifier. - - If #m_iType is cmSTRING the token identifier is the value of the string argument - for a string function. - \return #m_strTok - \throw nothrow - \sa m_strTok - */ - const TString& GetAsString() const - { - return m_strTok; - } - }; + If #m_iType is cmSTRING the token identifier is the value of the string argument + for a string function. + \return #m_strTok + \throw nothrow + \sa m_strTok + */ + const TString& GetAsString() const { return m_strTok; } +}; } // namespace mu #endif diff -Nru mstflint-4.17.0+1/ext_libs/muparser/muParserTokenReader.cpp mstflint-4.21.0+1/ext_libs/muparser/muParserTokenReader.cpp --- mstflint-4.17.0+1/ext_libs/muparser/muParserTokenReader.cpp 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/muParserTokenReader.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ /* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2013 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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. */ #include #include @@ -36,846 +36,850 @@ \brief This file contains the parser token reader implementation. */ - namespace mu { +// Forward declaration +class ParserBase; - // Forward declaration - class ParserBase; - - //--------------------------------------------------------------------------- - /** \brief Copy constructor. +//--------------------------------------------------------------------------- +/** \brief Copy constructor. - \sa Assign - \throw nothrow - */ - ParserTokenReader::ParserTokenReader(const ParserTokenReader &a_Reader) - { + \sa Assign + \throw nothrow +*/ +ParserTokenReader::ParserTokenReader(const ParserTokenReader& a_Reader) +{ Assign(a_Reader); - } - - //--------------------------------------------------------------------------- - /** \brief Assignment operator. - - Self assignment will be suppressed otherwise #Assign is called. - - \param a_Reader Object to copy to this token reader. - \throw nothrow - */ - ParserTokenReader& ParserTokenReader::operator=(const ParserTokenReader &a_Reader) - { - if (&a_Reader!=this) - Assign(a_Reader); +} + +//--------------------------------------------------------------------------- +/** \brief Assignment operator. + + Self assignment will be suppressed otherwise #Assign is called. + + \param a_Reader Object to copy to this token reader. + \throw nothrow +*/ +ParserTokenReader& ParserTokenReader::operator=(const ParserTokenReader& a_Reader) +{ + if (&a_Reader != this) + Assign(a_Reader); return *this; - } +} + +//--------------------------------------------------------------------------- +/** \brief Assign state of a token reader to this token reader. - //--------------------------------------------------------------------------- - /** \brief Assign state of a token reader to this token reader. - - \param a_Reader Object from which the state should be copied. - \throw nothrow - */ - void ParserTokenReader::Assign(const ParserTokenReader &a_Reader) - { + \param a_Reader Object from which the state should be copied. + \throw nothrow +*/ +void ParserTokenReader::Assign(const ParserTokenReader& a_Reader) +{ m_pParser = a_Reader.m_pParser; m_strFormula = a_Reader.m_strFormula; m_iPos = a_Reader.m_iPos; m_iSynFlags = a_Reader.m_iSynFlags; - - m_UsedVar = a_Reader.m_UsedVar; - m_pFunDef = a_Reader.m_pFunDef; - m_pConstDef = a_Reader.m_pConstDef; - m_pVarDef = a_Reader.m_pVarDef; - m_pStrVarDef = a_Reader.m_pStrVarDef; - m_pPostOprtDef = a_Reader.m_pPostOprtDef; - m_pInfixOprtDef = a_Reader.m_pInfixOprtDef; - m_pOprtDef = a_Reader.m_pOprtDef; + + m_UsedVar = a_Reader.m_UsedVar; + m_pFunDef = a_Reader.m_pFunDef; + m_pConstDef = a_Reader.m_pConstDef; + m_pVarDef = a_Reader.m_pVarDef; + m_pStrVarDef = a_Reader.m_pStrVarDef; + m_pPostOprtDef = a_Reader.m_pPostOprtDef; + m_pInfixOprtDef = a_Reader.m_pInfixOprtDef; + m_pOprtDef = a_Reader.m_pOprtDef; m_bIgnoreUndefVar = a_Reader.m_bIgnoreUndefVar; - m_vIdentFun = a_Reader.m_vIdentFun; - m_pFactory = a_Reader.m_pFactory; - m_pFactoryData = a_Reader.m_pFactoryData; - m_iBrackets = a_Reader.m_iBrackets; - m_cArgSep = a_Reader.m_cArgSep; - m_fZero = a_Reader.m_fZero; - m_lastTok = a_Reader.m_lastTok; - } - - //--------------------------------------------------------------------------- - /** \brief Constructor. - - Create a Token reader and bind it to a parser object. - - \pre [assert] a_pParser may not be NULL - \post #m_pParser==a_pParser - \param a_pParent Parent parser object of the token reader. - */ - ParserTokenReader::ParserTokenReader(ParserBase *a_pParent) - :m_pParser(a_pParent) - ,m_strFormula() - ,m_iPos(0) - ,m_iSynFlags(0) - ,m_bIgnoreUndefVar(false) - ,m_pFunDef(NULL) - ,m_pPostOprtDef(NULL) - ,m_pInfixOprtDef(NULL) - ,m_pOprtDef(NULL) - ,m_pConstDef(NULL) - ,m_pStrVarDef(NULL) - ,m_pVarDef(NULL) - ,m_pFactory(NULL) - ,m_pFactoryData(NULL) - ,m_vIdentFun() - ,m_UsedVar() - ,m_fZero(0) - ,m_iBrackets(0) - ,m_lastTok() - ,m_cArgSep(',') - { + m_vIdentFun = a_Reader.m_vIdentFun; + m_pFactory = a_Reader.m_pFactory; + m_pFactoryData = a_Reader.m_pFactoryData; + m_iBrackets = a_Reader.m_iBrackets; + m_cArgSep = a_Reader.m_cArgSep; + m_fZero = a_Reader.m_fZero; + m_lastTok = a_Reader.m_lastTok; +} + +//--------------------------------------------------------------------------- +/** \brief Constructor. + + Create a Token reader and bind it to a parser object. + + \pre [assert] a_pParser may not be NULL + \post #m_pParser==a_pParser + \param a_pParent Parent parser object of the token reader. +*/ +ParserTokenReader::ParserTokenReader(ParserBase* a_pParent) : + m_pParser(a_pParent), + m_strFormula(), + m_iPos(0), + m_iSynFlags(0), + m_bIgnoreUndefVar(false), + m_pFunDef(NULL), + m_pPostOprtDef(NULL), + m_pInfixOprtDef(NULL), + m_pOprtDef(NULL), + m_pConstDef(NULL), + m_pStrVarDef(NULL), + m_pVarDef(NULL), + m_pFactory(NULL), + m_pFactoryData(NULL), + m_vIdentFun(), + m_UsedVar(), + m_fZero(0), + m_iBrackets(0), + m_lastTok(), + m_cArgSep(',') +{ assert(m_pParser); SetParent(m_pParser); - } - - //--------------------------------------------------------------------------- - /** \brief Create instance of a ParserTokenReader identical with this - and return its pointer. - - This is a factory method the calling function must take care of the object destruction. - - \return A new ParserTokenReader object. - \throw nothrow - */ - ParserTokenReader* ParserTokenReader::Clone(ParserBase *a_pParent) const - { +} + +//--------------------------------------------------------------------------- +/** \brief Create instance of a ParserTokenReader identical with this + and return its pointer. + + This is a factory method the calling function must take care of the object destruction. + + \return A new ParserTokenReader object. + \throw nothrow +*/ +ParserTokenReader* ParserTokenReader::Clone(ParserBase* a_pParent) const +{ std::auto_ptr ptr(new ParserTokenReader(*this)); ptr->SetParent(a_pParent); return ptr.release(); - } +} - //--------------------------------------------------------------------------- - ParserTokenReader::token_type& ParserTokenReader::SaveBeforeReturn(const token_type &tok) - { +//--------------------------------------------------------------------------- +ParserTokenReader::token_type& ParserTokenReader::SaveBeforeReturn(const token_type& tok) +{ m_lastTok = tok; return m_lastTok; - } +} - //--------------------------------------------------------------------------- - void ParserTokenReader::AddValIdent(identfun_type a_pCallback) - { +//--------------------------------------------------------------------------- +void ParserTokenReader::AddValIdent(identfun_type a_pCallback) +{ // Use push_front is used to give user defined callbacks a higher priority than // the built in ones. Otherwise reading hex numbers would not work - // since the "0" in "0xff" would always be read first making parsing of + // since the "0" in "0xff" would always be read first making parsing of // the rest impossible. // reference: // http://sourceforge.net/projects/muparser/forums/forum/462843/topic/4824956 m_vIdentFun.push_front(a_pCallback); - } +} - //--------------------------------------------------------------------------- - void ParserTokenReader::SetVarCreator(facfun_type a_pFactory, void *pUserData) - { +//--------------------------------------------------------------------------- +void ParserTokenReader::SetVarCreator(facfun_type a_pFactory, void* pUserData) +{ m_pFactory = a_pFactory; m_pFactoryData = pUserData; - } +} - //--------------------------------------------------------------------------- - /** \brief Return the current position of the token reader in the formula string. +//--------------------------------------------------------------------------- +/** \brief Return the current position of the token reader in the formula string. - \return #m_iPos - \throw nothrow - */ - int ParserTokenReader::GetPos() const - { + \return #m_iPos + \throw nothrow +*/ +int ParserTokenReader::GetPos() const +{ return m_iPos; - } +} - //--------------------------------------------------------------------------- - /** \brief Return a reference to the formula. +//--------------------------------------------------------------------------- +/** \brief Return a reference to the formula. - \return #m_strFormula - \throw nothrow - */ - const string_type& ParserTokenReader::GetExpr() const - { + \return #m_strFormula + \throw nothrow +*/ +const string_type& ParserTokenReader::GetExpr() const +{ return m_strFormula; - } +} - //--------------------------------------------------------------------------- - /** \brief Return a map containing the used variables only. */ - varmap_type& ParserTokenReader::GetUsedVar() - { +//--------------------------------------------------------------------------- +/** \brief Return a map containing the used variables only. */ +varmap_type& ParserTokenReader::GetUsedVar() +{ return m_UsedVar; - } +} + +//--------------------------------------------------------------------------- +/** \brief Initialize the token Reader. - //--------------------------------------------------------------------------- - /** \brief Initialize the token Reader. - - Sets the formula position index to zero and set Syntax flags to default for initial formula parsing. - \pre [assert] triggered if a_szFormula==0 - */ - void ParserTokenReader::SetFormula(const string_type &a_strFormula) - { + Sets the formula position index to zero and set Syntax flags to default for initial formula parsing. + \pre [assert] triggered if a_szFormula==0 +*/ +void ParserTokenReader::SetFormula(const string_type& a_strFormula) +{ m_strFormula = a_strFormula; ReInit(); - } +} + +//--------------------------------------------------------------------------- +/** \brief Set Flag that controls behaviour in case of undefined variables being found. - //--------------------------------------------------------------------------- - /** \brief Set Flag that controls behaviour in case of undefined variables being found. - - If true, the parser does not throw an exception if an undefined variable is found. - otherwise it does. This variable is used internally only! - It suppresses a "undefined variable" exception in GetUsedVar(). - Those function should return a complete list of variables including - those the are not defined by the time of it's call. - */ - void ParserTokenReader::IgnoreUndefVar(bool bIgnore) - { + If true, the parser does not throw an exception if an undefined variable is found. + otherwise it does. This variable is used internally only! + It suppresses a "undefined variable" exception in GetUsedVar(). + Those function should return a complete list of variables including + those the are not defined by the time of it's call. +*/ +void ParserTokenReader::IgnoreUndefVar(bool bIgnore) +{ m_bIgnoreUndefVar = bIgnore; - } +} - //--------------------------------------------------------------------------- - /** \brief Reset the token reader to the start of the formula. +//--------------------------------------------------------------------------- +/** \brief Reset the token reader to the start of the formula. - The syntax flags will be reset to a value appropriate for the - start of a formula. - \post #m_iPos==0, #m_iSynFlags = noOPT | noBC | noPOSTOP | noSTR - \throw nothrow - \sa ESynCodes - */ - void ParserTokenReader::ReInit() - { + The syntax flags will be reset to a value appropriate for the + start of a formula. + \post #m_iPos==0, #m_iSynFlags = noOPT | noBC | noPOSTOP | noSTR + \throw nothrow + \sa ESynCodes +*/ +void ParserTokenReader::ReInit() +{ m_iPos = 0; m_iSynFlags = sfSTART_OF_LINE; m_iBrackets = 0; m_UsedVar.clear(); m_lastTok = token_type(); - } +} - //--------------------------------------------------------------------------- - /** \brief Read the next token from the string. */ - ParserTokenReader::token_type ParserTokenReader::ReadNextToken() - { +//--------------------------------------------------------------------------- +/** \brief Read the next token from the string. */ +ParserTokenReader::token_type ParserTokenReader::ReadNextToken() +{ assert(m_pParser); - const char_type *szFormula = m_strFormula.c_str(); + const char_type* szFormula = m_strFormula.c_str(); token_type tok; // Ignore all non printable characters when reading the expression - while (szFormula[m_iPos]>0 && szFormula[m_iPos]<=0x20) - ++m_iPos; + while (szFormula[m_iPos] > 0 && szFormula[m_iPos] <= 0x20) + ++m_iPos; - if ( IsEOF(tok) ) return SaveBeforeReturn(tok); // Check for end of formula - if ( IsOprt(tok) ) return SaveBeforeReturn(tok); // Check for user defined binary operator - if ( IsFunTok(tok) ) return SaveBeforeReturn(tok); // Check for function token - if ( IsBuiltIn(tok) ) return SaveBeforeReturn(tok); // Check built in operators / tokens - if ( IsArgSep(tok) ) return SaveBeforeReturn(tok); // Check for function argument separators - if ( IsValTok(tok) ) return SaveBeforeReturn(tok); // Check for values / constant tokens - if ( IsVarTok(tok) ) return SaveBeforeReturn(tok); // Check for variable tokens - if ( IsStrVarTok(tok) ) return SaveBeforeReturn(tok); // Check for string variables - if ( IsString(tok) ) return SaveBeforeReturn(tok); // Check for String tokens - if ( IsInfixOpTok(tok) ) return SaveBeforeReturn(tok); // Check for unary operators - if ( IsPostOpTok(tok) ) return SaveBeforeReturn(tok); // Check for unary operators + if (IsEOF(tok)) + return SaveBeforeReturn(tok); // Check for end of formula + if (IsOprt(tok)) + return SaveBeforeReturn(tok); // Check for user defined binary operator + if (IsFunTok(tok)) + return SaveBeforeReturn(tok); // Check for function token + if (IsBuiltIn(tok)) + return SaveBeforeReturn(tok); // Check built in operators / tokens + if (IsArgSep(tok)) + return SaveBeforeReturn(tok); // Check for function argument separators + if (IsValTok(tok)) + return SaveBeforeReturn(tok); // Check for values / constant tokens + if (IsVarTok(tok)) + return SaveBeforeReturn(tok); // Check for variable tokens + if (IsStrVarTok(tok)) + return SaveBeforeReturn(tok); // Check for string variables + if (IsString(tok)) + return SaveBeforeReturn(tok); // Check for String tokens + if (IsInfixOpTok(tok)) + return SaveBeforeReturn(tok); // Check for unary operators + if (IsPostOpTok(tok)) + return SaveBeforeReturn(tok); // Check for unary operators - // Check String for undefined variable token. Done only if a + // Check String for undefined variable token. Done only if a // flag is set indicating to ignore undefined variables. - // This is a way to conditionally avoid an error if - // undefined variables occur. + // This is a way to conditionally avoid an error if + // undefined variables occur. // (The GetUsedVar function must suppress the error for - // undefined variables in order to collect all variable + // undefined variables in order to collect all variable // names including the undefined ones.) - if ( (m_bIgnoreUndefVar || m_pFactory) && IsUndefVarTok(tok) ) - return SaveBeforeReturn(tok); + if ((m_bIgnoreUndefVar || m_pFactory) && IsUndefVarTok(tok)) + return SaveBeforeReturn(tok); // Check for unknown token - // + // // !!! From this point on there is no exit without an exception possible... - // + // string_type strTok; int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); - if (iEnd!=m_iPos) - Error(ecUNASSIGNABLE_TOKEN, m_iPos, strTok); + if (iEnd != m_iPos) + Error(ecUNASSIGNABLE_TOKEN, m_iPos, strTok); Error(ecUNASSIGNABLE_TOKEN, m_iPos, m_strFormula.substr(m_iPos)); return token_type(); // never reached - } +} - //--------------------------------------------------------------------------- - void ParserTokenReader::SetParent(ParserBase *a_pParent) - { - m_pParser = a_pParent; - m_pFunDef = &a_pParent->m_FunDef; - m_pOprtDef = &a_pParent->m_OprtDef; +//--------------------------------------------------------------------------- +void ParserTokenReader::SetParent(ParserBase* a_pParent) +{ + m_pParser = a_pParent; + m_pFunDef = &a_pParent->m_FunDef; + m_pOprtDef = &a_pParent->m_OprtDef; m_pInfixOprtDef = &a_pParent->m_InfixOprtDef; - m_pPostOprtDef = &a_pParent->m_PostOprtDef; - m_pVarDef = &a_pParent->m_VarDef; - m_pStrVarDef = &a_pParent->m_StrVarDef; - m_pConstDef = &a_pParent->m_ConstDef; - } - - //--------------------------------------------------------------------------- - /** \brief Extract all characters that belong to a certain charset. - - \param a_szCharSet [in] Const char array of the characters allowed in the token. - \param a_strTok [out] The string that consists entirely of characters listed in a_szCharSet. - \param a_iPos [in] Position in the string from where to start reading. - \return The Position of the first character not listed in a_szCharSet. - \throw nothrow - */ - int ParserTokenReader::ExtractToken(const char_type *a_szCharSet, - string_type &a_sTok, - int a_iPos) const - { + m_pPostOprtDef = &a_pParent->m_PostOprtDef; + m_pVarDef = &a_pParent->m_VarDef; + m_pStrVarDef = &a_pParent->m_StrVarDef; + m_pConstDef = &a_pParent->m_ConstDef; +} + +//--------------------------------------------------------------------------- +/** \brief Extract all characters that belong to a certain charset. + + \param a_szCharSet [in] Const char array of the characters allowed in the token. + \param a_strTok [out] The string that consists entirely of characters listed in a_szCharSet. + \param a_iPos [in] Position in the string from where to start reading. + \return The Position of the first character not listed in a_szCharSet. + \throw nothrow +*/ +int ParserTokenReader::ExtractToken(const char_type* a_szCharSet, string_type& a_sTok, int a_iPos) const +{ int iEnd = (int)m_strFormula.find_first_not_of(a_szCharSet, a_iPos); - if (iEnd==(int)string_type::npos) + if (iEnd == (int)string_type::npos) iEnd = (int)m_strFormula.length(); - + // Assign token string if there was something found - if (a_iPos!=iEnd) - a_sTok = string_type( m_strFormula.begin()+a_iPos, m_strFormula.begin()+iEnd); + if (a_iPos != iEnd) + a_sTok = string_type(m_strFormula.begin() + a_iPos, m_strFormula.begin() + iEnd); return iEnd; - } +} - //--------------------------------------------------------------------------- - /** \brief Check Expression for the presence of a binary operator token. - - Userdefined binary operator "++" gives inconsistent parsing result for - the equations "a++b" and "a ++ b" if alphabetic characters are allowed - in operator tokens. To avoid this this function checks specifically - for operator tokens. - */ - int ParserTokenReader::ExtractOperatorToken(string_type &a_sTok, - int a_iPos) const - { +//--------------------------------------------------------------------------- +/** \brief Check Expression for the presence of a binary operator token. + + Userdefined binary operator "++" gives inconsistent parsing result for + the equations "a++b" and "a ++ b" if alphabetic characters are allowed + in operator tokens. To avoid this this function checks specifically + for operator tokens. +*/ +int ParserTokenReader::ExtractOperatorToken(string_type& a_sTok, int a_iPos) const +{ // Changed as per Issue 6: https://code.google.com/p/muparser/issues/detail?id=6 int iEnd = (int)m_strFormula.find_first_not_of(m_pParser->ValidOprtChars(), a_iPos); - if (iEnd==(int)string_type::npos) - iEnd = (int)m_strFormula.length(); + if (iEnd == (int)string_type::npos) + iEnd = (int)m_strFormula.length(); // Assign token string if there was something found - if (a_iPos!=iEnd) + if (a_iPos != iEnd) { - a_sTok = string_type( m_strFormula.begin() + a_iPos, m_strFormula.begin() + iEnd); - return iEnd; + a_sTok = string_type(m_strFormula.begin() + a_iPos, m_strFormula.begin() + iEnd); + return iEnd; } else { - // There is still the chance of having to deal with an operator consisting exclusively - // of alphabetic characters. - return ExtractToken(MUP_CHARS, a_sTok, a_iPos); - } - } - - //--------------------------------------------------------------------------- - /** \brief Check if a built in operator or other token can be found - \param a_Tok [out] Operator token if one is found. This can either be a binary operator or an infix operator token. - \return true if an operator token has been found. - */ - bool ParserTokenReader::IsBuiltIn(token_type &a_Tok) - { - const char_type **const pOprtDef = m_pParser->GetOprtDef(), - *const szFormula = m_strFormula.c_str(); + // There is still the chance of having to deal with an operator consisting exclusively + // of alphabetic characters. + return ExtractToken(MUP_CHARS, a_sTok, a_iPos); + } +} + +//--------------------------------------------------------------------------- +/** \brief Check if a built in operator or other token can be found + \param a_Tok [out] Operator token if one is found. This can either be a binary operator or an infix operator token. + \return true if an operator token has been found. +*/ +bool ParserTokenReader::IsBuiltIn(token_type& a_Tok) +{ + const char_type **const pOprtDef = m_pParser->GetOprtDef(), *const szFormula = m_strFormula.c_str(); // Compare token with function and operator strings // check string for operator/function - for (int i=0; pOprtDef[i]; i++) + for (int i = 0; pOprtDef[i]; i++) { - std::size_t len( std::char_traits::length(pOprtDef[i]) ); - if ( string_type(pOprtDef[i]) == string_type(szFormula + m_iPos, szFormula + m_iPos + len) ) - { - switch(i) + std::size_t len(std::char_traits::length(pOprtDef[i])); + if (string_type(pOprtDef[i]) == string_type(szFormula + m_iPos, szFormula + m_iPos + len)) { - //case cmAND: - //case cmOR: - //case cmXOR: - case cmLAND: - case cmLOR: - case cmLT: - case cmGT: - case cmLE: - case cmGE: - case cmNEQ: - case cmEQ: - case cmADD: - case cmSUB: - case cmMUL: - case cmDIV: - case cmPOW: - case cmASSIGN: - //if (len!=sTok.length()) - // continue; - - // The assignment operator need special treatment - if (i==cmASSIGN && m_iSynFlags & noASSIGN) - Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[i]); - - if (!m_pParser->HasBuiltInOprt()) continue; - if (m_iSynFlags & noOPT) - { - // Maybe its an infix operator not an operator - // Both operator types can share characters in - // their identifiers - if ( IsInfixOpTok(a_Tok) ) - return true; - - Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[i]); - } - - m_iSynFlags = noBC | noOPT | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE | noEND; - break; - - case cmBO: - if (m_iSynFlags & noBO) - Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]); - - if (m_lastTok.GetCode()==cmFUNC) - m_iSynFlags = noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE; - else - m_iSynFlags = noBC | noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN| noIF | noELSE; - - ++m_iBrackets; - break; - - case cmBC: - if (m_iSynFlags & noBC) - Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]); - - m_iSynFlags = noBO | noVAR | noVAL | noFUN | noINFIXOP | noSTR | noASSIGN; - - if (--m_iBrackets<0) - Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]); - break; - - case cmELSE: - if (m_iSynFlags & noELSE) - Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]); - - m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE; - break; - - case cmIF: - if (m_iSynFlags & noIF) - Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]); - - m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE; - break; - - default: // The operator is listed in c_DefaultOprt, but not here. This is a bad thing... - Error(ecINTERNAL_ERROR); - } // switch operator id + switch (i) + { + // case cmAND: + // case cmOR: + // case cmXOR: + case cmLAND: + case cmLOR: + case cmLT: + case cmGT: + case cmLE: + case cmGE: + case cmNEQ: + case cmEQ: + case cmADD: + case cmSUB: + case cmMUL: + case cmDIV: + case cmPOW: + case cmASSIGN: + // if (len!=sTok.length()) + // continue; + + // The assignment operator need special treatment + if (i == cmASSIGN && m_iSynFlags & noASSIGN) + Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[i]); + + if (!m_pParser->HasBuiltInOprt()) + continue; + if (m_iSynFlags & noOPT) + { + // Maybe its an infix operator not an operator + // Both operator types can share characters in + // their identifiers + if (IsInfixOpTok(a_Tok)) + return true; + + Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[i]); + } + + m_iSynFlags = noBC | noOPT | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE | noEND; + break; + + case cmBO: + if (m_iSynFlags & noBO) + Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]); + + if (m_lastTok.GetCode() == cmFUNC) + m_iSynFlags = noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE; + else + m_iSynFlags = noBC | noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE; + + ++m_iBrackets; + break; + + case cmBC: + if (m_iSynFlags & noBC) + Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]); + + m_iSynFlags = noBO | noVAR | noVAL | noFUN | noINFIXOP | noSTR | noASSIGN; + + if (--m_iBrackets < 0) + Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]); + break; + + case cmELSE: + if (m_iSynFlags & noELSE) + Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]); + + m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE; + break; + + case cmIF: + if (m_iSynFlags & noIF) + Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]); + + m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE; + break; + + default: // The operator is listed in c_DefaultOprt, but not here. This is a bad thing... + Error(ecINTERNAL_ERROR); + } // switch operator id + + m_iPos += (int)len; + a_Tok.Set((ECmdCode)i, pOprtDef[i]); + return true; + } // if operator string found + } // end of for all operator strings - m_iPos += (int)len; - a_Tok.Set( (ECmdCode)i, pOprtDef[i] ); - return true; - } // if operator string found - } // end of for all operator strings - return false; - } +} - //--------------------------------------------------------------------------- - bool ParserTokenReader::IsArgSep(token_type &a_Tok) - { +//--------------------------------------------------------------------------- +bool ParserTokenReader::IsArgSep(token_type& a_Tok) +{ const char_type* szFormula = m_strFormula.c_str(); - if (szFormula[m_iPos]==m_cArgSep) + if (szFormula[m_iPos] == m_cArgSep) { - // copy the separator into null terminated string - char_type szSep[2]; - szSep[0] = m_cArgSep; - szSep[1] = 0; - - if (m_iSynFlags & noARG_SEP) - Error(ecUNEXPECTED_ARG_SEP, m_iPos, szSep); - - m_iSynFlags = noBC | noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN; - m_iPos++; - a_Tok.Set(cmARG_SEP, szSep); - return true; + // copy the separator into null terminated string + char_type szSep[2]; + szSep[0] = m_cArgSep; + szSep[1] = 0; + + if (m_iSynFlags & noARG_SEP) + Error(ecUNEXPECTED_ARG_SEP, m_iPos, szSep); + + m_iSynFlags = noBC | noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN; + m_iPos++; + a_Tok.Set(cmARG_SEP, szSep); + return true; } return false; - } +} - //--------------------------------------------------------------------------- - /** \brief Check for End of Formula. +//--------------------------------------------------------------------------- +/** \brief Check for End of Formula. - \return true if an end of formula is found false otherwise. - \param a_Tok [out] If an eof is found the corresponding token will be stored there. - \throw nothrow - \sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsString, IsInfixOpTok, IsPostOpTok - */ - bool ParserTokenReader::IsEOF(token_type &a_Tok) - { + \return true if an end of formula is found false otherwise. + \param a_Tok [out] If an eof is found the corresponding token will be stored there. + \throw nothrow + \sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsString, IsInfixOpTok, IsPostOpTok +*/ +bool ParserTokenReader::IsEOF(token_type& a_Tok) +{ const char_type* szFormula = m_strFormula.c_str(); // check for EOF - if ( !szFormula[m_iPos] /*|| szFormula[m_iPos] == '\n'*/) + if (!szFormula[m_iPos] /*|| szFormula[m_iPos] == '\n'*/) { - if ( m_iSynFlags & noEND ) - Error(ecUNEXPECTED_EOF, m_iPos); + if (m_iSynFlags & noEND) + Error(ecUNEXPECTED_EOF, m_iPos); - if (m_iBrackets>0) - Error(ecMISSING_PARENS, m_iPos, _T(")")); + if (m_iBrackets > 0) + Error(ecMISSING_PARENS, m_iPos, _T(")")); - m_iSynFlags = 0; - a_Tok.Set(cmEND); - return true; + m_iSynFlags = 0; + a_Tok.Set(cmEND); + return true; } return false; - } +} - //--------------------------------------------------------------------------- - /** \brief Check if a string position contains a unary infix operator. - \return true if a function token has been found false otherwise. - */ - bool ParserTokenReader::IsInfixOpTok(token_type &a_Tok) - { +//--------------------------------------------------------------------------- +/** \brief Check if a string position contains a unary infix operator. + \return true if a function token has been found false otherwise. +*/ +bool ParserTokenReader::IsInfixOpTok(token_type& a_Tok) +{ string_type sTok; int iEnd = ExtractToken(m_pParser->ValidInfixOprtChars(), sTok, m_iPos); - if (iEnd==m_iPos) - return false; + if (iEnd == m_iPos) + return false; // iterate over all postfix operator strings funmap_type::const_reverse_iterator it = m_pInfixOprtDef->rbegin(); - for ( ; it!=m_pInfixOprtDef->rend(); ++it) + for (; it != m_pInfixOprtDef->rend(); ++it) { - if (sTok.find(it->first)!=0) - continue; + if (sTok.find(it->first) != 0) + continue; - a_Tok.Set(it->second, it->first); - m_iPos += (int)it->first.length(); + a_Tok.Set(it->second, it->first); + m_iPos += (int)it->first.length(); - if (m_iSynFlags & noINFIXOP) - Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString()); + if (m_iSynFlags & noINFIXOP) + Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString()); - m_iSynFlags = noPOSTOP | noINFIXOP | noOPT | noBC | noSTR | noASSIGN; - return true; + m_iSynFlags = noPOSTOP | noINFIXOP | noOPT | noBC | noSTR | noASSIGN; + return true; } return false; -/* - a_Tok.Set(item->second, sTok); - m_iPos = (int)iEnd; + /* + a_Tok.Set(item->second, sTok); + m_iPos = (int)iEnd; - if (m_iSynFlags & noINFIXOP) - Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString()); + if (m_iSynFlags & noINFIXOP) + Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString()); - m_iSynFlags = noPOSTOP | noINFIXOP | noOPT | noBC | noSTR | noASSIGN; - return true; -*/ - } + m_iSynFlags = noPOSTOP | noINFIXOP | noOPT | noBC | noSTR | noASSIGN; + return true; + */ +} - //--------------------------------------------------------------------------- - /** \brief Check whether the token at a given position is a function token. - \param a_Tok [out] If a value token is found it will be placed here. - \throw ParserException if Syntaxflags do not allow a function at a_iPos - \return true if a function token has been found false otherwise. - \pre [assert] m_pParser!=0 - */ - bool ParserTokenReader::IsFunTok(token_type &a_Tok) - { +//--------------------------------------------------------------------------- +/** \brief Check whether the token at a given position is a function token. + \param a_Tok [out] If a value token is found it will be placed here. + \throw ParserException if Syntaxflags do not allow a function at a_iPos + \return true if a function token has been found false otherwise. + \pre [assert] m_pParser!=0 +*/ +bool ParserTokenReader::IsFunTok(token_type& a_Tok) +{ string_type strTok; int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); - if (iEnd==m_iPos) - return false; + if (iEnd == m_iPos) + return false; funmap_type::const_iterator item = m_pFunDef->find(strTok); - if (item==m_pFunDef->end()) - return false; + if (item == m_pFunDef->end()) + return false; // Check if the next sign is an opening bracket - const char_type *szFormula = m_strFormula.c_str(); - if (szFormula[iEnd]!='(') - return false; + const char_type* szFormula = m_strFormula.c_str(); + if (szFormula[iEnd] != '(') + return false; a_Tok.Set(item->second, strTok); m_iPos = (int)iEnd; if (m_iSynFlags & noFUN) - Error(ecUNEXPECTED_FUN, m_iPos-(int)a_Tok.GetAsString().length(), a_Tok.GetAsString()); + Error(ecUNEXPECTED_FUN, m_iPos - (int)a_Tok.GetAsString().length(), a_Tok.GetAsString()); m_iSynFlags = noANY ^ noBO; return true; - } +} - //--------------------------------------------------------------------------- - /** \brief Check if a string position contains a binary operator. - \param a_Tok [out] Operator token if one is found. This can either be a binary operator or an infix operator token. - \return true if an operator token has been found. - */ - bool ParserTokenReader::IsOprt(token_type &a_Tok) - { - const char_type *const szExpr = m_strFormula.c_str(); +//--------------------------------------------------------------------------- +/** \brief Check if a string position contains a binary operator. + \param a_Tok [out] Operator token if one is found. This can either be a binary operator or an infix operator token. + \return true if an operator token has been found. +*/ +bool ParserTokenReader::IsOprt(token_type& a_Tok) +{ + const char_type* const szExpr = m_strFormula.c_str(); string_type strTok; int iEnd = ExtractOperatorToken(strTok, m_iPos); - if (iEnd==m_iPos) - return false; + if (iEnd == m_iPos) + return false; // Check if the operator is a built in operator, if so ignore it here - const char_type **const pOprtDef = m_pParser->GetOprtDef(); - for (int i=0; m_pParser->HasBuiltInOprt() && pOprtDef[i]; ++i) + const char_type** const pOprtDef = m_pParser->GetOprtDef(); + for (int i = 0; m_pParser->HasBuiltInOprt() && pOprtDef[i]; ++i) { - if (string_type(pOprtDef[i])==strTok) - return false; + if (string_type(pOprtDef[i]) == strTok) + return false; } // Note: // All tokens in oprt_bin_maptype are have been sorted by their length // Long operators must come first! Otherwise short names (like: "add") that - // are part of long token names (like: "add123") will be found instead + // are part of long token names (like: "add123") will be found instead // of the long ones. // Length sorting is done with ascending length so we use a reverse iterator here. funmap_type::const_reverse_iterator it = m_pOprtDef->rbegin(); - for ( ; it!=m_pOprtDef->rend(); ++it) + for (; it != m_pOprtDef->rend(); ++it) { - const string_type &sID = it->first; - if ( sID == string_type(szExpr + m_iPos, szExpr + m_iPos + sID.length()) ) - { - a_Tok.Set(it->second, strTok); - - // operator was found - if (m_iSynFlags & noOPT) + const string_type& sID = it->first; + if (sID == string_type(szExpr + m_iPos, szExpr + m_iPos + sID.length())) { - // An operator was found but is not expected to occur at - // this position of the formula, maybe it is an infix - // operator, not a binary operator. Both operator types - // can share characters in their identifiers. - if ( IsInfixOpTok(a_Tok) ) - return true; - else - { - // nope, no infix operator - return false; - //Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString()); - } + a_Tok.Set(it->second, strTok); - } + // operator was found + if (m_iSynFlags & noOPT) + { + // An operator was found but is not expected to occur at + // this position of the formula, maybe it is an infix + // operator, not a binary operator. Both operator types + // can share characters in their identifiers. + if (IsInfixOpTok(a_Tok)) + return true; + else + { + // nope, no infix operator + return false; + // Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString()); + } + } - m_iPos += (int)sID.length(); - m_iSynFlags = noBC | noOPT | noARG_SEP | noPOSTOP | noEND | noASSIGN; - return true; - } + m_iPos += (int)sID.length(); + m_iSynFlags = noBC | noOPT | noARG_SEP | noPOSTOP | noEND | noASSIGN; + return true; + } } return false; - } +} - //--------------------------------------------------------------------------- - /** \brief Check if a string position contains a unary post value operator. */ - bool ParserTokenReader::IsPostOpTok(token_type &a_Tok) - { +//--------------------------------------------------------------------------- +/** \brief Check if a string position contains a unary post value operator. */ +bool ParserTokenReader::IsPostOpTok(token_type& a_Tok) +{ // Do not check for postfix operators if they are not allowed at // the current expression index. // - // This will fix the bug reported here: + // This will fix the bug reported here: // // http://sourceforge.net/tracker/index.php?func=detail&aid=3343891&group_id=137191&atid=737979 // if (m_iSynFlags & noPOSTOP) - return false; + return false; // // Tricky problem with equations like "3m+5": - // m is a postfix operator, + is a valid sign for postfix operators and - // for binary operators parser detects "m+" as operator string and + // m is a postfix operator, + is a valid sign for postfix operators and + // for binary operators parser detects "m+" as operator string and // finds no matching postfix operator. - // + // // This is a special case so this routine slightly differs from the other // token readers. - + // Test if there could be a postfix operator string_type sTok; int iEnd = ExtractToken(m_pParser->ValidOprtChars(), sTok, m_iPos); - if (iEnd==m_iPos) - return false; + if (iEnd == m_iPos) + return false; // iterate over all postfix operator strings funmap_type::const_reverse_iterator it = m_pPostOprtDef->rbegin(); - for ( ; it!=m_pPostOprtDef->rend(); ++it) + for (; it != m_pPostOprtDef->rend(); ++it) { - if (sTok.find(it->first)!=0) - continue; + if (sTok.find(it->first) != 0) + continue; - a_Tok.Set(it->second, sTok); - m_iPos += (int)it->first.length(); + a_Tok.Set(it->second, sTok); + m_iPos += (int)it->first.length(); - m_iSynFlags = noVAL | noVAR | noFUN | noBO | noPOSTOP | noSTR | noASSIGN; - return true; + m_iSynFlags = noVAL | noVAR | noFUN | noBO | noPOSTOP | noSTR | noASSIGN; + return true; } return false; - } +} - //--------------------------------------------------------------------------- - /** \brief Check whether the token at a given position is a value token. +//--------------------------------------------------------------------------- +/** \brief Check whether the token at a given position is a value token. - Value tokens are either values or constants. + Value tokens are either values or constants. - \param a_Tok [out] If a value token is found it will be placed here. - \return true if a value token has been found. - */ - bool ParserTokenReader::IsValTok(token_type &a_Tok) - { + \param a_Tok [out] If a value token is found it will be placed here. + \return true if a value token has been found. +*/ +bool ParserTokenReader::IsValTok(token_type& a_Tok) +{ assert(m_pConstDef); assert(m_pParser); string_type strTok; value_type fVal(0); int iEnd(0); - + // 2.) Check for user defined constant // Read everything that could be a constant name iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); - if (iEnd!=m_iPos) + if (iEnd != m_iPos) { - valmap_type::const_iterator item = m_pConstDef->find(strTok); - if (item!=m_pConstDef->end()) - { - m_iPos = iEnd; - a_Tok.SetVal(item->second, strTok); + valmap_type::const_iterator item = m_pConstDef->find(strTok); + if (item != m_pConstDef->end()) + { + m_iPos = iEnd; + a_Tok.SetVal(item->second, strTok); - if (m_iSynFlags & noVAL) - Error(ecUNEXPECTED_VAL, m_iPos - (int)strTok.length(), strTok); + if (m_iSynFlags & noVAL) + Error(ecUNEXPECTED_VAL, m_iPos - (int)strTok.length(), strTok); - m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN; - return true; - } + m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN; + return true; + } } // 3.call the value recognition functions provided by the user // Call user defined value recognition functions std::list::const_iterator item = m_vIdentFun.begin(); - for (item = m_vIdentFun.begin(); item!=m_vIdentFun.end(); ++item) + for (item = m_vIdentFun.begin(); item != m_vIdentFun.end(); ++item) { - int iStart = m_iPos; - if ( (*item)(m_strFormula.c_str() + m_iPos, &m_iPos, &fVal)==1 ) - { - // 2013-11-27 Issue 2: https://code.google.com/p/muparser/issues/detail?id=2 - strTok.assign(m_strFormula.c_str(), iStart, m_iPos-iStart); + int iStart = m_iPos; + if ((*item)(m_strFormula.c_str() + m_iPos, &m_iPos, &fVal) == 1) + { + // 2013-11-27 Issue 2: https://code.google.com/p/muparser/issues/detail?id=2 + strTok.assign(m_strFormula.c_str(), iStart, m_iPos - iStart); - if (m_iSynFlags & noVAL) - Error(ecUNEXPECTED_VAL, m_iPos - (int)strTok.length(), strTok); + if (m_iSynFlags & noVAL) + Error(ecUNEXPECTED_VAL, m_iPos - (int)strTok.length(), strTok); - a_Tok.SetVal(fVal, strTok); - m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN; - return true; - } + a_Tok.SetVal(fVal, strTok); + m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN; + return true; + } } return false; - } +} - //--------------------------------------------------------------------------- - /** \brief Check wheter a token at a given position is a variable token. - \param a_Tok [out] If a variable token has been found it will be placed here. - \return true if a variable token has been found. - */ - bool ParserTokenReader::IsVarTok(token_type &a_Tok) - { +//--------------------------------------------------------------------------- +/** \brief Check wheter a token at a given position is a variable token. + \param a_Tok [out] If a variable token has been found it will be placed here. + \return true if a variable token has been found. +*/ +bool ParserTokenReader::IsVarTok(token_type& a_Tok) +{ if (m_pVarDef->empty()) - return false; + return false; string_type strTok; int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); - if (iEnd==m_iPos) - return false; + if (iEnd == m_iPos) + return false; - varmap_type::const_iterator item = m_pVarDef->find(strTok); - if (item==m_pVarDef->end()) - return false; + varmap_type::const_iterator item = m_pVarDef->find(strTok); + if (item == m_pVarDef->end()) + return false; if (m_iSynFlags & noVAR) - Error(ecUNEXPECTED_VAR, m_iPos, strTok); + Error(ecUNEXPECTED_VAR, m_iPos, strTok); m_pParser->OnDetectVar(&m_strFormula, m_iPos, iEnd); m_iPos = iEnd; a_Tok.SetVar(item->second, strTok); - m_UsedVar[item->first] = item->second; // Add variable to used-var-list + m_UsedVar[item->first] = item->second; // Add variable to used-var-list m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR; -// Zur Info hier die SynFlags von IsVal(): -// m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN; + // Zur Info hier die SynFlags von IsVal(): + // m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN; return true; - } +} - //--------------------------------------------------------------------------- - bool ParserTokenReader::IsStrVarTok(token_type &a_Tok) - { +//--------------------------------------------------------------------------- +bool ParserTokenReader::IsStrVarTok(token_type& a_Tok) +{ if (!m_pStrVarDef || m_pStrVarDef->empty()) - return false; + return false; string_type strTok; int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); - if (iEnd==m_iPos) - return false; + if (iEnd == m_iPos) + return false; - strmap_type::const_iterator item = m_pStrVarDef->find(strTok); - if (item==m_pStrVarDef->end()) - return false; + strmap_type::const_iterator item = m_pStrVarDef->find(strTok); + if (item == m_pStrVarDef->end()) + return false; if (m_iSynFlags & noSTR) - Error(ecUNEXPECTED_VAR, m_iPos, strTok); + Error(ecUNEXPECTED_VAR, m_iPos, strTok); m_iPos = iEnd; if (!m_pParser->m_vStringVarBuf.size()) - Error(ecINTERNAL_ERROR); + Error(ecINTERNAL_ERROR); - a_Tok.SetString(m_pParser->m_vStringVarBuf[item->second], m_pParser->m_vStringVarBuf.size() ); + a_Tok.SetString(m_pParser->m_vStringVarBuf[item->second], m_pParser->m_vStringVarBuf.size()); - m_iSynFlags = noANY ^ ( noBC | noOPT | noEND | noARG_SEP); + m_iSynFlags = noANY ^ (noBC | noOPT | noEND | noARG_SEP); return true; - } - +} - //--------------------------------------------------------------------------- - /** \brief Check wheter a token at a given position is an undefined variable. +//--------------------------------------------------------------------------- +/** \brief Check wheter a token at a given position is an undefined variable. - \param a_Tok [out] If a variable tom_pParser->m_vStringBufken has been found it will be placed here. - \return true if a variable token has been found. - \throw nothrow - */ - bool ParserTokenReader::IsUndefVarTok(token_type &a_Tok) - { + \param a_Tok [out] If a variable tom_pParser->m_vStringBufken has been found it will be placed here. + \return true if a variable token has been found. + \throw nothrow +*/ +bool ParserTokenReader::IsUndefVarTok(token_type& a_Tok) +{ string_type strTok; - int iEnd( ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos) ); - if ( iEnd==m_iPos ) - return false; + int iEnd(ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos)); + if (iEnd == m_iPos) + return false; if (m_iSynFlags & noVAR) { - // 20061021 added token string strTok instead of a_Tok.GetAsString() as the - // token identifier. - // related bug report: - // http://sourceforge.net/tracker/index.php?func=detail&aid=1578779&group_id=137191&atid=737979 - Error(ecUNEXPECTED_VAR, m_iPos - (int)a_Tok.GetAsString().length(), strTok); + // 20061021 added token string strTok instead of a_Tok.GetAsString() as the + // token identifier. + // related bug report: + // http://sourceforge.net/tracker/index.php?func=detail&aid=1578779&group_id=137191&atid=737979 + Error(ecUNEXPECTED_VAR, m_iPos - (int)a_Tok.GetAsString().length(), strTok); } // If a factory is available implicitely create new variables if (m_pFactory) { - value_type *fVar = m_pFactory(strTok.c_str(), m_pFactoryData); - a_Tok.SetVar(fVar, strTok ); + value_type* fVar = m_pFactory(strTok.c_str(), m_pFactoryData); + a_Tok.SetVar(fVar, strTok); - // Do not use m_pParser->DefineVar( strTok, fVar ); - // in order to define the new variable, it will clear the - // m_UsedVar array which will kill previously defined variables - // from the list - // This is safe because the new variable can never override an existing one - // because they are checked first! - (*m_pVarDef)[strTok] = fVar; - m_UsedVar[strTok] = fVar; // Add variable to used-var-list + // Do not use m_pParser->DefineVar( strTok, fVar ); + // in order to define the new variable, it will clear the + // m_UsedVar array which will kill previously defined variables + // from the list + // This is safe because the new variable can never override an existing one + // because they are checked first! + (*m_pVarDef)[strTok] = fVar; + m_UsedVar[strTok] = fVar; // Add variable to used-var-list } else { - a_Tok.SetVar((value_type*)&m_fZero, strTok); - m_UsedVar[strTok] = 0; // Add variable to used-var-list + a_Tok.SetVar((value_type*)&m_fZero, strTok); + m_UsedVar[strTok] = 0; // Add variable to used-var-list } m_iPos = iEnd; @@ -883,76 +887,74 @@ // Call the variable factory in order to let it define a new parser variable m_iSynFlags = noVAL | noVAR | noFUN | noBO | noPOSTOP | noINFIXOP | noSTR; return true; - } +} +//--------------------------------------------------------------------------- +/** \brief Check wheter a token at a given position is a string. + \param a_Tok [out] If a variable token has been found it will be placed here. + \return true if a string token has been found. + \sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsEOF, IsInfixOpTok, IsPostOpTok + \throw nothrow +*/ +bool ParserTokenReader::IsString(token_type& a_Tok) +{ + if (m_strFormula[m_iPos] != '"') + return false; - //--------------------------------------------------------------------------- - /** \brief Check wheter a token at a given position is a string. - \param a_Tok [out] If a variable token has been found it will be placed here. - \return true if a string token has been found. - \sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsEOF, IsInfixOpTok, IsPostOpTok - \throw nothrow - */ - bool ParserTokenReader::IsString(token_type &a_Tok) - { - if (m_strFormula[m_iPos]!='"') - return false; - - string_type strBuf(&m_strFormula[m_iPos+1]); + string_type strBuf(&m_strFormula[m_iPos + 1]); std::size_t iEnd(0), iSkip(0); // parser over escaped '\"' end replace them with '"' - for(iEnd=(int)strBuf.find( _T("\"") ); iEnd!=0 && iEnd!=string_type::npos; iEnd=(int)strBuf.find( _T("\""), iEnd)) + for (iEnd = (int)strBuf.find(_T("\"")); iEnd != 0 && iEnd != string_type::npos; + iEnd = (int)strBuf.find(_T("\""), iEnd)) { - if (strBuf[iEnd-1]!='\\') break; - strBuf.replace(iEnd-1, 2, _T("\"") ); - iSkip++; + if (strBuf[iEnd - 1] != '\\') + break; + strBuf.replace(iEnd - 1, 2, _T("\"")); + iSkip++; } - if (iEnd==string_type::npos) - Error(ecUNTERMINATED_STRING, m_iPos, _T("\"") ); + if (iEnd == string_type::npos) + Error(ecUNTERMINATED_STRING, m_iPos, _T("\"")); - string_type strTok(strBuf.begin(), strBuf.begin()+iEnd); + string_type strTok(strBuf.begin(), strBuf.begin() + iEnd); if (m_iSynFlags & noSTR) - Error(ecUNEXPECTED_STR, m_iPos, strTok); + Error(ecUNEXPECTED_STR, m_iPos, strTok); - m_pParser->m_vStringBuf.push_back(strTok); // Store string in internal buffer + m_pParser->m_vStringBuf.push_back(strTok); // Store string in internal buffer a_Tok.SetString(strTok, m_pParser->m_vStringBuf.size()); - m_iPos += (int)strTok.length() + 2 + (int)iSkip; // +2 wg Anführungszeichen; +iSkip für entfernte escape zeichen - m_iSynFlags = noANY ^ ( noARG_SEP | noBC | noOPT | noEND ); + m_iPos += (int)strTok.length() + 2 + (int)iSkip; // +2 wg Anführungszeichen; +iSkip für entfernte escape zeichen + m_iSynFlags = noANY ^ (noARG_SEP | noBC | noOPT | noEND); return true; - } +} - //--------------------------------------------------------------------------- - /** \brief Create an error containing the parse error position. +//--------------------------------------------------------------------------- +/** \brief Create an error containing the parse error position. - This function will create an Parser Exception object containing the error text and its position. + This function will create an Parser Exception object containing the error text and its position. - \param a_iErrc [in] The error code of type #EErrorCodes. - \param a_iPos [in] The position where the error was detected. - \param a_strTok [in] The token string representation associated with the error. - \throw ParserException always throws thats the only purpose of this function. - */ - void ParserTokenReader::Error( EErrorCodes a_iErrc, - int a_iPos, - const string_type &a_sTok) const - { + \param a_iErrc [in] The error code of type #EErrorCodes. + \param a_iPos [in] The position where the error was detected. + \param a_strTok [in] The token string representation associated with the error. + \throw ParserException always throws thats the only purpose of this function. +*/ +void ParserTokenReader::Error(EErrorCodes a_iErrc, int a_iPos, const string_type& a_sTok) const +{ m_pParser->Error(a_iErrc, a_iPos, a_sTok); - } +} - //--------------------------------------------------------------------------- - void ParserTokenReader::SetArgSep(char_type cArgSep) - { +//--------------------------------------------------------------------------- +void ParserTokenReader::SetArgSep(char_type cArgSep) +{ m_cArgSep = cArgSep; - } +} - //--------------------------------------------------------------------------- - char_type ParserTokenReader::GetArgSep() const - { +//--------------------------------------------------------------------------- +char_type ParserTokenReader::GetArgSep() const +{ return m_cArgSep; - } +} } // namespace mu - diff -Nru mstflint-4.17.0+1/ext_libs/muparser/muParserTokenReader.h mstflint-4.21.0+1/ext_libs/muparser/muParserTokenReader.h --- mstflint-4.17.0+1/ext_libs/muparser/muParserTokenReader.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/muparser/muParserTokenReader.h 2022-07-31 09:33:57.000000000 +0000 @@ -1,26 +1,26 @@ /* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ Copyright (C) 2004-2013 Ingo Berg - Permission is hereby granted, free of charge, to any person obtaining a copy of this + 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 + 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 + 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. + 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 MU_PARSER_TOKEN_READER_H @@ -42,120 +42,110 @@ \brief This file contains the parser token reader definition. */ - namespace mu { - // Forward declaration - class ParserBase; +// Forward declaration +class ParserBase; + +/** \brief Token reader for the ParserBase class. - /** \brief Token reader for the ParserBase class. +*/ +class ParserTokenReader +{ +private: + typedef ParserToken token_type; - */ - class ParserTokenReader - { - private: - - typedef ParserToken token_type; - - public: - - ParserTokenReader(ParserBase *a_pParent); - ParserTokenReader* Clone(ParserBase *a_pParent) const; - - void AddValIdent(identfun_type a_pCallback); - void SetVarCreator(facfun_type a_pFactory, void *pUserData); - void SetFormula(const string_type &a_strFormula); - void SetArgSep(char_type cArgSep); - - int GetPos() const; - const string_type& GetExpr() const; - varmap_type& GetUsedVar(); - char_type GetArgSep() const; - - void IgnoreUndefVar(bool bIgnore); - void ReInit(); - token_type ReadNextToken(); - - private: - - /** \brief Syntax codes. - - The syntax codes control the syntax check done during the first time parsing of - the expression string. They are flags that indicate which tokens are allowed next - if certain tokens are identified. - */ - enum ESynCodes - { - noBO = 1 << 0, ///< to avoid i.e. "cos(7)(" - noBC = 1 << 1, ///< to avoid i.e. "sin)" or "()" - noVAL = 1 << 2, ///< to avoid i.e. "tan 2" or "sin(8)3.14" - noVAR = 1 << 3, ///< to avoid i.e. "sin a" or "sin(8)a" - noARG_SEP = 1 << 4, ///< to avoid i.e. ",," or "+," ... - noFUN = 1 << 5, ///< to avoid i.e. "sqrt cos" or "(1)sin" - noOPT = 1 << 6, ///< to avoid i.e. "(+)" - noPOSTOP = 1 << 7, ///< to avoid i.e. "(5!!)" "sin!" - noINFIXOP = 1 << 8, ///< to avoid i.e. "++4" "!!4" - noEND = 1 << 9, ///< to avoid unexpected end of formula - noSTR = 1 << 10, ///< to block numeric arguments on string functions - noASSIGN = 1 << 11, ///< to block assignement to constant i.e. "4=7" - noIF = 1 << 12, - noELSE = 1 << 13, +public: + ParserTokenReader(ParserBase* a_pParent); + ParserTokenReader* Clone(ParserBase* a_pParent) const; + + void AddValIdent(identfun_type a_pCallback); + void SetVarCreator(facfun_type a_pFactory, void* pUserData); + void SetFormula(const string_type& a_strFormula); + void SetArgSep(char_type cArgSep); + + int GetPos() const; + const string_type& GetExpr() const; + varmap_type& GetUsedVar(); + char_type GetArgSep() const; + + void IgnoreUndefVar(bool bIgnore); + void ReInit(); + token_type ReadNextToken(); + +private: + /** \brief Syntax codes. + + The syntax codes control the syntax check done during the first time parsing of + the expression string. They are flags that indicate which tokens are allowed next + if certain tokens are identified. + */ + enum ESynCodes + { + noBO = 1 << 0, ///< to avoid i.e. "cos(7)(" + noBC = 1 << 1, ///< to avoid i.e. "sin)" or "()" + noVAL = 1 << 2, ///< to avoid i.e. "tan 2" or "sin(8)3.14" + noVAR = 1 << 3, ///< to avoid i.e. "sin a" or "sin(8)a" + noARG_SEP = 1 << 4, ///< to avoid i.e. ",," or "+," ... + noFUN = 1 << 5, ///< to avoid i.e. "sqrt cos" or "(1)sin" + noOPT = 1 << 6, ///< to avoid i.e. "(+)" + noPOSTOP = 1 << 7, ///< to avoid i.e. "(5!!)" "sin!" + noINFIXOP = 1 << 8, ///< to avoid i.e. "++4" "!!4" + noEND = 1 << 9, ///< to avoid unexpected end of formula + noSTR = 1 << 10, ///< to block numeric arguments on string functions + noASSIGN = 1 << 11, ///< to block assignement to constant i.e. "4=7" + noIF = 1 << 12, + noELSE = 1 << 13, sfSTART_OF_LINE = noOPT | noBC | noPOSTOP | noASSIGN | noIF | noELSE | noARG_SEP, - noANY = ~0 ///< All of he above flags set - }; + noANY = ~0 ///< All of he above flags set + }; - ParserTokenReader(const ParserTokenReader &a_Reader); - ParserTokenReader& operator=(const ParserTokenReader &a_Reader); - void Assign(const ParserTokenReader &a_Reader); - - void SetParent(ParserBase *a_pParent); - int ExtractToken(const char_type *a_szCharSet, - string_type &a_strTok, - int a_iPos) const; - int ExtractOperatorToken(string_type &a_sTok, int a_iPos) const; - - bool IsBuiltIn(token_type &a_Tok); - bool IsArgSep(token_type &a_Tok); - bool IsEOF(token_type &a_Tok); - bool IsInfixOpTok(token_type &a_Tok); - bool IsFunTok(token_type &a_Tok); - bool IsPostOpTok(token_type &a_Tok); - bool IsOprt(token_type &a_Tok); - bool IsValTok(token_type &a_Tok); - bool IsVarTok(token_type &a_Tok); - bool IsStrVarTok(token_type &a_Tok); - bool IsUndefVarTok(token_type &a_Tok); - bool IsString(token_type &a_Tok); - void Error(EErrorCodes a_iErrc, - int a_iPos = -1, - const string_type &a_sTok = string_type() ) const; - - token_type& SaveBeforeReturn(const token_type &tok); - - ParserBase *m_pParser; - string_type m_strFormula; - int m_iPos; - int m_iSynFlags; - bool m_bIgnoreUndefVar; - - const funmap_type *m_pFunDef; - const funmap_type *m_pPostOprtDef; - const funmap_type *m_pInfixOprtDef; - const funmap_type *m_pOprtDef; - const valmap_type *m_pConstDef; - const strmap_type *m_pStrVarDef; - varmap_type *m_pVarDef; ///< The only non const pointer to parser internals - facfun_type m_pFactory; - void *m_pFactoryData; - std::list m_vIdentFun; ///< Value token identification function - varmap_type m_UsedVar; - value_type m_fZero; ///< Dummy value of zero, referenced by undefined variables - int m_iBrackets; - token_type m_lastTok; - char_type m_cArgSep; ///< The character used for separating function arguments - }; + ParserTokenReader(const ParserTokenReader& a_Reader); + ParserTokenReader& operator=(const ParserTokenReader& a_Reader); + void Assign(const ParserTokenReader& a_Reader); + + void SetParent(ParserBase* a_pParent); + int ExtractToken(const char_type* a_szCharSet, string_type& a_strTok, int a_iPos) const; + int ExtractOperatorToken(string_type& a_sTok, int a_iPos) const; + + bool IsBuiltIn(token_type& a_Tok); + bool IsArgSep(token_type& a_Tok); + bool IsEOF(token_type& a_Tok); + bool IsInfixOpTok(token_type& a_Tok); + bool IsFunTok(token_type& a_Tok); + bool IsPostOpTok(token_type& a_Tok); + bool IsOprt(token_type& a_Tok); + bool IsValTok(token_type& a_Tok); + bool IsVarTok(token_type& a_Tok); + bool IsStrVarTok(token_type& a_Tok); + bool IsUndefVarTok(token_type& a_Tok); + bool IsString(token_type& a_Tok); + void Error(EErrorCodes a_iErrc, int a_iPos = -1, const string_type& a_sTok = string_type()) const; + + token_type& SaveBeforeReturn(const token_type& tok); + + ParserBase* m_pParser; + string_type m_strFormula; + int m_iPos; + int m_iSynFlags; + bool m_bIgnoreUndefVar; + + const funmap_type* m_pFunDef; + const funmap_type* m_pPostOprtDef; + const funmap_type* m_pInfixOprtDef; + const funmap_type* m_pOprtDef; + const valmap_type* m_pConstDef; + const strmap_type* m_pStrVarDef; + varmap_type* m_pVarDef; ///< The only non const pointer to parser internals + facfun_type m_pFactory; + void* m_pFactoryData; + std::list m_vIdentFun; ///< Value token identification function + varmap_type m_UsedVar; + value_type m_fZero; ///< Dummy value of zero, referenced by undefined variables + int m_iBrackets; + token_type m_lastTok; + char_type m_cArgSep; ///< The character used for separating function arguments +}; } // namespace mu #endif - - diff -Nru mstflint-4.17.0+1/ext_libs/sqlite/Makefile.am mstflint-4.21.0+1/ext_libs/sqlite/Makefile.am --- mstflint-4.17.0+1/ext_libs/sqlite/Makefile.am 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/sqlite/Makefile.am 2022-04-28 11:50:03.000000000 +0000 @@ -1,5 +1,6 @@ #-- # Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved. +# Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU diff -Nru mstflint-4.17.0+1/ext_libs/sqlite/Makefile.in mstflint-4.21.0+1/ext_libs/sqlite/Makefile.in --- mstflint-4.17.0+1/ext_libs/sqlite/Makefile.in 2021-06-29 08:51:08.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/sqlite/Makefile.in 2022-07-31 09:34:41.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.3 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2020 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -16,6 +16,7 @@ #-- # Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved. +# Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU diff -Nru mstflint-4.17.0+1/ext_libs/sqlite/sqlite3.c mstflint-4.21.0+1/ext_libs/sqlite/sqlite3.c --- mstflint-4.17.0+1/ext_libs/sqlite/sqlite3.c 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/sqlite/sqlite3.c 2022-04-28 11:50:03.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.33.0. By combining all the individual C code files into this +** version 3.38.0. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -22,771 +22,6 @@ #ifndef SQLITE_PRIVATE # define SQLITE_PRIVATE static #endif -/************** Begin file ctime.c *******************************************/ -/* -** 2010 February 23 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file implements routines used to report what compile-time options -** SQLite was built with. -*/ - -#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS /* IMP: R-16824-07538 */ - -/* -** 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" -#define SQLITECONFIG_H 1 -#endif - -/* 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) - -/* 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) - -/* -** An array of names of all compile-time options. This array should -** be sorted A-Z. -** -** 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. -*/ -static const char * const sqlite3azCompileOpt[] = { - -/* -** BEGIN CODE GENERATED BY tool/mkctime.tcl -*/ -#if SQLITE_32BIT_ROWID - "32BIT_ROWID", -#endif -#if SQLITE_4_BYTE_ALIGNED_MALLOC - "4_BYTE_ALIGNED_MALLOC", -#endif -#if SQLITE_64BIT_STATS - "64BIT_STATS", -#endif -#if SQLITE_ALLOW_COVERING_INDEX_SCAN - "ALLOW_COVERING_INDEX_SCAN", -#endif -#if SQLITE_ALLOW_URI_AUTHORITY - "ALLOW_URI_AUTHORITY", -#endif -#ifdef SQLITE_BITMASK_TYPE - "BITMASK_TYPE=" CTIMEOPT_VAL(SQLITE_BITMASK_TYPE), -#endif -#if SQLITE_BUG_COMPATIBLE_20160819 - "BUG_COMPATIBLE_20160819", -#endif -#if SQLITE_CASE_SENSITIVE_LIKE - "CASE_SENSITIVE_LIKE", -#endif -#if 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 -#if SQLITE_COVERAGE_TEST - "COVERAGE_TEST", -#endif -#if SQLITE_DEBUG - "DEBUG", -#endif -#if SQLITE_DEFAULT_AUTOMATIC_INDEX - "DEFAULT_AUTOMATIC_INDEX", -#endif -#if SQLITE_DEFAULT_AUTOVACUUM - "DEFAULT_AUTOVACUUM", -#endif -#ifdef SQLITE_DEFAULT_CACHE_SIZE - "DEFAULT_CACHE_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_CACHE_SIZE), -#endif -#if 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 -#if 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 -#if SQLITE_DEFAULT_MEMSTATUS - "DEFAULT_MEMSTATUS", -#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 -#if 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 -#if SQLITE_DIRECT_OVERFLOW_READ - "DIRECT_OVERFLOW_READ", -#endif -#if SQLITE_DISABLE_DIRSYNC - "DISABLE_DIRSYNC", -#endif -#if SQLITE_DISABLE_FTS3_UNICODE - "DISABLE_FTS3_UNICODE", -#endif -#if SQLITE_DISABLE_FTS4_DEFERRED - "DISABLE_FTS4_DEFERRED", -#endif -#if SQLITE_DISABLE_INTRINSIC - "DISABLE_INTRINSIC", -#endif -#if SQLITE_DISABLE_LFS - "DISABLE_LFS", -#endif -#if SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS - "DISABLE_PAGECACHE_OVERFLOW_STATS", -#endif -#if SQLITE_DISABLE_SKIPAHEAD_DISTINCT - "DISABLE_SKIPAHEAD_DISTINCT", -#endif -#ifdef SQLITE_ENABLE_8_3_NAMES - "ENABLE_8_3_NAMES=" CTIMEOPT_VAL(SQLITE_ENABLE_8_3_NAMES), -#endif -#if SQLITE_ENABLE_API_ARMOR - "ENABLE_API_ARMOR", -#endif -#if SQLITE_ENABLE_ATOMIC_WRITE - "ENABLE_ATOMIC_WRITE", -#endif -#if SQLITE_ENABLE_BATCH_ATOMIC_WRITE - "ENABLE_BATCH_ATOMIC_WRITE", -#endif -#if SQLITE_ENABLE_BYTECODE_VTAB - "ENABLE_BYTECODE_VTAB", -#endif -#if SQLITE_ENABLE_CEROD - "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD), -#endif -#if SQLITE_ENABLE_COLUMN_METADATA - "ENABLE_COLUMN_METADATA", -#endif -#if SQLITE_ENABLE_COLUMN_USED_MASK - "ENABLE_COLUMN_USED_MASK", -#endif -#if SQLITE_ENABLE_COSTMULT - "ENABLE_COSTMULT", -#endif -#if SQLITE_ENABLE_CURSOR_HINTS - "ENABLE_CURSOR_HINTS", -#endif -#if SQLITE_ENABLE_DBSTAT_VTAB - "ENABLE_DBSTAT_VTAB", -#endif -#if SQLITE_ENABLE_EXPENSIVE_ASSERT - "ENABLE_EXPENSIVE_ASSERT", -#endif -#if SQLITE_ENABLE_FTS1 - "ENABLE_FTS1", -#endif -#if SQLITE_ENABLE_FTS2 - "ENABLE_FTS2", -#endif -#if SQLITE_ENABLE_FTS3 - "ENABLE_FTS3", -#endif -#if SQLITE_ENABLE_FTS3_PARENTHESIS - "ENABLE_FTS3_PARENTHESIS", -#endif -#if SQLITE_ENABLE_FTS3_TOKENIZER - "ENABLE_FTS3_TOKENIZER", -#endif -#if SQLITE_ENABLE_FTS4 - "ENABLE_FTS4", -#endif -#if SQLITE_ENABLE_FTS5 - "ENABLE_FTS5", -#endif -#if SQLITE_ENABLE_GEOPOLY - "ENABLE_GEOPOLY", -#endif -#if SQLITE_ENABLE_HIDDEN_COLUMNS - "ENABLE_HIDDEN_COLUMNS", -#endif -#if SQLITE_ENABLE_ICU - "ENABLE_ICU", -#endif -#if SQLITE_ENABLE_IOTRACE - "ENABLE_IOTRACE", -#endif -#if SQLITE_ENABLE_JSON1 - "ENABLE_JSON1", -#endif -#if SQLITE_ENABLE_LOAD_EXTENSION - "ENABLE_LOAD_EXTENSION", -#endif -#ifdef SQLITE_ENABLE_LOCKING_STYLE - "ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE), -#endif -#if SQLITE_ENABLE_MEMORY_MANAGEMENT - "ENABLE_MEMORY_MANAGEMENT", -#endif -#if SQLITE_ENABLE_MEMSYS3 - "ENABLE_MEMSYS3", -#endif -#if SQLITE_ENABLE_MEMSYS5 - "ENABLE_MEMSYS5", -#endif -#if SQLITE_ENABLE_MULTIPLEX - "ENABLE_MULTIPLEX", -#endif -#if SQLITE_ENABLE_NORMALIZE - "ENABLE_NORMALIZE", -#endif -#if SQLITE_ENABLE_NULL_TRIM - "ENABLE_NULL_TRIM", -#endif -#if SQLITE_ENABLE_OVERSIZE_CELL_CHECK - "ENABLE_OVERSIZE_CELL_CHECK", -#endif -#if SQLITE_ENABLE_PREUPDATE_HOOK - "ENABLE_PREUPDATE_HOOK", -#endif -#if SQLITE_ENABLE_QPSG - "ENABLE_QPSG", -#endif -#if SQLITE_ENABLE_RBU - "ENABLE_RBU", -#endif -#if SQLITE_ENABLE_RTREE - "ENABLE_RTREE", -#endif -#if SQLITE_ENABLE_SELECTTRACE - "ENABLE_SELECTTRACE", -#endif -#if SQLITE_ENABLE_SESSION - "ENABLE_SESSION", -#endif -#if SQLITE_ENABLE_SNAPSHOT - "ENABLE_SNAPSHOT", -#endif -#if SQLITE_ENABLE_SORTER_REFERENCES - "ENABLE_SORTER_REFERENCES", -#endif -#if SQLITE_ENABLE_SQLLOG - "ENABLE_SQLLOG", -#endif -#if defined(SQLITE_ENABLE_STAT4) - "ENABLE_STAT4", -#endif -#if SQLITE_ENABLE_STMTVTAB - "ENABLE_STMTVTAB", -#endif -#if SQLITE_ENABLE_STMT_SCANSTATUS - "ENABLE_STMT_SCANSTATUS", -#endif -#if SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION - "ENABLE_UNKNOWN_SQL_FUNCTION", -#endif -#if SQLITE_ENABLE_UNLOCK_NOTIFY - "ENABLE_UNLOCK_NOTIFY", -#endif -#if SQLITE_ENABLE_UPDATE_DELETE_LIMIT - "ENABLE_UPDATE_DELETE_LIMIT", -#endif -#if SQLITE_ENABLE_URI_00_ERROR - "ENABLE_URI_00_ERROR", -#endif -#if SQLITE_ENABLE_VFSTRACE - "ENABLE_VFSTRACE", -#endif -#if SQLITE_ENABLE_WHERETRACE - "ENABLE_WHERETRACE", -#endif -#if SQLITE_ENABLE_ZIPVFS - "ENABLE_ZIPVFS", -#endif -#if SQLITE_EXPLAIN_ESTIMATED_ROWS - "EXPLAIN_ESTIMATED_ROWS", -#endif -#if SQLITE_EXTRA_IFNULLROW - "EXTRA_IFNULLROW", -#endif -#ifdef SQLITE_EXTRA_INIT - "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT), -#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 -#if SQLITE_FTS5_ENABLE_TEST_MI - "FTS5_ENABLE_TEST_MI", -#endif -#if SQLITE_FTS5_NO_WITHOUT_ROWID - "FTS5_NO_WITHOUT_ROWID", -#endif -#if HAVE_ISNAN || SQLITE_HAVE_ISNAN - "HAVE_ISNAN", -#endif -#if SQLITE_HOMEGROWN_RECURSIVE_MUTEX - "HOMEGROWN_RECURSIVE_MUTEX", -#endif -#if SQLITE_IGNORE_AFP_LOCK_ERRORS - "IGNORE_AFP_LOCK_ERRORS", -#endif -#if SQLITE_IGNORE_FLOCK_LOCK_ERRORS - "IGNORE_FLOCK_LOCK_ERRORS", -#endif -#if SQLITE_INLINE_MEMCPY - "INLINE_MEMCPY", -#endif -#if SQLITE_INT64_TYPE - "INT64_TYPE", -#endif -#ifdef SQLITE_INTEGRITY_CHECK_ERROR_MAX - "INTEGRITY_CHECK_ERROR_MAX=" CTIMEOPT_VAL(SQLITE_INTEGRITY_CHECK_ERROR_MAX), -#endif -#if SQLITE_LIKE_DOESNT_MATCH_BLOBS - "LIKE_DOESNT_MATCH_BLOBS", -#endif -#if SQLITE_LOCK_TRACE - "LOCK_TRACE", -#endif -#if 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 -#if SQLITE_MEMDEBUG - "MEMDEBUG", -#endif -#if SQLITE_MIXED_ENDIAN_64BIT_FLOAT - "MIXED_ENDIAN_64BIT_FLOAT", -#endif -#if SQLITE_MMAP_READWRITE - "MMAP_READWRITE", -#endif -#if SQLITE_MUTEX_NOOP - "MUTEX_NOOP", -#endif -#if SQLITE_MUTEX_NREF - "MUTEX_NREF", -#endif -#if SQLITE_MUTEX_OMIT - "MUTEX_OMIT", -#endif -#if SQLITE_MUTEX_PTHREADS - "MUTEX_PTHREADS", -#endif -#if SQLITE_MUTEX_W32 - "MUTEX_W32", -#endif -#if SQLITE_NEED_ERR_NAME - "NEED_ERR_NAME", -#endif -#if SQLITE_NOINLINE - "NOINLINE", -#endif -#if SQLITE_NO_SYNC - "NO_SYNC", -#endif -#if SQLITE_OMIT_ALTERTABLE - "OMIT_ALTERTABLE", -#endif -#if SQLITE_OMIT_ANALYZE - "OMIT_ANALYZE", -#endif -#if SQLITE_OMIT_ATTACH - "OMIT_ATTACH", -#endif -#if SQLITE_OMIT_AUTHORIZATION - "OMIT_AUTHORIZATION", -#endif -#if SQLITE_OMIT_AUTOINCREMENT - "OMIT_AUTOINCREMENT", -#endif -#if SQLITE_OMIT_AUTOINIT - "OMIT_AUTOINIT", -#endif -#if SQLITE_OMIT_AUTOMATIC_INDEX - "OMIT_AUTOMATIC_INDEX", -#endif -#if SQLITE_OMIT_AUTORESET - "OMIT_AUTORESET", -#endif -#if SQLITE_OMIT_AUTOVACUUM - "OMIT_AUTOVACUUM", -#endif -#if SQLITE_OMIT_BETWEEN_OPTIMIZATION - "OMIT_BETWEEN_OPTIMIZATION", -#endif -#if SQLITE_OMIT_BLOB_LITERAL - "OMIT_BLOB_LITERAL", -#endif -#if SQLITE_OMIT_CAST - "OMIT_CAST", -#endif -#if SQLITE_OMIT_CHECK - "OMIT_CHECK", -#endif -#if SQLITE_OMIT_COMPLETE - "OMIT_COMPLETE", -#endif -#if SQLITE_OMIT_COMPOUND_SELECT - "OMIT_COMPOUND_SELECT", -#endif -#if SQLITE_OMIT_CONFLICT_CLAUSE - "OMIT_CONFLICT_CLAUSE", -#endif -#if SQLITE_OMIT_CTE - "OMIT_CTE", -#endif -#if SQLITE_OMIT_DATETIME_FUNCS - "OMIT_DATETIME_FUNCS", -#endif -#if SQLITE_OMIT_DECLTYPE - "OMIT_DECLTYPE", -#endif -#if SQLITE_OMIT_DEPRECATED - "OMIT_DEPRECATED", -#endif -#if SQLITE_OMIT_DISKIO - "OMIT_DISKIO", -#endif -#if SQLITE_OMIT_EXPLAIN - "OMIT_EXPLAIN", -#endif -#if SQLITE_OMIT_FLAG_PRAGMAS - "OMIT_FLAG_PRAGMAS", -#endif -#if SQLITE_OMIT_FLOATING_POINT - "OMIT_FLOATING_POINT", -#endif -#if SQLITE_OMIT_FOREIGN_KEY - "OMIT_FOREIGN_KEY", -#endif -#if SQLITE_OMIT_GET_TABLE - "OMIT_GET_TABLE", -#endif -#if SQLITE_OMIT_HEX_INTEGER - "OMIT_HEX_INTEGER", -#endif -#if SQLITE_OMIT_INCRBLOB - "OMIT_INCRBLOB", -#endif -#if SQLITE_OMIT_INTEGRITY_CHECK - "OMIT_INTEGRITY_CHECK", -#endif -#if SQLITE_OMIT_LIKE_OPTIMIZATION - "OMIT_LIKE_OPTIMIZATION", -#endif -#if SQLITE_OMIT_LOAD_EXTENSION - "OMIT_LOAD_EXTENSION", -#endif -#if SQLITE_OMIT_LOCALTIME - "OMIT_LOCALTIME", -#endif -#if SQLITE_OMIT_LOOKASIDE - "OMIT_LOOKASIDE", -#endif -#if SQLITE_OMIT_MEMORYDB - "OMIT_MEMORYDB", -#endif -#if SQLITE_OMIT_OR_OPTIMIZATION - "OMIT_OR_OPTIMIZATION", -#endif -#if SQLITE_OMIT_PAGER_PRAGMAS - "OMIT_PAGER_PRAGMAS", -#endif -#if SQLITE_OMIT_PARSER_TRACE - "OMIT_PARSER_TRACE", -#endif -#if SQLITE_OMIT_POPEN - "OMIT_POPEN", -#endif -#if SQLITE_OMIT_PRAGMA - "OMIT_PRAGMA", -#endif -#if SQLITE_OMIT_PROGRESS_CALLBACK - "OMIT_PROGRESS_CALLBACK", -#endif -#if SQLITE_OMIT_QUICKBALANCE - "OMIT_QUICKBALANCE", -#endif -#if SQLITE_OMIT_REINDEX - "OMIT_REINDEX", -#endif -#if SQLITE_OMIT_SCHEMA_PRAGMAS - "OMIT_SCHEMA_PRAGMAS", -#endif -#if SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS - "OMIT_SCHEMA_VERSION_PRAGMAS", -#endif -#if SQLITE_OMIT_SHARED_CACHE - "OMIT_SHARED_CACHE", -#endif -#if SQLITE_OMIT_SHUTDOWN_DIRECTORIES - "OMIT_SHUTDOWN_DIRECTORIES", -#endif -#if SQLITE_OMIT_SUBQUERY - "OMIT_SUBQUERY", -#endif -#if SQLITE_OMIT_TCL_VARIABLE - "OMIT_TCL_VARIABLE", -#endif -#if SQLITE_OMIT_TEMPDB - "OMIT_TEMPDB", -#endif -#if SQLITE_OMIT_TEST_CONTROL - "OMIT_TEST_CONTROL", -#endif -#if SQLITE_OMIT_TRACE - "OMIT_TRACE", -#endif -#if SQLITE_OMIT_TRIGGER - "OMIT_TRIGGER", -#endif -#if SQLITE_OMIT_TRUNCATE_OPTIMIZATION - "OMIT_TRUNCATE_OPTIMIZATION", -#endif -#if SQLITE_OMIT_UTF16 - "OMIT_UTF16", -#endif -#if SQLITE_OMIT_VACUUM - "OMIT_VACUUM", -#endif -#if SQLITE_OMIT_VIEW - "OMIT_VIEW", -#endif -#if SQLITE_OMIT_VIRTUALTABLE - "OMIT_VIRTUALTABLE", -#endif -#if SQLITE_OMIT_WAL - "OMIT_WAL", -#endif -#if SQLITE_OMIT_WSD - "OMIT_WSD", -#endif -#if SQLITE_OMIT_XFER_OPT - "OMIT_XFER_OPT", -#endif -#if SQLITE_PCACHE_SEPARATE_HEADER - "PCACHE_SEPARATE_HEADER", -#endif -#if SQLITE_PERFORMANCE_TRACE - "PERFORMANCE_TRACE", -#endif -#if SQLITE_POWERSAFE_OVERWRITE - "POWERSAFE_OVERWRITE", -#endif -#if SQLITE_PREFER_PROXY_LOCKING - "PREFER_PROXY_LOCKING", -#endif -#if SQLITE_PROXY_DEBUG - "PROXY_DEBUG", -#endif -#if SQLITE_REVERSE_UNORDERED_SELECTS - "REVERSE_UNORDERED_SELECTS", -#endif -#if SQLITE_RTREE_INT_ONLY - "RTREE_INT_ONLY", -#endif -#if SQLITE_SECURE_DELETE - "SECURE_DELETE", -#endif -#if SQLITE_SMALL_STACK - "SMALL_STACK", -#endif -#ifdef SQLITE_SORTER_PMASZ - "SORTER_PMASZ=" CTIMEOPT_VAL(SQLITE_SORTER_PMASZ), -#endif -#if 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 -#if SQLITE_SUBSTR_COMPATIBILITY - "SUBSTR_COMPATIBILITY", -#endif -#if SQLITE_SYSTEM_MALLOC - "SYSTEM_MALLOC", -#endif -#if SQLITE_TCL - "TCL", -#endif -#ifdef SQLITE_TEMP_STORE - "TEMP_STORE=" CTIMEOPT_VAL(SQLITE_TEMP_STORE), -#endif -#if SQLITE_TEST - "TEST", -#endif -#if defined(SQLITE_THREADSAFE) - "THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE), -#elif defined(THREADSAFE) - "THREADSAFE=" CTIMEOPT_VAL(THREADSAFE), -#else - "THREADSAFE=1", -#endif -#if SQLITE_UNLINK_AFTER_CLOSE - "UNLINK_AFTER_CLOSE", -#endif -#if SQLITE_UNTESTABLE - "UNTESTABLE", -#endif -#if SQLITE_USER_AUTHENTICATION - "USER_AUTHENTICATION", -#endif -#if SQLITE_USE_ALLOCA - "USE_ALLOCA", -#endif -#if SQLITE_USE_FCNTL_TRACE - "USE_FCNTL_TRACE", -#endif -#if SQLITE_USE_URI - "USE_URI", -#endif -#if SQLITE_VDBE_COVERAGE - "VDBE_COVERAGE", -#endif -#if SQLITE_WIN32_MALLOC - "WIN32_MALLOC", -#endif -#if SQLITE_ZERO_MALLOC - "ZERO_MALLOC", -#endif -/* -** END CODE GENERATED BY tool/mkctime.tcl -*/ -}; - -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 sqliteInt.h ***************************************/ /* ** 2001 September 15 @@ -990,6 +225,18 @@ # define MSVC_VERSION 0 #endif +/* +** Some C99 functions in "math.h" are only present for MSVC when its version +** is associated with Visual Studio 2013 or higher. +*/ +#ifndef SQLITE_HAVE_C99_MATH_FUNCS +# if MSVC_VERSION==0 || MSVC_VERSION>=1800 +# define SQLITE_HAVE_C99_MATH_FUNCS (1) +# else +# define SQLITE_HAVE_C99_MATH_FUNCS (0) +# endif +#endif + /* Needed for various definitions... */ #if defined(__GNUC__) && !defined(_GNU_SOURCE) # define _GNU_SOURCE @@ -1040,6 +287,17 @@ # define _USE_32BIT_TIME_T #endif +/* Optionally #include a user-defined header, whereby compilation options +** may be set prior to where they take effect, but after platform setup. +** If SQLITE_CUSTOM_INCLUDE=? is defined, its value names the #include +** file. +*/ +#ifdef SQLITE_CUSTOM_INCLUDE +# define INC_STRINGIFY_(f) #f +# define INC_STRINGIFY(f) INC_STRINGIFY_(f) +# include INC_STRINGIFY(SQLITE_CUSTOM_INCLUDE) +#endif + /* The public SQLite interface. The _FILE_OFFSET_BITS macro must appear ** first in QNX. Also, the _USE_32BIT_TIME_T macro must appear first for ** MinGW. @@ -1091,7 +349,30 @@ /* -** 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 @@ -1171,9 +452,9 @@ ** [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.38.0" +#define SQLITE_VERSION_NUMBER 3038000 +#define SQLITE_SOURCE_ID "2022-02-22 18:58:40 40fa792d359f84c3b9e9d6623743e1a59826274e221df1bde8f47086968a1bab" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -1552,6 +833,7 @@ #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_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) @@ -1584,12 +866,13 @@ #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_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 @@ -1597,6 +880,19 @@ ** 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() */ @@ -1619,6 +915,7 @@ #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: */ @@ -2175,6 +1472,23 @@ ** 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]] +** Used by the cksmvfs VFS module only. +** */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_FCNTL_GET_LOCKPROXYFILE 2 @@ -2214,6 +1528,8 @@ #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 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE @@ -3162,7 +2478,13 @@ ** 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
    @@ -3173,7 +2495,13 @@ ** 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
    @@ -3480,11 +2808,14 @@ ** 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. ** ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], @@ -3533,16 +2864,21 @@ ** */ 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 +2906,7 @@ ** */ SQLITE_API int sqlite3_total_changes(sqlite3*); +SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3*); /* ** CAPI3REF: Interrupt A Long-Running Query @@ -4399,6 +3736,14 @@ ** 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 has if +** [sqlite3_extended_result_codes(db,1)] where 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
    ** )^ @@ -4406,7 +3751,15 @@ ** 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 +3899,7 @@ ** 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 @@ -4744,7 +4098,7 @@ ** 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 @@ -4776,13 +4130,14 @@ ** 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 @@ -4797,6 +4152,13 @@ ** ^(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 +4178,7 @@ 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 @@ -5173,12 +4536,17 @@ ** 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 +4581,19 @@ ** [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); @@ -5281,6 +4662,8 @@ ** ** ^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 @@ -5382,18 +4765,22 @@ ** 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 passsed 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] @@ -6135,7 +5522,6 @@ ** 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 @@ -6145,7 +5531,6 @@ ** 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()].)^ @@ -7235,6 +6620,57 @@ 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 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 +** 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 ** @@ -7301,6 +6737,72 @@ 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 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 cancelled. 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 ** @@ -7941,24 +7443,56 @@ ** ** 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 no 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 @@ -7987,7 +7521,7 @@ ** 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()] @@ -8760,7 +8294,11 @@ #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_LAST 33 /* Largest TESTCTRL */ /* ** CAPI3REF: SQL Keyword Checking @@ -9283,6 +8821,16 @@ ** The counter is incremented on the first [sqlite3_step()] call of each ** 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 ** used to store the prepared statement. ^This value is not actually @@ -9297,6 +8845,8 @@ #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 /* @@ -9960,8 +9510,9 @@ ** ** 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. */ @@ -10240,10 +9791,11 @@ ** 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,25 +9804,281 @@ ** 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 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: ** -** 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. +**
      +**
    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); /* +** 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 that is +** either 0, 1, or 2. 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 "aOrderBy" columns +** are adjacent.)^ ^(Furthermore, only a single row for each particular +** combination of values in the columns identified by the "aOrderBy" field +** needs to be returned.)^ ^It is always ok for two or more rows with the same +** values in all "aOrderBy" columns to be returned, as long as all such rows +** are adjacent. ^The virtual table may, if it chooses, omit extra rows +** that have the same value for all columns identified by "aOrderBy". +** ^However omitting the extra rows is optional. +** This mode is used for a DISTINCT query. +**

    +** +** ^For the purposes of comparing virtual table output values to see if the +** values are 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 +** 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 postive 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) must 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 use 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_MISUSE])^ or perhaps +** exhibit some other undefined or harmful behavior. +** +** ^(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 an 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 appreviation 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} ** @@ -10393,6 +10201,7 @@ /* ** 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 @@ -10425,6 +10234,7 @@ /* ** 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 +10275,7 @@ ** 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_old()], [sqlite3_preupdate_new()], ** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces @@ -10503,6 +10313,15 @@ ** 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. This is because the +** in this case the new values are not available. In this case, when a +** callback made with op==SQLITE_DELETE is actuall 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 +10342,12 @@ 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. @@ -10760,8 +10581,8 @@ ** 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 */ @@ -10808,12 +10629,16 @@ ** database is currently in a read transaction or is involved in a backup ** operation. ** +** It is not possible to deserialized 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. +** ** 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 */ @@ -11062,6 +10887,38 @@ */ SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); +/* +** CAPIREF: Conigure 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 value for the second parameter is +** [SQLITE_SESSION_OBJCONFIG_SIZE]. +** +** Arguments for sqlite3session_object_config() +** +** The following values may passed as the the 4th 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_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); + +/* +*/ +#define SQLITE_SESSION_OBJCONFIG_SIZE 1 /* ** CAPI3REF: Enable Or Disable A Session Object @@ -11307,6 +11164,22 @@ ); /* +** 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 ** @@ -11424,6 +11297,14 @@ 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 ** @@ -11525,18 +11406,23 @@ ** 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 @@ -13229,7 +13115,7 @@ ** autoconf-based build */ #if defined(_HAVE_SQLITE_CONFIG_H) && !defined(SQLITECONFIG_H) -/* #include "config.h" */ +#include "config.h" #define SQLITECONFIG_H 1 #endif @@ -13297,11 +13183,7 @@ ** 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 @@ -13470,9 +13352,11 @@ # 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 @@ -13677,11 +13561,12 @@ ** 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 @@ -13712,6 +13597,14 @@ #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 ** expressions could be omitted from the code completely. But they @@ -13726,7 +13619,7 @@ ** 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) @@ -13738,26 +13631,6 @@ #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 ** the correct answer is still obtained, though perhaps not as quickly. @@ -13821,6 +13694,13 @@ #endif /* +** SQLITE_OMIT_VIRTUALTABLE implies SQLITE_OMIT_ALTERTABLE +*/ +#if defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_ALTERTABLE) +# define SQLITE_OMIT_ALTERTABLE +#endif + +/* ** 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() ** macros to verify that we have tested SQLite for large-file support. @@ -13932,7 +13812,7 @@ /* ** 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 +13844,8 @@ #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 @@ -14036,90 +13916,94 @@ #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_MATERIALIZED 97 +#define TK_REINDEX 98 +#define TK_RENAME 99 +#define TK_CTIME_KW 100 +#define TK_ANY 101 +#define TK_BITAND 102 +#define TK_BITOR 103 +#define TK_LSHIFT 104 +#define TK_RSHIFT 105 +#define TK_PLUS 106 +#define TK_MINUS 107 +#define TK_STAR 108 +#define TK_SLASH 109 +#define TK_REM 110 +#define TK_CONCAT 111 +#define TK_PTR 112 +#define TK_COLLATE 113 +#define TK_BITNOT 114 +#define TK_ON 115 +#define TK_INDEXED 116 +#define TK_STRING 117 +#define TK_JOIN_KW 118 +#define TK_CONSTRAINT 119 +#define TK_DEFAULT 120 +#define TK_NULL 121 +#define TK_PRIMARY 122 +#define TK_UNIQUE 123 +#define TK_CHECK 124 +#define TK_REFERENCES 125 +#define TK_AUTOINCR 126 +#define TK_INSERT 127 +#define TK_DELETE 128 +#define TK_UPDATE 129 +#define TK_SET 130 +#define TK_DEFERRABLE 131 +#define TK_FOREIGN 132 +#define TK_DROP 133 +#define TK_UNION 134 +#define TK_ALL 135 +#define TK_EXCEPT 136 +#define TK_INTERSECT 137 +#define TK_SELECT 138 +#define TK_VALUES 139 +#define TK_DISTINCT 140 +#define TK_DOT 141 +#define TK_FROM 142 +#define TK_JOIN 143 +#define TK_USING 144 +#define TK_ORDER 145 +#define TK_GROUP 146 +#define TK_HAVING 147 +#define TK_LIMIT 148 +#define TK_WHERE 149 +#define TK_RETURNING 150 +#define TK_INTO 151 +#define TK_NOTHING 152 +#define TK_FLOAT 153 +#define TK_BLOB 154 +#define TK_INTEGER 155 +#define TK_VARIABLE 156 +#define TK_CASE 157 +#define TK_WHEN 158 +#define TK_THEN 159 +#define TK_ELSE 160 +#define TK_INDEX 161 +#define TK_ALTER 162 +#define TK_ADD 163 +#define TK_WINDOW 164 +#define TK_OVER 165 +#define TK_FILTER 166 +#define TK_COLUMN 167 +#define TK_AGG_FUNCTION 168 +#define TK_AGG_COLUMN 169 +#define TK_TRUEFALSE 170 +#define TK_ISNOT 171 +#define TK_FUNCTION 172 +#define TK_UMINUS 173 +#define TK_UPLUS 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_SPACE 183 +#define TK_ILLEGAL 184 /************** End of parse.h ***********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ @@ -14225,7 +14109,7 @@ ** 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 @@ -14387,6 +14271,7 @@ # define SQLITE_PTRSIZE __SIZEOF_POINTER__ # elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \ defined(_M_ARM) || defined(__arm__) || defined(__x86) || \ + (defined(__APPLE__) && defined(__POWERPC__)) || \ (defined(__TOS_AIX__) && !defined(__64BIT__)) # define SQLITE_PTRSIZE 4 # else @@ -14535,15 +14420,14 @@ ** SELECTTRACE_ENABLED will be either 1 or 0 depending on whether or not ** the Select query generator 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 sqlite3SelectTrace; #endif -#if defined(SQLITE_ENABLE_SELECTTRACE) +#if defined(SQLITE_DEBUG) \ + && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_SELECTTRACE)) # define SELECTTRACE_ENABLED 1 # define SELECTTRACE(K,P,S,X) \ - if(sqlite3_unsupported_selecttrace&(K)) \ + if(sqlite3SelectTrace&(K)) \ sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\ sqlite3DebugPrintf X #else @@ -14552,6 +14436,19 @@ #endif /* +** 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 + + +/* ** An instance of the following structure is used to store the busy-handler ** callback for a given sqlite handle. ** @@ -14569,11 +14466,25 @@ /* ** Name of table that holds the database 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" +** +** The PREFERRED names are used whereever 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 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 +14496,7 @@ ** 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 @@ -14662,7 +14573,10 @@ 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 DbFixer DbFixer; typedef struct Schema Schema; typedef struct Expr Expr; typedef struct ExprList ExprList; @@ -14680,14 +14594,17 @@ typedef struct Module Module; typedef struct NameContext NameContext; typedef struct Parse Parse; +typedef struct ParseCleanup ParseCleanup; typedef struct PreUpdate PreUpdate; typedef struct PrintfArguments PrintfArguments; 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 SrcItem SrcItem; typedef struct SrcList SrcList; typedef struct sqlite3_str StrAccum; /* Internal alias for sqlite3_str */ typedef struct Table Table; @@ -14728,10 +14645,11 @@ /* ** 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) /* A VList object records a mapping between parameters/variables/wildcards ** in the SQL statement (such as $abc, @pqr, or :xyz) and the integer @@ -15082,16 +15000,24 @@ 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 +15038,7 @@ #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); @@ -15236,13 +15162,17 @@ #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 +15181,7 @@ #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. @@ -15328,6 +15259,12 @@ 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 +15281,8 @@ SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree*, int, int *, int *); #endif +SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor*, BtCursor*, i64); + /* ** If we are not using shared cache, then there is no need to ** use mutexes to access the BtShared structures. So make the @@ -15584,35 +15523,35 @@ #define OP_If 18 /* jump */ #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_IsNullOrType 21 /* jump, synopsis: if typeof(r[P1]) IN (P3,5) goto P2 */ +#define OP_IfNullRow 22 /* jump, synopsis: if P1.nullRow then r[P3]=NULL, goto P2 */ +#define OP_SeekLT 23 /* jump, synopsis: key=r[P3@P4] */ +#define OP_SeekLE 24 /* jump, synopsis: key=r[P3@P4] */ +#define OP_SeekGE 25 /* jump, synopsis: key=r[P3@P4] */ +#define OP_SeekGT 26 /* jump, synopsis: key=r[P3@P4] */ +#define OP_IfNotOpen 27 /* jump, synopsis: if( !csr[P1] ) goto P2 */ +#define OP_IfNoHope 28 /* jump, synopsis: key=r[P3@P4] */ +#define OP_NoConflict 29 /* jump, synopsis: key=r[P3@P4] */ +#define OP_NotFound 30 /* jump, synopsis: key=r[P3@P4] */ +#define OP_Found 31 /* jump, synopsis: key=r[P3@P4] */ +#define OP_SeekRowid 32 /* jump, synopsis: intkey=r[P3] */ +#define OP_NotExists 33 /* jump, synopsis: intkey=r[P3] */ +#define OP_Last 34 /* jump */ +#define OP_IfSmaller 35 /* jump */ +#define OP_SorterSort 36 /* jump */ +#define OP_Sort 37 /* jump */ +#define OP_Rewind 38 /* jump */ +#define OP_IdxLE 39 /* jump, synopsis: key=r[P3@P4] */ +#define OP_IdxGT 40 /* jump, synopsis: key=r[P3@P4] */ +#define OP_IdxLT 41 /* jump, synopsis: key=r[P3@P4] */ +#define OP_IdxGE 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_RowSetRead 45 /* jump, synopsis: r[P3]=rowset(P1) */ +#define OP_RowSetTest 46 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */ +#define OP_Program 47 /* jump */ +#define OP_FkIfZero 48 /* jump, synopsis: if fkctr[P1]==0 goto P2 */ +#define OP_IfPos 49 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, 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] */ @@ -15621,124 +15560,133 @@ #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_ElseEq 58 /* jump, same as TK_ESCAPE */ +#define OP_IfNotZero 59 /* jump, synopsis: if r[P1]!=0 then r[P1]--, goto P2 */ +#define OP_DecrJumpZero 60 /* jump, synopsis: if (--r[P1])==0 goto P2 */ +#define OP_IncrVacuum 61 /* jump */ +#define OP_VNext 62 /* jump */ +#define OP_Filter 63 /* jump, synopsis: if key(P3@P4) not in filter(P1) goto P2 */ +#define OP_Init 64 /* jump, synopsis: Start at 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_Null 74 /* synopsis: r[P2..P3]=NULL */ +#define OP_SoftNull 75 /* synopsis: r[P1]=NULL */ +#define OP_Blob 76 /* synopsis: r[P2]=P4 (len=P1) */ +#define OP_Variable 77 /* synopsis: r[P2]=parameter(P1,P4) */ +#define OP_Move 78 /* synopsis: r[P2@P3]=r[P1@P3] */ +#define OP_Copy 79 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */ +#define OP_SCopy 80 /* synopsis: r[P2]=r[P1] */ +#define OP_IntCopy 81 /* synopsis: r[P2]=r[P1] */ +#define OP_FkCheck 82 +#define OP_ResultRow 83 /* synopsis: output=r[P1@P2] */ +#define OP_CollSeq 84 +#define OP_AddImm 85 /* synopsis: r[P1]=r[P1]+P2 */ +#define OP_RealAffinity 86 +#define OP_Cast 87 /* synopsis: affinity(r[P1]) */ +#define OP_Permutation 88 +#define OP_Compare 89 /* synopsis: r[P1@P3] <-> r[P2@P3] */ +#define OP_IsTrue 90 /* synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 */ +#define OP_ZeroOrNull 91 /* synopsis: r[P2] = 0 OR NULL */ +#define OP_Offset 92 /* synopsis: r[P3] = sqlite_offset(P1) */ +#define OP_Column 93 /* synopsis: r[P3]=PX */ +#define OP_TypeCheck 94 /* synopsis: typecheck(r[P1@P2]) */ +#define OP_Affinity 95 /* synopsis: affinity(r[P1@P2]) */ +#define OP_MakeRecord 96 /* synopsis: r[P3]=mkrec(r[P1@P2]) */ +#define OP_Count 97 /* synopsis: r[P2]=count() */ +#define OP_ReadCookie 98 +#define OP_SetCookie 99 +#define OP_ReopenIdx 100 /* synopsis: root=P2 iDb=P3 */ +#define OP_OpenRead 101 /* synopsis: root=P2 iDb=P3 */ +#define OP_BitAnd 102 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */ +#define OP_BitOr 103 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */ +#define OP_ShiftLeft 104 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<>r[P1] */ +#define OP_Add 106 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */ +#define OP_Subtract 107 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */ +#define OP_Multiply 108 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */ +#define OP_Divide 109 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */ +#define OP_Remainder 110 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */ +#define OP_Concat 111 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */ +#define OP_OpenWrite 112 /* synopsis: root=P2 iDb=P3 */ +#define OP_OpenDup 113 +#define OP_BitNot 114 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */ +#define OP_OpenAutoindex 115 /* synopsis: nColumn=P2 */ +#define OP_OpenEphemeral 116 /* synopsis: nColumn=P2 */ +#define OP_String8 117 /* same as TK_STRING, synopsis: r[P2]='P4' */ +#define OP_SorterOpen 118 +#define OP_SequenceTest 119 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */ +#define OP_OpenPseudo 120 /* synopsis: P3 columns in r[P2] */ +#define OP_Close 121 +#define OP_ColumnsUsed 122 +#define OP_SeekScan 123 /* synopsis: Scan-ahead up to P1 rows */ +#define OP_SeekHit 124 /* synopsis: set P2<=seekHit<=P3 */ +#define OP_Sequence 125 /* synopsis: r[P2]=cursor[P1].ctr++ */ +#define OP_NewRowid 126 /* synopsis: r[P2]=rowid */ +#define OP_Insert 127 /* synopsis: intkey=r[P3] data=r[P2] */ +#define OP_RowCell 128 +#define OP_Delete 129 +#define OP_ResetCount 130 +#define OP_SorterCompare 131 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */ +#define OP_SorterData 132 /* synopsis: r[P2]=data */ +#define OP_RowData 133 /* synopsis: r[P2]=data */ +#define OP_Rowid 134 /* synopsis: r[P2]=rowid */ +#define OP_NullRow 135 +#define OP_SeekEnd 136 +#define OP_IdxInsert 137 /* synopsis: key=r[P2] */ +#define OP_SorterInsert 138 /* synopsis: key=r[P2] */ +#define OP_IdxDelete 139 /* synopsis: key=r[P2@P3] */ +#define OP_DeferredSeek 140 /* synopsis: Move P3 to P1.rowid if needed */ +#define OP_IdxRowid 141 /* synopsis: r[P2]=rowid */ +#define OP_FinishSeek 142 +#define OP_Destroy 143 +#define OP_Clear 144 +#define OP_ResetSorter 145 +#define OP_CreateBtree 146 /* synopsis: r[P2]=root iDb=P1 flags=P3 */ +#define OP_SqlExec 147 +#define OP_ParseSchema 148 +#define OP_LoadAnalysis 149 +#define OP_DropTable 150 +#define OP_DropIndex 151 +#define OP_DropTrigger 152 +#define OP_Real 153 /* same as TK_FLOAT, synopsis: r[P2]=P4 */ +#define OP_IntegrityCk 154 +#define OP_RowSetAdd 155 /* synopsis: rowset(P1)=r[P2] */ +#define OP_Param 156 +#define OP_FkCounter 157 /* synopsis: fkctr[P1]+=P2 */ +#define OP_MemMax 158 /* synopsis: r[P1]=max(r[P1],r[P2]) */ +#define OP_OffsetLimit 159 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ +#define OP_AggInverse 160 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */ +#define OP_AggStep 161 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +#define OP_AggStep1 162 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +#define OP_AggValue 163 /* synopsis: r[P3]=value N=P2 */ +#define OP_AggFinal 164 /* synopsis: accum=r[P1] N=P2 */ +#define OP_Expire 165 +#define OP_CursorLock 166 +#define OP_CursorUnlock 167 +#define OP_TableLock 168 /* synopsis: iDb=P1 root=P2 write=P3 */ +#define OP_VBegin 169 +#define OP_VCreate 170 +#define OP_VDestroy 171 +#define OP_VOpen 172 +#define OP_VInitIn 173 /* synopsis: r[P2]=ValueList(P1,P3) */ +#define OP_VColumn 174 /* synopsis: r[P3]=vcolumn(P2) */ +#define OP_VRename 175 +#define OP_Pagecount 176 +#define OP_MaxPgcnt 177 +#define OP_FilterAdd 178 /* synopsis: filter(P1) += key(P3@P4) */ +#define OP_Trace 179 +#define OP_CursorHint 180 +#define OP_ReleaseReg 181 /* synopsis: release r[P1@P2] mask P3 */ +#define OP_Noop 182 +#define OP_Explain 183 +#define OP_Abortable 184 /* Properties such as "out2" or "jump" that are specified in ** comments following the "case" for each opcode in the vdbe.c @@ -15753,35 +15701,36 @@ #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,\ +/* 16 */ 0x01, 0x01, 0x03, 0x12, 0x03, 0x03, 0x01, 0x09,\ +/* 24 */ 0x09, 0x09, 0x09, 0x01, 0x09, 0x09, 0x09, 0x09,\ +/* 32 */ 0x09, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\ +/* 40 */ 0x01, 0x01, 0x01, 0x26, 0x26, 0x23, 0x0b, 0x01,\ +/* 48 */ 0x01, 0x03, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\ +/* 56 */ 0x0b, 0x0b, 0x01, 0x03, 0x03, 0x01, 0x01, 0x01,\ +/* 64 */ 0x01, 0x00, 0x00, 0x02, 0x02, 0x08, 0x00, 0x10,\ +/* 72 */ 0x10, 0x10, 0x10, 0x00, 0x10, 0x10, 0x00, 0x00,\ +/* 80 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02,\ +/* 88 */ 0x00, 0x00, 0x12, 0x1e, 0x20, 0x00, 0x00, 0x00,\ +/* 96 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x26, 0x26,\ +/* 104 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\ +/* 112 */ 0x00, 0x00, 0x12, 0x00, 0x00, 0x10, 0x00, 0x00,\ +/* 120 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00,\ +/* 128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,\ +/* 136 */ 0x00, 0x04, 0x04, 0x00, 0x00, 0x10, 0x00, 0x10,\ +/* 144 */ 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 152 */ 0x00, 0x10, 0x00, 0x06, 0x10, 0x00, 0x04, 0x1a,\ /* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 168 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -} +/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,\ +/* 176 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 184 */ 0x00,} -/* The sqlite3P2Values() routine is able to run faster if it knows +/* 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 ***********************/ @@ -15841,7 +15790,7 @@ #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); @@ -16308,6 +16257,12 @@ # 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 + /* ** The default size of a disk sector */ @@ -16813,6 +16768,11 @@ #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 +16791,10 @@ 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 +16808,10 @@ 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 +16821,7 @@ 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 +16831,10 @@ 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 +16845,9 @@ 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 */ @@ -17013,6 +16977,7 @@ #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 */ /* Flags used only if debugging */ #ifdef SQLITE_DEBUG @@ -17040,24 +17005,31 @@ ** 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 /* The push-down optimization */ +#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_AllOpts 0xffffffff /* All optimizations */ /* ** Macros for testing whether or not optimizations are enabled or disabled. @@ -17071,17 +17043,16 @@ */ #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. -*/ -#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 */ +/* 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_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 @@ -17106,7 +17077,7 @@ 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,12 +17107,13 @@ ** 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_CONSTANT == SQLITE_DETERMINISTIC from the API +** SQLITE_FUNC_DIRECT == SQLITE_DIRECTONLY from the API +** SQLITE_FUNC_UNSAFE == SQLITE_INNOCUOUS ** SQLITE_FUNC_ENCMASK depends on SQLITE_UTF* macros in the API */ #define SQLITE_FUNC_ENCMASK 0x0003 /* SQLITE_UTF8, SQLITE_UTF16BE or UTF16LE */ @@ -17166,6 +17138,8 @@ #define SQLITE_FUNC_SUBTYPE 0x00100000 /* Result likely to have sub-type */ #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 */ +#define SQLITE_FUNC_ANYORDER 0x08000000 /* count/min/max aggregate */ /* Identifier numbers for each in-line function */ #define INLINEFUNC_coalesce 0 @@ -17213,6 +17187,9 @@ ** 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 +17202,7 @@ ** 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 +17217,55 @@ ** 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, iArg, xFunc) \ + {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|\ + SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \ SQLITE_INT_TO_PTR(iArg), 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 +17321,48 @@ ** 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.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: +/* 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 +17373,7 @@ #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_GENERATED 0x0060 /* Combo: _STORED, _VIRTUAL */ #define COLFLAG_NOINSERT 0x0062 /* Combo: _HIDDEN, _STORED, _VIRTUAL */ @@ -17418,9 +17439,7 @@ ** 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 */ @@ -17484,15 +17503,13 @@ #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 +17525,25 @@ 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 */ }; /* @@ -17532,24 +17557,39 @@ ** ** 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 +** 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_StatsUsed 0x00000100 /* 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 */ +#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 +17597,9 @@ ** 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!=0 && (X)->y.pTab->eTabType==TABTYP_VTAB) #else # define IsVirtual(X) 0 # define ExprIsVtab(X) 0 @@ -17646,16 +17686,22 @@ ** 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 algorthm 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 */ @@ -17909,10 +17955,10 @@ 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 */ } *aFunc; int nFunc; /* Number of entries in aFunc[] */ u32 selId; /* Select to which this AggInfo belongs */ - AggInfo *pNext; /* Next in list of them all */ }; /* @@ -17942,10 +17988,10 @@ ** 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 +18031,7 @@ ** 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 @@ -18041,7 +18087,10 @@ ** 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 iRightJoinTable; /* If EP_FromJoin, the right table of the join */ + 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 @@ -18054,8 +18103,7 @@ } 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 @@ -18078,12 +18126,12 @@ #define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */ #define EP_Win 0x008000 /* Contains window functions */ #define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */ - /* 0x020000 // available for reuse */ +#define EP_IfNullRow 0x020000 /* The TK_IF_NULL_ROW opcode */ #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 */ + /* 0x400000 // Available */ #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 */ @@ -18094,14 +18142,12 @@ #define EP_FromDDL 0x40000000 /* Originates from sqlite_schema */ /* 0x80000000 // Available */ -/* -** 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) @@ -18111,6 +18157,16 @@ #define ExprAlwaysTrue(E) (((E)->flags&(EP_FromJoin|EP_IsTrue))==EP_IsTrue) #define ExprAlwaysFalse(E) (((E)->flags&(EP_FromJoin|EP_IsFalse))==EP_IsFalse) +/* 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 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,6 +18238,7 @@ */ 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 */ @@ -18192,11 +18249,12 @@ unsigned bSorterRef :1; /* Defer evaluation until after sorting */ unsigned bNulls: 1; /* True if explicit "NULLS FIRST/LAST" */ 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 */ }; @@ -18232,6 +18290,53 @@ }; /* +** The SrcItem object represents a single term in the FROM clause of a query. +** The SrcList object is mostly an array of SrcItems. +** +** Union member validity: +** +** u1.zIndexedBy fg.isIndexedBy && !fg.isTabFunc +** u1.pFuncArg fg.isTabFunc && !fg.isIndexedBy +** u2.pIBIndex fg.isIndexedBy && !fg.isCte +** u2.pCteUse fg.isCte && !fg.isIndexedBy +*/ +struct SrcItem { + 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 */ + unsigned isCte :1; /* This is a CTE */ + unsigned notCte :1; /* This item may not match a CTE */ + } 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; + union { + Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */ + CteUse *pCteUse; /* CTE Usage info info fg.isCte is true */ + } u2; +}; + +/* ** 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. @@ -18253,36 +18358,7 @@ 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 { - 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 */ + SrcItem a[1]; /* One entry for each identifier on the list */ }; /* @@ -18316,9 +18392,9 @@ #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 */ + /* 0x1000 not currently used */ /* 0x2000 not currently used */ #define WHERE_USE_LIMIT 0x4000 /* Use the LIMIT in cost estimates */ /* 0x8000 not currently used */ @@ -18358,10 +18434,11 @@ 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 */ Select *pWinSelect; /* SELECT statement for any window functions */ }; @@ -18370,29 +18447,33 @@ ** 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_VarSelect 0x000040 /* A correlated 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 */ +#define NC_Complex 0x002000 /* True if a function or subquery seen */ +#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_OrderAgg 0x8000000 /* Has an aggregate other than count/min/max */ /* ** An instance of the following object describes a single ON CONFLICT @@ -18409,15 +18490,21 @@ ** 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 */ + /* 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 +18556,10 @@ ** "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 +18584,11 @@ #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 /* SELECT has be modified by 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 */ /* ** The results of a SELECT can be distributed in several ways, as defined @@ -18515,9 +18607,6 @@ ** 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. ** @@ -18574,13 +18663,18 @@ #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 */ @@ -18666,6 +18760,17 @@ #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 ** the parser and down into all the parser action routine in order to ** carry around information that is global to the entire parse. @@ -18696,6 +18801,9 @@ 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 */ +#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) + u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */ +#endif int nRangeReg; /* Size of the temporary register block */ int iRangeReg; /* First register in temporary register block */ int nErr; /* Number of errors seen */ @@ -18722,13 +18830,17 @@ 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 */ + TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ + ParseCleanup *pCleanup; /* List of cleanup operations to run after parse */ + union { + int addrCrTab; /* Address of OP_CreateBtree on CREATE TABLE */ + Returning *pReturning; /* The RETURNING clause */ + } u1; 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 bReturning; /* Coding a RETURNING trigger */ u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ u8 disableTriggers; /* True to disable triggers */ @@ -18740,6 +18852,7 @@ **************************************************************************/ int aTempReg[8]; /* Holding area for temporary registers */ + Parse *pOuterParse; /* Outer Parse object when nested */ Token sNameToken; /* Token with unqualified schema object name */ /************************************************************************ @@ -18774,15 +18887,14 @@ 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 +18903,8 @@ /* ** 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 */ @@ -18857,6 +18970,7 @@ #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 @@ -18878,6 +18992,7 @@ 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 */ @@ -18936,14 +19051,15 @@ * */ 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,18 +19068,16 @@ }; /* -** 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 */ }; /* @@ -19003,7 +19117,26 @@ /* ** 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 +** optimial 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. @@ -19059,16 +19192,21 @@ 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 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 }; /* @@ -19104,8 +19242,8 @@ 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 */ @@ -19115,10 +19253,26 @@ 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 */ + SrcItem *pSrcItem; /* A single FROM clause item */ + DbFixer *pFix; } 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 sqlite3WalkExprList(Walker*, ExprList*); @@ -19130,11 +19284,18 @@ 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 +19305,56 @@ #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[1]; /* For each CTE in the WITH clause.... */ }; +/* +** 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 */ +}; + + #ifdef SQLITE_DEBUG /* ** An instance of the TreeView object is used for printing the content of @@ -19231,11 +19428,10 @@ 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); @@ -19364,8 +19560,8 @@ 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 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); @@ -19481,9 +19677,10 @@ SQLITE_PRIVATE int sqlite3ErrorToParser(sqlite3*,int); SQLITE_PRIVATE void sqlite3Dequote(char*); SQLITE_PRIVATE void sqlite3DequoteExpr(Expr*); +SQLITE_PRIVATE void sqlite3DequoteToken(Token*); 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); @@ -19500,15 +19697,17 @@ 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 sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*); SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32); SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*); +SQLITE_PRIVATE void 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 u32 sqlite3ExprListFlags(const ExprList*); @@ -19524,7 +19723,12 @@ 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 Table *sqlite3ResultSetOfSelect(Parse*,Select*,char); @@ -19544,14 +19748,15 @@ #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 @@ -19617,7 +19822,7 @@ Token*, Select*, Expr*, IdList*); SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *); SQLITE_PRIVATE void sqlite3SrcListFuncArgs(Parse*, SrcList*, ExprList*); -SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *, struct SrcList_item *); +SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *, SrcItem *); SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(SrcList*); SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse*, SrcList*); SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3*, IdList*); @@ -19636,15 +19841,18 @@ #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*); @@ -19659,7 +19867,7 @@ SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse*, int, int, int); 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); @@ -19678,23 +19886,24 @@ #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 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); 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); @@ -19719,7 +19928,7 @@ #ifdef SQLITE_ENABLE_CURSOR_HINTS SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*); #endif -SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr*, int*); +SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr*, int*); SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*); SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); SQLITE_PRIVATE int sqlite3IsRowid(const char*); @@ -19744,20 +19953,26 @@ 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*); 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,6 +20021,7 @@ #endif SQLITE_PRIVATE int sqlite3JoinType(Parse*, Token*, Token*, Token*); +SQLITE_PRIVATE int sqlite3ColumnIndex(Table *pTab, const char *zCol); SQLITE_PRIVATE void sqlite3SetJoinExpr(Expr*,int); SQLITE_PRIVATE void sqlite3CreateForeignKey(Parse*, ExprList*, Token*, ExprList*, int); SQLITE_PRIVATE void sqlite3DeferForeignKey(Parse*, int); @@ -19828,7 +20044,6 @@ 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*); @@ -19843,14 +20058,8 @@ SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**); 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); @@ -19885,12 +20094,13 @@ 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 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); SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); SQLITE_PRIVATE u8 sqlite3HexToInt(int h); @@ -19900,7 +20110,7 @@ SQLITE_PRIVATE const char *sqlite3ErrName(int); #endif -#ifdef SQLITE_ENABLE_DESERIALIZE +#ifndef SQLITE_OMIT_DESERIALIZE SQLITE_PRIVATE int sqlite3MemdbInit(void); #endif @@ -19913,14 +20123,14 @@ 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); @@ -19945,16 +20155,22 @@ #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 sqlite3StdTypeMap[]; +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,6 +20189,7 @@ 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*, @@ -19990,8 +20207,9 @@ 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 +20231,7 @@ 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 +20246,20 @@ 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 void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int); +SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum*, int); 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 +20310,7 @@ #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 +20347,11 @@ #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*); @@ -20142,7 +20368,9 @@ 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 +20385,32 @@ 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 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 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 +20428,7 @@ 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 +20436,7 @@ #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,7 +20494,7 @@ 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 @@ -20326,8 +20565,8 @@ */ #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,10 +20591,10 @@ 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 @@ -20364,7 +20603,994 @@ #endif /* SQLITEINT_H */ -/************** End of sqliteInt.h *******************************************/ +/************** End of sqliteInt.h *******************************************/ +/************** 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 *******************************************/ +/************** 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. +** +** To modify this header, edit any of the various lists in that script +** which specify categories of generated conditionals in this file. +*/ + +/* +** 2010 February 23 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file implements routines used to report what compile-time options +** SQLite was built with. +*/ +#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS /* IMP: R-16824-07538 */ + +/* +** 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" */ +#define SQLITECONFIG_H 1 +#endif + +/* 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) + +/* 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" */ + +/* +** An array of names of all compile-time options. This array should +** be sorted A-Z. +** +** 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. +*/ +static const char * const sqlite3azCompileOpt[] = { + +#ifdef SQLITE_32BIT_ROWID + "32BIT_ROWID", +#endif +#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC + "4_BYTE_ALIGNED_MALLOC", +#endif +#ifdef SQLITE_64BIT_STATS + "64BIT_STATS", +#endif +#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 +#ifdef SQLITE_ALLOW_URI_AUTHORITY + "ALLOW_URI_AUTHORITY", +#endif +#ifdef SQLITE_ATOMIC_INTRINSICS + "ATOMIC_INTRINSICS=" CTIMEOPT_VAL(SQLITE_ATOMIC_INTRINSICS), +#endif +#ifdef SQLITE_BITMASK_TYPE + "BITMASK_TYPE=" CTIMEOPT_VAL(SQLITE_BITMASK_TYPE), +#endif +#ifdef SQLITE_BUG_COMPATIBLE_20160819 + "BUG_COMPATIBLE_20160819", +#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_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_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_SELECTTRACE + "ENABLE_SELECTTRACE", +#endif +#ifdef SQLITE_ENABLE_SESSION + "ENABLE_SESSION", +#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_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_IFNULLROW + "EXTRA_IFNULLROW", +#endif +#ifdef SQLITE_EXTRA_INIT + "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT), +#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_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_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_PCACHE_SEPARATE_HEADER + "PCACHE_SEPARATE_HEADER", +#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_USER_AUTHENTICATION + "USER_AUTHENTICATION", +#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 @@ -20405,7 +21631,7 @@ 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 + 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 */ @@ -20425,7 +21651,35 @@ 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 @@ -20619,13 +21873,14 @@ 0, /* xVdbeBranch */ 0, /* pVbeBranchArg */ #endif -#ifdef SQLITE_ENABLE_DESERIALIZE +#ifndef SQLITE_OMIT_DESERIALIZE SQLITE_MEMDB_DEFAULT_MAXSIZE, /* mxMemdbSize */ #endif #ifndef SQLITE_UNTESTABLE 0, /* xTestCallback */ #endif 0, /* bLocaltimeFault */ + 0, /* xAltLocaltime */ 0x7ffffffe, /* iOnceResetThreshold */ SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */ 0, /* iPrngSeed */ @@ -20638,6 +21893,18 @@ */ 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 @@ -20669,9 +21936,10 @@ #endif /* -** Flags for select tracing and the ".selecttrace" macro of the CLI +** Tracing flags set by SQLITE_TESTCTRL_TRACEFLAGS. */ -SQLITE_API u32 sqlite3_unsupported_selecttrace = 0; +SQLITE_PRIVATE u32 sqlite3SelectTrace = 0; +SQLITE_PRIVATE u32 sqlite3WhereTrace = 0; /* #include "opcodes.h" */ /* @@ -20687,6 +21955,48 @@ */ 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[]. +** +** sqlite3StdTypeMap[] The type value (as returned from +** sqlite3_column_type() or sqlite3_value_type()) +** for 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 sqlite3StdTypeMap[] = { + 0, + SQLITE_BLOB, + SQLITE_INTEGER, + SQLITE_INTEGER, + SQLITE_FLOAT, + SQLITE_TEXT +}; +SQLITE_PRIVATE const char *sqlite3StdType[] = { + "ANY", + "BLOB", + "INT", + "INTEGER", + "REAL", + "TEXT" +}; + /************** End of global.c **********************************************/ /************** Begin file status.c ******************************************/ /* @@ -20784,7 +22094,7 @@ 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) */ + 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 */ @@ -20795,10 +22105,13 @@ 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 */ + Bool hasBeenDuped:1; /* This cursor was source or target of OP_OpenDup */ + 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 */ - 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 @@ -20888,8 +22201,8 @@ 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 */ + i64 nChange; /* Statement changes (Vdbe.nChange) */ + i64 nDbChange; /* Value of db->nChange */ }; /* Magic number for sanity checking on VdbeFrame objects */ @@ -21090,13 +22403,13 @@ 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 */ + u32 iVdbeMagic; /* Magic number defining state of the SQL statement */ 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 */ + 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 */ @@ -21138,7 +22451,7 @@ bft bIsReader:1; /* True for statements that read */ 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 */ @@ -21180,6 +22493,7 @@ 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 *aNew; /* Array of new.* values */ @@ -21188,6 +22502,24 @@ }; /* +** 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 */ +}; + +/* ** Function prototypes */ SQLITE_PRIVATE void sqlite3VdbeError(Vdbe*, const char *, ...); @@ -21199,7 +22531,7 @@ 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*); +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 +22555,7 @@ 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 +22565,19 @@ 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 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); @@ -21268,7 +22605,8 @@ 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); @@ -22217,8 +23555,10 @@ ** 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 +23574,30 @@ 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 +23612,56 @@ #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->validYMD = 1; + p->validHMS = 1; + p->validJD = 0; + p->rawS = 0; + p->validTZ = 0; + p->isError = 0; + return SQLITE_OK; } #endif /* SQLITE_OMIT_LOCALTIME */ @@ -22329,18 +23674,17 @@ ** 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 }, + { 6, "second", 4.6427e+14, 1.0 }, + { 6, "minute", 7.7379e+12, 60.0 }, + { 4, "hour", 1.2897e+11, 3600.0 }, + { 3, "day", 5373485.0, 86400.0 }, + { 5, "month", 176546.0, 2592000.0 }, + { 4, "year", 14713.0, 31536000.0 }, }; /* @@ -22371,11 +23715,55 @@ 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 */ + if( !p->rawS || p->validJD ){ + rc = 0; + 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 */ + ){ + r = p->s*1000.0 + 210866760000000.0; + clearYMD_HMS_TZ(p); + p->iJD = (sqlite3_int64)(r + 0.5); + p->validJD = 1; + p->rawS = 0; + 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 +23772,7 @@ ** show local time. */ if( sqlite3_stricmp(z, "localtime")==0 && sqlite3NotPureFunc(pCtx) ){ - computeJD(p); - p->iJD += localtimeOffset(p, pCtx, &rc); - clearYMD_HMS_TZ(p); + rc = toLocaltime(p, pCtx); } break; } @@ -22399,6 +23785,7 @@ ** 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); @@ -22411,18 +23798,31 @@ #ifndef SQLITE_OMIT_LOCALTIME else if( sqlite3_stricmp(z, "utc")==0 && sqlite3NotPureFunc(pCtx) ){ if( p->tzSet==0 ){ - sqlite3_int64 c1; + i64 iOrigJD; /* Original localtime */ + i64 iGuess; /* Guess at the corresponding utc time */ + int cnt = 0; /* Safety to prevent infinite loop */ + int 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); - } + 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->tzSet = 1; - }else{ - rc = SQLITE_OK; } + rc = SQLITE_OK; } #endif break; @@ -22538,9 +23938,10 @@ && sqlite3_strnicmp(aXformType[i].zName, z, n)==0 && r>-aXformType[i].rLimit && rM += (int)r; x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; @@ -22550,8 +23951,9 @@ 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[i].zName,"year")==0 ); computeYMD_HMS(p); p->Y += y; p->validJD = 0; @@ -22560,7 +23962,7 @@ } } 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 +23997,7 @@ 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,7 +24012,7 @@ for(i=1; iisError || !validJulianDay(p->iJD) ) return 1; @@ -22640,6 +24043,24 @@ } /* +** 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); + sqlite3_result_int64(context, x.iJD/1000 - 21086676*(i64)10000); + } +} + +/* ** datetime( TIMESTRING, MOD, MOD, ...) ** ** Return YYYY-MM-DD HH:MM:SS @@ -22651,11 +24072,38 @@ ){ DateTime x; if( isDate(context, argc, argv, &x)==0 ){ - char zBuf[100]; + int Y, s; + char zBuf[24]; 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] = ':'; + s = (int)x.s; + zBuf[18] = '0' + (s/10)%10; + zBuf[19] = '0' + (s)%10; + zBuf[20] = 0; + if( x.Y<0 ){ + zBuf[0] = '-'; + sqlite3_result_text(context, zBuf, 20, SQLITE_TRANSIENT); + }else{ + sqlite3_result_text(context, &zBuf[1], 19, SQLITE_TRANSIENT); + } } } @@ -22671,10 +24119,20 @@ ){ DateTime x; if( isDate(context, argc, argv, &x)==0 ){ - char zBuf[100]; + int s; + 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] = ':'; + s = (int)x.s; + zBuf[6] = '0' + (s/10)%10; + zBuf[7] = '0' + (s)%10; + zBuf[8] = 0; + sqlite3_result_text(context, zBuf, 8, SQLITE_TRANSIENT); } } @@ -22690,10 +24148,28 @@ ){ 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); + } } } @@ -22722,131 +24198,100 @@ 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; - } - case 'J': { - sqlite3_snprintf(20, &z[j],"%.16g",x.iJD/86400000.0); - j+=sqlite3Strlen30(&z[j]); - break; - } - 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': { - 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; - } - case 'Y': { - sqlite3_snprintf(5,&z[j],"%04d",x.Y); j+=sqlite3Strlen30(&z[j]); - break; + if( zFmt[i]!='%' ) continue; + if( j59.999 ) s = 59.999; + sqlite3_str_appendf(&sRes, "%06.3f", s); + break; + } + case 'H': { + sqlite3_str_appendf(&sRes, "%02d", x.h); + 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_str_appendf(&sRes,"%02d",(nDay+7-wd)/7); + }else{ + sqlite3_str_appendf(&sRes,"%03d",nDay+1); } - default: z[j++] = '%'; break; + break; + } + case 'J': { + 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 's': { + i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000); + sqlite3_str_appendf(&sRes,"%lld",iS); + break; + } + case 'S': { + sqlite3_str_appendf(&sRes,"%02d",(int)x.s); + break; + } + case 'w': { + sqlite3_str_appendchar(&sRes, 1, + (char)(((x.iJD+129600000)/86400000) % 7) + '0'); + 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( jpMethods==0) ) return 0; return id->pMethods->xDeviceCharacteristics(id); } #ifndef SQLITE_OMIT_WAL @@ -23188,7 +24642,7 @@ 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 +24665,8 @@ } #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 +24728,15 @@ 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){ @@ -23995,7 +25454,7 @@ ** 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 +25701,7 @@ ** ** 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 +25723,7 @@ ** ** 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; @@ -26642,205 +28101,7 @@ /* ** 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. @@ -27478,7 +28739,6 @@ 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 ){ @@ -27634,7 +28894,7 @@ ** TRUE if p is a lookaside memory allocation from db */ #ifndef SQLITE_OMIT_LOOKASIDE -static int isLookaside(sqlite3 *db, void *p){ +static int isLookaside(sqlite3 *db, const void *p){ return SQLITE_WITHIN(p, db->lookaside.pStart, db->lookaside.pEnd); } #else @@ -27645,18 +28905,18 @@ ** 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) ){ @@ -27683,7 +28943,7 @@ } } } - 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) ); @@ -27791,12 +29051,17 @@ 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); + return 0; + } } pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT @@ -28072,8 +29337,15 @@ ** 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 +29353,11 @@ } DisableLookaside; if( db->pParse ){ + sqlite3ErrorMsg(db->pParse, "out of memory"); db->pParse->rc = SQLITE_NOMEM_BKPT; } } + return 0; } /* @@ -28103,12 +29377,15 @@ } /* -** 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,8 +29407,8 @@ */ 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; } @@ -28169,7 +29446,7 @@ #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 etSRCITEM 12 /* a pointer to a SrcItem */ #define etPOINTER 13 /* The %p conversion */ #define etSQLESCAPE3 14 /* %w -> Strings with '\"' doubled */ #define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */ @@ -28235,10 +29512,16 @@ /* 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 }, }; +/* Notes: +** +** %S Takes a pointer to SrcItem. Shows name or database.name +** %!S Like %S but prefer the zName over the zAlias +*/ + /* 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, @@ -28279,7 +29562,7 @@ /* ** 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 +29598,12 @@ 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; } @@ -28567,11 +29850,10 @@ 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; @@ -28984,31 +30266,44 @@ goto adjust_width_for_utf8; } case etTOKEN: { - Token *pToken; if( (pAccum->printfFlags & 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->zDatabase ){ + sqlite3_str_appendall(pAccum, pItem->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->pSelect) ){ + sqlite3_str_appendf(pAccum, "SUBQUERY %u", pItem->pSelect->selId); } - sqlite3_str_appendall(pAccum, pItem->zName); length = width = 0; break; } @@ -29041,6 +30336,42 @@ }/* 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_FromJoin) || pExpr->w.iOfst<=0) ){ + pExpr = pExpr->pLeft; + } + if( pExpr==0 ) 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,7 +30379,7 @@ ** 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, int N){ char *zNew; assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */ if( p->accError ){ @@ -29057,12 +30388,12 @@ 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; + szNew += (sqlite3_int64)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 +30401,7 @@ } if( szNew > p->mxAlloc ){ sqlite3_str_reset(p); - setStrAccumError(p, SQLITE_TOOBIG); + sqlite3StrAccumSetError(p, SQLITE_TOOBIG); return 0; }else{ p->nAlloc = (int)szNew; @@ -29088,7 +30419,7 @@ p->printfFlags |= SQLITE_PRINTF_MALLOCED; }else{ sqlite3_str_reset(p); - setStrAccumError(p, SQLITE_NOMEM); + sqlite3StrAccumSetError(p, SQLITE_NOMEM); return 0; } } @@ -29161,7 +30492,7 @@ 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; @@ -29177,6 +30508,22 @@ } /* +** 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 ** sqlite3_str object accepts no new text and always returns @@ -29565,7 +30912,10 @@ } sqlite3_str_appendf(&x, ")"); } - sqlite3_str_appendf(&x, " AS"); + 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); @@ -29581,29 +30931,27 @@ SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){ int i; for(i=0; inSrc; i++){ - const struct SrcList_item *pItem = &pSrc->a[i]; + const SrcItem *pItem = &pSrc->a[i]; StrAccum x; char zLine[100]; 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); - } + x.printfFlags |= SQLITE_PRINTF_INTERNAL; + sqlite3_str_appendf(&x, "{%d:*} %!S", pItem->iCursor, pItem); 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 ){ sqlite3_str_appendf(&x, " LEFT-JOIN"); + }else if( pItem->fg.jointype & JT_CROSS ){ + sqlite3_str_appendf(&x, " CROSS-JOIN"); } if( pItem->fg.fromDDL ){ sqlite3_str_appendf(&x, " DDL"); } + if( pItem->fg.isCte ){ + sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse); + } sqlite3StrAccumFinish(&x); sqlite3TreeViewItem(pView, zLine, inSrc-1); if( pItem->pSelect ){ @@ -29865,7 +31213,7 @@ 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); + sqlite3_str_appendf(&x, " iRJT=%d", pExpr->w.iRightJoinTable); } if( ExprHasProperty(pExpr, EP_FromDDL) ){ sqlite3_str_appendf(&x, " DDL"); @@ -29895,6 +31243,7 @@ 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 +31263,13 @@ } #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 +31278,19 @@ 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 +31300,14 @@ 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; @@ -30002,6 +31357,7 @@ } case TK_SPAN: { + assert( !ExprHasProperty(pExpr, EP_IntValue) ); sqlite3TreeViewLine(pView, "SPAN %Q", pExpr->u.zToken); sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); break; @@ -30013,6 +31369,7 @@ ** 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,6 +31385,7 @@ 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; @@ -30035,6 +31393,7 @@ 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, @@ -30066,11 +31425,13 @@ } #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; @@ -30078,7 +31439,7 @@ case TK_IN: { sqlite3TreeViewLine(pView, "IN flags=0x%x", pExpr->flags); 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,9 +31460,12 @@ ** 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; + 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"); sqlite3TreeViewExpr(pView, pX, 1); sqlite3TreeViewExpr(pView, pY, 1); @@ -30123,6 +31487,7 @@ case TK_CASE: { sqlite3TreeViewLine(pView, "CASE"); sqlite3TreeViewExpr(pView, pExpr->pLeft, 1); + assert( ExprUseXList(pExpr) ); sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0); break; } @@ -30135,6 +31500,7 @@ case OE_Fail: zType = "fail"; break; case OE_Ignore: zType = "ignore"; break; } + assert( !ExprHasProperty(pExpr, EP_IntValue) ); sqlite3TreeViewLine(pView, "RAISE %s(%Q)", zType, pExpr->u.zToken); break; } @@ -30147,12 +31513,16 @@ } 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 +31531,23 @@ 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; @@ -30310,11 +31697,16 @@ ** number generator) not as an encryption device. */ if( !wsdPrng.isInit ){ + sqlite3_vfs *pVfs = sqlite3_vfs_find(0); int i; char k[256]; wsdPrng.j = 0; wsdPrng.i = 0; - sqlite3OsRandomness(sqlite3_vfs_find(0), 256, k); + if( NEVER(pVfs==0) ){ + memset(k, 0, sizeof(k)); + }else{ + sqlite3OsRandomness(pVfs, 256, k); + } for(i=0; i<256; i++){ wsdPrng.s[i] = (u8)i; } @@ -31208,16 +32600,6 @@ #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. @@ -31246,11 +32628,21 @@ #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 */ @@ -31275,8 +32667,14 @@ ** 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 +32695,22 @@ 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); } /* @@ -31317,17 +32730,8 @@ ** 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 @@ -31351,13 +32755,6 @@ /* ** 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 +32767,19 @@ char *zMsg; va_list ap; sqlite3 *db = pParse->db; + assert( db!=0 ); + assert( db->pParse==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,12 +32842,35 @@ 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); } /* +** 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 */ SQLITE_PRIVATE void sqlite3TokenInit(Token *p, char *z){ @@ -31867,6 +33295,7 @@ incr = 1; }else{ incr = 2; + length &= ~1; assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 ); for(i=3-enc; imagic; - if( magic!=SQLITE_MAGIC_OPEN ){ + eOpenState = db->eOpenState; + if( eOpenState!=SQLITE_STATE_OPEN ){ if( sqlite3SafetyCheckSickOrOk(db) ){ testcase( sqlite3GlobalConfig.xLog!=0 ); logBadConnection("unopened"); @@ -32563,11 +33992,11 @@ } } 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; @@ -32732,7 +34161,6 @@ 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 +34175,9 @@ 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 +34185,9 @@ 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 @@ -33186,35 +34599,35 @@ /* 18 */ "If" OpHelp(""), /* 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)"), + /* 21 */ "IsNullOrType" OpHelp("if typeof(r[P1]) IN (P3,5) goto P2"), + /* 22 */ "IfNullRow" OpHelp("if P1.nullRow then r[P3]=NULL, goto P2"), + /* 23 */ "SeekLT" OpHelp("key=r[P3@P4]"), + /* 24 */ "SeekLE" OpHelp("key=r[P3@P4]"), + /* 25 */ "SeekGE" OpHelp("key=r[P3@P4]"), + /* 26 */ "SeekGT" OpHelp("key=r[P3@P4]"), + /* 27 */ "IfNotOpen" OpHelp("if( !csr[P1] ) goto P2"), + /* 28 */ "IfNoHope" OpHelp("key=r[P3@P4]"), + /* 29 */ "NoConflict" OpHelp("key=r[P3@P4]"), + /* 30 */ "NotFound" OpHelp("key=r[P3@P4]"), + /* 31 */ "Found" OpHelp("key=r[P3@P4]"), + /* 32 */ "SeekRowid" OpHelp("intkey=r[P3]"), + /* 33 */ "NotExists" OpHelp("intkey=r[P3]"), + /* 34 */ "Last" OpHelp(""), + /* 35 */ "IfSmaller" OpHelp(""), + /* 36 */ "SorterSort" OpHelp(""), + /* 37 */ "Sort" OpHelp(""), + /* 38 */ "Rewind" OpHelp(""), + /* 39 */ "IdxLE" OpHelp("key=r[P3@P4]"), + /* 40 */ "IdxGT" OpHelp("key=r[P3@P4]"), + /* 41 */ "IdxLT" OpHelp("key=r[P3@P4]"), + /* 42 */ "IdxGE" 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"), + /* 45 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"), + /* 46 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"), + /* 47 */ "Program" OpHelp(""), + /* 48 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"), + /* 49 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, 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]"), @@ -33223,124 +34636,133 @@ /* 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(""), + /* 58 */ "ElseEq" OpHelp(""), + /* 59 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]--, goto P2"), + /* 60 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"), + /* 61 */ "IncrVacuum" OpHelp(""), + /* 62 */ "VNext" OpHelp(""), + /* 63 */ "Filter" OpHelp("if key(P3@P4) not in filter(P1) goto P2"), + /* 64 */ "Init" OpHelp("Start at 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 */ "Null" OpHelp("r[P2..P3]=NULL"), + /* 75 */ "SoftNull" OpHelp("r[P1]=NULL"), + /* 76 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), + /* 77 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"), + /* 78 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), + /* 79 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), + /* 80 */ "SCopy" OpHelp("r[P2]=r[P1]"), + /* 81 */ "IntCopy" OpHelp("r[P2]=r[P1]"), + /* 82 */ "FkCheck" OpHelp(""), + /* 83 */ "ResultRow" OpHelp("output=r[P1@P2]"), + /* 84 */ "CollSeq" OpHelp(""), + /* 85 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), + /* 86 */ "RealAffinity" OpHelp(""), + /* 87 */ "Cast" OpHelp("affinity(r[P1])"), + /* 88 */ "Permutation" OpHelp(""), + /* 89 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"), + /* 90 */ "IsTrue" OpHelp("r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4"), + /* 91 */ "ZeroOrNull" OpHelp("r[P2] = 0 OR NULL"), + /* 92 */ "Offset" OpHelp("r[P3] = sqlite_offset(P1)"), + /* 93 */ "Column" OpHelp("r[P3]=PX"), + /* 94 */ "TypeCheck" OpHelp("typecheck(r[P1@P2])"), + /* 95 */ "Affinity" OpHelp("affinity(r[P1@P2])"), + /* 96 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), + /* 97 */ "Count" OpHelp("r[P2]=count()"), + /* 98 */ "ReadCookie" OpHelp(""), + /* 99 */ "SetCookie" OpHelp(""), + /* 100 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"), + /* 101 */ "OpenRead" OpHelp("root=P2 iDb=P3"), + /* 102 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"), + /* 103 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"), + /* 104 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<>r[P1]"), + /* 106 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"), + /* 107 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"), + /* 108 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"), + /* 109 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"), + /* 110 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"), + /* 111 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"), + /* 112 */ "OpenWrite" OpHelp("root=P2 iDb=P3"), + /* 113 */ "OpenDup" OpHelp(""), + /* 114 */ "BitNot" OpHelp("r[P2]= ~r[P1]"), + /* 115 */ "OpenAutoindex" OpHelp("nColumn=P2"), + /* 116 */ "OpenEphemeral" OpHelp("nColumn=P2"), + /* 117 */ "String8" OpHelp("r[P2]='P4'"), + /* 118 */ "SorterOpen" OpHelp(""), + /* 119 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"), + /* 120 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"), + /* 121 */ "Close" OpHelp(""), + /* 122 */ "ColumnsUsed" OpHelp(""), + /* 123 */ "SeekScan" OpHelp("Scan-ahead up to P1 rows"), + /* 124 */ "SeekHit" OpHelp("set P2<=seekHit<=P3"), + /* 125 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"), + /* 126 */ "NewRowid" OpHelp("r[P2]=rowid"), + /* 127 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"), + /* 128 */ "RowCell" OpHelp(""), + /* 129 */ "Delete" OpHelp(""), + /* 130 */ "ResetCount" OpHelp(""), + /* 131 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"), + /* 132 */ "SorterData" OpHelp("r[P2]=data"), + /* 133 */ "RowData" OpHelp("r[P2]=data"), + /* 134 */ "Rowid" OpHelp("r[P2]=rowid"), + /* 135 */ "NullRow" OpHelp(""), + /* 136 */ "SeekEnd" OpHelp(""), + /* 137 */ "IdxInsert" OpHelp("key=r[P2]"), + /* 138 */ "SorterInsert" OpHelp("key=r[P2]"), + /* 139 */ "IdxDelete" OpHelp("key=r[P2@P3]"), + /* 140 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"), + /* 141 */ "IdxRowid" OpHelp("r[P2]=rowid"), + /* 142 */ "FinishSeek" OpHelp(""), + /* 143 */ "Destroy" OpHelp(""), + /* 144 */ "Clear" OpHelp(""), + /* 145 */ "ResetSorter" OpHelp(""), + /* 146 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"), + /* 147 */ "SqlExec" OpHelp(""), + /* 148 */ "ParseSchema" OpHelp(""), + /* 149 */ "LoadAnalysis" OpHelp(""), + /* 150 */ "DropTable" OpHelp(""), + /* 151 */ "DropIndex" OpHelp(""), + /* 152 */ "DropTrigger" OpHelp(""), + /* 153 */ "Real" OpHelp("r[P2]=P4"), + /* 154 */ "IntegrityCk" OpHelp(""), + /* 155 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), + /* 156 */ "Param" OpHelp(""), + /* 157 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), + /* 158 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), + /* 159 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), + /* 160 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"), + /* 161 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), + /* 162 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"), + /* 163 */ "AggValue" OpHelp("r[P3]=value N=P2"), + /* 164 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), + /* 165 */ "Expire" OpHelp(""), + /* 166 */ "CursorLock" OpHelp(""), + /* 167 */ "CursorUnlock" OpHelp(""), + /* 168 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), + /* 169 */ "VBegin" OpHelp(""), + /* 170 */ "VCreate" OpHelp(""), + /* 171 */ "VDestroy" OpHelp(""), + /* 172 */ "VOpen" OpHelp(""), + /* 173 */ "VInitIn" OpHelp("r[P2]=ValueList(P1,P3)"), + /* 174 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), + /* 175 */ "VRename" OpHelp(""), + /* 176 */ "Pagecount" OpHelp(""), + /* 177 */ "MaxPgcnt" OpHelp(""), + /* 178 */ "FilterAdd" OpHelp("filter(P1) += key(P3@P4)"), + /* 179 */ "Trace" OpHelp(""), + /* 180 */ "CursorHint" OpHelp(""), + /* 181 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"), + /* 182 */ "Noop" OpHelp(""), + /* 183 */ "Explain" OpHelp(""), + /* 184 */ "Abortable" OpHelp(""), }; return azName[i]; } @@ -33472,7 +34894,8 @@ # 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 @@ -33646,205 +35069,7 @@ /* ** 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. @@ -35092,6 +36317,9 @@ return rc; } +/* Forward declaration*/ +static int unixSleep(sqlite3_vfs*,int); + /* ** Set a posix-advisory-lock. ** @@ -35121,7 +36349,7 @@ ** 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--; } @@ -35692,6 +36920,7 @@ } sqlite3_mutex_leave(pInode->pLockMutex); releaseInodeInfo(pFile); + assert( pFile->pShm==0 ); rc = closeUnixFile(id); unixLeaveMutex(); return rc; @@ -36918,7 +38147,24 @@ 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 */ @@ -37477,6 +38723,9 @@ /* Forward declaration */ static int unixGetTempname(int nBuf, char *zBuf); +#ifndef SQLITE_OMIT_WAL + static int unixFcntlExternalReader(unixFile*, int*); +#endif /* ** Information and control of an open file handle. @@ -37593,6 +38842,15 @@ return proxyFileControl(id,op,pArg); } #endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */ + + case SQLITE_FCNTL_EXTERNAL_READER: { +#ifndef SQLITE_OMIT_WAL + return unixFcntlExternalReader((unixFile*)id, (int*)pArg); +#else + *(int*)pArg = 0; + return SQLITE_OK; +#endif + } } return SQLITE_NOTFOUND; } @@ -37802,6 +39060,7 @@ 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 */ + 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 */ @@ -37838,6 +39097,40 @@ #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. ** ** Locks block if the mask is exactly UNIX_SHM_C and are non-blocking @@ -38343,6 +39636,38 @@ } /* +** 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){ + unixShm *pX; + int aLock[SQLITE_SHM_NLOCK]; + assert( sqlite3_mutex_held(pShmNode->pShmMutex) ); + + 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 + +/* ** Change the lock state for a shared-memory segment. ** ** Note that the relationship between SHAREd and EXCLUSIVE locks is a little @@ -38358,10 +39683,10 @@ ){ 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 */ int rc = SQLITE_OK; /* Result code */ u16 mask; /* Mask of locks to take or release */ + int *aLock = pShmNode->aLock; assert( pShmNode==pDbFd->pInode->pShmNode ); assert( pShmNode->pInode==pDbFd->pInode ); @@ -38400,78 +39725,76 @@ mask = (1<<(ofst+n)) - (1<1 || mask==(1<pShmMutex); + assert( assertLockingArrayOk(pShmNode) ); if( flags & SQLITE_SHM_UNLOCK ){ - u16 allMask = 0; /* Mask of locks held by siblings */ + if( (p->exclMask|p->sharedMask) & mask ){ + int ii; + int bUnlock = 1; - /* 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; - } + for(ii=ofst; ii((p->sharedMask & (1<sharedMask & (1<1 ); + aLock[ofst]--; + } - /* Undo the local locks */ - if( rc==SQLITE_OK ){ - p->exclMask &= ~mask; - 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" */ - - /* 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 ){ + assert( n==1 ); + assert( (p->exclMask & (1<sharedMask & mask)==0 ){ + if( aLock[ofst]<0 ){ rc = SQLITE_BUSY; - break; - } - allShared |= pX->sharedMask; - } - - /* Get shared locks at the system level, if necessary */ - if( rc==SQLITE_OK ){ - if( (allShared & mask)==0 ){ + }else if( aLock[ofst]==0 ){ rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n); - }else{ - rc = SQLITE_OK; } - } - /* Get the local shared locks */ - if( rc==SQLITE_OK ){ - p->sharedMask |= mask; + /* Get the local shared locks */ + if( rc==SQLITE_OK ){ + p->sharedMask |= mask; + aLock[ofst]++; + } } }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 ){ + ** lock. If any do, return SQLITE_BUSY right away. */ + int ii; + for(ii=ofst; iisharedMask & mask)==0 ); + if( ALWAYS((p->exclMask & (1<sharedMask & mask)==0 ); p->exclMask |= mask; + for(ii=ofst; iipShmMutex); OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n", p->id, osGetpid(0), p->sharedMask, p->exclMask)); @@ -39248,24 +40571,34 @@ } /* +** 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 +40607,8 @@ ){ 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; } @@ -39581,6 +40914,11 @@ } 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); @@ -39848,7 +41186,27 @@ } /* +** If the last component of the pathname in z[0]..z[j-1] is something +** other than ".." then back it out and return true. If the last +** component is empty or if it is ".." then return false. +*/ +static int unixBackupDir(const char *z, int *pJ){ + int j = *pJ; + int i; + if( j<=0 ) return 0; + for(i=j-1; i>0 && z[i-1]!='/'; i--){} + if( i==0 ) return 0; + if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0; + *pJ = i-1; + return 1; +} + +/* +** Convert a relative pathname into a full pathname. Also +** simplify the pathname as follows: ** +** Remove all instances of /./ +** Remove all isntances of /X/../ for any X */ static int mkFullPathname( const char *zPath, /* Input path */ @@ -39857,6 +41215,7 @@ ){ int nPath = sqlite3Strlen30(zPath); int iOff = 0; + int i, j; if( zPath[0]!='/' ){ if( osGetcwd(zOut, nOut-2)==0 ){ return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); @@ -39871,6 +41230,41 @@ return SQLITE_CANTOPEN_BKPT; } sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath); + + /* Remove duplicate '/' characters. Except, two // at the beginning + ** of a pathname is allowed since this is important on windows. */ + for(i=j=1; zOut[i]; i++){ + zOut[j++] = zOut[i]; + while( zOut[i]=='/' && zOut[i+1]=='/' ) i++; + } + zOut[j] = 0; + + assert( zOut[0]=='/' ); + for(i=j=0; zOut[i]; i++){ + if( zOut[i]=='/' ){ + /* Skip over internal "/." directory components */ + if( zOut[i+1]=='.' && zOut[i+2]=='/' ){ + i += 1; + continue; + } + + /* If this is a "/.." directory component then back out the + ** previous term of the directory if it is something other than "..". + */ + if( zOut[i+1]=='.' + && zOut[i+2]=='.' + && zOut[i+3]=='/' + && unixBackupDir(zOut, &j) + ){ + i += 2; + continue; + } + } + if( ALWAYS(j>=0) ) zOut[j] = zOut[i]; + j++; + } + if( NEVER(j==0) ) zOut[j++] = '/'; + zOut[j] = 0; return SQLITE_OK; } @@ -40091,7 +41485,8 @@ 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 +42059,7 @@ 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 +42085,7 @@ /* 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; } @@ -41466,6 +42861,28 @@ sqlite3_vfs_register(&aVfs[i], i==0); } 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; } @@ -41500,210 +42917,12 @@ ** This file contains code that is specific to Windows. */ /* #include "sqliteInt.h" */ -#if SQLITE_OS_WIN /* This file is used for Windows only */ - -/* -** 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_) */ +#if SQLITE_OS_WIN /* This file is used for Windows only */ -/************** End of os_common.h *******************************************/ -/************** Continuing where we left off in os_win.c *********************/ +/* +** Include code that is common to all os_*.c files +*/ +/* #include "os_common.h" */ /* ** Include the header file for the Windows VFS. @@ -46811,7 +48030,11 @@ 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 @@ -47855,32 +49078,89 @@ ** 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 */ static int memdbClose(sqlite3_file*); @@ -47933,7 +49213,10 @@ 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 = { @@ -47958,17 +49241,68 @@ 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 +49315,25 @@ 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 +49342,7 @@ 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 +49357,27 @@ 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 +49389,25 @@ ** 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 +49415,10 @@ ** 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 +49426,48 @@ ** 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); + if( eLock>SQLITE_LOCK_SHARED ){ + if( p->mFlags & SQLITE_DESERIALIZE_READONLY ){ + rc = SQLITE_READONLY; + }else if( pThis->eLock<=SQLITE_LOCK_SHARED ){ + if( p->nWrLock ){ + rc = SQLITE_BUSY; + }else{ + p->nWrLock = 1; + } + } + }else if( eLock==SQLITE_LOCK_SHARED ){ + if( pThis->eLock > SQLITE_LOCK_SHARED ){ + assert( p->nWrLock==1 ); + p->nWrLock = 0; + }else if( p->nWrLock ){ + rc = SQLITE_BUSY; + }else{ + p->nRdLock++; + } + }else{ + assert( eLock==SQLITE_LOCK_NONE ); + if( pThis->eLock>SQLITE_LOCK_SHARED ){ + assert( p->nWrLock==1 ); + p->nWrLock = 0; + } + assert( p->nRdLock>0 ); + p->nRdLock--; } - p->eLock = eLock; - return SQLITE_OK; + if( rc==SQLITE_OK ) pThis->eLock = eLock; + memdbLeave(p); + return rc; } -#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 +49475,14 @@ } #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 +49500,7 @@ *(sqlite3_int64*)pArg = iLimit; rc = SQLITE_OK; } + memdbLeave(p); return rc; } @@ -48129,6 +49517,7 @@ ** 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 +49531,26 @@ 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 +49560,79 @@ 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]=='/' ){ + 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) + szName + 3 ); + if( p==0 ){ + sqlite3_mutex_leave(pVfsMutex); + return SQLITE_NOMEM; + } + apNew = sqlite3Realloc(memdb_g.apMemStore, + sizeof(apNew[0])*(memdb_g.nMemStore+1) ); + 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 +49660,9 @@ int flags, int *pResOut ){ + UNUSED_PARAMETER(pVfs); + UNUSED_PARAMETER(zPath); + UNUSED_PARAMETER(flags); *pResOut = 0; return SQLITE_OK; } @@ -48221,6 +49678,7 @@ int nOut, char *zOut ){ + UNUSED_PARAMETER(pVfs); sqlite3_snprintf(nOut, zOut, "%s", zPath); return SQLITE_OK; } @@ -48293,9 +49751,14 @@ */ 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 +49794,14 @@ 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; } @@ -48406,13 +49871,18 @@ 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,19 +49896,24 @@ if( p==0 ){ rc = SQLITE_ERROR; }else{ - p->aData = pData; - p->sz = szDb; - p->szAlloc = szBuf; - p->szMax = szBuf; - if( p->szMaxszMax = sqlite3GlobalConfig.mxMemdbSize; + MemStore *pStore = p->pStore; + pStore->aData = pData; + pData = 0; + pStore->sz = szDb; + pStore->szAlloc = szBuf; + pStore->szMax = szBuf; + if( pStore->szMaxszMax = sqlite3GlobalConfig.mxMemdbSize; } - p->mFlags = mFlags; + 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; } @@ -48449,7 +49924,9 @@ */ 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 +49936,7 @@ 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 ******************************************/ @@ -48818,7 +50295,7 @@ sqlite3BitvecClear(0, 1, pTmpSpace); /* Run the program */ - pc = 0; + pc = i = 0; while( (op = aOp[pc])!=0 ){ switch( op ){ case 1: @@ -49122,11 +50599,14 @@ ** suggested cache size is set to N. */ return p->szCache; }else{ + i64 n; /* IMPLEMANTATION-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; } } @@ -50225,6 +51705,7 @@ p->page.pExtra = &p[1]; p->isBulkLocal = 0; p->isAnchor = 0; + p->pLruPrev = 0; /* Initializing this saves a valgrind error */ } (*pCache->pnPurgeable)++; return p; @@ -50580,12 +52061,18 @@ */ 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 +52088,7 @@ PCache1 *pCache = (PCache1*)p; if( pCache->bPurgeable ){ PGroup *pGroup = pCache->pGroup; - int savedMaxPage; + unsigned int savedMaxPage; pcache1EnterMutex(pGroup); savedMaxPage = pGroup->nMaxPage; pGroup->nMaxPage = 0; @@ -52143,6 +53630,7 @@ 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 +53825,7 @@ 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,8 +53875,8 @@ 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 */ + 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 */ @@ -52778,6 +54267,9 @@ 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; } } @@ -54194,6 +55686,7 @@ i64 nSuperJournal; /* Size of super-journal file */ char *zJournal; /* Pointer to one journal within MJ file */ char *zSuperPtr; /* Space to hold super-journal filename */ + char *zFree = 0; /* Free this buffer */ int nSuperPtr; /* Amount of space allocated to zSuperPtr[] */ /* Allocate space for both the pJournal and pSuper file descriptors. @@ -54218,11 +55711,13 @@ rc = sqlite3OsFileSize(pSuper, &nSuperJournal); if( rc!=SQLITE_OK ) goto delsuper_out; nSuperPtr = pVfs->mxPathname+1; - zSuperJournal = sqlite3Malloc(nSuperJournal + nSuperPtr + 2); - if( !zSuperJournal ){ + zFree = sqlite3Malloc(4 + nSuperJournal + nSuperPtr + 2); + if( !zFree ){ rc = SQLITE_NOMEM_BKPT; goto delsuper_out; } + 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 +55765,7 @@ rc = sqlite3OsDelete(pVfs, zSuper, 0); delsuper_out: - sqlite3_free(zSuperJournal); + sqlite3_free(zFree); if( pSuper ){ sqlite3OsClose(pSuper); assert( !isOpen(pJournal) ); @@ -54608,7 +56103,11 @@ pPager->changeCountDone = pPager->tempFile; if( rc==SQLITE_OK ){ - zSuper = pPager->pTmpSpace; + /* 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, pPager->pVfs->mxPathname+1); testcase( rc!=SQLITE_OK ); } @@ -54625,6 +56124,8 @@ /* 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(&zSuper[-4], 0, 4); rc = pager_delsuper(pPager, zSuper); testcase( rc!=SQLITE_OK ); } @@ -54719,6 +56220,7 @@ */ 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; @@ -55593,8 +57095,7 @@ ** 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 +57110,16 @@ */ #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 +57140,7 @@ ** 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; @@ -56359,7 +57868,7 @@ 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 +#ifndef SQLITE_OMIT_DESERIALIZE int memJM = 0; /* Memory journal mode */ #else # define memJM 0 @@ -56552,6 +58061,7 @@ pPager->zWal = 0; } #endif + (void)pPtr; /* Suppress warning about unused pPtr value */ if( nPathname ) sqlite3DbFree(0, zPathname); pPager->pVfs = pVfs; @@ -56563,8 +58073,8 @@ 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; +#ifndef SQLITE_OMIT_DESERIALIZE + pPager->memVfs = memJM = (fout&SQLITE_OPEN_MEMORY)!=0; #endif readOnly = (fout&SQLITE_OPEN_READONLY)!=0; @@ -56949,7 +58459,7 @@ ** 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( @@ -57351,6 +58861,7 @@ DbPage **ppPage, /* Write a pointer to the page here */ int flags /* PAGER_GET_XXX flags */ ){ + /* printf("PAGE %u\n", pgno); fflush(stdout); */ return pPager->xGet(pPager, pgno, ppPage, flags); } @@ -57531,7 +59042,7 @@ 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) ){ @@ -58431,8 +59942,8 @@ ** 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; @@ -58501,7 +60012,7 @@ ** 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 +60058,7 @@ } 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 +60137,18 @@ } 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. @@ -58818,7 +60332,7 @@ 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 +60467,12 @@ 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 @@ -58995,7 +60509,6 @@ 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. @@ -59107,6 +60620,18 @@ 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), @@ -59564,7 +61089,10 @@ ** 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 @@ -59800,6 +61328,70 @@ }; #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 @@ -59956,9 +61548,13 @@ ** 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 */ @@ -59991,7 +61587,9 @@ 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; @@ -60330,8 +61928,8 @@ ** slot in the hash table is set to N, it refers to frame number ** (pLoc->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 +61941,7 @@ 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 +61949,8 @@ }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; } @@ -60402,7 +62001,6 @@ 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 +62015,8 @@ */ 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 +62032,9 @@ /* 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 +62043,11 @@ 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 +62090,9 @@ ** 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 +62100,7 @@ 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 +62121,18 @@ */ 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++){ @@ -60814,14 +62413,43 @@ 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 @@ -61123,7 +62751,6 @@ int nEntry; /* Number of entries in this segment */ ht_slot *aIndex; /* Sorted index for this segment */ - sLoc.aPgno++; if( (i+1)==nSegment ){ nEntry = (int)(iLast - sLoc.iZero); }else{ @@ -61904,7 +63531,9 @@ } /* 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 +63547,7 @@ ** 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 ){ @@ -62262,7 +63891,8 @@ rc = walHashGet(pWal, walFramePage(i), &sLoc); if( rc!=SQLITE_OK ) break; - pgno = sLoc.aPgno[i-sLoc.iZero]; + assert( i - sLoc.iZero - 1 >=0 ); + pgno = sLoc.aPgno[i-sLoc.iZero-1]; iDbOff = (i64)(pgno-1) * szPage; if( iDbOff+szPage<=szDb ){ @@ -62495,7 +64125,7 @@ iKey = walHash(pgno); 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; } @@ -63747,7 +65377,6 @@ */ 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 */ @@ -63825,9 +65454,12 @@ 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 +65471,25 @@ ** 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 +65559,7 @@ 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() */ }; /* @@ -64595,6 +66242,17 @@ #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. @@ -65019,7 +66677,7 @@ 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){ @@ -65299,15 +66957,13 @@ 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; } @@ -65616,6 +67272,24 @@ } /* +** 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; + if( nPayload<=maxLocal ){ + return nPayload; + }else{ + int minLocal; /* Minimum amount of payload held locally */ + int surplus; /* Overflow payload available for local storage */ + minLocal = pPage->minLocal; + surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize-4); + return ( surplus <= maxLocal ) ? surplus : minLocal; + } +} + +/* ** The following routines are implementations of the MemPage.xParseCell() ** method. ** @@ -65681,18 +67355,32 @@ ** ** 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&0x7f)<<7) | ((x = *++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<7) | ((x =*++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<7) | ((x = *++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<7) | ((x = *++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<7) | ((x = *++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<7) | ((x = *++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<7) | ((x = *++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<8) | (*++pIter); + } + } + } + } + } } } } @@ -65702,7 +67390,7 @@ pInfo->nPayload = nPayload; pInfo->pPayload = pIter; testcase( nPayload==pPage->maxLocal ); - testcase( nPayload==pPage->maxLocal+1 ); + testcase( nPayload==(u32)pPage->maxLocal+1 ); if( nPayload<=pPage->maxLocal ){ /* This is the (easy) common case where the entire payload fits ** on the local page. No overflow is required. @@ -65739,7 +67427,7 @@ pInfo->nPayload = nPayload; pInfo->pPayload = pIter; testcase( nPayload==pPage->maxLocal ); - testcase( nPayload==pPage->maxLocal+1 ); + testcase( nPayload==(u32)pPage->maxLocal+1 ); if( nPayload<=pPage->maxLocal ){ /* This is the (easy) common case where the entire payload fits ** on the local page. No overflow is required. @@ -65802,7 +67490,7 @@ 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 +67498,7 @@ 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; } @@ -65902,6 +67590,7 @@ 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 ); @@ -65962,6 +67651,7 @@ cbrk = usableSize; iCellLast = usableSize - 4; + iCellStart = get2byte(&data[hdr+5]); for(i=0; iiCellLast ){ + if( pciCellLast ){ return SQLITE_CORRUPT_PAGE(pPage); } - assert( pc>=iCellFirst && pc<=iCellLast ); + assert( pc>=iCellStart && pc<=iCellLast ); size = pPage->xCellSize(pPage, &src[pc]); cbrk -= size; - if( cbrkusableSize ){ + if( cbrkusableSize ){ return SQLITE_CORRUPT_PAGE(pPage); } - assert( cbrk+size<=usableSize && cbrk>=iCellFirst ); + assert( cbrk+size<=usableSize && cbrk>=iCellStart ); 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); + memcpy(&temp[iCellStart], &data[iCellStart], usableSize - iCellStart); src = temp; } memcpy(&data[cbrk], &src[pc], size); @@ -66138,7 +67826,7 @@ 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; @@ -66224,7 +67912,7 @@ if( iFreeBlk>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 @@ -67088,7 +68776,7 @@ ** 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 +68837,38 @@ ** 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 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. + */ + memset(pBt->pTmpSpace, 0, 8); + pBt->pTmpSpace += 4; + return SQLITE_OK; } /* @@ -67191,19 +68887,23 @@ */ 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 @@ -67355,6 +69055,7 @@ ((pageSize-1)&pageSize)==0 ){ assert( (pageSize & 7)==0 ); assert( !pBt->pCursor ); + if( nReserve>32 && pageSize==512 ) pageSize = 1024; pBt->pageSize = (u32)pageSize; freeTempSpace(pBt); } @@ -67546,7 +69247,6 @@ 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 +69258,7 @@ /* 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 +69293,7 @@ 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 @@ -67665,9 +69365,13 @@ 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 @@ -68391,16 +70095,18 @@ /* ** This routine is called prior to sqlite3PagerCommit when a transaction ** is committed for an auto-vacuum database. -** -** If SQLITE_OK is returned, then *pnTrunc is set to the number of pages -** the database file should be truncated to during the commit process. -** i.e. the database has been reorganized so that only the first *pnTrunc -** pages are in use. */ -static int autoVacuumCommit(BtShared *pBt){ +static int autoVacuumCommit(Btree *p){ int rc = SQLITE_OK; - Pager *pPager = pBt->pPager; - 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 +70114,7 @@ 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 +70128,42 @@ } 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 +70214,7 @@ 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 +70315,7 @@ sqlite3BtreeLeave(p); return rc; } - p->iDataVersion--; /* Compensate for pPager->iDataVersion++; */ + p->iBDataVersion--; /* Compensate for pPager->iDataVersion++; */ pBt->inTransaction = TRANS_READ; btreeClearHasContent(pBt); } @@ -68670,7 +70401,7 @@ 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 +70613,6 @@ 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 +70629,25 @@ 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( @@ -68994,7 +70727,14 @@ 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; @@ -69281,7 +71021,9 @@ 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); @@ -69468,7 +71210,6 @@ 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 +71271,7 @@ 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); @@ -69681,7 +71422,7 @@ 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 ){ @@ -69728,7 +71469,6 @@ 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 ){ @@ -69836,7 +71576,9 @@ for(ii=0; iiiPage; ii++){ assert( pCur->aiIdx[ii]==pCur->apPage[ii]->nCell ); } - assert( pCur->ix==pCur->pPage->nCell-1 ); + assert( pCur->ix==pCur->pPage->nCell-1 || CORRUPT_DB ); + testcase( pCur->ix!=pCur->pPage->nCell-1 ); + /* ^-- dbsqlfuzz b92b72e4de80b5140c30ab71372ca719b8feb618 */ assert( pCur->pPage->leaf ); #endif *pRes = 0; @@ -69861,12 +71603,8 @@ return rc; } -/* Move the cursor so that it points to an entry near the key -** specified by pIdxKey or intKey. Return a success code. -** -** For INTKEY tables, the intKey parameter is used. pIdxKey -** must be NULL. For index tables, pIdxKey is used and intKey -** is ignored. +/* 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 @@ -69879,39 +71617,32 @@ ** *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 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/pIdxKey. +** exactly matches intKey. ** ** *pRes>0 The cursor is left pointing at an entry that -** is larger than intKey/pIdxKey. -** -** For index tables, the pIdxKey->eqSeen field is set to 1 if there -** exists an entry in the table that exactly matches pIdxKey. +** is larger than intKey. */ -SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( +SQLITE_PRIVATE int sqlite3BtreeTableMoveto( 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; - RecordCompare xRecordCompare; 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 ); + 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( pIdxKey==0 - && pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 - ){ + if( pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 ){ if( pCur->info.nKey==intKey ){ *pRes = 0; return SQLITE_OK; @@ -69933,25 +71664,16 @@ if( pCur->info.nKey==intKey ){ return SQLITE_OK; } - }else if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; - }else{ + }else if( rc!=SQLITE_DONE ){ return rc; } } } } - 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 */ - } +#ifdef SQLITE_DEBUG + pCur->pBtree->nSeek++; /* Performance measurement during testing */ +#endif rc = moveToRoot(pCur); if( rc ){ @@ -69967,7 +71689,8 @@ 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 ); + for(;;){ int lwr, upr, idx, c; Pgno chldPg; @@ -69981,133 +71704,236 @@ ** 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 ); 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); - } + 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; - } + } + 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>=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{ - for(;;){ - int nCell; /* Size of the pCell cell in bytes */ - pCell = findCellPastPtr(pPage, idx); + 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; +} - /* 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); +/* 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 +** 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 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 pIdxKey. +** +** *pRes>0 The cursor is left pointing at an entry that +** is larger than pIdxKey. +** +** The pIdxKey->eqSeen field is set to 1 if there +** exists an entry in the table that exactly matches pIdxKey. +*/ +SQLITE_PRIVATE int sqlite3BtreeIndexMoveto( + BtCursor *pCur, /* The cursor to be moved */ + UnpackedRecord *pIdxKey, /* Unpacked index key */ + int *pRes /* Write search results here */ +){ + int rc; + RecordCompare xRecordCompare; + + assert( cursorOwnsBtShared(pCur) ); + assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); + assert( pRes ); + assert( pCur->pKeyInfo!=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 + ); + + 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 || pIdxKey ); + 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==(pIdxKey==0) ); + lwr = 0; + upr = pPage->nCell-1; + 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( nCell+nOverrun ); + if( pCellKey==0 ){ + rc = SQLITE_NOMEM_BKPT; + goto moveto_index_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_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 ); @@ -70116,9 +71942,8 @@ 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{ @@ -70128,7 +71953,7 @@ rc = moveToChild(pCur, chldPg); if( rc ) break; } -moveto_finish: +moveto_index_finish: pCur->info.nSize = 0; assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); return rc; @@ -70218,7 +72043,7 @@ pPage = pCur->pPage; idx = ++pCur->ix; - if( !pPage->isInit ){ + if( !pPage->isInit || sqlite3FaultSim(412) ){ /* 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 @@ -70229,16 +72054,6 @@ 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])); @@ -70599,7 +72414,7 @@ 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; } @@ -70855,10 +72670,9 @@ } /* -** 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 +72684,7 @@ 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 +72740,21 @@ 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 @@ -71139,16 +72965,24 @@ int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */ if( *pRC ) return; - assert( idx>=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]) ); +#if 0 /* Not required. Omit for efficiency */ + if( pcnCell*2 ){ + *pRC = SQLITE_CORRUPT_BKPT; + return; + } +#endif + testcase( pc==(u32)get2byte(&data[hdr+5]) ); testcase( pc+sz==pPage->pBt->usableSize ); if( pc+sz > pPage->pBt->usableSize ){ *pRC = SQLITE_CORRUPT_BKPT; @@ -71440,7 +73274,7 @@ 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(kapCell[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 +73298,8 @@ 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 ){ @@ -71605,7 +73438,9 @@ } pFree = pCell; szFree = sz; - if( pFree+sz>pEnd ) return 0; + if( pFree+sz>pEnd ){ + return 0; + } }else{ pFree = pCell; szFree += sz; @@ -71670,6 +73505,7 @@ pData = &aData[get2byteNotZero(&aData[hdr+5])]; if( pDatapPg->aDataEnd ) goto editpage_fail; /* Add cells to the start of the page */ if( iNewpBt; assert( sqlite3_mutex_held(pBt->mutex) ); assert( sqlite3PagerIswriteable(pParent->pDbPage) ); @@ -72086,7 +73921,9 @@ } pgno = get4byte(pRight); while( 1 ){ - rc = getAndInitPage(pBt, pgno, &apOld[i], 0, 0); + if( rc==SQLITE_OK ){ + rc = getAndInitPage(pBt, pgno, &apOld[i], 0, 0); + } if( rc ){ memset(apOld, 0, (i+1)*sizeof(MemPage*)); goto balance_cleanup; @@ -72098,6 +73935,7 @@ goto balance_cleanup; } } + nMaxCells += apOld[i]->nCell + ArraySize(pParent->apOvfl); if( (i--)==0 ) break; if( pParent->nOverflow && i+nxDiv==pParent->aiOvfl[0] ){ @@ -72125,12 +73963,10 @@ 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 +73977,6 @@ /* 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; /* @@ -72258,7 +74093,7 @@ 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); @@ -72424,6 +74259,11 @@ 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 ); @@ -72460,7 +74300,7 @@ aPgOrder[i] = aPgno[i] = apNew[i]->pgno; aPgFlags[i] = apNew[i]->pDbPage->flags; for(j=0; jmaxLocal+23 ); assert( iOvflSpace <= (int)pBt->pageSize ); + for(k=0; b.ixNx[k]<=i && ALWAYS(kpgno, &rc); if( rc!=SQLITE_OK ) goto balance_cleanup; assert( sqlite3PagerIswriteable(pParent->pDbPage) ); @@ -72834,7 +74681,7 @@ ** 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 @@ -73063,7 +74910,7 @@ do{ rc = btreeGetPage(pBt, ovflPgno, &pPage, 0); if( rc ) return rc; - if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 ){ + if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 || pPage->isInit ){ rc = SQLITE_CORRUPT_BKPT; }else{ if( iOffset+ovflPageSize<(u32)nTotal ){ @@ -73128,7 +74975,8 @@ unsigned char *oldCell; unsigned char *newCell = 0; - assert( (flags & (BTREE_SAVEPOSITION|BTREE_APPEND))==flags ); + assert( (flags & (BTREE_SAVEPOSITION|BTREE_APPEND|BTREE_PREFORMAT))==flags ); + assert( (flags & BTREE_PREFORMAT)==0 || seekResult || pCur->pKeyInfo==0 ); if( pCur->eState==CURSOR_FAULT ){ assert( pCur->skipNext!=SQLITE_OK ); @@ -73146,7 +74994,7 @@ ** 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_PREFORMAT) || (pX->pKey==0)==(pCur->pKeyInfo==0) ); /* Save the positions of any other cursors open on this table. ** @@ -73162,13 +75010,23 @@ if( pCur->curFlags & BTCF_Multiple ){ rc = saveAllCursors(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_BKPT; + } } 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 +75059,8 @@ ** 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 +75083,11 @@ 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; } @@ -73249,17 +75106,16 @@ return btreeOverwriteCell(pCur, &x2); } } - } assert( pCur->eState==CURSOR_VALID || (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 ){ + if( NEVER(pCur->eState>CURSOR_INVALID) ){ rc = SQLITE_CORRUPT_BKPT; }else{ rc = btreeComputeFreeSpace(pPage); @@ -73273,14 +75129,31 @@ assert( pPage->isInit ); newCell = pBt->pTmpSpace; assert( newCell!=0 ); - rc = fillInCell(pPage, newCell, pX, &szNew); + if( flags & BTREE_PREFORMAT ){ + rc = SQLITE_OK; + szNew = pBt->nPreformatSize; + if( szNew<4 ) szNew = 4; + if( ISAUTOVACUUM && szNew>pPage->maxLocal ){ + CellInfo info; + pPage->xParseCell(pPage, newCell, &info); + if( info.nPayload!=info.nLocal ){ + Pgno ovfl = get4byte(&newCell[szNew-4]); + ptrmapPut(pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, &rc); + } + } + }else{ + rc = fillInCell(pPage, newCell, pX, &szNew); + } if( rc ) goto end_insert; assert( szNew==pPage->xCellSize(pPage, newCell) ); assert( szNew <= MX_CELL_SIZE(pBt) ); idx = pCur->ix; if( loc==0 ){ CellInfo info; - assert( idxnCell ); + assert( idx>=0 ); + if( idx>=pPage->nCell ){ + return SQLITE_CORRUPT_BKPT; + } rc = sqlite3PagerWrite(pPage->pDbPage); if( rc ){ goto end_insert; @@ -73289,7 +75162,7 @@ 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 @@ -73381,6 +75254,114 @@ } /* +** 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){ + int rc = SQLITE_OK; + 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); + aOut += putVarint32(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_BKPT; + } + nRem = pSrc->info.nPayload; + if( nIn==nRem && nInpPage->maxLocal ){ + memcpy(aOut, aIn, nIn); + pBt->nPreformatSize = nIn + (aOut - pBt->pTmpSpace); + }else{ + 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 = nOut + (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_BKPT; + } + 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 && 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. ** ** If the BTREE_SAVEPOSITION bit of the flags parameter is zero, then @@ -73400,14 +75381,13 @@ 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 ); @@ -73418,25 +75398,39 @@ assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 ); if( pCur->eState==CURSOR_REQUIRESEEK ){ rc = btreeRestoreCursorPosition(pCur); - if( rc ) return rc; + assert( rc!=SQLITE_OK || CORRUPT_DB || pCur->eState==CURSOR_VALID ); + if( rc || pCur->eState!=CURSOR_VALID ) return rc; } - assert( pCur->eState==CURSOR_VALID ); + assert( CORRUPT_DB || pCur->eState==CURSOR_VALID ); iCellDepth = pCur->iPage; iCellIdx = pCur->ix; pPage = pCur->pPage; + if( pPage->nCell<=iCellIdx ){ + return SQLITE_CORRUPT_BKPT; + } pCell = findCell(pPage, iCellIdx); - if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ) return SQLITE_CORRUPT; + if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ){ + return SQLITE_CORRUPT_BKPT; + } - /* 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) @@ -73447,7 +75441,7 @@ rc = saveCursorKey(pCur); if( rc ) return rc; }else{ - bSkipnext = 1; + bPreserve = 2; } } @@ -73473,7 +75467,7 @@ /* 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 +75476,7 @@ ** 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; @@ -73547,8 +75541,8 @@ } 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; @@ -73742,7 +75736,7 @@ 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; @@ -73757,11 +75751,12 @@ } rc = getAndInitPage(pBt, pgno, &pPage, 0, 0); if( rc ) return rc; - if( pPage->bBusy ){ + if( (pBt->openFlags & BTREE_SINGLE)==0 + && sqlite3PagerPageRefcount(pPage->pDbPage) != (1 + (pgno==1)) + ){ rc = SQLITE_CORRUPT_BKPT; goto cleardatabasepage_out; } - pPage->bBusy = 1; hdr = pPage->hdrOffset; for(i=0; inCell; i++){ pCell = findCell(pPage, i); @@ -73769,14 +75764,15 @@ 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 +75783,6 @@ } cleardatabasepage_out: - pPage->bBusy = 0; releasePage(pPage); return rc; } @@ -73801,11 +75796,10 @@ ** 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 +75811,9 @@ /* 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); @@ -73865,10 +75861,10 @@ return SQLITE_CORRUPT_BKPT; } - 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 +75973,7 @@ 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]); } @@ -74793,11 +76789,12 @@ } /* -** 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 +76823,8 @@ #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) ); @@ -75141,14 +77132,13 @@ 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 +77169,7 @@ ** 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; } @@ -75410,7 +77400,7 @@ ** 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; } @@ -75782,7 +77772,7 @@ 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 +77808,7 @@ sqlite3PagerClearCache(sqlite3BtreePager(b.pDest)); } - assert( sqlite3BtreeIsInTrans(pTo)==0 ); + assert( sqlite3BtreeTxnState(pTo)!=SQLITE_TXN_WRITE ); copy_finished: sqlite3BtreeLeave(pFrom); sqlite3BtreeLeave(pTo); @@ -75905,7 +77895,9 @@ /* 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: @@ -76028,6 +78020,7 @@ #ifndef SQLITE_OMIT_UTF16 int rc; #endif + assert( pMem!=0 ); assert( !sqlite3VdbeMemIsRowSet(pMem) ); assert( desiredEnc==SQLITE_UTF8 || desiredEnc==SQLITE_UTF16LE || desiredEnc==SQLITE_UTF16BE ); @@ -76069,7 +78062,9 @@ 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); @@ -76158,6 +78153,7 @@ ** 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 +78178,7 @@ #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 +78194,8 @@ 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 +78208,7 @@ ** 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 +78236,7 @@ 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)) ); @@ -76271,6 +78272,7 @@ sqlite3_context ctx; Mem t; assert( pFunc!=0 ); + assert( pMem!=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) ); @@ -76414,13 +78416,14 @@ ** ** 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; @@ -76449,6 +78452,7 @@ 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 ){ @@ -76481,6 +78485,7 @@ */ SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem *pMem){ i64 ix; + assert( pMem!=0 ); assert( pMem->flags & MEM_Real ); assert( !sqlite3VdbeMemIsRowSet(pMem) ); assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); @@ -76508,6 +78513,7 @@ ** 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 +78528,7 @@ ** 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) ); @@ -76555,6 +78562,7 @@ ** 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 ); @@ -76664,6 +78672,7 @@ ** 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 +78682,21 @@ 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 @@ -76898,14 +78922,15 @@ 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 */ + assert( pMem!=0 ); assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); assert( !sqlite3VdbeMemIsRowSet(pMem) ); @@ -76924,7 +78949,7 @@ 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){} } @@ -76936,7 +78961,7 @@ ** 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); } @@ -76962,7 +78987,7 @@ } } - pMem->n = nByte; + pMem->n = (int)(nByte & 0x7fffffff); pMem->flags = flags; if( enc ){ pMem->enc = enc; @@ -76982,7 +79007,7 @@ #endif if( nByte>iLimit ){ - return SQLITE_TOOBIG; + return sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG); } return SQLITE_OK; @@ -77214,7 +79239,7 @@ #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 */ @@ -77231,8 +79256,10 @@ 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); assert( pFunc ); if( (pFunc->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0 @@ -77308,7 +79335,7 @@ */ static int valueFromExpr( 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 */ @@ -77323,11 +79350,7 @@ assert( pExpr!=0 ); while( (op = pExpr->op)==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,7 +79359,9 @@ 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 ){ @@ -77409,6 +79434,7 @@ #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,6 +79452,7 @@ } #endif else if( op==TK_TRUEFALSE ){ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); pVal = valueNew(db, pCtx); if( pVal ){ pVal->flags = MEM_Int; @@ -77438,7 +79465,7 @@ 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 +79490,7 @@ */ 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 */ @@ -77773,7 +79800,7 @@ p->pNext = db->pVdbe; p->pPrev = 0; db->pVdbe = p; - p->magic = VDBE_MAGIC_INIT; + p->iVdbeMagic = VDBE_MAGIC_INIT; p->pParse = pParse; pParse->pVdbe = p; assert( pParse->aLabel==0 ); @@ -77974,13 +80001,15 @@ VdbeOp *pOp; i = p->nOp; - assert( p->magic==VDBE_MAGIC_INIT ); + assert( p->iVdbeMagic==VDBE_MAGIC_INIT ); 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; @@ -78209,10 +80238,12 @@ ** 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); + sqlite3MayAbort(p->pParse); } /* @@ -78302,7 +80333,7 @@ SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *v, int x){ Parse *p = v->pParse; int j = ADDR(x); - assert( v->magic==VDBE_MAGIC_INIT ); + assert( v->iVdbeMagic==VDBE_MAGIC_INIT ); assert( j<-p->nLabel ); assert( j>=0 ); #ifdef SQLITE_DEBUG @@ -78441,7 +80472,7 @@ 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_Halt || opcode==OP_HaltIfNull) && ((pOp->p1)!=SQLITE_OK && pOp->p2==OE_Abort)) ){ @@ -78627,7 +80658,7 @@ ** Return the address of the next instruction to be inserted. */ SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe *p){ - assert( p->magic==VDBE_MAGIC_INIT ); + assert( p->iVdbeMagic==VDBE_MAGIC_INIT ); return p->nOp; } @@ -78712,7 +80743,7 @@ int i; VdbeOp *pOut, *pFirst; assert( nOp>0 ); - assert( p->magic==VDBE_MAGIC_INIT ); + assert( p->iVdbeMagic==VDBE_MAGIC_INIT ); if( p->nOp + nOp > p->nOpAlloc && growOpArray(p, nOp) ){ return 0; } @@ -79036,7 +81067,7 @@ sqlite3 *db; assert( p!=0 ); db = p->db; - assert( p->magic==VDBE_MAGIC_INIT ); + assert( p->iVdbeMagic==VDBE_MAGIC_INIT ); assert( p->aOp!=0 || db->mallocFailed ); if( db->mallocFailed ){ if( n!=P4_VTAB ) freeP4(db, n, (void*)*(char**)&zP4); @@ -79112,8 +81143,7 @@ */ 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); @@ -79165,7 +81195,7 @@ /* 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 ); + assert( p->iVdbeMagic==VDBE_MAGIC_INIT ); if( addr<0 ){ addr = p->nOp - 1; } @@ -79221,13 +81251,9 @@ 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++){ @@ -79259,7 +81285,7 @@ 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 +81324,7 @@ 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 +81427,7 @@ 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; @@ -79644,8 +81671,8 @@ */ 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); }else if( p->szMalloc ){ sqlite3DbFreeNN(db, p->zMalloc); @@ -79850,7 +81877,7 @@ Op *pOp; /* Current opcode */ assert( p->explain ); - assert( p->magic==VDBE_MAGIC_RUN ); + assert( p->iVdbeMagic==VDBE_MAGIC_RUN ); assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM ); /* Even though this opcode does not use dynamic strings for @@ -80030,14 +82057,14 @@ int i; #endif assert( p!=0 ); - assert( p->magic==VDBE_MAGIC_INIT || p->magic==VDBE_MAGIC_RESET ); + assert( p->iVdbeMagic==VDBE_MAGIC_INIT || p->iVdbeMagic==VDBE_MAGIC_RESET ); /* 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->iVdbeMagic = VDBE_MAGIC_RUN; #ifdef SQLITE_DEBUG for(i=0; inMem; i++){ @@ -80093,8 +82120,10 @@ assert( p!=0 ); assert( p->nOp>0 ); assert( pParse!=0 ); - assert( p->magic==VDBE_MAGIC_INIT ); + assert( p->iVdbeMagic==VDBE_MAGIC_INIT ); assert( pParse==p->pParse ); + p->pVList = pParse->pVList; + pParse->pVList = 0; db = p->db; assert( db->mallocFailed==0 ); nVar = pParse->nVar; @@ -80179,8 +82208,6 @@ } } - p->pVList = pParse->pVList; - pParse->pVList = 0; if( db->mallocFailed ){ p->nVar = 0; p->nCursor = 0; @@ -80207,21 +82234,14 @@ if( pCx==0 ){ 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 @@ -80401,7 +82421,7 @@ */ 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 +82556,7 @@ */ 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 */ @@ -80750,9 +82770,9 @@ ** 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,7 +82798,7 @@ ** one, or the complete transaction if there is no statement transaction. */ - if( p->magic!=VDBE_MAGIC_RUN ){ + if( p->iVdbeMagic!=VDBE_MAGIC_RUN ){ return SQLITE_OK; } if( db->mallocFailed ){ @@ -80798,9 +82818,15 @@ 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 @@ -80852,6 +82878,9 @@ 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 @@ -80936,7 +82965,7 @@ assert( db->nVdbeRead>=db->nVdbeWrite ); assert( db->nVdbeWrite>=0 ); } - p->magic = VDBE_MAGIC_HALT; + p->iVdbeMagic = VDBE_MAGIC_HALT; checkActiveVdbeCnt(db); if( db->mallocFailed ){ p->rc = SQLITE_NOMEM_BKPT; @@ -80985,6 +83014,7 @@ sqlite3ValueSetNull(db->pErr); } db->errCode = rc; + db->errByteOffset = -1; return rc; } @@ -81109,7 +83139,7 @@ } } #endif - p->magic = VDBE_MAGIC_RESET; + p->iVdbeMagic = VDBE_MAGIC_RESET; return p->rc & db->errMask; } @@ -81119,7 +83149,7 @@ */ SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe *p){ int rc = SQLITE_OK; - if( p->magic==VDBE_MAGIC_RUN || p->magic==VDBE_MAGIC_HALT ){ + if( p->iVdbeMagic==VDBE_MAGIC_RUN || p->iVdbeMagic==VDBE_MAGIC_HALT ){ rc = sqlite3VdbeReset(p); assert( (rc & p->db->errMask)==rc ); } @@ -81180,7 +83210,7 @@ vdbeFreeOpArray(db, pSub->aOp, pSub->nOp); sqlite3DbFree(db, pSub); } - if( p->magic!=VDBE_MAGIC_INIT ){ + if( p->iVdbeMagic!=VDBE_MAGIC_INIT ){ releaseMemArray(p->aVar, p->nVar); sqlite3DbFree(db, p->pVList); sqlite3DbFree(db, p->pFree); @@ -81228,7 +83258,7 @@ if( p->pNext ){ p->pNext->pPrev = p->pPrev; } - p->magic = VDBE_MAGIC_DEAD; + p->iVdbeMagic = VDBE_MAGIC_DEAD; p->db = 0; sqlite3DbFreeNN(db, p); } @@ -81246,7 +83276,7 @@ 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 @@ -81305,7 +83335,8 @@ 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 ){ + assert( !p->isEphemeral ); + if( p->ub.aAltMap && (iMap = p->ub.aAltMap[1+*piCol])>0 && !p->nullRow ){ *pp = p->pAltCursor; *piCol = iMap - 1; return SQLITE_OK; @@ -81583,14 +83614,14 @@ /* ** 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 +83655,8 @@ memcpy(&pMem->u.r, &x, sizeof(x)); pMem->flags = IsNaN(x) ? MEM_Null : MEM_Real; } - return 8; } -SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( +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 +83667,13 @@ 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 +83681,7 @@ 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 +83689,7 @@ 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 +83697,7 @@ 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 +83709,7 @@ #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 +83717,14 @@ 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 +83732,7 @@ /* 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 +83743,10 @@ 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 @@ -81778,7 +83809,8 @@ /* 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; } @@ -81862,7 +83894,8 @@ /* 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 */ @@ -82029,12 +84062,15 @@ ** number. Return negative, zero, or positive if the first (i64) is less than, ** equal to, or greater than the second (double). */ -static int sqlite3IntFloatCompare(i64 i, double r){ +SQLITE_PRIVATE int sqlite3IntFloatCompare(i64 i, double r){ if( sizeof(LONGDOUBLE_TYPE)>8 ){ LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i; + testcase( xr ); + testcase( x==r ); if( xr ) return +1; - return 0; + if( x>r ) return +1; /*NO_TEST*/ /* work around bugs in gcov */ + return 0; /*NO_TEST*/ /* work around bugs in gcov */ }else{ i64 y; double s; @@ -82663,7 +84699,7 @@ /* 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) ){ @@ -82750,7 +84786,7 @@ ** 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; @@ -82930,7 +84966,8 @@ 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; @@ -82951,6 +84988,8 @@ } } + assert( pCsr!=0 ); + assert( pCsr->eCurType==CURTYPE_BTREE ); assert( pCsr->nField==pTab->nCol || (pCsr->nField==pTab->nCol+1 && op==SQLITE_DELETE && iReg==-1) ); @@ -82966,6 +85005,7 @@ 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); @@ -83349,8 +85389,8 @@ ** 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. @@ -83362,8 +85402,16 @@ u8 enc, /* Encoding of z. 0 for BLOBs */ void (*xDel)(void*) /* Destructor function */ ){ - if( sqlite3VdbeMemSetStr(pCtx->pOut, z, n, enc, xDel)==SQLITE_TOOBIG ){ - sqlite3_result_error_toobig(pCtx); + int rc = sqlite3VdbeMemSetStr(pCtx->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); + } } } static int invokeValueDestructor( @@ -83379,7 +85427,7 @@ }else{ xDel((void*)p); } - if( pCtx ) sqlite3_result_error_toobig(pCtx); + sqlite3_result_error_toobig(pCtx); return SQLITE_TOOBIG; } SQLITE_API void sqlite3_result_blob( @@ -83520,8 +85568,12 @@ if( n>(u64)pOut->db->aLimit[SQLITE_LIMIT_LENGTH] ){ 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){ pCtx->isError = errCode ? errCode : -1; @@ -83604,7 +85656,7 @@ int rc; assert(p); - if( p->magic!=VDBE_MAGIC_RUN ){ + if( p->iVdbeMagic!=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 @@ -83822,6 +85874,70 @@ } /* +** 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; + pRhs = (ValueList*)sqlite3_value_pointer(pVal, "ValueList"); + if( pRhs==0 ) return SQLITE_MISUSE; + 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 ** statement, the exact same time is returned for each invocation regardless @@ -84320,7 +86436,7 @@ return SQLITE_MISUSE_BKPT; } sqlite3_mutex_enter(p->db->mutex); - if( p->magic!=VDBE_MAGIC_RUN || p->pc>=0 ){ + if( p->iVdbeMagic!=VDBE_MAGIC_RUN || p->pc>=0 ){ sqlite3Error(p->db, SQLITE_MISUSE); sqlite3_mutex_leave(p->db->mutex); sqlite3_log(SQLITE_MISUSE, @@ -84361,7 +86477,7 @@ 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 */ ){ @@ -84413,11 +86529,7 @@ 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; @@ -84487,12 +86599,8 @@ unsigned char enc ){ assert( xDel!=SQLITE_DYNAMIC ); - if( nData>0x7fffffff ){ - return invokeValueDestructor(zData, xDel, 0); - }else{ - if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; - return bindText(pStmt, i, zData, (int)nData, xDel, enc); - } + if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; + return bindText(pStmt, i, zData, nData, xDel, enc); } #ifndef SQLITE_OMIT_UTF16 SQLITE_API int sqlite3_bind_text16( @@ -84513,7 +86621,10 @@ 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: { @@ -84541,7 +86652,11 @@ Vdbe *p = (Vdbe *)pStmt; rc = vdbeUnbind(p, i); if( rc==SQLITE_OK ){ +#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; @@ -84674,7 +86789,7 @@ */ 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->iVdbeMagic==VDBE_MAGIC_RUN && v->pc>=0; } /* @@ -84829,6 +86944,7 @@ 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; @@ -84894,6 +87010,17 @@ #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 = db->pPreUpdate; + 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. */ @@ -85125,11 +87252,9 @@ #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 +87291,7 @@ 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 ){ @@ -85479,7 +87604,6 @@ 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 @@ -85510,26 +87634,38 @@ 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]); 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 = 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 ){ + pCx->uc.pCursor = (BtCursor*) + &pMem->z[ROUND8(sizeof(VdbeCursor))+2*sizeof(u32)*nField]; + sqlite3BtreeCursorZero(pCx->uc.pCursor); } return pCx; } @@ -85676,7 +87812,10 @@ 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 ){ @@ -85814,6 +87953,11 @@ printf("\n"); sqlite3VdbeCheckMemInvariants(p); } +/**/ void sqlite3PrintMem(Mem *pMem){ + memTracePrint(pMem); + printf("\n"); + fflush(stdout); +} #endif #ifdef SQLITE_DEBUG @@ -85841,96 +87985,7 @@ ** 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 ***********************/ +/* #include "hwtime.h" */ #endif @@ -85977,6 +88032,42 @@ } } +/* +** 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) ){ + h += p->n; + if( p->flags & MEM_Zero ) h += p->u.nZero; + } + } + return h; +} + +/* +** 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. @@ -86012,7 +88103,7 @@ #endif /*** INSERT STACK UNION HERE ***/ - assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */ + assert( p->iVdbeMagic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */ sqlite3VdbeEnter(p); #ifndef SQLITE_OMIT_PROGRESS_CALLBACK if( db->xProgress ){ @@ -86618,12 +88709,18 @@ ** 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; @@ -86772,6 +88869,24 @@ 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] ** @@ -86785,37 +88900,9 @@ 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; @@ -87255,8 +89342,7 @@ ** 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 +89368,8 @@ ** 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 +89377,12 @@ ** 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 ){ + assert( (pOp->p5 & SQLITE_AFF_MASK)!=SQLITE_AFF_TEXT || CORRUPT_DB ); + /* Common case of comparison of two integers */ + if( pIn3->u.i > pIn1->u.i ){ + iCompare = +1; + if( sqlite3aGTb[pOp->opcode] ){ + VdbeBranchTaken(1, (pOp->p5 & SQLITE_NULLEQ)?2:3); + goto jump_to_p2; + } + }else if( pIn3->u.i < pIn1->u.i ){ + iCompare = -1; + if( sqlite3aLTb[pOp->opcode] ){ + VdbeBranchTaken(1, (pOp->p5 & SQLITE_NULLEQ)?2:3); + goto jump_to_p2; + } + }else{ + iCompare = 0; + if( sqlite3aEQb[pOp->opcode] ){ + VdbeBranchTaken(1, (pOp->p5 & SQLITE_NULLEQ)?2:3); + goto jump_to_p2; + } + } + 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,22 +89492,16 @@ ** 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; - } + iCompare = 1; /* Operands are not equal */ + VdbeBranchTaken(2,3); + if( pOp->p5 & SQLITE_JUMPIFNULL ){ + goto jump_to_p2; } 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 ){ @@ -87412,14 +89514,6 @@ 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 ){ testcase( pIn1->flags & MEM_Int ); @@ -87428,7 +89522,7 @@ sqlite3VdbeMemStringify(pIn1, encoding, 1); testcase( (flags1&MEM_Dyn) != (pIn1->flags&MEM_Dyn) ); flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask); - if( NEVER(pIn1==pIn3) ) flags3 = flags1 | MEM_Str; + if( pIn1==pIn3 ) flags3 = flags1 | MEM_Str; } if( (flags3 & MEM_Str)==0 && (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ testcase( pIn3->flags & MEM_Int ); @@ -87442,7 +89536,7 @@ 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 +89545,14 @@ ** 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; /* Undo any changes made by applyAffinity() to the input registers. */ assert( (pIn3->flags & MEM_Dyn) == (flags3 & MEM_Dyn) ); @@ -87468,67 +89560,39 @@ 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. +** 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; + VdbeBranchTaken(iCompare==0, 2); + if( iCompare==0 ) goto jump_to_p2; break; } @@ -87839,6 +89903,40 @@ break; } +/* Opcode: IsNullOrType P1 P2 P3 * * +** Synopsis: if typeof(r[P1]) IN (P3,5) goto P2 +** +** Jump to P2 if the value in register P1 is NULL or has a datatype P3. +** P3 is an integer which should be one of SQLITE_INTEGER, SQLITE_FLOAT, +** SQLITE_BLOB, SQLITE_NULL, or SQLITE_TEXT. +*/ +case OP_IsNullOrType: { /* jump, in1 */ + int doTheJump; + pIn1 = &aMem[pOp->p1]; + doTheJump = (pIn1->flags & MEM_Null)!=0 || sqlite3_value_type(pIn1)==pOp->p3; + VdbeBranchTaken( doTheJump, 2); + if( doTheJump ) goto jump_to_p2; + break; +} + +/* Opcode: ZeroOrNull P1 P2 P3 * * +** Synopsis: r[P2] = 0 OR NULL +** +** If all 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 ** @@ -87954,6 +90052,7 @@ assert( pC!=0 ); assert( p2<(u32)pC->nField ); 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 ); @@ -88189,6 +90288,110 @@ } } +/* 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 ); + testcase( (pIn1->flags & (MEM_Real|MEM_IntReal))==MEM_IntReal ); + 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]) ** @@ -88403,7 +90606,7 @@ 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 ){ @@ -88531,7 +90734,7 @@ 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 @@ -88814,7 +91017,8 @@ ** 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 @@ -88848,10 +91052,19 @@ 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; @@ -88873,7 +91086,7 @@ && 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,7 +91106,8 @@ } } assert( pOp->p5==0 || pOp->p4type==P4_INT32 ); - if( pOp->p5 + if( rc==SQLITE_OK + && pOp->p5 && (iMeta!=pOp->p3 || db->aDb[pOp->p1].pSchema->iGeneration!=pOp->p4.i) ){ @@ -88990,6 +91204,7 @@ /* When the schema cookie changes, record the new cookie internally */ pDb->pSchema->schema_cookie = 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; @@ -89103,6 +91318,8 @@ 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 @@ -89165,8 +91382,9 @@ 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; @@ -89206,9 +91424,9 @@ 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 +91434,10 @@ 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->hasBeenDuped = 1; + pOrig->hasBeenDuped = 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 +91447,7 @@ } -/* 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 +91467,10 @@ ** 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 @@ -89268,50 +91493,63 @@ 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->hasBeenDuped && ALWAYS(pOp->p2<=pCx->nField) ){ + /* If the ephermeral 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; - }else{ - pCx->pgnoRoot = SCHEMA_ROOT; - rc = sqlite3BtreeCursor(pCx->pBtx, SCHEMA_ROOT, BTREE_WRCSR, - 0, pCx->uc.pCursor); - pCx->isTable = 1; + } + pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); + if( rc ){ + sqlite3BtreeClose(pCx->ub.pBtx); } } - pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); } if( rc ) goto abort_due_to_error; pCx->nullRow = 1; @@ -89333,7 +91571,7 @@ 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 ); @@ -89382,7 +91620,7 @@ 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; @@ -89570,6 +91808,7 @@ /* 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 +91818,8 @@ 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 +91828,7 @@ ** (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 +91837,14 @@ /* 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; @@ -89651,7 +91891,7 @@ { int i; for(i=0; iuc.pCursor, &r, 0, 0, &res); + rc = sqlite3BtreeIndexMoveto(pC->uc.pCursor, &r, &res); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } @@ -89710,22 +91950,183 @@ break; } -/* Opcode: SeekHit P1 P2 * * * -** Synopsis: seekHit=P2 + +/* Opcode: SeekScan P1 P2 * * * +** 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 and P2 operands of this opcode are also used, and are called +** This.P1 and This.P2. +** +** 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.P4/P5 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. This opcode attempts to position the cursor on the target +** row by, perhaps by invoking sqlite3BtreeStep() on the cursor +** between 0 and This.P1 times. +** +** There are three possible outcomes from this opcode:
      +** +**
    1. If after This.P1 steps, the cursor is still pointing to a place that +** is earlier in the btree than the target row, then fall through +** into the subsquence OP_SeekGE opcode. +** +**
    2. If the cursor is successfully moved to the target row by 0 or more +** sqlite3BtreeNext() calls, then jump to This.P2, which will land just +** past the OP_IdxGT or OP_IdxGE opcode that follows the OP_SeekGE. +** +**
    3. If the cursor ends up past the target row (indicating the the target +** row does not exist in the btree) then jump to SeekOP.P2. +**
    +*/ +case OP_SeekScan: { + VdbeCursor *pC; + int res; + int nStep; + UnpackedRecord r; + + assert( pOp[1].opcode==OP_SeekGE ); + + /* pOp->p2 points to the first instruction past the OP_IdxGT that + ** follows the OP_SeekGE. */ + assert( pOp->p2>=(int)(pOp-aOp)+2 ); + assert( aOp[pOp->p2-1].opcode==OP_IdxGT || aOp[pOp->p2-1].opcode==OP_IdxGE ); + testcase( aOp[pOp->p2-1].opcode==OP_IdxGE ); + 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( 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 ){ + seekscan_search_fail: +#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 ){ +#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--; + 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: { 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; } @@ -89783,16 +92184,20 @@ ** Synopsis: key=r[P3@P4] ** ** Register P3 is the first of P4 registers that form an unpacked -** record. -** -** 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 behaves like OP_NotFound if the seekHit -** flag is clear and it behaves like OP_Noop if the seekHit flag is set. +** 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. +** +** This opcode is an optimization attempt only. If this opcode always +** falls through, the correct answer is still obtained, but extra works +** is performed. +** +** 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 skips 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 @@ -89834,7 +92239,12 @@ 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 } @@ -89900,7 +92310,7 @@ } } } - rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, pIdxKey, 0, 0, &res); + rc = sqlite3BtreeIndexMoveto(pC->uc.pCursor, pIdxKey, &res); if( pFree ) sqlite3DbFreeNN(db, pFree); if( rc!=SQLITE_OK ){ goto abort_due_to_error; @@ -89916,6 +92326,7 @@ }else{ VdbeBranchTaken(takeJump||alreadyExists==0,2); if( takeJump || !alreadyExists ) goto jump_to_p2; + if( pOp->opcode==OP_IfNoHope ) pC->seekHit = pOp->p4.i; } break; } @@ -90008,7 +92419,7 @@ 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 +92477,10 @@ 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 +92576,7 @@ 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 +92666,14 @@ 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 */ @@ -90272,7 +92685,7 @@ if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey; - assert( pData->flags & (MEM_Blob|MEM_Str) ); + 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); @@ -90283,7 +92696,8 @@ } x.pKey = 0; 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; @@ -90300,6 +92714,33 @@ 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. @@ -90378,13 +92819,14 @@ 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 +92834,7 @@ sqlite3VdbePreUpdateHook(p, pC, (opflags & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_DELETE, zDb, pTab, pC->movetoTarget, - pOp->p3 + pOp->p3, -1 ); } if( opflags & OPFLAG_ISNOOP ) break; @@ -90425,7 +92867,7 @@ /* 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 ); @@ -90955,7 +93397,7 @@ 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 +93408,7 @@ 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 +93454,8 @@ ** 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 +93476,13 @@ 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 ); @@ -91117,7 +93560,9 @@ pTabCur->movetoTarget = rowid; pTabCur->deferredMoveto = 1; 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); @@ -91148,7 +93593,7 @@ 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 +93604,7 @@ ** 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 +93615,7 @@ ** 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 +93626,7 @@ ** 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 @@ -91207,7 +93652,6 @@ 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 +93672,31 @@ } } #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); + sqlite3VdbeMemRelease(&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 +93706,7 @@ res++; } VdbeBranchTaken(res>0,2); - if( rc ) goto abort_due_to_error; + assert( rc==SQLITE_OK ); if( res>0 ) goto jump_to_p2; break; } @@ -91314,24 +93781,21 @@ ** P2==1 then the table to be clear 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 ){ @@ -91437,19 +93901,21 @@ 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; @@ -92319,6 +94785,7 @@ 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 +94832,7 @@ /* 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)); } @@ -92617,7 +95084,7 @@ 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++; @@ -92631,6 +95098,34 @@ #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 */ + 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", sqlite3_free); + break; +} +#endif /* SQLITE_OMIT_VIRTUALTABLE */ + + +#ifndef SQLITE_OMIT_VIRTUALTABLE /* Opcode: VFilter P1 P2 P3 P4 * ** Synopsis: iplan=r[P3] zplan='P4' ** @@ -92668,6 +95163,7 @@ 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,7 +95175,6 @@ iQuery = (int)pQuery->u.i; /* Invoke the xFilter method */ - res = 0; apArg = p->apArg; for(i = 0; iapCsr[pOp->p1]; + assert( pCur!=0 ); assert( pCur->eCurType==CURTYPE_VTAB ); assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); pDest = &aMem[pOp->p3]; @@ -92769,8 +95265,8 @@ 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 +95362,7 @@ const sqlite3_module *pModule; int nArg; int i; - sqlite_int64 rowid; + sqlite_int64 rowid = 0; Mem **apArg; Mem *pX; @@ -93054,6 +95550,77 @@ 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; + 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; + 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 @@ -93305,8 +95872,24 @@ ** 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)); } @@ -93317,6 +95900,9 @@ (int)(pOp - aOp), p->zSql, p->zErrMsg); 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); @@ -93448,7 +96034,10 @@ } 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 ){ @@ -93522,10 +96111,9 @@ 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; @@ -93540,7 +96128,7 @@ sqlite3ErrorMsg(&sParse, "cannot open table without rowid: %s", zTable); } #ifndef SQLITE_OMIT_VIEW - if( pTab && pTab->pSelect ){ + if( pTab && IsView(pTab) ){ pTab = 0; sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable); } @@ -93560,7 +96148,7 @@ /* Now search pTab for the exact column. */ for(iCol=0; iColnCol; iCol++) { - if( sqlite3StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){ + if( sqlite3StrICmp(pTab->aCol[iCol].zCnName, zColumn)==0 ){ break; } } @@ -93585,7 +96173,8 @@ ** 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 ){ @@ -93701,7 +96290,9 @@ 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 ){ @@ -93712,7 +96303,7 @@ } sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr); sqlite3DbFree(db, zErr); - sqlite3ParserReset(&sParse); + sqlite3ParseObjectReset(&sParse); rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; @@ -93792,8 +96383,10 @@ */ 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,6 +96457,7 @@ 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); @@ -94844,7 +97438,8 @@ } #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); @@ -94854,13 +97449,16 @@ 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 +97552,9 @@ 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( @@ -95169,7 +97768,7 @@ 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 @@ -95887,6 +98486,7 @@ vdbeMergeEngineFree(pMerger); rc = SQLITE_NOMEM_BKPT; } + assert( *ppOut!=0 || rc!=SQLITE_OK ); return rc; } @@ -97144,7 +99744,6 @@ 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 +99804,13 @@ /* ** 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 +99837,7 @@ } if( rc==SQLITE_OK ){ /* No error has occurred. Free the in-memory buffers. */ - memjrnlFreeChunks(©); + memjrnlFreeChunks(copy.pFirst); } } if( rc!=SQLITE_OK ){ @@ -97254,6 +99852,9 @@ } +/* Forward reference */ +static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size); + /* ** Write data to the file. */ @@ -97284,22 +99885,20 @@ ** 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 +99913,15 @@ 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 +99929,29 @@ } /* -** 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 +99963,7 @@ */ static int memjrnlClose(sqlite3_file *pJfd){ MemJournal *p = (MemJournal *)pJfd; - memjrnlFreeChunks(p); + memjrnlFreeChunks(p->pFirst); return SQLITE_OK; } @@ -97528,7 +100137,7 @@ ** 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 +100147,11 @@ 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; } @@ -97585,7 +100190,7 @@ 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 +100199,7 @@ } #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 } @@ -97623,6 +100228,16 @@ } /* +** 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 ** any expr callbacks and SELECT callbacks that come from subqueries. @@ -97635,13 +100250,18 @@ 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,10 +100279,10 @@ 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) ){ return WRC_Abort; @@ -97825,7 +100445,6 @@ 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 */ @@ -97837,9 +100456,13 @@ assert( pOrig!=0 ); 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{ + incrAggFunctionDepth(pDup, nSubquery); if( pExpr->op==TK_COLLATE ){ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken); } @@ -97859,15 +100482,12 @@ pExpr->flags |= EP_MemToken; } 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); } - ExprSetProperty(pExpr, EP_Alias); } @@ -97946,6 +100566,7 @@ Table *pExTab; n = pExpr->iColumn; + assert( ExprUseYTab(pExpr) ); pExTab = pExpr->y.pTab; assert( pExTab!=0 ); if( (pExTab->tabFlags & TF_HasGenerated)!=0 @@ -98002,8 +100623,8 @@ int cntTab = 0; /* Number of matching table names */ 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 */ @@ -98059,7 +100680,7 @@ u8 hCol; pTab = pItem->pTab; assert( pTab!=0 && pTab->zName!=0 ); - assert( pTab->nCol>0 ); + assert( pTab->nCol>0 || pParse->nErr ); if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){ int hit = 0; pEList = pItem->pSelect->pEList; @@ -98074,8 +100695,9 @@ } if( hit || zTab==0 ) continue; } - if( zDb && pTab->pSchema!=pSchema ){ - continue; + if( zDb ){ + if( pTab->pSchema!=pSchema ) continue; + if( pSchema==0 && strcmp(zDb,"*")!=0 ) continue; } if( zTab ){ const char *zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName; @@ -98083,16 +100705,16 @@ if( sqlite3StrICmp(zTabName, zTab)!=0 ){ continue; } + assert( ExprUseYTab(pExpr) ); if( IN_RENAME_OBJECT && pItem->zAlias ){ sqlite3RenameTokenRemap(pParse, 0, (void*)&pExpr->y.pTab); } } - if( 0==(cntTab++) ){ - pMatch = pItem; - } hCol = sqlite3StrIHash(zCol); for(j=0, pCol=pTab->aCol; jnCol; j++, pCol++){ - if( pCol->hName==hCol && sqlite3StrICmp(pCol->zName, zCol)==0 ){ + if( pCol->hName==hCol + && sqlite3StrICmp(pCol->zCnName, 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. @@ -98108,9 +100730,14 @@ break; } } + if( 0==cnt && VisibleRowid(pTab) ){ + cntTab++; + pMatch = pItem; + } } if( pMatch ){ pExpr->iTable = pMatch->iCursor; + assert( ExprUseYTab(pExpr) ); pExpr->y.pTab = pMatch->pTab; /* RIGHT JOIN not (yet) supported */ assert( (pMatch->fg.jointype & JT_RIGHT)==0 ); @@ -98124,25 +100751,33 @@ #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 + && (zTab==0 || sqlite3StrICmp(zTab,pParse->pTriggerTab->zName)==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; @@ -98157,7 +100792,9 @@ pSchema = pTab->pSchema; cntTab++; for(iCol=0, pCol=pTab->aCol; iColnCol; iCol++, pCol++){ - if( pCol->hName==hCol && sqlite3StrICmp(pCol->zName, zCol)==0 ){ + if( pCol->hName==hCol + && sqlite3StrICmp(pCol->zCnName, zCol)==0 + ){ if( iCol==pTab->iPKey ){ iCol = -1; } @@ -98170,9 +100807,11 @@ } 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 +100820,34 @@ 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->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)<ncFlags & (NC_IdxExpr|NC_GenCol))==0 && sqlite3IsRowid(zCol) - && VisibleRowid(pMatch->pTab) + && ALWAYS(VisibleRowid(pMatch->pTab)) ){ cnt = 1; pExpr->iColumn = -1; @@ -98241,8 +100887,8 @@ ** 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; @@ -98254,8 +100900,8 @@ ){ 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 +100917,7 @@ 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 +100973,7 @@ 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) ){ @@ -98349,8 +100995,9 @@ }else{ sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol); } + sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); pParse->checkSchema = 1; - pTopNC->nErr++; + pTopNC->nNcErr++; } /* If a column from a table in pSrcList is referenced, then record @@ -98373,18 +101020,24 @@ /* Clean up and return */ - sqlite3ExprDelete(db, pExpr->pLeft); - pExpr->pLeft = 0; - sqlite3ExprDelete(db, pExpr->pRight); - pExpr->pRight = 0; + if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){ + 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 +101059,10 @@ 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->pTab; p->iTable = pItem->iCursor; if( p->y.pTab->iPKey==iCol ){ p->iColumn = -1; @@ -98449,7 +101104,8 @@ 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 +101117,11 @@ #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 +101131,7 @@ 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,10 +101176,11 @@ */ 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; + assert( ExprUseYTab(pExpr) ); pExpr->y.pTab = pItem->pTab; pExpr->iTable = pItem->iCursor; pExpr->iColumn--; @@ -98529,6 +101188,49 @@ 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. + */ + 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( 0==sqlite3ExprCanBeNull(pExpr->pLeft) && !IN_RENAME_OBJECT ){ + testcase( ExprHasProperty(pExpr, EP_FromJoin) ); + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + if( pExpr->op==TK_NOTNULL ){ + pExpr->u.zToken = "true"; + ExprSetProperty(pExpr, EP_IsTrue); + }else{ + pExpr->u.zToken = "false"; + ExprSetProperty(pExpr, EP_IsFalse); + } + pExpr->op = TK_TRUEFALSE; + 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 @@ -98547,24 +101249,28 @@ if( pExpr->op==TK_ID ){ zDb = 0; zTable = 0; + assert( !ExprHasProperty(pExpr, EP_IntValue) ); zColumn = pExpr->u.zToken; }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); @@ -98581,7 +101287,6 @@ 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 +101294,8 @@ #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) ); 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 +101312,9 @@ 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,9 +101334,9 @@ 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; @@ -98654,7 +101358,7 @@ ** in a CHECK constraint. SQLServer, MySQL, and PostgreSQL all ** all 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; @@ -98667,7 +101371,7 @@ /* 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 @@ -98686,9 +101390,9 @@ ); 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 +101404,14 @@ }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,20 +101420,20 @@ && 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 if( is_agg ){ @@ -98753,9 +101457,10 @@ #ifndef SQLITE_OMIT_WINDOWFUNC if( pWin ){ Select *pSel = pNC->pWinSelect; - assert( pWin==pExpr->y.pWin ); + assert( pWin==0 || (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 +101470,7 @@ }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 +101478,22 @@ sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter); } #endif - while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){ + pNC2 = pNC; + while( pNC2 + && sqlite3ReferencesSrcList(pParse, pExpr, pNC2->pSrcList)==0 + ){ pExpr->op2++; pNC2 = pNC2->pNext; } assert( pDef!=0 || IN_RENAME_OBJECT ); if( pNC2 && pDef ){ 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,15 +101509,17 @@ #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); + 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); @@ -98821,7 +101534,7 @@ 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 +101543,7 @@ 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 +101566,7 @@ 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 +101586,13 @@ 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,7 +101617,9 @@ 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 && sqlite3_stricmp(pEList->a[i].zEName, zCol)==0 @@ -98952,11 +101670,11 @@ 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 +101700,13 @@ 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); } /* @@ -99039,9 +101759,10 @@ Expr *pE, *pDup; if( pItem->done ) continue; pE = sqlite3ExprSkipCollateAndLikely(pItem->pExpr); + if( NEVER(pE==0) ) continue; if( sqlite3ExprIsInteger(pE, &iCol) ){ 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 +101775,24 @@ ** 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; @@ -99142,11 +101858,10 @@ 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 +101927,13 @@ 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 ){ @@ -99234,7 +101950,7 @@ ** 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; @@ -99292,7 +102008,7 @@ */ 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; @@ -99301,8 +102017,10 @@ while( p ){ assert( (p->selFlags & SF_Expanded)!=0 ); assert( (p->selFlags & SF_Resolved)==0 ); + assert( db->suppressErr==0 ); /* SF_Resolved not set if errors suppressed */ p->selFlags |= SF_Resolved; + /* Resolve the expressions in the LIMIT and OFFSET clauses. These ** are not allowed to refer to any names, so pass an empty NameContext. */ @@ -99327,30 +102045,30 @@ p->pOrderBy = 0; } - /* Recursively resolve names in all subqueries + /* Recursively resolve names in all subqueries in the FROM clause */ for(i=0; ipSrc->nSrc; i++){ - struct SrcList_item *pItem = &p->pSrc->a[i]; + SrcItem *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 */ + 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); 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); + } } } @@ -99372,18 +102090,12 @@ 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 +102104,21 @@ ** 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( !pGroupBy ){ + sqlite3ErrorMsg(pParse, "a GROUP BY clause is required before HAVING"); + return WRC_Abort; + } + if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort; + } if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort; /* 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 +102126,19 @@ } } +#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 */ @@ -99435,7 +102166,8 @@ ** 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 +102195,6 @@ } } -#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 +102274,11 @@ 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 @@ -99578,7 +102297,7 @@ 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; } /* @@ -99599,8 +102318,8 @@ 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; @@ -99618,12 +102337,13 @@ 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 WRC_Abort; } pNC->ncFlags |= savedHasAgg; return WRC_Continue; @@ -99735,9 +102455,9 @@ /* ** 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,36 +102478,44 @@ */ 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 ); + while( 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; assert( pExpr!=0 ); } op = pExpr->op; + if( op==TK_REGISTER ) op = pExpr->op2; + if( op==TK_COLUMN || op==TK_AGG_COLUMN ){ + assert( ExprUseYTab(pExpr) ); + if( pExpr->y.pTab ){ + return sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn); + } + } if( op==TK_SELECT ){ - assert( pExpr->flags&EP_xIsSelect ); + 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); } - if( op==TK_REGISTER ) op = pExpr->op2; #ifndef SQLITE_OMIT_CAST 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 ); + assert( pExpr->pLeft!=0 && ExprUseXSelect(pExpr->pLeft) ); + assert( pExpr->iColumn < pExpr->iTable ); + assert( pExpr->iTable==pExpr->pLeft->x.pSelect->pEList->nExpr ); return sqlite3ExprAffinity( pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr ); } if( op==TK_VECTOR ){ + assert( ExprUseXList(pExpr) ); return sqlite3ExprAffinity(pExpr->x.pList->a[0].pExpr); } return pExpr->affExpr; @@ -99802,7 +102530,7 @@ ** 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 +102545,11 @@ } 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 +102561,7 @@ */ 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,12 +102575,12 @@ 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 ); + assert( pExpr->op==TK_COLLATE ); pExpr = pExpr->pLeft; } } @@ -99876,27 +102608,30 @@ 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 - ){ - /* 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; - pColl = sqlite3FindCollSeq(db, ENC(db), zColl, 0); + if( op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_TRIGGER ){ + assert( ExprUseYTab(p) ); + if( p->y.pTab!=0 ){ + /* 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 = sqlite3ColumnColl(&p->y.pTab->aCol[j]); + pColl = sqlite3FindCollSeq(db, ENC(db), zColl, 0); + } + break; } - break; } if( op==TK_CAST || op==TK_UPLUS ){ p = p->pLeft; continue; } if( op==TK_VECTOR ){ + 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,11 +102641,9 @@ }else{ Expr *pNext = p->pRight; /* The Expr.x union is never used at the same time as Expr.pRight */ + assert( ExprUseXList(p) ); assert( p->x.pList==0 || p->pRight==0 ); - if( p->x.pList!=0 - && !db->mallocFailed - && ALWAYS(!ExprHasProperty(p, EP_xIsSelect)) - ){ + if( p->x.pList!=0 && !db->mallocFailed ){ int i; for(i=0; ALWAYS(ix.pList->nExpr); i++){ if( ExprHasProperty(p->x.pList->a[i].pExpr, EP_Collate) ){ @@ -99993,7 +102726,7 @@ 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; @@ -100119,7 +102852,7 @@ ** 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 +102862,14 @@ ** 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 +102892,14 @@ ** 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 +102930,12 @@ 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 +102954,23 @@ */ pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, 0, 0); if( pRet ){ + 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 +103020,22 @@ 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 +103064,7 @@ int regLeft = 0; int regRight = 0; u8 opx = op; + int addrCmp = 0; int addrDone = sqlite3VdbeMakeLabel(pParse); int isCommuted = ExprHasProperty(pExpr,EP_Commuted); @@ -100331,21 +103084,24 @@ 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 +103165,14 @@ ** 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 +103180,8 @@ } } } -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 +103203,9 @@ ** 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( 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 +103232,7 @@ ** 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,7 +103243,8 @@ ** 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); } } @@ -100640,6 +103402,63 @@ } } +/* +** 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 @@ -100658,8 +103477,8 @@ }else if( (ExprAlwaysFalse(pLeft) || ExprAlwaysFalse(pRight)) && !IN_RENAME_OBJECT ){ - sqlite3ExprDelete(db, pLeft); - sqlite3ExprDelete(db, pRight); + sqlite3ExprDeferredDelete(pParse, pLeft); + sqlite3ExprDeferredDelete(pParse, pRight); return sqlite3Expr(db, TK_INTEGER, "0"); }else{ return sqlite3PExpr(pParse, TK_AND, pLeft, pRight); @@ -100673,7 +103492,7 @@ 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,12 +103503,16 @@ sqlite3ExprListDelete(db, pList); /* Avoid memory leak when malloc fails */ return 0; } - if( pList && pList->nExpr > pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){ + 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; @@ -100708,8 +103531,8 @@ */ 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 +103547,7 @@ ** 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 +103603,7 @@ 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 +103631,7 @@ pExpr->iColumn = x; if( x>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){ sqlite3ErrorMsg(pParse, "too many SQL variables"); + sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); } } @@ -100815,27 +103640,26 @@ */ 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( !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 ); + assert( (ExprUseXList(p) && p->x.pList==0) || p->pRight==0 ); if( p->pLeft && p->op!=TK_SELECT_COLUMN ) sqlite3ExprDeleteNN(db, p->pLeft); 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{ @@ -100847,7 +103671,10 @@ #endif } } - if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken); + if( ExprHasProperty(p, EP_MemToken) ){ + assert( !ExprHasProperty(p, EP_IntValue) ); + sqlite3DbFree(db, p->u.zToken); + } if( !ExprHasProperty(p, EP_Static) ){ sqlite3DbFreeNN(db, p); } @@ -100856,6 +103683,22 @@ if( p ) sqlite3ExprDeleteNN(db, p); } + +/* +** Arrange to cause pExpr to be deleted when the pParse is deleted. +** This is similar to sqlite3ExprDelete() except that the delete is +** deferred untilthe pParse is deleted. +** +** The pExpr might be deleted immediately on an OOM error. +** +** The deferred delete is (currently) implemented by adding the +** pExpr to the pParse->pConstExpr list with a register number of 0. +*/ +SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){ + pParse->pConstExpr = + sqlite3ExprListAppend(pParse, pParse->pConstExpr, pExpr); +} + /* Invoke sqlite3RenameExprUnmap() and sqlite3ExprDelete() on the ** expression. */ @@ -100873,7 +103716,7 @@ ** 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,7 +103756,7 @@ ** 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 ); @@ -100944,7 +103787,7 @@ ** 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; @@ -100965,7 +103808,7 @@ ** and Expr.pRight variables (but not for any structures pointed to or ** descended from the Expr.x.pList or Expr.x.pSelect variables). */ -static int dupedExprSize(Expr *p, int flags){ +static int dupedExprSize(const Expr *p, int flags){ int nByte = 0; if( p ){ nByte = dupedExprNodeSize(p, flags); @@ -100984,7 +103827,7 @@ ** if any. Before returning, *pzBuffer is set to the first byte past the ** portion of the buffer copied into by this function. */ -static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ +static Expr *exprDup(sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){ Expr *pNew; /* Value to return */ u8 *zAlloc; /* Memory space from which to build Expr object */ u32 staticFlag; /* EP_Static if space not obtained from malloc */ @@ -100998,6 +103841,7 @@ if( pzBuffer ){ zAlloc = *pzBuffer; staticFlag = EP_Static; + assert( zAlloc!=0 ); }else{ zAlloc = sqlite3DbMallocRawNN(db, dupedExprSize(p, dupFlags)); staticFlag = 0; @@ -101046,7 +103890,7 @@ 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); @@ -101075,8 +103919,8 @@ if( !ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){ if( pNew->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); } @@ -101093,7 +103937,7 @@ ** 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); @@ -101111,7 +103955,7 @@ return pRet; } #else -# define withDup(x,y) 0 +# define sqlite3WithDup(x,y) 0 #endif #ifndef SQLITE_OMIT_WINDOWFUNC @@ -101164,20 +104008,23 @@ ** 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,16 +104035,17 @@ && 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; - }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( pNewExpr->pRight ){ + pPriorSelectColOld = pOldExpr->pRight; + pPriorSelectColNew = pNewExpr->pRight; + pNewExpr->pLeft = pNewExpr->pRight; + }else{ + if( pOldExpr->pLeft!=pPriorSelectColOld ){ + pPriorSelectColOld = pOldExpr->pLeft; + pPriorSelectColNew = sqlite3ExprDup(db, pPriorSelectColOld, flags); + pNewExpr->pRight = pPriorSelectColNew; + } + pNewExpr->pLeft = pPriorSelectColNew; } } pItem->zEName = sqlite3DbStrDup(db, pOldItem->zEName); @@ -101219,7 +104067,7 @@ */ #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; @@ -101230,8 +104078,8 @@ 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); @@ -101244,7 +104092,10 @@ if( pNewItem->fg.isIndexedBy ){ pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy); } - pNewItem->pIBIndex = pOldItem->pIBIndex; + pNewItem->u2 = pOldItem->u2; + if( pNewItem->fg.isCte ){ + pNewItem->u2.pCteUse->nUse++; + } if( pNewItem->fg.isTabFunc ){ pNewItem->u1.pFuncArg = sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags); @@ -101260,7 +104111,7 @@ } 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 ); @@ -101284,11 +104135,11 @@ } 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){ @@ -101310,13 +104161,21 @@ 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; @@ -101325,7 +104184,7 @@ 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; } @@ -101347,41 +104206,64 @@ ** 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, sizeof(ExprList)+sizeof(pList->a[0])*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, + sizeof(*pList)+(pList->nAlloc-1)*sizeof(pList->a[0])); + 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 +104304,9 @@ } 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 ); @@ -101500,7 +104380,7 @@ 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 ); @@ -101518,7 +104398,7 @@ ** to the token-map. */ sqlite3Dequote(pItem->zEName); if( IN_RENAME_OBJECT ){ - sqlite3RenameTokenMap(pParse, (void*)pItem->zEName, pName); + sqlite3RenameTokenMap(pParse, (const void*)pItem->zEName, pName); } } } @@ -101637,7 +104517,7 @@ 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; @@ -101654,6 +104534,7 @@ SQLITE_PRIVATE int sqlite3ExprTruthValue(const Expr *pExpr){ pExpr = sqlite3ExprSkipCollate((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; @@ -101858,7 +104739,7 @@ } /* 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; } @@ -101946,7 +104827,7 @@ ** 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. */ -SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr *p, int *pValue){ +SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr *p, int *pValue){ int rc = 0; if( NEVER(p==0) ) return 0; /* Used to only happen following on OOM */ @@ -101965,9 +104846,9 @@ break; } case TK_UMINUS: { - int v; + int v = 0; if( sqlite3ExprIsInteger(p->pLeft, &v) ){ - assert( v!=(-2147483647-1) ); + assert( ((unsigned int)v)!=0x80000000 ); *pValue = -v; rc = 1; } @@ -101994,8 +104875,10 @@ */ 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 +104889,11 @@ case TK_BLOB: return 0; case TK_COLUMN: + assert( ExprUseYTab(p) ); 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 */ + && p->y.pTab->aCol!=0 /* Possible due to prior error */ && p->y.pTab->aCol[p->iColumn].notNull==0); default: return 1; @@ -102077,13 +104961,13 @@ ** table, then return NULL. */ #ifndef SQLITE_OMIT_SUBQUERY -static Select *isCandidateForInOpt(Expr *pX){ +static Select *isCandidateForInOpt(const Expr *pX){ Select *p; SrcList *pSrc; ExprList *pEList; Table *pTab; int i; - if( !ExprHasProperty(pX, EP_xIsSelect) ) return 0; /* Not a subquery */ + if( !ExprUseXSelect(pX) ) return 0; /* Not a subquery */ if( ExprHasProperty(pX, EP_VarSelect) ) return 0; /* Correlated subq */ p = pX->x.pSelect; if( p->pPrior ) return 0; /* Not a compound SELECT */ @@ -102101,7 +104985,7 @@ if( pSrc->a[0].pSelect ) return 0; /* FROM is not a subquery or view */ pTab = pSrc->a[0].pTab; 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 ); @@ -102254,7 +105138,7 @@ ** 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++){ @@ -102282,7 +105166,7 @@ /* Code an OP_Transaction and OP_TableLock for . */ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - assert( iDb>=0 && iDb=0 && iDbtnum, 0, pTab->zName); @@ -102410,7 +105294,7 @@ */ if( eType==0 && (inFlags & IN_INDEX_NOOP_OK) - && !ExprHasProperty(pX, EP_xIsSelect) + && ExprUseXList(pX) && (!sqlite3InRhsIsConstant(pX) || pX->x.pList->nExpr<=2) ){ eType = IN_INDEX_NOOP; @@ -102455,10 +105339,10 @@ ** 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 ); @@ -102508,7 +105392,7 @@ */ SQLITE_PRIVATE void sqlite3VectorErrorMsg(Parse *pParse, Expr *pExpr){ #ifndef SQLITE_OMIT_SUBQUERY - if( pExpr->flags & EP_xIsSelect ){ + if( ExprUseXSelect(pExpr) ){ sqlite3SubselectError(pParse, pExpr->x.pSelect->pEList->nExpr, 1); }else #endif @@ -102572,10 +105456,11 @@ */ if( ExprHasProperty(pExpr, EP_Subrtn) ){ 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); sqlite3VdbeAddOp2(v, OP_OpenDup, iTab, pExpr->iTable); @@ -102584,6 +105469,7 @@ } /* Begin coding the subroutine */ + assert( !ExprUseYWin(pExpr) ); ExprSetProperty(pExpr, EP_Subrtn); assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) ); pExpr->y.sub.regReturn = ++pParse->nMem; @@ -102604,7 +105490,7 @@ 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 +105498,7 @@ #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 +105513,23 @@ /* 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; sqlite3SelectDestInit(&dest, SRT_Set, iTab); dest.zAffSdst = exprINAffinity(pParse, pExpr); pSelect->iLimit = 0; 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( 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 ); @@ -102706,6 +105596,7 @@ if( addrOnce ){ sqlite3VdbeJumpHere(v, addrOnce); /* Subroutine return */ + assert( ExprUseYSub(pExpr) ); sqlite3VdbeAddOp1(v, OP_Return, pExpr->y.sub.regReturn); sqlite3VdbeChangeP1(v, pExpr->y.sub.iAddr-1, sqlite3VdbeCurrentAddr(v)-1); sqlite3ClearTempRegCache(pParse); @@ -102738,12 +105629,33 @@ 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_Integer, 0, pExpr->y.sub.regReturn) + 1; + VdbeComment((v, "return address")); + + /* The evaluation of the EXISTS/SELECT must be repeated every time it ** is encountered if any of the following is true: ** @@ -102755,22 +105667,6 @@ ** 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); } @@ -102819,19 +105715,21 @@ } 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); } + /* Subroutine return */ + assert( ExprUseYSub(pExpr) ); + sqlite3VdbeAddOp1(v, OP_Return, pExpr->y.sub.regReturn); + sqlite3VdbeChangeP1(v, pExpr->y.sub.iAddr-1, sqlite3VdbeCurrentAddr(v)-1); + sqlite3ClearTempRegCache(pParse); return rReg; } #endif /* SQLITE_OMIT_SUBQUERY */ @@ -102845,7 +105743,7 @@ */ 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; @@ -102979,13 +105877,15 @@ ** 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 +105933,9 @@ }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); @@ -103173,11 +106073,12 @@ 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,9 +106122,10 @@ ** 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; @@ -103234,7 +106136,7 @@ }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); } @@ -103270,12 +106172,13 @@ }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; } @@ -103334,6 +106237,7 @@ */ static void exprToRegister(Expr *pExpr, int iReg){ Expr *p = sqlite3ExprSkipCollateAndLikely(pExpr); + if( NEVER(p==0) ) return; p->op2 = p->op; p->op = TK_REGISTER; p->iTable = iReg; @@ -103367,6 +106271,7 @@ int i; iResult = pParse->nMem+1; pParse->nMem += nResult; + assert( ExprUseXList(p) ); for(i=0; ix.pList->a[i].pExpr, i+iResult); } @@ -103441,6 +106346,7 @@ ** 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 ); @@ -103474,7 +106380,6 @@ 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 @@ -103488,7 +106393,7 @@ (aff<=SQLITE_AFF_NONE) ? "none" : azAff[aff-SQLITE_AFF_BLOB]); break; } -#endif +#endif /* !defined(SQLITE_UNTESTABLE) */ } return target; } @@ -103542,7 +106447,8 @@ 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); } @@ -103564,6 +106470,7 @@ */ int aff; iReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft,target); + assert( ExprUseYTab(pExpr) ); if( pExpr->y.pTab ){ aff = sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn); }else{ @@ -103587,9 +106494,11 @@ ** 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 +106512,12 @@ 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,6 +106536,7 @@ iTab = pParse->iSelfTab - 1; } } + assert( ExprUseYTab(pExpr) ); iReg = sqlite3ExprCodeGetColumn(pParse, pExpr->y.pTab, pExpr->iColumn, iTab, target, pExpr->op2); @@ -103660,7 +106570,7 @@ ** 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; } @@ -103704,6 +106614,7 @@ sqlite3VdbeAddOp2(v, OP_SCopy, inReg, target); inReg = target; } + assert( !ExprHasProperty(pExpr, EP_IntValue) ); sqlite3VdbeAddOp2(v, OP_Cast, target, sqlite3AffinityType(pExpr->u.zToken, 0)); return inReg; @@ -103726,8 +106637,9 @@ }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 +106647,11 @@ 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,7 +106754,7 @@ || 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; } @@ -103865,8 +106782,8 @@ ** 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,7 +106795,7 @@ } #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 ){ @@ -103955,7 +106872,7 @@ sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ); } #ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC - if( pDef->funcFlags & SQLITE_FUNC_OFFSET ){ + if( (pDef->funcFlags & SQLITE_FUNC_OFFSET)!=0 && ALWAYS(pFarg!=0) ){ Expr *pArg = pFarg->a[0].pExpr; if( pArg->op==TK_COLUMN ){ sqlite3VdbeAddOp3(v, OP_Offset, pArg->iTable, pArg->iColumn, target); @@ -103985,7 +106902,10 @@ 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); @@ -103997,10 +106917,9 @@ if( pExpr->pLeft->iTable==0 ){ pExpr->pLeft->iTable = sqlite3CodeSubselect(pParse, pExpr->pLeft); } - assert( pExpr->iTable==0 || pExpr->pLeft->op==TK_SELECT ); - if( pExpr->iTable!=0 - && pExpr->iTable!=(n = sqlite3ExprVectorSize(pExpr->pLeft)) - ){ + assert( pExpr->pLeft->op==TK_SELECT || pExpr->pLeft->op==TK_ERROR ); + n = sqlite3ExprVectorSize(pExpr->pLeft); + if( pExpr->iTable!=n ){ sqlite3ErrorMsg(pParse, "%d columns assigned %d values", pExpr->iTable, n); } @@ -104068,9 +106987,14 @@ ** 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 +107005,7 @@ 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 @@ -104158,7 +107082,7 @@ 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; @@ -104321,6 +107245,7 @@ int r2; pExpr = sqlite3ExprSkipCollateAndLikely(pExpr); if( ConstFactorOk(pParse) + && ALWAYS(pExpr!=0) && pExpr->op!=TK_REGISTER && sqlite3ExprIsConstantNotJoin(pExpr) ){ @@ -104354,7 +107279,7 @@ inReg = sqlite3ExprCodeTarget(pParse, pExpr, target); if( inReg!=target ){ u8 op; - if( ExprHasProperty(pExpr,EP_Subquery) ){ + if( ALWAYS(pExpr) && ExprHasProperty(pExpr,EP_Subquery) ){ op = OP_Copy; }else{ op = OP_SCopy; @@ -104502,7 +107427,7 @@ 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; @@ -104892,7 +107817,11 @@ ** Otherwise, if the values are not the same or if pExpr is not a simple ** SQL value, zero is returned. */ -static int exprCompareVariable(Parse *pParse, Expr *pVar, Expr *pExpr){ +static int exprCompareVariable( + const Parse *pParse, + const Expr *pVar, + const Expr *pExpr +){ int res = 0; int iVar; sqlite3_value *pL, *pR = 0; @@ -104944,7 +107873,12 @@ ** Argument pParse should normally be NULL. If it is not NULL and pA or ** pB causes a return value of 2. */ -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; @@ -104968,7 +107902,9 @@ } 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 +107922,12 @@ 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 +107969,7 @@ ** 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; @@ -105047,7 +107988,7 @@ ** 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), @@ -105061,9 +108002,9 @@ ** 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 +108016,13 @@ 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; @@ -105156,7 +108098,12 @@ ** 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; } @@ -105252,10 +108199,14 @@ 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 + && pLeft->y.pTab!=0 + && IsVirtual(pLeft->y.pTab)) + || (pRight->op==TK_COLUMN + && pRight->y.pTab!=0 + && IsVirtual(pRight->y.pTab)) ){ return WRC_Prune; } @@ -105364,88 +108315,125 @@ } -/* -** 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 throught 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. -*/ -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; +** 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 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; 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); #ifndef SQLITE_OMIT_WINDOWFUNC if( ExprHasProperty(pExpr, EP_WinFunc) ){ sqlite3WalkExpr(&w, pExpr->y.pWin->pFilter); } #endif - return cnt.nThis>0 || cnt.nOther==0; + sqlite3DbFree(pParse->db, x.aiExclude); + if( w.eCode & 0x01 ){ + return 1; + }else if( w.eCode ){ + return 0; + }else{ + return -1; + } } /* @@ -105476,8 +108464,7 @@ pExpr = sqlite3ExprDup(db, pExpr, 0); if( pExpr ){ pAggInfo->aCol[iAgg].pCExpr = pExpr; - pParse->pConstExpr = - sqlite3ExprListAppend(pParse, pParse->pConstExpr, pExpr); + sqlite3ExprDeferredDelete(pParse, pExpr); } } }else{ @@ -105486,8 +108473,7 @@ pExpr = sqlite3ExprDup(db, pExpr, 0); if( pExpr ){ pAggInfo->aFunc[iAgg].pFExpr = pExpr; - pParse->pConstExpr = - sqlite3ExprListAppend(pParse, pParse->pConstExpr, pExpr); + sqlite3ExprDeferredDelete(pParse, pExpr); } } } @@ -105559,7 +108545,7 @@ /* 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) ); @@ -105582,6 +108568,7 @@ && (k = addAggInfoColumn(pParse->db, pAggInfo))>=0 ){ pCol = &pAggInfo->aCol[k]; + assert( ExprUseYTab(pExpr) ); pCol->pTab = pExpr->y.pTab; pCol->iTable = pExpr->iTable; pCol->iColumn = pExpr->iColumn; @@ -105630,6 +108617,7 @@ */ struct AggInfo_func *pItem = pAggInfo->aFunc; for(i=0; inFunc; i++, pItem++){ + if( pItem->pFExpr==pExpr ) break; if( sqlite3ExprCompare(0, pItem->pFExpr, pExpr, -1)==0 ){ break; } @@ -105644,7 +108632,7 @@ pItem = &pAggInfo->aFunc[i]; pItem->pFExpr = pExpr; pItem->iMem = ++pParse->nMem; - assert( !ExprHasProperty(pExpr, EP_IntValue) ); + assert( ExprUseUToken(pExpr) ); pItem->pFunc = sqlite3FindFunction(pParse->db, pExpr->u.zToken, pExpr->x.pList ? pExpr->x.pList->nExpr : 0, enc, 0); @@ -105830,6 +108818,7 @@ 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 +108837,56 @@ ** 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 +108895,12 @@ ** 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 +108922,7 @@ 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 +108931,6 @@ 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 +108959,7 @@ } #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 +109001,7 @@ /* 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 +109011,7 @@ /* 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 +109046,7 @@ "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 +109065,12 @@ } #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 +109111,9 @@ 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 +109122,7 @@ 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 +109156,8 @@ 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 +109194,30 @@ 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 +109226,25 @@ 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) + ){ + sqlite3NestedParse(pParse, + "SELECT CASE WHEN quick_check GLOB 'CHECK*'" + " THEN raise(ABORT,'CHECK constraint failed')" + " ELSE raise(ABORT,'NOT NULL constraint failed')" + " END" + " FROM pragma_quick_check(%Q,%Q)" + " WHERE quick_check GLOB 'CHECK*' OR quick_check GLOB 'NULL*'", + zTab, zDb + ); + } + } } /* @@ -106249,7 +109285,7 @@ #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 +109294,8 @@ } 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 @@ -106285,13 +109322,13 @@ memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*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->u.tab.addColOffset = pTab->u.tab.addColOffset; pNew->nTabRef = 1; exit_begin_add_column: @@ -106308,10 +109345,10 @@ ** 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 +109358,16 @@ } #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 +109396,7 @@ /* 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); @@ -106377,13 +109415,17 @@ 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( 0==sqlite3StrICmp(pTab->aCol[iCol].zCnName, zOld) ) break; } if( iCol==pTab->nCol ){ - sqlite3ErrorMsg(pParse, "no such column: \"%s\"", zOld); + 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 +109436,25 @@ 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 +109481,7 @@ ** 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,9 +109523,11 @@ ** 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; +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; u8 i = 0; for(p=pParse->pRename; p; p=p->pNext){ if( p->p ){ @@ -106510,7 +109553,11 @@ ** 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 +109579,7 @@ ** 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 +109595,10 @@ */ 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 +109609,31 @@ 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,12 +109643,12 @@ */ 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); + sqlite3RenameTokenRemap(pParse, 0, (const void*)pIdList->a[ii].zName); } } } @@ -106594,7 +109660,11 @@ 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++){ @@ -106607,7 +109677,7 @@ 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; + sqlite3WalkExpr(pWalker, pSrc->a[i].pOn); unmapColumnIdlistNames(pParse, pSrc->a[i].pUsing); } } @@ -106665,23 +109735,35 @@ /* ** 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 +109772,11 @@ ** 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 +109799,7 @@ 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); @@ -106752,7 +109839,7 @@ */ static void renameColumnParseError( sqlite3_context *pCtx, - int bPost, + const char *zWhen, sqlite3_value *pType, sqlite3_value *pObject, Parse *pParse @@ -106761,12 +109848,12 @@ 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 +109865,18 @@ 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; + const char *zName = pEList->a[i].zEName; if( ALWAYS(pEList->a[i].eEName==ENAME_NAME) && ALWAYS(zName!=0) && 0==sqlite3_stricmp(zName, zOld) ){ - renameTokenFind(pParse, pCtx, (void*)zName); + renameTokenFind(pParse, pCtx, (const void*)zName); } } } @@ -106803,15 +109890,15 @@ 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,21 +109917,17 @@ int bTemp /* True if SQL is from temp schema */ ){ int rc; - char *zErr = 0; 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); 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; + rc = zSql ? sqlite3RunParser(p, zSql) : SQLITE_NOMEM; if( db->mallocFailed ) rc = SQLITE_NOMEM; if( rc==SQLITE_OK && p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0 @@ -106884,51 +109967,76 @@ 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; + i64 nQuot = 0; + char *zBuf1 = 0; + char *zBuf2 = 0; - /* 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; + 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 ); + zOut = sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1); }else{ - nQuot = sqlite3Strlen30(zQuot); - } - if( bQuote ){ - zNew = zQuot; - nNew = nQuot; + zOut = (char*)sqlite3DbMallocZero(db, (nSql*2+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); while( pRename->pList ){ int iOff; /* Offset of token to replace in zOut */ - RenameToken *pBest = renameColumnTokenNext(pRename); - u32 nReplace; const char *zReplace; - if( sqlite3IsIdChar(*pBest->t.z) ){ - nReplace = nNew; - zReplace = zNew; - }else{ - nReplace = nQuot; - zReplace = zQuot; + RenameToken *pBest = renameColumnTokenNext(pRename); + + if( zNew ){ + if( bQuote==0 && sqlite3IsIdChar(*pBest->t.z) ){ + nReplace = nNew; + zReplace = zNew; + }else{ + nReplace = nQuot; + zReplace = zQuot; + if( pBest->t.z[pBest->t.n]=='"' ) nReplace++; + } + }else{ + /* 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); + sqlite3_snprintf(nSql*2, zBuf2, "%Q%s", zBuf1, + pBest->t.z[pBest->t.n]=='\'' ? " " : "" + ); + zReplace = zBuf2; + nReplace = sqlite3Strlen30(zReplace); } iOff = pBest->t.z - zSql; @@ -106994,16 +110102,27 @@ 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); + SrcItem *p = &pSrc->a[i]; p->iCursor = pParse->nTab++; - if( p->pTab==0 ){ - rc = SQLITE_ERROR; + if( p->pSelect ){ + sqlite3SelectPrep(pParse, p->pSelect, 0); + sqlite3ExpandSubquery(pParse, p); + assert( i>0 ); + assert( pStep->pFrom->a[i-1].pSelect ); + sqlite3SelectPrep(pParse, pStep->pFrom->a[i-1].pSelect, 0); }else{ - p->pTab->nTabRef++; - rc = sqlite3ViewGetColumnNames(pParse, p->pTab); + p->pTab = sqlite3LocateTableItem(pParse, 0, p); + if( p->pTab==0 ){ + rc = SQLITE_ERROR; + }else{ + p->pTab->nTabRef++; + rc = sqlite3ViewGetColumnNames(pParse, p->pTab); + } } } + if( rc==SQLITE_OK && db->mallocFailed ){ + rc = SQLITE_NOMEM; + } sNC.pSrcList = pSrc; if( rc==SQLITE_OK && pStep->pWhere ){ rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere); @@ -107012,9 +110131,8 @@ 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 +110181,12 @@ sqlite3WalkExpr(pWalker, pUpsert->pUpsertWhere); sqlite3WalkExpr(pWalker, pUpsert->pUpsertTargetWhere); } + if( pStep->pFrom ){ + int i; + for(i=0; ipFrom->nSrc; i++){ + sqlite3WalkSelect(pWalker, pStep->pFrom->a[i].pSelect); + } + } } } @@ -107084,13 +110208,13 @@ 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 +110232,8 @@ ** ** 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 +110271,7 @@ 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,8 +110290,8 @@ sCtx.pTab = pTab; if( rc!=SQLITE_OK ) goto renameColumnFunc_done; if( sParse.pNewTable ){ - Select *pSelect = sParse.pNewTable->pSelect; - if( pSelect ){ + if( IsView(sParse.pNewTable) ){ + Select *pSelect = sParse.pNewTable->u.view.pSelect; pSelect->selFlags &= ~SF_View; sParse.rc = SQLITE_OK; sqlite3SelectPrep(&sParse, pSelect, 0); @@ -107175,16 +110300,17 @@ 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 +110321,17 @@ 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 +110382,10 @@ 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 +110404,10 @@ */ 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,13 +110420,17 @@ 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]; + SrcItem *pItem = &pSrc->a[i]; if( pItem->pTab==p->pTab ){ renameTokenFind(pWalker->pParse, p, pItem->zName); } @@ -107364,28 +110502,31 @@ 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); + 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); } @@ -107442,8 +110583,10 @@ 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 +110603,131 @@ 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 &= ~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 +110739,16 @@ ** 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 +110760,8 @@ 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 +110769,20 @@ #endif UNUSED_PARAMETER(NotUsed); + if( zDb && zInput ){ int rc; Parse sParse; + int flags = db->flags; + if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL); rc = renameParseSql(&sParse, zDb, db, zInput, bTemp); + db->flags |= (flags & (SQLITE_DqsDML|SQLITE_DqsDDL)); 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 +110793,17 @@ 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); } @@ -107533,13 +110814,231 @@ } /* +** 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{ + sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOut); + } + 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)); } @@ -107982,7 +111481,6 @@ + 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); @@ -108401,28 +111899,19 @@ ** ** I = (K+D-1)/D */ - 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); + sqlite3_str_appendf(&sStat, " %llu", iVal); assert( p->current.anEq[i] ); } - 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 +111930,8 @@ } }else{ tRowcnt *aCnt = 0; + sqlite3_str sStat; + int i; assert( p->iGetnSample ); switch( eCall ){ @@ -108452,23 +111943,12 @@ 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 @@ -108517,7 +111997,7 @@ }else if( i==XN_EXPR ){ 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 @@ -108564,7 +112044,7 @@ if( v==0 || NEVER(pTab==0) ){ return; } - if( pTab->tnum==0 ){ + if( !IsOrdinaryTable(pTab) ){ /* Do not gather statistics on views or virtual tables */ return; } @@ -109321,6 +112801,7 @@ } pSpace = (tRowcnt*)&pIdx->aSample[nSample]; 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; @@ -109388,9 +112869,12 @@ */ 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( (pStat4 = sqlite3FindTable(db, "sqlite_stat4", zDb))!=0 + && IsOrdinaryTable(pStat4) + ){ rc = loadStatTbl(db, "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx", "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4", @@ -109427,6 +112911,7 @@ 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 +112934,9 @@ /* 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 ){ @@ -109589,7 +113076,7 @@ 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) @@ -109787,7 +113274,9 @@ 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; } @@ -109838,17 +113327,18 @@ 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; @@ -109926,6 +113416,65 @@ #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 ){ + if( pItem->zDatabase ){ + if( iDb!=sqlite3FindDbName(db, pItem->zDatabase) ){ + sqlite3ErrorMsg(pFix->pParse, + "%s %T cannot reference objects in database %s", + pFix->zType, pFix->pName, pItem->zDatabase); + return WRC_Abort; + } + sqlite3DbFree(db, pItem->zDatabase); + pItem->zDatabase = 0; + pItem->fg.notCte = 1; + } + pItem->pSchema = pFix->pSchema; + pItem->fg.fromDDL = 1; + } +#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) + if( sqlite3WalkExpr(&pFix->w, pList->a[i].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 +113485,7 @@ 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 +113493,13 @@ 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 +113520,27 @@ 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 +113550,30 @@ 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 @@ -110261,7 +113725,6 @@ 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 +113732,8 @@ 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,7 +113745,7 @@ 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; break; @@ -110290,18 +113753,18 @@ } } 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; } @@ -110327,11 +113790,7 @@ ** 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; } @@ -110440,21 +113899,20 @@ ** 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 ){ @@ -110477,6 +113935,17 @@ 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 +113953,8 @@ */ 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]; @@ -110529,19 +113996,52 @@ 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 ){ + if( pParse->bReturning ){ + Returning *pReturning = pParse->u1.pReturning; + int addrRewind; + int i; + int reg; + + if( NEVER(pReturning->nRetCol==0) ){ + assert( CORRUPT_DB ); + }else{ + 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); + } + } sqlite3VdbeAddOp0(v, OP_Halt); #if SQLITE_USER_AUTHENTICATION @@ -110619,15 +114119,25 @@ } } + if( pParse->bReturning ){ + Returning *pRet = pParse->u1.pReturning; + if( NEVER(pRet->nRetCol==0) ){ + assert( CORRUPT_DB ); + }else{ + 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,20 +114151,21 @@ /* ** 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. -** -** 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. +** currently under construction. Notes: +** +** * 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; @@ -110673,8 +114184,11 @@ 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); + sqlite3DbFree(db, pParse->zErrMsg); + pParse->zErrMsg = 0; + db->mDbFlags = savedDbFlags; sqlite3DbFree(db, zSql); memcpy(PARSE_TAIL(pParse), saveBuf, PARSE_TAIL_SZ); pParse->nested--; @@ -110731,17 +114245,17 @@ 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 +114273,11 @@ 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,12 +114317,13 @@ /* 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->disableVtab==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; } } @@ -110826,6 +114341,8 @@ }else{ sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName); } + }else{ + assert( HasRowid(p) || p->iPKey<0 ); } return p; @@ -110843,7 +114360,7 @@ SQLITE_PRIVATE Table *sqlite3LocateTableItem( Parse *pParse, u32 flags, - struct SrcList_item *p + SrcItem *p ){ const char *zDb; assert( p->pSchema==0 || p->zDatabase==0 ); @@ -110857,6 +114374,22 @@ } /* +** 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 ** and the name of the database that contains the index. @@ -111021,6 +114554,84 @@ } /* +** 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( NEVER(!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 squence 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). */ @@ -111030,12 +114641,20 @@ assert( pTable!=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); } sqlite3DbFree(db, pTable->aCol); + if( IsOrdinaryTable(pTable) ){ + sqlite3ExprListDelete(db, pTable->u.tab.pDfltList); + } + if( db==0 || db->pnBytesFreed==0 ){ + pTable->aCol = 0; + pTable->nCol = 0; + if( IsOrdinaryTable(pTable) ){ + pTable->u.tab.pDfltList = 0; + } + } } } @@ -111087,19 +114706,25 @@ sqlite3FreeIndex(db, pIndex); } - /* Delete any foreign keys attached to this table. */ - sqlite3FkDelete(db, pTable); + if( IsOrdinaryTable(pTable) ){ + sqlite3FkDelete(db, pTable); + } +#ifndef SQLITE_OMIT_VIRTUAL_TABLE + 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 */ @@ -111145,10 +114770,10 @@ ** 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 +114787,7 @@ */ 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 +114867,7 @@ 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; @@ -111412,6 +115037,23 @@ #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 ** to a CREATE TABLE statement. In particular, this routine is called @@ -111506,10 +115148,12 @@ 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 +115182,6 @@ 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 @@ -111601,7 +115234,8 @@ }else #endif { - pParse->addrCrTab = + assert( !pParse->bReturning ); + pParse->u1.addrCrTab = sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, reg2, BTREE_INTKEY); } sqlite3OpenSchemaTable(pParse, iDb); @@ -111617,6 +115251,7 @@ /* If an error occurs, we jump here */ begin_table_error: + pParse->checkSchema = 1; sqlite3DbFree(db, zName); return; } @@ -111626,14 +115261,88 @@ */ #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 +/* +** Name of the special TEMP trigger used to implement RETURNING. The +** name begins with "sqlite_" so that it is guaranteed not to collide +** with any application-generated triggers. +*/ +#define RETURNING_TRIGGER_NAME "sqlite_returning" + +/* +** Clean up the data structures associated with the RETURNING clause. +*/ +static void sqlite3DeleteReturning(sqlite3 *db, Returning *pRet){ + Hash *pHash; + pHash = &(db->aDb[1].pSchema->trigHash); + sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, 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->bReturning = 1; + pRet = sqlite3DbMallocZero(db, sizeof(*pRet)); + if( pRet==0 ){ + sqlite3ExprListDelete(db, pList); + return; + } + pParse->u1.pReturning = pRet; + pRet->pParse = pParse; + pRet->pReturnEL = pList; + sqlite3ParserAddCleanup(pParse, + (void(*)(sqlite3*,void*))sqlite3DeleteReturning, pRet); + testcase( pParse->earlyCleanup ); + if( db->mallocFailed ) return; + pRet->retTrig.zName = RETURNING_TRIGGER_NAME; + 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, RETURNING_TRIGGER_NAME)==0 || pParse->nErr ); + if( sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, &pRet->retTrig) + ==&pRet->retTrig ){ + sqlite3OomFault(db); + } +} /* ** Add a new column to the table currently being constructed. @@ -111643,60 +115352,104 @@ ** 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; + u8 hName; + 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 indentifiers + ** 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); + hName = sqlite3StrIHash(z); for(i=0; inCol; i++){ - if( sqlite3_stricmp(z, p->aCol[i].zName)==0 ){ + if( p->aCol[i].hName==hName && sqlite3StrICmp(z, p->aCol[i].zCnName)==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->hName = sqlite3StrIHash(z); + pCol->zCnName = z; + pCol->hName = hName; 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; @@ -111851,7 +115604,7 @@ 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 +115615,15 @@ /* 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,9 +115719,11 @@ assert( pCExpr!=0 ); sqlite3StringToId(pCExpr); if( pCExpr->op==TK_ID ){ - const char *zCName = pCExpr->u.zToken; + const char *zCName; + assert( !ExprHasProperty(pCExpr, EP_IntValue) ); + zCName = pCExpr->u.zToken; for(iCol=0; iColnCol; iCol++){ - if( sqlite3StrICmp(zCName, pTab->aCol[iCol].zName)==0 ){ + if( sqlite3StrICmp(zCName, pTab->aCol[iCol].zCnName)==0 ){ pCol = &pTab->aCol[iCol]; makeColumnPartOfPrimaryKey(pParse, pCol); break; @@ -111979,7 +115734,7 @@ } if( nTerm==1 && pCol - && sqlite3StrICmp(sqlite3ColumnType(pCol,""), "INTEGER")==0 + && pCol->eCType==COLTYPE_INTEGER && sortOrder!=SQLITE_SO_DESC ){ if( IN_RENAME_OBJECT && pList ){ @@ -112012,8 +115767,10 @@ ** 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; @@ -112024,6 +115781,13 @@ pTab->pCheck = sqlite3ExprListAppend(pParse, pTab->pCheck, pCheckExpr); if( pParse->constraintName.n ){ sqlite3ExprListSetName(pParse, pTab->pCheck, &pParse->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 +115806,7 @@ 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 +115814,7 @@ 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 +115823,11 @@ 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 +115847,7 @@ 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 +115865,13 @@ if( pCol->colFlags & COLFLAG_PRIMKEY ){ makeColumnPartOfPrimaryKey(pParse, pCol); /* For the error message */ } - pCol->pDflt = pExpr; + 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 @@ -112211,7 +115973,7 @@ 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 ){ @@ -112247,7 +116009,7 @@ sqlite3_snprintf(n-k, &zStmt[k], zSep); k += sqlite3Strlen30(&zStmt[k]); zSep = zSep2; - identPut(zStmt, &k, pCol->zName); + identPut(zStmt, &k, pCol->zCnName); assert( pCol->affinity-SQLITE_AFF_BLOB >= 0 ); assert( pCol->affinity-SQLITE_AFF_BLOB < ArraySize(azType) ); testcase( pCol->affinity==SQLITE_AFF_BLOB ); @@ -112277,12 +116039,15 @@ int nByte; if( pIdx->nColumn>=N ) return SQLITE_OK; assert( pIdx->isResized==0 ); - nByte = (sizeof(char*) + sizeof(i16) + 1)*N; + nByte = (sizeof(char*) + sizeof(LogEst) + sizeof(i16) + 1)*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; @@ -112328,7 +116093,6 @@ */ static int hasColumn(const i16 *aiCol, int nCol, int x){ while( nCol-- > 0 ){ - assert( aiCol[0]>=0 ); if( x==*(aiCol++) ){ return 1; } @@ -112441,7 +116205,9 @@ */ 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 +116217,10 @@ /* Convert the P3 operand of the OP_CreateBtree opcode from BTREE_INTKEY ** into BTREE_BLOBKEY. */ - if( pParse->addrCrTab ){ + assert( !pParse->bReturning ); + if( pParse->u1.addrCrTab ){ assert( v ); - sqlite3VdbeChangeP3(v, pParse->addrCrTab, BTREE_BLOBKEY); + sqlite3VdbeChangeP3(v, pParse->u1.addrCrTab, BTREE_BLOBKEY); } /* Locate the PRIMARY KEY index. Or, if this table was originally @@ -112462,10 +116229,13 @@ 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); } @@ -112474,7 +116244,11 @@ 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{ @@ -112586,7 +116360,7 @@ 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; @@ -112596,6 +116370,41 @@ #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 ** connection. ** @@ -112667,7 +116476,7 @@ 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 +116487,6 @@ if( pEnd==0 && pSelect==0 ){ return; } - assert( !db->mallocFailed ); p = pParse->pNewTable; if( p==0 ) return; @@ -112696,7 +116504,7 @@ ** 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 +116512,44 @@ 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 +116594,7 @@ 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 +116604,8 @@ ** 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 +116645,7 @@ /* ** Initialize zType for the new view or table. */ - if( p->pSelect==0 ){ + if( IsOrdinaryTable(p) ){ /* A regular table */ zType = "table"; zType2 = "TABLE"; @@ -112885,7 +116731,7 @@ ** the information we've collected. */ 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, @@ -112903,7 +116749,7 @@ /* 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 +116763,7 @@ /* 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); } /* Add the table to the in-memory representation of the database. @@ -112926,6 +116772,7 @@ 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 +116782,27 @@ 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 +116835,16 @@ 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. */ +#ifndef SQLITE_ALLOW_ROWID_IN_VIEW + p->tabFlags |= TF_NoVisibleRowid; +#endif + sqlite3TwoPartName(pParse, pName1, pName2, &pName); iDb = sqlite3SchemaToIndex(db, p->pSchema); sqlite3FixInit(&sFix, pParse, iDb, "view", pName); @@ -112992,12 +116857,13 @@ */ 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 @@ -113051,13 +116917,12 @@ 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 @@ -113094,8 +116959,8 @@ ** 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; pParse->eParseMode = PARSE_MODE_NORMAL; @@ -113124,10 +116989,10 @@ */ sqlite3ColumnsFromExprList(pParse, pTable->pCheck, &pTable->nCol, &pTable->aCol); - if( db->mallocFailed==0 - && pParse->nErr==0 + if( pParse->nErr==0 && pTable->nCol==pSel->pEList->nExpr ){ + assert( db->mallocFailed==0 ); sqlite3SelectAddColumnTypeAndCollation(pParse, pTable, pSel, SQLITE_AFF_NONE); } @@ -113138,6 +117003,7 @@ 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,8 +117019,6 @@ pTable->pSchema->schemaFlags |= DB_UnresetViews; if( db->mallocFailed ){ sqlite3DeleteColumnNames(db, pTable); - pTable->aCol = 0; - pTable->nCol = 0; } #endif /* SQLITE_OMIT_VIEW */ return nErr; @@ -113171,10 +117035,8 @@ 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 +117110,7 @@ ** 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 +117245,7 @@ ** 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 +117273,7 @@ if( (db->flags & SQLITE_Defensive)!=0 && db->pVtabCtx==0 && db->nVdbeExec==0 + && !sqlite3VtabInSync(db) ){ return 1; } @@ -113430,6 +117293,9 @@ if( (pTab->tabFlags & TF_Shadow)!=0 && sqlite3ReadOnlyShadowTables(db) ){ return 1; } + if( pTab->tabFlags & TF_Eponymous ){ + return 1; + } return 0; } @@ -113455,7 +117321,10 @@ if( noErr ) db->suppressErr--; if( pTab==0 ){ - if( noErr ) sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); + if( noErr ){ + sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); + sqlite3ForceNotReadOnly(pParse); + } goto exit_drop_table; } iDb = sqlite3SchemaToIndex(db, pTab->pSchema); @@ -113511,11 +117380,11 @@ /* 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 +117435,7 @@ FKey *pFKey = 0; FKey *pNextTo; Table *p = pParse->pNewTable; - int nByte; + i64 nByte; int i; int nCol; char *z; @@ -113579,7 +117448,7 @@ 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; @@ -113602,7 +117471,8 @@ 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 +117489,7 @@ 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 +117537,8 @@ /* 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 +117559,9 @@ #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 +117611,7 @@ 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++; @@ -113902,9 +117775,11 @@ 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; } @@ -113968,7 +117843,6 @@ pDb = &db->aDb[iDb]; assert( pTab!=0 ); - assert( pParse->nErr==0 ); if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 && db->init.busy==0 && pTblName!=0 @@ -113980,7 +117854,7 @@ 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; } @@ -114025,6 +117899,7 @@ }else{ assert( !db->init.busy ); sqlite3CodeVerifySchema(pParse, iDb); + sqlite3ForceNotReadOnly(pParse); } goto exit_create_index; } @@ -114070,7 +117945,7 @@ 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 +117963,7 @@ 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)); } } @@ -114183,6 +118059,7 @@ 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,7 +118068,7 @@ 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) ){ @@ -114389,13 +118266,13 @@ /* 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 +118282,7 @@ 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 +118303,12 @@ /* 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 +118321,16 @@ } 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 +118382,7 @@ 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,10 +118406,10 @@ 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 ); if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ goto exit_drop_index; @@ -114526,9 +118417,10 @@ pIndex = sqlite3FindIndex(db, pName->a[0].zName, pName->a[0].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); + sqlite3ForceNotReadOnly(pParse); } pParse->checkSchema = 1; goto exit_drop_index; @@ -114548,7 +118440,7 @@ 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 +118452,7 @@ 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); @@ -114798,7 +118690,7 @@ 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 ); @@ -114839,11 +118731,11 @@ */ 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); @@ -114857,18 +118749,18 @@ */ SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){ int i; - struct SrcList_item *pItem; + SrcItem *pItem; if( pList==0 ) return; for(pItem=pList->a, i=0; inSrc; i++, pItem++){ - sqlite3DbFree(db, pItem->zDatabase); + if( pItem->zDatabase ) sqlite3DbFreeNN(db, pItem->zDatabase); sqlite3DbFree(db, pItem->zName); - sqlite3DbFree(db, pItem->zAlias); + if( pItem->zAlias ) sqlite3DbFreeNN(db, pItem->zAlias); 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); + if( pItem->pSelect ) sqlite3SelectDelete(db, pItem->pSelect); + if( pItem->pOn ) sqlite3ExprDelete(db, pItem->pOn); + if( pItem->pUsing ) sqlite3IdListDelete(db, pItem->pUsing); } sqlite3DbFreeNN(db, pList); } @@ -114899,7 +118791,7 @@ Expr *pOn, /* The ON clause of a join */ IdList *pUsing /* The USING clause of a join */ ){ - struct SrcList_item *pItem; + SrcItem *pItem; sqlite3 *db = pParse->db; if( !p && (pOn || pUsing) ){ sqlite3ErrorMsg(pParse, "a JOIN clause is required before %s", @@ -114928,7 +118820,7 @@ pItem->pUsing = pUsing; return p; - append_from_error: +append_from_error: assert( p==0 ); sqlite3ExprDelete(db, pOn); sqlite3IdListDelete(db, pUsing); @@ -114943,7 +118835,7 @@ 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 +118848,7 @@ }else{ pItem->u1.zIndexedBy = sqlite3NameFromToken(pParse->db, pIndexedBy); pItem->fg.isIndexedBy = 1; + assert( pItem->fg.isCte==0 ); /* No collision on union u2 */ } } } @@ -114973,7 +118866,7 @@ 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); } } @@ -114986,7 +118879,7 @@ */ 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 ); @@ -115040,7 +118933,16 @@ 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 +119031,11 @@ ** 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 +119043,10 @@ } } } +SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ + sqlite3CodeVerifySchemaAtToplevel(sqlite3ParseToplevel(pParse), iDb); +} + /* ** If argument zDb is NULL, then call sqlite3CodeVerifySchema() for each @@ -115174,7 +119078,7 @@ */ 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 +119129,9 @@ 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 +119161,7 @@ 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 +119188,7 @@ 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); @@ -115471,23 +119377,75 @@ #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 teh 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++){ @@ -115506,16 +119464,11 @@ 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,10 +119481,7 @@ 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); } @@ -115879,6 +119829,7 @@ ){ 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 +119850,7 @@ 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] ); @@ -116110,7 +120061,7 @@ ** */ 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); @@ -116118,13 +120069,23 @@ pItem->pTab = pTab; 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: @@ -116165,7 +120126,7 @@ return 1; } #ifndef SQLITE_OMIT_VIEW - if( !viewOk && pTab->pSelect ){ + if( !viewOk && IsView(pTab) ){ sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName); return 1; } @@ -116269,13 +120230,13 @@ }else{ Index *pPk = sqlite3PrimaryKeyIndex(pTab); if( pPk->nKeyCol==1 ){ - const char *zName = pTab->aCol[pPk->aiColumn[0]].zName; + const char *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 = sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zCnName); pEList = sqlite3ExprListAppend(pParse, pEList, p); } pLhs = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); @@ -116288,9 +120249,16 @@ /* 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); + pSelectSrc = sqlite3SrcListDup(db, pSrc, 0); pSrc->a[0].pTab = pTab; - pSrc->a[0].pIBIndex = 0; + 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, @@ -116357,9 +120325,11 @@ 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 ); @@ -116376,7 +120346,7 @@ */ #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 @@ -116468,6 +120438,7 @@ if( (db->flags & SQLITE_CountRows)!=0 && !pParse->nested && !pParse->pTriggerTab + && !pParse->bReturning ){ memCnt = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt); @@ -116502,11 +120473,14 @@ 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) ){ + sqlite3VdbeChangeP3(v, -1, memCnt ? memCnt : -1); + } } }else #endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */ { - u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK|WHERE_SEEK_TABLE; + u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK; if( sNC.ncFlags & NC_VarSelect ) bComplex = 1; wcf |= (bComplex ? 0 : WHERE_ONEPASS_MULTIROW); if( HasRowid(pTab) ){ @@ -116536,12 +120510,15 @@ ** 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 ); 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 +120553,7 @@ 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 +120567,6 @@ 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 +120596,7 @@ 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 +120663,7 @@ ** 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: @@ -116859,7 +120828,7 @@ ** 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)); @@ -117016,20 +120985,18 @@ 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 +121114,18 @@ 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 @@ -117308,7 +121287,7 @@ } /* -** Implementation of the printf() function. +** Implementation of the printf() (a.k.a. format()) SQL function. */ static void printfFunc( sqlite3_context *context, @@ -117621,9 +121600,9 @@ /* ** 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 +121611,12 @@ ){ 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 +121625,9 @@ ){ 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)); } /* @@ -117744,7 +121723,8 @@ /* 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; } @@ -118076,39 +122056,42 @@ }; /* -** 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){ + /* 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, "%!.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, "%!.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); + int 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,43 +122101,49 @@ 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, "%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; } } } /* +** 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. +*/ +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]); + 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); + } +} + +/* ** The unicode() function. Return the integer unicode code-point value ** for the first character of the input string. */ @@ -118365,10 +122354,10 @@ ){ const unsigned char *zIn; /* Input string */ const unsigned char *zCharSet; /* Set of characters to trim */ - int nIn; /* Number of bytes in input */ + unsigned int nIn; /* Number of bytes in input */ int flags; /* 1: trimleft 2: trimright 3: trim */ int i; /* Loop counter */ - unsigned char *aLen = 0; /* Length of each character in zCharSet */ + unsigned int *aLen = 0; /* Length of each character in zCharSet */ unsigned char **azChar = 0; /* Individual characters in zCharSet */ int nChar; /* Number of characters in zCharSet */ @@ -118377,13 +122366,13 @@ } zIn = sqlite3_value_text(argv[0]); if( zIn==0 ) return; - nIn = sqlite3_value_bytes(argv[0]); + nIn = (unsigned)sqlite3_value_bytes(argv[0]); assert( zIn==sqlite3_value_text(argv[0]) ); if( argc==1 ){ - static const unsigned char lenOne[] = { 1 }; + static const unsigned lenOne[] = { 1 }; static unsigned char * const azOne[] = { (u8*)" " }; nChar = 1; - aLen = (u8*)lenOne; + aLen = (unsigned*)lenOne; azChar = (unsigned char **)azOne; zCharSet = 0; }else if( (zCharSet = sqlite3_value_text(argv[1]))==0 ){ @@ -118394,15 +122383,16 @@ SQLITE_SKIP_UTF8(z); } if( nChar>0 ){ - 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 +122400,7 @@ 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; imxAlloc==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, ','); + } +#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); + } } - if( zSep ) sqlite3_str_append(pAccum, zSep, nSep); +#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 + pGCC = (GroupConcatCtx*)sqlite3_aggregate_context(context, sizeof(*pGCC)); + /* pGCC 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]); - }else{ - n++; - } - if( n>=(int)pAccum->nChar ){ - pAccum->nChar = 0; - }else{ - pAccum->nChar -= n; - memmove(pAccum->zText, &pAccum->zText[n], pAccum->nChar); + if( ALWAYS(pGCC) ){ + int nVS; + /* 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{ + /* If removing single accumulated string, harmlessly over-do. */ + nVS += pGCC->nFirstSepLength; + } + if( nVS>=(int)pGCC->str.nChar ){ + pGCC->str.nChar = 0; + }else{ + 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{ const char *zText = sqlite3_str_value(pAccum); - sqlite3_result_text(context, zText, -1, SQLITE_TRANSIENT); + sqlite3_result_text(context, zText, pAccum->nChar, SQLITE_TRANSIENT); } } } @@ -118915,11 +122975,14 @@ 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 +123006,7 @@ 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 +123018,201 @@ 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); } + +/* +** 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{ + ans = log(x); + switch( SQLITE_PTR_TO_INT(sqlite3_user_data(context)) ){ + case 1: + /* Convert from natural logarithm to log base 10 */ + ans *= 1.0/M_LN10; + break; + case 2: + /* Convert from natural logarithm to log base 2 */ + ans *= 1.0/M_LN2; + break; + default: + 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 ); + 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); +} + /* ** All of the FuncDef structures in the aBuiltinFunc[] array above ** to the global function hash table. This occurs at start-time (as @@ -118974,12 +123233,12 @@ */ static FuncDef aBuiltinFunc[] = { /***** Functions only available with SQLITE_TESTCTRL_INTERNAL_FUNCTIONS *****/ +#if !defined(SQLITE_UNTESTABLE) TEST_FUNC(implies_nonnull_row, 2, INLINEFUNC_implies_nonnull_row, 0), TEST_FUNC(expr_compare, 2, INLINEFUNC_expr_compare, 0), TEST_FUNC(expr_implies_expr, 2, INLINEFUNC_expr_implies_expr, 0), -#ifdef SQLITE_DEBUG - TEST_FUNC(affinity, 1, INLINEFUNC_affinity, 0), -#endif + TEST_FUNC(affinity, 1, INLINEFUNC_affinity, 0), +#endif /* !defined(SQLITE_UNTESTABLE) */ /***** Regular functions *****/ #ifdef SQLITE_SOUNDEX FUNCTION(soundex, 1, 0, 0, soundexFunc ), @@ -119011,15 +123270,17 @@ FUNCTION(min, -1, 0, 1, minmaxFunc ), FUNCTION(min, 0, 0, 1, 0 ), WAGGREGATE(min, 1, 0, 1, minmaxStep, minMaxFinalize, minMaxValue, 0, - SQLITE_FUNC_MINMAX ), + SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ), FUNCTION(max, -1, 1, 1, minmaxFunc ), FUNCTION(max, 0, 1, 1, 0 ), WAGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize, minMaxValue, 0, - SQLITE_FUNC_MINMAX ), + SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ), FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF), + FUNCTION2(subtype, 1, 0, 0, subtypeFunc, SQLITE_FUNC_TYPEOF), FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH), FUNCTION(instr, 2, 0, 0, instrFunc ), FUNCTION(printf, -1, 0, 0, printfFunc ), + FUNCTION(format, -1, 0, 0, printfFunc ), FUNCTION(unicode, 1, 0, 0, unicodeFunc ), FUNCTION(char, -1, 0, 0, charFunc ), FUNCTION(abs, 1, 0, 0, absFunc ), @@ -119045,13 +123306,16 @@ FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ), FUNCTION(substr, 2, 0, 0, substrFunc ), FUNCTION(substr, 3, 0, 0, substrFunc ), + FUNCTION(substring, 2, 0, 0, substrFunc ), + FUNCTION(substring, 3, 0, 0, substrFunc ), WAGGREGATE(sum, 1,0,0, sumStep, sumFinalize, sumFinalize, sumInverse, 0), WAGGREGATE(total, 1,0,0, sumStep,totalFinalize,totalFinalize,sumInverse, 0), WAGGREGATE(avg, 1,0,0, sumStep, avgFinalize, avgFinalize, sumInverse, 0), WAGGREGATE(count, 0,0,0, countStep, - countFinalize, countFinalize, countInverse, SQLITE_FUNC_COUNT ), + countFinalize, countFinalize, countInverse, + SQLITE_FUNC_COUNT|SQLITE_FUNC_ANYORDER ), WAGGREGATE(count, 1,0,0, countStep, - countFinalize, countFinalize, countInverse, 0 ), + countFinalize, countFinalize, countInverse, SQLITE_FUNC_ANYORDER ), WAGGREGATE(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize, groupConcatValue, groupConcatInverse, 0), WAGGREGATE(group_concat, 2, 0, 0, groupConcatStep, @@ -119070,6 +123334,43 @@ #endif FUNCTION(coalesce, 1, 0, 0, 0 ), FUNCTION(coalesce, 0, 0, 0, 0 ), +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS + MFUNCTION(ceil, 1, xCeil, ceilingFunc ), + MFUNCTION(ceiling, 1, xCeil, ceilingFunc ), + MFUNCTION(floor, 1, xFloor, ceilingFunc ), +#if SQLITE_HAVE_C99_MATH_FUNCS + MFUNCTION(trunc, 1, trunc, ceilingFunc ), +#endif + FUNCTION(ln, 1, 0, 0, logFunc ), + FUNCTION(log, 1, 1, 0, logFunc ), + FUNCTION(log10, 1, 1, 0, logFunc ), + FUNCTION(log2, 1, 2, 0, logFunc ), + FUNCTION(log, 2, 0, 0, logFunc ), + MFUNCTION(exp, 1, exp, math1Func ), + MFUNCTION(pow, 2, pow, math2Func ), + MFUNCTION(power, 2, pow, math2Func ), + MFUNCTION(mod, 2, fmod, math2Func ), + MFUNCTION(acos, 1, acos, math1Func ), + MFUNCTION(asin, 1, asin, math1Func ), + MFUNCTION(atan, 1, atan, math1Func ), + MFUNCTION(atan2, 2, atan2, math2Func ), + MFUNCTION(cos, 1, cos, math1Func ), + MFUNCTION(sin, 1, sin, math1Func ), + MFUNCTION(tan, 1, tan, math1Func ), + MFUNCTION(cosh, 1, cosh, math1Func ), + MFUNCTION(sinh, 1, sinh, math1Func ), + MFUNCTION(tanh, 1, tanh, math1Func ), +#if SQLITE_HAVE_C99_MATH_FUNCS + MFUNCTION(acosh, 1, acosh, math1Func ), + MFUNCTION(asinh, 1, asinh, math1Func ), + MFUNCTION(atanh, 1, atanh, math1Func ), +#endif + MFUNCTION(sqrt, 1, sqrt, math1Func ), + MFUNCTION(radians, 1, degToRad, math1Func ), + MFUNCTION(degrees, 1, radToDeg, math1Func ), + FUNCTION(pi, 0, 0, 0, piFunc ), +#endif /* SQLITE_ENABLE_MATH_FUNCTIONS */ + FUNCTION(sign, 1, 0, 0, signFunc ), INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ), INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ), }; @@ -119078,6 +123379,7 @@ #endif sqlite3WindowFunctions(); sqlite3RegisterDateTimeFunctions(); + sqlite3RegisterJsonFunctions(); sqlite3InsertBuiltinFuncs(aBuiltinFunc, ArraySize(aBuiltinFunc)); #if 0 /* Enable to print out how the built-in functions are hashed */ @@ -119089,6 +123391,7 @@ for(p=sqlite3BuiltinFunctions.a[i]; p; p=p->u.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 +123619,9 @@ */ 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 +123663,11 @@ /* 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; @@ -119586,7 +123891,7 @@ 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 +123914,7 @@ ){ Expr *pExpr = sqlite3Expr(db, TK_COLUMN, 0); if( pExpr ){ + assert( ExprUseYTab(pExpr) ); pExpr->y.pTab = pTab; pExpr->iTable = iCursor; pExpr->iColumn = iCol; @@ -119695,7 +124001,7 @@ 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 +124036,7 @@ 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 +124055,7 @@ ** 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); @@ -119801,6 +124107,25 @@ } /* +** 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 ** to this function contains a single entry guaranteed to resolve to @@ -119819,12 +124144,12 @@ */ 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 +124157,7 @@ ** 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 +124246,7 @@ 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; } @@ -119988,13 +124313,14 @@ /* 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 +124387,7 @@ ** 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,7 +124451,7 @@ ** child table as a SrcList for sqlite3WhereBegin() */ pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); if( pSrc ){ - struct SrcList_item *pItem = pSrc->a; + SrcItem *pItem = pSrc->a; pItem->pTab = pFKey->pFrom; pItem->zName = pFKey->pFrom->zName; pItem->pTab->nTabRef++; @@ -120176,10 +124502,10 @@ 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 +124539,9 @@ ** ** 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,23 +124553,24 @@ 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; } } @@ -120249,12 +124578,12 @@ for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ if( fkParentIsModified(pTab, p, aChange, chngRowid) ){ if( p->aAction[1]!=OE_None ) return 2; - eRet = 1; + bHaveFK = 1; } } } } - return eRet; + return bHaveFK ? eRet : 0; } /* @@ -120331,8 +124660,8 @@ 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 +124706,7 @@ testcase( pCol->colFlags & COLFLAG_STORED ); pDflt = 0; }else{ - pDflt = pCol->pDflt; + pDflt = sqlite3ColumnExpr(pFKey->pFrom, pCol); } if( pDflt ){ pNew = sqlite3ExprDup(db, pDflt, 0); @@ -120514,9 +124843,9 @@ 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) ); + 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 ){ @@ -120585,7 +124914,8 @@ ){ 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); @@ -120595,7 +124925,7 @@ }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)); @@ -120662,28 +124992,68 @@ } /* +** 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 appropropriate 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; + 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 = sqlite3VdbeGetOp(v, -1); + 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); @@ -120709,6 +125079,8 @@ if( iReg ){ sqlite3VdbeAddOp4(v, OP_Affinity, iReg, i, 0, zColAff, i); }else{ + assert( sqlite3VdbeGetOp(v, -1)->opcode==OP_MakeRecord + || sqlite3VdbeDb(v)->mallocFailed ); sqlite3VdbeChangeP4(v, -1, zColAff, i); } } @@ -120792,24 +125164,30 @@ ** 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 = sqlite3VdbeGetOp(pParse->pVdbe,-1); + 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 +125223,7 @@ 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 +125232,13 @@ 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 +125288,7 @@ ** Ticket d8dc2b3a58cd5dc2918a1d4acb 2018-05-23 */ if( pSeqTab==0 || !HasRowid(pSeqTab) - || IsVirtual(pSeqTab) + || NEVER(IsVirtual(pSeqTab)) || pSeqTab->nCol!=2 ){ pParse->nErr++; @@ -120922,7 +125300,9 @@ 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; @@ -121217,9 +125597,11 @@ #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 +125635,7 @@ */ #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 @@ -121344,7 +125726,7 @@ } for(i=0; inId; i++){ for(j=0; jnCol; j++){ - if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){ + if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zCnName)==0 ){ pColumn->a[i].idx = j; if( i!=j ) bIdListInOrder = 0; if( j==pTab->iPKey ){ @@ -121354,7 +125736,7 @@ if( pTab->aCol[j].colFlags & (COLFLAG_STORED|COLFLAG_VIRTUAL) ){ sqlite3ErrorMsg(pParse, "cannot INSERT into generated column \"%s\"", - pTab->aCol[j].zName); + pTab->aCol[j].zCnName); goto insert_cleanup; } #endif @@ -121367,7 +125749,7 @@ 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; } @@ -121395,7 +125777,9 @@ dest.nSdst = pTab->nCol; rc = sqlite3Select(pParse, pSelect, &dest); regFromSelect = dest.iSdst; - if( rc || db->mallocFailed || pParse->nErr ) goto insert_cleanup; + 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 ); @@ -121480,19 +125864,24 @@ } } #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 +125893,7 @@ if( (db->flags & SQLITE_CountRows)!=0 && !pParse->nested && !pParse->pTriggerTab + && !pParse->bReturning ){ regRowCount = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); @@ -121527,12 +125917,13 @@ } #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 +125931,19 @@ 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) ){ + goto insert_cleanup; + } + } + pNx = pNx->pNextUpsert; + }while( pNx!=0 ); } #endif @@ -121625,7 +126022,9 @@ }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); + sqlite3ExprCodeFactorable(pParse, + sqlite3ColumnExpr(pTab, &pTab->aCol[i]), + iRegStore); continue; } } @@ -121634,13 +126033,17 @@ if( j>=pColumn->nId ){ /* 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; }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; @@ -121687,11 +126090,6 @@ 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 ); sqlite3VdbeAddOp3(v, OP_Copy, regRowid+1, regCols+1, pTab->nNVCol-1); @@ -121790,7 +126188,7 @@ }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 @@ -121810,6 +126208,13 @@ 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 +126251,9 @@ 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 +126268,7 @@ ** 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: @@ -121952,6 +126357,70 @@ } /* +** 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. ** @@ -122059,7 +126528,7 @@ ){ Vdbe *v; /* VDBE under constrution */ 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 +126536,11 @@ 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 +126550,13 @@ 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 +126607,7 @@ } 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 +126629,8 @@ 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 +126640,7 @@ case OE_Rollback: case OE_Fail: { char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName, - pCol->zName); + pCol->zCnName); sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL, onError, iReg); sqlite3VdbeAppendP4(v, zMsg, P4_DYNAMIC); @@ -122238,7 +126709,7 @@ 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, @@ -122278,19 +126749,63 @@ ** 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 +126868,20 @@ } /* 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 +126891,9 @@ ** 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 +126989,9 @@ } } 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,7 +127004,10 @@ ** 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++){ + for(pIdx = indexIteratorFirst(&sIdxIter, &ix); + pIdx; + pIdx = indexIteratorNext(&sIdxIter, &ix) + ){ int regIdx; /* Range of registers hold conent for pIdx */ int regR; /* Range of registers holding conflicting PK */ int iThisCur; /* Cursor for this UNIQUE index */ @@ -122485,15 +127015,14 @@ 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 +127059,7 @@ 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 +127093,8 @@ } /* 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 +127111,7 @@ ** 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 +127119,7 @@ && ( 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 +127133,7 @@ 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,7 +127154,7 @@ 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 ){ @@ -122688,7 +127218,8 @@ 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 +127286,23 @@ 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,7 +127355,7 @@ 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); @@ -122828,6 +127363,32 @@ #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. ** A consecutive range of registers starting at regNewData contains the @@ -122857,9 +127418,9 @@ || 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 +127433,11 @@ } 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, @@ -122953,12 +127505,13 @@ assert( op==OP_OpenWrite || p5==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++; @@ -123083,7 +127636,7 @@ 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 */ @@ -123169,13 +127722,8 @@ 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 +127731,9 @@ 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 +127770,9 @@ ** 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 +127782,8 @@ 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 +127791,15 @@ } /* 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 */ } @@ -123278,7 +127836,8 @@ ** 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 +127859,7 @@ 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 +127895,13 @@ 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 +127909,28 @@ 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 +127972,22 @@ 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); @@ -123930,6 +128513,22 @@ int,const char**); void (*free_filename)(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**); }; /* @@ -124234,6 +128833,20 @@ #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 #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) @@ -124716,6 +129329,20 @@ 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, + sqlite3_vtab_rhs_value, + sqlite3_vtab_distinct, + sqlite3_vtab_in, + sqlite3_vtab_in_first, + sqlite3_vtab_in_next }; /* True if x is the directory separator character @@ -124751,7 +129378,7 @@ const char *zEntry; char *zAltEntry = 0; void **aHandle; - u64 nMsg = 300 + sqlite3Strlen30(zFile); + u64 nMsg = strlen(zFile); int ii; int rc; @@ -124785,6 +129412,12 @@ 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 */ + if( nMsg>SQLITE_MAX_PATHLEN ) 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 */ @@ -125168,13 +129805,14 @@ #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 +129845,51 @@ /* 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", - /* 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 */ + /* 15 */ "schema", /* Used by: table_list */ + /* 16 */ "name", + /* 17 */ "type", + /* 18 */ "ncol", + /* 19 */ "wr", + /* 20 */ "strict", + /* 21 */ "seqno", /* Used by: index_xinfo */ + /* 22 */ "cid", + /* 23 */ "name", + /* 24 */ "desc", + /* 25 */ "coll", + /* 26 */ "key", + /* 27 */ "name", /* Used by: function_list */ + /* 28 */ "builtin", + /* 29 */ "type", + /* 30 */ "enc", + /* 31 */ "narg", + /* 32 */ "flags", + /* 33 */ "tbl", /* Used by: stats */ + /* 34 */ "idx", + /* 35 */ "wdth", + /* 36 */ "hght", + /* 37 */ "flgs", + /* 38 */ "seq", /* Used by: index_list */ + /* 39 */ "name", + /* 40 */ "unique", + /* 41 */ "origin", + /* 42 */ "partial", + /* 43 */ "table", /* Used by: foreign_key_check */ + /* 44 */ "rowid", + /* 45 */ "parent", + /* 46 */ "fkid", + /* index_info reuses 21 */ + /* 47 */ "seq", /* Used by: database_list */ + /* 48 */ "name", + /* 49 */ "file", + /* 50 */ "busy", /* Used by: wal_checkpoint */ + /* 51 */ "log", + /* 52 */ "checkpointed", + /* collation_list reuses 38 */ + /* 53 */ "database", /* Used by: lock_status */ + /* 54 */ "status", + /* 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 +129940,7 @@ {/* 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 +129979,7 @@ {/* zName: */ "collation_list", /* ePragTyp: */ PragTyp_COLLATION_LIST, /* ePragFlg: */ PragFlg_Result0, - /* ColNames: */ 32, 2, + /* ColNames: */ 38, 2, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS) @@ -125370,14 +130014,14 @@ {/* zName: */ "database_list", /* ePragTyp: */ PragTyp_DATABASE_LIST, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0, - /* ColNames: */ 41, 3, + /* ColNames: */ 47, 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 +130051,7 @@ {/* 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 +130094,7 @@ {/* zName: */ "function_list", /* ePragTyp: */ PragTyp_FUNCTION_LIST, /* ePragFlg: */ PragFlg_Result0, - /* ColNames: */ 21, 6, + /* ColNames: */ 27, 6, /* iArg: */ 0 }, #endif #endif @@ -125479,23 +130123,23 @@ {/* zName: */ "index_info", /* ePragTyp: */ PragTyp_INDEX_INFO, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 15, 3, + /* ColNames: */ 21, 3, /* iArg: */ 0 }, {/* zName: */ "index_list", /* ePragTyp: */ PragTyp_INDEX_LIST, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 32, 5, + /* ColNames: */ 38, 5, /* iArg: */ 0 }, {/* zName: */ "index_xinfo", /* ePragTyp: */ PragTyp_INDEX_INFO, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 15, 6, + /* ColNames: */ 21, 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 +130173,7 @@ {/* 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 +130247,7 @@ #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 +130312,7 @@ {/* zName: */ "stats", /* ePragTyp: */ PragTyp_STATS, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq, - /* ColNames: */ 27, 5, + /* ColNames: */ 33, 5, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) @@ -125684,6 +130328,11 @@ /* 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: */ 15, 6, + /* iArg: */ 0 }, {/* zName: */ "table_xinfo", /* ePragTyp: */ PragTyp_TABLE_INFO, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, @@ -125759,7 +130408,7 @@ {/* zName: */ "wal_checkpoint", /* ePragTyp: */ PragTyp_WAL_CHECKPOINT, /* ePragFlg: */ PragFlg_NeedSchema, - /* ColNames: */ 44, 3, + /* ColNames: */ 50, 3, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) @@ -125770,7 +130419,7 @@ /* 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 *********************/ @@ -125876,7 +130525,9 @@ 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; @@ -126210,7 +130861,11 @@ /* 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 ){ @@ -126860,6 +131515,14 @@ }else{ db->flags &= ~mask; if( mask==SQLITE_DeferFKs ) db->nDeferredImmCons = 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 +131563,7 @@ 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 +131584,16 @@ }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 +131601,85 @@ } 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(db, zSql, -1, &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 +131689,7 @@ 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 +131739,7 @@ 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 +131808,13 @@ 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 +131848,8 @@ 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 +131862,7 @@ 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 */ @@ -127160,7 +131908,7 @@ 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); @@ -127168,7 +131916,8 @@ if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = 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 +131939,14 @@ 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 +131954,7 @@ ** 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. */ + if( regRow+pFK->nCol>pParse->nMem ) pParse->nMem = regRow+pFK->nCol; for(j=0; jnCol; j++){ int iCol = aiCols ? aiCols[j] : pFK->aCol[j].iFrom; sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, iCol, regRow+j); @@ -127221,7 +131972,7 @@ 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. */ @@ -127390,8 +132141,9 @@ int loopTop; int iDataCur, iIdxCur; int r1 = -1; + int bStrict; - if( pTab->tnum<1 ) continue; /* Skip VIEWs or VIRTUAL TABLEs */ + if( !IsOrdinaryTable(pTab) ) continue; if( pObjTab && pObjTab!=pTab ) continue; pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0, @@ -127411,23 +132163,48 @@ /* Sanity check on record header decoding */ sqlite3VdbeAddOp3(v, OP_Column, iDataCur, pTab->nNVCol-1,3); sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG); + VdbeComment((v, "(right-most column)")); } - /* Verify that all NOT NULL columns really are NOT NULL */ + /* Verify that all NOT NULL columns really are NOT NULL. At the + ** same time verify the type of the content of STRICT tables */ + bStrict = (pTab->tabFlags & TF_Strict)!=0; for(j=0; jnCol; j++){ char *zErr; - int jmp2; + Column *pCol = pTab->aCol + j; + int doError, jmp2; if( j==pTab->iPKey ) continue; - if( pTab->aCol[j].notNull==0 ) continue; + if( pCol->notNull==0 && !bStrict ) continue; + doError = bStrict ? sqlite3VdbeMakeLabel(pParse) : 0; 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); - integrityCheckResultRow(v); - sqlite3VdbeJumpHere(v, jmp2); + if( pCol->notNull ){ + jmp2 = sqlite3VdbeAddOp1(v, OP_NotNull, 3); 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( bStrict && pCol->eCType!=COLTYPE_ANY ){ + sqlite3VdbeGoto(v, doError); + }else{ + integrityCheckResultRow(v); + } + sqlite3VdbeJumpHere(v, jmp2); + } + if( (pTab->tabFlags & TF_Strict)!=0 + && pCol->eCType!=COLTYPE_ANY + ){ + jmp2 = sqlite3VdbeAddOp3(v, OP_IsNullOrType, 3, 0, + sqlite3StdTypeMap[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); + sqlite3VdbeResolveLabel(v, doError); + integrityCheckResultRow(v); + sqlite3VdbeJumpHere(v, jmp2); + } } /* Verify CHECK constraints */ if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){ @@ -127712,7 +132489,7 @@ ** 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 ){ @@ -127961,12 +132738,12 @@ case PragTyp_ANALYSIS_LIMIT: { sqlite3_int64 N; if( zRight - && sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK + && sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK /* IMP: R-40975-20399 */ && N>=0 ){ db->nAnalysisLimit = (int)(N&0x7fffffff); } - returnSingleInt(v, db->nAnalysisLimit); + returnSingleInt(v, db->nAnalysisLimit); /* IMP: R-57594-65522 */ break; } @@ -128360,7 +133137,7 @@ */ 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 +133145,23 @@ 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 +133217,28 @@ 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 +133251,11 @@ || (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 +133270,14 @@ 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 +133288,7 @@ 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 +133296,7 @@ || sqlite3IndexHasDuplicateRootPage(pIndex) ){ if( sqlite3Config.bExtraSchemaChecks ){ - corruptSchema(pData, argv[1], "invalid rootpage"); + corruptSchema(pData, argv, "invalid rootpage"); } } } @@ -128579,7 +133373,7 @@ ** 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 +133499,22 @@ } #endif } + assert( pDb == &(db->aDb[iDb]) ); if( db->mallocFailed ){ rc = SQLITE_NOMEM_BKPT; sqlite3ResetAllSchemasOfConnection(db); - } + pDb = &db->aDb[iDb]; + }else 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. + /* 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 +133620,11 @@ /* 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; @@ -128883,36 +133682,104 @@ } /* -** Deallocate a single AggInfo object -*/ -static void agginfoFree(sqlite3 *db, AggInfo *p){ - sqlite3DbFree(db, p->aCol); - sqlite3DbFree(db, p->aFunc); - sqlite3DbFree(db, p); -} - -/* ** Free all memory allocations in the pParse object */ -SQLITE_PRIVATE void sqlite3ParserReset(Parse *pParse){ +SQLITE_PRIVATE void sqlite3ParseObjectReset(Parse *pParse){ sqlite3 *db = pParse->db; - AggInfo *pThis = pParse->pAggList; - while( pThis ){ - AggInfo *pNext = pThis->pNext; - agginfoFree(db, pThis); - pThis = pNext; + assert( db!=0 ); + assert( db->pParse==pParse ); + assert( pParse->nested==0 ); +#ifndef SQLITE_OMIT_SHARED_CACHE + sqlite3DbFree(db, pParse->aTableLock); +#endif + while( pParse->pCleanup ){ + ParseCleanup *pCleanup = pParse->pCleanup; + pParse->pCleanup = pCleanup->pNext; + pCleanup->xCleanup(db, pCleanup->pPtr); + sqlite3DbFreeNN(db, pCleanup); } 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; + 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; + pParse->db = 0; pParse->disableLookaside = 0; } /* +** 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 mechansim (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 = 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 + } + 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"); +} + +/* ** Compile the UTF-8 encoded SQL statement zSql into a statement handle. */ static int sqlite3Prepare( @@ -128924,16 +133791,19 @@ 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.pOuterParse = db->pParse; + db->pParse = &sParse; + sParse.db = db; sParse.pReprepare = pReprepare; assert( ppStmt && *ppStmt==0 ); - /* assert( !db->mallocFailed ); // not true with SQLITE_USE_ALLOCA */ + if( db->mallocFailed ) sqlite3ErrorMsg(&sParse, "out of memory"); assert( sqlite3_mutex_held(db->mutex) ); /* For a long-term use prepared statement avoid the use of @@ -128986,7 +133856,6 @@ sqlite3VtabUnlockList(db); - sParse.db = db; if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){ char *zSqlCopy; int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; @@ -128999,23 +133868,17 @@ } 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 +133888,30 @@ } 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 +133922,7 @@ end_prepare: - sqlite3ParserReset(&sParse); + sqlite3ParseObjectReset(&sParse); return rc; } static int sqlite3LockAndPrepare( @@ -129080,11 +133952,13 @@ ** reset is considered a permanent error. */ rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail); assert( rc==SQLITE_OK || *ppStmt==0 ); + if( rc==SQLITE_OK || db->mallocFailed ) break; }while( rc==SQLITE_ERROR_RETRY || (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt++)==0) ); sqlite3BtreeLeaveAll(db); rc = sqlite3ApiExit(db, rc); assert( (rc&db->errMask)==rc ); + db->busyHandler.nBusy = 0; sqlite3_mutex_leave(db->mutex); return rc; } @@ -129384,12 +134258,16 @@ 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); p = pPrior; bFree = 1; @@ -129561,12 +134439,12 @@ ** 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; + if( pCol->hName==h && sqlite3StrICmp(pCol->zCnName, zCol)==0 ) return i; } return -1; } @@ -129593,7 +134471,7 @@ assert( (piTab==0)==(piCol==0) ); /* Both or neither are NULL */ for(i=0; ia[i].pTab, zCol); + iCol = sqlite3ColumnIndex(pSrc->a[i].pTab, zCol); if( iCol>=0 && (bIgnoreHidden==0 || IsHiddenColumn(&pSrc->a[i].pTab->aCol[iCol])==0) ){ @@ -129642,18 +134520,21 @@ pE2 = sqlite3CreateColumnExpr(db, pSrc, iRight, iColRight); pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2); + assert( pE2!=0 || pEq==0 ); /* Due to db->mallocFailed test + ** in sqlite3DbMallocRawNN() called from + ** sqlite3PExpr(). */ if( pEq && isOuterJoin ){ ExprSetProperty(pEq, EP_FromJoin); assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) ); ExprSetVVAProperty(pEq, EP_NoReduce); - pEq->iRightJoinTable = (i16)pE2->iTable; + pEq->w.iRightJoinTable = 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 +** And set the Expr.w.iRightJoinTable to iTable for every term in the ** expression. ** ** The EP_FromJoin property is used on terms of an expression to tell @@ -129663,8 +134544,8 @@ ** 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.iRightJoinTable tells the WHERE clause processing that the +** expression depends on table w.iRightJoinTable even if that table is not ** explicitly mentioned in the expression. That information is needed ** for cases like this: ** @@ -129682,11 +134563,14 @@ ExprSetProperty(p, EP_FromJoin); 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.iRightJoinTable = 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); + } } } sqlite3SetJoinExpr(p->pLeft, iTable); @@ -129695,7 +134579,7 @@ } /* Undo the work of sqlite3SetJoinExpr(). In the expression p, convert every -** term that is marked with EP_FromJoin and iRightJoinTable==iTable into +** term that is marked with EP_FromJoin and w.iRightJoinTable==iTable into ** an ordinary term that omits the EP_FromJoin mark. ** ** This happens when a LEFT JOIN is simplified into an ordinary JOIN. @@ -129703,13 +134587,19 @@ static void unsetJoinExpr(Expr *p, int iTable){ while( p ){ if( ExprHasProperty(p, EP_FromJoin) - && (iTable<0 || p->iRightJoinTable==iTable) ){ + && (iTable<0 || p->w.iRightJoinTable==iTable) ){ ExprClearProperty(p, EP_FromJoin); } - 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 ){ + ExprClearProperty(p, EP_CanBeNull); + } + if( p->op==TK_FUNCTION ){ + assert( ExprUseXList(p) ); + if( p->x.pList ){ + int i; + for(i=0; ix.pList->nExpr; i++){ + unsetJoinExpr(p->x.pList->a[i].pExpr, iTable); + } } } unsetJoinExpr(p->pLeft, iTable); @@ -129734,8 +134624,8 @@ static int sqliteProcessJoin(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]; @@ -129762,7 +134652,7 @@ int iLeftCol; /* Matching column in the left table */ if( IsHiddenColumn(&pRightTab->aCol[j]) ) continue; - zName = pRightTab->aCol[j].zName; + zName = pRightTab->aCol[j].zCnName; if( tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol, 1) ){ addWhereTerm(pParse, pSrc, iLeft, iLeftCol, i+1, j, isOuter, &p->pWhere); @@ -129803,7 +134693,7 @@ int iRightCol; /* Column number of matching column on the right */ zName = pList->a[j].zName; - iRightCol = columnIndex(pRightTab, zName); + iRightCol = sqlite3ColumnIndex(pRightTab, zName); if( iRightCol<0 || !tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol, 0) ){ @@ -130033,31 +134923,157 @@ } /* -** 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. +** 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 ephermal +** 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 void codeDistinct( +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 @@ -130098,9 +135114,13 @@ 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); @@ -130305,59 +135326,11 @@ ** 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); } @@ -130604,7 +135577,7 @@ p->nRef = 1; memset(&p[1], 0, nExtra); }else{ - sqlite3OomFault(db); + return (KeyInfo*)sqlite3OomFault(db); } return p; } @@ -130682,7 +135655,7 @@ /* ** 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; @@ -130775,6 +135748,9 @@ 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{ @@ -131017,13 +135993,19 @@ 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 +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + && iCol>=0 +#else + && ALWAYS(iCol>=0) +#endif + ){ /* 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 +136027,7 @@ 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 +136053,11 @@ ** 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 +136149,7 @@ ** 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 */ ){ @@ -131202,7 +136186,8 @@ 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 */ + assert( p->op!=TK_COLUMN + || (ExprUseYTab(p) && p->y.pTab!=0) ); /* Covering idx not yet coded */ if( pEList->a[i].zEName && pEList->a[i].eEName==ENAME_NAME ){ /* An AS clause always takes first priority */ char *zName = pEList->a[i].zEName; @@ -131217,7 +136202,7 @@ if( iCol<0 ){ zCol = "rowid"; }else{ - zCol = pTab->aCol[iCol].zName; + zCol = pTab->aCol[iCol].zCnName; } if( fullName ){ char *zName = 0; @@ -131255,7 +136240,7 @@ ** 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 +136256,14 @@ 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; @@ -131293,17 +136279,18 @@ /* If the column contains an "AS " phrase, use as the name */ }else{ Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pEList->a[i].pExpr); - while( pColExpr->op==TK_DOT ){ + 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) ) + && (pTab = pColExpr->y.pTab)!=0 + ){ /* For columns use the column name name */ int iCol = pColExpr->iColumn; - Table *pTab = pColExpr->y.pTab; - assert( pTab!=0 ); 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; @@ -131331,7 +136318,7 @@ zName = sqlite3MPrintf(db, "%.*z:%u", nName, zName, ++cnt); if( cnt>3 ) sqlite3_randomness(sizeof(cnt), &cnt); } - pCol->zName = zName; + pCol->zCnName = zName; pCol->hName = sqlite3StrIHash(zName); sqlite3ColumnPropertiesFromName(0, pCol); if( zName && sqlite3HashInsert(&ht, zName, pCol)==pCol ){ @@ -131341,7 +136328,7 @@ sqlite3HashClear(&ht); if( db->mallocFailed ){ for(j=0; jpEList->a; for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){ const char *zType; - int n, m; + i64 n, m; + 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); 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); + n = sqlite3Strlen30(pCol->zCnName); + pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+m+2); + if( pCol->zCnName ){ + memcpy(&pCol->zCnName[n+1], zType, m+1); pCol->colFlags |= COLFLAG_HASTYPE; + }else{ + testcase( pCol->colFlags & COLFLAG_HASTYPE ); + pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL); } } 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 */ @@ -131566,7 +136558,7 @@ */ 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 ){ @@ -131638,7 +136630,8 @@ 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 */ @@ -131714,7 +136707,24 @@ /* 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 +136757,11 @@ /* 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); @@ -131825,6 +136831,16 @@ } /* +** 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 ** INTERSECT @@ -131876,12 +136892,8 @@ 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 +136921,7 @@ 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 +136944,14 @@ 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; + SELECTTRACE(1, 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 +136967,14 @@ } } ExplainQueryPlan((pParse, 1, "UNION ALL")); + SELECTTRACE(1, 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) && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit) ){ p->nSelectRow = sqlite3LogEst((u64)nLimit); @@ -132001,10 +137015,12 @@ assert( p->pEList ); } + /* Code the SELECT statements to our left */ assert( !pPrior->pOrderBy ); sqlite3SelectDestInit(&uniondest, priorOp, unionTab); + SELECTTRACE(1, pParse, p, ("multiSelect EXCEPT/UNION left...\n")); rc = sqlite3Select(pParse, pPrior, &uniondest); if( rc ){ goto multi_select_end; @@ -132023,7 +137039,8 @@ p->pLimit = 0; uniondest.eDest = op; ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE", - selectOpName(p->op))); + sqlite3SelectOpName(p->op))); + SELECTTRACE(1, pParse, p, ("multiSelect EXCEPT/UNION right...\n")); rc = sqlite3Select(pParse, p, &uniondest); testcase( rc!=SQLITE_OK ); assert( p->pOrderBy==0 ); @@ -132084,6 +137101,7 @@ /* Code the SELECTs to our left into temporary table "tab1". */ sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1); + SELECTTRACE(1, pParse, p, ("multiSelect INTERSECT left...\n")); rc = sqlite3Select(pParse, pPrior, &intersectdest); if( rc ){ goto multi_select_end; @@ -132099,7 +137117,8 @@ p->pLimit = 0; intersectdest.iSDParm = tab2; ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE", - selectOpName(p->op))); + sqlite3SelectOpName(p->op))); + SELECTTRACE(1, pParse, p, ("multiSelect INTERSECT right...\n")); rc = sqlite3Select(pParse, p, &intersectdest); testcase( rc!=SQLITE_OK ); pDelete = p->pPrior; @@ -132160,6 +137179,7 @@ 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 +137214,11 @@ multi_select_end: pDest->iSdst = dest.iSdst; pDest->nSdst = dest.nSdst; - sqlite3SelectDelete(db, pDelete); + if( pDelete ){ + sqlite3ParserAddCleanup(pParse, + (void(*)(sqlite3*,void*))sqlite3SelectDelete, + pDelete); + } return rc; } #endif /* SQLITE_OMIT_COMPOUND_SELECT */ @@ -132208,7 +137232,8 @@ 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)); } } @@ -132305,10 +137330,8 @@ ** 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; } @@ -132449,6 +137472,8 @@ ){ 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 */ @@ -132494,8 +137519,7 @@ /* 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 +137532,7 @@ 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 +137559,7 @@ 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 +137569,6 @@ 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 +137593,30 @@ /* 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 +137639,7 @@ 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. @@ -132728,11 +137767,13 @@ /* Reassembly the compound query so that it will be freed correctly ** by the calling function */ - if( p->pPrior ){ - sqlite3SelectDelete(db, p->pPrior); + if( pSplit->pPrior ){ + sqlite3SelectDelete(db, 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 ****/ @@ -132780,17 +137821,20 @@ ){ if( pExpr==0 ) return 0; if( ExprHasProperty(pExpr, EP_FromJoin) - && pExpr->iRightJoinTable==pSubst->iTable + && pExpr->w.iRightJoinTable==pSubst->iTable ){ - pExpr->iRightJoinTable = pSubst->iNewTable; + pExpr->w.iRightJoinTable = 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; Expr ifNullRow; @@ -132805,32 +137849,33 @@ ifNullRow.op = TK_IF_NULL_ROW; ifNullRow.pLeft = pCopy; ifNullRow.iTable = pSubst->iNewTable; - ifNullRow.flags = EP_Skip; + 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->isLeftJoin ){ ExprSetProperty(pNew, EP_CanBeNull); } - if( pNew && ExprHasProperty(pExpr,EP_FromJoin) ){ - pNew->iRightJoinTable = pExpr->iRightJoinTable; - ExprSetProperty(pNew, EP_FromJoin); + if( ExprHasProperty(pExpr,EP_FromJoin) ){ + sqlite3SetJoinExpr(pNew, pExpr->w.iRightJoinTable); } 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, - (pColl ? pColl->zName : "BINARY") - ); - } - ExprClearProperty(pExpr, EP_Collate); + if( pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE ){ + CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse, pExpr); + pExpr = sqlite3ExprAddCollateString(pSubst->pParse, pExpr, + (pColl ? pColl->zName : "BINARY") + ); } + ExprClearProperty(pExpr, EP_Collate); } } }else{ @@ -132839,7 +137884,7 @@ } 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 +137916,7 @@ int doPrior /* Do substitutes on p->pPrior too */ ){ SrcList *pSrc; - struct SrcList_item *pItem; + SrcItem *pItem; int i; if( !p ) return; do{ @@ -132901,7 +137946,7 @@ ** 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,7 +137956,7 @@ } 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; @@ -132926,6 +137971,103 @@ #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]; + for(p=pItem->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_FromJoin) ){ + renumberCursorDoMapping(pWalker, &pExpr->w.iRightJoinTable); + } + 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 !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) +/* ** This routine attempts to flatten subqueries as a performance optimization. ** This routine returns 1 if it makes changes and 0 if no flattening occurs. ** @@ -133018,9 +138160,9 @@ ** (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. ** ** The parent and sub-query may contain WHERE clauses. Subject to ** rules (11), (13) and (14), they may also contain ORDER BY, @@ -133036,8 +138178,8 @@ ** 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 +138195,8 @@ ** ** (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(). ** @@ -133097,9 +138238,10 @@ int isLeftJoin = 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. */ @@ -133195,13 +138337,14 @@ 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 || isLeftJoin>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) */ @@ -133222,15 +138365,16 @@ 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; + + if( pSrc->nSrc>1 ){ + if( pParse->nSelect>500 ) 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", @@ -133242,6 +138386,17 @@ testcase( i==SQLITE_DENY ); pParse->zAuthContext = zSavedAuthContext; + /* Delete the transient structures associated with thesubquery */ + pSub1 = pSubitem->pSelect; + sqlite3DbFree(db, pSubitem->zDatabase); + sqlite3DbFree(db, pSubitem->zName); + sqlite3DbFree(db, pSubitem->zAlias); + pSubitem->zDatabase = 0; + pSubitem->zName = 0; + pSubitem->zAlias = 0; + pSubitem->pSelect = 0; + assert( pSubitem->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,18 +138435,23 @@ ExprList *pOrderBy = p->pOrderBy; Expr *pLimit = p->pLimit; Select *pPrior = p->pPrior; + Table *pItemTab = pSubitem->pTab; + pSubitem->pTab = 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->pTab = 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; @@ -133299,24 +138459,13 @@ SELECTTRACE(2,pParse,p,("compound-subquery flattener" " creates %u as peer\n",pNew->selId)); } - if( db->mallocFailed ) return 1; + assert( pSubitem->pSelect==0 ); + } + sqlite3DbFree(db, aCsrMap); + if( db->mallocFailed ){ + pSubitem->pSelect = pSub1; + 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 @@ -133329,8 +138478,10 @@ Table *pTabToDel = pSubitem->pTab; if( pTabToDel->nTabRef==1 ){ Parse *pToplevel = sqlite3ParseToplevel(pParse); - pTabToDel->pNextZombie = pToplevel->pZombieTab; - pToplevel->pZombieTab = pTabToDel; + sqlite3ParserAddCleanup(pToplevel, + (void(*)(sqlite3*,void*))sqlite3DeleteTable, + pTabToDel); + testcase( pToplevel->earlyCleanup ); }else{ pTabToDel->nTabRef--; } @@ -133350,6 +138501,7 @@ ** 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; @@ -133358,14 +138510,8 @@ 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 @@ -133485,7 +138631,7 @@ sqlite3SelectDelete(db, pSub1); #if SELECTTRACE_ENABLED - if( sqlite3_unsupported_selecttrace & 0x100 ){ + if( sqlite3SelectTrace & 0x100 ){ SELECTTRACE(0x100,pParse,p,("After flattening:\n")); sqlite3TreeViewSelect(0, p, 0); } @@ -133502,8 +138648,10 @@ 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 */ Expr **apExpr; /* [i*2] is COLUMN and [i*2+1] is VALUE */ }; @@ -133542,6 +138690,9 @@ return; /* Already present. Return without doing anything. */ } } + if( sqlite3ExprAffinity(pColumn)==SQLITE_AFF_BLOB ){ + pConst->bHasAffBlob = 1; + } pConst->nConst++; pConst->apExpr = sqlite3DbReallocOrFree(pConst->pParse->db, pConst->apExpr, @@ -133562,7 +138713,7 @@ */ static void findConstInWhere(WhereConst *pConst, Expr *pExpr){ Expr *pRight, *pLeft; - if( pExpr==0 ) return; + if( NEVER(pExpr==0) ) return; if( ExprHasProperty(pExpr, EP_FromJoin) ) return; if( pExpr->op==TK_AND ){ findConstInWhere(pConst, pExpr->pRight); @@ -133583,38 +138734,84 @@ } /* -** 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) ){ testcase( ExprHasProperty(pExpr, EP_FixedCol) ); testcase( ExprHasProperty(pExpr, EP_FromJoin) ); 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; + if( bIgnoreAffBlob && sqlite3ExprAffinity(pColumn)==SQLITE_AFF_BLOB ){ + break; + } /* A match is found. Add the EP_FixedCol property */ pConst->nChng++; 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. ** ** If the WHERE clause contains terms of the form COLUMN=CONSTANT or @@ -133649,6 +138846,21 @@ ** 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 +138870,12 @@ 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; findConstInWhere(&x, p->pWhere); if( x.nConst ){ memset(&w, 0, sizeof(w)); @@ -133680,6 +138894,35 @@ } #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 clase. +*/ +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 ** the WHERE clause of subquery. Example: @@ -133726,9 +138969,24 @@ ** 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.) ** ** Return 0 if no changes are made and non-zero if one or more WHERE clause ** terms are duplicated into the subquery. @@ -133742,13 +139000,17 @@ ){ Expr *pNew; int nChng = 0; - Select *pSel; if( pWhere==0 ) return 0; - if( pSubq->selFlags & SF_Recursive ) return 0; /* restriction (2) */ + if( pSubq->selFlags & (SF_Recursive|SF_MultiPart) ) return 0; #ifndef SQLITE_OMIT_WINDOWFUNC - for(pSel=pSubq; pSel; pSel=pSel->pPrior){ - if( pSel->pWin ) return 0; /* restriction (6) */ + if( pSubq->pPrior ){ + Select *pSel; + for(pSel=pSubq; pSel; pSel=pSel->pPrior){ + if( pSel->pWin ) return 0; /* restriction (6b) */ + } + }else{ + if( pSubq->pWin && pSubq->pWin->pPartition==0 ) return 0; } #endif @@ -133775,15 +139037,18 @@ } if( isLeftJoin && (ExprHasProperty(pWhere,EP_FromJoin)==0 - || pWhere->iRightJoinTable!=iCursor) + || pWhere->w.iRightJoinTable!=iCursor) ){ return 0; /* restriction (4) */ } - if( ExprHasProperty(pWhere,EP_FromJoin) && pWhere->iRightJoinTable!=iCursor ){ + if( ExprHasProperty(pWhere,EP_FromJoin) + && pWhere->w.iRightJoinTable!=iCursor + ){ return 0; /* restriction (5) */ } if( sqlite3ExprIsTableConstant(pWhere, iCursor) ){ nChng++; + pSubq->selFlags |= SF_PushDown; while( pSubq ){ SubstContext x; pNew = sqlite3ExprDup(pParse->db, pWhere, 0); @@ -133794,6 +139059,14 @@ x.isLeftJoin = 0; x.pEList = pSubq->pEList; 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{ @@ -133824,7 +139097,7 @@ */ 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 +139105,16 @@ 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; @@ -133862,7 +139142,13 @@ ** ** 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 +139156,26 @@ 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].pSelect + || pAggInfo->nFunc!=1 ){ return 0; } pTab = p->pSrc->a[0].pTab; + 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 +139188,27 @@ ** 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->pTab; + 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. @@ -133999,7 +139295,7 @@ ** 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 +139316,22 @@ */ 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->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 +139341,92 @@ ** ** 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, + (void(*)(sqlite3*,void*))sqlite3WithDelete, + 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. -** -** 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. -** -** 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. -*/ -static int withExpand( - Walker *pWalker, - struct SrcList_item *pFrom +** 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 matches a CTE according to either of these two above, pFrom->pTab +** and other fields are populated accordingly. +** +** Return 0 if no match is found. +** Return 1 if a match is found. +** Return 2 if an error condition is detected. +*/ +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 ); + 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; + } + if( pFrom->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 +139434,98 @@ ** 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; + 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->pTab = 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; + if( db->mallocFailed ) return 2; + pFrom->pSelect->selFlags |= SF_CopyCte; assert( pFrom->pSelect ); + if( pFrom->fg.isIndexedBy ){ + sqlite3ErrorMsg(pParse, "no such index: \"%s\"", pFrom->u1.zIndexedBy); + return 2; + } + pFrom->fg.isCte = 1; + pFrom->u2.pCteUse = pCteUse; + pCteUse->nUse++; + if( pCteUse->nUse>=2 && pCteUse->eM10d==M10d_Any ){ + pCteUse->eM10d = M10d_Yes; + } /* Check if this is a recursive CTE. */ - pSel = pFrom->pSelect; + pRecTerm = pSel = pFrom->pSelect; 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]; + SrcItem *pItem = &pSrc->a[i]; if( pItem->zDatabase==0 && pItem->zName!=0 && 0==sqlite3StrICmp(pItem->zName, pCte->zName) - ){ + ){ pItem->pTab = pTab; - pItem->fg.isRecursive = 1; 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 +139537,7 @@ pCte->zName, pEList->nExpr, pCte->pCols->nExpr ); pParse->pWith = pSavedWith; - return SQLITE_ERROR; + return 2; } pEList = pCte->pCols; } @@ -134187,9 +139553,9 @@ } pCte->zCteErr = 0; pParse->pWith = pSavedWith; + return 1; /* Success */ } - - return SQLITE_OK; + return 0; /* No match */ } #endif @@ -134202,7 +139568,7 @@ ** 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,8 +139578,6 @@ } } } -#else -#define selectPopWith 0 #endif /* @@ -134223,7 +139587,7 @@ ** SQLITE_OK is returned. Otherwise, if an OOM error is encountered, ** SQLITE_NOMEM. */ -SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, struct SrcList_item *pFrom){ +SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, SrcItem *pFrom){ Select *pSel = pFrom->pSelect; Table *pTab; @@ -134240,7 +139604,13 @@ sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol); pTab->iPKey = -1; 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 + pTab->tabFlags |= TF_Ephemeral; /* Legacy compatibility mode */ +#endif + return pParse->nErr ? SQLITE_ERROR : SQLITE_OK; } @@ -134271,10 +139641,10 @@ */ 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 +139664,15 @@ } pTabList = p->pSrc; pEList = p->pEList; + if( pParse->pWith && (p->selFlags & SF_View) ){ + if( p->pWith==0 ){ + p->pWith = (With*)sqlite3DbMallocZero(db, sizeof(With)); + 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 @@ -134310,10 +139689,6 @@ assert( pFrom->fg.isRecursive==0 || pFrom->pTab!=0 ); if( pFrom->pTab ) 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; @@ -134323,6 +139698,12 @@ 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->pTab; + assert( pTab!=0 ); +#endif }else{ /* An ordinary table or view name in the FROM clause */ assert( pFrom->pTab==0 ); @@ -134339,26 +139720,31 @@ 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); + if( IsView(pTab) ){ + if( (db->flags & SQLITE_EnableView)==0 + && pTab->pSchema!=db->aDb[1].pSchema + ){ + sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited", + pTab->zName); + } + pFrom->pSelect = sqlite3SelectDup(db, pTab->u.view.pSelect, 0); } #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 */ @@ -134370,14 +139756,15 @@ } /* 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 || sqliteProcessJoin(pParse, p) ){ return WRC_Abort; } @@ -134458,7 +139845,7 @@ zSchemaName = iDb>=0 ? db->aDb[iDb].zDbSName : "*"; } for(j=0; jnCol; j++){ - char *zName = pTab->aCol[j].zName; + char *zName = pTab->aCol[j].zCnName; char *zColname; /* The computed column name */ char *zToFree; /* Malloced string that needs to be freed */ Token sColname; /* Computed column name as a token */ @@ -134589,7 +139976,7 @@ sqlite3WalkSelect(&w, pSelect); } w.xSelectCallback = selectExpander; - w.xSelectCallback2 = selectPopWith; + w.xSelectCallback2 = sqlite3SelectPopWith; w.eCode = 0; sqlite3WalkSelect(&w, pSelect); } @@ -134613,7 +140000,7 @@ Parse *pParse; int i; SrcList *pTabList; - struct SrcList_item *pFrom; + SrcItem *pFrom; assert( p->selFlags & SF_Resolved ); if( p->selFlags & SF_HasTypeInfo ) return; @@ -134674,12 +140061,13 @@ 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); } @@ -134696,8 +140084,10 @@ int i; struct AggInfo_func *pFunc; int nReg = pAggInfo->nFunc + pAggInfo->nColumn; + assert( pParse->db->pParse==pParse ); + assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 ); if( nReg==0 ) return; - if( pParse->nErr || pParse->db->mallocFailed ) return; + if( pParse->nErr ) return; #ifdef SQLITE_DEBUG /* Verify that all AggInfo registers are within the range specified by ** AggInfo.mnReg..AggInfo.mxReg */ @@ -134715,15 +140105,17 @@ 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)); } } } @@ -134738,8 +140130,9 @@ 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) ); + ExprList *pList; + assert( ExprUseXList(pF->pFExpr) ); + pList = pF->pFExpr->x.pList; sqlite3VdbeAddOp2(v, OP_AggFinal, pF->iMem, pList ? pList->nExpr : 0); sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); } @@ -134755,7 +140148,12 @@ ** registers if register regAcc contains 0. The caller will take care ** of setting and clearing regAcc. */ -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; @@ -134768,9 +140166,10 @@ int nArg; int addrNext = 0; int regAgg; - ExprList *pList = pF->pFExpr->x.pList; - assert( !ExprHasProperty(pF->pFExpr, EP_xIsSelect) ); + ExprList *pList; + assert( ExprUseXList(pF->pFExpr) ); assert( !IsWindowFunc(pF->pFExpr) ); + pList = pF->pFExpr->x.pList; if( ExprHasProperty(pF->pFExpr, EP_WinFunc) ){ Expr *pFilter = pF->pFExpr->y.pWin->pFilter; if( pAggInfo->nAccumulator @@ -134801,13 +140200,12 @@ 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, regAgg); } if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ CollSeq *pColl = 0; @@ -134859,7 +140257,7 @@ ){ 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 +140282,17 @@ 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 ){ @@ -134923,7 +140331,7 @@ sWalker.u.pSelect = p; sqlite3WalkExpr(&sWalker, p->pHaving); #if SELECTTRACE_ENABLED - if( sWalker.eCode && (sqlite3_unsupported_selecttrace & 0x100)!=0 ){ + if( sWalker.eCode && (sqlite3SelectTrace & 0x100)!=0 ){ SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n")); sqlite3TreeViewSelect(0, p, 0); } @@ -134935,11 +140343,13 @@ ** If it is, then return the SrcList_item for the prior view. If it is not, ** 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 */ ){ - struct SrcList_item *pItem; + SrcItem *pItem; + assert( pThis->pSelect!=0 ); + if( pThis->pSelect->selFlags & SF_PushDown ) return 0; for(pItem = pTabList->a; pItempSelect==0 ) continue; @@ -134955,9 +140365,7 @@ ** 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( pItem->pSelect->selFlags & SF_PushDown ){ /* The view was modified by some other optimization such as ** pushDownWhereTerms() */ continue; @@ -134967,6 +140375,15 @@ return 0; } +/* +** Deallocate a single AggInfo object +*/ +static void agginfoFree(sqlite3 *db, AggInfo *p){ + sqlite3DbFree(db, p->aCol); + sqlite3DbFree(db, p->aFunc); + sqlite3DbFreeNN(db, p); +} + #ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION /* ** Attempt to transform a query of the form @@ -134998,7 +140415,9 @@ if( p->pGroupBy ) 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; @@ -135045,7 +140464,7 @@ p->selFlags &= ~SF_Aggregate; #if SELECTTRACE_ENABLED - if( sqlite3_unsupported_selecttrace & 0x400 ){ + if( sqlite3SelectTrace & 0x400 ){ SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n")); sqlite3TreeViewSelect(0, p, 0); } @@ -135091,14 +140510,16 @@ 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 ){ + if( sqlite3SelectTrace & 0x100 ){ sqlite3TreeViewSelect(0, p, 0); } #endif @@ -135107,38 +140528,53 @@ 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; + 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 SELECTTRACE_ENABLED + SELECTTRACE(1,pParse,p, ("dropping superfluous ORDER BY:\n")); + if( sqlite3SelectTrace & 0x100 ){ + sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY"); + } +#endif + sqlite3ParserAddCleanup(pParse, + (void(*)(sqlite3*,void*))sqlite3ExprListDelete, + p->pOrderBy); + testcase( pParse->earlyCleanup ); + p->pOrderBy = 0; + } p->selFlags &= ~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 ){ + if( sqlite3SelectTrace & 0x104 ){ SELECTTRACE(0x104,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]; + ** 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]; for(i=1; ipSrc->nSrc; i++){ - struct SrcList_item *p1 = &p->pSrc->a[i]; + SrcItem *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", @@ -135147,20 +140583,25 @@ 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 &= ~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 ){ + if( p->pWin && (sqlite3SelectTrace & 0x108)!=0 ){ SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n")); sqlite3TreeViewSelect(0, p, 0); } @@ -135176,7 +140617,7 @@ */ #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) for(i=0; !p->pPrior && inSrc; i++){ - struct SrcList_item *pItem = &pTabList->a[i]; + SrcItem *pItem = &pTabList->a[i]; Select *pSub = pItem->pSelect; Table *pTab = pItem->pTab; @@ -135219,6 +140660,39 @@ if( (pSub->selFlags & SF_Aggregate)!=0 ) continue; assert( pSub->pGroupBy==0 ); + /* 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 + */ + if( pSub->pOrderBy!=0 + && (p->pOrderBy!=0 || pTabList->nSrc>1) /* Condition (5) */ + && pSub->pLimit==0 /* Condition (1) */ + && (pSub->selFlags & SF_OrderByReqd)==0 /* Condition (2) */ + && (p->selFlags & SF_OrderByReqd)==0 /* Condition (3) and (4) */ + && OptimizationEnabled(db, SQLITE_OmitOrderBy) + ){ + SELECTTRACE(0x100,pParse,p, + ("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1)); + sqlite3ExprListDelete(db, 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 @@ -135267,7 +140741,7 @@ 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( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ sqlite3TreeViewSelect(0, p, 0); } #endif @@ -135281,12 +140755,13 @@ ** as the equivalent optimization will be handled by query planner in ** sqlite3WhereBegin(). */ - 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 ){ + if( sqlite3SelectTrace & 0x100 ){ SELECTTRACE(0x100,pParse,p,("After constant propagation:\n")); sqlite3TreeViewSelect(0, p, 0); } @@ -135310,7 +140785,8 @@ ** (2) Generate code for all sub-queries */ for(i=0; inSrc; i++){ - struct SrcList_item *pItem = &pTabList->a[i]; + SrcItem *pItem = &pTabList->a[i]; + SrcItem *pPrior; SelectDest dest; Select *pSub; #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) @@ -135343,19 +140819,8 @@ pSub = pItem->pSelect; if( pSub==0 ) continue; - /* 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. */ + assert( pItem->addrFillSub==0 ); /* Increment Parse.nHeight by the height of the largest expression ** tree referred to by this, the parent select. The child select @@ -135370,16 +140835,19 @@ ** inside the subquery. This can help the subquery to run more efficiently. */ if( OptimizationEnabled(db, SQLITE_PushDown) + && (pItem->fg.isCte==0 + || (pItem->u2.pCteUse->eM10d!=M10d_Yes && pItem->u2.pCteUse->nUse<2)) && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor, (pItem->fg.jointype & JT_OUTER)!=0) ){ #if SELECTTRACE_ENABLED - if( sqlite3_unsupported_selecttrace & 0x100 ){ + if( sqlite3SelectTrace & 0x100 ){ SELECTTRACE(0x100,pParse,p, ("After WHERE-clause push-down into subquery %d:\n", pSub->selId)); sqlite3TreeViewSelect(0, p, 0); } #endif + assert( pItem->pSelect && (pItem->pSelect->selFlags & SF_PushDown)!=0 ); }else{ SELECTTRACE(0x100,pParse,p,("Push-down not possible\n")); } @@ -135389,16 +140857,18 @@ /* 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) + ** The subquery is implemented as a co-routine if: + ** (1) the subquery is guaranteed to be the outer loop (so that + ** it does not need to be computed more than once), and + ** (2) the subquery is not a CTE that should be materialized ** - ** TODO: Are there other reasons beside (1) to use a co-routine + ** TODO: Are there other reasons beside (1) and (2) to use a co-routine ** implementation? */ if( i==0 && (pTabList->nSrc==1 || (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0) /* (1) */ + && (pItem->fg.isCte==0 || pItem->u2.pCteUse->eM10d!=M10d_Yes) /* (2) */ ){ /* Implement a co-routine that will return a single row of the result ** set on each invocation. @@ -135407,10 +140877,10 @@ pItem->regReturn = ++pParse->nMem; sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->regReturn, 0, addrTop); - VdbeComment((v, "%s", pItem->pTab->zName)); + VdbeComment((v, "%!S", pItem)); pItem->addrFillSub = addrTop; sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn); - ExplainQueryPlan((pParse, 1, "CO-ROUTINE %u", pSub->selId)); + ExplainQueryPlan((pParse, 1, "CO-ROUTINE %!S", pItem)); sqlite3Select(pParse, pSub, &dest); pItem->pTab->nRowLogEst = pSub->nSelectRow; pItem->fg.viaCoroutine = 1; @@ -135418,18 +140888,34 @@ sqlite3VdbeEndCoroutine(v, pItem->regReturn); sqlite3VdbeJumpHere(v, addrTop-1); sqlite3ClearTempRegCache(pParse); - }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 - */ + }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, + ** the make the pItem->iCursor be a copy of the ephemerial table that + ** holds the result of the materialization. */ + 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 ){ + /* This view has already been materialized by a prior entry in + ** this same FROM clause. Reuse it. */ + if( pPrior->addrFillSub ){ + sqlite3VdbeAddOp2(v, OP_Gosub, pPrior->regReturn, pPrior->addrFillSub); + } + sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor); + pSub->nSelectRow = pPrior->pSelect->nSelectRow; + }else{ + /* 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. */ int topAddr; int onceAddr = 0; int retAddr; - struct SrcList_item *pPrior; - testcase( pItem->addrFillSub==0 ); /* Ticket c52b09c7f38903b1311 */ pItem->regReturn = ++pParse->nMem; topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn); pItem->addrFillSub = topAddr+1; @@ -135438,26 +140924,26 @@ ** 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)); - } - 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); + VdbeNoopComment((v, "materialize %!S", pItem)); } + sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); + ExplainQueryPlan((pParse, 1, "MATERIALIZE %!S", pItem)); + sqlite3Select(pParse, pSub, &dest); pItem->pTab->nRowLogEst = pSub->nSelectRow; if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr); retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn); - VdbeComment((v, "end %s", pItem->pTab->zName)); + VdbeComment((v, "end %!S", pItem)); sqlite3VdbeChangeP1(v, topAddr, retAddr); sqlite3ClearTempRegCache(pParse); + if( pItem->fg.isCte && pItem->fg.isCorrelated==0 ){ + CteUse *pCteUse = pItem->u2.pCteUse; + pCteUse->addrM9e = pItem->addrFillSub; + pCteUse->regRtn = pItem->regReturn; + pCteUse->iCur = pItem->iCursor; + pCteUse->nRowEst = pSub->nSelectRow; + } } if( db->mallocFailed ) goto select_end; pParse->nHeight -= sqlite3SelectExprHeight(p); @@ -135474,7 +140960,7 @@ sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0; #if SELECTTRACE_ENABLED - if( sqlite3_unsupported_selecttrace & 0x400 ){ + if( sqlite3SelectTrace & 0x400 ){ SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n")); sqlite3TreeViewSelect(0, p, 0); } @@ -135510,7 +140996,7 @@ assert( sDistinct.isTnct ); #if SELECTTRACE_ENABLED - if( sqlite3_unsupported_selecttrace & 0x400 ){ + if( sqlite3SelectTrace & 0x400 ){ SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n")); sqlite3TreeViewSelect(0, p, 0); } @@ -135587,7 +141073,7 @@ /* Begin the database scan. */ SELECTTRACE(1,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); @@ -135602,6 +141088,7 @@ sSort.pOrderBy = 0; } } + SELECTTRACE(1,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,6 +141127,7 @@ /* End the database scan loop. */ + SELECTTRACE(1,pParse,p,("WhereEnd\n")); sqlite3WhereEnd(pWInfo); } }else{ @@ -135710,11 +141198,14 @@ ** SELECT statement. */ pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) ); - if( pAggInfo==0 ){ + if( pAggInfo ){ + sqlite3ParserAddCleanup(pParse, + (void(*)(sqlite3*,void*))agginfoFree, pAggInfo); + testcase( pParse->earlyCleanup ); + } + if( db->mallocFailed ){ goto select_end; } - pAggInfo->pNext = pParse->pAggList; - pParse->pAggList = pAggInfo; pAggInfo->selId = p->selId; memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; @@ -135744,7 +141235,7 @@ } for(i=0; inFunc; i++){ Expr *pExpr = pAggInfo->aFunc[i].pFExpr; - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); + assert( ExprUseXList(pExpr) ); sNC.ncFlags |= NC_InAggFunc; sqlite3ExprAnalyzeAggList(&sNC, pExpr->x.pList); #ifndef SQLITE_OMIT_WINDOWFUNC @@ -135758,10 +141249,14 @@ pAggInfo->mxReg = pParse->nMem; if( db->mallocFailed ) goto select_end; #if SELECTTRACE_ENABLED - if( sqlite3_unsupported_selecttrace & 0x400 ){ + if( sqlite3SelectTrace & 0x400 ){ int ii; SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", pAggInfo)); sqlite3TreeViewSelect(0, p, 0); + if( minMaxFlag ){ + sqlite3DebugPrintf("MIN/MAX Optimization (0x%02x) adds:\n", minMaxFlag); + sqlite3TreeViewExprList(0, pMinMaxOrderBy, 0, "ORDERBY"); + } for(ii=0; iinColumn; ii++){ sqlite3DebugPrintf("agg-column[%d] iMem=%d\n", ii, pAggInfo->aCol[ii].iMem); @@ -135789,6 +141284,22 @@ 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 @@ -135825,10 +141336,15 @@ */ 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 + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, pDistinct, + 0, (WHERE_GROUPBY|(orderByGrp ? WHERE_SORTBYGROUP : 0)|distFlag), 0 ); - if( pWInfo==0 ) goto select_end; + if( pWInfo==0 ){ + sqlite3ExprListDelete(db, pDistinct); + goto select_end; + } + eDist = sqlite3WhereIsDistinct(pWInfo); + SELECTTRACE(1,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 @@ -135877,6 +141393,7 @@ sqlite3VdbeAddOp2(v, OP_SorterInsert, pAggInfo->sortingIdx, regRecord); sqlite3ReleaseTempReg(pParse, regRecord); sqlite3ReleaseTempRange(pParse, regBase, nCol); + SELECTTRACE(1,pParse,p,("WhereEnd\n")); sqlite3WhereEnd(pWInfo); pAggInfo->sortingIdxPTab = sortPTab = pParse->nTab++; sortOut = sqlite3GetTempReg(pParse); @@ -135944,19 +141461,21 @@ ** 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{ + SELECTTRACE(1,pParse,p,("WhereEnd\n")); sqlite3WhereEnd(pWInfo); sqlite3VdbeChangeToNoop(v, addrSortingIdx); } + sqlite3ExprListDelete(db, pDistinct); /* Output the final row of result */ @@ -136000,6 +141519,10 @@ VdbeComment((v, "indicate accumulator empty")); sqlite3VdbeAddOp1(v, OP_Return, regReset); + if( 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 { Table *pTab; @@ -136063,7 +141586,9 @@ explainSimpleCount(pParse, pTab, pBest); }else{ 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,6 +141612,10 @@ 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; } /* This case runs if the aggregate has no GROUP BY clause. The @@ -136106,16 +141635,23 @@ SELECTTRACE(1,pParse,p,("WhereBegin\n")); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMaxOrderBy, - 0, minMaxFlag, 0); + pDistinct, 0, minMaxFlag|distFlag, 0); if( pWInfo==0 ){ goto select_end; } - updateAccumulator(pParse, regAcc, pAggInfo); + SELECTTRACE(1,pParse,p,("WhereBegin returns\n")); + eDist = sqlite3WhereIsDistinct(pWInfo); + updateAccumulator(pParse, regAcc, pAggInfo, eDist); + if( eDist!=WHERE_DISTINCT_NOOP ){ + struct AggInfo_func *pF = &pAggInfo->aFunc[0]; + 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); } + SELECTTRACE(1,pParse,p,("WhereEnd\n")); sqlite3WhereEnd(pWInfo); finalizeAggFunctions(pParse, pAggInfo); } @@ -136155,20 +141691,20 @@ ** 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 if( pAggInfo && !db->mallocFailed ){ for(i=0; inColumn; i++){ Expr *pExpr = pAggInfo->aCol[i].pCExpr; - assert( pExpr!=0 || db->mallocFailed ); - if( pExpr==0 ) continue; + assert( pExpr!=0 ); 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 ); } @@ -136177,7 +141713,7 @@ #if SELECTTRACE_ENABLED SELECTTRACE(0x1,pParse,p,("end processing\n")); - if( (sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ + if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ sqlite3TreeViewSelect(0, p, 0); } #endif @@ -136438,28 +141974,51 @@ ** 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 */ + Schema *pTmpSchema; /* Schema of the pTab table */ + Trigger *pList; /* List of triggers to return */ + HashElem *p; /* Loop variable for TEMP triggers */ if( pParse->disableTriggers ){ return 0; } - - 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; - } + 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->pNext = pList; + pList = pTrig; + }else if( pTrig->op==TK_RETURNING +#ifndef SQLITE_OMIT_VIRTUALTABLE + && pParse->db->pVtabCtx==0 +#endif + ){ + assert( pParse->bReturning ); + assert( &(pParse->u1.pReturning->retTrig) == pTrig ); + pTrig->table = pTab->zName; + pTrig->pTabSchema = pTab->pSchema; + pTrig->pNext = pList; + pList = pTrig; } + p = sqliteHashNext(p); } - - return (pList ? pList : pTab->pTrigger); +#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); + } +#endif + return pList; } /* @@ -136547,22 +142106,11 @@ 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; } /* Check that the trigger name is not reserved and that no trigger of the @@ -136597,15 +142145,15 @@ /* 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 +142213,23 @@ }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; } /* @@ -136722,14 +142287,14 @@ 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 ){ @@ -136942,7 +142507,7 @@ ** 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); @@ -136984,7 +142549,7 @@ } 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 +142601,7 @@ */ 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); @@ -137107,15 +142672,53 @@ 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; } @@ -137159,6 +142762,146 @@ } /* +** 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->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->eEName = pList->a[i].eEName; + } + } + } + return pNew; +} + +/* +** 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 sFrom; + + assert( v!=0 ); + assert( pParse->bReturning ); + assert( db->pParse==pParse ); + pReturning = pParse->u1.pReturning; + assert( pTrigger == &(pReturning->retTrig) ); + memset(&sSelect, 0, sizeof(sSelect)); + memset(&sFrom, 0, sizeof(sFrom)); + sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0); + sSelect.pSrc = &sFrom; + sFrom.nSrc = 1; + sFrom.a[0].pTab = pTab; + sFrom.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( !db->mallocFailed ){ + 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; + 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 +142950,7 @@ sqlite3ExprDup(db, pStep->pWhere, 0), pParse->eOrconf, 0, 0, 0 ); + sqlite3VdbeAddOp0(v, OP_ResetCount); break; } case TK_INSERT: { @@ -137217,6 +142961,7 @@ pParse->eOrconf, sqlite3UpsertDup(db, pStep->pUpsert) ); + sqlite3VdbeAddOp0(v, OP_ResetCount); break; } case TK_DELETE: { @@ -137224,6 +142969,7 @@ sqlite3TriggerStepSrc(pParse, pStep), sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0 ); + sqlite3VdbeAddOp0(v, OP_ResetCount); break; } default: assert( pStep->op==TK_SELECT ); { @@ -137235,9 +142981,6 @@ break; } } - if( pStep->op!=TK_SELECT ){ - sqlite3VdbeAddOp0(v, OP_ResetCount); - } } return 0; @@ -137295,8 +143038,8 @@ 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 +143061,17 @@ /* 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; + sNC.pParse = &sSubParse; + sSubParse.pTriggerTab = pTab; + sSubParse.pToplevel = pTop; + sSubParse.zAuthContext = pTrigger->zName; + sSubParse.eTriggerOp = pTrigger->op; + sSubParse.nQueryLoop = pParse->nQueryLoop; + sSubParse.disableVtab = pParse->disableVtab; - v = sqlite3GetVdbe(pSubParse); + v = sqlite3GetVdbe(&sSubParse); if( v ){ VdbeComment((v, "Start: %s.%s (%s %s%s%s ON %s)", pTrigger->zName, onErrorText(orconf), @@ -137353,17 +143094,17 @@ ** 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 +143112,24 @@ } 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 +143162,7 @@ /* 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 +143185,7 @@ 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. */ @@ -137486,7 +143228,7 @@ ** ... ... ** 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 +143273,20 @@ 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); + } } } } @@ -137581,13 +143331,18 @@ assert( isNew==1 || isNew==0 ); 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]; + } } } } @@ -137661,13 +143416,14 @@ */ SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){ assert( pTab!=0 ); - if( !pTab->pSelect ){ + if( !IsView(pTab) ){ sqlite3_value *pValue = 0; u8 enc = ENC(sqlite3VdbeDb(v)); Column *pCol = &pTab->aCol[i]; - VdbeComment((v, "%s.%s", pTab->zName, pCol->zName)); + 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); @@ -137821,6 +143577,7 @@ assert( pTabList->nSrc>1 ); if( pSrc ){ + pSrc->a[0].fg.notCte = 1; pSrc->a[0].iCursor = -1; pSrc->a[0].pTab->nTabRef--; pSrc->a[0].pTab = 0; @@ -137835,8 +143592,8 @@ #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 +143607,8 @@ } #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 +143616,9 @@ } } pSelect = sqlite3SelectNew(pParse, pList, - pSrc, pWhere2, pGrp, 0, pOrderBy2, SF_UpdateFrom|SF_IncludeHidden, pLimit2 + pSrc, pWhere2, pGrp, 0, pOrderBy2, SF_UFSrcCheck|SF_IncludeHidden, pLimit2 ); + if( pSelect ) pSelect->selFlags |= SF_OrderByReqd; sqlite3SelectDestInit(&dest, eDest, iEph); dest.iSDParm2 = (pPk ? pPk->nKeyCol : -1); sqlite3Select(pParse, pSelect, &dest); @@ -137944,9 +143703,11 @@ 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 +143720,7 @@ */ #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 @@ -138048,13 +143809,16 @@ */ chngRowid = chngPk = 0; for(i=0; inExpr; i++){ + u8 hCol = sqlite3StrIHash(pChanges->a[i].zEName); /* If this is an UPDATE with a FROM clause, do not resolve expressions ** here. The call to sqlite3Select() below will do that. */ 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( pTab->aCol[j].hName==hCol + && sqlite3StrICmp(pTab->aCol[j].zCnName, pChanges->a[i].zEName)==0 + ){ if( j==pTab->iPKey ){ chngRowid = 1; pRowidExpr = pChanges->a[i].pExpr; @@ -138068,7 +143832,7 @@ testcase( pTab->aCol[j].colFlags & COLFLAG_STORED ); sqlite3ErrorMsg(pParse, "cannot UPDATE generated column \"%s\"", - pTab->aCol[j].zName); + pTab->aCol[j].zCnName); goto update_cleanup; } #endif @@ -138092,7 +143856,7 @@ { 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 +143888,10 @@ 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 +144010,7 @@ if( (db->flags&SQLITE_CountRows)!=0 && !pParse->pTriggerTab && !pParse->nested + && !pParse->bReturning && pUpsert==0 ){ regRowCount = ++pParse->nMem; @@ -138252,6 +144019,8 @@ 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; @@ -138306,11 +144075,11 @@ ** 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; + flags = WHERE_ONEPASS_DESIRED; if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){ 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 +144112,10 @@ ** 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 @@ -138396,7 +144166,12 @@ /* 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 +144208,9 @@ 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); @@ -138684,11 +144460,9 @@ }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 +144479,7 @@ ** 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: @@ -138788,12 +144560,26 @@ 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); @@ -138807,7 +144593,7 @@ } } - 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 +144601,9 @@ 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. */ @@ -138926,16 +144714,23 @@ /* ** 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 +144740,8 @@ 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 +144753,25 @@ 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; } @@ -138997,6 +144796,7 @@ 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 ); @@ -139010,87 +144810,131 @@ 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].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 ); + 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 ); + 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; + 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; + if( pNext==0 ) return 1; + if( pNext->pUpsertTarget==0 ) return 1; + if( pNext->pUpsertIdx==0 ) return 1; + 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 +144958,13 @@ 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 +144983,7 @@ 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 +144994,17 @@ 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")); } @@ -139321,8 +145165,8 @@ 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 */ @@ -139420,7 +145264,9 @@ /* 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; } @@ -139509,8 +145355,8 @@ 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,7 +145628,8 @@ 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 ){ @@ -139797,21 +145644,24 @@ /* ** 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 +145671,7 @@ 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 +145699,7 @@ 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; @@ -139912,37 +145762,41 @@ ** database connection. */ SQLITE_PRIVATE void sqlite3VtabClear(sqlite3 *db, Table *p){ + assert( IsVirtual(p) ); if( !db || db->pnBytesFreed==0 ) vtabDisconnectAll(0, p); - if( p->azModuleArg ){ + 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 +145819,11 @@ 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 +145840,11 @@ ** 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 +145872,10 @@ 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 @@ -140052,7 +145908,7 @@ */ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); 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, @@ -140066,24 +145922,20 @@ 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 +145986,16 @@ 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 +146022,7 @@ 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 ); @@ -140206,12 +146061,12 @@ 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 +146092,7 @@ zType[i-1] = '\0'; } pTab->aCol[iCol].colFlags |= COLFLAG_HIDDEN; + pTab->tabFlags |= TF_HasHidden; oooHidden = TF_OOOHidden; }else{ pTab->tabFlags |= oooHidden; @@ -140263,16 +146119,17 @@ 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 +146192,10 @@ 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,8 +146230,8 @@ VtabCtx *pCtx; int rc = SQLITE_OK; Table *pTab; - char *zErr = 0; Parse sParse; + int initBusy; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){ @@ -140391,21 +146248,27 @@ pTab = pCtx->pTab; assert( IsVirtual(pTab) ); - memset(&sParse, 0, sizeof(sParse)); + sqlite3ParseObjectInit(&sParse, db); sParse.eParseMode = PARSE_MODE_DECLARE_VTAB; - sParse.db = db; + /* 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) + && ALWAYS(sParse.pNewTable!=0) + && ALWAYS(!db->mallocFailed) + && IsOrdinaryTable(sParse.pNewTable) ){ + assert( sParse.zErrMsg==0 ); if( !pTab->aCol ){ Table *pNew = sParse.pNewTable; Index *pIdx; pTab->aCol = pNew->aCol; - pTab->nCol = pNew->nCol; + 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 +146292,9 @@ } 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 +146303,8 @@ 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 +146324,13 @@ 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 +146344,9 @@ 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); @@ -140692,6 +146560,7 @@ /* 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( !IsVirtual(pTab) ) return pDef; @@ -140766,8 +146635,9 @@ /* ** 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 @@ -140794,9 +146664,11 @@ } 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 +146677,6 @@ sqlite3ErrorMsg(pParse, "%s", zErr); sqlite3DbFree(db, zErr); sqlite3VtabEponymousTableClear(db, pMod); - return 0; } return 1; } @@ -140937,19 +146808,6 @@ #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 */ @@ -140997,6 +146855,7 @@ u32 iLikeRepCntr; /* LIKE range processing counter register (times 2) */ int addrLikeRep; /* LIKE range processing address */ #endif + int regFilter; /* Bloom filter */ 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 */ @@ -141011,7 +146870,7 @@ 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 */ @@ -141055,10 +146914,12 @@ } 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 */ 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 */ @@ -141181,9 +147042,11 @@ 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 +147063,8 @@ #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 +147076,7 @@ #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 +147087,11 @@ 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,6 +147115,7 @@ u8 hasOr; /* True if any a[].eOperator is WO_OR */ int nTerm; /* Number of terms */ int nSlot; /* Number of entries in a[] */ + int nBase; /* Number of terms through the last non-Virtual */ WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */ #if defined(SQLITE_SMALL_STACK) WhereTerm aStatic[1]; /* Initial static space for a[] */ @@ -141313,18 +147174,12 @@ }; /* -** 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. */ 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 @@ -141392,6 +147247,9 @@ ExprList *pOrderBy; /* The ORDER BY clause or NULL */ ExprList *pResultSet; /* Result set of the query */ Expr *pWhere; /* The complete WHERE clause */ +#ifndef SQLITE_OMIT_VIRTUALTABLE + Select *pLimit; /* Used to access LIMIT expr/registers for vtabs */ +#endif 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 */ @@ -141408,6 +147266,7 @@ unsigned sorted :1; /* True if really sorted (not just grouped) */ LogEst nRowOut; /* Estimated number of output rows */ 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 */ Bitmask revMask; /* Mask of ORDER BY terms that need reversing */ @@ -141444,8 +147303,14 @@ 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 */ +); #else # define sqlite3WhereExplainOneScan(u,v,w,x) 0 +# define sqlite3WhereExplainBloomFilter(u,v,w) 0 #endif /* SQLITE_OMIT_EXPLAIN */ #ifdef SQLITE_ENABLE_STMT_SCANSTATUS SQLITE_PRIVATE void sqlite3WhereAddScanStatus( @@ -141470,11 +147335,12 @@ 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*); @@ -141536,6 +147402,11 @@ #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 */ #endif /* !defined(SQLITE_WHEREINT_H) */ @@ -141551,7 +147422,7 @@ 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; } /* @@ -141651,7 +147522,7 @@ if( sqlite3ParseToplevel(pParse)->explain==2 ) #endif { - struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom]; + SrcItem *pItem = &pTabList->a[pLevel->iFrom]; Vdbe *v = pParse->pVdbe; /* VM being constructed */ sqlite3 *db = pParse->db; /* Database handle */ int isSearch; /* True for a SEARCH. False for SCAN. */ @@ -141670,16 +147541,8 @@ || (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; @@ -141706,19 +147569,27 @@ 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 ){ @@ -141741,6 +147612,56 @@ } 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->pTab; + 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); + return ret; +} #endif /* SQLITE_OMIT_EXPLAIN */ #ifdef SQLITE_ENABLE_STMT_SCANSTATUS @@ -141827,6 +147748,12 @@ }else{ pTerm->wtFlags |= TERM_CODED; } +#ifdef WHERETRACE_ENABLED + if( sqlite3WhereTrace & 0x20000 ){ + sqlite3DebugPrintf("DISABLE-"); + sqlite3WhereTermPrint(pTerm, (int)(pTerm - (pTerm->pWC->a))); + } +#endif if( pTerm->iParent<0 ) break; pTerm = &pTerm->pWC->a[pTerm->iParent]; assert( pTerm!=0 ); @@ -141940,16 +147867,23 @@ 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 *pOrigRhs; /* Original unmodified RHS */ + ExprList *pOrigLhs; /* 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 */ + assert( ExprUseXSelect(pNew) ); + pOrigRhs = pNew->x.pSelect->pEList; + assert( pNew->pLeft!=0 ); + assert( ExprUseXList(pNew->pLeft) ); + pOrigLhs = pNew->pLeft->x.pList; for(i=iEq; inLTerm; i++){ if( pLoop->aLTerm[i]->pExpr==pX ){ - int iField = pLoop->aLTerm[i]->iField - 1; + int iField; + assert( (pLoop->aLTerm[i]->eOperator & (WO_OR|WO_AND))==0 ); + iField = pLoop->aLTerm[i]->u.x.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; @@ -142064,7 +147998,7 @@ } iTab = 0; - if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){ + if( !ExprUseXSelect(pX) || pX->x.pSelect->pEList->nExpr==1 ){ eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0, &iTab); }else{ sqlite3 *db = pParse->db; @@ -142086,12 +148020,15 @@ sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0); VdbeCoverageIf(v, bRev); VdbeCoverageIf(v, !bRev); - assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 ); + 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; @@ -142118,7 +148055,6 @@ if( iEq>0 ){ pIn->iBase = iReg - i; pIn->nPrefix = i; - pLoop->wsFlags |= WHERE_IN_EARLYOUT; }else{ pIn->nPrefix = 0; } @@ -142128,13 +148064,36 @@ 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 } - 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; } @@ -142220,6 +148179,7 @@ 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); @@ -142254,9 +148214,12 @@ sqlite3ReleaseTempReg(pParse, regBase); regBase = r1; }else{ - sqlite3VdbeAddOp2(v, OP_SCopy, r1, regBase+j); + sqlite3VdbeAddOp2(v, OP_Copy, r1, regBase+j); } } + } + for(j=nSkip; jaLTerm[j]; if( pTerm->eOperator & WO_IN ){ if( pTerm->pExpr->flags & EP_xIsSelect ){ /* No affinity ever needs to be (or should be) applied to a value @@ -142271,7 +148234,8 @@ 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; } @@ -142434,7 +148398,7 @@ ** 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 +148425,7 @@ 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; @@ -142491,7 +148455,7 @@ if( pTabItem->fg.jointype & JT_LEFT ){ Expr *pExpr = pTerm->pExpr; if( !ExprHasProperty(pExpr, EP_FromJoin) - || pExpr->iRightJoinTable!=pTabItem->iCursor + || pExpr->w.iRightJoinTable!=pTabItem->iCursor ){ sWalker.eCode = 0; sWalker.xExprCallback = codeCursorHintIsOrFunction; @@ -142603,7 +148567,7 @@ 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,14 +148577,16 @@ #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); } } @@ -142666,10 +148632,10 @@ 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); + ExprClearProperty(pExpr, EP_Skip|EP_Unlikely|EP_WinFunc|EP_Subrtn); + pExpr->y.pTab = 0; return WRC_Prune; }else{ return WRC_Continue; @@ -142684,7 +148650,7 @@ if( pExpr->op==TK_COLUMN ){ IdxExprTrans *pX = p->u.pIdxTrans; if( pExpr->iTable==pX->iTabCur && pExpr->iColumn==pX->iTabCol ){ - assert( pExpr->y.pTab!=0 ); + assert( ExprUseYTab(pExpr) && pExpr->y.pTab!=0 ); preserveExpr(pX, pExpr); pExpr->affExpr = sqlite3TableColumnAffinity(pExpr->y.pTab,pExpr->iColumn); pExpr->iTable = pX->iIdxCur; @@ -142732,15 +148698,16 @@ for(iIdxCol=0; iIdxColnColumn; iIdxCol++){ i16 iRef = pIdx->aiColumn[iIdxCol]; if( iRef==XN_EXPR ){ - assert( aColExpr->a[iIdxCol].pExpr!=0 ); + assert( aColExpr!=0 && 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) + && ((pTab->aCol[iRef].colFlags & COLFLAG_HASCOLL)==0 + || sqlite3StrICmp(sqlite3ColumnColl(&pTab->aCol[iRef]), + sqlite3StrBINARY)==0) ){ /* Check to see if there are direct references to generated columns ** that are contained in the index. Pulling the generated column @@ -142790,6 +148757,64 @@ } /* +** 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; + /* ,--- Because sqlite3ConstructBloomFilter() has will not have set + ** vvvvv--' pLevel->regFilter if this were true. */ + if( NEVER(pLoop->prereq & notReady) ) continue; + 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); + 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; + } +} + +/* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. */ @@ -142809,7 +148834,7 @@ 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 */ @@ -142900,11 +148925,27 @@ 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->pLimit!=0 ); + assert( pWInfo->pLimit->iOffset>0 ); + sqlite3VdbeAddOp2(v, OP_Integer, 0, pWInfo->pLimit->iOffset); + VdbeComment((v,"Zero OFFSET counter")); + } } } sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg); @@ -142914,18 +148955,32 @@ 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; + assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 ); + if( pLoop->wsFlags & WHERE_IN_ABLE ){ + iIn = pLevel->u.in.nIn; + }else{ + iIn = 0; + } for(j=nConstraint-1; j>=0; j--){ + int bIn; /* True to generate byte code to loop over RHS IN values */ pTerm = pLoop->aLTerm[j]; - if( (pTerm->eOperator & WO_IN)!=0 ) iIn--; + if( (pTerm->eOperator & WO_IN)!=0 + && (SMASKBIT32(j) & pLoop->u.vtab.mHandleIn)==0 + ){ + bIn = 1; + }else{ + bIn = 0; + } + if( bIn ) 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 - ){ + }else if( bIn && sqlite3ExprVectorSize(pTerm->pExpr->pLeft)==1 ){ Expr *pCompare; /* The comparison operator */ Expr *pRight; /* RHS of the comparison */ VdbeOp *pOp; /* Opcode to access the value of the IN constraint */ @@ -142991,12 +149046,15 @@ iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg); if( iRowidReg!=iReleaseReg ) sqlite3ReleaseTempReg(pParse, iReleaseReg); addrNxt = pLevel->addrNxt; + if( pLevel->regFilter ){ + 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 ){ @@ -143172,6 +149230,7 @@ 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 +149302,18 @@ ** 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 +149373,33 @@ ** 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); + VdbeCoverage(v); + } sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); VdbeCoverage(v); VdbeCoverageIf(v, op==OP_Rewind); testcase( op==OP_Rewind ); @@ -143351,8 +149431,19 @@ ** range (if any). */ nConstraint = nEq; + assert( pLevel->p2==0 ); if( pRangeEnd ){ Expr *pRight = pRangeEnd->pExpr->pRight; + if( addrSeekScan ){ + /* For a seek-scan that has a range on the lowest term of the index, + ** we have to make the top of the loop be code that sets the end + ** condition of the range. Otherwise, the OP_SeekScan might jump + ** over that initialization, leaving the range-end value set to the + ** range-start value, resulting in a wrong answer. + ** See ticket 5981a8c041a3c2f3 (2021-11-02). + */ + pLevel->p2 = sqlite3VdbeCurrentAddr(v); + } codeExprOrVector(pParse, pRight, regBase+nEq, nTop); whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd); if( (pRangeEnd->wtFlags & TERM_VNULL)==0 @@ -143386,7 +149477,7 @@ sqlite3DbFree(db, zEndAff); /* Top of the loop body */ - pLevel->p2 = sqlite3VdbeCurrentAddr(v); + if( pLevel->p2==0 ) pLevel->p2 = sqlite3VdbeCurrentAddr(v); /* Check if the index cursor is past the end of the range. */ if( nConstraint ){ @@ -143402,6 +149493,7 @@ 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,8 +149513,8 @@ 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 */ @@ -143431,17 +149523,7 @@ 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); @@ -143568,7 +149650,6 @@ 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; @@ -143586,7 +149667,7 @@ */ 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])); @@ -143629,7 +149710,7 @@ 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. ** @@ -143641,6 +149722,12 @@ ** 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 + ** + ** 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. */ if( pWC->nTerm>1 ){ int iTerm; @@ -143649,7 +149736,10 @@ 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 ); pExpr = sqlite3ExprDup(db, pExpr, 0); @@ -143659,7 +149749,7 @@ /* 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,17 +149759,22 @@ ** 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) ); /* 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; @@ -143687,9 +149782,9 @@ /* 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 ); + 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 +149881,22 @@ }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; @@ -143921,7 +150023,7 @@ ** 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; @@ -143938,12 +150040,13 @@ #endif assert( !ExprHasProperty(pE, EP_FromJoin) ); 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 +150058,7 @@ sEAlt = *pAlt->pExpr; sEAlt.pLeft = pE->pLeft; sqlite3ExprIfFalse(pParse, &sEAlt, addrCont, SQLITE_JUMPIFNULL); + pAlt->wtFlags |= TERM_CODED; } /* For a LEFT OUTER JOIN, generate code that will record the fact that @@ -143964,7 +150068,7 @@ 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++){ + 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; @@ -144075,6 +150179,7 @@ pWC->nSlot = sqlite3DbMallocSize(db, pWC->a)/sizeof(pWC->a[0]); } 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{ @@ -144191,6 +150296,7 @@ #ifdef SQLITE_EBCDIC if( *pnoCase ) return 0; #endif + assert( ExprUseXList(pExpr) ); pList = pExpr->x.pList; pLeft = pList->a[1].pExpr; @@ -144206,7 +150312,8 @@ 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 ){ @@ -144235,7 +150342,9 @@ 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) ) + && pLeft->y.pTab + && IsVirtual(pLeft->y.pTab)) /* Might be numeric */ ){ int isNum; double rDummy; @@ -144287,6 +150398,7 @@ 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 +150472,7 @@ Expr *pCol; /* Column reference */ int i; + assert( ExprUseXList(pExpr) ); pList = pExpr->x.pList; if( pList==0 || pList->nExpr!=2 ){ return 0; @@ -144373,9 +150486,11 @@ ** MATCH(expression,vtab_column) */ pCol = pList->a[1].pExpr; + assert( pCol->op!=TK_COLUMN || ExprUseYTab(pCol) ); testcase( pCol->op==TK_COLUMN && 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,6 +150511,7 @@ ** with function names in an arbitrary case. */ pCol = pList->a[0].pExpr; + assert( pCol->op!=TK_COLUMN || ExprUseYTab(pCol) ); testcase( pCol->op==TK_COLUMN && pCol->y.pTab==0 ); if( ExprIsVtab(pCol) ){ sqlite3_vtab *pVtab; @@ -144405,6 +150521,7 @@ 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); @@ -144420,10 +150537,12 @@ int res = 0; Expr *pLeft = pExpr->pLeft; Expr *pRight = pExpr->pRight; + assert( pLeft->op!=TK_COLUMN || ExprUseYTab(pLeft) ); testcase( pLeft->op==TK_COLUMN && pLeft->y.pTab==0 ); if( ExprIsVtab(pLeft) ){ res++; } + assert( pRight==0 || pRight->op!=TK_COLUMN || ExprUseYTab(pRight) ); testcase( pRight && pRight->op==TK_COLUMN && pRight->y.pTab==0 ); if( pRight && ExprIsVtab(pRight) ){ res++; @@ -144447,7 +150566,7 @@ static void transferJoinMarkings(Expr *pDerived, Expr *pBase){ if( pDerived ){ pDerived->flags |= pBase->flags & EP_FromJoin; - pDerived->iRightJoinTable = pBase->iRightJoinTable; + pDerived->w.iRightJoinTable = pBase->w.iRightJoinTable; } } @@ -144507,6 +150626,7 @@ 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 +150795,7 @@ 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 +150838,10 @@ ** 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 +150896,7 @@ 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. */ @@ -144794,7 +150914,8 @@ 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 +150935,10 @@ 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 +150953,7 @@ if( affRight!=0 && affRight!=affLeft ){ okToChngToIN = 0; }else{ - pOrTerm->wtFlags |= TERM_OR_OK; + pOrTerm->wtFlags |= TERM_OK; } } } @@ -144848,10 +150970,11 @@ 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 +150985,12 @@ 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); @@ -144990,7 +151113,9 @@ assert( TK_ISop==TK_VECTOR && (op>=TK_GT && ALWAYS(op<=TK_GE)) ){ + assert( ExprUseXList(pExpr) ); pExpr = pExpr->x.pList->a[0].pExpr; + } if( pExpr->op==TK_COLUMN ){ @@ -145003,6 +151128,7 @@ return exprMightBeIndexed2(pFrom,mPrereq,aiCurCol,pExpr); } + /* ** 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 @@ -145045,30 +151171,47 @@ 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; + +#ifdef SQLITE_DEBUG + if( prereqAll!=sqlite3WhereExprUsageNN(pMaskSet, pExpr) ){ + printf("\n*** Incorrect prereqAll computed for:\n"); + sqlite3TreeViewExpr(0,pExpr,0); + abort(); + } +#endif + if( ExprHasProperty(pExpr, EP_FromJoin) ){ - Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->iRightJoinTable); + Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->w.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 */ @@ -145087,25 +151230,28 @@ 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) ){ 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) + && !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); @@ -145131,11 +151277,23 @@ } 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_FromJoin) + && 0==sqlite3ExprCanBeNull(pLeft) + ){ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + pExpr->op = TK_TRUEFALSE; + pExpr->u.zToken = "false"; + ExprSetProperty(pExpr, EP_IsFalse); + pTerm->prereqAll = 0; + pTerm->eOperator = 0; } } @@ -145156,9 +151314,11 @@ ** 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 +151347,42 @@ 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_FromJoin) + ){ + 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 +151398,8 @@ ** 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 +151411,12 @@ 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 @@ -145272,76 +151473,33 @@ } #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]; @@ -145352,67 +151510,77 @@ /* 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 + && ALWAYS( ExprUseXSelect(pExpr) ) && pExpr->x.pSelect->pPrior==0 #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; + 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; - 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; + 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->w.iRightJoinTable = pExpr->w.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.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 +151615,7 @@ 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); @@ -145457,6 +151626,113 @@ } /* +** 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) && 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 sqlite3WhereAddLimit(WhereClause *pWC, Select *p){ + assert( p==0 || (p->pGroupBy==0 && (p->selFlags & SF_Aggregate)==0) ); + if( (p && p->pLimit) /* 1 */ + && (p->selFlags & (SF_Distinct|SF_Aggregate))==0 /* 2 */ + && (p->pSrc->nSrc==1 && IsVirtual(p->pSrc->a[0].pTab)) /* 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==0 ); + continue; + } + if( pWC->a[ii].leftCursor!=iCsr ) 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].sortFlags & KEYINFO_ORDER_BIGNULL ) return; + } + } + + /* All conditions are met. Add the terms to the where-clause object. */ + assert( p->pLimit->op==TK_LIMIT ); + whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft, + iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT); + if( p->iOffset>0 ){ + whereAddLimitExpr(pWC, p->iOffset, p->pLimit->pRight, + iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET); + } + } +} + +/* ** Initialize a preallocated WhereClause structure. */ SQLITE_PRIVATE void sqlite3WhereClauseInit( @@ -145467,6 +151743,7 @@ pWC->hasOr = 0; pWC->pOuter = 0; pWC->nTerm = 0; + pWC->nBase = 0; pWC->nSlot = ArraySize(pWC->aStatic); pWC->a = pWC->aStatic; } @@ -145477,17 +151754,34 @@ ** 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); - } - if( a->wtFlags & TERM_ORINFO ){ - whereOrInfoDelete(db, a->u.pOrInfo); - }else if( a->wtFlags & TERM_ANDINFO ){ - whereAndInfoDelete(db, a->u.pAndInfo); + 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 ); + } +#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 ){ @@ -145500,28 +151794,52 @@ ** 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 +151847,15 @@ #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 +151898,7 @@ */ 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; @@ -145596,7 +151923,9 @@ 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); @@ -145641,19 +151970,19 @@ */ 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[1]; /* RHS values for constraints. MUST BE LAST + ** because extra space is allocated to hold up + ** to nTerm such values */ }; /* 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 */ @@ -145717,6 +152046,32 @@ } /* +** 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); +} + +/* ** Return the VDBE address or label to jump to in order to continue ** immediately with the next row of a WHERE clause. */ @@ -145825,7 +152180,12 @@ 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); } @@ -145847,6 +152207,18 @@ } /* +** 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; +} + +/* ** Advance to the next WhereTerm that matches according to the criteria ** established when the pScan object was initialized by whereScanInit(). ** Return NULL if there are no more matching WhereTerms. @@ -145865,10 +152237,12 @@ 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) @@ -145876,8 +152250,7 @@ ){ 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++){ @@ -145909,7 +152282,8 @@ } } 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 +152292,18 @@ } 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 +152370,16 @@ 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 +152459,8 @@ 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 ){ @@ -146137,7 +152524,9 @@ */ 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 +152544,7 @@ */ 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; @@ -146209,14 +152599,14 @@ pOp->p2 = pOp->p3; pOp->p3 = 0; }else if( pOp->opcode==OP_Rowid ){ - if( iAutoidxCur ){ - pOp->opcode = OP_Sequence; - pOp->p1 = iAutoidxCur; - }else{ + 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 } } } @@ -146232,12 +152622,14 @@ int i; if( !sqlite3WhereTrace ) return; 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", @@ -146273,9 +152665,9 @@ ** 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; if( pTerm->leftCursor!=pSrc->iCursor ) return 0; @@ -146290,8 +152682,9 @@ return 0; } 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 ); + if( pTerm->u.x.leftColumn<0 ) return 0; + aff = pSrc->pTab->aCol[pTerm->u.x.leftColumn].affinity; if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; testcase( pTerm->pExpr->op==TK_IS ); return 1; @@ -146305,11 +152698,11 @@ ** 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 WhereClause *pWC, /* The WHERE clause */ + const SrcItem *pSrc, /* The FROM clause term to get the next index */ + 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 */ @@ -146332,7 +152725,7 @@ u8 sentWarning = 0; /* True if a warnning has been issued */ Expr *pPartial = 0; /* Partial Index Expression */ int iContinue = 0; /* Jump here to skip excluded rows */ - struct SrcList_item *pTabItem; /* FROM clause term being indexed */ + SrcItem *pTabItem; /* FROM clause term being indexed */ int addrCounter = 0; /* Address where integer counter is initialized */ int regBase; /* Array of registers where record is assembled */ @@ -146351,25 +152744,28 @@ 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 + && ((pSrc->fg.jointype&JT_LEFT)==0 || ExprHasProperty(pExpr,EP_FromJoin)) + && sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor) + ){ 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 +152777,7 @@ } } } - 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; @@ -146415,14 +152811,17 @@ 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; @@ -146458,6 +152857,10 @@ sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "for %s", pTable->zName)); + if( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) ){ + pLevel->regFilter = ++pParse->nMem; + sqlite3VdbeAddOp2(v, OP_Blob, 10000, pLevel->regFilter); + } /* Fill the automatic index with content */ pTabItem = &pWC->pWInfo->pTabList->a[pLevel->iFrom]; @@ -146480,6 +152883,10 @@ 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); + } sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue); @@ -146506,22 +152913,146 @@ } #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 */ + + assert( pLoop!=0 ); + assert( v!=0 ); + assert( pLoop->wsFlags & WHERE_BLOOMFILTER ); + + addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); + do{ + const SrcItem *pItem; + const Table *pTab; + u64 sz; + 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. + */ + pItem = &pWInfo->pTabList->a[pLevel->iFrom]; + assert( pItem!=0 ); + pTab = pItem->pTab; + 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 + && sqlite3ExprIsTableConstant(pExpr, iCur) + ){ + 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; jjaiColumn[jj]; + assert( pIdx->pTable==pItem->pTab ); + sqlite3ExprCodeGetColumnOfTable(v, pIdx->pTable, iCur, iCol,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 ){ + pLevel = &pWInfo->a[iLevel]; + 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); +} + + #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** 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,10 +153061,21 @@ int nOrderBy; sqlite3_index_info *pIdxInfo; u16 mNoOmit = 0; + const Table *pTab; + int eDistinct = 0; + ExprList *pOrderBy = pWInfo->pOrderBy; + + assert( pSrc!=0 ); + pTab = pSrc->pTab; + assert( pTab!=0 ); + assert( IsVirtual(pTab) ); - /* Count the number of possible WHERE clause constraints referring - ** to this virtual table */ + /* 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(i=nTerm=0, pTerm=pWC->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) ); @@ -146543,8 +153085,21 @@ testcase( pTerm->eOperator & WO_ALL ); if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue; if( pTerm->wtFlags & TERM_VNULL ) continue; - assert( pTerm->u.leftColumn>=(-1) ); + + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); + assert( pTerm->u.x.leftColumn>=XN_ROWID ); + assert( pTerm->u.x.leftColumnnCol ); + + /* 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; + } nTerm++; + pTerm->wtFlags |= TERM_OK; } /* If the ORDER BY clause contains only columns in the current @@ -146556,11 +153111,47 @@ int n = pOrderBy->nExpr; for(i=0; ia[i].pExpr; - if( pExpr->op!=TK_COLUMN || pExpr->iTable!=pSrc->iCursor ) break; + Expr *pE2; + + /* Skip over constant terms in the ORDER BY clause */ + if( sqlite3ExprIsConstant(pExpr) ){ + continue; + } + + /* Virtual tables are unable to deal with NULLS FIRST */ if( pOrderBy->a[i].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_GROUPBY|WHERE_DISTINCTBY)) ){ + eDistinct = 1 + ((pWInfo->wctrlFlags & WHERE_DISTINCTBY)!=0); + } } } @@ -146568,46 +153159,35 @@ */ pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo) + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm - + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden) ); + + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden) + + sizeof(sqlite3_value*)*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; pHidden->pWC = pWC; pHidden->pParse = pParse; + pHidden->eDistinct = eDistinct; + pHidden->mIn = 0; 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; + if( (pTerm->wtFlags & TERM_OK)==0 ) continue; + pIdxCons[j].iColumn = pTerm->u.x.leftColumn; pIdxCons[j].iTermOffset = i; op = pTerm->eOperator & WO_ALL; - if( op==WO_IN ) op = WO_EQ; + 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) ){ @@ -146640,18 +153220,43 @@ 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(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].sortFlags & KEYINFO_ORDER_DESC; + j++; } + pIdxInfo->nOrderBy = j; *pmNoOmit = mNoOmit; return pIdxInfo; } /* +** 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; + } + 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() ** method of the virtual table with the sqlite3_index_info object that @@ -146672,7 +153277,9 @@ int rc; whereTraceIndexInfoInputs(p); + pParse->db->nSchemaLock++; rc = pVtab->pModule->xBestIndex(pVtab, p); + pParse->db->nSchemaLock--; whereTraceIndexInfoOutputs(p); if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){ @@ -147366,10 +153973,11 @@ if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) 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 +153991,8 @@ 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); @@ -147414,7 +154022,7 @@ 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; + SrcItem *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, @@ -147445,9 +154053,9 @@ 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 ){ @@ -147518,7 +154126,7 @@ 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)); + memset(pTo, 0, WHERE_LOOP_XFER_SZ); return SQLITE_NOMEM_BKPT; } memcpy(pTo, pFrom, WHERE_LOOP_XFER_SZ); @@ -147547,7 +154155,8 @@ assert( pWInfo!=0 ); for(i=0; inLevel; i++){ WhereLevel *pLevel = &pWInfo->a[i]; - if( pLevel->pWLoop && (pLevel->pWLoop->wsFlags & WHERE_IN_ABLE) ){ + if( pLevel->pWLoop && (pLevel->pWLoop->wsFlags & WHERE_IN_ABLE)!=0 ){ + assert( (pLevel->pWLoop->wsFlags & WHERE_MULTI_OR)==0 ); sqlite3DbFree(db, pLevel->u.in.aInLoop); } } @@ -147561,10 +154170,22 @@ sqlite3DbFreeNN(db, pWInfo); } +/* Undo all Expr node modifications +*/ +static void whereUndoExprMods(WhereInfo *pWInfo){ + while( pWInfo->pExprMods ){ + WhereExprMod *p = pWInfo->pExprMods; + pWInfo->pExprMods = p->pNext; + memcpy(p->pExpr, &p->orig, sizeof(p->orig)); + sqlite3DbFree(pWInfo->pParse->db, p); + } +} + /* ** Return TRUE if all of the following are true: ** -** (1) X has the same or lower cost that Y +** (1) X has the same or lower cost, or returns the same or fewer rows, +** than 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 @@ -147587,11 +154208,8 @@ 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; 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 */ - } for(i=pX->nLTerm-1; i>=0; i--){ if( pX->aLTerm[i]==0 ) continue; for(j=pY->nLTerm-1; j>=0; j--){ @@ -147607,8 +154225,8 @@ } /* -** 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 +154247,20 @@ /* 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); } } } @@ -147893,11 +154515,11 @@ 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 +154527,13 @@ if( pX->iParent>=0 && (&pWC->a[pX->iParent])==pTerm ) break; } if( j<0 ){ + 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". */ + 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. */ @@ -147932,7 +154561,9 @@ } } } - if( pLoop->nOut > nRow-iReduce ) pLoop->nOut = nRow - iReduce; + if( pLoop->nOut > nRow-iReduce ){ + pLoop->nOut = nRow - iReduce; + } } /* @@ -147969,9 +154600,12 @@ 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; @@ -148025,7 +154659,7 @@ */ 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) */ ){ @@ -148051,9 +154685,9 @@ pNew = pBuilder->pNew; if( db->mallocFailed ) return SQLITE_NOMEM_BKPT; - WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d, nSkip=%d\n", + 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 ); @@ -148066,6 +154700,8 @@ 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; @@ -148130,7 +154766,7 @@ 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; nIn = 46; assert( 46==sqlite3LogEst(25) ); @@ -148147,8 +154783,8 @@ /* "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 +154802,30 @@ ** 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; @@ -148198,6 +154844,7 @@ 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) ){ @@ -148210,7 +154857,7 @@ pBtm = pTerm; pTop = 0; if( pTerm->wtFlags & TERM_LIKEOPT ){ - /* Range contraints that come from the LIKE optimization are + /* Range constraints that come from the LIKE optimization are ** always used in pairs. */ pTop = &pTerm[1]; assert( (pTop-(pTerm->pWC->a))pWC->nTerm ); @@ -148259,8 +154906,8 @@ 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; @@ -148341,6 +154988,8 @@ if( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 && pNew->u.btree.nEqnColumn + && (pNew->u.btree.nEqnKeyCol || + pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY) ){ whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn); } @@ -148421,6 +155070,7 @@ if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0; for(ii=0; iinExpr; ii++){ Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr); + if( NEVER(pExpr==0) ) continue; if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){ if( pExpr->iColumn<0 ) return 1; for(jj=0; jjnKeyCol; jj++){ @@ -148458,9 +155108,10 @@ for(i=0, pTerm=pWC->a; inTerm; i++, pTerm++){ Expr *pExpr; pExpr = pTerm->pExpr; - if( (!ExprHasProperty(pExpr, EP_FromJoin) || pExpr->iRightJoinTable==iTab) + if( (!ExprHasProperty(pExpr, EP_FromJoin) || pExpr->w.iRightJoinTable==iTab) && (isLeft==0 || ExprHasProperty(pExpr, EP_FromJoin)) && sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, iTab) + && (pTerm->wtFlags & TERM_VNULL)==0 ){ return 1; } @@ -148514,13 +155165,12 @@ 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 */ @@ -148532,9 +155182,10 @@ pWC = pBuilder->pWC; assert( !IsVirtual(pSrc->pTab) ); - 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{ @@ -148563,22 +155214,23 @@ 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->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. */ ){ /* 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,7 +155248,7 @@ ** 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; @@ -148620,7 +155272,7 @@ /* 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 @@ -148652,8 +155304,23 @@ /* 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); rc = whereLoopInsert(pBuilder, pNew); @@ -148744,6 +155411,15 @@ #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; +} + +/* ** Argument pIdxInfo is already populated with all constraints that may ** be used by the virtual table identified by pBuilder->pNew->iTab. This ** function marks a subset of those constraints usable, invokes the @@ -148770,9 +155446,11 @@ 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 +155458,7 @@ 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 ); @@ -148795,6 +155473,7 @@ pIdxCons->usable = 0; if( (pTerm->prereqRight & mUsable)==pTerm->prereqRight && (pTerm->eOperator & mExclude)==0 + && (pbRetryLimit || !isLimitTerm(pTerm)) ){ pIdxCons->usable = 1; } @@ -148810,6 +155489,7 @@ 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); @@ -148827,8 +155507,8 @@ 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; ieMatchOp==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 +155558,21 @@ pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE; *pbIn = 1; assert( (mExclude & WO_IN)==0 ); } + + if( isLimitTerm(pTerm) && *pbIn ){ + /* 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. Set output + ** variable *pbRetryLimit to true to tell the caller to retry with + ** LIMIT and OFFSET disabled. */ + if( pIdxInfo->needToFreeIdxStr ){ + sqlite3_free(pIdxInfo->idxStr); + pIdxInfo->idxStr = 0; + pIdxInfo->needToFreeIdxStr = 0; + } + *pbRetryLimit = 1; + return SQLITE_OK; + } } } @@ -148917,11 +155617,19 @@ } /* -** 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 constaint, 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]; @@ -148939,6 +155647,73 @@ } /* +** 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; /* EV: R-30545-25046 */ + }else{ + if( pH->aRhs[iCons]==0 ){ + WhereTerm *pTerm = &pH->pWC->a[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==1 + || pHidden->eDistinct==2 ); + return pHidden->eDistinct; +} + +/* ** Add all WhereLoop objects for a table of the join identified by ** pBuilder->pNew->iTab. That table is guaranteed to be a virtual table. ** @@ -148972,13 +155747,14 @@ 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; @@ -148987,8 +155763,7 @@ pNew = pBuilder->pNew; pSrc = &pWInfo->pTabList->a[pNew->iTab]; assert( IsVirtual(pSrc->pTab) ); - p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc, pBuilder->pOrderBy, - &mNoOmit); + p = allocateIndexInfo(pWInfo, pWC, mUnusable, pSrc, &mNoOmit); if( p==0 ) return SQLITE_NOMEM_BKPT; pNew->rSetup = 0; pNew->wsFlags = WHERE_VIRTUALTABLE; @@ -148996,14 +155771,22 @@ 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); + 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) @@ -149021,7 +155804,7 @@ if( bIn ){ WHERETRACE(0x40, (" 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 ){ @@ -149048,7 +155831,7 @@ WHERETRACE(0x40, (" VirtualOne: mPrev=%04llx mNext=%04llx\n", (sqlite3_uint64)mPrev, (sqlite3_uint64)mNext)); rc = whereLoopAddVirtualOne( - pBuilder, mPrereq, mNext|mPrereq, 0, p, mNoOmit, &bIn); + pBuilder, mPrereq, mNext|mPrereq, 0, p, mNoOmit, &bIn, 0); if( pNew->prereq==mPrereq ){ seenZero = 1; if( bIn==0 ) seenZeroNoIN = 1; @@ -149061,7 +155844,7 @@ if( rc==SQLITE_OK && seenZero==0 ){ WHERETRACE(0x40, (" 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; } @@ -149071,12 +155854,12 @@ if( rc==SQLITE_OK && seenZeroNoIN==0 ){ WHERETRACE(0x40, (" 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); + freeIndexInfo(pParse->db, p); WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pTab->zName, rc)); return rc; } @@ -149100,7 +155883,7 @@ WhereClause tempWC; WhereLoopBuilder sSubBuild; WhereOrSet sSum, sCur; - struct SrcList_item *pItem; + SrcItem *pItem; pWC = pBuilder->pWC; pWCEnd = pWC->a + pWC->nTerm; @@ -149120,7 +155903,6 @@ int i, j; sSubBuild = *pBuilder; - sSubBuild.pOrderBy = 0; sSubBuild.pOrSet = &sCur; WHERETRACE(0x200, ("Begin processing OR-clause %p\n", pTerm)); @@ -149132,6 +155914,7 @@ tempWC.pOuter = pWC; tempWC.op = TK_AND; tempWC.nTerm = 1; + tempWC.nBase = 1; tempWC.a = pOrTerm; sSubBuild.pWC = &tempWC; }else{ @@ -149156,7 +155939,9 @@ if( rc==SQLITE_OK ){ rc = whereLoopAddOr(&sSubBuild, mPrereq, mUnusable); } - assert( rc==SQLITE_OK || rc==SQLITE_DONE || sCur.n==0 ); + assert( rc==SQLITE_OK || rc==SQLITE_DONE || sCur.n==0 + || rc==SQLITE_NOMEM ); + testcase( rc==SQLITE_NOMEM && sCur.n>0 ); testcase( rc==SQLITE_DONE ); if( sCur.n==0 ){ sSum.n = 0; @@ -149216,8 +156001,8 @@ 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; WhereLoop *pNew; @@ -149240,7 +156025,7 @@ } #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pItem->pTab) ){ - struct SrcList_item *p; + SrcItem *p; for(p=&pItem[1]; pfg.jointype & (JT_LEFT|JT_CROSS)) ){ mUnusable |= sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor); @@ -149384,7 +156169,8 @@ 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); @@ -149424,6 +156210,10 @@ 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 isOrderDistint 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,14 +156281,18 @@ } /* 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 @@ -149510,9 +156304,10 @@ pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[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{ @@ -149591,7 +156386,7 @@ if( obSat==obDone ) return (i8)nOrderBy; if( !isOrderDistinct ){ for(i=nOrderBy-1; i>0; i--){ - Bitmask m = MASKBIT(i) - 1; + Bitmask m = ALWAYS(i0 && 66==sqlite3LogEst(100) ); rScale = sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66; rSortCost = nRow + rScale + 16; /* Multiple by log(M) where M is the number of output rows. - ** Use the LIMIT for M if it is smaller */ + ** 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 && 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; @@ -150085,7 +156888,7 @@ */ static int whereShortCut(WhereLoopBuilder *pBuilder){ WhereInfo *pWInfo; - struct SrcList_item *pItem; + SrcItem *pItem; WhereClause *pWC; WhereTerm *pTerm; WhereLoop *pLoop; @@ -150093,6 +156896,7 @@ int j; Table *pTab; Index *pIdx; + WhereScan scan; pWInfo = pBuilder->pWInfo; if( pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE ) return 0; @@ -150106,7 +156910,8 @@ 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 +156930,8 @@ ) 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,9 +156960,15 @@ 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 ){ + sqlite3DebugPrintf("whereShortCut() used to compute solution\n"); + } +#endif return 1; } return 0; @@ -150210,6 +157022,150 @@ # 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. +** +** 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; + + /* 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); + } + for(i=pWInfo->nLevel-1; i>=1; i--){ + WhereTerm *pTerm, *pEnd; + SrcItem *pItem; + WhereLoop *pLoop; + pLoop = pWInfo->a[i].pWLoop; + pItem = &pWInfo->pTabList->a[pLoop->iTab]; + if( (pItem->fg.jointype & JT_LEFT)==0 ) 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_FromJoin) + || pTerm->pExpr->w.iRightJoinTable!=pItem->iCursor + ){ + break; + } + } + } + if( pTerm drop loop %c not used\n", pLoop->cId)); + 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; + + assert( pWInfo->nLevel>=2 ); + assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_BloomFilter) ); + nSearch = pWInfo->a[0].pWLoop->nOut; + for(i=1; inLevel; i++){ + WhereLoop *pLoop = pWInfo->a[i].pWLoop; + const unsigned int reqFlags = (WHERE_SELFCULL|WHERE_COLUMN_EQ); + if( (pLoop->wsFlags & reqFlags)==reqFlags + /* vvvvvv--- Always the case if WHERE_COLUMN_EQ is defined */ + && ALWAYS((pLoop->wsFlags & (WHERE_IPK|WHERE_INDEXED))!=0) + ){ + SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab]; + Table *pTab = pItem->pTab; + pTab->tabFlags |= TF_StatsUsed; + if( nSearch > pTab->nRowLogEst + && (pTab->tabFlags & TF_HasStat1)!=0 + ){ + testcase( pItem->fg.jointype & JT_LEFT ); + pLoop->wsFlags |= WHERE_BLOOMFILTER; + pLoop->wsFlags &= ~WHERE_IDX_ONLY; + WHERETRACE(0xffff, ( + "-> 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; + } +} + /* ** Generate the beginning of the loop used for WHERE clause processing. ** The return value is a pointer to an opaque structure that contains @@ -150304,6 +157260,7 @@ 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 *pLimit, /* Use this LIMIT/OFFSET clause, if any */ 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 */ @@ -150338,13 +157295,6 @@ /* 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) ){ - wctrlFlags &= ~WHERE_WANT_DISTINCT; - } /* The number of tables in the FROM clause is limited by the number of ** bits in a Bitmask @@ -150387,11 +157337,18 @@ pWInfo->wctrlFlags = wctrlFlags; pWInfo->iLimit = iAuxArg; pWInfo->savedNQueryLoop = pParse->nQueryLoop; +#ifndef SQLITE_OMIT_VIRTUALTABLE + pWInfo->pLimit = pLimit; +#endif 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 +157361,6 @@ /* 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,7 +157368,9 @@ */ 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")); @@ -150450,6 +157408,7 @@ /* Analyze all of the subexpressions. */ sqlite3WhereExprAnalyze(pTabList, &pWInfo->sWC); + sqlite3WhereAddLimit(&pWInfo->sWC, pLimit); if( db->mallocFailed ) goto whereBeginError; /* Special case: WHERE terms that do not refer to any tables in the join @@ -150463,7 +157422,7 @@ ** FROM ... WHERE random()>0; -- eval random() once per row ** FROM ... WHERE (SELECT random())>0; -- eval random() once overall */ - for(ii=0; iinTerm; ii++){ + for(ii=0; iinBase; ii++){ WhereTerm *pT = &sWLB.pWC->a[ii]; if( pT->wtFlags & TERM_VIRTUAL ) continue; if( pT->prereqAll==0 && (nTabList==0 || exprIsDeterministic(pT->pExpr)) ){ @@ -150473,7 +157432,12 @@ } 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 ){ @@ -150544,9 +157508,10 @@ if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){ pWInfo->revMask = ALLBITS; } - 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); @@ -150574,83 +157539,36 @@ } #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: + /* Attempt to omit tables from a join that do not affect the result. + ** See the comment on whereOmitNoopJoin() for further information. ** - ** 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: - ** - ** 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 - && pResultSet!=0 /* guarantees condition (1) above */ + && pResultSet!=0 /* these two combine to guarantee */ + && 0==(wctrlFlags & WHERE_AGG_DISTINCT) /* condition (1) above */ && OptimizationEnabled(db, SQLITE_OmitNoopJoin) ){ - 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--; - } + 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 + && OptimizationEnabled(db, SQLITE_BloomFilter) + ){ + whereCheckIfBloomFilterIsUseful(pWInfo); } + #if defined(WHERETRACE_ENABLED) if( sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */ sqlite3DebugPrintf("---- WHERE clause at end of analysis:\n"); @@ -150705,13 +157623,13 @@ for(ii=0, pLevel=pWInfo->a; iia[pLevel->iFrom]; pTab = pTabItem->pTab; 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 @@ -150737,6 +157655,7 @@ 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 @@ -150800,6 +157719,7 @@ 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 ){ @@ -150835,15 +157755,20 @@ for(ii=0; iinErr ) goto whereBeginError; pLevel = &pWInfo->a[ii]; wsFlags = pLevel->pWLoop->wsFlags; + 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, + &pTabList->a[pLevel->iFrom], notReady, pLevel); +#endif + }else{ + sqlite3ConstructBloomFilter(pWInfo, ii, pLevel, notReady); + } if( db->mallocFailed ) goto whereBeginError; } -#endif addrExplain = sqlite3WhereExplainOneScan( pParse, pTabList, pLevel, wctrlFlags ); @@ -150857,11 +157782,14 @@ /* Done. */ VdbeModuleComment((v, "Begin WHERE-core")); + pWInfo->iEndWhere = sqlite3VdbeCurrentAddr(v); return pWInfo; /* Jump here if malloc fails */ whereBeginError: if( pWInfo ){ + testcase( pWInfo->pExprMods!=0 ); + whereUndoExprMods(pWInfo); pParse->nQueryLoop = pWInfo->savedNQueryLoop; whereInfoFree(db, pWInfo); } @@ -150900,6 +157828,7 @@ WhereLoop *pLoop; SrcList *pTabList = pWInfo->pTabList; sqlite3 *db = pParse->db; + int iEnd = sqlite3VdbeCurrentAddr(v); /* Generate loop termination code. */ @@ -150952,15 +157881,19 @@ }else{ 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 +157904,19 @@ ** 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); @@ -151014,8 +157950,14 @@ 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 ){ @@ -151037,9 +157979,9 @@ 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]; + SrcItem *pTabItem = &pTabList->a[pLevel->iFrom]; Table *pTab = pTabItem->pTab; assert( pTab!=0 ); pLoop = pLevel->pWLoop; @@ -151062,7 +158004,7 @@ ** created for the ONEPASS optimization. */ if( (pTab->tabFlags & TF_Ephemeral)==0 - && pTab->pSelect==0 + && !IsView(pTab) && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ){ int ws = pLoop->wsFlags; @@ -151092,23 +158034,34 @@ 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; + } + k = pLevel->addrBody + 1; #ifdef SQLITE_DEBUG if( db->flags & SQLITE_VdbeAddopTrace ){ printf("TRANSLATE opcodes in range %d..%d\n", 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 @@ -151139,23 +158092,19 @@ 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); - } - /* Final cleanup */ + if( pWInfo->pExprMods ) whereUndoExprMods(pWInfo); pParse->nQueryLoop = pWInfo->savedNQueryLoop; whereInfoFree(db, pWInfo); return; @@ -151746,7 +158695,7 @@ /* 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 +158703,7 @@ /* 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 +158712,7 @@ ** 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} \ } @@ -151953,6 +158902,7 @@ 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,14 +159012,17 @@ int i; int nInit = pList ? pList->nExpr : 0; for(i=0; inExpr; i++){ - Expr *pDup = sqlite3ExprDup(pParse->db, pAppend->a[i].pExpr, 0); + sqlite3 *db = pParse->db; + Expr *pDup = sqlite3ExprDup(db, pAppend->a[i].pExpr, 0); assert( pDup==0 || !ExprHasProperty(pDup, EP_MemToken) ); - if( bIntToNull && pDup ){ + 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 ); - } + pSub = sqlite3ExprSkipCollateAndLikely(pDup); if( sqlite3ExprIsInteger(pSub, &iDummy) ){ pSub->op = TK_NULL; pSub->flags &= ~(EP_IntValue|EP_IsTrue|EP_IsFalse); @@ -152100,6 +159053,15 @@ 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 +159071,7 @@ */ 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) ){ Vdbe *v = sqlite3GetVdbe(pParse); sqlite3 *db = pParse->db; Select *pSub = 0; /* The subquery */ @@ -152133,6 +159095,11 @@ } 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; @@ -152177,7 +159144,9 @@ ** window function - one for the accumulator, another for interim ** results. */ for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - ExprList *pArgs = pWin->pOwner->x.pList; + ExprList *pArgs; + assert( ExprUseXList(pWin->pOwner) ); + pArgs = pWin->pOwner->x.pList; if( pWin->pFunc->funcFlags & SQLITE_FUNC_SUBTYPE ){ selectWindowRewriteEList(pParse, pMWin, pSrc, pArgs, pTab, &pSublist); pWin->iArgCol = (pSublist ? pSublist->nExpr : 0); @@ -152214,11 +159183,14 @@ ("New window-function subquery in FROM clause of (%u/%p)\n", p->selId, p)); p->pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); + assert( pSub!=0 || p->pSrc==0 ); /* Due to db->mallocFailed test inside + ** of sqlite3DbMallocRawNN() called from + ** sqlite3SrcListAppend() */ if( p->pSrc ){ Table *pTab2; p->pSrc->a[0].pSelect = pSub; 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 ){ @@ -152241,15 +159213,14 @@ 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; } @@ -152469,15 +159440,19 @@ ** 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 +159461,12 @@ ** 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; @@ -152558,8 +159538,11 @@ ** 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; @@ -152630,6 +159613,7 @@ 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 +159630,9 @@ ** 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); } @@ -152724,6 +159710,7 @@ 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; @@ -152830,6 +159817,7 @@ int addrIf = 0; 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); @@ -152840,15 +159828,16 @@ } 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++){ + for(iEnd=sqlite3VdbeCurrentAddr(v); iOpopcode==OP_Column && pOp->p1==pWin->iEphCsr ){ pOp->p1 = csr; } @@ -152857,6 +159846,7 @@ 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); } @@ -153042,6 +160032,7 @@ for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ FuncDef *pFunc = pWin->pFunc; + assert( ExprUseXList(pWin->pOwner) ); if( pFunc->zName==nth_valueName || pFunc->zName==first_valueName ){ @@ -153207,7 +160198,7 @@ ** 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,6 +160217,12 @@ 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 ); @@ -153238,34 +160235,11 @@ 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 @@ -153302,21 +160276,46 @@ 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); + sqlite3VdbeChangeP2(v, -1, addrDone); } } + /* 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 +160391,24 @@ /* 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 ); @@ -153898,7 +160905,6 @@ 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" */ @@ -153970,7 +160976,7 @@ 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 +161032,9 @@ } /* 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 */ @@ -154146,6 +161152,7 @@ sqlite3VdbeJumpHere(v, addrGosubFlush); } + s.regRowid = 0; addrEmpty = sqlite3VdbeAddOp1(v, OP_Rewind, csrWrite); VdbeCoverage(v); if( pMWin->eEnd==TK_PRECEDING ){ @@ -154208,8 +161215,10 @@ /************** 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,22 +161228,15 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** Driver template for the LEMON parser generator. -** -** 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. +** This file contains SQLite's SQL parser. ** -** 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 grammer. 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" */ @@ -154327,11 +161329,21 @@ 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 && @@ -154342,11 +161354,21 @@ } } - - /* 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. + /* 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 token */ static Expr *tokenExpr(Parse *pParse, int op, Token t){ Expr *p = sqlite3DbMallocRawNN(pParse->db, sizeof(Expr)+t.n+1); if( p ){ @@ -154357,15 +161379,16 @@ ExprClearVVAProperties(p); p->iAgg = -1; 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 +161441,195 @@ # 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_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_MATERIALIZED 97 +#define TK_REINDEX 98 +#define TK_RENAME 99 +#define TK_CTIME_KW 100 +#define TK_ANY 101 +#define TK_BITAND 102 +#define TK_BITOR 103 +#define TK_LSHIFT 104 +#define TK_RSHIFT 105 +#define TK_PLUS 106 +#define TK_MINUS 107 +#define TK_STAR 108 +#define TK_SLASH 109 +#define TK_REM 110 +#define TK_CONCAT 111 +#define TK_PTR 112 +#define TK_COLLATE 113 +#define TK_BITNOT 114 +#define TK_ON 115 +#define TK_INDEXED 116 +#define TK_STRING 117 +#define TK_JOIN_KW 118 +#define TK_CONSTRAINT 119 +#define TK_DEFAULT 120 +#define TK_NULL 121 +#define TK_PRIMARY 122 +#define TK_UNIQUE 123 +#define TK_CHECK 124 +#define TK_REFERENCES 125 +#define TK_AUTOINCR 126 +#define TK_INSERT 127 +#define TK_DELETE 128 +#define TK_UPDATE 129 +#define TK_SET 130 +#define TK_DEFERRABLE 131 +#define TK_FOREIGN 132 +#define TK_DROP 133 +#define TK_UNION 134 +#define TK_ALL 135 +#define TK_EXCEPT 136 +#define TK_INTERSECT 137 +#define TK_SELECT 138 +#define TK_VALUES 139 +#define TK_DISTINCT 140 +#define TK_DOT 141 +#define TK_FROM 142 +#define TK_JOIN 143 +#define TK_USING 144 +#define TK_ORDER 145 +#define TK_GROUP 146 +#define TK_HAVING 147 +#define TK_LIMIT 148 +#define TK_WHERE 149 +#define TK_RETURNING 150 +#define TK_INTO 151 +#define TK_NOTHING 152 +#define TK_FLOAT 153 +#define TK_BLOB 154 +#define TK_INTEGER 155 +#define TK_VARIABLE 156 +#define TK_CASE 157 +#define TK_WHEN 158 +#define TK_THEN 159 +#define TK_ELSE 160 +#define TK_INDEX 161 +#define TK_ALTER 162 +#define TK_ADD 163 +#define TK_WINDOW 164 +#define TK_OVER 165 +#define TK_FILTER 166 +#define TK_COLUMN 167 +#define TK_AGG_FUNCTION 168 +#define TK_AGG_COLUMN 169 +#define TK_TRUEFALSE 170 +#define TK_ISNOT 171 +#define TK_FUNCTION 172 +#define TK_UMINUS 173 +#define TK_UPLUS 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_SPACE 183 +#define TK_ILLEGAL 184 +#endif +/**************** End token definitions ***************************************/ /* The next sections is a series of control #defines. ** various aspects of the generated parser. @@ -154480,28 +161687,30 @@ #endif /************* Begin control #defines *****************************************/ #define YYCODETYPE unsigned short int -#define YYNOCODE 310 +#define YYNOCODE 319 #define YYACTIONTYPE unsigned short int -#define YYWILDCARD 100 +#define YYWILDCARD 101 #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; + TriggerStep* yy33; + Window* yy41; + Select* yy47; + SrcList* yy131; + struct TrigEvent yy180; + struct {int value; int mask;} yy231; + IdList* yy254; + u32 yy285; + ExprList* yy322; + Cte* yy385; + int yy394; + Upsert* yy444; + u8 yy516; + With* yy521; + const char* yy522; + Expr* yy528; + struct FrameBound yy595; } YYMINORTYPE; #ifndef YYSTACKDEPTH #define YYSTACKDEPTH 100 @@ -154517,18 +161726,18 @@ #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 574 +#define YYNRULE 402 +#define YYNRULE_WITH_ACTION 340 +#define YYNTOKEN 185 +#define YY_MAX_SHIFT 573 +#define YY_MIN_SHIFTREDUCE 831 +#define YY_MAX_SHIFTREDUCE 1232 +#define YY_ERROR_ACTION 1233 +#define YY_ACCEPT_ACTION 1234 +#define YY_NO_ACTION 1235 +#define YY_MIN_REDUCE 1236 +#define YY_MAX_REDUCE 1637 /************* End control #defines *******************************************/ #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) @@ -154595,586 +161804,612 @@ ** yy_default[] Default action for each state. ** *********** Begin parsing tables **********************************************/ -#define YY_ACTTAB_COUNT (1962) +#define YY_ACTTAB_COUNT (2070) 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 */ 566, 1307, 566, 1286, 201, 201, 566, 116, 112, 222, + /* 10 */ 566, 1307, 377, 566, 116, 112, 222, 397, 408, 409, + /* 20 */ 1260, 378, 1269, 41, 41, 41, 41, 1412, 1517, 71, + /* 30 */ 71, 967, 1258, 41, 41, 491, 71, 71, 272, 968, + /* 40 */ 298, 476, 298, 123, 124, 114, 1210, 1210, 1044, 1047, + /* 50 */ 1036, 1036, 121, 121, 122, 122, 122, 122, 543, 409, + /* 60 */ 1234, 1, 1, 573, 2, 1238, 548, 116, 112, 222, + /* 70 */ 309, 480, 142, 548, 1272, 524, 116, 112, 222, 1320, + /* 80 */ 417, 523, 547, 123, 124, 114, 1210, 1210, 1044, 1047, + /* 90 */ 1036, 1036, 121, 121, 122, 122, 122, 122, 424, 116, + /* 100 */ 112, 222, 120, 120, 120, 120, 119, 119, 118, 118, + /* 110 */ 118, 117, 113, 444, 277, 277, 277, 277, 560, 560, + /* 120 */ 560, 1558, 376, 1560, 1186, 375, 1157, 563, 1157, 563, + /* 130 */ 409, 1558, 537, 252, 219, 1553, 99, 141, 449, 6, + /* 140 */ 365, 233, 120, 120, 120, 120, 119, 119, 118, 118, + /* 150 */ 118, 117, 113, 444, 123, 124, 114, 1210, 1210, 1044, + /* 160 */ 1047, 1036, 1036, 121, 121, 122, 122, 122, 122, 138, + /* 170 */ 289, 1186, 1546, 448, 118, 118, 118, 117, 113, 444, + /* 180 */ 125, 1186, 1187, 1188, 144, 465, 334, 566, 150, 127, + /* 190 */ 444, 122, 122, 122, 122, 115, 120, 120, 120, 120, + /* 200 */ 119, 119, 118, 118, 118, 117, 113, 444, 454, 419, + /* 210 */ 13, 13, 215, 120, 120, 120, 120, 119, 119, 118, + /* 220 */ 118, 118, 117, 113, 444, 422, 308, 557, 1186, 1187, + /* 230 */ 1188, 441, 440, 409, 1271, 122, 122, 122, 122, 120, + /* 240 */ 120, 120, 120, 119, 119, 118, 118, 118, 117, 113, + /* 250 */ 444, 1543, 98, 1033, 1033, 1045, 1048, 123, 124, 114, + /* 260 */ 1210, 1210, 1044, 1047, 1036, 1036, 121, 121, 122, 122, + /* 270 */ 122, 122, 566, 406, 405, 1186, 566, 409, 1217, 319, + /* 280 */ 1217, 80, 81, 120, 120, 120, 120, 119, 119, 118, + /* 290 */ 118, 118, 117, 113, 444, 70, 70, 1186, 1604, 71, + /* 300 */ 71, 123, 124, 114, 1210, 1210, 1044, 1047, 1036, 1036, + /* 310 */ 121, 121, 122, 122, 122, 122, 120, 120, 120, 120, + /* 320 */ 119, 119, 118, 118, 118, 117, 113, 444, 1037, 210, + /* 330 */ 1186, 365, 1186, 1187, 1188, 245, 548, 399, 504, 501, + /* 340 */ 500, 108, 558, 138, 4, 516, 933, 433, 499, 217, + /* 350 */ 514, 522, 352, 879, 1186, 1187, 1188, 383, 561, 566, + /* 360 */ 120, 120, 120, 120, 119, 119, 118, 118, 118, 117, + /* 370 */ 113, 444, 277, 277, 16, 16, 1598, 441, 440, 153, + /* 380 */ 409, 445, 13, 13, 1279, 563, 1214, 1186, 1187, 1188, + /* 390 */ 1003, 1216, 264, 555, 1574, 186, 566, 427, 138, 1215, + /* 400 */ 308, 557, 472, 138, 123, 124, 114, 1210, 1210, 1044, + /* 410 */ 1047, 1036, 1036, 121, 121, 122, 122, 122, 122, 55, + /* 420 */ 55, 413, 1023, 507, 1217, 1186, 1217, 474, 106, 106, + /* 430 */ 1312, 1312, 1186, 171, 566, 384, 107, 380, 445, 568, + /* 440 */ 567, 430, 1543, 1013, 332, 549, 565, 263, 280, 360, + /* 450 */ 510, 355, 509, 250, 491, 308, 557, 71, 71, 351, + /* 460 */ 308, 557, 374, 120, 120, 120, 120, 119, 119, 118, + /* 470 */ 118, 118, 117, 113, 444, 1013, 1013, 1015, 1016, 27, + /* 480 */ 277, 277, 1186, 1187, 1188, 1152, 566, 528, 409, 1186, + /* 490 */ 1187, 1188, 348, 563, 548, 1260, 533, 517, 1152, 1516, + /* 500 */ 317, 1152, 285, 550, 485, 569, 566, 569, 482, 51, + /* 510 */ 51, 207, 123, 124, 114, 1210, 1210, 1044, 1047, 1036, + /* 520 */ 1036, 121, 121, 122, 122, 122, 122, 171, 1412, 13, + /* 530 */ 13, 409, 277, 277, 1186, 505, 119, 119, 118, 118, + /* 540 */ 118, 117, 113, 444, 429, 563, 518, 220, 515, 1552, + /* 550 */ 365, 546, 1186, 6, 532, 123, 124, 114, 1210, 1210, + /* 560 */ 1044, 1047, 1036, 1036, 121, 121, 122, 122, 122, 122, + /* 570 */ 145, 120, 120, 120, 120, 119, 119, 118, 118, 118, + /* 580 */ 117, 113, 444, 245, 566, 474, 504, 501, 500, 566, + /* 590 */ 1481, 1186, 1187, 1188, 1310, 1310, 499, 1186, 149, 425, + /* 600 */ 1186, 480, 409, 274, 365, 952, 872, 56, 56, 1186, + /* 610 */ 1187, 1188, 71, 71, 120, 120, 120, 120, 119, 119, + /* 620 */ 118, 118, 118, 117, 113, 444, 123, 124, 114, 1210, + /* 630 */ 1210, 1044, 1047, 1036, 1036, 121, 121, 122, 122, 122, + /* 640 */ 122, 409, 541, 1552, 83, 865, 98, 6, 928, 529, + /* 650 */ 848, 543, 151, 927, 1186, 1187, 1188, 1186, 1187, 1188, + /* 660 */ 290, 1543, 187, 1633, 395, 123, 124, 114, 1210, 1210, + /* 670 */ 1044, 1047, 1036, 1036, 121, 121, 122, 122, 122, 122, + /* 680 */ 566, 954, 566, 453, 953, 120, 120, 120, 120, 119, + /* 690 */ 119, 118, 118, 118, 117, 113, 444, 1152, 221, 1186, + /* 700 */ 331, 453, 452, 13, 13, 13, 13, 1003, 365, 463, + /* 710 */ 1152, 193, 409, 1152, 382, 1543, 1170, 32, 297, 474, + /* 720 */ 195, 1527, 5, 952, 120, 120, 120, 120, 119, 119, + /* 730 */ 118, 118, 118, 117, 113, 444, 123, 124, 114, 1210, + /* 740 */ 1210, 1044, 1047, 1036, 1036, 121, 121, 122, 122, 122, + /* 750 */ 122, 409, 1067, 419, 1186, 1024, 1186, 1187, 1188, 1186, + /* 760 */ 419, 332, 460, 320, 544, 1545, 442, 442, 442, 566, + /* 770 */ 3, 117, 113, 444, 453, 123, 124, 114, 1210, 1210, + /* 780 */ 1044, 1047, 1036, 1036, 121, 121, 122, 122, 122, 122, + /* 790 */ 1473, 566, 15, 15, 293, 120, 120, 120, 120, 119, + /* 800 */ 119, 118, 118, 118, 117, 113, 444, 1186, 566, 1486, + /* 810 */ 1412, 1186, 1187, 1188, 13, 13, 1186, 1187, 1188, 1544, + /* 820 */ 271, 271, 409, 286, 308, 557, 1008, 1486, 1488, 196, + /* 830 */ 288, 71, 71, 563, 120, 120, 120, 120, 119, 119, + /* 840 */ 118, 118, 118, 117, 113, 444, 123, 124, 114, 1210, + /* 850 */ 1210, 1044, 1047, 1036, 1036, 121, 121, 122, 122, 122, + /* 860 */ 122, 409, 201, 1087, 1186, 1187, 1188, 1324, 304, 1529, + /* 870 */ 388, 278, 278, 450, 564, 402, 922, 922, 566, 563, + /* 880 */ 566, 426, 491, 480, 563, 123, 124, 114, 1210, 1210, + /* 890 */ 1044, 1047, 1036, 1036, 121, 121, 122, 122, 122, 122, + /* 900 */ 1486, 71, 71, 13, 13, 120, 120, 120, 120, 119, + /* 910 */ 119, 118, 118, 118, 117, 113, 444, 566, 545, 566, + /* 920 */ 1577, 573, 2, 1238, 1092, 1092, 488, 1480, 309, 1525, + /* 930 */ 142, 324, 409, 836, 837, 838, 312, 1320, 305, 363, + /* 940 */ 43, 43, 57, 57, 120, 120, 120, 120, 119, 119, + /* 950 */ 118, 118, 118, 117, 113, 444, 123, 124, 114, 1210, + /* 960 */ 1210, 1044, 1047, 1036, 1036, 121, 121, 122, 122, 122, + /* 970 */ 122, 12, 277, 277, 566, 1152, 409, 572, 428, 1238, + /* 980 */ 465, 334, 296, 474, 309, 563, 142, 249, 1152, 308, + /* 990 */ 557, 1152, 321, 1320, 323, 491, 455, 71, 71, 233, + /* 1000 */ 283, 101, 114, 1210, 1210, 1044, 1047, 1036, 1036, 121, + /* 1010 */ 121, 122, 122, 122, 122, 120, 120, 120, 120, 119, + /* 1020 */ 119, 118, 118, 118, 117, 113, 444, 1108, 277, 277, + /* 1030 */ 1412, 448, 394, 1230, 439, 277, 277, 248, 247, 246, + /* 1040 */ 1319, 563, 1109, 313, 198, 294, 491, 1318, 563, 464, + /* 1050 */ 566, 1427, 394, 1130, 1023, 233, 414, 1110, 295, 120, + /* 1060 */ 120, 120, 120, 119, 119, 118, 118, 118, 117, 113, + /* 1070 */ 444, 1014, 104, 71, 71, 1013, 322, 496, 908, 566, + /* 1080 */ 277, 277, 277, 277, 1108, 1261, 415, 448, 909, 361, + /* 1090 */ 1571, 1315, 409, 563, 952, 563, 9, 202, 255, 1109, + /* 1100 */ 316, 487, 44, 44, 249, 559, 415, 1013, 1013, 1015, + /* 1110 */ 443, 1231, 409, 1603, 1110, 897, 123, 124, 114, 1210, + /* 1120 */ 1210, 1044, 1047, 1036, 1036, 121, 121, 122, 122, 122, + /* 1130 */ 122, 1231, 409, 1207, 215, 554, 123, 124, 114, 1210, + /* 1140 */ 1210, 1044, 1047, 1036, 1036, 121, 121, 122, 122, 122, + /* 1150 */ 122, 1131, 1631, 470, 1631, 255, 123, 111, 114, 1210, + /* 1160 */ 1210, 1044, 1047, 1036, 1036, 121, 121, 122, 122, 122, + /* 1170 */ 122, 1131, 1632, 414, 1632, 120, 120, 120, 120, 119, + /* 1180 */ 119, 118, 118, 118, 117, 113, 444, 221, 209, 351, + /* 1190 */ 1207, 1207, 147, 1426, 491, 120, 120, 120, 120, 119, + /* 1200 */ 119, 118, 118, 118, 117, 113, 444, 1256, 539, 519, + /* 1210 */ 888, 551, 952, 12, 566, 120, 120, 120, 120, 119, + /* 1220 */ 119, 118, 118, 118, 117, 113, 444, 538, 566, 860, + /* 1230 */ 1129, 361, 1571, 346, 1356, 409, 1163, 58, 58, 339, + /* 1240 */ 1355, 508, 277, 277, 277, 277, 277, 277, 1207, 889, + /* 1250 */ 1129, 59, 59, 459, 363, 563, 566, 563, 96, 563, + /* 1260 */ 124, 114, 1210, 1210, 1044, 1047, 1036, 1036, 121, 121, + /* 1270 */ 122, 122, 122, 122, 566, 1412, 566, 281, 1186, 60, + /* 1280 */ 60, 110, 392, 392, 391, 266, 389, 860, 1163, 845, + /* 1290 */ 566, 481, 566, 436, 341, 1152, 344, 61, 61, 62, + /* 1300 */ 62, 967, 227, 1550, 315, 431, 540, 6, 1152, 968, + /* 1310 */ 566, 1152, 314, 45, 45, 46, 46, 512, 120, 120, + /* 1320 */ 120, 120, 119, 119, 118, 118, 118, 117, 113, 444, + /* 1330 */ 416, 173, 1532, 47, 47, 1186, 1187, 1188, 108, 558, + /* 1340 */ 325, 4, 229, 1551, 928, 566, 437, 6, 566, 927, + /* 1350 */ 164, 566, 1290, 137, 1190, 561, 566, 1549, 566, 1089, + /* 1360 */ 566, 6, 566, 1089, 531, 566, 868, 8, 49, 49, + /* 1370 */ 228, 50, 50, 566, 63, 63, 566, 457, 445, 64, + /* 1380 */ 64, 65, 65, 14, 14, 66, 66, 407, 129, 129, + /* 1390 */ 555, 566, 458, 566, 1505, 486, 67, 67, 566, 52, + /* 1400 */ 52, 546, 407, 467, 535, 410, 226, 1023, 566, 534, + /* 1410 */ 308, 557, 1190, 407, 68, 68, 69, 69, 566, 1023, + /* 1420 */ 566, 53, 53, 868, 1014, 106, 106, 525, 1013, 566, + /* 1430 */ 1504, 159, 159, 107, 451, 445, 568, 567, 471, 307, + /* 1440 */ 1013, 160, 160, 76, 76, 566, 1548, 466, 407, 407, + /* 1450 */ 6, 1225, 54, 54, 478, 276, 219, 566, 887, 886, + /* 1460 */ 1013, 1013, 1015, 84, 206, 1206, 230, 282, 72, 72, + /* 1470 */ 329, 483, 1013, 1013, 1015, 1016, 27, 1576, 1174, 447, + /* 1480 */ 130, 130, 281, 148, 105, 38, 103, 392, 392, 391, + /* 1490 */ 266, 389, 566, 1126, 845, 396, 566, 108, 558, 566, + /* 1500 */ 4, 311, 566, 30, 17, 566, 279, 227, 566, 315, + /* 1510 */ 108, 558, 468, 4, 561, 73, 73, 314, 566, 157, + /* 1520 */ 157, 566, 131, 131, 526, 132, 132, 561, 128, 128, + /* 1530 */ 566, 158, 158, 566, 31, 291, 566, 445, 330, 521, + /* 1540 */ 98, 152, 152, 420, 136, 136, 1005, 229, 254, 555, + /* 1550 */ 445, 479, 336, 135, 135, 164, 133, 133, 137, 134, + /* 1560 */ 134, 875, 555, 535, 566, 473, 566, 254, 536, 475, + /* 1570 */ 335, 254, 98, 894, 895, 228, 535, 566, 1023, 566, + /* 1580 */ 1074, 534, 210, 232, 106, 106, 1352, 75, 75, 77, + /* 1590 */ 77, 1023, 107, 340, 445, 568, 567, 106, 106, 1013, + /* 1600 */ 74, 74, 42, 42, 566, 107, 343, 445, 568, 567, + /* 1610 */ 410, 497, 1013, 251, 359, 308, 557, 1135, 349, 875, + /* 1620 */ 98, 1070, 345, 251, 358, 1591, 347, 48, 48, 1017, + /* 1630 */ 1303, 1013, 1013, 1015, 1016, 27, 1289, 1287, 1074, 451, + /* 1640 */ 961, 925, 254, 110, 1013, 1013, 1015, 1016, 27, 1174, + /* 1650 */ 447, 970, 971, 281, 108, 558, 1288, 4, 392, 392, + /* 1660 */ 391, 266, 389, 1343, 1086, 845, 1086, 1085, 858, 1085, + /* 1670 */ 146, 561, 926, 354, 110, 303, 364, 553, 227, 1364, + /* 1680 */ 315, 108, 558, 1411, 4, 1339, 492, 1017, 314, 1350, + /* 1690 */ 1565, 552, 1417, 1268, 445, 204, 1259, 1247, 561, 1246, + /* 1700 */ 1248, 1584, 269, 1336, 367, 369, 555, 371, 11, 212, + /* 1710 */ 393, 225, 1393, 284, 1398, 456, 287, 327, 229, 328, + /* 1720 */ 292, 445, 1386, 216, 333, 1403, 164, 477, 373, 137, + /* 1730 */ 1402, 400, 502, 555, 1286, 1023, 357, 1477, 199, 1587, + /* 1740 */ 211, 106, 106, 932, 1476, 1225, 228, 556, 175, 107, + /* 1750 */ 200, 445, 568, 567, 258, 387, 1013, 1524, 1522, 223, + /* 1760 */ 1222, 418, 1023, 83, 208, 79, 82, 184, 106, 106, + /* 1770 */ 1482, 169, 177, 461, 179, 462, 107, 1399, 445, 568, + /* 1780 */ 567, 410, 180, 1013, 495, 181, 308, 557, 1013, 1013, + /* 1790 */ 1015, 1016, 27, 182, 35, 235, 100, 558, 398, 4, + /* 1800 */ 96, 1405, 1404, 36, 484, 469, 1407, 188, 401, 1471, + /* 1810 */ 451, 89, 1493, 561, 239, 1013, 1013, 1015, 1016, 27, + /* 1820 */ 490, 338, 270, 241, 192, 342, 493, 242, 403, 1249, + /* 1830 */ 243, 511, 432, 1297, 1306, 91, 445, 1305, 1304, 879, + /* 1840 */ 217, 434, 435, 1570, 1276, 1602, 520, 1601, 555, 301, + /* 1850 */ 527, 404, 1275, 302, 356, 1274, 1600, 95, 1347, 366, + /* 1860 */ 1296, 362, 1348, 368, 256, 257, 1556, 1555, 438, 1346, + /* 1870 */ 370, 126, 1345, 10, 1371, 546, 381, 1023, 102, 1457, + /* 1880 */ 97, 530, 34, 106, 106, 570, 1180, 372, 265, 1329, + /* 1890 */ 379, 107, 203, 445, 568, 567, 1328, 385, 1013, 1370, + /* 1900 */ 386, 267, 268, 571, 1244, 161, 1239, 162, 1509, 1510, + /* 1910 */ 1508, 143, 1507, 299, 832, 213, 214, 78, 446, 205, + /* 1920 */ 310, 306, 163, 224, 1084, 140, 1082, 318, 165, 176, + /* 1930 */ 1013, 1013, 1015, 1016, 27, 178, 1206, 231, 911, 234, + /* 1940 */ 326, 1098, 183, 421, 166, 167, 411, 185, 85, 423, + /* 1950 */ 412, 86, 174, 87, 168, 88, 1101, 236, 1097, 237, + /* 1960 */ 154, 18, 238, 254, 337, 1219, 489, 1090, 240, 190, + /* 1970 */ 37, 847, 189, 494, 358, 244, 350, 506, 191, 877, + /* 1980 */ 90, 498, 19, 20, 503, 92, 353, 890, 300, 170, + /* 1990 */ 155, 93, 513, 94, 1168, 156, 1050, 1137, 39, 218, + /* 2000 */ 273, 275, 1136, 960, 194, 955, 110, 1154, 1158, 253, + /* 2010 */ 7, 1162, 1156, 21, 22, 1161, 1142, 23, 24, 25, + /* 2020 */ 33, 542, 26, 260, 197, 98, 1065, 1051, 1049, 1053, + /* 2030 */ 1107, 1054, 1106, 259, 28, 40, 562, 1018, 859, 109, + /* 2040 */ 29, 921, 390, 1176, 172, 139, 1175, 1235, 261, 1235, + /* 2050 */ 1235, 1235, 1235, 1235, 1235, 1235, 1235, 262, 1235, 1235, + /* 2060 */ 1235, 1235, 1235, 1235, 1235, 1235, 1235, 1235, 1593, 1592, }; 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, + /* 0 */ 193, 223, 193, 225, 193, 193, 193, 274, 275, 276, + /* 10 */ 193, 233, 219, 193, 274, 275, 276, 206, 206, 19, + /* 20 */ 193, 219, 216, 216, 217, 216, 217, 193, 295, 216, + /* 30 */ 217, 31, 205, 216, 217, 193, 216, 217, 213, 39, + /* 40 */ 228, 193, 230, 43, 44, 45, 46, 47, 48, 49, + /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 193, 19, + /* 60 */ 185, 186, 187, 188, 189, 190, 253, 274, 275, 276, + /* 70 */ 195, 193, 197, 253, 216, 262, 274, 275, 276, 204, + /* 80 */ 238, 204, 262, 43, 44, 45, 46, 47, 48, 49, + /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 264, 274, + /* 100 */ 275, 276, 102, 103, 104, 105, 106, 107, 108, 109, + /* 110 */ 110, 111, 112, 113, 239, 240, 239, 240, 210, 211, + /* 120 */ 212, 314, 315, 314, 59, 316, 86, 252, 88, 252, + /* 130 */ 19, 314, 315, 256, 257, 309, 25, 72, 296, 313, + /* 140 */ 193, 266, 102, 103, 104, 105, 106, 107, 108, 109, + /* 150 */ 110, 111, 112, 113, 43, 44, 45, 46, 47, 48, + /* 160 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 81, + /* 170 */ 292, 59, 307, 298, 108, 109, 110, 111, 112, 113, + /* 180 */ 69, 116, 117, 118, 72, 128, 129, 193, 241, 22, + /* 190 */ 113, 54, 55, 56, 57, 58, 102, 103, 104, 105, + /* 200 */ 106, 107, 108, 109, 110, 111, 112, 113, 120, 193, + /* 210 */ 216, 217, 25, 102, 103, 104, 105, 106, 107, 108, + /* 220 */ 109, 110, 111, 112, 113, 231, 138, 139, 116, 117, + /* 230 */ 118, 106, 107, 19, 216, 54, 55, 56, 57, 102, + /* 240 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + /* 250 */ 113, 304, 25, 46, 47, 48, 49, 43, 44, 45, + /* 260 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + /* 270 */ 56, 57, 193, 106, 107, 59, 193, 19, 153, 263, + /* 280 */ 155, 67, 24, 102, 103, 104, 105, 106, 107, 108, + /* 290 */ 109, 110, 111, 112, 113, 216, 217, 59, 230, 216, + /* 300 */ 217, 43, 44, 45, 46, 47, 48, 49, 50, 51, + /* 310 */ 52, 53, 54, 55, 56, 57, 102, 103, 104, 105, + /* 320 */ 106, 107, 108, 109, 110, 111, 112, 113, 121, 142, + /* 330 */ 59, 193, 116, 117, 118, 119, 253, 204, 122, 123, + /* 340 */ 124, 19, 20, 81, 22, 262, 108, 19, 132, 165, + /* 350 */ 166, 193, 24, 126, 116, 117, 118, 278, 36, 193, /* 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, + /* 370 */ 112, 113, 239, 240, 216, 217, 215, 106, 107, 241, + /* 380 */ 19, 59, 216, 217, 223, 252, 115, 116, 117, 118, + /* 390 */ 73, 120, 26, 71, 193, 22, 193, 231, 81, 128, + /* 400 */ 138, 139, 269, 81, 43, 44, 45, 46, 47, 48, + /* 410 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 216, + /* 420 */ 217, 198, 100, 95, 153, 59, 155, 193, 106, 107, + /* 430 */ 235, 236, 59, 193, 193, 249, 114, 251, 116, 117, + /* 440 */ 118, 113, 304, 121, 127, 204, 193, 119, 120, 121, + /* 450 */ 122, 123, 124, 125, 193, 138, 139, 216, 217, 131, + /* 460 */ 138, 139, 193, 102, 103, 104, 105, 106, 107, 108, + /* 470 */ 109, 110, 111, 112, 113, 153, 154, 155, 156, 157, + /* 480 */ 239, 240, 116, 117, 118, 76, 193, 193, 19, 116, + /* 490 */ 117, 118, 23, 252, 253, 193, 87, 204, 89, 238, + /* 500 */ 193, 92, 268, 262, 281, 203, 193, 205, 285, 216, + /* 510 */ 217, 150, 43, 44, 45, 46, 47, 48, 49, 50, + /* 520 */ 51, 52, 53, 54, 55, 56, 57, 193, 193, 216, + /* 530 */ 217, 19, 239, 240, 59, 23, 106, 107, 108, 109, + /* 540 */ 110, 111, 112, 113, 231, 252, 253, 193, 308, 309, + /* 550 */ 193, 145, 59, 313, 145, 43, 44, 45, 46, 47, + /* 560 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + /* 570 */ 164, 102, 103, 104, 105, 106, 107, 108, 109, 110, + /* 580 */ 111, 112, 113, 119, 193, 193, 122, 123, 124, 193, + /* 590 */ 283, 116, 117, 118, 235, 236, 132, 59, 241, 264, + /* 600 */ 59, 193, 19, 23, 193, 25, 23, 216, 217, 116, + /* 610 */ 117, 118, 216, 217, 102, 103, 104, 105, 106, 107, + /* 620 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, + /* 630 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 640 */ 57, 19, 308, 309, 151, 23, 25, 313, 135, 253, + /* 650 */ 21, 193, 241, 140, 116, 117, 118, 116, 117, 118, + /* 660 */ 268, 304, 22, 301, 302, 43, 44, 45, 46, 47, + /* 670 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + /* 680 */ 193, 143, 193, 193, 143, 102, 103, 104, 105, 106, + /* 690 */ 107, 108, 109, 110, 111, 112, 113, 76, 118, 59, + /* 700 */ 292, 211, 212, 216, 217, 216, 217, 73, 193, 80, + /* 710 */ 89, 25, 19, 92, 193, 304, 23, 22, 231, 193, + /* 720 */ 231, 193, 22, 143, 102, 103, 104, 105, 106, 107, + /* 730 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, + /* 740 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 750 */ 57, 19, 123, 193, 59, 23, 116, 117, 118, 59, + /* 760 */ 193, 127, 128, 129, 306, 307, 210, 211, 212, 193, + /* 770 */ 22, 111, 112, 113, 284, 43, 44, 45, 46, 47, + /* 780 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + /* 790 */ 161, 193, 216, 217, 268, 102, 103, 104, 105, 106, + /* 800 */ 107, 108, 109, 110, 111, 112, 113, 59, 193, 193, + /* 810 */ 193, 116, 117, 118, 216, 217, 116, 117, 118, 304, + /* 820 */ 239, 240, 19, 263, 138, 139, 23, 211, 212, 231, + /* 830 */ 263, 216, 217, 252, 102, 103, 104, 105, 106, 107, + /* 840 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, + /* 850 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 860 */ 57, 19, 193, 11, 116, 117, 118, 240, 253, 193, + /* 870 */ 201, 239, 240, 193, 134, 206, 136, 137, 193, 252, + /* 880 */ 193, 264, 193, 193, 252, 43, 44, 45, 46, 47, + /* 890 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + /* 900 */ 284, 216, 217, 216, 217, 102, 103, 104, 105, 106, + /* 910 */ 107, 108, 109, 110, 111, 112, 113, 193, 231, 193, + /* 920 */ 187, 188, 189, 190, 127, 128, 129, 238, 195, 193, + /* 930 */ 197, 16, 19, 7, 8, 9, 193, 204, 253, 193, + /* 940 */ 216, 217, 216, 217, 102, 103, 104, 105, 106, 107, + /* 950 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, + /* 960 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 970 */ 57, 213, 239, 240, 193, 76, 19, 188, 232, 190, + /* 980 */ 128, 129, 292, 193, 195, 252, 197, 46, 89, 138, + /* 990 */ 139, 92, 77, 204, 79, 193, 269, 216, 217, 266, + /* 1000 */ 204, 159, 45, 46, 47, 48, 49, 50, 51, 52, + /* 1010 */ 53, 54, 55, 56, 57, 102, 103, 104, 105, 106, + /* 1020 */ 107, 108, 109, 110, 111, 112, 113, 12, 239, 240, + /* 1030 */ 193, 298, 22, 23, 253, 239, 240, 127, 128, 129, + /* 1040 */ 238, 252, 27, 193, 286, 204, 193, 204, 252, 291, + /* 1050 */ 193, 273, 22, 23, 100, 266, 115, 42, 268, 102, + /* 1060 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + /* 1070 */ 113, 117, 159, 216, 217, 121, 161, 19, 63, 193, + /* 1080 */ 239, 240, 239, 240, 12, 208, 209, 298, 73, 311, + /* 1090 */ 312, 238, 19, 252, 25, 252, 22, 24, 24, 27, + /* 1100 */ 193, 264, 216, 217, 46, 208, 209, 153, 154, 155, + /* 1110 */ 253, 101, 19, 23, 42, 25, 43, 44, 45, 46, + /* 1120 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 1130 */ 57, 101, 19, 59, 25, 63, 43, 44, 45, 46, + /* 1140 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 1150 */ 57, 22, 23, 115, 25, 24, 43, 44, 45, 46, + /* 1160 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 1170 */ 57, 22, 23, 115, 25, 102, 103, 104, 105, 106, + /* 1180 */ 107, 108, 109, 110, 111, 112, 113, 118, 150, 131, + /* 1190 */ 59, 117, 22, 273, 193, 102, 103, 104, 105, 106, + /* 1200 */ 107, 108, 109, 110, 111, 112, 113, 204, 66, 204, + /* 1210 */ 35, 204, 143, 213, 193, 102, 103, 104, 105, 106, + /* 1220 */ 107, 108, 109, 110, 111, 112, 113, 85, 193, 59, + /* 1230 */ 101, 311, 312, 16, 193, 19, 94, 216, 217, 238, + /* 1240 */ 193, 66, 239, 240, 239, 240, 239, 240, 117, 74, + /* 1250 */ 101, 216, 217, 193, 193, 252, 193, 252, 149, 252, + /* 1260 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + /* 1270 */ 54, 55, 56, 57, 193, 193, 193, 5, 59, 216, + /* 1280 */ 217, 25, 10, 11, 12, 13, 14, 117, 146, 17, + /* 1290 */ 193, 291, 193, 232, 77, 76, 79, 216, 217, 216, + /* 1300 */ 217, 31, 30, 309, 32, 130, 87, 313, 89, 39, + /* 1310 */ 193, 92, 40, 216, 217, 216, 217, 108, 102, 103, + /* 1320 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + /* 1330 */ 299, 300, 193, 216, 217, 116, 117, 118, 19, 20, + /* 1340 */ 193, 22, 70, 309, 135, 193, 264, 313, 193, 140, + /* 1350 */ 78, 193, 226, 81, 59, 36, 193, 309, 193, 29, + /* 1360 */ 193, 313, 193, 33, 145, 193, 59, 48, 216, 217, + /* 1370 */ 98, 216, 217, 193, 216, 217, 193, 244, 59, 216, + /* 1380 */ 217, 216, 217, 216, 217, 216, 217, 254, 216, 217, + /* 1390 */ 71, 193, 244, 193, 193, 65, 216, 217, 193, 216, + /* 1400 */ 217, 145, 254, 244, 85, 133, 15, 100, 193, 90, + /* 1410 */ 138, 139, 117, 254, 216, 217, 216, 217, 193, 100, + /* 1420 */ 193, 216, 217, 116, 117, 106, 107, 19, 121, 193, + /* 1430 */ 193, 216, 217, 114, 162, 116, 117, 118, 244, 244, + /* 1440 */ 121, 216, 217, 216, 217, 193, 309, 129, 254, 254, + /* 1450 */ 313, 60, 216, 217, 19, 256, 257, 193, 120, 121, + /* 1460 */ 153, 154, 155, 149, 150, 25, 24, 99, 216, 217, + /* 1470 */ 152, 193, 153, 154, 155, 156, 157, 0, 1, 2, + /* 1480 */ 216, 217, 5, 22, 158, 24, 160, 10, 11, 12, + /* 1490 */ 13, 14, 193, 23, 17, 25, 193, 19, 20, 193, + /* 1500 */ 22, 133, 193, 22, 22, 193, 22, 30, 193, 32, + /* 1510 */ 19, 20, 129, 22, 36, 216, 217, 40, 193, 216, + /* 1520 */ 217, 193, 216, 217, 116, 216, 217, 36, 216, 217, + /* 1530 */ 193, 216, 217, 193, 53, 152, 193, 59, 23, 19, + /* 1540 */ 25, 216, 217, 61, 216, 217, 23, 70, 25, 71, + /* 1550 */ 59, 116, 193, 216, 217, 78, 216, 217, 81, 216, + /* 1560 */ 217, 59, 71, 85, 193, 23, 193, 25, 90, 23, + /* 1570 */ 23, 25, 25, 7, 8, 98, 85, 193, 100, 193, + /* 1580 */ 59, 90, 142, 141, 106, 107, 193, 216, 217, 216, + /* 1590 */ 217, 100, 114, 193, 116, 117, 118, 106, 107, 121, + /* 1600 */ 216, 217, 216, 217, 193, 114, 193, 116, 117, 118, + /* 1610 */ 133, 23, 121, 25, 121, 138, 139, 97, 23, 117, + /* 1620 */ 25, 23, 193, 25, 131, 141, 193, 216, 217, 59, + /* 1630 */ 193, 153, 154, 155, 156, 157, 226, 193, 117, 162, + /* 1640 */ 23, 23, 25, 25, 153, 154, 155, 156, 157, 1, + /* 1650 */ 2, 83, 84, 5, 19, 20, 226, 22, 10, 11, + /* 1660 */ 12, 13, 14, 258, 153, 17, 155, 153, 23, 155, + /* 1670 */ 25, 36, 23, 193, 25, 255, 193, 236, 30, 193, + /* 1680 */ 32, 19, 20, 193, 22, 193, 288, 117, 40, 193, + /* 1690 */ 318, 193, 193, 193, 59, 242, 193, 193, 36, 193, + /* 1700 */ 193, 193, 287, 255, 255, 255, 71, 255, 243, 214, + /* 1710 */ 191, 297, 267, 245, 271, 259, 259, 293, 70, 246, + /* 1720 */ 246, 59, 267, 229, 245, 271, 78, 293, 259, 81, + /* 1730 */ 271, 271, 220, 71, 225, 100, 219, 219, 249, 196, + /* 1740 */ 243, 106, 107, 108, 219, 60, 98, 280, 297, 114, + /* 1750 */ 249, 116, 117, 118, 141, 245, 121, 200, 200, 297, + /* 1760 */ 38, 200, 100, 151, 150, 294, 294, 22, 106, 107, + /* 1770 */ 283, 43, 234, 18, 237, 200, 114, 272, 116, 117, + /* 1780 */ 118, 133, 237, 121, 18, 237, 138, 139, 153, 154, + /* 1790 */ 155, 156, 157, 237, 270, 199, 19, 20, 246, 22, + /* 1800 */ 149, 272, 272, 270, 200, 246, 234, 234, 246, 246, + /* 1810 */ 162, 158, 290, 36, 199, 153, 154, 155, 156, 157, + /* 1820 */ 62, 289, 200, 199, 22, 200, 221, 199, 221, 200, + /* 1830 */ 199, 115, 64, 227, 218, 22, 59, 218, 218, 126, + /* 1840 */ 165, 24, 113, 312, 218, 224, 305, 224, 71, 282, + /* 1850 */ 144, 221, 220, 282, 218, 218, 218, 115, 261, 260, + /* 1860 */ 227, 221, 261, 260, 200, 91, 317, 317, 82, 261, + /* 1870 */ 260, 148, 261, 22, 265, 145, 200, 100, 158, 277, + /* 1880 */ 147, 146, 25, 106, 107, 202, 13, 260, 194, 250, + /* 1890 */ 249, 114, 248, 116, 117, 118, 250, 247, 121, 265, + /* 1900 */ 246, 194, 6, 192, 192, 207, 192, 207, 213, 213, + /* 1910 */ 213, 222, 213, 222, 4, 214, 214, 213, 3, 22, + /* 1920 */ 163, 279, 207, 15, 23, 16, 23, 139, 130, 151, + /* 1930 */ 153, 154, 155, 156, 157, 142, 25, 24, 20, 144, + /* 1940 */ 16, 1, 142, 61, 130, 130, 303, 151, 53, 37, + /* 1950 */ 303, 53, 300, 53, 130, 53, 116, 34, 1, 141, + /* 1960 */ 5, 22, 115, 25, 161, 75, 41, 68, 141, 115, + /* 1970 */ 24, 20, 68, 19, 131, 125, 23, 96, 22, 59, + /* 1980 */ 22, 67, 22, 22, 67, 22, 24, 28, 67, 37, + /* 1990 */ 23, 149, 22, 25, 23, 23, 23, 23, 22, 141, + /* 2000 */ 23, 23, 97, 116, 22, 143, 25, 88, 75, 34, + /* 2010 */ 44, 75, 86, 34, 34, 93, 23, 34, 34, 34, + /* 2020 */ 22, 24, 34, 22, 25, 25, 23, 23, 23, 23, + /* 2030 */ 23, 11, 23, 25, 22, 22, 25, 23, 23, 22, + /* 2040 */ 22, 135, 15, 1, 25, 23, 1, 319, 141, 319, + /* 2050 */ 319, 319, 319, 319, 319, 319, 319, 141, 319, 319, + /* 2060 */ 319, 319, 319, 319, 319, 319, 319, 319, 141, 141, + /* 2070 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2080 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2090 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2100 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2110 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2120 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2130 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2140 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2150 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2160 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2170 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2180 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2190 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2200 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2210 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2220 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2230 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2240 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2250 */ 319, 319, 319, 319, 319, }; -#define YY_SHIFT_COUNT (552) +#define YY_SHIFT_COUNT (573) #define YY_SHIFT_MIN (0) -#define YY_SHIFT_MAX (1951) +#define YY_SHIFT_MAX (2045) 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, -}; -#define YY_REDUCE_COUNT (391) -#define YY_REDUCE_MIN (-262) -#define YY_REDUCE_MAX (1625) + /* 0 */ 1648, 1477, 1272, 322, 322, 262, 1319, 1478, 1491, 1662, + /* 10 */ 1662, 1662, 317, 0, 0, 214, 1093, 1662, 1662, 1662, + /* 20 */ 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, + /* 30 */ 271, 271, 1219, 1219, 216, 88, 262, 262, 262, 262, + /* 40 */ 262, 40, 111, 258, 361, 469, 512, 583, 622, 693, + /* 50 */ 732, 803, 842, 913, 1073, 1093, 1093, 1093, 1093, 1093, + /* 60 */ 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, + /* 70 */ 1093, 1093, 1093, 1113, 1093, 1216, 957, 957, 1635, 1662, + /* 80 */ 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, + /* 90 */ 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, + /* 100 */ 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, + /* 110 */ 1662, 1662, 1662, 1662, 1777, 1662, 1662, 1662, 1662, 1662, + /* 120 */ 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 137, 181, + /* 130 */ 181, 181, 181, 181, 94, 430, 66, 65, 112, 366, + /* 140 */ 475, 475, 629, 1058, 475, 475, 125, 125, 475, 686, + /* 150 */ 686, 686, 660, 686, 57, 184, 184, 77, 77, 2070, + /* 160 */ 2070, 328, 328, 328, 493, 373, 373, 373, 373, 1015, + /* 170 */ 1015, 409, 366, 1129, 1149, 475, 475, 475, 475, 475, + /* 180 */ 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, + /* 190 */ 475, 475, 475, 475, 475, 621, 621, 475, 852, 899, + /* 200 */ 899, 1295, 1295, 406, 851, 2070, 2070, 2070, 2070, 2070, + /* 210 */ 2070, 2070, 1307, 954, 954, 640, 464, 695, 238, 700, + /* 220 */ 538, 541, 748, 475, 475, 475, 475, 475, 475, 475, + /* 230 */ 475, 475, 475, 634, 475, 475, 475, 475, 475, 475, + /* 240 */ 475, 475, 475, 475, 475, 475, 1175, 1175, 1175, 475, + /* 250 */ 475, 475, 580, 475, 475, 475, 1074, 1142, 475, 475, + /* 260 */ 1072, 475, 475, 475, 475, 475, 475, 475, 475, 797, + /* 270 */ 1330, 740, 1131, 1131, 1131, 1131, 1069, 740, 740, 1209, + /* 280 */ 167, 926, 1391, 1038, 1314, 187, 1408, 1314, 1408, 1435, + /* 290 */ 1109, 1038, 1038, 1109, 1038, 187, 1435, 227, 1090, 941, + /* 300 */ 1270, 1270, 1270, 1408, 1256, 1256, 1326, 1440, 513, 1461, + /* 310 */ 1685, 1685, 1613, 1613, 1722, 1722, 1613, 1612, 1614, 1745, + /* 320 */ 1728, 1755, 1755, 1755, 1755, 1613, 1766, 1651, 1614, 1614, + /* 330 */ 1651, 1745, 1728, 1651, 1728, 1651, 1613, 1766, 1653, 1758, + /* 340 */ 1613, 1766, 1802, 1613, 1766, 1613, 1766, 1802, 1716, 1716, + /* 350 */ 1716, 1768, 1813, 1813, 1802, 1716, 1713, 1716, 1768, 1716, + /* 360 */ 1716, 1675, 1817, 1729, 1729, 1802, 1706, 1742, 1706, 1742, + /* 370 */ 1706, 1742, 1706, 1742, 1613, 1774, 1774, 1786, 1786, 1723, + /* 380 */ 1730, 1851, 1613, 1720, 1723, 1733, 1735, 1651, 1857, 1873, + /* 390 */ 1873, 1896, 1896, 1896, 2070, 2070, 2070, 2070, 2070, 2070, + /* 400 */ 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 207, + /* 410 */ 915, 1010, 1030, 1217, 910, 1170, 1470, 1368, 1481, 1442, + /* 420 */ 1318, 1383, 1515, 1482, 1523, 1542, 1546, 1547, 1588, 1595, + /* 430 */ 1502, 1338, 1566, 1493, 1520, 1521, 1598, 1617, 1568, 1618, + /* 440 */ 1511, 1514, 1645, 1649, 1570, 1484, 1910, 1915, 1897, 1757, + /* 450 */ 1908, 1909, 1901, 1903, 1788, 1778, 1798, 1911, 1911, 1913, + /* 460 */ 1793, 1918, 1795, 1924, 1940, 1800, 1814, 1911, 1815, 1882, + /* 470 */ 1912, 1911, 1796, 1895, 1898, 1900, 1902, 1824, 1840, 1923, + /* 480 */ 1818, 1957, 1955, 1939, 1847, 1803, 1899, 1938, 1904, 1890, + /* 490 */ 1925, 1827, 1854, 1946, 1951, 1954, 1843, 1850, 1956, 1914, + /* 500 */ 1958, 1960, 1953, 1961, 1917, 1920, 1962, 1881, 1959, 1963, + /* 510 */ 1921, 1952, 1967, 1842, 1970, 1971, 1972, 1973, 1968, 1974, + /* 520 */ 1976, 1905, 1858, 1977, 1978, 1887, 1975, 1982, 1862, 1981, + /* 530 */ 1979, 1980, 1983, 1984, 1919, 1933, 1926, 1966, 1936, 1922, + /* 540 */ 1985, 1993, 1998, 1997, 1999, 2000, 1988, 2003, 1981, 2004, + /* 550 */ 2005, 2006, 2007, 2008, 2009, 2001, 2020, 2012, 2013, 2014, + /* 560 */ 2015, 2017, 2018, 2011, 1906, 1907, 1916, 1927, 1928, 2019, + /* 570 */ 2022, 2027, 2042, 2045, +}; +#define YY_REDUCE_COUNT (408) +#define YY_REDUCE_MIN (-267) +#define YY_REDUCE_MAX (1715) 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 */ -125, 733, 789, 241, 293, -123, -193, -191, -183, -187, + /* 10 */ -180, 83, 133, -207, -198, -267, -175, -6, 166, 313, + /* 20 */ 487, 396, 489, 598, 615, 685, 687, 79, 781, 857, + /* 30 */ 490, 616, 240, 334, -188, 796, 841, 843, 1003, 1005, + /* 40 */ 1007, -260, -260, -260, -260, -260, -260, -260, -260, -260, + /* 50 */ -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, + /* 60 */ -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, + /* 70 */ -260, -260, -260, -260, -260, -260, -260, -260, 158, 203, + /* 80 */ 391, 576, 724, 726, 886, 1021, 1035, 1063, 1081, 1083, + /* 90 */ 1097, 1099, 1117, 1152, 1155, 1158, 1163, 1165, 1167, 1169, + /* 100 */ 1172, 1180, 1183, 1198, 1200, 1205, 1215, 1225, 1227, 1236, + /* 110 */ 1252, 1264, 1299, 1303, 1306, 1309, 1312, 1315, 1325, 1328, + /* 120 */ 1337, 1340, 1343, 1371, 1373, 1384, 1386, 1411, -260, -260, + /* 130 */ -260, -260, -260, -260, -260, -260, -260, -53, 138, 302, + /* 140 */ -158, 357, 223, -222, 411, 458, -92, 556, 669, 581, + /* 150 */ 632, 581, -260, 632, 758, 778, 920, -260, -260, -260, + /* 160 */ -260, 161, 161, 161, 307, 234, 392, 526, 790, 195, + /* 170 */ 359, -174, -173, 362, 362, -189, 16, 560, 567, 261, + /* 180 */ 689, 802, 853, -122, -166, 408, 335, 617, 690, 837, + /* 190 */ 1001, 746, 1061, 515, 1082, 994, 1034, -135, 1000, 1048, + /* 200 */ 1137, 877, 897, 186, 627, 1031, 1133, 1148, 1159, 1194, + /* 210 */ 1199, 1195, -194, -142, 18, -152, 68, 201, 253, 269, + /* 220 */ 294, 354, 521, 528, 676, 680, 736, 743, 850, 907, + /* 230 */ 1041, 1047, 1060, 727, 1139, 1147, 1201, 1237, 1278, 1359, + /* 240 */ 1393, 1400, 1413, 1429, 1433, 1437, 1126, 1410, 1430, 1444, + /* 250 */ 1480, 1483, 1405, 1486, 1490, 1492, 1420, 1372, 1496, 1498, + /* 260 */ 1441, 1499, 253, 1500, 1503, 1504, 1506, 1507, 1508, 1398, + /* 270 */ 1415, 1453, 1448, 1449, 1450, 1452, 1405, 1453, 1453, 1465, + /* 280 */ 1495, 1519, 1414, 1443, 1445, 1468, 1456, 1455, 1457, 1424, + /* 290 */ 1473, 1454, 1459, 1474, 1460, 1479, 1434, 1512, 1494, 1509, + /* 300 */ 1517, 1518, 1525, 1469, 1489, 1501, 1467, 1510, 1497, 1543, + /* 310 */ 1451, 1462, 1557, 1558, 1471, 1472, 1561, 1487, 1505, 1524, + /* 320 */ 1538, 1537, 1545, 1548, 1556, 1575, 1596, 1552, 1529, 1530, + /* 330 */ 1559, 1533, 1572, 1562, 1573, 1563, 1604, 1615, 1522, 1532, + /* 340 */ 1622, 1624, 1605, 1625, 1628, 1629, 1631, 1607, 1616, 1619, + /* 350 */ 1620, 1606, 1621, 1623, 1630, 1626, 1632, 1636, 1633, 1637, + /* 360 */ 1638, 1531, 1541, 1567, 1571, 1640, 1597, 1599, 1601, 1603, + /* 370 */ 1608, 1610, 1611, 1627, 1664, 1549, 1550, 1609, 1634, 1639, + /* 380 */ 1641, 1602, 1676, 1642, 1646, 1644, 1650, 1654, 1683, 1694, + /* 390 */ 1707, 1711, 1712, 1714, 1643, 1647, 1652, 1698, 1695, 1696, + /* 400 */ 1697, 1699, 1700, 1689, 1691, 1701, 1702, 1704, 1715, }; 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 */ 1637, 1637, 1637, 1466, 1233, 1344, 1233, 1233, 1233, 1466, + /* 10 */ 1466, 1466, 1233, 1374, 1374, 1519, 1266, 1233, 1233, 1233, + /* 20 */ 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1465, 1233, 1233, + /* 30 */ 1233, 1233, 1554, 1554, 1233, 1233, 1233, 1233, 1233, 1233, + /* 40 */ 1233, 1233, 1383, 1233, 1390, 1233, 1233, 1233, 1233, 1233, + /* 50 */ 1467, 1468, 1233, 1233, 1233, 1518, 1520, 1483, 1397, 1396, + /* 60 */ 1395, 1394, 1501, 1361, 1388, 1381, 1385, 1461, 1462, 1460, + /* 70 */ 1464, 1468, 1467, 1233, 1384, 1431, 1445, 1430, 1233, 1233, + /* 80 */ 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, + /* 90 */ 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, + /* 100 */ 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, + /* 110 */ 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, + /* 120 */ 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1439, 1444, + /* 130 */ 1451, 1443, 1440, 1433, 1432, 1434, 1435, 1233, 1233, 1257, + /* 140 */ 1233, 1233, 1254, 1308, 1233, 1233, 1233, 1233, 1233, 1538, + /* 150 */ 1537, 1233, 1436, 1233, 1266, 1425, 1424, 1448, 1437, 1447, + /* 160 */ 1446, 1526, 1590, 1589, 1484, 1233, 1233, 1233, 1233, 1233, + /* 170 */ 1233, 1554, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, + /* 180 */ 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, + /* 190 */ 1233, 1233, 1233, 1233, 1233, 1554, 1554, 1233, 1266, 1554, + /* 200 */ 1554, 1262, 1262, 1368, 1233, 1533, 1335, 1335, 1335, 1335, + /* 210 */ 1344, 1335, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, + /* 220 */ 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1523, 1521, 1233, + /* 230 */ 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, + /* 240 */ 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, + /* 250 */ 1233, 1233, 1233, 1233, 1233, 1233, 1340, 1233, 1233, 1233, + /* 260 */ 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1583, 1233, + /* 270 */ 1496, 1322, 1340, 1340, 1340, 1340, 1342, 1323, 1321, 1334, + /* 280 */ 1267, 1240, 1629, 1400, 1389, 1341, 1363, 1389, 1363, 1626, + /* 290 */ 1387, 1400, 1400, 1387, 1400, 1341, 1626, 1283, 1606, 1278, + /* 300 */ 1374, 1374, 1374, 1363, 1368, 1368, 1463, 1341, 1334, 1233, + /* 310 */ 1629, 1629, 1349, 1349, 1628, 1628, 1349, 1484, 1613, 1409, + /* 320 */ 1311, 1317, 1317, 1317, 1317, 1349, 1251, 1387, 1613, 1613, + /* 330 */ 1387, 1409, 1311, 1387, 1311, 1387, 1349, 1251, 1500, 1623, + /* 340 */ 1349, 1251, 1474, 1349, 1251, 1349, 1251, 1474, 1309, 1309, + /* 350 */ 1309, 1298, 1233, 1233, 1474, 1309, 1283, 1309, 1298, 1309, + /* 360 */ 1309, 1572, 1233, 1478, 1478, 1474, 1367, 1362, 1367, 1362, + /* 370 */ 1367, 1362, 1367, 1362, 1349, 1564, 1564, 1377, 1377, 1382, + /* 380 */ 1368, 1469, 1349, 1233, 1382, 1380, 1378, 1387, 1301, 1586, + /* 390 */ 1586, 1582, 1582, 1582, 1634, 1634, 1533, 1599, 1266, 1266, + /* 400 */ 1266, 1266, 1599, 1285, 1285, 1267, 1267, 1266, 1599, 1233, + /* 410 */ 1233, 1233, 1233, 1233, 1233, 1594, 1233, 1528, 1485, 1353, + /* 420 */ 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, + /* 430 */ 1233, 1233, 1233, 1233, 1539, 1233, 1233, 1233, 1233, 1233, + /* 440 */ 1233, 1233, 1233, 1233, 1233, 1414, 1233, 1236, 1530, 1233, + /* 450 */ 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1391, 1392, 1354, + /* 460 */ 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1406, 1233, 1233, + /* 470 */ 1233, 1401, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, + /* 480 */ 1625, 1233, 1233, 1233, 1233, 1233, 1233, 1499, 1498, 1233, + /* 490 */ 1233, 1351, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, + /* 500 */ 1233, 1233, 1233, 1233, 1233, 1281, 1233, 1233, 1233, 1233, + /* 510 */ 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, + /* 520 */ 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1379, + /* 530 */ 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, + /* 540 */ 1233, 1233, 1233, 1233, 1569, 1369, 1233, 1233, 1616, 1233, + /* 550 */ 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, + /* 560 */ 1233, 1233, 1233, 1610, 1325, 1416, 1233, 1415, 1419, 1255, + /* 570 */ 1233, 1245, 1233, 1233, }; /********** End of lemon-generated parsing tables *****************************/ @@ -155219,8 +162454,8 @@ 0, /* LP => nothing */ 0, /* RP => nothing */ 0, /* AS => nothing */ - 59, /* WITHOUT => ID */ 0, /* COMMA => nothing */ + 59, /* WITHOUT => ID */ 59, /* ABORT => ID */ 59, /* ACTION => ID */ 59, /* AFTER => ID */ @@ -155291,6 +162526,7 @@ 59, /* TIES => ID */ 59, /* GENERATED => ID */ 59, /* ALWAYS => ID */ + 59, /* MATERIALIZED => ID */ 59, /* REINDEX => ID */ 59, /* RENAME => ID */ 59, /* CTIME_KW => ID */ @@ -155305,6 +162541,7 @@ 0, /* SLASH => nothing */ 0, /* REM => nothing */ 0, /* CONCAT => nothing */ + 0, /* PTR => nothing */ 0, /* COLLATE => nothing */ 0, /* BITNOT => nothing */ 0, /* ON => nothing */ @@ -155342,6 +162579,7 @@ 0, /* HAVING => nothing */ 0, /* LIMIT => nothing */ 0, /* WHERE => nothing */ + 0, /* RETURNING => nothing */ 0, /* INTO => nothing */ 0, /* NOTHING => nothing */ 0, /* FLOAT => nothing */ @@ -155373,6 +162611,7 @@ 0, /* IF_NULL_ROW => nothing */ 0, /* ASTERISK => nothing */ 0, /* SPAN => nothing */ + 0, /* ERROR => nothing */ 0, /* SPACE => nothing */ 0, /* ILLEGAL => nothing */ }; @@ -155426,6 +162665,7 @@ }; typedef struct yyParser yyParser; +/* #include */ #ifndef NDEBUG /* #include */ static FILE *yyTraceFILE = 0; @@ -155487,8 +162727,8 @@ /* 22 */ "LP", /* 23 */ "RP", /* 24 */ "AS", - /* 25 */ "WITHOUT", - /* 26 */ "COMMA", + /* 25 */ "COMMA", + /* 26 */ "WITHOUT", /* 27 */ "ABORT", /* 28 */ "ACTION", /* 29 */ "AFTER", @@ -155559,219 +162799,228 @@ /* 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", + /* 97 */ "MATERIALIZED", + /* 98 */ "REINDEX", + /* 99 */ "RENAME", + /* 100 */ "CTIME_KW", + /* 101 */ "ANY", + /* 102 */ "BITAND", + /* 103 */ "BITOR", + /* 104 */ "LSHIFT", + /* 105 */ "RSHIFT", + /* 106 */ "PLUS", + /* 107 */ "MINUS", + /* 108 */ "STAR", + /* 109 */ "SLASH", + /* 110 */ "REM", + /* 111 */ "CONCAT", + /* 112 */ "PTR", + /* 113 */ "COLLATE", + /* 114 */ "BITNOT", + /* 115 */ "ON", + /* 116 */ "INDEXED", + /* 117 */ "STRING", + /* 118 */ "JOIN_KW", + /* 119 */ "CONSTRAINT", + /* 120 */ "DEFAULT", + /* 121 */ "NULL", + /* 122 */ "PRIMARY", + /* 123 */ "UNIQUE", + /* 124 */ "CHECK", + /* 125 */ "REFERENCES", + /* 126 */ "AUTOINCR", + /* 127 */ "INSERT", + /* 128 */ "DELETE", + /* 129 */ "UPDATE", + /* 130 */ "SET", + /* 131 */ "DEFERRABLE", + /* 132 */ "FOREIGN", + /* 133 */ "DROP", + /* 134 */ "UNION", + /* 135 */ "ALL", + /* 136 */ "EXCEPT", + /* 137 */ "INTERSECT", + /* 138 */ "SELECT", + /* 139 */ "VALUES", + /* 140 */ "DISTINCT", + /* 141 */ "DOT", + /* 142 */ "FROM", + /* 143 */ "JOIN", + /* 144 */ "USING", + /* 145 */ "ORDER", + /* 146 */ "GROUP", + /* 147 */ "HAVING", + /* 148 */ "LIMIT", + /* 149 */ "WHERE", + /* 150 */ "RETURNING", + /* 151 */ "INTO", + /* 152 */ "NOTHING", + /* 153 */ "FLOAT", + /* 154 */ "BLOB", + /* 155 */ "INTEGER", + /* 156 */ "VARIABLE", + /* 157 */ "CASE", + /* 158 */ "WHEN", + /* 159 */ "THEN", + /* 160 */ "ELSE", + /* 161 */ "INDEX", + /* 162 */ "ALTER", + /* 163 */ "ADD", + /* 164 */ "WINDOW", + /* 165 */ "OVER", + /* 166 */ "FILTER", + /* 167 */ "COLUMN", + /* 168 */ "AGG_FUNCTION", + /* 169 */ "AGG_COLUMN", + /* 170 */ "TRUEFALSE", + /* 171 */ "ISNOT", + /* 172 */ "FUNCTION", + /* 173 */ "UMINUS", + /* 174 */ "UPLUS", + /* 175 */ "TRUTH", + /* 176 */ "REGISTER", + /* 177 */ "VECTOR", + /* 178 */ "SELECT_COLUMN", + /* 179 */ "IF_NULL_ROW", + /* 180 */ "ASTERISK", + /* 181 */ "SPAN", + /* 182 */ "ERROR", + /* 183 */ "SPACE", + /* 184 */ "ILLEGAL", + /* 185 */ "input", + /* 186 */ "cmdlist", + /* 187 */ "ecmd", + /* 188 */ "cmdx", + /* 189 */ "explain", + /* 190 */ "cmd", + /* 191 */ "transtype", + /* 192 */ "trans_opt", + /* 193 */ "nm", + /* 194 */ "savepoint_opt", + /* 195 */ "create_table", + /* 196 */ "create_table_args", + /* 197 */ "createkw", + /* 198 */ "temp", + /* 199 */ "ifnotexists", + /* 200 */ "dbnm", + /* 201 */ "columnlist", + /* 202 */ "conslist_opt", + /* 203 */ "table_option_set", + /* 204 */ "select", + /* 205 */ "table_option", + /* 206 */ "columnname", + /* 207 */ "carglist", + /* 208 */ "typetoken", + /* 209 */ "typename", + /* 210 */ "signed", + /* 211 */ "plus_num", + /* 212 */ "minus_num", + /* 213 */ "scanpt", + /* 214 */ "scantok", + /* 215 */ "ccons", + /* 216 */ "term", + /* 217 */ "expr", + /* 218 */ "onconf", + /* 219 */ "sortorder", + /* 220 */ "autoinc", + /* 221 */ "eidlist_opt", + /* 222 */ "refargs", + /* 223 */ "defer_subclause", + /* 224 */ "generated", + /* 225 */ "refarg", + /* 226 */ "refact", + /* 227 */ "init_deferred_pred_opt", + /* 228 */ "conslist", + /* 229 */ "tconscomma", + /* 230 */ "tcons", + /* 231 */ "sortlist", + /* 232 */ "eidlist", + /* 233 */ "defer_subclause_opt", + /* 234 */ "orconf", + /* 235 */ "resolvetype", + /* 236 */ "raisetype", + /* 237 */ "ifexists", + /* 238 */ "fullname", + /* 239 */ "selectnowith", + /* 240 */ "oneselect", + /* 241 */ "wqlist", + /* 242 */ "multiselect_op", + /* 243 */ "distinct", + /* 244 */ "selcollist", + /* 245 */ "from", + /* 246 */ "where_opt", + /* 247 */ "groupby_opt", + /* 248 */ "having_opt", + /* 249 */ "orderby_opt", + /* 250 */ "limit_opt", + /* 251 */ "window_clause", + /* 252 */ "values", + /* 253 */ "nexprlist", + /* 254 */ "sclp", + /* 255 */ "as", + /* 256 */ "seltablist", + /* 257 */ "stl_prefix", + /* 258 */ "joinop", + /* 259 */ "indexed_opt", + /* 260 */ "on_opt", + /* 261 */ "using_opt", + /* 262 */ "exprlist", + /* 263 */ "xfullname", + /* 264 */ "idlist", + /* 265 */ "nulls", + /* 266 */ "with", + /* 267 */ "where_opt_ret", + /* 268 */ "setlist", + /* 269 */ "insert_cmd", + /* 270 */ "idlist_opt", + /* 271 */ "upsert", + /* 272 */ "returning", + /* 273 */ "filter_over", + /* 274 */ "likeop", + /* 275 */ "between_op", + /* 276 */ "in_op", + /* 277 */ "paren_exprlist", + /* 278 */ "case_operand", + /* 279 */ "case_exprlist", + /* 280 */ "case_else", + /* 281 */ "uniqueflag", + /* 282 */ "collate", + /* 283 */ "vinto", + /* 284 */ "nmnum", + /* 285 */ "trigger_decl", + /* 286 */ "trigger_cmd_list", + /* 287 */ "trigger_time", + /* 288 */ "trigger_event", + /* 289 */ "foreach_clause", + /* 290 */ "when_clause", + /* 291 */ "trigger_cmd", + /* 292 */ "trnm", + /* 293 */ "tridxby", + /* 294 */ "database_kw_opt", + /* 295 */ "key_opt", + /* 296 */ "add_column_fullname", + /* 297 */ "kwcolumn_opt", + /* 298 */ "create_vtab", + /* 299 */ "vtabarglist", + /* 300 */ "vtabarg", + /* 301 */ "vtabargtoken", + /* 302 */ "lp", + /* 303 */ "anylist", + /* 304 */ "wqitem", + /* 305 */ "wqas", + /* 306 */ "windowdefn_list", + /* 307 */ "windowdefn", + /* 308 */ "window", + /* 309 */ "frame_opt", + /* 310 */ "part_opt", + /* 311 */ "filter_clause", + /* 312 */ "over_clause", + /* 313 */ "range_or_rows", + /* 314 */ "frame_bound", + /* 315 */ "frame_bound_s", + /* 316 */ "frame_bound_e", + /* 317 */ "frame_exclude_opt", + /* 318 */ "frame_exclude", }; #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ @@ -155798,372 +163047,389 @@ /* 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 */ "values ::= values COMMA LP nexprlist RP", + /* 96 */ "distinct ::= DISTINCT", + /* 97 */ "distinct ::= ALL", + /* 98 */ "distinct ::=", + /* 99 */ "sclp ::=", + /* 100 */ "selcollist ::= sclp scanpt expr scanpt as", + /* 101 */ "selcollist ::= sclp scanpt STAR", + /* 102 */ "selcollist ::= sclp scanpt nm DOT STAR", + /* 103 */ "as ::= AS nm", + /* 104 */ "as ::=", + /* 105 */ "from ::=", + /* 106 */ "from ::= FROM seltablist", + /* 107 */ "stl_prefix ::= seltablist joinop", + /* 108 */ "stl_prefix ::=", + /* 109 */ "seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt", + /* 110 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt", + /* 111 */ "seltablist ::= stl_prefix LP select RP as on_opt using_opt", + /* 112 */ "seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt", + /* 113 */ "dbnm ::=", + /* 114 */ "dbnm ::= DOT nm", + /* 115 */ "fullname ::= nm", + /* 116 */ "fullname ::= nm DOT nm", + /* 117 */ "xfullname ::= nm", + /* 118 */ "xfullname ::= nm DOT nm", + /* 119 */ "xfullname ::= nm DOT nm AS nm", + /* 120 */ "xfullname ::= nm AS nm", + /* 121 */ "joinop ::= COMMA|JOIN", + /* 122 */ "joinop ::= JOIN_KW JOIN", + /* 123 */ "joinop ::= JOIN_KW nm JOIN", + /* 124 */ "joinop ::= JOIN_KW nm nm JOIN", + /* 125 */ "on_opt ::= ON expr", + /* 126 */ "on_opt ::=", + /* 127 */ "indexed_opt ::=", + /* 128 */ "indexed_opt ::= INDEXED BY nm", + /* 129 */ "indexed_opt ::= NOT INDEXED", + /* 130 */ "using_opt ::= USING LP idlist RP", + /* 131 */ "using_opt ::=", + /* 132 */ "orderby_opt ::=", + /* 133 */ "orderby_opt ::= ORDER BY sortlist", + /* 134 */ "sortlist ::= sortlist COMMA expr sortorder nulls", + /* 135 */ "sortlist ::= expr sortorder nulls", + /* 136 */ "sortorder ::= ASC", + /* 137 */ "sortorder ::= DESC", + /* 138 */ "sortorder ::=", + /* 139 */ "nulls ::= NULLS FIRST", + /* 140 */ "nulls ::= NULLS LAST", + /* 141 */ "nulls ::=", + /* 142 */ "groupby_opt ::=", + /* 143 */ "groupby_opt ::= GROUP BY nexprlist", + /* 144 */ "having_opt ::=", + /* 145 */ "having_opt ::= HAVING expr", + /* 146 */ "limit_opt ::=", + /* 147 */ "limit_opt ::= LIMIT expr", + /* 148 */ "limit_opt ::= LIMIT expr OFFSET expr", + /* 149 */ "limit_opt ::= LIMIT expr COMMA expr", + /* 150 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret", + /* 151 */ "where_opt ::=", + /* 152 */ "where_opt ::= WHERE expr", + /* 153 */ "where_opt_ret ::=", + /* 154 */ "where_opt_ret ::= WHERE expr", + /* 155 */ "where_opt_ret ::= RETURNING selcollist", + /* 156 */ "where_opt_ret ::= WHERE expr RETURNING selcollist", + /* 157 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret", + /* 158 */ "setlist ::= setlist COMMA nm EQ expr", + /* 159 */ "setlist ::= setlist COMMA LP idlist RP EQ expr", + /* 160 */ "setlist ::= nm EQ expr", + /* 161 */ "setlist ::= LP idlist RP EQ expr", + /* 162 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert", + /* 163 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning", + /* 164 */ "upsert ::=", + /* 165 */ "upsert ::= RETURNING selcollist", + /* 166 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert", + /* 167 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert", + /* 168 */ "upsert ::= ON CONFLICT DO NOTHING returning", + /* 169 */ "upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning", + /* 170 */ "returning ::= RETURNING selcollist", + /* 171 */ "insert_cmd ::= INSERT orconf", + /* 172 */ "insert_cmd ::= REPLACE", + /* 173 */ "idlist_opt ::=", + /* 174 */ "idlist_opt ::= LP idlist RP", + /* 175 */ "idlist ::= idlist COMMA nm", + /* 176 */ "idlist ::= nm", + /* 177 */ "expr ::= LP expr RP", + /* 178 */ "expr ::= ID|INDEXED", + /* 179 */ "expr ::= JOIN_KW", + /* 180 */ "expr ::= nm DOT nm", + /* 181 */ "expr ::= nm DOT nm DOT nm", + /* 182 */ "term ::= NULL|FLOAT|BLOB", + /* 183 */ "term ::= STRING", + /* 184 */ "term ::= INTEGER", + /* 185 */ "expr ::= VARIABLE", + /* 186 */ "expr ::= expr COLLATE ID|STRING", + /* 187 */ "expr ::= CAST LP expr AS typetoken RP", + /* 188 */ "expr ::= ID|INDEXED LP distinct exprlist RP", + /* 189 */ "expr ::= ID|INDEXED LP STAR RP", + /* 190 */ "expr ::= ID|INDEXED LP distinct exprlist RP filter_over", + /* 191 */ "expr ::= ID|INDEXED LP STAR RP filter_over", + /* 192 */ "term ::= CTIME_KW", + /* 193 */ "expr ::= LP nexprlist COMMA expr RP", + /* 194 */ "expr ::= expr AND expr", + /* 195 */ "expr ::= expr OR expr", + /* 196 */ "expr ::= expr LT|GT|GE|LE expr", + /* 197 */ "expr ::= expr EQ|NE expr", + /* 198 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", + /* 199 */ "expr ::= expr PLUS|MINUS expr", + /* 200 */ "expr ::= expr STAR|SLASH|REM expr", + /* 201 */ "expr ::= expr CONCAT expr", + /* 202 */ "likeop ::= NOT LIKE_KW|MATCH", + /* 203 */ "expr ::= expr likeop expr", + /* 204 */ "expr ::= expr likeop expr ESCAPE expr", + /* 205 */ "expr ::= expr ISNULL|NOTNULL", + /* 206 */ "expr ::= expr NOT NULL", + /* 207 */ "expr ::= expr IS expr", + /* 208 */ "expr ::= expr IS NOT expr", + /* 209 */ "expr ::= NOT expr", + /* 210 */ "expr ::= BITNOT expr", + /* 211 */ "expr ::= PLUS|MINUS expr", + /* 212 */ "expr ::= expr PTR expr", + /* 213 */ "between_op ::= BETWEEN", + /* 214 */ "between_op ::= NOT BETWEEN", + /* 215 */ "expr ::= expr between_op expr AND expr", + /* 216 */ "in_op ::= IN", + /* 217 */ "in_op ::= NOT IN", + /* 218 */ "expr ::= expr in_op LP exprlist RP", + /* 219 */ "expr ::= LP select RP", + /* 220 */ "expr ::= expr in_op LP select RP", + /* 221 */ "expr ::= expr in_op nm dbnm paren_exprlist", + /* 222 */ "expr ::= EXISTS LP select RP", + /* 223 */ "expr ::= CASE case_operand case_exprlist case_else END", + /* 224 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", + /* 225 */ "case_exprlist ::= WHEN expr THEN expr", + /* 226 */ "case_else ::= ELSE expr", + /* 227 */ "case_else ::=", + /* 228 */ "case_operand ::= expr", + /* 229 */ "case_operand ::=", + /* 230 */ "exprlist ::=", + /* 231 */ "nexprlist ::= nexprlist COMMA expr", + /* 232 */ "nexprlist ::= expr", + /* 233 */ "paren_exprlist ::=", + /* 234 */ "paren_exprlist ::= LP exprlist RP", + /* 235 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", + /* 236 */ "uniqueflag ::= UNIQUE", + /* 237 */ "uniqueflag ::=", + /* 238 */ "eidlist_opt ::=", + /* 239 */ "eidlist_opt ::= LP eidlist RP", + /* 240 */ "eidlist ::= eidlist COMMA nm collate sortorder", + /* 241 */ "eidlist ::= nm collate sortorder", + /* 242 */ "collate ::=", + /* 243 */ "collate ::= COLLATE ID|STRING", + /* 244 */ "cmd ::= DROP INDEX ifexists fullname", + /* 245 */ "cmd ::= VACUUM vinto", + /* 246 */ "cmd ::= VACUUM nm vinto", + /* 247 */ "vinto ::= INTO expr", + /* 248 */ "vinto ::=", + /* 249 */ "cmd ::= PRAGMA nm dbnm", + /* 250 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", + /* 251 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", + /* 252 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", + /* 253 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", + /* 254 */ "plus_num ::= PLUS INTEGER|FLOAT", + /* 255 */ "minus_num ::= MINUS INTEGER|FLOAT", + /* 256 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", + /* 257 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", + /* 258 */ "trigger_time ::= BEFORE|AFTER", + /* 259 */ "trigger_time ::= INSTEAD OF", + /* 260 */ "trigger_time ::=", + /* 261 */ "trigger_event ::= DELETE|INSERT", + /* 262 */ "trigger_event ::= UPDATE", + /* 263 */ "trigger_event ::= UPDATE OF idlist", + /* 264 */ "when_clause ::=", + /* 265 */ "when_clause ::= WHEN expr", + /* 266 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", + /* 267 */ "trigger_cmd_list ::= trigger_cmd SEMI", + /* 268 */ "trnm ::= nm DOT nm", + /* 269 */ "tridxby ::= INDEXED BY nm", + /* 270 */ "tridxby ::= NOT INDEXED", + /* 271 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", + /* 272 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", + /* 273 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", + /* 274 */ "trigger_cmd ::= scanpt select scanpt", + /* 275 */ "expr ::= RAISE LP IGNORE RP", + /* 276 */ "expr ::= RAISE LP raisetype COMMA nm RP", + /* 277 */ "raisetype ::= ROLLBACK", + /* 278 */ "raisetype ::= ABORT", + /* 279 */ "raisetype ::= FAIL", + /* 280 */ "cmd ::= DROP TRIGGER ifexists fullname", + /* 281 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", + /* 282 */ "cmd ::= DETACH database_kw_opt expr", + /* 283 */ "key_opt ::=", + /* 284 */ "key_opt ::= KEY expr", + /* 285 */ "cmd ::= REINDEX", + /* 286 */ "cmd ::= REINDEX nm dbnm", + /* 287 */ "cmd ::= ANALYZE", + /* 288 */ "cmd ::= ANALYZE nm dbnm", + /* 289 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", + /* 290 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", + /* 291 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm", + /* 292 */ "add_column_fullname ::= fullname", + /* 293 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", + /* 294 */ "cmd ::= create_vtab", + /* 295 */ "cmd ::= create_vtab LP vtabarglist RP", + /* 296 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", + /* 297 */ "vtabarg ::=", + /* 298 */ "vtabargtoken ::= ANY", + /* 299 */ "vtabargtoken ::= lp anylist RP", + /* 300 */ "lp ::= LP", + /* 301 */ "with ::= WITH wqlist", + /* 302 */ "with ::= WITH RECURSIVE wqlist", + /* 303 */ "wqas ::= AS", + /* 304 */ "wqas ::= AS MATERIALIZED", + /* 305 */ "wqas ::= AS NOT MATERIALIZED", + /* 306 */ "wqitem ::= nm eidlist_opt wqas LP select RP", + /* 307 */ "wqlist ::= wqitem", + /* 308 */ "wqlist ::= wqlist COMMA wqitem", + /* 309 */ "windowdefn_list ::= windowdefn", + /* 310 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", + /* 311 */ "windowdefn ::= nm AS LP window RP", + /* 312 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", + /* 313 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", + /* 314 */ "window ::= ORDER BY sortlist frame_opt", + /* 315 */ "window ::= nm ORDER BY sortlist frame_opt", + /* 316 */ "window ::= frame_opt", + /* 317 */ "window ::= nm frame_opt", + /* 318 */ "frame_opt ::=", + /* 319 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", + /* 320 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", + /* 321 */ "range_or_rows ::= RANGE|ROWS|GROUPS", + /* 322 */ "frame_bound_s ::= frame_bound", + /* 323 */ "frame_bound_s ::= UNBOUNDED PRECEDING", + /* 324 */ "frame_bound_e ::= frame_bound", + /* 325 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", + /* 326 */ "frame_bound ::= expr PRECEDING|FOLLOWING", + /* 327 */ "frame_bound ::= CURRENT ROW", + /* 328 */ "frame_exclude_opt ::=", + /* 329 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", + /* 330 */ "frame_exclude ::= NO OTHERS", + /* 331 */ "frame_exclude ::= CURRENT ROW", + /* 332 */ "frame_exclude ::= GROUP|TIES", + /* 333 */ "window_clause ::= WINDOW windowdefn_list", + /* 334 */ "filter_over ::= filter_clause over_clause", + /* 335 */ "filter_over ::= over_clause", + /* 336 */ "filter_over ::= filter_clause", + /* 337 */ "over_clause ::= OVER LP window RP", + /* 338 */ "over_clause ::= OVER nm", + /* 339 */ "filter_clause ::= FILTER LP WHERE expr RP", + /* 340 */ "input ::= cmdlist", + /* 341 */ "cmdlist ::= cmdlist ecmd", + /* 342 */ "cmdlist ::= ecmd", + /* 343 */ "ecmd ::= SEMI", + /* 344 */ "ecmd ::= cmdx SEMI", + /* 345 */ "ecmd ::= explain cmdx SEMI", + /* 346 */ "trans_opt ::=", + /* 347 */ "trans_opt ::= TRANSACTION", + /* 348 */ "trans_opt ::= TRANSACTION nm", + /* 349 */ "savepoint_opt ::= SAVEPOINT", + /* 350 */ "savepoint_opt ::=", + /* 351 */ "cmd ::= create_table create_table_args", + /* 352 */ "table_option_set ::= table_option", + /* 353 */ "columnlist ::= columnlist COMMA columnname carglist", + /* 354 */ "columnlist ::= columnname carglist", + /* 355 */ "nm ::= ID|INDEXED", + /* 356 */ "nm ::= STRING", + /* 357 */ "nm ::= JOIN_KW", + /* 358 */ "typetoken ::= typename", + /* 359 */ "typename ::= ID|STRING", + /* 360 */ "signed ::= plus_num", + /* 361 */ "signed ::= minus_num", + /* 362 */ "carglist ::= carglist ccons", + /* 363 */ "carglist ::=", + /* 364 */ "ccons ::= NULL onconf", + /* 365 */ "ccons ::= GENERATED ALWAYS AS generated", + /* 366 */ "ccons ::= AS generated", + /* 367 */ "conslist_opt ::= COMMA conslist", + /* 368 */ "conslist ::= conslist tconscomma tcons", + /* 369 */ "conslist ::= tcons", + /* 370 */ "tconscomma ::=", + /* 371 */ "defer_subclause_opt ::= defer_subclause", + /* 372 */ "resolvetype ::= raisetype", + /* 373 */ "selectnowith ::= oneselect", + /* 374 */ "oneselect ::= values", + /* 375 */ "sclp ::= selcollist COMMA", + /* 376 */ "as ::= ID|STRING", + /* 377 */ "returning ::=", + /* 378 */ "expr ::= term", + /* 379 */ "likeop ::= LIKE_KW|MATCH", + /* 380 */ "exprlist ::= nexprlist", + /* 381 */ "nmnum ::= plus_num", + /* 382 */ "nmnum ::= nm", + /* 383 */ "nmnum ::= ON", + /* 384 */ "nmnum ::= DELETE", + /* 385 */ "nmnum ::= DEFAULT", + /* 386 */ "plus_num ::= INTEGER|FLOAT", + /* 387 */ "foreach_clause ::=", + /* 388 */ "foreach_clause ::= FOR EACH ROW", + /* 389 */ "trnm ::= nm", + /* 390 */ "tridxby ::=", + /* 391 */ "database_kw_opt ::= DATABASE", + /* 392 */ "database_kw_opt ::=", + /* 393 */ "kwcolumn_opt ::=", + /* 394 */ "kwcolumn_opt ::= COLUMNKW", + /* 395 */ "vtabarglist ::= vtabarg", + /* 396 */ "vtabarglist ::= vtabarglist COMMA vtabarg", + /* 397 */ "vtabarg ::= vtabarg vtabargtoken", + /* 398 */ "anylist ::=", + /* 399 */ "anylist ::= anylist LP anylist RP", + /* 400 */ "anylist ::= anylist ANY", + /* 401 */ "with ::=", }; #endif /* NDEBUG */ @@ -156289,98 +163555,99 @@ ** inside the C code. */ /********* Begin destructor definitions ***************************************/ - case 200: /* select */ - case 234: /* selectnowith */ - case 235: /* oneselect */ - case 247: /* values */ + case 204: /* select */ + case 239: /* selectnowith */ + case 240: /* oneselect */ + case 252: /* values */ { -sqlite3SelectDelete(pParse->db, (yypminor->yy539)); +sqlite3SelectDelete(pParse->db, (yypminor->yy47)); } 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 */ + case 216: /* term */ + case 217: /* expr */ + case 246: /* where_opt */ + case 248: /* having_opt */ + case 260: /* on_opt */ + case 267: /* where_opt_ret */ + case 278: /* case_operand */ + case 280: /* case_else */ + case 283: /* vinto */ + case 290: /* when_clause */ + case 295: /* key_opt */ + case 311: /* filter_clause */ { -sqlite3ExprDelete(pParse->db, (yypminor->yy202)); +sqlite3ExprDelete(pParse->db, (yypminor->yy528)); } 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 */ + case 221: /* eidlist_opt */ + case 231: /* sortlist */ + case 232: /* eidlist */ + case 244: /* selcollist */ + case 247: /* groupby_opt */ + case 249: /* orderby_opt */ + case 253: /* nexprlist */ + case 254: /* sclp */ + case 262: /* exprlist */ + case 268: /* setlist */ + case 277: /* paren_exprlist */ + case 279: /* case_exprlist */ + case 310: /* part_opt */ { -sqlite3ExprListDelete(pParse->db, (yypminor->yy242)); +sqlite3ExprListDelete(pParse->db, (yypminor->yy322)); } break; - case 233: /* fullname */ - case 240: /* from */ - case 251: /* seltablist */ - case 252: /* stl_prefix */ - case 258: /* xfullname */ + case 238: /* fullname */ + case 245: /* from */ + case 256: /* seltablist */ + case 257: /* stl_prefix */ + case 263: /* xfullname */ { -sqlite3SrcListDelete(pParse->db, (yypminor->yy47)); +sqlite3SrcListDelete(pParse->db, (yypminor->yy131)); } break; - case 236: /* wqlist */ + case 241: /* wqlist */ { -sqlite3WithDelete(pParse->db, (yypminor->yy131)); +sqlite3WithDelete(pParse->db, (yypminor->yy521)); } break; - case 246: /* window_clause */ - case 297: /* windowdefn_list */ + case 251: /* window_clause */ + case 306: /* windowdefn_list */ { -sqlite3WindowListDelete(pParse->db, (yypminor->yy303)); +sqlite3WindowListDelete(pParse->db, (yypminor->yy41)); } break; - case 256: /* using_opt */ - case 259: /* idlist */ - case 264: /* idlist_opt */ + case 261: /* using_opt */ + case 264: /* idlist */ + case 270: /* idlist_opt */ { -sqlite3IdListDelete(pParse->db, (yypminor->yy600)); +sqlite3IdListDelete(pParse->db, (yypminor->yy254)); } break; - case 266: /* filter_over */ - case 298: /* windowdefn */ - case 299: /* window */ - case 300: /* frame_opt */ - case 303: /* over_clause */ + case 273: /* filter_over */ + case 307: /* windowdefn */ + case 308: /* window */ + case 309: /* frame_opt */ + case 312: /* over_clause */ { -sqlite3WindowDelete(pParse->db, (yypminor->yy303)); +sqlite3WindowDelete(pParse->db, (yypminor->yy41)); } break; - case 279: /* trigger_cmd_list */ - case 284: /* trigger_cmd */ + case 286: /* trigger_cmd_list */ + case 291: /* trigger_cmd */ { -sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy447)); +sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy33)); } break; - case 281: /* trigger_event */ + case 288: /* trigger_event */ { -sqlite3IdListDelete(pParse->db, (yypminor->yy230).b); +sqlite3IdListDelete(pParse->db, (yypminor->yy180).b); } break; - case 305: /* frame_bound */ - case 306: /* frame_bound_s */ - case 307: /* frame_bound_e */ + case 314: /* frame_bound */ + case 315: /* frame_bound_s */ + case 316: /* frame_bound_e */ { -sqlite3ExprDelete(pParse->db, (yypminor->yy77).pExpr); +sqlite3ExprDelete(pParse->db, (yypminor->yy595).pExpr); } break; /********* End destructor definitions *****************************************/ @@ -156547,7 +163814,7 @@ #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); @@ -156671,391 +163938,408 @@ /* 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 ::= */ + 189, /* (0) explain ::= EXPLAIN */ + 189, /* (1) explain ::= EXPLAIN QUERY PLAN */ + 188, /* (2) cmdx ::= cmd */ + 190, /* (3) cmd ::= BEGIN transtype trans_opt */ + 191, /* (4) transtype ::= */ + 191, /* (5) transtype ::= DEFERRED */ + 191, /* (6) transtype ::= IMMEDIATE */ + 191, /* (7) transtype ::= EXCLUSIVE */ + 190, /* (8) cmd ::= COMMIT|END trans_opt */ + 190, /* (9) cmd ::= ROLLBACK trans_opt */ + 190, /* (10) cmd ::= SAVEPOINT nm */ + 190, /* (11) cmd ::= RELEASE savepoint_opt nm */ + 190, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ + 195, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ + 197, /* (14) createkw ::= CREATE */ + 199, /* (15) ifnotexists ::= */ + 199, /* (16) ifnotexists ::= IF NOT EXISTS */ + 198, /* (17) temp ::= TEMP */ + 198, /* (18) temp ::= */ + 196, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */ + 196, /* (20) create_table_args ::= AS select */ + 203, /* (21) table_option_set ::= */ + 203, /* (22) table_option_set ::= table_option_set COMMA table_option */ + 205, /* (23) table_option ::= WITHOUT nm */ + 205, /* (24) table_option ::= nm */ + 206, /* (25) columnname ::= nm typetoken */ + 208, /* (26) typetoken ::= */ + 208, /* (27) typetoken ::= typename LP signed RP */ + 208, /* (28) typetoken ::= typename LP signed COMMA signed RP */ + 209, /* (29) typename ::= typename ID|STRING */ + 213, /* (30) scanpt ::= */ + 214, /* (31) scantok ::= */ + 215, /* (32) ccons ::= CONSTRAINT nm */ + 215, /* (33) ccons ::= DEFAULT scantok term */ + 215, /* (34) ccons ::= DEFAULT LP expr RP */ + 215, /* (35) ccons ::= DEFAULT PLUS scantok term */ + 215, /* (36) ccons ::= DEFAULT MINUS scantok term */ + 215, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */ + 215, /* (38) ccons ::= NOT NULL onconf */ + 215, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */ + 215, /* (40) ccons ::= UNIQUE onconf */ + 215, /* (41) ccons ::= CHECK LP expr RP */ + 215, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */ + 215, /* (43) ccons ::= defer_subclause */ + 215, /* (44) ccons ::= COLLATE ID|STRING */ + 224, /* (45) generated ::= LP expr RP */ + 224, /* (46) generated ::= LP expr RP ID */ + 220, /* (47) autoinc ::= */ + 220, /* (48) autoinc ::= AUTOINCR */ + 222, /* (49) refargs ::= */ + 222, /* (50) refargs ::= refargs refarg */ + 225, /* (51) refarg ::= MATCH nm */ + 225, /* (52) refarg ::= ON INSERT refact */ + 225, /* (53) refarg ::= ON DELETE refact */ + 225, /* (54) refarg ::= ON UPDATE refact */ + 226, /* (55) refact ::= SET NULL */ + 226, /* (56) refact ::= SET DEFAULT */ + 226, /* (57) refact ::= CASCADE */ + 226, /* (58) refact ::= RESTRICT */ + 226, /* (59) refact ::= NO ACTION */ + 223, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ + 223, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ + 227, /* (62) init_deferred_pred_opt ::= */ + 227, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */ + 227, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ + 202, /* (65) conslist_opt ::= */ + 229, /* (66) tconscomma ::= COMMA */ + 230, /* (67) tcons ::= CONSTRAINT nm */ + 230, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ + 230, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */ + 230, /* (70) tcons ::= CHECK LP expr RP onconf */ + 230, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ + 233, /* (72) defer_subclause_opt ::= */ + 218, /* (73) onconf ::= */ + 218, /* (74) onconf ::= ON CONFLICT resolvetype */ + 234, /* (75) orconf ::= */ + 234, /* (76) orconf ::= OR resolvetype */ + 235, /* (77) resolvetype ::= IGNORE */ + 235, /* (78) resolvetype ::= REPLACE */ + 190, /* (79) cmd ::= DROP TABLE ifexists fullname */ + 237, /* (80) ifexists ::= IF EXISTS */ + 237, /* (81) ifexists ::= */ + 190, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ + 190, /* (83) cmd ::= DROP VIEW ifexists fullname */ + 190, /* (84) cmd ::= select */ + 204, /* (85) select ::= WITH wqlist selectnowith */ + 204, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */ + 204, /* (87) select ::= selectnowith */ + 239, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */ + 242, /* (89) multiselect_op ::= UNION */ + 242, /* (90) multiselect_op ::= UNION ALL */ + 242, /* (91) multiselect_op ::= EXCEPT|INTERSECT */ + 240, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ + 240, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ + 252, /* (94) values ::= VALUES LP nexprlist RP */ + 252, /* (95) values ::= values COMMA LP nexprlist RP */ + 243, /* (96) distinct ::= DISTINCT */ + 243, /* (97) distinct ::= ALL */ + 243, /* (98) distinct ::= */ + 254, /* (99) sclp ::= */ + 244, /* (100) selcollist ::= sclp scanpt expr scanpt as */ + 244, /* (101) selcollist ::= sclp scanpt STAR */ + 244, /* (102) selcollist ::= sclp scanpt nm DOT STAR */ + 255, /* (103) as ::= AS nm */ + 255, /* (104) as ::= */ + 245, /* (105) from ::= */ + 245, /* (106) from ::= FROM seltablist */ + 257, /* (107) stl_prefix ::= seltablist joinop */ + 257, /* (108) stl_prefix ::= */ + 256, /* (109) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ + 256, /* (110) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ + 256, /* (111) seltablist ::= stl_prefix LP select RP as on_opt using_opt */ + 256, /* (112) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ + 200, /* (113) dbnm ::= */ + 200, /* (114) dbnm ::= DOT nm */ + 238, /* (115) fullname ::= nm */ + 238, /* (116) fullname ::= nm DOT nm */ + 263, /* (117) xfullname ::= nm */ + 263, /* (118) xfullname ::= nm DOT nm */ + 263, /* (119) xfullname ::= nm DOT nm AS nm */ + 263, /* (120) xfullname ::= nm AS nm */ + 258, /* (121) joinop ::= COMMA|JOIN */ + 258, /* (122) joinop ::= JOIN_KW JOIN */ + 258, /* (123) joinop ::= JOIN_KW nm JOIN */ + 258, /* (124) joinop ::= JOIN_KW nm nm JOIN */ + 260, /* (125) on_opt ::= ON expr */ + 260, /* (126) on_opt ::= */ + 259, /* (127) indexed_opt ::= */ + 259, /* (128) indexed_opt ::= INDEXED BY nm */ + 259, /* (129) indexed_opt ::= NOT INDEXED */ + 261, /* (130) using_opt ::= USING LP idlist RP */ + 261, /* (131) using_opt ::= */ + 249, /* (132) orderby_opt ::= */ + 249, /* (133) orderby_opt ::= ORDER BY sortlist */ + 231, /* (134) sortlist ::= sortlist COMMA expr sortorder nulls */ + 231, /* (135) sortlist ::= expr sortorder nulls */ + 219, /* (136) sortorder ::= ASC */ + 219, /* (137) sortorder ::= DESC */ + 219, /* (138) sortorder ::= */ + 265, /* (139) nulls ::= NULLS FIRST */ + 265, /* (140) nulls ::= NULLS LAST */ + 265, /* (141) nulls ::= */ + 247, /* (142) groupby_opt ::= */ + 247, /* (143) groupby_opt ::= GROUP BY nexprlist */ + 248, /* (144) having_opt ::= */ + 248, /* (145) having_opt ::= HAVING expr */ + 250, /* (146) limit_opt ::= */ + 250, /* (147) limit_opt ::= LIMIT expr */ + 250, /* (148) limit_opt ::= LIMIT expr OFFSET expr */ + 250, /* (149) limit_opt ::= LIMIT expr COMMA expr */ + 190, /* (150) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ + 246, /* (151) where_opt ::= */ + 246, /* (152) where_opt ::= WHERE expr */ + 267, /* (153) where_opt_ret ::= */ + 267, /* (154) where_opt_ret ::= WHERE expr */ + 267, /* (155) where_opt_ret ::= RETURNING selcollist */ + 267, /* (156) where_opt_ret ::= WHERE expr RETURNING selcollist */ + 190, /* (157) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ + 268, /* (158) setlist ::= setlist COMMA nm EQ expr */ + 268, /* (159) setlist ::= setlist COMMA LP idlist RP EQ expr */ + 268, /* (160) setlist ::= nm EQ expr */ + 268, /* (161) setlist ::= LP idlist RP EQ expr */ + 190, /* (162) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + 190, /* (163) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ + 271, /* (164) upsert ::= */ + 271, /* (165) upsert ::= RETURNING selcollist */ + 271, /* (166) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ + 271, /* (167) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ + 271, /* (168) upsert ::= ON CONFLICT DO NOTHING returning */ + 271, /* (169) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ + 272, /* (170) returning ::= RETURNING selcollist */ + 269, /* (171) insert_cmd ::= INSERT orconf */ + 269, /* (172) insert_cmd ::= REPLACE */ + 270, /* (173) idlist_opt ::= */ + 270, /* (174) idlist_opt ::= LP idlist RP */ + 264, /* (175) idlist ::= idlist COMMA nm */ + 264, /* (176) idlist ::= nm */ + 217, /* (177) expr ::= LP expr RP */ + 217, /* (178) expr ::= ID|INDEXED */ + 217, /* (179) expr ::= JOIN_KW */ + 217, /* (180) expr ::= nm DOT nm */ + 217, /* (181) expr ::= nm DOT nm DOT nm */ + 216, /* (182) term ::= NULL|FLOAT|BLOB */ + 216, /* (183) term ::= STRING */ + 216, /* (184) term ::= INTEGER */ + 217, /* (185) expr ::= VARIABLE */ + 217, /* (186) expr ::= expr COLLATE ID|STRING */ + 217, /* (187) expr ::= CAST LP expr AS typetoken RP */ + 217, /* (188) expr ::= ID|INDEXED LP distinct exprlist RP */ + 217, /* (189) expr ::= ID|INDEXED LP STAR RP */ + 217, /* (190) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ + 217, /* (191) expr ::= ID|INDEXED LP STAR RP filter_over */ + 216, /* (192) term ::= CTIME_KW */ + 217, /* (193) expr ::= LP nexprlist COMMA expr RP */ + 217, /* (194) expr ::= expr AND expr */ + 217, /* (195) expr ::= expr OR expr */ + 217, /* (196) expr ::= expr LT|GT|GE|LE expr */ + 217, /* (197) expr ::= expr EQ|NE expr */ + 217, /* (198) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + 217, /* (199) expr ::= expr PLUS|MINUS expr */ + 217, /* (200) expr ::= expr STAR|SLASH|REM expr */ + 217, /* (201) expr ::= expr CONCAT expr */ + 274, /* (202) likeop ::= NOT LIKE_KW|MATCH */ + 217, /* (203) expr ::= expr likeop expr */ + 217, /* (204) expr ::= expr likeop expr ESCAPE expr */ + 217, /* (205) expr ::= expr ISNULL|NOTNULL */ + 217, /* (206) expr ::= expr NOT NULL */ + 217, /* (207) expr ::= expr IS expr */ + 217, /* (208) expr ::= expr IS NOT expr */ + 217, /* (209) expr ::= NOT expr */ + 217, /* (210) expr ::= BITNOT expr */ + 217, /* (211) expr ::= PLUS|MINUS expr */ + 217, /* (212) expr ::= expr PTR expr */ + 275, /* (213) between_op ::= BETWEEN */ + 275, /* (214) between_op ::= NOT BETWEEN */ + 217, /* (215) expr ::= expr between_op expr AND expr */ + 276, /* (216) in_op ::= IN */ + 276, /* (217) in_op ::= NOT IN */ + 217, /* (218) expr ::= expr in_op LP exprlist RP */ + 217, /* (219) expr ::= LP select RP */ + 217, /* (220) expr ::= expr in_op LP select RP */ + 217, /* (221) expr ::= expr in_op nm dbnm paren_exprlist */ + 217, /* (222) expr ::= EXISTS LP select RP */ + 217, /* (223) expr ::= CASE case_operand case_exprlist case_else END */ + 279, /* (224) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + 279, /* (225) case_exprlist ::= WHEN expr THEN expr */ + 280, /* (226) case_else ::= ELSE expr */ + 280, /* (227) case_else ::= */ + 278, /* (228) case_operand ::= expr */ + 278, /* (229) case_operand ::= */ + 262, /* (230) exprlist ::= */ + 253, /* (231) nexprlist ::= nexprlist COMMA expr */ + 253, /* (232) nexprlist ::= expr */ + 277, /* (233) paren_exprlist ::= */ + 277, /* (234) paren_exprlist ::= LP exprlist RP */ + 190, /* (235) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + 281, /* (236) uniqueflag ::= UNIQUE */ + 281, /* (237) uniqueflag ::= */ + 221, /* (238) eidlist_opt ::= */ + 221, /* (239) eidlist_opt ::= LP eidlist RP */ + 232, /* (240) eidlist ::= eidlist COMMA nm collate sortorder */ + 232, /* (241) eidlist ::= nm collate sortorder */ + 282, /* (242) collate ::= */ + 282, /* (243) collate ::= COLLATE ID|STRING */ + 190, /* (244) cmd ::= DROP INDEX ifexists fullname */ + 190, /* (245) cmd ::= VACUUM vinto */ + 190, /* (246) cmd ::= VACUUM nm vinto */ + 283, /* (247) vinto ::= INTO expr */ + 283, /* (248) vinto ::= */ + 190, /* (249) cmd ::= PRAGMA nm dbnm */ + 190, /* (250) cmd ::= PRAGMA nm dbnm EQ nmnum */ + 190, /* (251) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + 190, /* (252) cmd ::= PRAGMA nm dbnm EQ minus_num */ + 190, /* (253) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + 211, /* (254) plus_num ::= PLUS INTEGER|FLOAT */ + 212, /* (255) minus_num ::= MINUS INTEGER|FLOAT */ + 190, /* (256) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + 285, /* (257) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + 287, /* (258) trigger_time ::= BEFORE|AFTER */ + 287, /* (259) trigger_time ::= INSTEAD OF */ + 287, /* (260) trigger_time ::= */ + 288, /* (261) trigger_event ::= DELETE|INSERT */ + 288, /* (262) trigger_event ::= UPDATE */ + 288, /* (263) trigger_event ::= UPDATE OF idlist */ + 290, /* (264) when_clause ::= */ + 290, /* (265) when_clause ::= WHEN expr */ + 286, /* (266) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + 286, /* (267) trigger_cmd_list ::= trigger_cmd SEMI */ + 292, /* (268) trnm ::= nm DOT nm */ + 293, /* (269) tridxby ::= INDEXED BY nm */ + 293, /* (270) tridxby ::= NOT INDEXED */ + 291, /* (271) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ + 291, /* (272) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + 291, /* (273) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + 291, /* (274) trigger_cmd ::= scanpt select scanpt */ + 217, /* (275) expr ::= RAISE LP IGNORE RP */ + 217, /* (276) expr ::= RAISE LP raisetype COMMA nm RP */ + 236, /* (277) raisetype ::= ROLLBACK */ + 236, /* (278) raisetype ::= ABORT */ + 236, /* (279) raisetype ::= FAIL */ + 190, /* (280) cmd ::= DROP TRIGGER ifexists fullname */ + 190, /* (281) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + 190, /* (282) cmd ::= DETACH database_kw_opt expr */ + 295, /* (283) key_opt ::= */ + 295, /* (284) key_opt ::= KEY expr */ + 190, /* (285) cmd ::= REINDEX */ + 190, /* (286) cmd ::= REINDEX nm dbnm */ + 190, /* (287) cmd ::= ANALYZE */ + 190, /* (288) cmd ::= ANALYZE nm dbnm */ + 190, /* (289) cmd ::= ALTER TABLE fullname RENAME TO nm */ + 190, /* (290) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + 190, /* (291) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ + 296, /* (292) add_column_fullname ::= fullname */ + 190, /* (293) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + 190, /* (294) cmd ::= create_vtab */ + 190, /* (295) cmd ::= create_vtab LP vtabarglist RP */ + 298, /* (296) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 300, /* (297) vtabarg ::= */ + 301, /* (298) vtabargtoken ::= ANY */ + 301, /* (299) vtabargtoken ::= lp anylist RP */ + 302, /* (300) lp ::= LP */ + 266, /* (301) with ::= WITH wqlist */ + 266, /* (302) with ::= WITH RECURSIVE wqlist */ + 305, /* (303) wqas ::= AS */ + 305, /* (304) wqas ::= AS MATERIALIZED */ + 305, /* (305) wqas ::= AS NOT MATERIALIZED */ + 304, /* (306) wqitem ::= nm eidlist_opt wqas LP select RP */ + 241, /* (307) wqlist ::= wqitem */ + 241, /* (308) wqlist ::= wqlist COMMA wqitem */ + 306, /* (309) windowdefn_list ::= windowdefn */ + 306, /* (310) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + 307, /* (311) windowdefn ::= nm AS LP window RP */ + 308, /* (312) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + 308, /* (313) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + 308, /* (314) window ::= ORDER BY sortlist frame_opt */ + 308, /* (315) window ::= nm ORDER BY sortlist frame_opt */ + 308, /* (316) window ::= frame_opt */ + 308, /* (317) window ::= nm frame_opt */ + 309, /* (318) frame_opt ::= */ + 309, /* (319) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + 309, /* (320) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + 313, /* (321) range_or_rows ::= RANGE|ROWS|GROUPS */ + 315, /* (322) frame_bound_s ::= frame_bound */ + 315, /* (323) frame_bound_s ::= UNBOUNDED PRECEDING */ + 316, /* (324) frame_bound_e ::= frame_bound */ + 316, /* (325) frame_bound_e ::= UNBOUNDED FOLLOWING */ + 314, /* (326) frame_bound ::= expr PRECEDING|FOLLOWING */ + 314, /* (327) frame_bound ::= CURRENT ROW */ + 317, /* (328) frame_exclude_opt ::= */ + 317, /* (329) frame_exclude_opt ::= EXCLUDE frame_exclude */ + 318, /* (330) frame_exclude ::= NO OTHERS */ + 318, /* (331) frame_exclude ::= CURRENT ROW */ + 318, /* (332) frame_exclude ::= GROUP|TIES */ + 251, /* (333) window_clause ::= WINDOW windowdefn_list */ + 273, /* (334) filter_over ::= filter_clause over_clause */ + 273, /* (335) filter_over ::= over_clause */ + 273, /* (336) filter_over ::= filter_clause */ + 312, /* (337) over_clause ::= OVER LP window RP */ + 312, /* (338) over_clause ::= OVER nm */ + 311, /* (339) filter_clause ::= FILTER LP WHERE expr RP */ + 185, /* (340) input ::= cmdlist */ + 186, /* (341) cmdlist ::= cmdlist ecmd */ + 186, /* (342) cmdlist ::= ecmd */ + 187, /* (343) ecmd ::= SEMI */ + 187, /* (344) ecmd ::= cmdx SEMI */ + 187, /* (345) ecmd ::= explain cmdx SEMI */ + 192, /* (346) trans_opt ::= */ + 192, /* (347) trans_opt ::= TRANSACTION */ + 192, /* (348) trans_opt ::= TRANSACTION nm */ + 194, /* (349) savepoint_opt ::= SAVEPOINT */ + 194, /* (350) savepoint_opt ::= */ + 190, /* (351) cmd ::= create_table create_table_args */ + 203, /* (352) table_option_set ::= table_option */ + 201, /* (353) columnlist ::= columnlist COMMA columnname carglist */ + 201, /* (354) columnlist ::= columnname carglist */ + 193, /* (355) nm ::= ID|INDEXED */ + 193, /* (356) nm ::= STRING */ + 193, /* (357) nm ::= JOIN_KW */ + 208, /* (358) typetoken ::= typename */ + 209, /* (359) typename ::= ID|STRING */ + 210, /* (360) signed ::= plus_num */ + 210, /* (361) signed ::= minus_num */ + 207, /* (362) carglist ::= carglist ccons */ + 207, /* (363) carglist ::= */ + 215, /* (364) ccons ::= NULL onconf */ + 215, /* (365) ccons ::= GENERATED ALWAYS AS generated */ + 215, /* (366) ccons ::= AS generated */ + 202, /* (367) conslist_opt ::= COMMA conslist */ + 228, /* (368) conslist ::= conslist tconscomma tcons */ + 228, /* (369) conslist ::= tcons */ + 229, /* (370) tconscomma ::= */ + 233, /* (371) defer_subclause_opt ::= defer_subclause */ + 235, /* (372) resolvetype ::= raisetype */ + 239, /* (373) selectnowith ::= oneselect */ + 240, /* (374) oneselect ::= values */ + 254, /* (375) sclp ::= selcollist COMMA */ + 255, /* (376) as ::= ID|STRING */ + 272, /* (377) returning ::= */ + 217, /* (378) expr ::= term */ + 274, /* (379) likeop ::= LIKE_KW|MATCH */ + 262, /* (380) exprlist ::= nexprlist */ + 284, /* (381) nmnum ::= plus_num */ + 284, /* (382) nmnum ::= nm */ + 284, /* (383) nmnum ::= ON */ + 284, /* (384) nmnum ::= DELETE */ + 284, /* (385) nmnum ::= DEFAULT */ + 211, /* (386) plus_num ::= INTEGER|FLOAT */ + 289, /* (387) foreach_clause ::= */ + 289, /* (388) foreach_clause ::= FOR EACH ROW */ + 292, /* (389) trnm ::= nm */ + 293, /* (390) tridxby ::= */ + 294, /* (391) database_kw_opt ::= DATABASE */ + 294, /* (392) database_kw_opt ::= */ + 297, /* (393) kwcolumn_opt ::= */ + 297, /* (394) kwcolumn_opt ::= COLUMNKW */ + 299, /* (395) vtabarglist ::= vtabarg */ + 299, /* (396) vtabarglist ::= vtabarglist COMMA vtabarg */ + 300, /* (397) vtabarg ::= vtabarg vtabargtoken */ + 303, /* (398) anylist ::= */ + 303, /* (399) anylist ::= anylist LP anylist RP */ + 303, /* (400) anylist ::= anylist ANY */ + 266, /* (401) with ::= */ }; /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number @@ -157080,372 +164364,389 @@ -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 */ + -5, /* (95) values ::= values COMMA LP nexprlist RP */ + -1, /* (96) distinct ::= DISTINCT */ + -1, /* (97) distinct ::= ALL */ + 0, /* (98) distinct ::= */ + 0, /* (99) sclp ::= */ + -5, /* (100) selcollist ::= sclp scanpt expr scanpt as */ + -3, /* (101) selcollist ::= sclp scanpt STAR */ + -5, /* (102) selcollist ::= sclp scanpt nm DOT STAR */ + -2, /* (103) as ::= AS nm */ + 0, /* (104) as ::= */ + 0, /* (105) from ::= */ + -2, /* (106) from ::= FROM seltablist */ + -2, /* (107) stl_prefix ::= seltablist joinop */ + 0, /* (108) stl_prefix ::= */ + -7, /* (109) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ + -9, /* (110) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ + -7, /* (111) seltablist ::= stl_prefix LP select RP as on_opt using_opt */ + -7, /* (112) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ + 0, /* (113) dbnm ::= */ + -2, /* (114) dbnm ::= DOT nm */ + -1, /* (115) fullname ::= nm */ + -3, /* (116) fullname ::= nm DOT nm */ + -1, /* (117) xfullname ::= nm */ + -3, /* (118) xfullname ::= nm DOT nm */ + -5, /* (119) xfullname ::= nm DOT nm AS nm */ + -3, /* (120) xfullname ::= nm AS nm */ + -1, /* (121) joinop ::= COMMA|JOIN */ + -2, /* (122) joinop ::= JOIN_KW JOIN */ + -3, /* (123) joinop ::= JOIN_KW nm JOIN */ + -4, /* (124) joinop ::= JOIN_KW nm nm JOIN */ + -2, /* (125) on_opt ::= ON expr */ + 0, /* (126) on_opt ::= */ + 0, /* (127) indexed_opt ::= */ + -3, /* (128) indexed_opt ::= INDEXED BY nm */ + -2, /* (129) indexed_opt ::= NOT INDEXED */ + -4, /* (130) using_opt ::= USING LP idlist RP */ + 0, /* (131) using_opt ::= */ + 0, /* (132) orderby_opt ::= */ + -3, /* (133) orderby_opt ::= ORDER BY sortlist */ + -5, /* (134) sortlist ::= sortlist COMMA expr sortorder nulls */ + -3, /* (135) sortlist ::= expr sortorder nulls */ + -1, /* (136) sortorder ::= ASC */ + -1, /* (137) sortorder ::= DESC */ + 0, /* (138) sortorder ::= */ + -2, /* (139) nulls ::= NULLS FIRST */ + -2, /* (140) nulls ::= NULLS LAST */ + 0, /* (141) nulls ::= */ + 0, /* (142) groupby_opt ::= */ + -3, /* (143) groupby_opt ::= GROUP BY nexprlist */ + 0, /* (144) having_opt ::= */ + -2, /* (145) having_opt ::= HAVING expr */ + 0, /* (146) limit_opt ::= */ + -2, /* (147) limit_opt ::= LIMIT expr */ + -4, /* (148) limit_opt ::= LIMIT expr OFFSET expr */ + -4, /* (149) limit_opt ::= LIMIT expr COMMA expr */ + -6, /* (150) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ + 0, /* (151) where_opt ::= */ + -2, /* (152) where_opt ::= WHERE expr */ + 0, /* (153) where_opt_ret ::= */ + -2, /* (154) where_opt_ret ::= WHERE expr */ + -2, /* (155) where_opt_ret ::= RETURNING selcollist */ + -4, /* (156) where_opt_ret ::= WHERE expr RETURNING selcollist */ + -9, /* (157) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ + -5, /* (158) setlist ::= setlist COMMA nm EQ expr */ + -7, /* (159) setlist ::= setlist COMMA LP idlist RP EQ expr */ + -3, /* (160) setlist ::= nm EQ expr */ + -5, /* (161) setlist ::= LP idlist RP EQ expr */ + -7, /* (162) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + -8, /* (163) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ + 0, /* (164) upsert ::= */ + -2, /* (165) upsert ::= RETURNING selcollist */ + -12, /* (166) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ + -9, /* (167) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ + -5, /* (168) upsert ::= ON CONFLICT DO NOTHING returning */ + -8, /* (169) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ + -2, /* (170) returning ::= RETURNING selcollist */ + -2, /* (171) insert_cmd ::= INSERT orconf */ + -1, /* (172) insert_cmd ::= REPLACE */ + 0, /* (173) idlist_opt ::= */ + -3, /* (174) idlist_opt ::= LP idlist RP */ + -3, /* (175) idlist ::= idlist COMMA nm */ + -1, /* (176) idlist ::= nm */ + -3, /* (177) expr ::= LP expr RP */ + -1, /* (178) expr ::= ID|INDEXED */ + -1, /* (179) expr ::= JOIN_KW */ + -3, /* (180) expr ::= nm DOT nm */ + -5, /* (181) expr ::= nm DOT nm DOT nm */ + -1, /* (182) term ::= NULL|FLOAT|BLOB */ + -1, /* (183) term ::= STRING */ + -1, /* (184) term ::= INTEGER */ + -1, /* (185) expr ::= VARIABLE */ + -3, /* (186) expr ::= expr COLLATE ID|STRING */ + -6, /* (187) expr ::= CAST LP expr AS typetoken RP */ + -5, /* (188) expr ::= ID|INDEXED LP distinct exprlist RP */ + -4, /* (189) expr ::= ID|INDEXED LP STAR RP */ + -6, /* (190) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ + -5, /* (191) expr ::= ID|INDEXED LP STAR RP filter_over */ + -1, /* (192) term ::= CTIME_KW */ + -5, /* (193) expr ::= LP nexprlist COMMA expr RP */ + -3, /* (194) expr ::= expr AND expr */ + -3, /* (195) expr ::= expr OR expr */ + -3, /* (196) expr ::= expr LT|GT|GE|LE expr */ + -3, /* (197) expr ::= expr EQ|NE expr */ + -3, /* (198) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + -3, /* (199) expr ::= expr PLUS|MINUS expr */ + -3, /* (200) expr ::= expr STAR|SLASH|REM expr */ + -3, /* (201) expr ::= expr CONCAT expr */ + -2, /* (202) likeop ::= NOT LIKE_KW|MATCH */ + -3, /* (203) expr ::= expr likeop expr */ + -5, /* (204) expr ::= expr likeop expr ESCAPE expr */ + -2, /* (205) expr ::= expr ISNULL|NOTNULL */ + -3, /* (206) expr ::= expr NOT NULL */ + -3, /* (207) expr ::= expr IS expr */ + -4, /* (208) expr ::= expr IS NOT expr */ + -2, /* (209) expr ::= NOT expr */ + -2, /* (210) expr ::= BITNOT expr */ + -2, /* (211) expr ::= PLUS|MINUS expr */ + -3, /* (212) expr ::= expr PTR expr */ + -1, /* (213) between_op ::= BETWEEN */ + -2, /* (214) between_op ::= NOT BETWEEN */ + -5, /* (215) expr ::= expr between_op expr AND expr */ + -1, /* (216) in_op ::= IN */ + -2, /* (217) in_op ::= NOT IN */ + -5, /* (218) expr ::= expr in_op LP exprlist RP */ + -3, /* (219) expr ::= LP select RP */ + -5, /* (220) expr ::= expr in_op LP select RP */ + -5, /* (221) expr ::= expr in_op nm dbnm paren_exprlist */ + -4, /* (222) expr ::= EXISTS LP select RP */ + -5, /* (223) expr ::= CASE case_operand case_exprlist case_else END */ + -5, /* (224) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + -4, /* (225) case_exprlist ::= WHEN expr THEN expr */ + -2, /* (226) case_else ::= ELSE expr */ + 0, /* (227) case_else ::= */ + -1, /* (228) case_operand ::= expr */ + 0, /* (229) case_operand ::= */ + 0, /* (230) exprlist ::= */ + -3, /* (231) nexprlist ::= nexprlist COMMA expr */ + -1, /* (232) nexprlist ::= expr */ + 0, /* (233) paren_exprlist ::= */ + -3, /* (234) paren_exprlist ::= LP exprlist RP */ + -12, /* (235) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + -1, /* (236) uniqueflag ::= UNIQUE */ + 0, /* (237) uniqueflag ::= */ + 0, /* (238) eidlist_opt ::= */ + -3, /* (239) eidlist_opt ::= LP eidlist RP */ + -5, /* (240) eidlist ::= eidlist COMMA nm collate sortorder */ + -3, /* (241) eidlist ::= nm collate sortorder */ + 0, /* (242) collate ::= */ + -2, /* (243) collate ::= COLLATE ID|STRING */ + -4, /* (244) cmd ::= DROP INDEX ifexists fullname */ + -2, /* (245) cmd ::= VACUUM vinto */ + -3, /* (246) cmd ::= VACUUM nm vinto */ + -2, /* (247) vinto ::= INTO expr */ + 0, /* (248) vinto ::= */ + -3, /* (249) cmd ::= PRAGMA nm dbnm */ + -5, /* (250) cmd ::= PRAGMA nm dbnm EQ nmnum */ + -6, /* (251) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + -5, /* (252) cmd ::= PRAGMA nm dbnm EQ minus_num */ + -6, /* (253) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + -2, /* (254) plus_num ::= PLUS INTEGER|FLOAT */ + -2, /* (255) minus_num ::= MINUS INTEGER|FLOAT */ + -5, /* (256) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + -11, /* (257) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + -1, /* (258) trigger_time ::= BEFORE|AFTER */ + -2, /* (259) trigger_time ::= INSTEAD OF */ + 0, /* (260) trigger_time ::= */ + -1, /* (261) trigger_event ::= DELETE|INSERT */ + -1, /* (262) trigger_event ::= UPDATE */ + -3, /* (263) trigger_event ::= UPDATE OF idlist */ + 0, /* (264) when_clause ::= */ + -2, /* (265) when_clause ::= WHEN expr */ + -3, /* (266) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + -2, /* (267) trigger_cmd_list ::= trigger_cmd SEMI */ + -3, /* (268) trnm ::= nm DOT nm */ + -3, /* (269) tridxby ::= INDEXED BY nm */ + -2, /* (270) tridxby ::= NOT INDEXED */ + -9, /* (271) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ + -8, /* (272) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + -6, /* (273) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + -3, /* (274) trigger_cmd ::= scanpt select scanpt */ + -4, /* (275) expr ::= RAISE LP IGNORE RP */ + -6, /* (276) expr ::= RAISE LP raisetype COMMA nm RP */ + -1, /* (277) raisetype ::= ROLLBACK */ + -1, /* (278) raisetype ::= ABORT */ + -1, /* (279) raisetype ::= FAIL */ + -4, /* (280) cmd ::= DROP TRIGGER ifexists fullname */ + -6, /* (281) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + -3, /* (282) cmd ::= DETACH database_kw_opt expr */ + 0, /* (283) key_opt ::= */ + -2, /* (284) key_opt ::= KEY expr */ + -1, /* (285) cmd ::= REINDEX */ + -3, /* (286) cmd ::= REINDEX nm dbnm */ + -1, /* (287) cmd ::= ANALYZE */ + -3, /* (288) cmd ::= ANALYZE nm dbnm */ + -6, /* (289) cmd ::= ALTER TABLE fullname RENAME TO nm */ + -7, /* (290) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + -6, /* (291) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ + -1, /* (292) add_column_fullname ::= fullname */ + -8, /* (293) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + -1, /* (294) cmd ::= create_vtab */ + -4, /* (295) cmd ::= create_vtab LP vtabarglist RP */ + -8, /* (296) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 0, /* (297) vtabarg ::= */ + -1, /* (298) vtabargtoken ::= ANY */ + -3, /* (299) vtabargtoken ::= lp anylist RP */ + -1, /* (300) lp ::= LP */ + -2, /* (301) with ::= WITH wqlist */ + -3, /* (302) with ::= WITH RECURSIVE wqlist */ + -1, /* (303) wqas ::= AS */ + -2, /* (304) wqas ::= AS MATERIALIZED */ + -3, /* (305) wqas ::= AS NOT MATERIALIZED */ + -6, /* (306) wqitem ::= nm eidlist_opt wqas LP select RP */ + -1, /* (307) wqlist ::= wqitem */ + -3, /* (308) wqlist ::= wqlist COMMA wqitem */ + -1, /* (309) windowdefn_list ::= windowdefn */ + -3, /* (310) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + -5, /* (311) windowdefn ::= nm AS LP window RP */ + -5, /* (312) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + -6, /* (313) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + -4, /* (314) window ::= ORDER BY sortlist frame_opt */ + -5, /* (315) window ::= nm ORDER BY sortlist frame_opt */ + -1, /* (316) window ::= frame_opt */ + -2, /* (317) window ::= nm frame_opt */ + 0, /* (318) frame_opt ::= */ + -3, /* (319) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + -6, /* (320) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + -1, /* (321) range_or_rows ::= RANGE|ROWS|GROUPS */ + -1, /* (322) frame_bound_s ::= frame_bound */ + -2, /* (323) frame_bound_s ::= UNBOUNDED PRECEDING */ + -1, /* (324) frame_bound_e ::= frame_bound */ + -2, /* (325) frame_bound_e ::= UNBOUNDED FOLLOWING */ + -2, /* (326) frame_bound ::= expr PRECEDING|FOLLOWING */ + -2, /* (327) frame_bound ::= CURRENT ROW */ + 0, /* (328) frame_exclude_opt ::= */ + -2, /* (329) frame_exclude_opt ::= EXCLUDE frame_exclude */ + -2, /* (330) frame_exclude ::= NO OTHERS */ + -2, /* (331) frame_exclude ::= CURRENT ROW */ + -1, /* (332) frame_exclude ::= GROUP|TIES */ + -2, /* (333) window_clause ::= WINDOW windowdefn_list */ + -2, /* (334) filter_over ::= filter_clause over_clause */ + -1, /* (335) filter_over ::= over_clause */ + -1, /* (336) filter_over ::= filter_clause */ + -4, /* (337) over_clause ::= OVER LP window RP */ + -2, /* (338) over_clause ::= OVER nm */ + -5, /* (339) filter_clause ::= FILTER LP WHERE expr RP */ + -1, /* (340) input ::= cmdlist */ + -2, /* (341) cmdlist ::= cmdlist ecmd */ + -1, /* (342) cmdlist ::= ecmd */ + -1, /* (343) ecmd ::= SEMI */ + -2, /* (344) ecmd ::= cmdx SEMI */ + -3, /* (345) ecmd ::= explain cmdx SEMI */ + 0, /* (346) trans_opt ::= */ + -1, /* (347) trans_opt ::= TRANSACTION */ + -2, /* (348) trans_opt ::= TRANSACTION nm */ + -1, /* (349) savepoint_opt ::= SAVEPOINT */ + 0, /* (350) savepoint_opt ::= */ + -2, /* (351) cmd ::= create_table create_table_args */ + -1, /* (352) table_option_set ::= table_option */ + -4, /* (353) columnlist ::= columnlist COMMA columnname carglist */ + -2, /* (354) columnlist ::= columnname carglist */ + -1, /* (355) nm ::= ID|INDEXED */ + -1, /* (356) nm ::= STRING */ + -1, /* (357) nm ::= JOIN_KW */ + -1, /* (358) typetoken ::= typename */ + -1, /* (359) typename ::= ID|STRING */ + -1, /* (360) signed ::= plus_num */ + -1, /* (361) signed ::= minus_num */ + -2, /* (362) carglist ::= carglist ccons */ + 0, /* (363) carglist ::= */ + -2, /* (364) ccons ::= NULL onconf */ + -4, /* (365) ccons ::= GENERATED ALWAYS AS generated */ + -2, /* (366) ccons ::= AS generated */ + -2, /* (367) conslist_opt ::= COMMA conslist */ + -3, /* (368) conslist ::= conslist tconscomma tcons */ + -1, /* (369) conslist ::= tcons */ + 0, /* (370) tconscomma ::= */ + -1, /* (371) defer_subclause_opt ::= defer_subclause */ + -1, /* (372) resolvetype ::= raisetype */ + -1, /* (373) selectnowith ::= oneselect */ + -1, /* (374) oneselect ::= values */ + -2, /* (375) sclp ::= selcollist COMMA */ + -1, /* (376) as ::= ID|STRING */ + 0, /* (377) returning ::= */ + -1, /* (378) expr ::= term */ + -1, /* (379) likeop ::= LIKE_KW|MATCH */ + -1, /* (380) exprlist ::= nexprlist */ + -1, /* (381) nmnum ::= plus_num */ + -1, /* (382) nmnum ::= nm */ + -1, /* (383) nmnum ::= ON */ + -1, /* (384) nmnum ::= DELETE */ + -1, /* (385) nmnum ::= DEFAULT */ + -1, /* (386) plus_num ::= INTEGER|FLOAT */ + 0, /* (387) foreach_clause ::= */ + -3, /* (388) foreach_clause ::= FOR EACH ROW */ + -1, /* (389) trnm ::= nm */ + 0, /* (390) tridxby ::= */ + -1, /* (391) database_kw_opt ::= DATABASE */ + 0, /* (392) database_kw_opt ::= */ + 0, /* (393) kwcolumn_opt ::= */ + -1, /* (394) kwcolumn_opt ::= COLUMNKW */ + -1, /* (395) vtabarglist ::= vtabarg */ + -3, /* (396) vtabarglist ::= vtabarglist COMMA vtabarg */ + -2, /* (397) vtabarg ::= vtabarg vtabargtoken */ + 0, /* (398) anylist ::= */ + -4, /* (399) anylist ::= anylist LP anylist RP */ + -2, /* (400) anylist ::= anylist ANY */ + 0, /* (401) with ::= */ }; static void yy_accept(yyParser*); /* Forward Declaration */ @@ -157475,54 +164776,6 @@ (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 @@ -157545,16 +164798,16 @@ { sqlite3FinishCoding(pParse); } break; case 3: /* cmd ::= BEGIN transtype trans_opt */ -{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy192);} +{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy394);} break; case 4: /* transtype ::= */ -{yymsp[1].minor.yy192 = TK_DEFERRED;} +{yymsp[1].minor.yy394 = 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 321: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==321); +{yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/} break; case 8: /* cmd ::= COMMIT|END trans_opt */ case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9); @@ -157577,7 +164830,7 @@ 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.yy394,0,0,yymsp[-2].minor.yy394); } break; case 14: /* createkw ::= CREATE */ @@ -157585,96 +164838,112 @@ 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 98: /* distinct ::= */ yytestcase(yyruleno==98); + case 242: /* collate ::= */ yytestcase(yyruleno==242); +{yymsp[1].minor.yy394 = 0;} break; case 16: /* ifnotexists ::= IF NOT EXISTS */ -{yymsp[-2].minor.yy192 = 1;} +{yymsp[-2].minor.yy394 = 1;} break; case 17: /* temp ::= TEMP */ - case 46: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==46); -{yymsp[0].minor.yy192 = 1;} +{yymsp[0].minor.yy394 = 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.yy285,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.yy47); + sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy47); } break; - case 22: /* table_options ::= WITHOUT nm */ + case 21: /* table_option_set ::= */ +{yymsp[1].minor.yy285 = 0;} + break; + case 22: /* table_option_set ::= table_option_set COMMA table_option */ +{yylhsminor.yy285 = yymsp[-2].minor.yy285|yymsp[0].minor.yy285;} + yymsp[-2].minor.yy285 = yylhsminor.yy285; + 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.yy285 = TF_WithoutRowid | TF_NoVisibleRowid; + }else{ + yymsp[-1].minor.yy285 = 0; + sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); + } +} + break; + case 24: /* table_option ::= nm */ +{ + if( yymsp[0].minor.yy0.n==6 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"strict",6)==0 ){ + yylhsminor.yy285 = TF_Strict; }else{ - yymsp[-1].minor.yy192 = 0; + yylhsminor.yy285 = 0; sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); } } + yymsp[0].minor.yy285 = yylhsminor.yy285; break; - case 23: /* columnname ::= nm typetoken */ -{sqlite3AddColumn(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} + 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 104: /* as ::= */ yytestcase(yyruleno==104); {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.yy522 = 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); + case 32: /* ccons ::= CONSTRAINT nm */ + case 67: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==67); {pParse->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.yy528,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.yy528,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.yy528,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.yy528, 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,177 +164953,162 @@ 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.yy394);} 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.yy394,yymsp[0].minor.yy394,yymsp[-2].minor.yy394);} 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.yy394,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.yy528,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.yy322,yymsp[0].minor.yy394);} break; - case 41: /* ccons ::= defer_subclause */ -{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy192);} + case 43: /* ccons ::= defer_subclause */ +{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy394);} 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.yy528,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.yy528,&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.yy394 = 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.yy394 = 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.yy394 = (yymsp[-1].minor.yy394 & ~yymsp[0].minor.yy231.mask) | yymsp[0].minor.yy231.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.yy231.value = 0; yymsp[-1].minor.yy231.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.yy231.value = 0; yymsp[-2].minor.yy231.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.yy231.value = yymsp[0].minor.yy394; yymsp[-2].minor.yy231.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.yy231.value = yymsp[0].minor.yy394<<8; yymsp[-2].minor.yy231.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.yy394 = 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.yy394 = 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.yy394 = 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.yy394 = 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.yy394 = 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.yy394 = 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 171: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==171); +{yymsp[-1].minor.yy394 = yymsp[0].minor.yy394;} 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 214: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==214); + case 217: /* in_op ::= NOT IN */ yytestcase(yyruleno==217); + case 243: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==243); +{yymsp[-1].minor.yy394 = 1;} break; - case 64: /* tconscomma ::= COMMA */ + case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ +{yymsp[-1].minor.yy394 = 0;} + break; + case 66: /* tconscomma ::= COMMA */ {pParse->constraintName.n = 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 68: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ +{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy322,yymsp[0].minor.yy394,yymsp[-2].minor.yy394,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 69: /* tcons ::= UNIQUE LP sortlist RP onconf */ +{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy322,yymsp[0].minor.yy394,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.yy528,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.yy322, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy322, yymsp[-1].minor.yy394); + sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy394); } 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.yy394 = 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.yy394 = yymsp[0].minor.yy394;} break; - case 75: /* resolvetype ::= IGNORE */ -{yymsp[0].minor.yy192 = OE_Ignore;} + case 77: /* resolvetype ::= IGNORE */ +{yymsp[0].minor.yy394 = 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 172: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==172); +{yymsp[0].minor.yy394 = 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.yy131, 0, yymsp[-1].minor.yy394); } 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.yy322, yymsp[0].minor.yy47, yymsp[-7].minor.yy394, yymsp[-5].minor.yy394); } 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.yy131, 1, yymsp[-1].minor.yy394); } 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); + sqlite3Select(pParse, yymsp[0].minor.yy47, &dest); + sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy47); } 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); - } - yymsp[-2].minor.yy539 = p; -} + case 85: /* select ::= WITH wqlist selectnowith */ +{yymsp[-2].minor.yy47 = attachWithToSelect(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy521);} 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 86: /* select ::= WITH RECURSIVE wqlist selectnowith */ +{yymsp[-3].minor.yy47 = attachWithToSelect(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy521);} break; - case 85: /* select ::= selectnowith */ + case 87: /* select ::= selectnowith */ { - Select *p = yymsp[0].minor.yy539; + Select *p = yymsp[0].minor.yy47; if( p ){ parserDoubleLinkSelect(pParse, p); } - yymsp[0].minor.yy539 = p; /*A-overwrites-X*/ + yymsp[0].minor.yy47 = 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.yy47; + Select *pLhs = yymsp[-2].minor.yy47; if( pRhs && pRhs->pPrior ){ SrcList *pFrom; Token x; @@ -157864,140 +165118,140 @@ 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.yy394; 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( yymsp[-1].minor.yy394!=TK_ALL ) pParse->hasCompound = 1; }else{ sqlite3SelectDelete(pParse->db, pLhs); } - yymsp[-2].minor.yy539 = pRhs; + yymsp[-2].minor.yy47 = 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.yy394 = 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.yy394 = 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.yy47 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy322,yymsp[-5].minor.yy131,yymsp[-4].minor.yy528,yymsp[-3].minor.yy322,yymsp[-2].minor.yy528,yymsp[-1].minor.yy322,yymsp[-7].minor.yy394,yymsp[0].minor.yy528); } 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.yy47 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy322,yymsp[-6].minor.yy131,yymsp[-5].minor.yy528,yymsp[-4].minor.yy322,yymsp[-3].minor.yy528,yymsp[-1].minor.yy322,yymsp[-8].minor.yy394,yymsp[0].minor.yy528); + if( yymsp[-9].minor.yy47 ){ + yymsp[-9].minor.yy47->pWinDefn = yymsp[-2].minor.yy41; }else{ - sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy303); + sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy41); } } 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.yy47 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy322,0,0,0,0,0,SF_Values,0); } break; - case 93: /* values ::= values COMMA LP nexprlist RP */ + case 95: /* values ::= values COMMA LP nexprlist RP */ { - Select *pRight, *pLeft = yymsp[-4].minor.yy539; - pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy242,0,0,0,0,0,SF_Values|SF_MultiValue,0); + Select *pRight, *pLeft = yymsp[-4].minor.yy47; + pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy322,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; + yymsp[-4].minor.yy47 = pRight; }else{ - yymsp[-4].minor.yy539 = pLeft; + yymsp[-4].minor.yy47 = pLeft; } } break; - case 94: /* distinct ::= DISTINCT */ -{yymsp[0].minor.yy192 = SF_Distinct;} + case 96: /* distinct ::= DISTINCT */ +{yymsp[0].minor.yy394 = SF_Distinct;} break; - case 95: /* distinct ::= ALL */ -{yymsp[0].minor.yy192 = SF_All;} + case 97: /* distinct ::= ALL */ +{yymsp[0].minor.yy394 = 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 99: /* sclp ::= */ + case 132: /* orderby_opt ::= */ yytestcase(yyruleno==132); + case 142: /* groupby_opt ::= */ yytestcase(yyruleno==142); + case 230: /* exprlist ::= */ yytestcase(yyruleno==230); + case 233: /* paren_exprlist ::= */ yytestcase(yyruleno==233); + case 238: /* eidlist_opt ::= */ yytestcase(yyruleno==238); +{yymsp[1].minor.yy322 = 0;} break; - case 98: /* selcollist ::= sclp scanpt expr scanpt as */ + case 100: /* 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.yy322 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[-2].minor.yy528); + if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy322, &yymsp[0].minor.yy0, 1); + sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy322,yymsp[-3].minor.yy522,yymsp[-1].minor.yy522); } break; - case 99: /* selcollist ::= sclp scanpt STAR */ + case 101: /* selcollist ::= sclp scanpt STAR */ { Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0); - yymsp[-2].minor.yy242 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy242, p); + yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy322, p); } break; - case 100: /* selcollist ::= sclp scanpt nm DOT STAR */ + case 102: /* 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 *pLeft = tokenExpr(pParse, TK_ID, yymsp[-2].minor.yy0); Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); - yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy242, pDot); + yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, 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 103: /* as ::= AS nm */ + case 114: /* dbnm ::= DOT nm */ yytestcase(yyruleno==114); + case 254: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==254); + case 255: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==255); {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 105: /* from ::= */ + case 108: /* stl_prefix ::= */ yytestcase(yyruleno==108); +{yymsp[1].minor.yy131 = 0;} break; - case 104: /* from ::= FROM seltablist */ + case 106: /* from ::= FROM seltablist */ { - yymsp[-1].minor.yy47 = yymsp[0].minor.yy47; - sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy47); + yymsp[-1].minor.yy131 = yymsp[0].minor.yy131; + sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy131); } break; - case 105: /* stl_prefix ::= seltablist joinop */ + case 107: /* 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.yy131 && yymsp[-1].minor.yy131->nSrc>0) ) yymsp[-1].minor.yy131->a[yymsp[-1].minor.yy131->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy394; } break; - case 107: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ + case 109: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ { - 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[-6].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy131,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy528,yymsp[0].minor.yy254); + sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy131, &yymsp[-2].minor.yy0); } break; - case 108: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ + case 110: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ { - 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[-8].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy131,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy528,yymsp[0].minor.yy254); + sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy131, yymsp[-4].minor.yy322); } break; - case 109: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */ + case 111: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */ { - 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[-6].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy131,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy47,yymsp[-1].minor.yy528,yymsp[0].minor.yy254); } break; - case 110: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ + case 112: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ { - 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[-6].minor.yy131==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy528==0 && yymsp[0].minor.yy254==0 ){ + yymsp[-6].minor.yy131 = yymsp[-4].minor.yy131; + }else if( yymsp[-4].minor.yy131->nSrc==1 ){ + yymsp[-6].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy131,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy528,yymsp[0].minor.yy254); + if( yymsp[-6].minor.yy131 ){ + SrcItem *pNew = &yymsp[-6].minor.yy131->a[yymsp[-6].minor.yy131->nSrc-1]; + SrcItem *pOld = yymsp[-4].minor.yy131->a; pNew->zName = pOld->zName; pNew->zDatabase = pOld->zDatabase; pNew->pSelect = pOld->pSelect; @@ -158010,250 +165264,263 @@ pOld->zName = pOld->zDatabase = 0; pOld->pSelect = 0; } - sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy47); + sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy131); }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(yymsp[-4].minor.yy131); + pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy131,0,0,0,0,SF_NestedFrom,0); + yymsp[-6].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy131,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy528,yymsp[0].minor.yy254); } } break; - case 111: /* dbnm ::= */ - case 125: /* indexed_opt ::= */ yytestcase(yyruleno==125); + case 113: /* dbnm ::= */ + case 127: /* indexed_opt ::= */ yytestcase(yyruleno==127); {yymsp[1].minor.yy0.z=0; yymsp[1].minor.yy0.n=0;} break; - case 113: /* fullname ::= nm */ + case 115: /* 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.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); + if( IN_RENAME_OBJECT && yylhsminor.yy131 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy131->a[0].zName, &yymsp[0].minor.yy0); } - yymsp[0].minor.yy47 = yylhsminor.yy47; + yymsp[0].minor.yy131 = yylhsminor.yy131; break; - case 114: /* fullname ::= nm DOT nm */ + case 116: /* 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.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); + if( IN_RENAME_OBJECT && yylhsminor.yy131 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy131->a[0].zName, &yymsp[0].minor.yy0); } - yymsp[-2].minor.yy47 = yylhsminor.yy47; + yymsp[-2].minor.yy131 = yylhsminor.yy131; break; - case 115: /* xfullname ::= nm */ -{yymsp[0].minor.yy47 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} + case 117: /* xfullname ::= nm */ +{yymsp[0].minor.yy131 = 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 118: /* xfullname ::= nm DOT nm */ +{yymsp[-2].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} break; - case 117: /* xfullname ::= nm DOT nm AS nm */ + case 119: /* 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.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ + if( yymsp[-4].minor.yy131 ) yymsp[-4].minor.yy131->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); } break; - case 118: /* xfullname ::= nm AS nm */ + case 120: /* 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.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ + if( yymsp[-2].minor.yy131 ) yymsp[-2].minor.yy131->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); } break; - case 119: /* joinop ::= COMMA|JOIN */ -{ yymsp[0].minor.yy192 = JT_INNER; } + case 121: /* joinop ::= COMMA|JOIN */ +{ yymsp[0].minor.yy394 = JT_INNER; } break; - case 120: /* joinop ::= JOIN_KW JOIN */ -{yymsp[-1].minor.yy192 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,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*/} - 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*/} - 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;} - 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 122: /* joinop ::= JOIN_KW JOIN */ +{yymsp[-1].minor.yy394 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} + break; + case 123: /* joinop ::= JOIN_KW nm JOIN */ +{yymsp[-2].minor.yy394 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} + break; + case 124: /* joinop ::= JOIN_KW nm nm JOIN */ +{yymsp[-3].minor.yy394 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} + break; + case 125: /* on_opt ::= ON expr */ + case 145: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==145); + case 152: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==152); + case 154: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==154); + case 226: /* case_else ::= ELSE expr */ yytestcase(yyruleno==226); + case 247: /* vinto ::= INTO expr */ yytestcase(yyruleno==247); +{yymsp[-1].minor.yy528 = yymsp[0].minor.yy528;} + break; + case 126: /* on_opt ::= */ + case 144: /* having_opt ::= */ yytestcase(yyruleno==144); + case 146: /* limit_opt ::= */ yytestcase(yyruleno==146); + case 151: /* where_opt ::= */ yytestcase(yyruleno==151); + case 153: /* where_opt_ret ::= */ yytestcase(yyruleno==153); + case 227: /* case_else ::= */ yytestcase(yyruleno==227); + case 229: /* case_operand ::= */ yytestcase(yyruleno==229); + case 248: /* vinto ::= */ yytestcase(yyruleno==248); +{yymsp[1].minor.yy528 = 0;} break; - case 126: /* indexed_opt ::= INDEXED BY nm */ + case 128: /* indexed_opt ::= INDEXED BY nm */ {yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;} break; - case 127: /* indexed_opt ::= NOT INDEXED */ + case 129: /* indexed_opt ::= 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;} + case 130: /* using_opt ::= USING LP idlist RP */ +{yymsp[-3].minor.yy254 = yymsp[-1].minor.yy254;} 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 131: /* using_opt ::= */ + case 173: /* idlist_opt ::= */ yytestcase(yyruleno==173); +{yymsp[1].minor.yy254 = 0;} + break; + case 133: /* orderby_opt ::= ORDER BY sortlist */ + case 143: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==143); +{yymsp[-2].minor.yy322 = yymsp[0].minor.yy322;} break; - case 132: /* sortlist ::= sortlist COMMA expr sortorder nulls */ + case 134: /* 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.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322,yymsp[-2].minor.yy528); + sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy322,yymsp[-1].minor.yy394,yymsp[0].minor.yy394); } break; - case 133: /* sortlist ::= expr sortorder nulls */ + case 135: /* 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.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy528); /*A-overwrites-Y*/ + sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy322,yymsp[-1].minor.yy394,yymsp[0].minor.yy394); } break; - case 134: /* sortorder ::= ASC */ -{yymsp[0].minor.yy192 = SQLITE_SO_ASC;} + case 136: /* sortorder ::= ASC */ +{yymsp[0].minor.yy394 = SQLITE_SO_ASC;} break; - case 135: /* sortorder ::= DESC */ -{yymsp[0].minor.yy192 = SQLITE_SO_DESC;} + case 137: /* sortorder ::= DESC */ +{yymsp[0].minor.yy394 = SQLITE_SO_DESC;} break; - case 136: /* sortorder ::= */ - case 139: /* nulls ::= */ yytestcase(yyruleno==139); -{yymsp[1].minor.yy192 = SQLITE_SO_UNDEFINED;} + case 138: /* sortorder ::= */ + case 141: /* nulls ::= */ yytestcase(yyruleno==141); +{yymsp[1].minor.yy394 = SQLITE_SO_UNDEFINED;} break; - case 137: /* nulls ::= NULLS FIRST */ -{yymsp[-1].minor.yy192 = SQLITE_SO_ASC;} + case 139: /* nulls ::= NULLS FIRST */ +{yymsp[-1].minor.yy394 = SQLITE_SO_ASC;} break; - case 138: /* nulls ::= NULLS LAST */ -{yymsp[-1].minor.yy192 = SQLITE_SO_DESC;} + case 140: /* nulls ::= NULLS LAST */ +{yymsp[-1].minor.yy394 = SQLITE_SO_DESC;} break; - case 145: /* limit_opt ::= LIMIT expr */ -{yymsp[-1].minor.yy202 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy202,0);} + case 147: /* limit_opt ::= LIMIT expr */ +{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,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 148: /* limit_opt ::= LIMIT expr OFFSET expr */ +{yymsp[-3].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);} 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 COMMA expr */ +{yymsp[-3].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,yymsp[-2].minor.yy528);} break; - case 148: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt */ + case 150: /* 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.yy131, &yymsp[-1].minor.yy0); + sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy131,yymsp[0].minor.yy528,0,0); } break; - case 151: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt */ + case 155: /* where_opt_ret ::= RETURNING selcollist */ +{sqlite3AddReturning(pParse,yymsp[0].minor.yy322); yymsp[-1].minor.yy528 = 0;} + break; + case 156: /* where_opt_ret ::= WHERE expr RETURNING selcollist */ +{sqlite3AddReturning(pParse,yymsp[0].minor.yy322); yymsp[-3].minor.yy528 = yymsp[-2].minor.yy528;} + break; + case 157: /* 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.yy131, &yymsp[-4].minor.yy0); + sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy322,"set list"); + yymsp[-5].minor.yy131 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy131, yymsp[-1].minor.yy131); + sqlite3Update(pParse,yymsp[-5].minor.yy131,yymsp[-2].minor.yy322,yymsp[0].minor.yy528,yymsp[-6].minor.yy394,0,0,0); } break; - case 152: /* setlist ::= setlist COMMA nm EQ expr */ + case 158: /* 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.yy322 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[0].minor.yy528); + sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, 1); } break; - case 153: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ + case 159: /* 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.yy322 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy322, yymsp[-3].minor.yy254, yymsp[0].minor.yy528); } break; - case 154: /* setlist ::= nm EQ expr */ + case 160: /* setlist ::= nm EQ expr */ { - yylhsminor.yy242 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy202); - sqlite3ExprListSetName(pParse, yylhsminor.yy242, &yymsp[-2].minor.yy0, 1); + yylhsminor.yy322 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy528); + sqlite3ExprListSetName(pParse, yylhsminor.yy322, &yymsp[-2].minor.yy0, 1); } - yymsp[-2].minor.yy242 = yylhsminor.yy242; + yymsp[-2].minor.yy322 = yylhsminor.yy322; break; - case 155: /* setlist ::= LP idlist RP EQ expr */ + case 161: /* setlist ::= LP idlist RP EQ expr */ { - yymsp[-4].minor.yy242 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy600, yymsp[0].minor.yy202); + yymsp[-4].minor.yy322 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy254, yymsp[0].minor.yy528); } break; - case 156: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + case 162: /* 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.yy131, yymsp[-1].minor.yy47, yymsp[-2].minor.yy254, yymsp[-5].minor.yy394, yymsp[0].minor.yy444); } break; - case 157: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ + case 163: /* 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.yy131, 0, yymsp[-3].minor.yy254, yymsp[-6].minor.yy394, 0); } break; - case 158: /* upsert ::= */ -{ yymsp[1].minor.yy318 = 0; } + case 164: /* upsert ::= */ +{ yymsp[1].minor.yy444 = 0; } + break; + case 165: /* upsert ::= RETURNING selcollist */ +{ yymsp[-1].minor.yy444 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy322); } 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 166: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ +{ yymsp[-11].minor.yy444 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy322,yymsp[-6].minor.yy528,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528,yymsp[0].minor.yy444);} 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 167: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ +{ yymsp[-8].minor.yy444 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy322,yymsp[-3].minor.yy528,0,0,yymsp[0].minor.yy444); } break; - case 161: /* upsert ::= ON CONFLICT DO NOTHING */ -{ yymsp[-3].minor.yy318 = sqlite3UpsertNew(pParse->db,0,0,0,0); } + case 168: /* upsert ::= ON CONFLICT DO NOTHING returning */ +{ yymsp[-4].minor.yy444 = 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 169: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ +{ yymsp[-7].minor.yy444 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528,0);} break; - case 166: /* idlist ::= idlist COMMA nm */ -{yymsp[-2].minor.yy600 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy600,&yymsp[0].minor.yy0);} + case 170: /* returning ::= RETURNING selcollist */ +{sqlite3AddReturning(pParse,yymsp[0].minor.yy322);} break; - case 167: /* idlist ::= nm */ -{yymsp[0].minor.yy600 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} + case 174: /* idlist_opt ::= LP idlist RP */ +{yymsp[-2].minor.yy254 = yymsp[-1].minor.yy254;} break; - case 168: /* expr ::= LP expr RP */ -{yymsp[-2].minor.yy202 = yymsp[-1].minor.yy202;} + case 175: /* idlist ::= idlist COMMA nm */ +{yymsp[-2].minor.yy254 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy254,&yymsp[0].minor.yy0);} 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 176: /* idlist ::= nm */ +{yymsp[0].minor.yy254 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} break; - case 171: /* expr ::= nm DOT nm */ + case 177: /* expr ::= LP expr RP */ +{yymsp[-2].minor.yy528 = yymsp[-1].minor.yy528;} + break; + case 178: /* expr ::= ID|INDEXED */ + case 179: /* expr ::= JOIN_KW */ yytestcase(yyruleno==179); +{yymsp[0].minor.yy528=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} + break; + case 180: /* 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.yy528 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); } - yymsp[-2].minor.yy202 = yylhsminor.yy202; + yymsp[-2].minor.yy528 = yylhsminor.yy528; break; - case 172: /* expr ::= nm DOT nm DOT nm */ + case 181: /* 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.yy528 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); } - yymsp[-4].minor.yy202 = yylhsminor.yy202; + yymsp[-4].minor.yy528 = yylhsminor.yy528; 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 182: /* term ::= NULL|FLOAT|BLOB */ + case 183: /* term ::= STRING */ yytestcase(yyruleno==183); +{yymsp[0].minor.yy528=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} break; - case 175: /* term ::= INTEGER */ + case 184: /* term ::= INTEGER */ { - yylhsminor.yy202 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); + yylhsminor.yy528 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); + if( yylhsminor.yy528 ) yylhsminor.yy528->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail); } - yymsp[0].minor.yy202 = yylhsminor.yy202; + yymsp[0].minor.yy528 = yylhsminor.yy528; break; - case 176: /* expr ::= VARIABLE */ + case 185: /* 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.yy528 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); + sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy528, 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 @@ -158262,159 +165529,167 @@ assert( t.n>=2 ); if( pParse->nested==0 ){ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t); - yymsp[0].minor.yy202 = 0; + yymsp[0].minor.yy528 = 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.yy528 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); + if( yymsp[0].minor.yy528 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy528->iTable); } } } break; - case 177: /* expr ::= expr COLLATE ID|STRING */ + case 186: /* expr ::= expr COLLATE ID|STRING */ { - yymsp[-2].minor.yy202 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy202, &yymsp[0].minor.yy0, 1); + yymsp[-2].minor.yy528 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy528, &yymsp[0].minor.yy0, 1); } break; - case 178: /* expr ::= CAST LP expr AS typetoken RP */ + case 187: /* expr ::= CAST LP expr AS typetoken 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); + yymsp[-5].minor.yy528 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); + sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy528, yymsp[-3].minor.yy528, 0); } break; - case 179: /* expr ::= ID|INDEXED LP distinct exprlist RP */ + case 188: /* expr ::= ID|INDEXED LP distinct exprlist RP */ { - yylhsminor.yy202 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy242, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy192); + yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy394); } - yymsp[-4].minor.yy202 = yylhsminor.yy202; + yymsp[-4].minor.yy528 = yylhsminor.yy528; break; - case 180: /* expr ::= ID|INDEXED LP STAR RP */ + case 189: /* expr ::= ID|INDEXED LP STAR RP */ { - yylhsminor.yy202 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); + yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); } - yymsp[-3].minor.yy202 = yylhsminor.yy202; + yymsp[-3].minor.yy528 = yylhsminor.yy528; break; - case 181: /* expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ + case 190: /* expr ::= ID|INDEXED LP distinct exprlist 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.yy528 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy322, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy394); + sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41); } - yymsp[-5].minor.yy202 = yylhsminor.yy202; + yymsp[-5].minor.yy528 = yylhsminor.yy528; break; - case 182: /* expr ::= ID|INDEXED LP STAR RP filter_over */ + case 191: /* expr ::= ID|INDEXED LP STAR RP filter_over */ { - yylhsminor.yy202 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); - sqlite3WindowAttach(pParse, yylhsminor.yy202, yymsp[0].minor.yy303); + yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); + sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41); } - yymsp[-4].minor.yy202 = yylhsminor.yy202; + yymsp[-4].minor.yy528 = yylhsminor.yy528; break; - case 183: /* term ::= CTIME_KW */ + case 192: /* term ::= CTIME_KW */ { - yylhsminor.yy202 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); + yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); } - yymsp[0].minor.yy202 = yylhsminor.yy202; + yymsp[0].minor.yy528 = yylhsminor.yy528; break; - case 184: /* expr ::= LP nexprlist COMMA expr RP */ + case 193: /* 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.yy322, yymsp[-1].minor.yy528); + yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); + if( yymsp[-4].minor.yy528 ){ + yymsp[-4].minor.yy528->x.pList = pList; if( ALWAYS(pList->nExpr) ){ - yymsp[-4].minor.yy202->flags |= pList->a[0].pExpr->flags & EP_Propagate; + yymsp[-4].minor.yy528->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 194: /* expr ::= expr AND expr */ +{yymsp[-2].minor.yy528=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);} 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 195: /* expr ::= expr OR expr */ + case 196: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==196); + case 197: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==197); + case 198: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==198); + case 199: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==199); + case 200: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==200); + case 201: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==201); +{yymsp[-2].minor.yy528=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);} break; - case 193: /* likeop ::= NOT LIKE_KW|MATCH */ + case 202: /* 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 203: /* 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.yy528); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy528); + yymsp[-2].minor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); + if( bNot ) yymsp[-2].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy528, 0); + if( yymsp[-2].minor.yy528 ) yymsp[-2].minor.yy528->flags |= EP_InfixFunc; } break; - case 195: /* expr ::= expr likeop expr ESCAPE expr */ + case 204: /* 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.yy528); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy528); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528); + yymsp[-4].minor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); + if( bNot ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); + if( yymsp[-4].minor.yy528 ) yymsp[-4].minor.yy528->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 205: /* expr ::= expr ISNULL|NOTNULL */ +{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy528,0);} break; - case 197: /* expr ::= expr NOT NULL */ -{yymsp[-2].minor.yy202 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy202,0);} + case 206: /* expr ::= expr NOT NULL */ +{yymsp[-2].minor.yy528 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy528,0);} break; - case 198: /* expr ::= expr IS expr */ + case 207: /* 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.yy528 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy528,yymsp[0].minor.yy528); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-2].minor.yy528, TK_ISNULL); } break; - case 199: /* expr ::= expr IS NOT expr */ + case 208: /* 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.yy528 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy528,yymsp[0].minor.yy528); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-3].minor.yy528, 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 209: /* expr ::= NOT expr */ + case 210: /* expr ::= BITNOT expr */ yytestcase(yyruleno==210); +{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy528, 0);/*A-overwrites-B*/} break; - case 202: /* expr ::= PLUS|MINUS expr */ + case 211: /* expr ::= PLUS|MINUS expr */ { - yymsp[-1].minor.yy202 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy202, 0); + yymsp[-1].minor.yy528 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy528, 0); /*A-overwrites-B*/ } break; - case 203: /* between_op ::= BETWEEN */ - case 206: /* in_op ::= IN */ yytestcase(yyruleno==206); -{yymsp[0].minor.yy192 = 0;} + case 212: /* expr ::= expr PTR expr */ +{ + ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy528); + pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy528); + yylhsminor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); +} + yymsp[-2].minor.yy528 = yylhsminor.yy528; break; - case 205: /* expr ::= expr between_op expr AND expr */ + case 213: /* between_op ::= BETWEEN */ + case 216: /* in_op ::= IN */ yytestcase(yyruleno==216); +{yymsp[0].minor.yy394 = 0;} + break; + case 215: /* expr ::= expr between_op expr AND 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; + ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528); + yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy528, 0); + if( yymsp[-4].minor.yy528 ){ + yymsp[-4].minor.yy528->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.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); } break; - case 208: /* expr ::= expr in_op LP exprlist RP */ + case 218: /* expr ::= expr in_op LP exprlist RP */ { - if( yymsp[-1].minor.yy242==0 ){ + if( yymsp[-1].minor.yy322==0 ){ /* Expressions of the form ** ** expr1 IN () @@ -158423,197 +165698,205 @@ ** simplify to constants 0 (false) and 1 (true), respectively, ** regardless of the value of expr1. */ - 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); + sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy528); + yymsp[-4].minor.yy528 = sqlite3Expr(pParse->db, TK_INTEGER, yymsp[-3].minor.yy394 ? "1" : "0"); }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.yy322->a[0].pExpr; + if( yymsp[-1].minor.yy322->nExpr==1 && sqlite3ExprIsConstant(pRHS) && yymsp[-4].minor.yy528->op!=TK_VECTOR ){ + yymsp[-1].minor.yy322->a[0].pExpr = 0; + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322); + pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0); + yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy528, pRHS); }else{ - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy242); + yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0); + if( yymsp[-4].minor.yy528==0 ){ + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322); + }else if( yymsp[-4].minor.yy528->pLeft->op==TK_VECTOR ){ + int nExpr = yymsp[-4].minor.yy528->pLeft->x.pList->nExpr; + Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy322); + if( pSelectRHS ){ + parserDoubleLinkSelect(pParse, pSelectRHS); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pSelectRHS); + } + }else{ + yymsp[-4].minor.yy528->x.pList = yymsp[-1].minor.yy322; + sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy528); + } } - if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); + if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); } } break; - case 209: /* expr ::= LP select RP */ + case 219: /* 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.yy528 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); + sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy528, yymsp[-1].minor.yy47); } break; - case 210: /* expr ::= expr in_op LP select RP */ + case 220: /* 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.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, yymsp[-1].minor.yy47); + if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); } break; - case 211: /* expr ::= expr in_op nm dbnm paren_exprlist */ + case 221: /* 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.yy322 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy322); + yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pSelect); + if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); } break; - case 212: /* expr ::= EXISTS LP select RP */ + case 222: /* 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.yy528 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); + sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy47); } break; - case 213: /* expr ::= CASE case_operand case_exprlist case_else END */ + case 223: /* 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.yy528 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy528, 0); + if( yymsp[-4].minor.yy528 ){ + yymsp[-4].minor.yy528->x.pList = yymsp[-1].minor.yy528 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528) : yymsp[-2].minor.yy322; + sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy528); }else{ - sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy242); - sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy202); + sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy322); + sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528); } } break; - case 214: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ + case 224: /* 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.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[-2].minor.yy528); + yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[0].minor.yy528); } break; - case 215: /* case_exprlist ::= WHEN expr THEN expr */ + case 225: /* 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.yy322 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528); + yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy322, yymsp[0].minor.yy528); } break; - case 218: /* case_operand ::= expr */ -{yymsp[0].minor.yy202 = yymsp[0].minor.yy202; /*A-overwrites-X*/} + case 228: /* case_operand ::= expr */ +{yymsp[0].minor.yy528 = yymsp[0].minor.yy528; /*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 231: /* nexprlist ::= nexprlist COMMA expr */ +{yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[0].minor.yy528);} break; - case 222: /* nexprlist ::= expr */ -{yymsp[0].minor.yy242 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy202); /*A-overwrites-Y*/} + case 232: /* nexprlist ::= expr */ +{yymsp[0].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy528); /*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 234: /* paren_exprlist ::= LP exprlist RP */ + case 239: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==239); +{yymsp[-2].minor.yy322 = yymsp[-1].minor.yy322;} break; - case 225: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + case 235: /* 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.yy322, yymsp[-10].minor.yy394, + &yymsp[-11].minor.yy0, yymsp[0].minor.yy528, SQLITE_SO_ASC, yymsp[-8].minor.yy394, 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 236: /* uniqueflag ::= UNIQUE */ + case 278: /* raisetype ::= ABORT */ yytestcase(yyruleno==278); +{yymsp[0].minor.yy394 = OE_Abort;} break; - case 227: /* uniqueflag ::= */ -{yymsp[1].minor.yy192 = OE_None;} + case 237: /* uniqueflag ::= */ +{yymsp[1].minor.yy394 = OE_None;} break; - case 230: /* eidlist ::= eidlist COMMA nm collate sortorder */ + case 240: /* 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.yy322 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); } break; - case 231: /* eidlist ::= nm collate sortorder */ + case 241: /* 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.yy322 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); /*A-overwrites-Y*/ } break; - case 234: /* cmd ::= DROP INDEX ifexists fullname */ -{sqlite3DropIndex(pParse, yymsp[0].minor.yy47, yymsp[-1].minor.yy192);} + case 244: /* cmd ::= DROP INDEX ifexists fullname */ +{sqlite3DropIndex(pParse, yymsp[0].minor.yy131, yymsp[-1].minor.yy394);} break; - case 235: /* cmd ::= VACUUM vinto */ -{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy202);} + case 245: /* cmd ::= VACUUM vinto */ +{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy528);} break; - case 236: /* cmd ::= VACUUM nm vinto */ -{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy202);} + case 246: /* cmd ::= VACUUM nm vinto */ +{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy528);} break; - case 239: /* cmd ::= PRAGMA nm dbnm */ + case 249: /* 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 250: /* 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 251: /* 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 252: /* 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 253: /* 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 256: /* 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.yy33, &all); } break; - case 247: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + case 257: /* 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.yy394, yymsp[-4].minor.yy180.a, yymsp[-4].minor.yy180.b, yymsp[-2].minor.yy131, yymsp[0].minor.yy528, yymsp[-10].minor.yy394, yymsp[-8].minor.yy394); yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/ } break; - case 248: /* trigger_time ::= BEFORE|AFTER */ -{ yymsp[0].minor.yy192 = yymsp[0].major; /*A-overwrites-X*/ } + case 258: /* trigger_time ::= BEFORE|AFTER */ +{ yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/ } break; - case 249: /* trigger_time ::= INSTEAD OF */ -{ yymsp[-1].minor.yy192 = TK_INSTEAD;} + case 259: /* trigger_time ::= INSTEAD OF */ +{ yymsp[-1].minor.yy394 = TK_INSTEAD;} break; - case 250: /* trigger_time ::= */ -{ yymsp[1].minor.yy192 = TK_BEFORE; } + case 260: /* trigger_time ::= */ +{ yymsp[1].minor.yy394 = 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 261: /* trigger_event ::= DELETE|INSERT */ + case 262: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==262); +{yymsp[0].minor.yy180.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy180.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 263: /* trigger_event ::= UPDATE OF idlist */ +{yymsp[-2].minor.yy180.a = TK_UPDATE; yymsp[-2].minor.yy180.b = yymsp[0].minor.yy254;} break; - case 254: /* when_clause ::= */ - case 273: /* key_opt ::= */ yytestcase(yyruleno==273); -{ yymsp[1].minor.yy202 = 0; } + case 264: /* when_clause ::= */ + case 283: /* key_opt ::= */ yytestcase(yyruleno==283); +{ yymsp[1].minor.yy528 = 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 265: /* when_clause ::= WHEN expr */ + case 284: /* key_opt ::= KEY expr */ yytestcase(yyruleno==284); +{ yymsp[-1].minor.yy528 = yymsp[0].minor.yy528; } break; - case 256: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + case 266: /* 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.yy33!=0 ); + yymsp[-2].minor.yy33->pLast->pNext = yymsp[-1].minor.yy33; + yymsp[-2].minor.yy33->pLast = yymsp[-1].minor.yy33; } break; - case 257: /* trigger_cmd_list ::= trigger_cmd SEMI */ + case 267: /* 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.yy33!=0 ); + yymsp[-1].minor.yy33->pLast = yymsp[-1].minor.yy33; } break; - case 258: /* trnm ::= nm DOT nm */ + case 268: /* trnm ::= nm DOT nm */ { yymsp[-2].minor.yy0 = yymsp[0].minor.yy0; sqlite3ErrorMsg(pParse, @@ -158621,344 +165904,369 @@ "statements within triggers"); } break; - case 259: /* tridxby ::= INDEXED BY nm */ + case 269: /* 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 270: /* 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 271: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ +{yylhsminor.yy33 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy131, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528, yymsp[-7].minor.yy394, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy522);} + yymsp[-8].minor.yy33 = yylhsminor.yy33; break; - case 262: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + case 272: /* 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.yy33 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy254,yymsp[-2].minor.yy47,yymsp[-6].minor.yy394,yymsp[-1].minor.yy444,yymsp[-7].minor.yy522,yymsp[0].minor.yy522);/*yylhsminor.yy33-overwrites-yymsp[-6].minor.yy394*/ } - yymsp[-7].minor.yy447 = yylhsminor.yy447; + yymsp[-7].minor.yy33 = yylhsminor.yy33; 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 273: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ +{yylhsminor.yy33 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy528, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy522);} + yymsp[-5].minor.yy33 = yylhsminor.yy33; 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 274: /* trigger_cmd ::= scanpt select scanpt */ +{yylhsminor.yy33 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy47, yymsp[-2].minor.yy522, yymsp[0].minor.yy522); /*yylhsminor.yy33-overwrites-yymsp[-1].minor.yy47*/} + yymsp[-2].minor.yy33 = yylhsminor.yy33; break; - case 265: /* expr ::= RAISE LP IGNORE RP */ + case 275: /* 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.yy528 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); + if( yymsp[-3].minor.yy528 ){ + yymsp[-3].minor.yy528->affExpr = OE_Ignore; } } break; - case 266: /* expr ::= RAISE LP raisetype COMMA nm RP */ + case 276: /* expr ::= RAISE LP raisetype COMMA nm 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.yy528 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); + if( yymsp[-5].minor.yy528 ) { + yymsp[-5].minor.yy528->affExpr = (char)yymsp[-3].minor.yy394; } } break; - case 267: /* raisetype ::= ROLLBACK */ -{yymsp[0].minor.yy192 = OE_Rollback;} + case 277: /* raisetype ::= ROLLBACK */ +{yymsp[0].minor.yy394 = OE_Rollback;} break; - case 269: /* raisetype ::= FAIL */ -{yymsp[0].minor.yy192 = OE_Fail;} + case 279: /* raisetype ::= FAIL */ +{yymsp[0].minor.yy394 = OE_Fail;} break; - case 270: /* cmd ::= DROP TRIGGER ifexists fullname */ + case 280: /* cmd ::= DROP TRIGGER ifexists fullname */ { - sqlite3DropTrigger(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy192); + sqlite3DropTrigger(pParse,yymsp[0].minor.yy131,yymsp[-1].minor.yy394); } break; - case 271: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + case 281: /* 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.yy528, yymsp[-1].minor.yy528, yymsp[0].minor.yy528); } break; - case 272: /* cmd ::= DETACH database_kw_opt expr */ + case 282: /* cmd ::= DETACH database_kw_opt expr */ { - sqlite3Detach(pParse, yymsp[0].minor.yy202); + sqlite3Detach(pParse, yymsp[0].minor.yy528); } break; - case 275: /* cmd ::= REINDEX */ + case 285: /* cmd ::= REINDEX */ {sqlite3Reindex(pParse, 0, 0);} break; - case 276: /* cmd ::= REINDEX nm dbnm */ + case 286: /* cmd ::= REINDEX nm dbnm */ {sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 277: /* cmd ::= ANALYZE */ + case 287: /* cmd ::= ANALYZE */ {sqlite3Analyze(pParse, 0, 0);} break; - case 278: /* cmd ::= ANALYZE nm dbnm */ + case 288: /* 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 289: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ { - sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy47,&yymsp[0].minor.yy0); + sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy131,&yymsp[0].minor.yy0); } break; - case 280: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + case 290: /* 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 291: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ +{ + sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy131, &yymsp[0].minor.yy0); +} + break; + case 292: /* add_column_fullname ::= fullname */ { disableLookaside(pParse); - sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy47); + sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy131); } break; - case 282: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + case 293: /* 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.yy131, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); } break; - case 283: /* cmd ::= create_vtab */ + case 294: /* cmd ::= create_vtab */ {sqlite3VtabFinishParse(pParse,0);} break; - case 284: /* cmd ::= create_vtab LP vtabarglist RP */ + case 295: /* 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 296: /* 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.yy394); } break; - case 286: /* vtabarg ::= */ + case 297: /* 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 298: /* vtabargtoken ::= ANY */ + case 299: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==299); + case 300: /* lp ::= LP */ yytestcase(yyruleno==300); {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 301: /* with ::= WITH wqlist */ + case 302: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==302); +{ sqlite3WithPush(pParse, yymsp[0].minor.yy521, 1); } + break; + case 303: /* wqas ::= AS */ +{yymsp[0].minor.yy516 = M10d_Any;} + break; + case 304: /* wqas ::= AS MATERIALIZED */ +{yymsp[-1].minor.yy516 = M10d_Yes;} + break; + case 305: /* wqas ::= AS NOT MATERIALIZED */ +{yymsp[-2].minor.yy516 = M10d_No;} break; - case 292: /* wqlist ::= nm eidlist_opt AS LP select RP */ + case 306: /* wqitem ::= nm 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.yy385 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy322, yymsp[-1].minor.yy47, yymsp[-3].minor.yy516); /*A-overwrites-X*/ } break; - case 293: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ + case 307: /* 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.yy521 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy385); /*A-overwrites-X*/ } break; - case 294: /* windowdefn_list ::= windowdefn */ -{ yylhsminor.yy303 = yymsp[0].minor.yy303; } - yymsp[0].minor.yy303 = yylhsminor.yy303; + case 308: /* wqlist ::= wqlist COMMA wqitem */ +{ + yymsp[-2].minor.yy521 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy521, yymsp[0].minor.yy385); +} break; - case 295: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ + case 309: /* windowdefn_list ::= windowdefn */ +{ yylhsminor.yy41 = yymsp[0].minor.yy41; } + yymsp[0].minor.yy41 = yylhsminor.yy41; + break; + case 310: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ { - 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; + assert( yymsp[0].minor.yy41!=0 ); + sqlite3WindowChain(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy41); + yymsp[0].minor.yy41->pNextWin = yymsp[-2].minor.yy41; + yylhsminor.yy41 = yymsp[0].minor.yy41; } - yymsp[-2].minor.yy303 = yylhsminor.yy303; + yymsp[-2].minor.yy41 = yylhsminor.yy41; break; - case 296: /* windowdefn ::= nm AS LP window RP */ + case 311: /* windowdefn ::= nm AS LP window RP */ { - if( ALWAYS(yymsp[-1].minor.yy303) ){ - yymsp[-1].minor.yy303->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); + if( ALWAYS(yymsp[-1].minor.yy41) ){ + yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); } - yylhsminor.yy303 = yymsp[-1].minor.yy303; + yylhsminor.yy41 = yymsp[-1].minor.yy41; } - yymsp[-4].minor.yy303 = yylhsminor.yy303; + yymsp[-4].minor.yy41 = yylhsminor.yy41; break; - case 297: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + case 312: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ { - yymsp[-4].minor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, yymsp[-2].minor.yy242, yymsp[-1].minor.yy242, 0); + yymsp[-4].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, 0); } break; - case 298: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + case 313: /* window ::= nm 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); + yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, &yymsp[-5].minor.yy0); } - yymsp[-5].minor.yy303 = yylhsminor.yy303; + yymsp[-5].minor.yy41 = yylhsminor.yy41; break; - case 299: /* window ::= ORDER BY sortlist frame_opt */ + case 314: /* window ::= ORDER BY sortlist frame_opt */ { - yymsp[-3].minor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, 0, yymsp[-1].minor.yy242, 0); + yymsp[-3].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, 0); } break; - case 300: /* window ::= nm ORDER BY sortlist frame_opt */ + case 315: /* window ::= nm ORDER BY sortlist frame_opt */ { - yylhsminor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, 0, yymsp[-1].minor.yy242, &yymsp[-4].minor.yy0); + yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0); } - yymsp[-4].minor.yy303 = yylhsminor.yy303; + yymsp[-4].minor.yy41 = yylhsminor.yy41; break; - case 301: /* window ::= frame_opt */ - case 320: /* filter_over ::= over_clause */ yytestcase(yyruleno==320); + case 316: /* window ::= frame_opt */ + case 335: /* filter_over ::= over_clause */ yytestcase(yyruleno==335); { - yylhsminor.yy303 = yymsp[0].minor.yy303; + yylhsminor.yy41 = yymsp[0].minor.yy41; } - yymsp[0].minor.yy303 = yylhsminor.yy303; + yymsp[0].minor.yy41 = yylhsminor.yy41; break; - case 302: /* window ::= nm frame_opt */ + case 317: /* window ::= nm frame_opt */ { - yylhsminor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, 0, 0, &yymsp[-1].minor.yy0); + yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, 0, &yymsp[-1].minor.yy0); } - yymsp[-1].minor.yy303 = yylhsminor.yy303; + yymsp[-1].minor.yy41 = yylhsminor.yy41; break; - case 303: /* frame_opt ::= */ + case 318: /* frame_opt ::= */ { - yymsp[1].minor.yy303 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); + yymsp[1].minor.yy41 = 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 319: /* 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.yy41 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy394, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy516); } - yymsp[-2].minor.yy303 = yylhsminor.yy303; + yymsp[-2].minor.yy41 = yylhsminor.yy41; break; - case 305: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + case 320: /* 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.yy41 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy394, yymsp[-3].minor.yy595.eType, yymsp[-3].minor.yy595.pExpr, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, yymsp[0].minor.yy516); } - yymsp[-5].minor.yy303 = yylhsminor.yy303; + yymsp[-5].minor.yy41 = yylhsminor.yy41; 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 322: /* frame_bound_s ::= frame_bound */ + case 324: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==324); +{yylhsminor.yy595 = yymsp[0].minor.yy595;} + yymsp[0].minor.yy595 = yylhsminor.yy595; 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 323: /* frame_bound_s ::= UNBOUNDED PRECEDING */ + case 325: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==325); + case 327: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==327); +{yylhsminor.yy595.eType = yymsp[-1].major; yylhsminor.yy595.pExpr = 0;} + yymsp[-1].minor.yy595 = yylhsminor.yy595; 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 326: /* frame_bound ::= expr PRECEDING|FOLLOWING */ +{yylhsminor.yy595.eType = yymsp[0].major; yylhsminor.yy595.pExpr = yymsp[-1].minor.yy528;} + yymsp[-1].minor.yy595 = yylhsminor.yy595; break; - case 313: /* frame_exclude_opt ::= */ -{yymsp[1].minor.yy58 = 0;} + case 328: /* frame_exclude_opt ::= */ +{yymsp[1].minor.yy516 = 0;} break; - case 314: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ -{yymsp[-1].minor.yy58 = yymsp[0].minor.yy58;} + case 329: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ +{yymsp[-1].minor.yy516 = yymsp[0].minor.yy516;} 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 330: /* frame_exclude ::= NO OTHERS */ + case 331: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==331); +{yymsp[-1].minor.yy516 = yymsp[-1].major; /*A-overwrites-X*/} break; - case 317: /* frame_exclude ::= GROUP|TIES */ -{yymsp[0].minor.yy58 = yymsp[0].major; /*A-overwrites-X*/} + case 332: /* frame_exclude ::= GROUP|TIES */ +{yymsp[0].minor.yy516 = yymsp[0].major; /*A-overwrites-X*/} break; - case 318: /* window_clause ::= WINDOW windowdefn_list */ -{ yymsp[-1].minor.yy303 = yymsp[0].minor.yy303; } + case 333: /* window_clause ::= WINDOW windowdefn_list */ +{ yymsp[-1].minor.yy41 = yymsp[0].minor.yy41; } break; - case 319: /* filter_over ::= filter_clause over_clause */ + case 334: /* 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.yy41 ){ + yymsp[0].minor.yy41->pFilter = yymsp[-1].minor.yy528; + }else{ + sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528); + } + yylhsminor.yy41 = yymsp[0].minor.yy41; } - yymsp[-1].minor.yy303 = yylhsminor.yy303; + yymsp[-1].minor.yy41 = yylhsminor.yy41; break; - case 321: /* filter_over ::= filter_clause */ + case 336: /* filter_over ::= filter_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.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); + if( yylhsminor.yy41 ){ + yylhsminor.yy41->eFrmType = TK_FILTER; + yylhsminor.yy41->pFilter = yymsp[0].minor.yy528; }else{ - sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy202); + sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy528); } } - yymsp[0].minor.yy303 = yylhsminor.yy303; + yymsp[0].minor.yy41 = yylhsminor.yy41; break; - case 322: /* over_clause ::= OVER LP window RP */ + case 337: /* over_clause ::= OVER LP window RP */ { - yymsp[-3].minor.yy303 = yymsp[-1].minor.yy303; - assert( yymsp[-3].minor.yy303!=0 ); + yymsp[-3].minor.yy41 = yymsp[-1].minor.yy41; + assert( yymsp[-3].minor.yy41!=0 ); } break; - case 323: /* over_clause ::= OVER nm */ + case 338: /* 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.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); + if( yymsp[-1].minor.yy41 ){ + yymsp[-1].minor.yy41->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 339: /* filter_clause ::= FILTER LP WHERE expr RP */ +{ yymsp[-4].minor.yy528 = yymsp[-1].minor.yy528; } 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); + /* (340) input ::= cmdlist */ yytestcase(yyruleno==340); + /* (341) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==341); + /* (342) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=342); + /* (343) ecmd ::= SEMI */ yytestcase(yyruleno==343); + /* (344) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==344); + /* (345) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=345); + /* (346) trans_opt ::= */ yytestcase(yyruleno==346); + /* (347) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==347); + /* (348) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==348); + /* (349) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==349); + /* (350) savepoint_opt ::= */ yytestcase(yyruleno==350); + /* (351) cmd ::= create_table create_table_args */ yytestcase(yyruleno==351); + /* (352) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=352); + /* (353) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==353); + /* (354) columnlist ::= columnname carglist */ yytestcase(yyruleno==354); + /* (355) nm ::= ID|INDEXED */ yytestcase(yyruleno==355); + /* (356) nm ::= STRING */ yytestcase(yyruleno==356); + /* (357) nm ::= JOIN_KW */ yytestcase(yyruleno==357); + /* (358) typetoken ::= typename */ yytestcase(yyruleno==358); + /* (359) typename ::= ID|STRING */ yytestcase(yyruleno==359); + /* (360) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=360); + /* (361) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=361); + /* (362) carglist ::= carglist ccons */ yytestcase(yyruleno==362); + /* (363) carglist ::= */ yytestcase(yyruleno==363); + /* (364) ccons ::= NULL onconf */ yytestcase(yyruleno==364); + /* (365) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==365); + /* (366) ccons ::= AS generated */ yytestcase(yyruleno==366); + /* (367) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==367); + /* (368) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==368); + /* (369) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=369); + /* (370) tconscomma ::= */ yytestcase(yyruleno==370); + /* (371) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=371); + /* (372) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=372); + /* (373) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=373); + /* (374) oneselect ::= values */ yytestcase(yyruleno==374); + /* (375) sclp ::= selcollist COMMA */ yytestcase(yyruleno==375); + /* (376) as ::= ID|STRING */ yytestcase(yyruleno==376); + /* (377) returning ::= */ yytestcase(yyruleno==377); + /* (378) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=378); + /* (379) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==379); + /* (380) exprlist ::= nexprlist */ yytestcase(yyruleno==380); + /* (381) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=381); + /* (382) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=382); + /* (383) nmnum ::= ON */ yytestcase(yyruleno==383); + /* (384) nmnum ::= DELETE */ yytestcase(yyruleno==384); + /* (385) nmnum ::= DEFAULT */ yytestcase(yyruleno==385); + /* (386) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==386); + /* (387) foreach_clause ::= */ yytestcase(yyruleno==387); + /* (388) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==388); + /* (389) trnm ::= nm */ yytestcase(yyruleno==389); + /* (390) tridxby ::= */ yytestcase(yyruleno==390); + /* (391) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==391); + /* (392) database_kw_opt ::= */ yytestcase(yyruleno==392); + /* (393) kwcolumn_opt ::= */ yytestcase(yyruleno==393); + /* (394) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==394); + /* (395) vtabarglist ::= vtabarg */ yytestcase(yyruleno==395); + /* (396) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==396); + /* (397) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==397); + /* (398) anylist ::= */ yytestcase(yyruleno==398); + /* (399) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==399); + /* (400) anylist ::= anylist ANY */ yytestcase(yyruleno==400); + /* (401) with ::= */ yytestcase(yyruleno==401); break; /********** End reduce actions ************************************************/ }; @@ -159110,12 +166418,56 @@ } #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 YYSTACKDEPTH>0 + if( yypParser->yytos>=yypParser->yystackEnd ){ + yyStackOverflow(yypParser); + break; + } +#else + if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){ + if( yyGrowStack(yypParser) ){ + yyStackOverflow(yypParser); + break; + } + } +#endif + } + 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 +166523,13 @@ 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 +166579,7 @@ break; #endif } - }while( yypParser->yytos>yypParser->yystack ); + } #ifndef NDEBUG if( yyTraceFILE ){ yyStackEntry *i; @@ -159289,8 +166640,8 @@ ** 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 +166666,49 @@ #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 +166773,21 @@ ** 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 +166808,87 @@ '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[147] = { + 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[147] = { 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[147] = { 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[147] = { 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 +166906,19 @@ 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 +166942,7 @@ ** 18: TRANSACTION RIGHT ** 19: WHEN ** 20: SET HAVING -** 21: IF +** 21: MATERIALIZED IF ** 22: ROWS ** 23: SELECT ** 24: @@ -159682,7 +167038,7 @@ ** 114: INTERSECT UNBOUNDED ** 115: ** 116: -** 117: ON +** 117: RETURNING ON ** 118: ** 119: WHERE ** 120: NO INNER @@ -159700,7 +167056,7 @@ int i, j; const char *zKW; if( n>=2 ){ - i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n) % 127; + i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n*1) % 127; for(i=((int)aKWHash[i])-1; i>=0; i=((int)aKWNext[i])-1){ if( aKWLen[i]!=n ) continue; zKW = &zKWText[aKWOffset[i]]; @@ -159805,63 +167161,65 @@ testcase( i==85 ); /* PLAN */ testcase( i==86 ); /* ANALYZE */ testcase( i==87 ); /* PRAGMA */ - testcase( i==88 ); /* ABORT */ - testcase( i==89 ); /* UPDATE */ - testcase( i==90 ); /* VALUES */ - testcase( i==91 ); /* VIRTUAL */ - testcase( i==92 ); /* ALWAYS */ - testcase( i==93 ); /* WHEN */ - testcase( i==94 ); /* WHERE */ - testcase( i==95 ); /* RECURSIVE */ - testcase( i==96 ); /* AFTER */ - testcase( i==97 ); /* RENAME */ - testcase( i==98 ); /* AND */ - testcase( i==99 ); /* DEFERRED */ - testcase( i==100 ); /* DISTINCT */ - testcase( i==101 ); /* IS */ - testcase( i==102 ); /* AUTOINCREMENT */ - testcase( i==103 ); /* TO */ - testcase( i==104 ); /* IN */ - testcase( i==105 ); /* CAST */ - testcase( i==106 ); /* COLUMN */ - testcase( i==107 ); /* COMMIT */ - testcase( i==108 ); /* CONFLICT */ - testcase( i==109 ); /* CROSS */ - testcase( i==110 ); /* CURRENT_TIMESTAMP */ - testcase( i==111 ); /* CURRENT_TIME */ - testcase( i==112 ); /* CURRENT */ - testcase( i==113 ); /* PARTITION */ - testcase( i==114 ); /* DROP */ - testcase( i==115 ); /* PRECEDING */ - testcase( i==116 ); /* FAIL */ - testcase( i==117 ); /* LAST */ - testcase( i==118 ); /* FILTER */ - testcase( i==119 ); /* REPLACE */ - testcase( i==120 ); /* FIRST */ - testcase( i==121 ); /* FOLLOWING */ - testcase( i==122 ); /* FROM */ - testcase( i==123 ); /* FULL */ - testcase( i==124 ); /* LIMIT */ - testcase( i==125 ); /* IF */ - testcase( i==126 ); /* ORDER */ - testcase( i==127 ); /* RESTRICT */ - testcase( i==128 ); /* OTHERS */ - testcase( i==129 ); /* OVER */ - testcase( i==130 ); /* RIGHT */ - testcase( i==131 ); /* ROLLBACK */ - testcase( i==132 ); /* ROWS */ - testcase( i==133 ); /* ROW */ - testcase( i==134 ); /* UNBOUNDED */ - testcase( i==135 ); /* UNION */ - testcase( i==136 ); /* USING */ - testcase( i==137 ); /* VACUUM */ - testcase( i==138 ); /* VIEW */ - testcase( i==139 ); /* WINDOW */ - testcase( i==140 ); /* DO */ - testcase( i==141 ); /* BY */ - testcase( i==142 ); /* INITIALLY */ - testcase( i==143 ); /* ALL */ - testcase( i==144 ); /* PRIMARY */ + testcase( i==88 ); /* MATERIALIZED */ + testcase( i==89 ); /* DEFERRED */ + testcase( i==90 ); /* DISTINCT */ + testcase( i==91 ); /* IS */ + testcase( i==92 ); /* UPDATE */ + testcase( i==93 ); /* VALUES */ + testcase( i==94 ); /* VIRTUAL */ + testcase( i==95 ); /* ALWAYS */ + testcase( i==96 ); /* WHEN */ + testcase( i==97 ); /* WHERE */ + testcase( i==98 ); /* RECURSIVE */ + testcase( i==99 ); /* ABORT */ + testcase( i==100 ); /* AFTER */ + testcase( i==101 ); /* RENAME */ + testcase( i==102 ); /* AND */ + testcase( i==103 ); /* DROP */ + testcase( i==104 ); /* PARTITION */ + testcase( i==105 ); /* AUTOINCREMENT */ + testcase( i==106 ); /* TO */ + testcase( i==107 ); /* IN */ + testcase( i==108 ); /* CAST */ + testcase( i==109 ); /* COLUMN */ + testcase( i==110 ); /* COMMIT */ + testcase( i==111 ); /* CONFLICT */ + testcase( i==112 ); /* CROSS */ + testcase( i==113 ); /* CURRENT_TIMESTAMP */ + testcase( i==114 ); /* CURRENT_TIME */ + testcase( i==115 ); /* CURRENT */ + testcase( i==116 ); /* PRECEDING */ + testcase( i==117 ); /* FAIL */ + testcase( i==118 ); /* LAST */ + testcase( i==119 ); /* FILTER */ + testcase( i==120 ); /* REPLACE */ + testcase( i==121 ); /* FIRST */ + testcase( i==122 ); /* FOLLOWING */ + testcase( i==123 ); /* FROM */ + testcase( i==124 ); /* FULL */ + testcase( i==125 ); /* LIMIT */ + testcase( i==126 ); /* IF */ + testcase( i==127 ); /* ORDER */ + testcase( i==128 ); /* RESTRICT */ + testcase( i==129 ); /* OTHERS */ + testcase( i==130 ); /* OVER */ + testcase( i==131 ); /* RETURNING */ + testcase( i==132 ); /* RIGHT */ + testcase( i==133 ); /* ROLLBACK */ + testcase( i==134 ); /* ROWS */ + testcase( i==135 ); /* ROW */ + testcase( i==136 ); /* UNBOUNDED */ + testcase( i==137 ); /* UNION */ + testcase( i==138 ); /* USING */ + testcase( i==139 ); /* VACUUM */ + testcase( i==140 ); /* VIEW */ + testcase( i==141 ); /* WINDOW */ + testcase( i==142 ); /* DO */ + testcase( i==143 ); /* BY */ + testcase( i==144 ); /* INITIALLY */ + testcase( i==145 ); /* ALL */ + testcase( i==146 ); /* PRIMARY */ *pType = aKWCode[i]; break; } @@ -159873,7 +167231,7 @@ 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; *pzName = zKWText + aKWOffset[i]; @@ -160031,6 +167389,9 @@ for(i=2; (c=z[i])!=0 && c!='\n'; i++){} *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ return i; + }else if( z[1]=='>' ){ + *tokenType = TK_PTR; + return 2 + (z[2]=='>'); } *tokenType = TK_MINUS; return 1; @@ -160242,7 +167603,7 @@ if( n==0 ) *tokenType = TK_ILLEGAL; return i; } - case CC_KYWD: { + case CC_KYWD0: { for(i=1; aiClass[z[i]]<=CC_KYWD; i++){} if( IdChar(z[i]) ){ /* This token started out using characters that can appear in keywords, @@ -160272,10 +167633,19 @@ ** 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 +167661,9 @@ } /* -** 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 +167671,7 @@ 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 +167684,6 @@ } pParse->rc = SQLITE_OK; pParse->zTail = zSql; - assert( pzErrMsg!=0 ); #ifdef SQLITE_DEBUG if( db->flags & SQLITE_ParserTrace ){ printf("parser: [[[%s]]]\n", zSql); @@ -160340,7 +167706,7 @@ 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); @@ -160360,6 +167726,7 @@ #endif /* SQLITE_OMIT_WINDOWFUNC */ if( AtomicLoad(&db->u1.isInterrupted) ){ pParse->rc = SQLITE_INTERRUPT; + pParse->nErr++; break; } if( tokenType==TK_SPACE ){ @@ -160389,7 +167756,10 @@ tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed); #endif /* SQLITE_OMIT_WINDOWFUNC */ }else{ - sqlite3ErrorMsg(pParse, "unrecognized token: \"%.*s\"", n, zSql); + Token x; + x.z = zSql; + x.n = n; + sqlite3ErrorMsg(pParse, "unrecognized token: \"%T\"", &x); break; } } @@ -160417,58 +167787,30 @@ 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)); + } + 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; + db->pParse = pParentParse; assert( nErr==0 || pParse->rc!=SQLITE_OK ); return nErr; } @@ -161049,9 +168391,6 @@ #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 @@ -161086,8 +168425,8 @@ sqlite3DbstatRegister, #endif sqlite3TestExtInit, -#ifdef SQLITE_ENABLE_JSON1 - sqlite3Json1Init, +#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) + sqlite3JsonTableFunctions, #endif #ifdef SQLITE_ENABLE_STMTVTAB sqlite3StmtVtabInit, @@ -161304,7 +168643,7 @@ sqlite3GlobalConfig.isPCacheInit = 1; rc = sqlite3OsInit(); } -#ifdef SQLITE_ENABLE_DESERIALIZE +#ifndef SQLITE_OMIT_DESERIALIZE if( rc==SQLITE_OK ){ rc = sqlite3MemdbInit(); } @@ -161719,12 +169058,12 @@ } #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 */ default: { rc = SQLITE_ERROR; @@ -161896,7 +169235,7 @@ 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 ){ @@ -162085,7 +169424,7 @@ /* ** 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 +169433,14 @@ #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 +169449,9 @@ #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 +169476,9 @@ ** 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 ){ @@ -162235,15 +169582,45 @@ /* 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 +169646,7 @@ ** 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; } @@ -162355,7 +169732,7 @@ 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 +169741,11 @@ ** 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 +169780,7 @@ 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 +169798,7 @@ /* 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) ){ @@ -162753,7 +170133,7 @@ */ 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; } @@ -162782,7 +170162,6 @@ FuncDestructor *pDestructor ){ FuncDef *p; - int nName; int extraFlags; assert( sqlite3_mutex_held(db->mutex) ); @@ -162792,7 +170171,7 @@ || ((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); } @@ -163251,6 +170645,34 @@ } #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 +170765,7 @@ 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; @@ -163366,6 +170788,8 @@ 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 +170838,7 @@ ** 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 +170853,11 @@ 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 +170935,19 @@ 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 @@ -163769,6 +171208,8 @@ if( newLimit>=0 ){ /* IMP: R-52476-28732 */ if( newLimit>aHardLimit[limitId] ){ newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */ + }else if( newLimit<1 && limitId==SQLITE_LIMIT_LENGTH ){ + newLimit = 1; } db->aLimit[limitId] = newLimit; } @@ -164040,7 +171481,7 @@ */ 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 +171541,8 @@ ** 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 +171577,9 @@ } } 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,7 +171591,15 @@ 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 @@ -164298,7 +171747,7 @@ 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 +171809,12 @@ 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 +171825,7 @@ } #endif sqlite3_free_filename(zOpen); - return rc & 0xff; + return rc; } @@ -164676,7 +172125,7 @@ /* Locate the table in question */ pTab = sqlite3FindTable(db, zTableName, zDbName); - if( !pTab || pTab->pSelect ){ + if( !pTab || IsView(pTab) ){ pTab = 0; goto error_out; } @@ -164687,7 +172136,7 @@ }else{ for(iCol=0; iColnCol; iCol++){ pCol = &pTab->aCol[iCol]; - if( 0==sqlite3StrICmp(pCol->zName, zColumnName) ){ + if( 0==sqlite3StrICmp(pCol->zCnName, zColumnName) ){ break; } } @@ -164714,7 +172163,7 @@ */ 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; @@ -164821,7 +172270,9 @@ } rc = SQLITE_OK; }else{ + int nSave = db->busyHandler.nBusy; rc = sqlite3OsFileControl(fd, op, pArg); + db->busyHandler.nBusy = nSave; } sqlite3BtreeLeave(pBtree); } @@ -164919,12 +172370,16 @@ ** 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; } @@ -165047,17 +172502,31 @@ */ 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_LOCALTIME_FAULT, onoff, xAlt); ** - ** If parameter onoff is non-zero, subsequent calls to localtime() - ** and its variants fail. If onoff is zero, undo this setting. + ** 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; } @@ -165162,12 +172631,16 @@ */ 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 +172677,94 @@ 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 sqlite3SelectTrace in *ptr + ** op==1 Set sqlite3SelectTrace to the value *ptr + ** op==3 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 = sqlite3SelectTrace; break; + case 1: sqlite3SelectTrace = *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); + u64 iInt = sqlite3LogEstToInt(rLogEst); + va_arg(ap, int*)[0] = rLogEst; + va_arg(ap, u64*)[0] = iInt; + va_arg(ap, int*)[0] = sqlite3LogEst(iInt); + 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 } va_end(ap); #endif /* SQLITE_UNTESTABLE */ @@ -165311,7 +172872,7 @@ 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 +172915,14 @@ ** 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 +172933,7 @@ return 0; #else zFilename = sqlite3_filename_journal(zFilename); - zFilename += sqlite3Strlen30(zFilename) + 1; + if( zFilename ) zFilename += sqlite3Strlen30(zFilename) + 1; return zFilename; #endif } @@ -165439,7 +173002,7 @@ 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) ){ rc = sqlite3BtreeBeginTrans(pBt, 0, 0); if( rc==SQLITE_OK ){ rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot); @@ -165475,10 +173038,10 @@ 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 ){ @@ -165527,7 +173090,7 @@ 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)); @@ -166646,7 +174209,7 @@ ** 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 +174226,18 @@ ** 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 /* @@ -167132,6 +174696,7 @@ 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 ** @@ -167151,7 +174716,7 @@ SQLITE_PRIVATE int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr); /* 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 @@ -167184,25 +174749,26 @@ 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]. @@ -168053,7 +175619,7 @@ 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 +176339,7 @@ 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 +176355,8 @@ ** 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 +176409,20 @@ */ 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); @@ -170462,14 +178028,20 @@ */ 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; } /* @@ -170882,9 +178454,12 @@ ** 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 +178489,7 @@ */ 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 +178517,24 @@ 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 +178543,7 @@ #ifdef SQLITE_TEST if( rc==SQLITE_OK ){ - rc = sqlite3Fts3ExprInitTestInterface(db, pHash); + rc = sqlite3Fts3ExprInitTestInterface(db, &pHash->hash); } #endif @@ -170976,23 +178552,26 @@ ** 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 +178580,7 @@ /* 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; @@ -171348,7 +178927,7 @@ 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 ){ @@ -171998,16 +179577,15 @@ #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 +179666,9 @@ ); 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; } @@ -172213,8 +179791,8 @@ 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); @@ -172852,6 +180430,9 @@ if( bEofSave==0 && pNear->iDocid==iDocid ) break; } assert( rc!=SQLITE_OK || pPhrase->bIncr==0 ); + if( rc==SQLITE_OK && pNear->bEof!=bEofSave ){ + rc = FTS_CORRUPT_VTAB; + } } if( bTreeEof ){ while( rc==SQLITE_OK && !pNear->bEof ){ @@ -173274,6 +180855,7 @@ 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 +180911,6 @@ } pCsr->iCol = 0; - rc = SQLITE_OK; }else{ pCsr->isEof = 1; } @@ -173383,6 +180968,7 @@ 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; @@ -173653,7 +181239,7 @@ ** 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; @@ -173734,7 +181320,7 @@ rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); if( rc==SQLITE_OK ){ nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken; - pRet = (Fts3Expr *)fts3MallocZero(nByte); + pRet = (Fts3Expr *)sqlite3Fts3MallocZero(nByte); if( !pRet ){ rc = SQLITE_NOMEM; }else{ @@ -173989,7 +181575,7 @@ 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 +181610,11 @@ 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 +181754,7 @@ && 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; @@ -174197,7 +181788,7 @@ /* 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; @@ -177053,7 +184644,7 @@ ** 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 */ @@ -177082,7 +184673,9 @@ }; 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; } @@ -178427,8 +186020,18 @@ char *aCopy; PendingList *pList = (PendingList *)fts3HashData(pElem); int nCopy = pList->nData+1; - pReader->zTerm = (char *)fts3HashKey(pElem); - pReader->nTerm = fts3HashKeysize(pElem); + + int nTerm = fts3HashKeysize(pElem); + if( (nTerm+1)>pReader->nTermAlloc ){ + sqlite3_free(pReader->zTerm); + pReader->zTerm = (char*)sqlite3_malloc((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_malloc(nCopy); if( !aCopy ) return SQLITE_NOMEM; memcpy(aCopy, pList->aData, nCopy); @@ -178681,9 +186284,7 @@ */ 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); } @@ -178899,7 +186500,7 @@ if( rc==0 ){ rc = pRhs->iIdx - pLhs->iIdx; } - assert( rc!=0 ); + assert_fts3_nc( rc!=0 ); return rc; } @@ -179095,8 +186696,8 @@ int nNext /* Size of buffer zNext in bytes */ ){ int n; - UNUSED_PARAMETER(nNext); - for(n=0; nterm, 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; @@ -181269,7 +188870,11 @@ 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; @@ -181427,17 +189032,20 @@ 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 - ); + ); if( rc==SQLITE_OK ){ memcpy(pNode->block.a, aBlock, nBlock); pNode->block.n = nBlock; @@ -181910,7 +189518,7 @@ 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; } } @@ -182906,6 +190514,10 @@ /* #include */ /* #include */ +#ifndef SQLITE_AMALGAMATION +typedef sqlite3_int64 i64; +#endif + /* ** Characters that may appear in the second argument to matchinfo(). */ @@ -182956,9 +190568,9 @@ 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 */ }; @@ -183023,9 +190635,8 @@ + sizeof(MatchinfoBuffer); 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] + sizeof(u32)*((int)nElem+1); @@ -183123,7 +190734,7 @@ ** 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); @@ -183232,10 +190843,10 @@ ** 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( iIteraPhrase[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; @@ -183364,7 +190975,7 @@ 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 +191040,10 @@ ** 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. @@ -183997,10 +191607,12 @@ ** 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,9 +191646,8 @@ /* 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); for(i=0; inPhrase; i++){ @@ -184428,8 +192039,8 @@ 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 { @@ -184448,7 +192059,7 @@ 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); @@ -184497,7 +192108,7 @@ 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; @@ -184518,13 +192129,13 @@ 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 = fts3ExprIterate(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 +192536,7 @@ 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 +193034,7 @@ #endif /* !defined(SQLITE_DISABLE_FTS3_UNICODE) */ /************** End of fts3_unicode2.c ***************************************/ -/************** Begin file json1.c *******************************************/ +/************** Begin file json.c ********************************************/ /* ** 2015-08-12 ** @@ -185435,10 +193047,10 @@ ** ****************************************************************************** ** -** This SQLite extension implements JSON functions. The interface is -** modeled after MySQL JSON functions: +** This SQLite JSON functions. ** -** https://dev.mysql.com/doc/refman/5.7/en/json.html +** 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. ** ** 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 @@ -185446,48 +193058,8 @@ ** This implementation parses JSON text at 250 MB/s, so it is hard to see ** how JSONB might improve on that.) */ -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1) -#if !defined(SQLITEINT_H) -/* #include "sqlite3ext.h" */ -#endif -SQLITE_EXTENSION_INIT1 -/* #include */ -/* #include */ -/* #include */ -/* #include */ - -/* Mark a function parameter as unused, to suppress nuisance compiler -** warnings. */ -#ifndef UNUSED_PARAM -# define UNUSED_PARAM(X) (void)(X) -#endif - -#ifndef LARGEST_INT64 -# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32)) -# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64) -#endif - -#ifndef deliberate_fall_through -# define deliberate_fall_through -#endif - -/* -** Versions of isspace(), isalnum() and isdigit() to which it is safe -** to pass signed char values. -*/ -#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)) -#endif +#ifndef SQLITE_OMIT_JSON +/* #include "sqliteInt.h" */ /* ** Growing our own isspace() routine this way is twice as fast as @@ -185512,15 +193084,12 @@ 0, 0, 0, 0, 0, 0, 0, 0, 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]) +#define fast_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; +#if !defined(SQLITE_DEBUG) && !defined(SQLITE_COVERAGE_TEST) +# define VVA(X) +#else +# define VVA(X) X #endif /* Objects */ @@ -185579,13 +193148,14 @@ struct JsonNode { u8 eType; /* One of the JSON_ type values */ u8 jnFlags; /* JNODE flags */ + u8 eU; /* Which union element to use */ 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 */ + const char *zJContent; /* 1: Content for INT, REAL, and STRING */ + u32 iAppend; /* 2: More terms for ARRAY and OBJECT */ + u32 iKey; /* 3: Key for ARRAY objects in json_tree() */ + u32 iReplace; /* 4: Replacement content for JNODE_REPLACE */ + JsonNode *pPatch; /* 5: Node chain of patch for JNODE_PATCH */ } u; }; @@ -185724,7 +193294,7 @@ */ 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; + if( zIn==0 || ((N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0) ) return; p->zBuf[p->nUsed++] = '"'; for(i=0; ijnFlags & (JNODE_REPLACE|JNODE_PATCH) ){ - if( pNode->jnFlags & JNODE_REPLACE ){ + if( (pNode->jnFlags & JNODE_REPLACE)!=0 && ALWAYS(aReplace!=0) ){ + assert( pNode->eU==4 ); jsonAppendValue(pOut, aReplace[pNode->u.iReplace]); return; } + assert( pNode->eU==5 ); pNode = pNode->u.pPatch; } switch( pNode->eType ){ @@ -185886,6 +193459,7 @@ } case JSON_STRING: { if( pNode->jnFlags & JNODE_RAW ){ + assert( pNode->eU==1 ); jsonAppendString(pOut, pNode->u.zJContent, pNode->n); break; } @@ -185893,6 +193467,7 @@ } case JSON_REAL: case JSON_INT: { + assert( pNode->eU==1 ); jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); break; } @@ -185908,6 +193483,7 @@ j += jsonNodeSize(&pNode[j]); } if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; + assert( pNode->eU==2 ); pNode = &pNode[pNode->u.iAppend]; j = 1; } @@ -185928,6 +193504,7 @@ j += 1 + jsonNodeSize(&pNode[j+1]); } if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; + assert( pNode->eU==2 ); pNode = &pNode[pNode->u.iAppend]; j = 1; } @@ -185972,10 +193549,10 @@ */ 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]) ); + assert( sqlite3Isxdigit(z[0]) ); + assert( sqlite3Isxdigit(z[1]) ); + assert( sqlite3Isxdigit(z[2]) ); + assert( sqlite3Isxdigit(z[3]) ); v = (jsonHexToInt(z[0])<<12) + (jsonHexToInt(z[1])<<8) + (jsonHexToInt(z[2])<<4) @@ -186007,7 +193584,9 @@ } case JSON_INT: { sqlite3_int64 i = 0; - const char *z = pNode->u.zJContent; + const char *z; + assert( pNode->eU==1 ); + z = pNode->u.zJContent; if( z[0]=='-' ){ z++; } while( z[0]>='0' && z[0]<='9' ){ unsigned v = *(z++) - '0'; @@ -186030,14 +193609,17 @@ sqlite3_result_int64(pCtx, i); int_done: break; - int_as_real: i=0; /* no break */ deliberate_fall_through + int_as_real: ; /* no break */ deliberate_fall_through } case JSON_REAL: { double r; #ifdef SQLITE_AMALGAMATION - const char *z = pNode->u.zJContent; + const char *z; + assert( pNode->eU==1 ); + z = pNode->u.zJContent; sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8); #else + assert( pNode->eU==1 ); r = strtod(pNode->u.zJContent, 0); #endif sqlite3_result_double(pCtx, r); @@ -186048,6 +193630,7 @@ ** json_insert() and json_replace() and those routines do not ** call jsonReturn() */ if( pNode->jnFlags & JNODE_RAW ){ + assert( pNode->eU==1 ); sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n, SQLITE_TRANSIENT); }else @@ -186055,15 +193638,18 @@ assert( (pNode->jnFlags & JNODE_RAW)==0 ); if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){ /* JSON formatted without any backslash-escapes */ + assert( pNode->eU==1 ); 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; + const char *z; char *zOut; u32 j; + assert( pNode->eU==1 ); + z = pNode->u.zJContent; zOut = sqlite3_malloc( n+1 ); if( zOut==0 ){ sqlite3_result_error_nomem(pCtx); @@ -186184,12 +193770,13 @@ const char *zContent /* Content */ ){ JsonNode *p; - if( pParse->nNode>=pParse->nAlloc ){ + if( pParse->aNode==0 || pParse->nNode>=pParse->nAlloc ){ return jsonParseAddNodeExpand(pParse, eType, n, zContent); } p = &pParse->aNode[pParse->nNode]; p->eType = (u8)eType; p->jnFlags = 0; + VVA( p->eU = zContent ? 1 : 0 ); p->n = n; p->u.zJContent = zContent; return pParse->nNode++; @@ -186200,7 +193787,7 @@ */ static int jsonIs4Hex(const char *z){ int i; - for(i=0; i<4; i++) if( !safe_isxdigit(z[i]) ) return 0; + for(i=0; i<4; i++) if( !sqlite3Isxdigit(z[i]) ) return 0; return 1; } @@ -186219,13 +193806,13 @@ int x; JsonNode *pNode; const char *z = pParse->zJson; - while( safe_isspace(z[i]) ){ i++; } + while( fast_isspace(z[i]) ){ i++; } if( (c = z[i])=='{' ){ /* Parse object */ iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); if( iThis<0 ) return -1; for(j=i+1;;j++){ - while( safe_isspace(z[j]) ){ j++; } + while( fast_isspace(z[j]) ){ j++; } if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; x = jsonParseValue(pParse, j); if( x<0 ){ @@ -186238,14 +193825,14 @@ if( pNode->eType!=JSON_STRING ) return -1; pNode->jnFlags |= JNODE_LABEL; j = x; - while( safe_isspace(z[j]) ){ j++; } + while( fast_isspace(z[j]) ){ j++; } if( z[j]!=':' ) return -1; j++; x = jsonParseValue(pParse, j); pParse->iDepth--; if( x<0 ) return -1; j = x; - while( safe_isspace(z[j]) ){ j++; } + while( fast_isspace(z[j]) ){ j++; } c = z[j]; if( c==',' ) continue; if( c!='}' ) return -1; @@ -186257,8 +193844,9 @@ /* Parse array */ iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); if( iThis<0 ) return -1; + memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u)); for(j=i+1;;j++){ - while( safe_isspace(z[j]) ){ j++; } + while( fast_isspace(z[j]) ){ j++; } if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; x = jsonParseValue(pParse, j); pParse->iDepth--; @@ -186267,7 +193855,7 @@ return -1; } j = x; - while( safe_isspace(z[j]) ){ j++; } + while( fast_isspace(z[j]) ){ j++; } c = z[j]; if( c==',' ) continue; if( c!=']' ) return -1; @@ -186304,17 +193892,17 @@ return j+1; }else if( c=='n' && strncmp(z+i,"null",4)==0 - && !safe_isalnum(z[i+4]) ){ + && !sqlite3Isalnum(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]) ){ + && !sqlite3Isalnum(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]) ){ + && !sqlite3Isalnum(z[i+5]) ){ jsonParseAddNode(pParse, JSON_FALSE, 0, 0); return i+5; }else if( c=='-' || (c>='0' && c<='9') ){ @@ -186385,7 +193973,7 @@ if( pParse->oom ) i = -1; if( i>0 ){ assert( pParse->iDepth==0 ); - while( safe_isspace(zJson[i]) ) i++; + while( fast_isspace(zJson[i]) ) i++; if( zJson[i] ) i = -1; } if( i<=0 ){ @@ -186521,6 +194109,7 @@ ** a match. */ static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){ + assert( pNode->eU==1 ); if( pNode->jnFlags & JNODE_RAW ){ if( pNode->n!=nKey ) return 0; return strncmp(pNode->u.zJContent, zKey, nKey)==0; @@ -186586,6 +194175,7 @@ j += jsonNodeSize(&pRoot[j]); } if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; + assert( pRoot->eU==2 ); iRoot += pRoot->u.iAppend; pRoot = &pParse->aNode[iRoot]; j = 1; @@ -186600,8 +194190,10 @@ if( pParse->oom ) return 0; if( pNode ){ pRoot = &pParse->aNode[iRoot]; + assert( pRoot->eU==0 ); pRoot->u.iAppend = iStart - iRoot; pRoot->jnFlags |= JNODE_APPEND; + VVA( pRoot->eU = 2 ); pParse->aNode[iLabel].jnFlags |= JNODE_RAW; } return pNode; @@ -186609,7 +194201,7 @@ }else if( zPath[0]=='[' ){ i = 0; j = 1; - while( safe_isdigit(zPath[j]) ){ + while( sqlite3Isdigit(zPath[j]) ){ i = i*10 + zPath[j] - '0'; j++; } @@ -186624,18 +194216,19 @@ j += jsonNodeSize(&pBase[j]); } if( (pBase->jnFlags & JNODE_APPEND)==0 ) break; + assert( pBase->eU==2 ); iBase += pBase->u.iAppend; pBase = &pParse->aNode[iBase]; j = 1; } j = 2; - if( zPath[2]=='-' && safe_isdigit(zPath[3]) ){ + if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){ unsigned int x = 0; j = 3; do{ x = x*10 + zPath[j] - '0'; j++; - }while( safe_isdigit(zPath[j]) ); + }while( sqlite3Isdigit(zPath[j]) ); if( x>i ) return 0; i -= x; } @@ -186657,6 +194250,7 @@ j += jsonNodeSize(&pRoot[j]); } if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; + assert( pRoot->eU==2 ); iRoot += pRoot->u.iAppend; pRoot = &pParse->aNode[iRoot]; j = 1; @@ -186672,8 +194266,10 @@ if( pParse->oom ) return 0; if( pNode ){ pRoot = &pParse->aNode[iRoot]; + assert( pRoot->eU==0 ); pRoot->u.iAppend = iStart - iRoot; pRoot->jnFlags |= JNODE_APPEND; + VVA( pRoot->eU = 2 ); } return pNode; } @@ -186827,9 +194423,13 @@ } jsonPrintf(100, &s,"node %3u: %7s n=%-4d up=%-4d", i, zType, x.aNode[i].n, x.aUp[i]); + assert( x.aNode[i].eU==0 || x.aNode[i].eU==1 ); if( x.aNode[i].u.zJContent!=0 ){ + assert( x.aNode[i].eU==1 ); jsonAppendRaw(&s, " ", 1); jsonAppendRaw(&s, x.aNode[i].u.zJContent, x.aNode[i].n); + }else{ + assert( x.aNode[i].eU==0 ); } jsonAppendRaw(&s, "\n", 1); } @@ -186847,7 +194447,7 @@ int argc, sqlite3_value **argv ){ - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); sqlite3_result_int(ctx, sqlite3_value_subtype(argv[0])==JSON_SUBTYPE); } #endif /* SQLITE_DEBUG */ @@ -186868,7 +194468,7 @@ sqlite3_value **argv ){ JsonString jx; - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); jsonInit(&jx, ctx); jsonAppendValue(&jx, argv[0]); @@ -186940,12 +194540,33 @@ } /* +** Bit values for the flags passed into jsonExtractFunc() or +** jsonSetFunc() via the user-data value. +*/ +#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() */ + +/* ** json_extract(JSON, PATH, ...) +** "->"(JSON,PATH) +** "->>"(JSON,PATH) +** +** 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. ** -** 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. +** Abbreviated JSON path expressions are allows if JSON_ABPATH, for +** compatibility with PG. */ static void jsonExtractFunc( sqlite3_context *ctx, @@ -186955,35 +194576,77 @@ JsonParse *p; /* The parse */ JsonNode *pNode; const char *zPath; + int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); JsonString jx; - int i; if( argc<2 ) return; p = jsonParseCached(ctx, argv, ctx); if( p==0 ) return; - jsonInit(&jx, ctx); - jsonAppendChar(&jx, '['); - for(i=1; inErr ) break; - if( argc>2 ){ + if( argc==2 ){ + /* With a single PATH argument */ + zPath = (const char*)sqlite3_value_text(argv[1]); + if( zPath==0 ) return; + if( flags & JSON_ABPATH ){ + if( zPath[0]!='$' ){ + /* 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 + */ + jsonInit(&jx, ctx); + if( sqlite3Isdigit(zPath[0]) ){ + jsonAppendRaw(&jx, "$[", 2); + jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); + jsonAppendRaw(&jx, "]", 2); + }else{ + jsonAppendRaw(&jx, "$.", 1 + (zPath[0]!='[')); + jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); + jsonAppendChar(&jx, 0); + } + pNode = jx.bErr ? 0 : jsonLookup(p, jx.zBuf, 0, ctx); + jsonReset(&jx); + }else{ + pNode = jsonLookup(p, zPath, 0, ctx); + } + if( pNode ){ + if( flags & JSON_JSON ){ + jsonReturnJson(pNode, ctx, 0); + }else{ + jsonReturn(pNode, ctx, 0); + sqlite3_result_subtype(ctx, 0); + } + } + }else{ + pNode = jsonLookup(p, zPath, 0, ctx); + if( p->nErr==0 && pNode ) jsonReturn(pNode, ctx, 0); + } + }else{ + /* Two or more PATH arguments results in a JSON array with each + ** element of the array being the value selected by one of the PATHs */ + int i; + jsonInit(&jx, ctx); + jsonAppendChar(&jx, '['); + for(i=1; inErr ) break; jsonAppendSeparator(&jx); if( pNode ){ jsonRenderNode(pNode, &jx, 0); }else{ jsonAppendRaw(&jx, "null", 4); } - }else if( pNode ){ - jsonReturn(pNode, ctx, 0); } + if( i==argc ){ + jsonAppendChar(&jx, ']'); + jsonResult(&jx); + sqlite3_result_subtype(ctx, JSON_SUBTYPE); + } + jsonReset(&jx); } - if( argc>2 && i==argc ){ - jsonAppendChar(&jx, ']'); - jsonResult(&jx); - sqlite3_result_subtype(ctx, JSON_SUBTYPE); - } - jsonReset(&jx); } /* This is the RFC 7396 MergePatch algorithm. @@ -187012,6 +194675,7 @@ const char *zKey; assert( pPatch[i].eType==JSON_STRING ); assert( pPatch[i].jnFlags & JNODE_LABEL ); + assert( pPatch[i].eU==1 ); nKey = pPatch[i].n; zKey = pPatch[i].u.zJContent; assert( (pPatch[i].jnFlags & JNODE_RAW)==0 ); @@ -187028,6 +194692,12 @@ if( pNew==0 ) return 0; pTarget = &pParse->aNode[iTarget]; if( pNew!=&pTarget[j+1] ){ + assert( pTarget[j+1].eU==0 + || pTarget[j+1].eU==1 + || pTarget[j+1].eU==2 ); + testcase( pTarget[j+1].eU==1 ); + testcase( pTarget[j+1].eU==2 ); + VVA( pTarget[j+1].eU = 5 ); pTarget[j+1].u.pPatch = pNew; pTarget[j+1].jnFlags |= JNODE_PATCH; } @@ -187043,9 +194713,14 @@ if( pParse->oom ) return 0; jsonRemoveAllNulls(pPatch); pTarget = &pParse->aNode[iTarget]; + assert( pParse->aNode[iRoot].eU==0 || pParse->aNode[iRoot].eU==2 ); + testcase( pParse->aNode[iRoot].eU==2 ); pParse->aNode[iRoot].jnFlags |= JNODE_APPEND; + VVA( pParse->aNode[iRoot].eU = 2 ); pParse->aNode[iRoot].u.iAppend = iStart - iRoot; iRoot = iStart; + assert( pParse->aNode[iPatch].eU==0 ); + VVA( pParse->aNode[iPatch].eU = 5 ); pParse->aNode[iPatch].jnFlags |= JNODE_PATCH; pParse->aNode[iPatch].u.pPatch = &pPatch[i+1]; } @@ -187067,7 +194742,7 @@ JsonParse y; /* The patch */ JsonNode *pResult; /* The result of the merge */ - UNUSED_PARAM(argc); + UNUSED_PARAMETER(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); @@ -187187,11 +194862,15 @@ pNode = jsonLookup(&x, zPath, 0, ctx); if( x.nErr ) goto replace_err; if( pNode ){ + assert( pNode->eU==0 || pNode->eU==1 || pNode->eU==4 ); + testcase( pNode->eU!=0 && pNode->eU!=1 ); pNode->jnFlags |= (u8)JNODE_REPLACE; + VVA( pNode->eU = 4 ); pNode->u.iReplace = i + 1; } } if( x.aNode[0].jnFlags & JNODE_REPLACE ){ + assert( x.aNode[0].eU==4 ); sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]); }else{ jsonReturnJson(x.aNode, ctx, argv); @@ -187200,6 +194879,7 @@ jsonParseReset(&x); } + /* ** json_set(JSON, PATH, VALUE, ...) ** @@ -187222,7 +194902,7 @@ const char *zPath; u32 i; int bApnd; - int bIsSet = *(int*)sqlite3_user_data(ctx); + int bIsSet = sqlite3_user_data(ctx)!=0; if( argc<1 ) return; if( (argc&1)==0 ) { @@ -187241,11 +194921,15 @@ }else if( x.nErr ){ goto jsonSetDone; }else if( pNode && (bApnd || bIsSet) ){ + testcase( pNode->eU!=0 && pNode->eU!=1 ); + assert( pNode->eU!=3 && pNode->eU!=5 ); + VVA( pNode->eU = 4 ); pNode->jnFlags |= (u8)JNODE_REPLACE; pNode->u.iReplace = i + 1; } } if( x.aNode[0].jnFlags & JNODE_REPLACE ){ + assert( x.aNode[0].eU==4 ); sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]); }else{ jsonReturnJson(x.aNode, ctx, argv); @@ -187258,8 +194942,8 @@ ** 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, @@ -187295,7 +194979,7 @@ sqlite3_value **argv ){ JsonParse *p; /* The parse */ - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); p = jsonParseCached(ctx, argv, 0); sqlite3_result_int(ctx, p!=0); } @@ -187315,7 +194999,7 @@ sqlite3_value **argv ){ JsonString *pStr; - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr)); if( pStr ){ if( pStr->zBuf==0 ){ @@ -187323,8 +195007,8 @@ jsonAppendChar(pStr, '['); }else if( pStr->nUsed>1 ){ jsonAppendChar(pStr, ','); - pStr->pCtx = ctx; } + pStr->pCtx = ctx; jsonAppendValue(pStr, argv[0]); } } @@ -187375,8 +195059,8 @@ 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 @@ -187384,11 +195068,7 @@ 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 +195078,13 @@ 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,7 +195104,7 @@ JsonString *pStr; const char *z; u32 n; - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr)); if( pStr ){ if( pStr->zBuf==0 ){ @@ -187427,8 +195112,8 @@ jsonAppendChar(pStr, '{'); }else if( pStr->nUsed>1 ){ jsonAppendChar(pStr, ','); - pStr->pCtx = ctx; } + pStr->pCtx = ctx; z = (const char*)sqlite3_value_text(argv[0]); n = (u32)sqlite3_value_bytes(argv[0]); jsonAppendString(pStr, z, n); @@ -187510,10 +195195,10 @@ #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)"); @@ -187536,7 +195221,7 @@ static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ JsonEachCursor *pCur; - UNUSED_PARAM(p); + UNUSED_PARAMETER(p); pCur = sqlite3_malloc( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); @@ -187595,6 +195280,9 @@ JsonNode *pUp = &p->sParse.aNode[iUp]; p->eType = pUp->eType; if( pUp->eType==JSON_ARRAY ){ + assert( pUp->eU==0 || pUp->eU==3 ); + testcase( pUp->eU==3 ); + VVA( pUp->eU = 3 ); if( iUp==p->i-1 ){ pUp->u.iKey = 0; }else{ @@ -187641,12 +195329,15 @@ pNode = &p->sParse.aNode[i]; pUp = &p->sParse.aNode[iUp]; if( pUp->eType==JSON_ARRAY ){ + assert( pUp->eU==3 || (pUp->eU==0 && pUp->u.iKey==0) ); + testcase( pUp->eU==0 ); 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 ); + assert( pNode->eU==1 ); jsonPrintf(pNode->n+1, pStr, ".%.*s", pNode->n-2, pNode->u.zJContent+1); } } @@ -187668,6 +195359,7 @@ u32 iKey; if( p->bRecursive ){ if( p->iRowid==0 ) break; + assert( p->sParse.aNode[p->sParse.aUp[p->i]].eU==3 ); iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey; }else{ iKey = p->iRowid; @@ -187717,6 +195409,7 @@ if( p->eType==JSON_ARRAY ){ jsonPrintf(30, &x, "[%d]", p->iRowid); }else if( p->eType==JSON_OBJECT ){ + assert( pThis->eU==1 ); jsonPrintf(pThis->n, &x, ".%.*s", pThis->n-2, pThis->u.zJContent+1); } } @@ -187775,7 +195468,7 @@ /* 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 +195477,7 @@ 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; @@ -187830,8 +195524,8 @@ const char *zRoot = 0; sqlite3_int64 n; - 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]); @@ -187881,6 +195575,8 @@ p->iBegin = p->i = (int)(pNode - p->sParse.aNode); p->eType = pNode->eType; if( p->eType>=JSON_ARRAY ){ + assert( pNode->eU==0 ); + VVA( pNode->eU = 3 ); pNode->u.iKey = 0; p->iEnd = p->i + pNode->n + 1; if( p->bRecursive ){ @@ -187954,108 +195650,68 @@ 0 /* xShadowName */ }; #endif /* SQLITE_OMIT_VIRTUALTABLE */ +#endif /* !defined(SQLITE_OMIT_JSON) */ -/**************************************************************************** -** 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 }, - +/* +** Register JSON functions. +*/ +SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void){ +#ifndef SQLITE_OMIT_JSON + static FuncDef aJsonFunc[] = { + JFUNCTION(json, 1, 0, jsonRemoveFunc), + JFUNCTION(json_array, -1, 0, jsonArrayFunc), + JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc), + JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc), + JFUNCTION(json_extract, -1, 0, jsonExtractFunc), + JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc), + JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc), + JFUNCTION(json_insert, -1, 0, jsonSetFunc), + JFUNCTION(json_object, -1, 0, jsonObjectFunc), + JFUNCTION(json_patch, 2, 0, jsonPatchFunc), + JFUNCTION(json_quote, 1, 0, jsonQuoteFunc), + JFUNCTION(json_remove, -1, 0, jsonRemoveFunc), + JFUNCTION(json_replace, -1, 0, jsonReplaceFunc), + JFUNCTION(json_set, -1, JSON_ISSET, jsonSetFunc), + JFUNCTION(json_type, 1, 0, jsonTypeFunc), + JFUNCTION(json_type, 2, 0, jsonTypeFunc), + JFUNCTION(json_valid, 1, 0, jsonValidFunc), #if SQLITE_DEBUG - /* DEBUG and TESTING functions */ - { "json_parse", 1, 0, jsonParseFunc }, - { "json_test1", 1, 0, jsonTest1Func }, + JFUNCTION(json_parse, 1, 0, jsonParseFunc), + JFUNCTION(json_test1, 1, 0, jsonTest1Func), #endif + WAGGREGATE(json_group_array, 1, 0, 0, + jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse, + SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS), + WAGGREGATE(json_group_object, 2, 0, 0, + jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse, + SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS) }; + 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 */ /* #include */ @@ -188194,7 +195867,9 @@ 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 @@ -188476,7 +196151,12 @@ ** 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 /* @@ -188726,18 +196406,6 @@ } /* -** 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; -} - -/* ** Obtain a reference to an r-tree node. */ static int nodeAcquire( @@ -188753,14 +196421,7 @@ ** 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 && pParent!=pNode->pParent ){ RTREE_IS_CORRUPT(pRtree); return SQLITE_CORRUPT_VTAB; } @@ -188818,7 +196479,7 @@ ** 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; @@ -189341,20 +197002,29 @@ 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; } @@ -189424,11 +197094,12 @@ */ 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 +197222,8 @@ 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 +197284,7 @@ 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,7 +197425,7 @@ RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr); int rc = SQLITE_OK; RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); - if( rc==SQLITE_OK && p ){ + if( rc==SQLITE_OK && ALWAYS(p) ){ *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell); } return rc; @@ -189771,7 +197443,7 @@ RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); if( rc ) return rc; - if( p==0 ) return SQLITE_OK; + if( NEVER(p==0) ) return SQLITE_OK; if( i==0 ){ sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell)); }else if( i<=pRtree->nDim2 ){ @@ -189970,8 +197642,11 @@ } 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 +197723,7 @@ 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; @@ -190254,7 +197929,7 @@ int nCell = NCELL(pNode); RtreeCell cell; - RtreeNode *pChild; + RtreeNode *pChild = 0; RtreeCell *aCell = 0; @@ -190301,12 +197976,19 @@ ){ RtreeNode *p = pNode; int cnt = 0; + int rc; while( p->pParent ){ 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; } @@ -190595,12 +198277,17 @@ xSetMapping = ((iHeight==0)?rowidWrite:parentWrite); if( iHeight>0 ){ 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 +198377,12 @@ 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 +198457,7 @@ */ 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 +198488,7 @@ 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 ){ @@ -191022,7 +198711,7 @@ } }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 +198817,7 @@ 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); } @@ -191463,7 +199152,7 @@ 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 +199169,10 @@ 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; } @@ -191643,9 +199322,12 @@ 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); } } @@ -191910,6 +199592,7 @@ 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( nDataz[0]) ) p->z++; + while( fast_isspace(p->z[0]) ) p->z++; return p->z[0]; } @@ -192734,11 +200421,16 @@ ){ GeoPoly *p = 0; int nByte; + testcase( pCtx==0 ); if( sqlite3_value_type(pVal)==SQLITE_BLOB && (nByte = sqlite3_value_bytes(pVal))>=(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 @@ -193112,7 +200804,7 @@ aCoord[2].f = mnY; aCoord[3].f = mxY; } - }else{ + }else if( aCoord ){ memset(aCoord, 0, sizeof(RtreeCoord)*4); } return pOut; @@ -193504,7 +201196,7 @@ 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 +201255,11 @@ }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; } } @@ -193811,6 +201503,7 @@ RtreeCoord bbox[4]; RtreeConstraint *p; assert( argc==1 ); + assert( argv[0]!=0 ); geopolyBBox(0, argv[0], bbox, &rc); if( rc ){ goto geopoly_filter_end; @@ -194038,6 +201731,7 @@ || !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 ){ @@ -194391,7 +202085,10 @@ /* Allocate and populate the context object. */ pGeomCtx = (RtreeGeomCallback *)sqlite3_malloc(sizeof(RtreeGeomCallback)); - if( !pGeomCtx ) return SQLITE_NOMEM; + if( !pGeomCtx ){ + if( xDestructor ) xDestructor(pContext); + return SQLITE_NOMEM; + } pGeomCtx->xGeom = 0; pGeomCtx->xQueryFunc = xQueryFunc; pGeomCtx->xDestructor = xDestructor; @@ -195963,6 +203660,13 @@ #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 ** keys mapped to values as follows: @@ -197046,7 +204750,9 @@ 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 +205112,7 @@ ** 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 +205178,9 @@ 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; } @@ -198577,7 +206285,7 @@ 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,13 +206312,19 @@ /* ** 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); @@ -198976,15 +206690,31 @@ /* -** 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", SQLITE_FCNTL_FILE_POINTER, &fd); + + if( 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,18 +206772,24 @@ ** 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; + dbMain = rbuOpenDbhandle(p, p->zTarget, 1); + if( dbMain ){ + assert( p->rc==SQLITE_OK ); + p->rc = rbuLockDatabase(dbMain); + } + if( p->rc==SQLITE_OK ){ #if defined(_WIN32_WCE) { LPWSTR zWideOal; @@ -199080,11 +206816,19 @@ #else p->rc = rename(zOal, zWal) ? SQLITE_IOERR : SQLITE_OK; #endif + } - if( p->rc==SQLITE_OK ){ - rbuOpenDatabase(p, 0); - rbuSetupCheckpoint(p, 0); - } + 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); } } @@ -199835,9 +207579,9 @@ ** 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 +207676,14 @@ }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 +207721,6 @@ const char *zState ){ if( zTarget==0 || zRbu==0 ){ return rbuMisuseError(); } - /* TODO: Check that zTarget and zRbu are non-NULL */ return openRbuHandle(zTarget, zRbu, zState); } @@ -200680,22 +208431,24 @@ #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<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; @@ -201185,6 +208924,15 @@ && !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: ** ** The value of the 'path' column describes the path taken from the @@ -201251,9 +208999,8 @@ /* 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(): */ @@ -201465,18 +209212,25 @@ } 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 +209295,7 @@ 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 +209366,7 @@ 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); @@ -201672,6 +209426,38 @@ } /* +** 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), ** the next entry is the next btree. @@ -201689,7 +209475,7 @@ 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 +209487,7 @@ 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 +209538,8 @@ 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 +209558,7 @@ }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 ){ @@ -201903,6 +209688,7 @@ } if( rc==SQLITE_OK ){ + pCsr->iPage = -1; rc = statNext(pCursor); } return rc; @@ -202476,12 +210262,15 @@ 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 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 */ @@ -202524,6 +210313,7 @@ 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 */ @@ -202723,8 +210513,9 @@ ** 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" */ + 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 */ @@ -202849,7 +210640,7 @@ 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 +210656,26 @@ 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 @@ -203332,13 +211143,19 @@ ** 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 +211176,7 @@ } } - sqlite3_free(pTab->apChange); + sessionFree(pSession, pTab->apChange); pTab->nChange = nNew; pTab->apChange = apNew; } @@ -203393,6 +211210,7 @@ ** 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 */ @@ -203427,16 +211245,32 @@ }else if( rc==SQLITE_ERROR ){ zPragma = sqlite3_mprintf(""); }else{ + *pazCol = 0; + *pabPK = 0; + *pnCol = 0; + if( pzTab ) *pzTab = 0; return rc; } }else{ zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis); } - if( !zPragma ) return SQLITE_NOMEM; + if( !zPragma ){ + *pazCol = 0; + *pabPK = 0; + *pnCol = 0; + if( pzTab ) *pzTab = 0; + 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 ){ + *pazCol = 0; + *pabPK = 0; + *pnCol = 0; + if( pzTab ) *pzTab = 0; + return rc; + } nByte = nThis + 1; while( SQLITE_ROW==sqlite3_step(pStmt) ){ @@ -203447,7 +211281,7 @@ if( rc==SQLITE_OK ){ nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1); - pAlloc = sqlite3_malloc64(nByte); + pAlloc = sessionMalloc64(pSession, nByte); if( pAlloc==0 ){ rc = SQLITE_NOMEM; } @@ -203490,7 +211324,7 @@ *pabPK = 0; *pnCol = 0; if( pzTab ) *pzTab = 0; - sqlite3_free(azCol); + sessionFree(pSession, azCol); } sqlite3_finalize(pStmt); return rc; @@ -203512,7 +211346,7 @@ if( pTab->nCol==0 ){ u8 *abPK; assert( pTab->azCol==0 || pTab->abPK==0 ); - pSession->rc = sessionTableInfo(pSession->db, pSession->zDb, + pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb, pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK ); if( pSession->rc==SQLITE_OK ){ @@ -203526,6 +211360,12 @@ if( 0==sqlite3_stricmp("sqlite_stat1", pTab->zName) ){ pTab->bStat1 = 1; } + + if( pSession->bEnableSize ){ + pSession->nMaxChangesetSize += ( + 1 + sessionVarintLen(pTab->nCol) + pTab->nCol + strlen(pTab->zName)+1 + ); + } } } return (pSession->rc || pTab->abPK==0); @@ -203571,6 +211411,103 @@ 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( op!=SQLITE_DELETE ){ + int ii; + for(ii=0; iinCol; ii++){ + sqlite3_value *p = 0; + pSession->hook.xNew(pSession->hook.pCtx, 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; + for(ii=0; iinCol; ii++){ + int bChanged = 1; + int nOld = 0; + int eType; + sqlite3_value *p = 0; + pSession->hook.xNew(pSession->hook.pCtx, ii, &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 @@ -203603,7 +211540,7 @@ } /* Grow the hash table if required */ - if( sessionGrowHash(0, pTab) ){ + if( sessionGrowHash(pSession, 0, pTab) ){ pSession->rc = SQLITE_NOMEM; return; } @@ -203644,7 +211581,6 @@ /* 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 */ @@ -203670,13 +211606,13 @@ } /* 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(), @@ -203691,17 +211627,17 @@ }else if( pTab->abPK[i] ){ pSession->hook.xNew(pSession->hook.pCtx, i, &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->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 +211648,14 @@ 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 +211688,11 @@ ){ 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) ); } } @@ -204043,7 +211989,7 @@ int nCol; /* Columns in zFrom.zTbl */ u8 *abPK; const char **azCol = 0; - rc = sessionTableInfo(db, zFrom, zTbl, &nCol, 0, &azCol, &abPK); + rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, &abPK); if( rc==SQLITE_OK ){ if( pTo->nCol!=nCol ){ bMismatch = 1; @@ -204141,7 +212087,7 @@ ** Free the list of table objects passed as the first argument. The contents ** of the changed-rows hash tables are also deleted. */ -static void sessionDeleteTable(SessionTable *pList){ +static void sessionDeleteTable(sqlite3_session *pSession, SessionTable *pList){ SessionTable *pNext; SessionTable *pTab; @@ -204153,12 +212099,12 @@ SessionChange *pNextChange; for(p=pTab->apChange[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); + sessionFree(pSession, (char*)pTab->azCol); /* cast works around VC++ bug */ + sessionFree(pSession, pTab->apChange); + sessionFree(pSession, pTab); } } @@ -204186,9 +212132,11 @@ /* 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. */ + /* Assert that all allocations have been freed and then free the + ** session object itself. */ + assert( pSession->nMalloc==0 ); sqlite3_free(pSession); } @@ -204235,7 +212183,8 @@ 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{ @@ -204265,13 +212214,29 @@ ** 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)nBuf + nByte; + if( *pRc==SQLITE_OK && nReq>p->nAlloc ){ u8 *aNew; i64 nNew = p->nAlloc ? p->nAlloc : 128; + do { nNew = nNew*2; - }while( (size_t)(nNew-p->nBuf)SESSION_MAX_BUFFER_SZ ){ + nNew = SESSION_MAX_BUFFER_SZ; + if( nNewaBuf, nNew); if( 0==aNew ){ @@ -204500,6 +212465,7 @@ 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; ipTable; 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 */ + int nCol = 0; /* Number of columns in table */ + u8 *abPK = 0; /* 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 */ @@ -204832,7 +212800,7 @@ int nNoop; /* Size of buffer after writing tbl header */ /* Check the table schema is still Ok. */ - rc = sessionTableInfo(db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK); + rc = sessionTableInfo(0, db, pSession->zDb, zName, &nCol, 0,&azCol,&abPK); if( !rc && (pTab->nCol!=nCol || memcmp(abPK, pTab->abPK, nCol)) ){ rc = SQLITE_SCHEMA; } @@ -204862,6 +212830,7 @@ sessionAppendCol(&buf, pSel, iCol, &rc); } }else{ + assert( abPK!=0 ); /* Because sessionSelectStmt() returned ok */ rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, abPK); } }else if( p->op!=SQLITE_INSERT ){ @@ -204922,7 +212891,14 @@ 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 +212909,7 @@ 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 +212921,7 @@ 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 +212937,7 @@ 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); } @@ -205008,6 +212987,46 @@ } /* +** 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; + } + + 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(). */ static int sessionChangesetStart( @@ -205016,7 +213035,8 @@ 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 +213057,7 @@ 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 +213072,7 @@ 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 +213081,7 @@ 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 +213092,7 @@ 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 +213101,7 @@ 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); } /* @@ -205206,11 +213227,14 @@ 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; } @@ -205401,31 +213426,27 @@ } /* -** Advance the changeset iterator to the next change. -** -** If both paRec and pnRec are NULL, then this function works like the public -** API sqlite3changeset_next(). If SQLITE_ROW is returned, then the -** sqlite3changeset_new() and old() APIs may be used to query for values. +** Advance the changeset iterator to the next change. The differences between +** this function and sessionChangesetNext() are that ** -** Otherwise, if paRec and pnRec are not NULL, then a pointer to the change -** record is written to *paRec before returning and the number of bytes in -** the record to *pnRec. +** * 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. ** -** Either way, this function returns SQLITE_ROW if the iterator is -** successfully advanced to the next change in the changeset, an SQLite -** error code if an error occurs, or SQLITE_DONE if there are no further -** changes in the changeset. +** * If the iterator is configured to skip no-op UPDATEs, +** sessionChangesetNext() does that. This function does not. */ -static int sessionChangesetNext( +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 *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; @@ -205498,13 +213519,13 @@ /* 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); + 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); + p->rc = sessionReadRecord(&p->in, p->nCol, 0, apNew, pbEmpty); if( p->rc!=SQLITE_OK ) return p->rc; } @@ -205532,6 +213553,37 @@ } /* +** Advance the changeset iterator to the next change. +** +** If both paRec and pnRec are NULL, then this function works like the public +** API sqlite3changeset_next(). If SQLITE_ROW is returned, then the +** sqlite3changeset_new() and old() APIs may be used to query for values. +** +** Otherwise, if paRec and pnRec are not NULL, then a pointer to the change +** record is written to *paRec before returning and the number of bytes in +** the record to *pnRec. +** +** Either way, this function returns SQLITE_ROW if the iterator is +** successfully advanced to the next change in the changeset, an SQLite +** error code if an error occurs, or SQLITE_DONE if there are no further +** changes in the changeset. +*/ +static int sessionChangesetNext( + 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 bEmpty; + int rc; + do { + bEmpty = 0; + rc = sessionChangesetNextOne(p, paRec, pnRec, pbNew, &bEmpty); + }while( rc==SQLITE_ROW && p->bSkipEmpty && bEmpty); + return rc; +} + +/* ** Advance an iterator created by sqlite3changeset_start() to the next ** change in the changeset. This function may return SQLITE_ROW, SQLITE_DONE ** or SQLITE_CORRUPT. @@ -205803,9 +213855,9 @@ /* 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 +213901,11 @@ } 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 +213958,25 @@ 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 */ @@ -205925,6 +213986,167 @@ u8 bRebase; /* True to collect rebase information */ }; +/* 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: @@ -205995,103 +214217,6 @@ } /* -** 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: ** @@ -206172,17 +214297,6 @@ ); } 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 " "CASE WHEN length(?2)=0 AND typeof(?2)='blob' THEN NULL ELSE ?2 END " @@ -206247,7 +214361,7 @@ for(i=0; rc==SQLITE_OK && ipDelete && p->pUpdate && p->pInsert && p->pSelect ); + assert( p->pDelete && p->pInsert && p->pSelect ); assert( p->azCol && p->abPK ); assert( !pbReplace || *pbReplace==0 ); @@ -206538,29 +214652,28 @@ }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 @@ -206692,7 +214805,7 @@ 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*); @@ -206783,14 +214896,13 @@ ); 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; @@ -206818,7 +214930,7 @@ int i; sqlite3changeset_pk(pIter, &abPK, 0); - rc = sessionTableInfo( + rc = sessionTableInfo(0, db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK ); if( rc!=SQLITE_OK ) break; @@ -206854,11 +214966,10 @@ } sApply.bStat1 = 1; }else{ - if((rc = sessionSelectRow(db, zTab, &sApply)) - || (rc = sessionUpdateRow(db, zTab, &sApply)) - || (rc = sessionDeleteRow(db, zTab, &sApply)) - || (rc = sessionInsertRow(db, zTab, &sApply)) - ){ + if( (rc = sessionSelectRow(db, zTab, &sApply)) + || (rc = sessionDeleteRow(db, zTab, &sApply)) + || (rc = sessionInsertRow(db, zTab, &sApply)) + ){ break; } sApply.bStat1 = 0; @@ -206917,9 +215028,9 @@ *pnRebase = sApply.rebase.nBuf; sApply.rebase.aBuf = 0; } + sessionUpdateFree(&sApply); sqlite3_finalize(sApply.pInsert); sqlite3_finalize(sApply.pDelete); - sqlite3_finalize(sApply.pUpdate); sqlite3_finalize(sApply.pSelect); sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */ sqlite3_free((char*)sApply.constraints.aBuf); @@ -206950,8 +215061,8 @@ 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 @@ -207009,7 +215120,7 @@ ){ 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 @@ -207297,7 +215408,7 @@ } } - if( sessionGrowHash(pIter->bPatchset, pTab) ){ + if( sessionGrowHash(0, pIter->bPatchset, pTab) ){ rc = SQLITE_NOMEM; break; } @@ -207393,9 +215504,9 @@ 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; } } @@ -207483,7 +215594,7 @@ */ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){ if( pGrp ){ - sessionDeleteTable(pGrp->pList); + sessionDeleteTable(0, pGrp->pList); sqlite3_free(pGrp); } } @@ -207629,7 +215740,7 @@ 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 ){ @@ -207795,7 +215906,7 @@ 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 +215995,7 @@ */ SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p){ if( p ){ - sessionDeleteTable(p->grp.pList); + sessionDeleteTable(0, p->grp.pList); sqlite3_free(p); } } @@ -208538,8 +216649,20 @@ #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)) @@ -208599,7 +216722,7 @@ ** 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. */ @@ -208687,6 +216810,7 @@ Fts5Tokenizer *pTok; fts5_tokenizer *pTokApi; int bLock; /* True when table is preparing statement */ + int ePattern; /* FTS_PATTERN_XXX constant */ /* Values loaded from the %_config table */ int iCookie; /* Incremented when %_config is modified */ @@ -208707,17 +216831,19 @@ }; /* Current expected value of %_config table 'version' field */ -#define FTS5_CURRENT_VERSION 4 +#define FTS5_CURRENT_VERSION 4 #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_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** @@ -208935,6 +217061,9 @@ */ static const char *sqlite3Fts5IterTerm(Fts5IndexIter*, int*); static int sqlite3Fts5IterNextScan(Fts5IndexIter*); +static void *sqlite3Fts5StructureRef(Fts5Index*); +static void sqlite3Fts5StructureRelease(void*); +static int sqlite3Fts5StructureTest(Fts5Index*, void*); /* @@ -208987,7 +217116,7 @@ /* ** 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 @@ -209057,8 +217186,7 @@ Fts5Global*, const char **azArg, int nArg, - Fts5Tokenizer**, - fts5_tokenizer**, + Fts5Config*, char **pzErr ); @@ -209142,7 +217270,7 @@ static int sqlite3Fts5StorageContentInsert(Fts5Storage *p, 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*); @@ -209187,11 +217315,19 @@ /* 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); @@ -209300,6 +217436,10 @@ */ static int sqlite3Fts5TokenizerInit(fts5_api*); +static int sqlite3Fts5TokenizerPattern( + int (*xCreate)(void*, const char**, int, Fts5Tokenizer**), + Fts5Tokenizer *pTok +); /* ** End of interface to code in fts5_tokenizer.c. **************************************************************************/ @@ -209346,6 +217486,8 @@ #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 ** @@ -209370,8 +217512,6 @@ ** 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 +217541,26 @@ #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. @@ -209686,6 +217841,7 @@ }; typedef struct fts5yyParser fts5yyParser; +/* #include */ #ifndef NDEBUG /* #include */ static FILE *fts5yyTraceFILE = 0; @@ -210102,7 +218258,7 @@ #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); @@ -210316,54 +218472,6 @@ (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 +218774,56 @@ } #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 fts5YYSTACKDEPTH>0 + if( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystackEnd ){ + fts5yyStackOverflow(fts5yypParser); + break; + } +#else + if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz-1] ){ + if( fts5yyGrowStack(fts5yypParser) ){ + fts5yyStackOverflow(fts5yypParser); + break; + } + } +#endif + } + 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 +218879,13 @@ 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 +218935,7 @@ break; #endif } - }while( fts5yypParser->fts5yytos>fts5yypParser->fts5yystack ); + } #ifndef NDEBUG if( fts5yyTraceFILE ){ fts5yyStackEntry *i; @@ -211382,7 +219533,7 @@ 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 */ @@ -211456,7 +219607,7 @@ ){ 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,17 +219639,15 @@ 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); @@ -211599,7 +219748,6 @@ u32 nData, const u8 *pData ){ - assert_nc( *pRc || nData>=0 ); if( nData ){ if( fts5BufferGrow(pRc, pBuf, nData) ) return; memcpy(&pBuf->p[pBuf->n], pData, nData); @@ -211709,7 +219857,7 @@ return 1; }else{ i64 iOff = *piOff; - int iVal; + u32 iVal; fts5FastGetVarint32(a, i, iVal); if( iVal<=1 ){ if( iVal==0 ){ @@ -211718,15 +219866,19 @@ } 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 +219917,16 @@ 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( @@ -212262,7 +220416,7 @@ rc = SQLITE_ERROR; }else{ rc = sqlite3Fts5GetTokenizer(pGlobal, - (const char**)azArg, (int)nArg, &pConfig->pTok, &pConfig->pTokApi, + (const char**)azArg, (int)nArg, pConfig, pzErr ); } @@ -212334,9 +220488,7 @@ */ static int fts5ConfigDefaultTokenizer(Fts5Global *pGlobal, Fts5Config *pConfig){ assert( pConfig->pTok==0 && pConfig->pTokApi==0 ); - return sqlite3Fts5GetTokenizer( - pGlobal, 0, 0, &pConfig->pTok, &pConfig->pTokApi, 0 - ); + return sqlite3Fts5GetTokenizer(pGlobal, 0, 0, pConfig, 0); } /* @@ -212476,7 +220628,7 @@ 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; @@ -212501,6 +220653,7 @@ z = fts5ConfigSkipWhitespace(z); if( z && *z=='=' ){ bOption = 1; + assert( zOne!=0 ); z++; if( bMustBeCol ) z = 0; } @@ -212517,7 +220670,11 @@ rc = SQLITE_ERROR; }else{ if( bOption ){ - rc = fts5ConfigParseSpecial(pGlobal, pRet, zOne, zTwo?zTwo:"", pzErr); + rc = fts5ConfigParseSpecial(pGlobal, pRet, + ALWAYS(zOne)?zOne:"", + zTwo?zTwo:"", + pzErr + ); }else{ rc = fts5ConfigParseColumn(pRet, zOne, zTwo, pzErr); zOne = 0; @@ -213028,12 +221185,14 @@ 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" */ }; 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 +221275,7 @@ static int sqlite3Fts5ExprNew( Fts5Config *pConfig, /* FTS5 Configuration */ + int bPhraseToAnd, int iCol, const char *zExpr, /* Expression text */ Fts5Expr **ppNew, @@ -213131,6 +221291,7 @@ *ppNew = 0; *pzErr = 0; memset(&sParse, 0, sizeof(sParse)); + sParse.bPhraseToAnd = bPhraseToAnd; pEngine = sqlite3Fts5ParserAlloc(fts5ParseAlloc); if( pEngine==0 ){ return SQLITE_NOMEM; } sParse.pConfig = pConfig; @@ -213173,6 +221334,7 @@ pNew->pConfig = pConfig; pNew->apExprPhrase = sParse.apPhrase; pNew->nPhrase = sParse.nPhrase; + pNew->bDesc = 0; sParse.apPhrase = 0; } }else{ @@ -213185,6 +221347,81 @@ } /* +** This function is only called when using the special 'trigram' tokenizer. +** Argument zText contains the text of a LIKE or GLOB pattern matched +** against column iCol. This function creates and compiles an FTS5 MATCH +** expression that will match a superset of the rows matched by the LIKE or +** GLOB. If successful, SQLITE_OK is returned. Otherwise, an SQLite error +** code. +*/ +static int sqlite3Fts5ExprPattern( + Fts5Config *pConfig, int bGlob, int iCol, const char *zText, Fts5Expr **pp +){ + i64 nText = strlen(zText); + char *zExpr = (char*)sqlite3_malloc64(nText*4 + 1); + int rc = SQLITE_OK; + + if( zExpr==0 ){ + rc = SQLITE_NOMEM; + }else{ + char aSpec[3]; + int iOut = 0; + int i = 0; + int iFirst = 0; + + if( bGlob==0 ){ + aSpec[0] = '_'; + aSpec[1] = '%'; + aSpec[2] = 0; + }else{ + aSpec[0] = '*'; + aSpec[1] = '?'; + aSpec[2] = '['; + } + + while( i<=nText ){ + if( i==nText + || zText[i]==aSpec[0] || zText[i]==aSpec[1] || zText[i]==aSpec[2] + ){ + if( i-iFirst>=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. */ static void sqlite3Fts5ParseNodeFree(Fts5ExprNode *p){ @@ -213254,6 +221491,7 @@ int bRetValid = 0; Fts5ExprTerm *p; + assert( pTerm ); assert( pTerm->pSynonym ); assert( bDesc==0 || bDesc==1 ); for(p=pTerm; p; p=p->pSynonym){ @@ -214321,8 +222559,8 @@ } /* 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; @@ -214561,6 +222799,20 @@ 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 @@ -214596,16 +222848,9 @@ }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++; } @@ -214687,7 +222932,7 @@ 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; @@ -214958,9 +223203,8 @@ ){ 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); @@ -215013,6 +223257,67 @@ } /* +** 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 = sizeof(Fts5ExprNode) + nTerm*sizeof(Fts5ExprNode*); + pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); + if( pRet ){ + pRet->eType = FTS5_AND; + pRet->nChild = nTerm; + fts5ExprAssignXNext(pRet); + pParse->nPhrase--; + for(ii=0; iirc, sizeof(Fts5ExprPhrase) + ); + if( pPhrase ){ + if( parseGrowPhraseArray(pParse) ){ + fts5ExprPhraseFree(pPhrase); + }else{ + pParse->apPhrase[pParse->nPhrase++] = pPhrase; + pPhrase->nTerm = 1; + pPhrase->aTerm[0].zTerm = sqlite3Fts5Strndup( + &pParse->rc, pNear->apPhrase[0]->aTerm[ii].zTerm, -1 + ); + 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; +} + +/* ** Allocate and return a new expression object. If anything goes wrong (i.e. ** OOM error), leave an error code in pParse and return NULL. */ @@ -215036,51 +223341,55 @@ 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 = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1); + 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( 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); - pRet = 0; + 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" + ); + sqlite3_free(pRet); + pRet = 0; + } } + }else{ + fts5ExprAddChildren(pRet, pLeft); + fts5ExprAddChildren(pRet, pRight); } - }else{ - fts5ExprAddChildren(pRet, pLeft); - fts5ExprAddChildren(pRet, pRight); } } } @@ -215158,6 +223467,7 @@ return pRet; } +#ifdef SQLITE_TEST static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){ sqlite3_int64 nByte = 0; Fts5ExprTerm *p; @@ -215301,8 +223611,17 @@ 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 +223744,7 @@ 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 +223834,14 @@ sqlite3_result_int(pCtx, sqlite3Fts5UnicodeFold(iCode, bRemoveDiacritics)); } } +#endif /* ifdef SQLITE_TEST */ /* ** 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){ +#ifdef SQLITE_TEST struct Fts5ExprFunc { const char *z; void (*x)(sqlite3_context*,int,sqlite3_value**); @@ -215538,6 +223859,10 @@ 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 +223913,15 @@ 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 +223931,7 @@ 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) ){ @@ -216098,7 +224432,6 @@ 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 +224464,9 @@ /* 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 +224482,7 @@ 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; @@ -216784,7 +225118,7 @@ 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 */ /* Next method */ void (*xNext)(Fts5Index*, Fts5SegIter*, int*); @@ -216953,8 +225287,11 @@ ** 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); } @@ -217050,6 +225387,7 @@ return pRet; } + /* ** Release a reference to data record returned by an earlier call to ** fts5DataRead(). @@ -217174,6 +225512,58 @@ 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 = sizeof(Fts5Structure)+(p->nLevel-1)*sizeof(Fts5StructureLevel); + 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. @@ -217275,9 +225665,11 @@ } /* -** +** Add a level to the Fts5Structure.aLevel[] array of structure object +** (*ppStruct). */ static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){ + fts5StructureMakeWritable(pRc, ppStruct); if( *pRc==SQLITE_OK ){ Fts5Structure *pStruct = *ppStruct; int nLevel = pStruct->nLevel; @@ -217964,7 +226356,7 @@ 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 ){ @@ -217997,7 +226389,7 @@ */ 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); @@ -218071,6 +226463,7 @@ if( p->rc==SQLITE_OK ){ 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; @@ -218107,7 +226500,7 @@ ASSERT_SZLEAF_OK(pIter->pLeaf); while( 1 ){ - i64 iDelta = 0; + u64 iDelta = 0; if( eDetail==FTS5_DETAIL_NONE ){ /* todo */ @@ -218122,7 +226515,7 @@ 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 +226566,12 @@ 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 +226618,7 @@ 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 +226627,7 @@ 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); @@ -218423,14 +226820,9 @@ }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; @@ -218459,7 +226851,7 @@ if( pDlidx ){ 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 +226878,7 @@ ** 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 +226909,10 @@ 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 +226921,6 @@ }else{ pIter->iEndofDoclist = fts5LeafFirstTermOff(pLast); } - } fts5SegIterReverseInitPage(p, pIter); @@ -218577,21 +226972,20 @@ 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 +227051,15 @@ 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; } @@ -219033,7 +227427,7 @@ fts5SegIterNextPage(p, pIter); assert( p->rc!=SQLITE_OK || pIter->iLeafPgno==iLeafPgno ); - if( p->rc==SQLITE_OK ){ + if( p->rc==SQLITE_OK && ALWAYS(pIter->pLeaf!=0) ){ int iOff; u8 *a = pIter->pLeaf->p; int n = pIter->pLeaf->szLeaf; @@ -219422,7 +227816,7 @@ 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 +227829,9 @@ 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 +227859,11 @@ 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 +227887,72 @@ } /* -** 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. -*/ -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( pbase.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 void fts5IndexExtractColset( int *pRc, Fts5Colset *pColset, /* Colset to filter on */ const u8 *pPos, int nPos, /* Position list */ - Fts5Buffer *pBuf /* Output buffer */ + Fts5Iter *pIter ){ if( *pRc==SQLITE_OK ){ - int i; - fts5BufferZero(pBuf); - for(i=0; inCol; 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 +228072,9 @@ /* 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 +228087,7 @@ } 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 ){ @@ -219758,7 +228159,10 @@ } } *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; @@ -219822,6 +228226,10 @@ fts5MultiIterFree(pNew); *ppOut = 0; } + +fts5MultiIterNew_post_check: + assert( (*ppOut)!=0 || p->rc!=SQLITE_OK ); + return; } /* @@ -219869,7 +228277,8 @@ ** 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); @@ -220673,6 +229082,7 @@ ** 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; @@ -220773,7 +229183,7 @@ 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 */ @@ -220896,14 +229306,14 @@ fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist); }else{ i64 iRowid = 0; - i64 iDelta = 0; + u64 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 && iOffaPoslist + 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 +229587,9 @@ } pIter->aPoslist = p; + if( &pIter->aPoslist[pIter->nPoslist]>pIter->aEof ){ + pIter->aPoslist = 0; + } } } @@ -221185,9 +229598,11 @@ 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 @@ -221241,16 +229656,20 @@ 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,177 +229696,214 @@ 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 */ + int nBuf, /* Number of buffers in array aBuf[] */ + Fts5Buffer *aBuf /* Other lists to merge in */ ){ - 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); +#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<=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; + } - 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) ); + if( pHead==0 || pHead->pNext==0 ){ + p->rc = FTS5_CORRUPT; + 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; - - sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1); - sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2); - assert_nc( iPos1>=0 && iPos2>=0 ); - - if( iPos1=0 && iPos2>=0 ){ - while( 1 ){ - 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); + /* 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; + } + fts5BufferZero(&tmp); + + pThis = pHead; + pHead = pThis->pNext; + sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pThis->iPos); + fts5PrefixMergerNextPosition(pThis); + fts5PrefixMergerInsertByPosition(&pHead, pThis); + + while( pHead->pNext ){ + pThis = pHead; + if( pThis->iPos!=iPrev ){ + sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pThis->iPos); } + fts5PrefixMergerNextPosition(pThis); + pHead = pThis->pNext; + fts5PrefixMergerInsertByPosition(&pHead, pThis); + } - /* 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; + if( pHead->iPos!=iPrev ){ + sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pHead->iPos); + } + nTail = pHead->iter.nPoslist - pHead->iOff; + + /* 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) ); } - } - if( i1.aPoslist ){ - fts5MergeAppendDocid(&out, iLastRowid, i1.iRowid); - fts5BufferSafeAppendBlob(&out, i1.aPoslist, i1.aEof - i1.aPoslist); - } - else if( i2.aPoslist ){ - fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid); - fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.aEof - i2.aPoslist); + }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); } - assert_nc( out.n<=(p1->n+p2->n+9) ); - - fts5BufferSet(&p->rc, p1, out.n, out.p); - fts5BufferFree(&tmp); - fts5BufferFree(&out); } + + fts5BufferFree(p1); + fts5BufferFree(&tmp); + memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING); + *p1 = out; } 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 */ ){ Fts5Structure *pStruct; Fts5Buffer *aBuf; - const int nBuf = 32; + int nBuf = 32; + int nMerge = 1; - void (*xMerge)(Fts5Index*, Fts5Buffer*, Fts5Buffer*); + void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*); void (*xAppend)(Fts5Index*, i64, Fts5Iter*, Fts5Buffer*); if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ xMerge = fts5MergeRowidLists; xAppend = fts5AppendRowid; }else{ + nMerge = FTS5_MERGE_NLIST-1; + nBuf = nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */ xMerge = fts5MergePrefixLists; xAppend = fts5AppendPoslist; } @@ -221467,6 +229923,27 @@ int bNewTerm = 1; memset(&doclist, 0, sizeof(doclist)); + if( iIdx!=0 ){ + int dummy = 0; + const int f2 = FTS5INDEX_QUERY_SKIPEMPTY|FTS5INDEX_QUERY_NOOUTPUT; + pToken[0] = FTS5_MAIN_PREFIX; + fts5MultiIterNew(p, pStruct, f2, pColset, pToken, nToken, -1, 0, &p1); + fts5IterSetOutputCb(&p->rc, p1); + for(; + fts5MultiIterEof(p, p1)==0; + fts5MultiIterNext2(p, p1, &dummy) + ){ + Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; + p1->xSetOutputs(p1, pSeg); + if( p1->base.nData ){ + xAppend(p, p1->base.iRowid-iLastRowid, p1, &doclist); + iLastRowid = p1->base.iRowid; + } + } + fts5MultiIterFree(p1); + } + + pToken[0] = FTS5_MAIN_PREFIX + iIdx; fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1); fts5IterSetOutputCb(&p->rc, p1); for( /* no-op */ ; @@ -221487,13 +229964,21 @@ if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){ for(i=0; p->rc==SQLITE_OK && doclist.n; i++){ - assert( ibase.iRowid; } - for(i=0; irc==SQLITE_OK ){ - xMerge(p, &doclist, &aBuf[i]); + xMerge(p, &doclist, nMerge, &aBuf[i]); + } + for(iFree=i; iFreerc, &buf, nToken+1)==0 ){ int iIdx = 0; /* Index to search */ - if( nToken ) memcpy(&buf.p[1], pToken, nToken); + int iPrefixIdx = 0; /* +1 prefix index */ + if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken); /* 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,7 +230273,9 @@ 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; } } @@ -221800,13 +230292,16 @@ }else{ /* Scan multiple terms in the main index */ 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); + } } } @@ -221874,8 +230369,9 @@ 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); } /* @@ -222053,7 +230549,7 @@ Fts5IndexIter *pIter = 0; int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 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 ){ @@ -222412,12 +230908,13 @@ ** 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 +230925,16 @@ /* 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); } } @@ -222473,6 +230971,7 @@ }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 +230982,7 @@ 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,6 +230998,7 @@ ** function only. */ +#ifdef SQLITE_TEST /* ** Decode a segment-data rowid from the %_data table. This function is ** the opposite of macro FTS5_SEGMENT_ROWID(). @@ -222521,7 +231021,9 @@ *piSegid = (int)(iRowid & (((i64)1 << FTS5_DATA_ID_B) - 1)); } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ int iSegid, iHeight, iPgno, bDlidx; /* Rowid compenents */ fts5DecodeRowid(iKey, &iSegid, &bDlidx, &iHeight, &iPgno); @@ -222539,7 +231041,9 @@ ); } } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST static void fts5DebugStructure( int *pRc, /* IN/OUT: error code */ Fts5Buffer *pBuf, @@ -222561,7 +231065,9 @@ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "}"); } } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** This is part of the fts5_decode() debugging aid. ** @@ -222586,7 +231092,9 @@ fts5DebugStructure(pRc, pBuf, p); fts5StructureRelease(p); } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** This is part of the fts5_decode() debugging aid. ** @@ -222609,7 +231117,9 @@ zSpace = " "; } } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** 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 +231136,9 @@ } return iOff; } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** 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 +231171,9 @@ return iOff; } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** This function is part of the fts5_decode() debugging function. It is ** only ever used with detail=none tables. @@ -222700,7 +231214,9 @@ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " %lld%s", iRowid, zApp); } } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** The implementation of user-defined scalar function fts5_decode(). */ @@ -222909,7 +231425,9 @@ } fts5BufferFree(&s); } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** The implementation of user-defined scalar function fts5_rowid(). */ @@ -222943,6 +231461,7 @@ } } } +#endif /* SQLITE_TEST */ /* ** This is called as part of registering the FTS5 module with database @@ -222953,6 +231472,7 @@ ** SQLite error code is returned instead. */ static int sqlite3Fts5IndexInit(sqlite3 *db){ +#ifdef SQLITE_TEST int rc = sqlite3_create_function( db, "fts5_decode", 2, SQLITE_UTF8, 0, fts5DecodeFunction, 0, 0 ); @@ -222970,6 +231490,10 @@ ); } return rc; +#else + return SQLITE_OK; + UNUSED_PARAM(db); +#endif } @@ -223005,7 +231529,9 @@ ** 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; @@ -223447,6 +231973,23 @@ #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->ePattern==FTS5_PATTERN_GLOB && p->op==FTS5_PATTERN_GLOB ){ + return 1; + } + if( pConfig->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 +232019,9 @@ ** ** 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: ">" @@ -223537,7 +232082,7 @@ 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; @@ -223561,25 +232106,29 @@ if( bSeenRank ) continue; idxStr[iIdxStr++] = 'r'; bSeenRank = 1; - }else{ + }else if( iCol>=0 ){ 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' ); + }else if( bSeenEq==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0 ){ + idxStr[iIdxStr++] = '='; + bSeenEq = 1; + pInfo->aConstraintUsage[i].argvIndex = ++iCons; + } } } @@ -224212,19 +232761,14 @@ case 'r': pRank = apVal[i]; break; - case 'm': { + case 'M': { const char *zText = (const char*)sqlite3_value_text(apVal[i]); if( zText==0 ) zText = ""; - - 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; - } + 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 @@ -224234,7 +232778,7 @@ goto filter_out; }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; @@ -224244,6 +232788,25 @@ 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 '=': pRowidEq = apVal[i]; break; @@ -224318,7 +232881,8 @@ 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); @@ -224491,7 +233055,8 @@ 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); @@ -224892,13 +233457,15 @@ 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; } @@ -225122,7 +233689,8 @@ 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 +233749,8 @@ 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 +233758,8 @@ 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 ){ @@ -225654,8 +234224,7 @@ Fts5Global *pGlobal, const char **azArg, int nArg, - Fts5Tokenizer **ppTok, - fts5_tokenizer **ppTokApi, + Fts5Config *pConfig, char **pzErr ){ Fts5TokenizerModule *pMod; @@ -225667,16 +234236,22 @@ rc = SQLITE_ERROR; *pzErr = sqlite3_mprintf("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"); + rc = pMod->x.xCreate( + pMod->pUserData, (azArg?&azArg[1]:0), (nArg?nArg-1:0), &pConfig->pTok + ); + pConfig->pTokApi = &pMod->x; + if( rc!=SQLITE_OK ){ + if( pzErr ) *pzErr = sqlite3_mprintf("error in tokenizer constructor"); + }else{ + pConfig->ePattern = sqlite3Fts5TokenizerPattern( + pMod->x.xCreate, pConfig->pTok + ); } } if( rc!=SQLITE_OK ){ - *ppTokApi = 0; - *ppTok = 0; + pConfig->pTokApi = 0; + pConfig->pTok = 0; } return rc; @@ -225725,7 +234300,7 @@ ){ 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: 2022-02-22 18:58:40 40fa792d359f84c3b9e9d6623743e1a59826274e221df1bde8f47086968a1bab", -1, SQLITE_TRANSIENT); } /* @@ -226276,21 +234851,32 @@ if( pConfig->abUnindexed[iCol-1]==0 ){ const char *zText; int nText; + 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); - }else{ + }else if( ALWAYS(apVal) ){ zText = (const char*)sqlite3_value_text(apVal[iCol-1]); nText = sqlite3_value_bytes(apVal[iCol-1]); + }else{ + continue; } ctx.szCol = 0; rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, zText, nText, (void*)&ctx, fts5StorageInsertCallback ); p->aTotalSize[iCol-1] -= (i64)ctx.szCol; + if( p->aTotalSize[iCol-1]<0 ){ + rc = FTS5_CORRUPT; + } } } - 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; @@ -226733,13 +235319,14 @@ ** 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 +235335,88 @@ 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( pConfig->bColumnsize ){ + rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize); } - 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( 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] ) continue; + ctx.iCol = i; + 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( 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 +235496,9 @@ 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 +235511,8 @@ if( bCorrupt && rc==SQLITE_OK ){ rc = FTS5_CORRUPT; } + }else{ + assert( rc!=SQLITE_OK ); } return rc; @@ -228266,6 +236861,133 @@ ); } +/************************************************************************** +** Start of trigram implementation. +*/ +typedef struct TrigramTokenizer TrigramTokenizer; +struct TrigramTokenizer { + int bFold; /* True to fold to lower-case */ +}; + +/* +** 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 = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew)); + UNUSED_PARAM(pUnused); + if( pNew==0 ){ + rc = SQLITE_NOMEM; + }else{ + int i; + pNew->bFold = 1; + for(i=0; rc==SQLITE_OK && ibFold = (zArg[0]=='0'); + } + }else{ + 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]; + const unsigned char *zIn = (const unsigned char*)pText; + const unsigned char *zEof = &zIn[nText]; + u32 iCode; + + UNUSED_PARAM(unusedFlags); + while( 1 ){ + char *zOut = aBuf; + int iStart = zIn - (const unsigned char*)pText; + const unsigned char *zNext; + + READ_UTF8(zIn, zEof, iCode); + if( iCode==0 ) break; + zNext = zIn; + if( zInbFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0); + WRITE_UTF8(zOut, iCode); + READ_UTF8(zIn, zEof, iCode); + if( iCode==0 ) break; + }else{ + break; + } + if( zInbFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0); + WRITE_UTF8(zOut, iCode); + READ_UTF8(zIn, zEof, iCode); + if( iCode==0 ) break; + if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0); + WRITE_UTF8(zOut, iCode); + }else{ + break; + } + rc = xToken(pCtx, 0, aBuf, zOut-aBuf, iStart, iStart + zOut-aBuf); + if( rc!=SQLITE_OK ) break; + zIn = zNext; + } + + 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; + return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB; + } + return FTS5_PATTERN_NONE; +} + /* ** Register all built-in tokenizers with FTS5. */ @@ -228277,6 +236999,7 @@ { "unicode61", {fts5UnicodeCreate, fts5UnicodeDelete, fts5UnicodeTokenize}}, { "ascii", {fts5AsciiCreate, fts5AsciiDelete, fts5AsciiTokenize }}, { "porter", {fts5PorterCreate, fts5PorterDelete, fts5PorterTokenize }}, + { "trigram", {fts5TriCreate, fts5TriDelete, fts5TriTokenize}}, }; int rc = SQLITE_OK; /* Return code */ @@ -229069,8 +237792,10 @@ } iTbl++; } + aAscii[0] = 0; /* 0x00 is never a token character */ } + /* ** 2015 May 30 ** @@ -229478,6 +238203,7 @@ 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 */ @@ -229791,7 +238517,7 @@ } 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 +238537,8 @@ 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 +238616,11 @@ 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 ){ @@ -230064,6 +238794,9 @@ 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); @@ -230508,10 +239241,6 @@ #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; } /************************** End of sqlite3.c ******************************/ diff -Nru mstflint-4.17.0+1/ext_libs/sqlite/sqlite3.h mstflint-4.21.0+1/ext_libs/sqlite/sqlite3.h --- mstflint-4.17.0+1/ext_libs/sqlite/sqlite3.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/ext_libs/sqlite/sqlite3.h 2022-07-31 09:33:57.000000000 +0000 @@ -32,39 +32,62 @@ */ #ifndef SQLITE3_H #define SQLITE3_H -#include /* Needed for the definition of va_list */ +#include /* Needed for the definition of va_list */ /* ** Make sure we can call this stuff from C++. */ #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif - /* -** 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 +#define SQLITE_EXTERN extern #endif #ifndef SQLITE_API -# define SQLITE_API +#define SQLITE_API #endif #ifndef SQLITE_CDECL -# define SQLITE_CDECL +#define SQLITE_CDECL #endif #ifndef SQLITE_APICALL -# define SQLITE_APICALL +#define SQLITE_APICALL #endif #ifndef SQLITE_STDCALL -# define SQLITE_STDCALL SQLITE_APICALL +#define SQLITE_STDCALL SQLITE_APICALL #endif #ifndef SQLITE_CALLBACK -# define SQLITE_CALLBACK +#define SQLITE_CALLBACK #endif #ifndef SQLITE_SYSAPI -# define SQLITE_SYSAPI +#define SQLITE_SYSAPI #endif /* @@ -87,10 +110,10 @@ ** Ensure these symbols were not defined by some previous header file. */ #ifdef SQLITE_VERSION -# undef SQLITE_VERSION +#undef SQLITE_VERSION #endif #ifdef SQLITE_VERSION_NUMBER -# undef SQLITE_VERSION_NUMBER +#undef SQLITE_VERSION_NUMBER #endif /* @@ -123,46 +146,46 @@ ** [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" - -/* -** CAPI3REF: Run-Time Library Version Numbers -** KEYWORDS: sqlite3_version sqlite3_sourceid -** -** These interfaces provide the same information as the [SQLITE_VERSION], -** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros -** but are associated with the library instead of the header file. ^(Cautious -** programmers might include assert() statements in their application to -** verify that values returned by these interfaces match the macros in -** the header, and thus ensure that the application is -** compiled with matching library and header files. -** -**
    -** assert( sqlite3_libversion_number()==SQLITE_VERSION_NUMBER );
    -** assert( strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,80)==0 );
    -** assert( strcmp(sqlite3_libversion(),SQLITE_VERSION)==0 );
    -** 
    )^ -** -** ^The sqlite3_version[] string constant contains the text of [SQLITE_VERSION] -** macro. ^The sqlite3_libversion() function returns a pointer to the -** to the sqlite3_version[] string constant. The sqlite3_libversion() -** function is provided for use in DLLs since DLL users usually do not have -** direct access to string constants within the DLL. ^The -** sqlite3_libversion_number() function returns an integer equal to -** [SQLITE_VERSION_NUMBER]. ^(The sqlite3_sourceid() function returns -** a pointer to a string constant whose value is the same as the -** [SQLITE_SOURCE_ID] C preprocessor macro. Except if SQLite is built -** using an edited copy of [the amalgamation], then the last four characters -** of the hash might be different from [SQLITE_SOURCE_ID].)^ -** -** See also: [sqlite_version()] and [sqlite_source_id()]. -*/ -SQLITE_API SQLITE_EXTERN const char sqlite3_version[]; -SQLITE_API const char *sqlite3_libversion(void); -SQLITE_API const char *sqlite3_sourceid(void); -SQLITE_API int sqlite3_libversion_number(void); +#define SQLITE_VERSION "3.38.0" +#define SQLITE_VERSION_NUMBER 3038000 +#define SQLITE_SOURCE_ID "2022-02-22 18:58:40 40fa792d359f84c3b9e9d6623743e1a59826274e221df1bde8f47086968a1bab" + + /* + ** CAPI3REF: Run-Time Library Version Numbers + ** KEYWORDS: sqlite3_version sqlite3_sourceid + ** + ** These interfaces provide the same information as the [SQLITE_VERSION], + ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros + ** but are associated with the library instead of the header file. ^(Cautious + ** programmers might include assert() statements in their application to + ** verify that values returned by these interfaces match the macros in + ** the header, and thus ensure that the application is + ** compiled with matching library and header files. + ** + **
    +    ** assert( sqlite3_libversion_number()==SQLITE_VERSION_NUMBER );
    +    ** assert( strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,80)==0 );
    +    ** assert( strcmp(sqlite3_libversion(),SQLITE_VERSION)==0 );
    +    ** 
    )^ + ** + ** ^The sqlite3_version[] string constant contains the text of [SQLITE_VERSION] + ** macro. ^The sqlite3_libversion() function returns a pointer to the + ** to the sqlite3_version[] string constant. The sqlite3_libversion() + ** function is provided for use in DLLs since DLL users usually do not have + ** direct access to string constants within the DLL. ^The + ** sqlite3_libversion_number() function returns an integer equal to + ** [SQLITE_VERSION_NUMBER]. ^(The sqlite3_sourceid() function returns + ** a pointer to a string constant whose value is the same as the + ** [SQLITE_SOURCE_ID] C preprocessor macro. Except if SQLite is built + ** using an edited copy of [the amalgamation], then the last four characters + ** of the hash might be different from [SQLITE_SOURCE_ID].)^ + ** + ** See also: [sqlite_version()] and [sqlite_source_id()]. + */ + SQLITE_API SQLITE_EXTERN const char sqlite3_version[]; + SQLITE_API const char* sqlite3_libversion(void); + SQLITE_API const char* sqlite3_sourceid(void); + SQLITE_API int sqlite3_libversion_number(void); /* ** CAPI3REF: Run-Time Library Compilation Options Diagnostics @@ -187,66 +210,66 @@ ** [sqlite_compileoption_get()] and the [compile_options pragma]. */ #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS -SQLITE_API int sqlite3_compileoption_used(const char *zOptName); -SQLITE_API const char *sqlite3_compileoption_get(int N); + SQLITE_API int sqlite3_compileoption_used(const char* zOptName); + SQLITE_API const char* sqlite3_compileoption_get(int N); #else -# define sqlite3_compileoption_used(X) 0 -# define sqlite3_compileoption_get(X) ((void*)0) +#define sqlite3_compileoption_used(X) 0 +#define sqlite3_compileoption_get(X) ((void*)0) #endif -/* -** CAPI3REF: Test To See If The Library Is Threadsafe -** -** ^The sqlite3_threadsafe() function returns zero if and only if -** SQLite was compiled with mutexing code omitted due to the -** [SQLITE_THREADSAFE] compile-time option being set to 0. -** -** SQLite can be compiled with or without mutexes. When -** the [SQLITE_THREADSAFE] C preprocessor macro is 1 or 2, mutexes -** are enabled and SQLite is threadsafe. When the -** [SQLITE_THREADSAFE] macro is 0, -** the mutexes are omitted. Without the mutexes, it is not safe -** to use SQLite concurrently from more than one thread. -** -** Enabling mutexes incurs a measurable performance penalty. -** So if speed is of utmost importance, it makes sense to disable -** the mutexes. But for maximum safety, mutexes should be enabled. -** ^The default behavior is for mutexes to be enabled. -** -** This interface can be used by an application to make sure that the -** version of SQLite that it is linking against was compiled with -** the desired setting of the [SQLITE_THREADSAFE] macro. -** -** This interface only reports on the compile-time mutex setting -** of the [SQLITE_THREADSAFE] flag. If SQLite is compiled with -** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but -** can be fully or partially disabled using a call to [sqlite3_config()] -** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD], -** or [SQLITE_CONFIG_SERIALIZED]. ^(The return value of the -** sqlite3_threadsafe() function shows only the compile-time setting of -** thread safety, not any run-time changes to that setting made by -** sqlite3_config(). In other words, the return value from sqlite3_threadsafe() -** is unchanged by calls to sqlite3_config().)^ -** -** See the [threading mode] documentation for additional information. -*/ -SQLITE_API int sqlite3_threadsafe(void); - -/* -** CAPI3REF: Database Connection Handle -** KEYWORDS: {database connection} {database connections} -** -** Each open SQLite database is represented by a pointer to an instance of -** the opaque structure named "sqlite3". It is useful to think of an sqlite3 -** pointer as an object. The [sqlite3_open()], [sqlite3_open16()], and -** [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()] -** and [sqlite3_close_v2()] are its destructors. There are many other -** interfaces (such as -** [sqlite3_prepare_v2()], [sqlite3_create_function()], and -** [sqlite3_busy_timeout()] to name but three) that are methods on an -** sqlite3 object. -*/ -typedef struct sqlite3 sqlite3; + /* + ** CAPI3REF: Test To See If The Library Is Threadsafe + ** + ** ^The sqlite3_threadsafe() function returns zero if and only if + ** SQLite was compiled with mutexing code omitted due to the + ** [SQLITE_THREADSAFE] compile-time option being set to 0. + ** + ** SQLite can be compiled with or without mutexes. When + ** the [SQLITE_THREADSAFE] C preprocessor macro is 1 or 2, mutexes + ** are enabled and SQLite is threadsafe. When the + ** [SQLITE_THREADSAFE] macro is 0, + ** the mutexes are omitted. Without the mutexes, it is not safe + ** to use SQLite concurrently from more than one thread. + ** + ** Enabling mutexes incurs a measurable performance penalty. + ** So if speed is of utmost importance, it makes sense to disable + ** the mutexes. But for maximum safety, mutexes should be enabled. + ** ^The default behavior is for mutexes to be enabled. + ** + ** This interface can be used by an application to make sure that the + ** version of SQLite that it is linking against was compiled with + ** the desired setting of the [SQLITE_THREADSAFE] macro. + ** + ** This interface only reports on the compile-time mutex setting + ** of the [SQLITE_THREADSAFE] flag. If SQLite is compiled with + ** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but + ** can be fully or partially disabled using a call to [sqlite3_config()] + ** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD], + ** or [SQLITE_CONFIG_SERIALIZED]. ^(The return value of the + ** sqlite3_threadsafe() function shows only the compile-time setting of + ** thread safety, not any run-time changes to that setting made by + ** sqlite3_config(). In other words, the return value from sqlite3_threadsafe() + ** is unchanged by calls to sqlite3_config().)^ + ** + ** See the [threading mode] documentation for additional information. + */ + SQLITE_API int sqlite3_threadsafe(void); + + /* + ** CAPI3REF: Database Connection Handle + ** KEYWORDS: {database connection} {database connections} + ** + ** Each open SQLite database is represented by a pointer to an instance of + ** the opaque structure named "sqlite3". It is useful to think of an sqlite3 + ** pointer as an object. The [sqlite3_open()], [sqlite3_open16()], and + ** [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()] + ** and [sqlite3_close_v2()] are its destructors. There are many other + ** interfaces (such as + ** [sqlite3_prepare_v2()], [sqlite3_create_function()], and + ** [sqlite3_busy_timeout()] to name but three) that are methods on an + ** sqlite3 object. + */ + typedef struct sqlite3 sqlite3; /* ** CAPI3REF: 64-Bit Integer Types @@ -265,147 +288,146 @@ ** between 0 and +18446744073709551615 inclusive. */ #ifdef SQLITE_INT64_TYPE - typedef SQLITE_INT64_TYPE sqlite_int64; -# ifdef SQLITE_UINT64_TYPE + typedef SQLITE_INT64_TYPE sqlite_int64; +#ifdef SQLITE_UINT64_TYPE typedef SQLITE_UINT64_TYPE sqlite_uint64; -# else +#else typedef unsigned SQLITE_INT64_TYPE sqlite_uint64; -# endif +#endif #elif defined(_MSC_VER) || defined(__BORLANDC__) - typedef __int64 sqlite_int64; - typedef unsigned __int64 sqlite_uint64; +typedef __int64 sqlite_int64; +typedef unsigned __int64 sqlite_uint64; #else - typedef long long int sqlite_int64; - typedef unsigned long long int sqlite_uint64; +typedef long long int sqlite_int64; +typedef unsigned long long int sqlite_uint64; #endif -typedef sqlite_int64 sqlite3_int64; -typedef sqlite_uint64 sqlite3_uint64; + typedef sqlite_int64 sqlite3_int64; + typedef sqlite_uint64 sqlite3_uint64; /* ** If compiling for a processor that lacks floating point support, ** substitute integer for floating-point. */ #ifdef SQLITE_OMIT_FLOATING_POINT -# define double sqlite3_int64 +#define double sqlite3_int64 #endif -/* -** CAPI3REF: Closing A Database Connection -** DESTRUCTOR: sqlite3 -** -** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors -** for the [sqlite3] object. -** ^Calls to sqlite3_close() and sqlite3_close_v2() return [SQLITE_OK] if -** the [sqlite3] object is successfully destroyed and all associated -** resources are deallocated. -** -** Ideally, applications should [sqlite3_finalize | finalize] all -** [prepared statements], [sqlite3_blob_close | close] all [BLOB handles], and -** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated -** with the [sqlite3] object prior to attempting to close the object. -** ^If the database connection is associated with unfinalized prepared -** statements, BLOB handlers, and/or unfinished sqlite3_backup objects then -** sqlite3_close() will leave the database connection open and return -** [SQLITE_BUSY]. ^If sqlite3_close_v2() is called with unfinalized prepared -** statements, unclosed BLOB handlers, and/or unfinished sqlite3_backups, -** it returns [SQLITE_OK] regardless, but instead of deallocating the database -** connection immediately, it marks the database connection as an unusable -** "zombie" and makes arrangements to automatically deallocate the database -** connection after all prepared statements are finalized, all BLOB handles -** are closed, and all backups have finished. The sqlite3_close_v2() interface -** is intended for use with host languages that are garbage collected, and -** where the order in which destructors are called is arbitrary. -** -** ^If an [sqlite3] object is destroyed while a transaction is open, -** the transaction is automatically rolled back. -** -** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)] -** must be either a NULL -** pointer or an [sqlite3] object pointer obtained -** from [sqlite3_open()], [sqlite3_open16()], or -** [sqlite3_open_v2()], and not previously closed. -** ^Calling sqlite3_close() or sqlite3_close_v2() with a NULL pointer -** argument is a harmless no-op. -*/ -SQLITE_API int sqlite3_close(sqlite3*); -SQLITE_API int sqlite3_close_v2(sqlite3*); - -/* -** The type for a callback function. -** This is legacy and deprecated. It is included for historical -** compatibility and is not documented. -*/ -typedef int (*sqlite3_callback)(void*,int,char**, char**); - -/* -** CAPI3REF: One-Step Query Execution Interface -** METHOD: sqlite3 -** -** The sqlite3_exec() interface is a convenience wrapper around -** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()], -** that allows an application to run multiple statements of SQL -** without having to use a lot of C code. -** -** ^The sqlite3_exec() interface runs zero or more UTF-8 encoded, -** semicolon-separate SQL statements passed into its 2nd argument, -** in the context of the [database connection] passed in as its 1st -** argument. ^If the callback function of the 3rd argument to -** sqlite3_exec() is not NULL, then it is invoked for each result row -** coming out of the evaluated SQL statements. ^The 4th argument to -** sqlite3_exec() is relayed through to the 1st argument of each -** callback invocation. ^If the callback pointer to sqlite3_exec() -** is NULL, then no callback is ever invoked and result rows are -** ignored. -** -** ^If an error occurs while evaluating the SQL statements passed into -** sqlite3_exec(), then execution of the current statement stops and -** subsequent statements are skipped. ^If the 5th parameter to sqlite3_exec() -** is not NULL then any error message is written into memory obtained -** from [sqlite3_malloc()] and passed back through the 5th parameter. -** To avoid memory leaks, the application should invoke [sqlite3_free()] -** on error message strings returned through the 5th parameter of -** sqlite3_exec() after the error message string is no longer needed. -** ^If the 5th parameter to sqlite3_exec() is not NULL and no errors -** occur, then sqlite3_exec() sets the pointer in its 5th parameter to -** NULL before returning. -** -** ^If an sqlite3_exec() callback returns non-zero, the sqlite3_exec() -** routine returns SQLITE_ABORT without invoking the callback again and -** without running any subsequent SQL statements. -** -** ^The 2nd argument to the sqlite3_exec() callback function is the -** number of columns in the result. ^The 3rd argument to the sqlite3_exec() -** callback is an array of pointers to strings obtained as if from -** [sqlite3_column_text()], one for each column. ^If an element of a -** result row is NULL then the corresponding string pointer for the -** sqlite3_exec() callback is a NULL pointer. ^The 4th argument to the -** sqlite3_exec() callback is an array of pointers to strings where each -** entry represents the name of corresponding result column as obtained -** from [sqlite3_column_name()]. -** -** ^If the 2nd parameter to sqlite3_exec() is a NULL pointer, a pointer -** to an empty string, or a pointer that contains only whitespace and/or -** SQL comments, then no SQL statements are evaluated and the database -** is not changed. -** -** Restrictions: -** -**
      -**
    • The application must ensure that the 1st parameter to sqlite3_exec() -** is a valid and open [database connection]. -**
    • The application must not close the [database connection] specified by -** 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. -**
    -*/ -SQLITE_API int sqlite3_exec( - sqlite3*, /* An open database */ - const char *sql, /* SQL to be evaluated */ - int (*callback)(void*,int,char**,char**), /* Callback function */ - void *, /* 1st argument to callback */ - char **errmsg /* Error msg written here */ -); + /* + ** CAPI3REF: Closing A Database Connection + ** DESTRUCTOR: sqlite3 + ** + ** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors + ** for the [sqlite3] object. + ** ^Calls to sqlite3_close() and sqlite3_close_v2() return [SQLITE_OK] if + ** the [sqlite3] object is successfully destroyed and all associated + ** resources are deallocated. + ** + ** Ideally, applications should [sqlite3_finalize | finalize] all + ** [prepared statements], [sqlite3_blob_close | close] all [BLOB handles], and + ** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated + ** with the [sqlite3] object prior to attempting to close the object. + ** ^If the database connection is associated with unfinalized prepared + ** statements, BLOB handlers, and/or unfinished sqlite3_backup objects then + ** sqlite3_close() will leave the database connection open and return + ** [SQLITE_BUSY]. ^If sqlite3_close_v2() is called with unfinalized prepared + ** statements, unclosed BLOB handlers, and/or unfinished sqlite3_backups, + ** it returns [SQLITE_OK] regardless, but instead of deallocating the database + ** connection immediately, it marks the database connection as an unusable + ** "zombie" and makes arrangements to automatically deallocate the database + ** connection after all prepared statements are finalized, all BLOB handles + ** are closed, and all backups have finished. The sqlite3_close_v2() interface + ** is intended for use with host languages that are garbage collected, and + ** where the order in which destructors are called is arbitrary. + ** + ** ^If an [sqlite3] object is destroyed while a transaction is open, + ** the transaction is automatically rolled back. + ** + ** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)] + ** must be either a NULL + ** pointer or an [sqlite3] object pointer obtained + ** from [sqlite3_open()], [sqlite3_open16()], or + ** [sqlite3_open_v2()], and not previously closed. + ** ^Calling sqlite3_close() or sqlite3_close_v2() with a NULL pointer + ** argument is a harmless no-op. + */ + SQLITE_API int sqlite3_close(sqlite3*); + SQLITE_API int sqlite3_close_v2(sqlite3*); + + /* + ** The type for a callback function. + ** This is legacy and deprecated. It is included for historical + ** compatibility and is not documented. + */ + typedef int (*sqlite3_callback)(void*, int, char**, char**); + + /* + ** CAPI3REF: One-Step Query Execution Interface + ** METHOD: sqlite3 + ** + ** The sqlite3_exec() interface is a convenience wrapper around + ** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()], + ** that allows an application to run multiple statements of SQL + ** without having to use a lot of C code. + ** + ** ^The sqlite3_exec() interface runs zero or more UTF-8 encoded, + ** semicolon-separate SQL statements passed into its 2nd argument, + ** in the context of the [database connection] passed in as its 1st + ** argument. ^If the callback function of the 3rd argument to + ** sqlite3_exec() is not NULL, then it is invoked for each result row + ** coming out of the evaluated SQL statements. ^The 4th argument to + ** sqlite3_exec() is relayed through to the 1st argument of each + ** callback invocation. ^If the callback pointer to sqlite3_exec() + ** is NULL, then no callback is ever invoked and result rows are + ** ignored. + ** + ** ^If an error occurs while evaluating the SQL statements passed into + ** sqlite3_exec(), then execution of the current statement stops and + ** subsequent statements are skipped. ^If the 5th parameter to sqlite3_exec() + ** is not NULL then any error message is written into memory obtained + ** from [sqlite3_malloc()] and passed back through the 5th parameter. + ** To avoid memory leaks, the application should invoke [sqlite3_free()] + ** on error message strings returned through the 5th parameter of + ** sqlite3_exec() after the error message string is no longer needed. + ** ^If the 5th parameter to sqlite3_exec() is not NULL and no errors + ** occur, then sqlite3_exec() sets the pointer in its 5th parameter to + ** NULL before returning. + ** + ** ^If an sqlite3_exec() callback returns non-zero, the sqlite3_exec() + ** routine returns SQLITE_ABORT without invoking the callback again and + ** without running any subsequent SQL statements. + ** + ** ^The 2nd argument to the sqlite3_exec() callback function is the + ** number of columns in the result. ^The 3rd argument to the sqlite3_exec() + ** callback is an array of pointers to strings obtained as if from + ** [sqlite3_column_text()], one for each column. ^If an element of a + ** result row is NULL then the corresponding string pointer for the + ** sqlite3_exec() callback is a NULL pointer. ^The 4th argument to the + ** sqlite3_exec() callback is an array of pointers to strings where each + ** entry represents the name of corresponding result column as obtained + ** from [sqlite3_column_name()]. + ** + ** ^If the 2nd parameter to sqlite3_exec() is a NULL pointer, a pointer + ** to an empty string, or a pointer that contains only whitespace and/or + ** SQL comments, then no SQL statements are evaluated and the database + ** is not changed. + ** + ** Restrictions: + ** + **
      + **
    • The application must ensure that the 1st parameter to sqlite3_exec() + ** is a valid and open [database connection]. + **
    • The application must not close the [database connection] specified by + ** 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. + **
    + */ + SQLITE_API int sqlite3_exec(sqlite3*, /* An open database */ + const char* sql, /* SQL to be evaluated */ + int (*callback)(void*, int, char**, char**), /* Callback function */ + void*, /* 1st argument to callback */ + char** errmsg /* Error msg written here */ + ); /* ** CAPI3REF: Result Codes @@ -418,38 +440,38 @@ ** ** See also: [extended result code definitions] */ -#define SQLITE_OK 0 /* Successful result */ +#define SQLITE_OK 0 /* Successful result */ /* beginning-of-error-codes */ -#define SQLITE_ERROR 1 /* Generic error */ -#define SQLITE_INTERNAL 2 /* Internal logic error in SQLite */ -#define SQLITE_PERM 3 /* Access permission denied */ -#define SQLITE_ABORT 4 /* Callback routine requested an abort */ -#define SQLITE_BUSY 5 /* The database file is locked */ -#define SQLITE_LOCKED 6 /* A table in the database is locked */ -#define SQLITE_NOMEM 7 /* A malloc() failed */ -#define SQLITE_READONLY 8 /* Attempt to write a readonly database */ -#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/ -#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ -#define SQLITE_CORRUPT 11 /* The database disk image is malformed */ -#define SQLITE_NOTFOUND 12 /* Unknown opcode in sqlite3_file_control() */ -#define SQLITE_FULL 13 /* Insertion failed because database is full */ -#define SQLITE_CANTOPEN 14 /* Unable to open the database file */ -#define SQLITE_PROTOCOL 15 /* Database lock protocol error */ -#define SQLITE_EMPTY 16 /* Internal use only */ -#define SQLITE_SCHEMA 17 /* The database schema changed */ -#define SQLITE_TOOBIG 18 /* String or BLOB exceeds size limit */ -#define SQLITE_CONSTRAINT 19 /* Abort due to constraint violation */ -#define SQLITE_MISMATCH 20 /* Data type mismatch */ -#define SQLITE_MISUSE 21 /* Library used incorrectly */ -#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */ -#define SQLITE_AUTH 23 /* Authorization denied */ -#define SQLITE_FORMAT 24 /* Not used */ -#define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */ -#define SQLITE_NOTADB 26 /* File opened that is not a database file */ -#define SQLITE_NOTICE 27 /* Notifications from sqlite3_log() */ -#define SQLITE_WARNING 28 /* Warnings from sqlite3_log() */ -#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */ -#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */ +#define SQLITE_ERROR 1 /* Generic error */ +#define SQLITE_INTERNAL 2 /* Internal logic error in SQLite */ +#define SQLITE_PERM 3 /* Access permission denied */ +#define SQLITE_ABORT 4 /* Callback routine requested an abort */ +#define SQLITE_BUSY 5 /* The database file is locked */ +#define SQLITE_LOCKED 6 /* A table in the database is locked */ +#define SQLITE_NOMEM 7 /* A malloc() failed */ +#define SQLITE_READONLY 8 /* Attempt to write a readonly database */ +#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/ +#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ +#define SQLITE_CORRUPT 11 /* The database disk image is malformed */ +#define SQLITE_NOTFOUND 12 /* Unknown opcode in sqlite3_file_control() */ +#define SQLITE_FULL 13 /* Insertion failed because database is full */ +#define SQLITE_CANTOPEN 14 /* Unable to open the database file */ +#define SQLITE_PROTOCOL 15 /* Database lock protocol error */ +#define SQLITE_EMPTY 16 /* Internal use only */ +#define SQLITE_SCHEMA 17 /* The database schema changed */ +#define SQLITE_TOOBIG 18 /* String or BLOB exceeds size limit */ +#define SQLITE_CONSTRAINT 19 /* Abort due to constraint violation */ +#define SQLITE_MISMATCH 20 /* Data type mismatch */ +#define SQLITE_MISUSE 21 /* Library used incorrectly */ +#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */ +#define SQLITE_AUTH 23 /* Authorization denied */ +#define SQLITE_FORMAT 24 /* Not used */ +#define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */ +#define SQLITE_NOTADB 26 /* File opened that is not a database file */ +#define SQLITE_NOTICE 27 /* Notifications from sqlite3_log() */ +#define SQLITE_WARNING 28 /* Warnings from sqlite3_log() */ +#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */ +#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */ /* end-of-error-codes */ /* @@ -469,79 +491,81 @@ ** the most recent error can be obtained using ** [sqlite3_extended_errcode()]. */ -#define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8)) -#define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8)) -#define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8)) -#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8)) -#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8)) -#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8)) -#define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8)) -#define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8)) -#define SQLITE_IOERR_TRUNCATE (SQLITE_IOERR | (6<<8)) -#define SQLITE_IOERR_FSTAT (SQLITE_IOERR | (7<<8)) -#define SQLITE_IOERR_UNLOCK (SQLITE_IOERR | (8<<8)) -#define SQLITE_IOERR_RDLOCK (SQLITE_IOERR | (9<<8)) -#define SQLITE_IOERR_DELETE (SQLITE_IOERR | (10<<8)) -#define SQLITE_IOERR_BLOCKED (SQLITE_IOERR | (11<<8)) -#define SQLITE_IOERR_NOMEM (SQLITE_IOERR | (12<<8)) -#define SQLITE_IOERR_ACCESS (SQLITE_IOERR | (13<<8)) -#define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14<<8)) -#define SQLITE_IOERR_LOCK (SQLITE_IOERR | (15<<8)) -#define SQLITE_IOERR_CLOSE (SQLITE_IOERR | (16<<8)) -#define SQLITE_IOERR_DIR_CLOSE (SQLITE_IOERR | (17<<8)) -#define SQLITE_IOERR_SHMOPEN (SQLITE_IOERR | (18<<8)) -#define SQLITE_IOERR_SHMSIZE (SQLITE_IOERR | (19<<8)) -#define SQLITE_IOERR_SHMLOCK (SQLITE_IOERR | (20<<8)) -#define SQLITE_IOERR_SHMMAP (SQLITE_IOERR | (21<<8)) -#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8)) -#define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23<<8)) -#define SQLITE_IOERR_MMAP (SQLITE_IOERR | (24<<8)) -#define SQLITE_IOERR_GETTEMPPATH (SQLITE_IOERR | (25<<8)) -#define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8)) -#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8)) -#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) -#define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) -#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_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) -#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) -#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) -#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) -#define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) -#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) -#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) -#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) -#define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8)) -#define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5<<8)) /* Not Used */ -#define SQLITE_CANTOPEN_SYMLINK (SQLITE_CANTOPEN | (6<<8)) -#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) -#define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<8)) -#define SQLITE_CORRUPT_INDEX (SQLITE_CORRUPT | (3<<8)) -#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) -#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) -#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8)) -#define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8)) -#define SQLITE_READONLY_CANTINIT (SQLITE_READONLY | (5<<8)) -#define SQLITE_READONLY_DIRECTORY (SQLITE_READONLY | (6<<8)) -#define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8)) -#define SQLITE_CONSTRAINT_CHECK (SQLITE_CONSTRAINT | (1<<8)) -#define SQLITE_CONSTRAINT_COMMITHOOK (SQLITE_CONSTRAINT | (2<<8)) -#define SQLITE_CONSTRAINT_FOREIGNKEY (SQLITE_CONSTRAINT | (3<<8)) -#define SQLITE_CONSTRAINT_FUNCTION (SQLITE_CONSTRAINT | (4<<8)) -#define SQLITE_CONSTRAINT_NOTNULL (SQLITE_CONSTRAINT | (5<<8)) -#define SQLITE_CONSTRAINT_PRIMARYKEY (SQLITE_CONSTRAINT | (6<<8)) -#define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7<<8)) -#define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8)) -#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_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8)) -#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<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_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1 << 8)) +#define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2 << 8)) +#define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3 << 8)) +#define SQLITE_IOERR_READ (SQLITE_IOERR | (1 << 8)) +#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2 << 8)) +#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3 << 8)) +#define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4 << 8)) +#define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5 << 8)) +#define SQLITE_IOERR_TRUNCATE (SQLITE_IOERR | (6 << 8)) +#define SQLITE_IOERR_FSTAT (SQLITE_IOERR | (7 << 8)) +#define SQLITE_IOERR_UNLOCK (SQLITE_IOERR | (8 << 8)) +#define SQLITE_IOERR_RDLOCK (SQLITE_IOERR | (9 << 8)) +#define SQLITE_IOERR_DELETE (SQLITE_IOERR | (10 << 8)) +#define SQLITE_IOERR_BLOCKED (SQLITE_IOERR | (11 << 8)) +#define SQLITE_IOERR_NOMEM (SQLITE_IOERR | (12 << 8)) +#define SQLITE_IOERR_ACCESS (SQLITE_IOERR | (13 << 8)) +#define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14 << 8)) +#define SQLITE_IOERR_LOCK (SQLITE_IOERR | (15 << 8)) +#define SQLITE_IOERR_CLOSE (SQLITE_IOERR | (16 << 8)) +#define SQLITE_IOERR_DIR_CLOSE (SQLITE_IOERR | (17 << 8)) +#define SQLITE_IOERR_SHMOPEN (SQLITE_IOERR | (18 << 8)) +#define SQLITE_IOERR_SHMSIZE (SQLITE_IOERR | (19 << 8)) +#define SQLITE_IOERR_SHMLOCK (SQLITE_IOERR | (20 << 8)) +#define SQLITE_IOERR_SHMMAP (SQLITE_IOERR | (21 << 8)) +#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22 << 8)) +#define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23 << 8)) +#define SQLITE_IOERR_MMAP (SQLITE_IOERR | (24 << 8)) +#define SQLITE_IOERR_GETTEMPPATH (SQLITE_IOERR | (25 << 8)) +#define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26 << 8)) +#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27 << 8)) +#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28 << 8)) +#define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29 << 8)) +#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_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1 << 8)) +#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2 << 8)) +#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1 << 8)) +#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2 << 8)) +#define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3 << 8)) +#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1 << 8)) +#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2 << 8)) +#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3 << 8)) +#define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4 << 8)) +#define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5 << 8)) /* Not Used */ +#define SQLITE_CANTOPEN_SYMLINK (SQLITE_CANTOPEN | (6 << 8)) +#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1 << 8)) +#define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2 << 8)) +#define SQLITE_CORRUPT_INDEX (SQLITE_CORRUPT | (3 << 8)) +#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1 << 8)) +#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2 << 8)) +#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3 << 8)) +#define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4 << 8)) +#define SQLITE_READONLY_CANTINIT (SQLITE_READONLY | (5 << 8)) +#define SQLITE_READONLY_DIRECTORY (SQLITE_READONLY | (6 << 8)) +#define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2 << 8)) +#define SQLITE_CONSTRAINT_CHECK (SQLITE_CONSTRAINT | (1 << 8)) +#define SQLITE_CONSTRAINT_COMMITHOOK (SQLITE_CONSTRAINT | (2 << 8)) +#define SQLITE_CONSTRAINT_FOREIGNKEY (SQLITE_CONSTRAINT | (3 << 8)) +#define SQLITE_CONSTRAINT_FUNCTION (SQLITE_CONSTRAINT | (4 << 8)) +#define SQLITE_CONSTRAINT_NOTNULL (SQLITE_CONSTRAINT | (5 << 8)) +#define SQLITE_CONSTRAINT_PRIMARYKEY (SQLITE_CONSTRAINT | (6 << 8)) +#define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7 << 8)) +#define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8 << 8)) +#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_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)) /* internal use only */ /* ** CAPI3REF: Flags For File Open Operations @@ -549,33 +573,46 @@ ** 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. -*/ -#define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_CREATE 0x00000004 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_DELETEONCLOSE 0x00000008 /* VFS only */ -#define SQLITE_OPEN_EXCLUSIVE 0x00000010 /* VFS only */ -#define SQLITE_OPEN_AUTOPROXY 0x00000020 /* VFS only */ -#define SQLITE_OPEN_URI 0x00000040 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_MEMORY 0x00000080 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_MAIN_DB 0x00000100 /* VFS only */ -#define SQLITE_OPEN_TEMP_DB 0x00000200 /* VFS only */ -#define SQLITE_OPEN_TRANSIENT_DB 0x00000400 /* VFS only */ -#define SQLITE_OPEN_MAIN_JOURNAL 0x00000800 /* VFS only */ -#define SQLITE_OPEN_TEMP_JOURNAL 0x00001000 /* VFS only */ -#define SQLITE_OPEN_SUBJOURNAL 0x00002000 /* VFS only */ -#define SQLITE_OPEN_SUPER_JOURNAL 0x00004000 /* VFS only */ -#define SQLITE_OPEN_NOMUTEX 0x00008000 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_FULLMUTEX 0x00010000 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_SHAREDCACHE 0x00020000 /* Ok for sqlite3_open_v2() */ -#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() */ +** +** 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() */ +#define SQLITE_OPEN_CREATE 0x00000004 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_DELETEONCLOSE 0x00000008 /* VFS only */ +#define SQLITE_OPEN_EXCLUSIVE 0x00000010 /* VFS only */ +#define SQLITE_OPEN_AUTOPROXY 0x00000020 /* VFS only */ +#define SQLITE_OPEN_URI 0x00000040 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_MEMORY 0x00000080 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_MAIN_DB 0x00000100 /* VFS only */ +#define SQLITE_OPEN_TEMP_DB 0x00000200 /* VFS only */ +#define SQLITE_OPEN_TRANSIENT_DB 0x00000400 /* VFS only */ +#define SQLITE_OPEN_MAIN_JOURNAL 0x00000800 /* VFS only */ +#define SQLITE_OPEN_TEMP_JOURNAL 0x00001000 /* VFS only */ +#define SQLITE_OPEN_SUBJOURNAL 0x00002000 /* VFS only */ +#define SQLITE_OPEN_SUPER_JOURNAL 0x00004000 /* VFS only */ +#define SQLITE_OPEN_NOMUTEX 0x00008000 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_FULLMUTEX 0x00010000 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_SHAREDCACHE 0x00020000 /* Ok for sqlite3_open_v2() */ +#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: */ -#define SQLITE_OPEN_MASTER_JOURNAL 0x00004000 /* VFS only */ - +#define SQLITE_OPEN_MASTER_JOURNAL 0x00004000 /* VFS only */ /* ** CAPI3REF: Device Characteristics @@ -610,21 +647,21 @@ ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. */ -#define SQLITE_IOCAP_ATOMIC 0x00000001 -#define SQLITE_IOCAP_ATOMIC512 0x00000002 -#define SQLITE_IOCAP_ATOMIC1K 0x00000004 -#define SQLITE_IOCAP_ATOMIC2K 0x00000008 -#define SQLITE_IOCAP_ATOMIC4K 0x00000010 -#define SQLITE_IOCAP_ATOMIC8K 0x00000020 -#define SQLITE_IOCAP_ATOMIC16K 0x00000040 -#define SQLITE_IOCAP_ATOMIC32K 0x00000080 -#define SQLITE_IOCAP_ATOMIC64K 0x00000100 -#define SQLITE_IOCAP_SAFE_APPEND 0x00000200 -#define SQLITE_IOCAP_SEQUENTIAL 0x00000400 -#define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 -#define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 -#define SQLITE_IOCAP_IMMUTABLE 0x00002000 -#define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 +#define SQLITE_IOCAP_ATOMIC 0x00000001 +#define SQLITE_IOCAP_ATOMIC512 0x00000002 +#define SQLITE_IOCAP_ATOMIC1K 0x00000004 +#define SQLITE_IOCAP_ATOMIC2K 0x00000008 +#define SQLITE_IOCAP_ATOMIC4K 0x00000010 +#define SQLITE_IOCAP_ATOMIC8K 0x00000020 +#define SQLITE_IOCAP_ATOMIC16K 0x00000040 +#define SQLITE_IOCAP_ATOMIC32K 0x00000080 +#define SQLITE_IOCAP_ATOMIC64K 0x00000100 +#define SQLITE_IOCAP_SAFE_APPEND 0x00000200 +#define SQLITE_IOCAP_SEQUENTIAL 0x00000400 +#define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 +#define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 +#define SQLITE_IOCAP_IMMUTABLE 0x00002000 +#define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 /* ** CAPI3REF: File Locking Levels @@ -633,11 +670,11 @@ ** argument to calls it makes to the xLock() and xUnlock() methods ** of an [sqlite3_io_methods] object. */ -#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 +#define SQLITE_LOCK_SHARED 1 +#define SQLITE_LOCK_RESERVED 2 +#define SQLITE_LOCK_PENDING 3 +#define SQLITE_LOCK_EXCLUSIVE 4 /* ** CAPI3REF: Synchronization Type Flags @@ -665,146 +702,148 @@ ** operating systems natively supported by SQLite, only Mac OSX ** cares about the difference.) */ -#define SQLITE_SYNC_NORMAL 0x00002 -#define SQLITE_SYNC_FULL 0x00003 -#define SQLITE_SYNC_DATAONLY 0x00010 - -/* -** CAPI3REF: OS Interface Open File Handle -** -** An [sqlite3_file] object represents an open file in the -** [sqlite3_vfs | OS interface layer]. Individual OS interface -** implementations will -** want to subclass this object by appending additional fields -** for their own use. The pMethods entry is a pointer to an -** [sqlite3_io_methods] object that defines methods for performing -** I/O operations on the open file. -*/ -typedef struct sqlite3_file sqlite3_file; -struct sqlite3_file { - const struct sqlite3_io_methods *pMethods; /* Methods for an open file */ -}; - -/* -** CAPI3REF: OS Interface File Virtual Methods Object -** -** Every file opened by the [sqlite3_vfs.xOpen] method populates an -** [sqlite3_file] object (or, more commonly, a subclass of the -** [sqlite3_file] object) with a pointer to an instance of this object. -** This object defines the methods used to perform various operations -** against the open file represented by the [sqlite3_file] object. -** -** If the [sqlite3_vfs.xOpen] method sets the sqlite3_file.pMethods element -** to a non-NULL pointer, then the sqlite3_io_methods.xClose method -** may be invoked even if the [sqlite3_vfs.xOpen] reported that it failed. The -** only way to prevent a call to xClose following a failed [sqlite3_vfs.xOpen] -** is for the [sqlite3_vfs.xOpen] to set the sqlite3_file.pMethods element -** to NULL. -** -** The flags argument to xSync may be one of [SQLITE_SYNC_NORMAL] or -** [SQLITE_SYNC_FULL]. The first choice is the normal fsync(). -** The second choice is a Mac OS X style fullsync. The [SQLITE_SYNC_DATAONLY] -** flag may be ORed in to indicate that only the data of the file -** and not its inode needs to be synced. -** -** The integer values to xLock() and xUnlock() are one of -**
      -**
    • [SQLITE_LOCK_NONE], -**
    • [SQLITE_LOCK_SHARED], -**
    • [SQLITE_LOCK_RESERVED], -**
    • [SQLITE_LOCK_PENDING], or -**
    • [SQLITE_LOCK_EXCLUSIVE]. -**
    -** xLock() increases the lock. xUnlock() decreases the lock. -** 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. -** -** The xFileControl() method is a generic interface that allows custom -** VFS implementations to directly control an open file using the -** [sqlite3_file_control()] interface. The second "op" argument is an -** integer opcode. The third argument is a generic pointer intended to -** point to a structure that may contain arguments or space in which to -** write return values. Potential uses for xFileControl() might be -** functions to enable blocking locks with timeouts, to change the -** locking strategy (for example to use dot-file locks), to inquire -** about the status of a lock, or to break stale locks. The SQLite -** core reserves all opcodes less than 100 for its own use. -** A [file control opcodes | list of opcodes] less than 100 is available. -** Applications that define a custom xFileControl method should use opcodes -** greater than 100 to avoid conflicts. VFS implementations should -** return [SQLITE_NOTFOUND] for file control opcodes that they do not -** recognize. -** -** The xSectorSize() method returns the sector size of the -** device that underlies the file. The sector size is the -** minimum write that can be performed without disturbing -** other bytes in the file. The xDeviceCharacteristics() -** method returns a bit vector describing behaviors of the -** underlying device: -** -**
      -**
    • [SQLITE_IOCAP_ATOMIC] -**
    • [SQLITE_IOCAP_ATOMIC512] -**
    • [SQLITE_IOCAP_ATOMIC1K] -**
    • [SQLITE_IOCAP_ATOMIC2K] -**
    • [SQLITE_IOCAP_ATOMIC4K] -**
    • [SQLITE_IOCAP_ATOMIC8K] -**
    • [SQLITE_IOCAP_ATOMIC16K] -**
    • [SQLITE_IOCAP_ATOMIC32K] -**
    • [SQLITE_IOCAP_ATOMIC64K] -**
    • [SQLITE_IOCAP_SAFE_APPEND] -**
    • [SQLITE_IOCAP_SEQUENTIAL] -**
    • [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN] -**
    • [SQLITE_IOCAP_POWERSAFE_OVERWRITE] -**
    • [SQLITE_IOCAP_IMMUTABLE] -**
    • [SQLITE_IOCAP_BATCH_ATOMIC] -**
    -** -** The SQLITE_IOCAP_ATOMIC property means that all writes of -** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values -** mean that writes of blocks that are nnn bytes in size and -** are aligned to an address which is an integer multiple of -** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means -** that when data is appended to a file, the data is appended -** first then the size of the file is extended, never the other -** way around. The SQLITE_IOCAP_SEQUENTIAL property means that -** information is written to disk in the same order as calls -** to xWrite(). -** -** If xRead() returns SQLITE_IOERR_SHORT_READ it must also fill -** in the unread portions of the buffer with zeros. A VFS that -** fails to zero-fill short reads might seem to work. However, -** failure to zero-fill short reads will eventually lead to -** database corruption. -*/ -typedef struct sqlite3_io_methods sqlite3_io_methods; -struct sqlite3_io_methods { - int iVersion; - int (*xClose)(sqlite3_file*); - int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); - int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst); - int (*xTruncate)(sqlite3_file*, sqlite3_int64 size); - int (*xSync)(sqlite3_file*, int flags); - int (*xFileSize)(sqlite3_file*, sqlite3_int64 *pSize); - int (*xLock)(sqlite3_file*, int); - int (*xUnlock)(sqlite3_file*, int); - int (*xCheckReservedLock)(sqlite3_file*, int *pResOut); - int (*xFileControl)(sqlite3_file*, int op, void *pArg); - int (*xSectorSize)(sqlite3_file*); - int (*xDeviceCharacteristics)(sqlite3_file*); - /* Methods above are valid for version 1 */ - int (*xShmMap)(sqlite3_file*, int iPg, int pgsz, int, void volatile**); - int (*xShmLock)(sqlite3_file*, int offset, int n, int flags); - void (*xShmBarrier)(sqlite3_file*); - int (*xShmUnmap)(sqlite3_file*, int deleteFlag); - /* Methods above are valid for version 2 */ - int (*xFetch)(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp); - int (*xUnfetch)(sqlite3_file*, sqlite3_int64 iOfst, void *p); - /* Methods above are valid for version 3 */ - /* Additional methods may be added in future releases */ -}; +#define SQLITE_SYNC_NORMAL 0x00002 +#define SQLITE_SYNC_FULL 0x00003 +#define SQLITE_SYNC_DATAONLY 0x00010 + + /* + ** CAPI3REF: OS Interface Open File Handle + ** + ** An [sqlite3_file] object represents an open file in the + ** [sqlite3_vfs | OS interface layer]. Individual OS interface + ** implementations will + ** want to subclass this object by appending additional fields + ** for their own use. The pMethods entry is a pointer to an + ** [sqlite3_io_methods] object that defines methods for performing + ** I/O operations on the open file. + */ + typedef struct sqlite3_file sqlite3_file; + struct sqlite3_file + { + const struct sqlite3_io_methods* pMethods; /* Methods for an open file */ + }; + + /* + ** CAPI3REF: OS Interface File Virtual Methods Object + ** + ** Every file opened by the [sqlite3_vfs.xOpen] method populates an + ** [sqlite3_file] object (or, more commonly, a subclass of the + ** [sqlite3_file] object) with a pointer to an instance of this object. + ** This object defines the methods used to perform various operations + ** against the open file represented by the [sqlite3_file] object. + ** + ** If the [sqlite3_vfs.xOpen] method sets the sqlite3_file.pMethods element + ** to a non-NULL pointer, then the sqlite3_io_methods.xClose method + ** may be invoked even if the [sqlite3_vfs.xOpen] reported that it failed. The + ** only way to prevent a call to xClose following a failed [sqlite3_vfs.xOpen] + ** is for the [sqlite3_vfs.xOpen] to set the sqlite3_file.pMethods element + ** to NULL. + ** + ** The flags argument to xSync may be one of [SQLITE_SYNC_NORMAL] or + ** [SQLITE_SYNC_FULL]. The first choice is the normal fsync(). + ** The second choice is a Mac OS X style fullsync. The [SQLITE_SYNC_DATAONLY] + ** flag may be ORed in to indicate that only the data of the file + ** and not its inode needs to be synced. + ** + ** The integer values to xLock() and xUnlock() are one of + **
      + **
    • [SQLITE_LOCK_NONE], + **
    • [SQLITE_LOCK_SHARED], + **
    • [SQLITE_LOCK_RESERVED], + **
    • [SQLITE_LOCK_PENDING], or + **
    • [SQLITE_LOCK_EXCLUSIVE]. + **
    + ** xLock() increases the lock. xUnlock() decreases the lock. + ** 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. + ** + ** The xFileControl() method is a generic interface that allows custom + ** VFS implementations to directly control an open file using the + ** [sqlite3_file_control()] interface. The second "op" argument is an + ** integer opcode. The third argument is a generic pointer intended to + ** point to a structure that may contain arguments or space in which to + ** write return values. Potential uses for xFileControl() might be + ** functions to enable blocking locks with timeouts, to change the + ** locking strategy (for example to use dot-file locks), to inquire + ** about the status of a lock, or to break stale locks. The SQLite + ** core reserves all opcodes less than 100 for its own use. + ** A [file control opcodes | list of opcodes] less than 100 is available. + ** Applications that define a custom xFileControl method should use opcodes + ** greater than 100 to avoid conflicts. VFS implementations should + ** return [SQLITE_NOTFOUND] for file control opcodes that they do not + ** recognize. + ** + ** The xSectorSize() method returns the sector size of the + ** device that underlies the file. The sector size is the + ** minimum write that can be performed without disturbing + ** other bytes in the file. The xDeviceCharacteristics() + ** method returns a bit vector describing behaviors of the + ** underlying device: + ** + **
      + **
    • [SQLITE_IOCAP_ATOMIC] + **
    • [SQLITE_IOCAP_ATOMIC512] + **
    • [SQLITE_IOCAP_ATOMIC1K] + **
    • [SQLITE_IOCAP_ATOMIC2K] + **
    • [SQLITE_IOCAP_ATOMIC4K] + **
    • [SQLITE_IOCAP_ATOMIC8K] + **
    • [SQLITE_IOCAP_ATOMIC16K] + **
    • [SQLITE_IOCAP_ATOMIC32K] + **
    • [SQLITE_IOCAP_ATOMIC64K] + **
    • [SQLITE_IOCAP_SAFE_APPEND] + **
    • [SQLITE_IOCAP_SEQUENTIAL] + **
    • [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN] + **
    • [SQLITE_IOCAP_POWERSAFE_OVERWRITE] + **
    • [SQLITE_IOCAP_IMMUTABLE] + **
    • [SQLITE_IOCAP_BATCH_ATOMIC] + **
    + ** + ** The SQLITE_IOCAP_ATOMIC property means that all writes of + ** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values + ** mean that writes of blocks that are nnn bytes in size and + ** are aligned to an address which is an integer multiple of + ** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means + ** that when data is appended to a file, the data is appended + ** first then the size of the file is extended, never the other + ** way around. The SQLITE_IOCAP_SEQUENTIAL property means that + ** information is written to disk in the same order as calls + ** to xWrite(). + ** + ** If xRead() returns SQLITE_IOERR_SHORT_READ it must also fill + ** in the unread portions of the buffer with zeros. A VFS that + ** fails to zero-fill short reads might seem to work. However, + ** failure to zero-fill short reads will eventually lead to + ** database corruption. + */ + typedef struct sqlite3_io_methods sqlite3_io_methods; + struct sqlite3_io_methods + { + int iVersion; + int (*xClose)(sqlite3_file*); + int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); + int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst); + int (*xTruncate)(sqlite3_file*, sqlite3_int64 size); + int (*xSync)(sqlite3_file*, int flags); + int (*xFileSize)(sqlite3_file*, sqlite3_int64* pSize); + int (*xLock)(sqlite3_file*, int); + int (*xUnlock)(sqlite3_file*, int); + int (*xCheckReservedLock)(sqlite3_file*, int* pResOut); + int (*xFileControl)(sqlite3_file*, int op, void* pArg); + int (*xSectorSize)(sqlite3_file*); + int (*xDeviceCharacteristics)(sqlite3_file*); + /* Methods above are valid for version 1 */ + int (*xShmMap)(sqlite3_file*, int iPg, int pgsz, int, void volatile**); + int (*xShmLock)(sqlite3_file*, int offset, int n, int flags); + void (*xShmBarrier)(sqlite3_file*); + int (*xShmUnmap)(sqlite3_file*, int deleteFlag); + /* Methods above are valid for version 2 */ + int (*xFetch)(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void** pp); + int (*xUnfetch)(sqlite3_file*, sqlite3_int64 iOfst, void* p); + /* Methods above are valid for version 3 */ + /* Additional methods may be added in future releases */ + }; /* ** CAPI3REF: Standard File Control Opcodes @@ -1127,283 +1166,301 @@ ** file to the database file, but before the *-shm file is updated to ** record the fact that the pages have been checkpointed. ** -*/ -#define SQLITE_FCNTL_LOCKSTATE 1 -#define SQLITE_FCNTL_GET_LOCKPROXYFILE 2 -#define SQLITE_FCNTL_SET_LOCKPROXYFILE 3 -#define SQLITE_FCNTL_LAST_ERRNO 4 -#define SQLITE_FCNTL_SIZE_HINT 5 -#define SQLITE_FCNTL_CHUNK_SIZE 6 -#define SQLITE_FCNTL_FILE_POINTER 7 -#define SQLITE_FCNTL_SYNC_OMITTED 8 -#define SQLITE_FCNTL_WIN32_AV_RETRY 9 -#define SQLITE_FCNTL_PERSIST_WAL 10 -#define SQLITE_FCNTL_OVERWRITE 11 -#define SQLITE_FCNTL_VFSNAME 12 -#define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13 -#define SQLITE_FCNTL_PRAGMA 14 -#define SQLITE_FCNTL_BUSYHANDLER 15 -#define SQLITE_FCNTL_TEMPFILENAME 16 -#define SQLITE_FCNTL_MMAP_SIZE 18 -#define SQLITE_FCNTL_TRACE 19 -#define SQLITE_FCNTL_HAS_MOVED 20 -#define SQLITE_FCNTL_SYNC 21 -#define SQLITE_FCNTL_COMMIT_PHASETWO 22 -#define SQLITE_FCNTL_WIN32_SET_HANDLE 23 -#define SQLITE_FCNTL_WAL_BLOCK 24 -#define SQLITE_FCNTL_ZIPVFS 25 -#define SQLITE_FCNTL_RBU 26 -#define SQLITE_FCNTL_VFS_POINTER 27 -#define SQLITE_FCNTL_JOURNAL_POINTER 28 -#define SQLITE_FCNTL_WIN32_GET_HANDLE 29 -#define SQLITE_FCNTL_PDB 30 -#define SQLITE_FCNTL_BEGIN_ATOMIC_WRITE 31 -#define SQLITE_FCNTL_COMMIT_ATOMIC_WRITE 32 -#define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 -#define SQLITE_FCNTL_LOCK_TIMEOUT 34 -#define SQLITE_FCNTL_DATA_VERSION 35 -#define SQLITE_FCNTL_SIZE_LIMIT 36 -#define SQLITE_FCNTL_CKPT_DONE 37 -#define SQLITE_FCNTL_RESERVE_BYTES 38 -#define SQLITE_FCNTL_CKPT_START 39 - -/* deprecated names */ -#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE -#define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE -#define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO - - -/* -** CAPI3REF: Mutex Handle -** -** The mutex module within SQLite defines [sqlite3_mutex] to be an -** abstract type for a mutex object. The SQLite core never looks -** at the internal representation of an [sqlite3_mutex]. It only -** deals with pointers to the [sqlite3_mutex] object. -** -** Mutexes are created using [sqlite3_mutex_alloc()]. -*/ -typedef struct sqlite3_mutex sqlite3_mutex; - -/* -** CAPI3REF: Loadable Extension Thunk -** -** A pointer to the opaque sqlite3_api_routines structure is passed as -** the third parameter to entry points of [loadable extensions]. This -** structure must be typedefed in order to work around compiler warnings -** on some platforms. -*/ -typedef struct sqlite3_api_routines sqlite3_api_routines; - -/* -** CAPI3REF: OS Interface Object -** -** An instance of the sqlite3_vfs object defines the interface between -** the SQLite core and the underlying operating system. The "vfs" -** in the name of the object stands for "virtual file system". See -** the [VFS | VFS documentation] for further information. -** -** The VFS interface is sometimes extended by adding new methods onto -** the end. Each time such an extension occurs, the iVersion field -** is incremented. The iVersion value started out as 1 in -** SQLite [version 3.5.0] on [dateof:3.5.0], then increased to 2 -** with SQLite [version 3.7.0] on [dateof:3.7.0], and then increased -** to 3 with SQLite [version 3.7.6] on [dateof:3.7.6]. Additional fields -** may be appended to the sqlite3_vfs object and the iVersion value -** may increase again in future versions of SQLite. -** Note that due to an oversight, the structure -** of the sqlite3_vfs object changed in the transition from -** SQLite [version 3.5.9] to [version 3.6.0] on [dateof:3.6.0] -** and yet the iVersion field was not increased. -** -** The szOsFile field is the size of the subclassed [sqlite3_file] -** structure used by this VFS. mxPathname is the maximum length of -** a pathname in this VFS. -** -** Registered sqlite3_vfs objects are kept on a linked list formed by -** the pNext pointer. The [sqlite3_vfs_register()] -** and [sqlite3_vfs_unregister()] interfaces manage this list -** in a thread-safe way. The [sqlite3_vfs_find()] interface -** searches the list. Neither the application code nor the VFS -** implementation should use the pNext pointer. -** -** The pNext field is the only field in the sqlite3_vfs -** structure that SQLite will ever modify. SQLite will only access -** or modify this field while holding a particular static mutex. -** The application should never modify anything within the sqlite3_vfs -** object once the object has been registered. -** -** The zName field holds the name of the VFS module. The name must -** be unique across all VFS modules. -** -** [[sqlite3_vfs.xOpen]] -** ^SQLite guarantees that the zFilename parameter to xOpen -** is either a NULL pointer or string obtained -** from xFullPathname() with an optional suffix added. -** ^If a suffix is added to the zFilename parameter, it will -** consist of a single "-" character followed by no more than -** 11 alphanumeric and/or "-" characters. -** ^SQLite further guarantees that -** the string will be valid and unchanged until xClose() is -** called. Because of the previous sentence, -** the [sqlite3_file] can safely store a pointer to the -** filename if it needs to remember the filename for some reason. -** If the zFilename parameter to xOpen is a NULL pointer then xOpen -** must invent its own temporary name for the file. ^Whenever the -** xFilename parameter is NULL it will also be the case that the -** flags parameter will include [SQLITE_OPEN_DELETEONCLOSE]. -** -** The flags argument to xOpen() includes all bits set in -** the flags argument to [sqlite3_open_v2()]. Or if [sqlite3_open()] -** or [sqlite3_open16()] is used, then flags includes at least -** [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]. -** If xOpen() opens a file read-only then it sets *pOutFlags to -** include [SQLITE_OPEN_READONLY]. Other bits in *pOutFlags may be set. ** -** ^(SQLite will also add one of the following flags to the xOpen() -** call, depending on the object being opened: -** -**
      -**
    • [SQLITE_OPEN_MAIN_DB] -**
    • [SQLITE_OPEN_MAIN_JOURNAL] -**
    • [SQLITE_OPEN_TEMP_DB] -**
    • [SQLITE_OPEN_TEMP_JOURNAL] -**
    • [SQLITE_OPEN_TRANSIENT_DB] -**
    • [SQLITE_OPEN_SUBJOURNAL] -**
    • [SQLITE_OPEN_SUPER_JOURNAL] -**
    • [SQLITE_OPEN_WAL] -**
    )^ -** -** The file I/O implementation can use the object type flags to -** change the way it deals with files. For example, an application -** that does not care about crash recovery or rollback might make -** the open of a journal file a no-op. Writes to this journal would -** also be no-ops, and any attempt to read the journal would return -** SQLITE_IOERR. Or the implementation might recognize that a database -** file will be doing page-aligned sector reads and writes in a random -** order and set up its I/O subsystem accordingly. -** -** SQLite might also add one of the following flags to the xOpen method: -** -**
      -**
    • [SQLITE_OPEN_DELETEONCLOSE] -**
    • [SQLITE_OPEN_EXCLUSIVE] +**
    • [[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. **
    ** -** The [SQLITE_OPEN_DELETEONCLOSE] flag means the file should be -** deleted when it is closed. ^The [SQLITE_OPEN_DELETEONCLOSE] -** will be set for TEMP databases and their journals, transient -** databases, and subjournals. -** -** ^The [SQLITE_OPEN_EXCLUSIVE] flag is always used in conjunction -** with the [SQLITE_OPEN_CREATE] flag, which are both directly -** analogous to the O_EXCL and O_CREAT flags of the POSIX open() -** API. The SQLITE_OPEN_EXCLUSIVE flag, when paired with the -** SQLITE_OPEN_CREATE, is used to indicate that file should always -** be created, and that it is an error if it already exists. -** It is not used to indicate the file should be opened -** for exclusive access. -** -** ^At least szOsFile bytes of memory are allocated by SQLite -** to hold the [sqlite3_file] structure passed as the third -** argument to xOpen. The xOpen method does not have to -** allocate the structure; it should just fill it in. Note that -** the xOpen method must set the sqlite3_file.pMethods to either -** a valid [sqlite3_io_methods] object or to NULL. xOpen must do -** this even if the open fails. SQLite expects that the sqlite3_file.pMethods -** element will be valid after xOpen returns regardless of the success -** or failure of the xOpen call. -** -** [[sqlite3_vfs.xAccess]] -** ^The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS] -** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to -** test whether a file is readable and writable, or [SQLITE_ACCESS_READ] -** to test whether a file is at least readable. The SQLITE_ACCESS_READ -** flag is never actually used and is not implemented in the built-in -** VFSes of SQLite. The file is named by the second argument and can be a -** directory. The xAccess method returns [SQLITE_OK] on success or some -** non-zero error code if there is an I/O error or if the name of -** the file given in the second argument is illegal. If SQLITE_OK -** is returned, then non-zero or zero is written into *pResOut to indicate -** whether or not the file is accessible. -** -** ^SQLite will always allocate at least mxPathname+1 bytes for the -** output buffer xFullPathname. The exact size of the output buffer -** is also passed as a parameter to both methods. If the output buffer -** is not large enough, [SQLITE_CANTOPEN] should be returned. Since this is -** handled as a fatal error by SQLite, vfs implementations should endeavor -** to prevent this by setting mxPathname to a sufficiently large value. -** -** The xRandomness(), xSleep(), xCurrentTime(), and xCurrentTimeInt64() -** interfaces are not strictly a part of the filesystem, but they are -** included in the VFS structure for completeness. -** The xRandomness() function attempts to return nBytes bytes -** of good-quality randomness into zOut. The return value is -** the actual number of bytes of randomness obtained. -** The xSleep() method causes the calling thread to sleep for at -** least the number of microseconds given. ^The xCurrentTime() -** method returns a Julian Day Number for the current date and time as -** a floating point value. -** ^The xCurrentTimeInt64() method returns, as an integer, the Julian -** Day Number multiplied by 86400000 (the number of milliseconds in -** a 24-hour day). -** ^SQLite will use the xCurrentTimeInt64() method to get the current -** date and time if that method is available (if iVersion is 2 or -** greater and the function pointer is not NULL) and will fall back -** to xCurrentTime() if xCurrentTimeInt64() is unavailable. -** -** ^The xSetSystemCall(), xGetSystemCall(), and xNestSystemCall() interfaces -** are not used by the SQLite core. These optional interfaces are provided -** by some VFSes to facilitate testing of the VFS code. By overriding -** system calls with functions under its control, a test program can -** simulate faults and error conditions that would otherwise be difficult -** or impossible to induce. The set of system calls that can be overridden -** varies from one VFS to another, and from one version of the same VFS to the -** next. Applications that use these interfaces must be prepared for any -** or all of these interfaces to be NULL or for their behavior to change -** from one release to the next. Applications must not attempt to access -** any of these methods if the iVersion of the VFS is less than 3. -*/ -typedef struct sqlite3_vfs sqlite3_vfs; -typedef void (*sqlite3_syscall_ptr)(void); -struct sqlite3_vfs { - int iVersion; /* Structure version number (currently 3) */ - int szOsFile; /* Size of subclassed sqlite3_file */ - int mxPathname; /* Maximum file pathname length */ - 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 flags, int *pOutFlags); - int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir); - int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut); - int (*xFullPathname)(sqlite3_vfs*, const char *zName, int nOut, char *zOut); - void *(*xDlOpen)(sqlite3_vfs*, const char *zFilename); - void (*xDlError)(sqlite3_vfs*, int nByte, char *zErrMsg); - void (*(*xDlSym)(sqlite3_vfs*,void*, const char *zSymbol))(void); - void (*xDlClose)(sqlite3_vfs*, void*); - int (*xRandomness)(sqlite3_vfs*, int nByte, char *zOut); - int (*xSleep)(sqlite3_vfs*, int microseconds); - int (*xCurrentTime)(sqlite3_vfs*, double*); - int (*xGetLastError)(sqlite3_vfs*, int, char *); - /* - ** The methods above are in version 1 of the sqlite_vfs object - ** definition. Those that follow are added in version 2 or later - */ - int (*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*); - /* - ** The methods above are in versions 1 and 2 of the sqlite_vfs object. - ** Those below are for version 3 and greater. - */ - int (*xSetSystemCall)(sqlite3_vfs*, const char *zName, sqlite3_syscall_ptr); - sqlite3_syscall_ptr (*xGetSystemCall)(sqlite3_vfs*, const char *zName); - const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName); - /* - ** The methods above are in versions 1 through 3 of the sqlite_vfs object. - ** New fields may be appended in future versions. The iVersion - ** value will increment whenever this happens. - */ -}; +**
  • [[SQLITE_FCNTL_CKSM_FILE]] +** Used by the cksmvfs VFS module only. +** +*/ +#define SQLITE_FCNTL_LOCKSTATE 1 +#define SQLITE_FCNTL_GET_LOCKPROXYFILE 2 +#define SQLITE_FCNTL_SET_LOCKPROXYFILE 3 +#define SQLITE_FCNTL_LAST_ERRNO 4 +#define SQLITE_FCNTL_SIZE_HINT 5 +#define SQLITE_FCNTL_CHUNK_SIZE 6 +#define SQLITE_FCNTL_FILE_POINTER 7 +#define SQLITE_FCNTL_SYNC_OMITTED 8 +#define SQLITE_FCNTL_WIN32_AV_RETRY 9 +#define SQLITE_FCNTL_PERSIST_WAL 10 +#define SQLITE_FCNTL_OVERWRITE 11 +#define SQLITE_FCNTL_VFSNAME 12 +#define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13 +#define SQLITE_FCNTL_PRAGMA 14 +#define SQLITE_FCNTL_BUSYHANDLER 15 +#define SQLITE_FCNTL_TEMPFILENAME 16 +#define SQLITE_FCNTL_MMAP_SIZE 18 +#define SQLITE_FCNTL_TRACE 19 +#define SQLITE_FCNTL_HAS_MOVED 20 +#define SQLITE_FCNTL_SYNC 21 +#define SQLITE_FCNTL_COMMIT_PHASETWO 22 +#define SQLITE_FCNTL_WIN32_SET_HANDLE 23 +#define SQLITE_FCNTL_WAL_BLOCK 24 +#define SQLITE_FCNTL_ZIPVFS 25 +#define SQLITE_FCNTL_RBU 26 +#define SQLITE_FCNTL_VFS_POINTER 27 +#define SQLITE_FCNTL_JOURNAL_POINTER 28 +#define SQLITE_FCNTL_WIN32_GET_HANDLE 29 +#define SQLITE_FCNTL_PDB 30 +#define SQLITE_FCNTL_BEGIN_ATOMIC_WRITE 31 +#define SQLITE_FCNTL_COMMIT_ATOMIC_WRITE 32 +#define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 +#define SQLITE_FCNTL_LOCK_TIMEOUT 34 +#define SQLITE_FCNTL_DATA_VERSION 35 +#define SQLITE_FCNTL_SIZE_LIMIT 36 +#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 + +/* deprecated names */ +#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE +#define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE +#define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO + + /* + ** CAPI3REF: Mutex Handle + ** + ** The mutex module within SQLite defines [sqlite3_mutex] to be an + ** abstract type for a mutex object. The SQLite core never looks + ** at the internal representation of an [sqlite3_mutex]. It only + ** deals with pointers to the [sqlite3_mutex] object. + ** + ** Mutexes are created using [sqlite3_mutex_alloc()]. + */ + typedef struct sqlite3_mutex sqlite3_mutex; + + /* + ** CAPI3REF: Loadable Extension Thunk + ** + ** A pointer to the opaque sqlite3_api_routines structure is passed as + ** the third parameter to entry points of [loadable extensions]. This + ** structure must be typedefed in order to work around compiler warnings + ** on some platforms. + */ + typedef struct sqlite3_api_routines sqlite3_api_routines; + + /* + ** CAPI3REF: OS Interface Object + ** + ** An instance of the sqlite3_vfs object defines the interface between + ** the SQLite core and the underlying operating system. The "vfs" + ** in the name of the object stands for "virtual file system". See + ** the [VFS | VFS documentation] for further information. + ** + ** The VFS interface is sometimes extended by adding new methods onto + ** the end. Each time such an extension occurs, the iVersion field + ** is incremented. The iVersion value started out as 1 in + ** SQLite [version 3.5.0] on [dateof:3.5.0], then increased to 2 + ** with SQLite [version 3.7.0] on [dateof:3.7.0], and then increased + ** to 3 with SQLite [version 3.7.6] on [dateof:3.7.6]. Additional fields + ** may be appended to the sqlite3_vfs object and the iVersion value + ** may increase again in future versions of SQLite. + ** Note that due to an oversight, the structure + ** of the sqlite3_vfs object changed in the transition from + ** SQLite [version 3.5.9] to [version 3.6.0] on [dateof:3.6.0] + ** and yet the iVersion field was not increased. + ** + ** The szOsFile field is the size of the subclassed [sqlite3_file] + ** structure used by this VFS. mxPathname is the maximum length of + ** a pathname in this VFS. + ** + ** Registered sqlite3_vfs objects are kept on a linked list formed by + ** the pNext pointer. The [sqlite3_vfs_register()] + ** and [sqlite3_vfs_unregister()] interfaces manage this list + ** in a thread-safe way. The [sqlite3_vfs_find()] interface + ** searches the list. Neither the application code nor the VFS + ** implementation should use the pNext pointer. + ** + ** The pNext field is the only field in the sqlite3_vfs + ** structure that SQLite will ever modify. SQLite will only access + ** or modify this field while holding a particular static mutex. + ** The application should never modify anything within the sqlite3_vfs + ** object once the object has been registered. + ** + ** The zName field holds the name of the VFS module. The name must + ** be unique across all VFS modules. + ** + ** [[sqlite3_vfs.xOpen]] + ** ^SQLite guarantees that the zFilename parameter to xOpen + ** is either a NULL pointer or string obtained + ** from xFullPathname() with an optional suffix added. + ** ^If a suffix is added to the zFilename parameter, it will + ** consist of a single "-" character followed by no more than + ** 11 alphanumeric and/or "-" characters. + ** ^SQLite further guarantees that + ** the string will be valid and unchanged until xClose() is + ** called. Because of the previous sentence, + ** the [sqlite3_file] can safely store a pointer to the + ** filename if it needs to remember the filename for some reason. + ** If the zFilename parameter to xOpen is a NULL pointer then xOpen + ** must invent its own temporary name for the file. ^Whenever the + ** xFilename parameter is NULL it will also be the case that the + ** flags parameter will include [SQLITE_OPEN_DELETEONCLOSE]. + ** + ** The flags argument to xOpen() includes all bits set in + ** the flags argument to [sqlite3_open_v2()]. Or if [sqlite3_open()] + ** or [sqlite3_open16()] is used, then flags includes at least + ** [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]. + ** If xOpen() opens a file read-only then it sets *pOutFlags to + ** include [SQLITE_OPEN_READONLY]. Other bits in *pOutFlags may be set. + ** + ** ^(SQLite will also add one of the following flags to the xOpen() + ** call, depending on the object being opened: + ** + **
      + **
    • [SQLITE_OPEN_MAIN_DB] + **
    • [SQLITE_OPEN_MAIN_JOURNAL] + **
    • [SQLITE_OPEN_TEMP_DB] + **
    • [SQLITE_OPEN_TEMP_JOURNAL] + **
    • [SQLITE_OPEN_TRANSIENT_DB] + **
    • [SQLITE_OPEN_SUBJOURNAL] + **
    • [SQLITE_OPEN_SUPER_JOURNAL] + **
    • [SQLITE_OPEN_WAL] + **
    )^ + ** + ** The file I/O implementation can use the object type flags to + ** change the way it deals with files. For example, an application + ** that does not care about crash recovery or rollback might make + ** the open of a journal file a no-op. Writes to this journal would + ** also be no-ops, and any attempt to read the journal would return + ** SQLITE_IOERR. Or the implementation might recognize that a database + ** file will be doing page-aligned sector reads and writes in a random + ** order and set up its I/O subsystem accordingly. + ** + ** SQLite might also add one of the following flags to the xOpen method: + ** + **
      + **
    • [SQLITE_OPEN_DELETEONCLOSE] + **
    • [SQLITE_OPEN_EXCLUSIVE] + **
    + ** + ** The [SQLITE_OPEN_DELETEONCLOSE] flag means the file should be + ** deleted when it is closed. ^The [SQLITE_OPEN_DELETEONCLOSE] + ** will be set for TEMP databases and their journals, transient + ** databases, and subjournals. + ** + ** ^The [SQLITE_OPEN_EXCLUSIVE] flag is always used in conjunction + ** with the [SQLITE_OPEN_CREATE] flag, which are both directly + ** analogous to the O_EXCL and O_CREAT flags of the POSIX open() + ** API. The SQLITE_OPEN_EXCLUSIVE flag, when paired with the + ** SQLITE_OPEN_CREATE, is used to indicate that file should always + ** be created, and that it is an error if it already exists. + ** It is not used to indicate the file should be opened + ** for exclusive access. + ** + ** ^At least szOsFile bytes of memory are allocated by SQLite + ** to hold the [sqlite3_file] structure passed as the third + ** argument to xOpen. The xOpen method does not have to + ** allocate the structure; it should just fill it in. Note that + ** the xOpen method must set the sqlite3_file.pMethods to either + ** a valid [sqlite3_io_methods] object or to NULL. xOpen must do + ** this even if the open fails. SQLite expects that the sqlite3_file.pMethods + ** element will be valid after xOpen returns regardless of the success + ** or failure of the xOpen call. + ** + ** [[sqlite3_vfs.xAccess]] + ** ^The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS] + ** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to + ** test whether a file is readable and writable, or [SQLITE_ACCESS_READ] + ** to test whether a file is at least readable. The SQLITE_ACCESS_READ + ** flag is never actually used and is not implemented in the built-in + ** VFSes of SQLite. The file is named by the second argument and can be a + ** directory. The xAccess method returns [SQLITE_OK] on success or some + ** non-zero error code if there is an I/O error or if the name of + ** the file given in the second argument is illegal. If SQLITE_OK + ** is returned, then non-zero or zero is written into *pResOut to indicate + ** whether or not the file is accessible. + ** + ** ^SQLite will always allocate at least mxPathname+1 bytes for the + ** output buffer xFullPathname. The exact size of the output buffer + ** is also passed as a parameter to both methods. If the output buffer + ** is not large enough, [SQLITE_CANTOPEN] should be returned. Since this is + ** handled as a fatal error by SQLite, vfs implementations should endeavor + ** to prevent this by setting mxPathname to a sufficiently large value. + ** + ** The xRandomness(), xSleep(), xCurrentTime(), and xCurrentTimeInt64() + ** interfaces are not strictly a part of the filesystem, but they are + ** included in the VFS structure for completeness. + ** The xRandomness() function attempts to return nBytes bytes + ** of good-quality randomness into zOut. The return value is + ** the actual number of bytes of randomness obtained. + ** The xSleep() method causes the calling thread to sleep for at + ** least the number of microseconds given. ^The xCurrentTime() + ** method returns a Julian Day Number for the current date and time as + ** a floating point value. + ** ^The xCurrentTimeInt64() method returns, as an integer, the Julian + ** Day Number multiplied by 86400000 (the number of milliseconds in + ** a 24-hour day). + ** ^SQLite will use the xCurrentTimeInt64() method to get the current + ** date and time if that method is available (if iVersion is 2 or + ** greater and the function pointer is not NULL) and will fall back + ** to xCurrentTime() if xCurrentTimeInt64() is unavailable. + ** + ** ^The xSetSystemCall(), xGetSystemCall(), and xNestSystemCall() interfaces + ** are not used by the SQLite core. These optional interfaces are provided + ** by some VFSes to facilitate testing of the VFS code. By overriding + ** system calls with functions under its control, a test program can + ** simulate faults and error conditions that would otherwise be difficult + ** or impossible to induce. The set of system calls that can be overridden + ** varies from one VFS to another, and from one version of the same VFS to the + ** next. Applications that use these interfaces must be prepared for any + ** or all of these interfaces to be NULL or for their behavior to change + ** from one release to the next. Applications must not attempt to access + ** any of these methods if the iVersion of the VFS is less than 3. + */ + typedef struct sqlite3_vfs sqlite3_vfs; + typedef void (*sqlite3_syscall_ptr)(void); + struct sqlite3_vfs + { + int iVersion; /* Structure version number (currently 3) */ + int szOsFile; /* Size of subclassed sqlite3_file */ + int mxPathname; /* Maximum file pathname length */ + 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 flags, int* pOutFlags); + int (*xDelete)(sqlite3_vfs*, const char* zName, int syncDir); + int (*xAccess)(sqlite3_vfs*, const char* zName, int flags, int* pResOut); + int (*xFullPathname)(sqlite3_vfs*, const char* zName, int nOut, char* zOut); + void* (*xDlOpen)(sqlite3_vfs*, const char* zFilename); + void (*xDlError)(sqlite3_vfs*, int nByte, char* zErrMsg); + void (*(*xDlSym)(sqlite3_vfs*, void*, const char* zSymbol))(void); + void (*xDlClose)(sqlite3_vfs*, void*); + int (*xRandomness)(sqlite3_vfs*, int nByte, char* zOut); + int (*xSleep)(sqlite3_vfs*, int microseconds); + int (*xCurrentTime)(sqlite3_vfs*, double*); + int (*xGetLastError)(sqlite3_vfs*, int, char*); + /* + ** The methods above are in version 1 of the sqlite_vfs object + ** definition. Those that follow are added in version 2 or later + */ + int (*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*); + /* + ** The methods above are in versions 1 and 2 of the sqlite_vfs object. + ** Those below are for version 3 and greater. + */ + int (*xSetSystemCall)(sqlite3_vfs*, const char* zName, sqlite3_syscall_ptr); + sqlite3_syscall_ptr (*xGetSystemCall)(sqlite3_vfs*, const char* zName); + const char* (*xNextSystemCall)(sqlite3_vfs*, const char* zName); + /* + ** The methods above are in versions 1 through 3 of the sqlite_vfs object. + ** New fields may be appended in future versions. The iVersion + ** value will increment whenever this happens. + */ + }; /* ** CAPI3REF: Flags for the xAccess VFS method @@ -1425,9 +1482,9 @@ ** currently unused, though it might be used in a future release of ** SQLite. */ -#define SQLITE_ACCESS_EXISTS 0 -#define SQLITE_ACCESS_READWRITE 1 /* Used by PRAGMA temp_store_directory */ -#define SQLITE_ACCESS_READ 2 /* Unused */ +#define SQLITE_ACCESS_EXISTS 0 +#define SQLITE_ACCESS_READWRITE 1 /* Used by PRAGMA temp_store_directory */ +#define SQLITE_ACCESS_READ 2 /* Unused */ /* ** CAPI3REF: Flags for the xShmLock VFS method @@ -1451,10 +1508,10 @@ ** between unlocked and EXCLUSIVE. It cannot transition between SHARED ** and EXCLUSIVE. */ -#define SQLITE_SHM_UNLOCK 1 -#define SQLITE_SHM_LOCK 2 -#define SQLITE_SHM_SHARED 4 -#define SQLITE_SHM_EXCLUSIVE 8 +#define SQLITE_SHM_UNLOCK 1 +#define SQLITE_SHM_LOCK 2 +#define SQLITE_SHM_SHARED 4 +#define SQLITE_SHM_EXCLUSIVE 8 /* ** CAPI3REF: Maximum xShmLock index @@ -1464,215 +1521,215 @@ ** The SQLite core will never attempt to acquire or release a ** lock outside of this range */ -#define SQLITE_SHM_NLOCK 8 - - -/* -** CAPI3REF: Initialize The SQLite Library -** -** ^The sqlite3_initialize() routine initializes the -** SQLite library. ^The sqlite3_shutdown() routine -** deallocates any resources that were allocated by sqlite3_initialize(). -** These routines are designed to aid in process initialization and -** shutdown on embedded systems. Workstation applications using -** SQLite normally do not need to invoke either of these routines. -** -** A call to sqlite3_initialize() is an "effective" call if it is -** the first time sqlite3_initialize() is invoked during the lifetime of -** the process, or if it is the first time sqlite3_initialize() is invoked -** following a call to sqlite3_shutdown(). ^(Only an effective call -** of sqlite3_initialize() does any initialization. All other calls -** are harmless no-ops.)^ -** -** A call to sqlite3_shutdown() is an "effective" call if it is the first -** call to sqlite3_shutdown() since the last sqlite3_initialize(). ^(Only -** an effective call to sqlite3_shutdown() does any deinitialization. -** All other valid calls to sqlite3_shutdown() are harmless no-ops.)^ -** -** The sqlite3_initialize() interface is threadsafe, but sqlite3_shutdown() -** is not. The sqlite3_shutdown() interface must only be called from a -** single thread. All open [database connections] must be closed and all -** other SQLite resources must be deallocated prior to invoking -** sqlite3_shutdown(). -** -** Among other things, ^sqlite3_initialize() will invoke -** sqlite3_os_init(). Similarly, ^sqlite3_shutdown() -** will invoke sqlite3_os_end(). -** -** ^The sqlite3_initialize() routine returns [SQLITE_OK] on success. -** ^If for some reason, sqlite3_initialize() is unable to initialize -** the library (perhaps it is unable to allocate a needed resource such -** as a mutex) it returns an [error code] other than [SQLITE_OK]. -** -** ^The sqlite3_initialize() routine is called internally by many other -** SQLite interfaces so that an application usually does not need to -** invoke sqlite3_initialize() directly. For example, [sqlite3_open()] -** calls sqlite3_initialize() so the SQLite library will be automatically -** initialized when [sqlite3_open()] is called if it has not be initialized -** already. ^However, if SQLite is compiled with the [SQLITE_OMIT_AUTOINIT] -** compile-time option, then the automatic calls to sqlite3_initialize() -** are omitted and the application must call sqlite3_initialize() directly -** prior to using any other SQLite interface. For maximum portability, -** it is recommended that applications always invoke sqlite3_initialize() -** directly prior to using any other SQLite interface. Future releases -** of SQLite may require this. In other words, the behavior exhibited -** when SQLite is compiled with [SQLITE_OMIT_AUTOINIT] might become the -** default behavior in some future release of SQLite. -** -** The sqlite3_os_init() routine does operating-system specific -** initialization of the SQLite library. The sqlite3_os_end() -** routine undoes the effect of sqlite3_os_init(). Typical tasks -** performed by these routines include allocation or deallocation -** of static resources, initialization of global variables, -** setting up a default [sqlite3_vfs] module, or setting up -** a default configuration using [sqlite3_config()]. -** -** The application should never invoke either sqlite3_os_init() -** or sqlite3_os_end() directly. The application should only invoke -** sqlite3_initialize() and sqlite3_shutdown(). The sqlite3_os_init() -** interface is called automatically by sqlite3_initialize() and -** sqlite3_os_end() is called by sqlite3_shutdown(). Appropriate -** implementations for sqlite3_os_init() and sqlite3_os_end() -** are built into SQLite when it is compiled for Unix, Windows, or OS/2. -** When [custom builds | built for other platforms] -** (using the [SQLITE_OS_OTHER=1] compile-time -** option) the application must supply a suitable implementation for -** sqlite3_os_init() and sqlite3_os_end(). An application-supplied -** implementation of sqlite3_os_init() or sqlite3_os_end() -** must return [SQLITE_OK] on success and some other [error code] upon -** failure. -*/ -SQLITE_API int sqlite3_initialize(void); -SQLITE_API int sqlite3_shutdown(void); -SQLITE_API int sqlite3_os_init(void); -SQLITE_API int sqlite3_os_end(void); - -/* -** CAPI3REF: Configuring The SQLite Library -** -** The sqlite3_config() interface is used to make global configuration -** changes to SQLite in order to tune SQLite to the specific needs of -** the application. The default configuration is recommended for most -** applications and so this routine is usually not necessary. It is -** provided to support rare applications with unusual needs. -** -** The sqlite3_config() interface is not threadsafe. The application -** 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. -** -** ^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]. -*/ -SQLITE_API int sqlite3_config(int, ...); +#define SQLITE_SHM_NLOCK 8 -/* -** CAPI3REF: Configure database connections -** METHOD: sqlite3 -** -** The sqlite3_db_config() interface is used to make configuration -** changes to a [database connection]. The interface is similar to -** [sqlite3_config()] except that the changes apply to a single -** [database connection] (specified in the first argument). -** -** The second argument to sqlite3_db_config(D,V,...) is the -** [SQLITE_DBCONFIG_LOOKASIDE | configuration verb] - an integer code -** that indicates what aspect of the [database connection] is being configured. -** Subsequent arguments vary depending on the configuration verb. -** -** ^Calls to sqlite3_db_config() return SQLITE_OK if and only if -** the call is considered successful. -*/ -SQLITE_API int sqlite3_db_config(sqlite3*, int op, ...); - -/* -** CAPI3REF: Memory Allocation Routines -** -** An instance of this object defines the interface between SQLite -** and low-level memory allocation routines. -** -** This object is used in only one place in the SQLite interface. -** A pointer to an instance of this object is the argument to -** [sqlite3_config()] when the configuration option is -** [SQLITE_CONFIG_MALLOC] or [SQLITE_CONFIG_GETMALLOC]. -** By creating an instance of this object -** and passing it to [sqlite3_config]([SQLITE_CONFIG_MALLOC]) -** during configuration, an application can specify an alternative -** memory allocation subsystem for SQLite to use for all of its -** dynamic memory needs. -** -** Note that SQLite comes with several [built-in memory allocators] -** that are perfectly adequate for the overwhelming majority of applications -** and that this object is only useful to a tiny minority of applications -** with specialized memory allocation requirements. This object is -** also used during testing of SQLite in order to specify an alternative -** memory allocator that simulates memory out-of-memory conditions in -** order to verify that SQLite recovers gracefully from such -** conditions. -** -** The xMalloc, xRealloc, and xFree methods must work like the -** malloc(), realloc() and free() functions from the standard C library. -** ^SQLite guarantees that the second argument to -** xRealloc is always a value returned by a prior call to xRoundup. -** -** xSize should return the allocated size of a memory allocation -** previously obtained from xMalloc or xRealloc. The allocated size -** is always at least as big as the requested size but may be larger. -** -** The xRoundup method returns what would be the allocated size of -** a memory allocation given a particular requested size. Most memory -** allocators round up memory allocations at least to the next multiple -** of 8. Some allocators round up to a larger multiple or to a power of 2. -** Every memory allocation request coming in through [sqlite3_malloc()] -** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0, -** that causes the corresponding memory allocation to fail. -** -** The xInit method initializes the memory allocator. For example, -** it might allocate any required mutexes or initialize internal data -** structures. The xShutdown method is invoked (indirectly) by -** [sqlite3_shutdown()] and should deallocate any resources acquired -** by xInit. The pAppData pointer is used as the only parameter to -** xInit and xShutdown. -** -** SQLite holds the [SQLITE_MUTEX_STATIC_MAIN] mutex when it invokes -** the xInit method, so the xInit method need not be threadsafe. The -** xShutdown method is only called from [sqlite3_shutdown()] so it does -** not need to be threadsafe either. For all other methods, SQLite -** holds the [SQLITE_MUTEX_STATIC_MEM] mutex as long as the -** [SQLITE_CONFIG_MEMSTATUS] configuration option is turned on (which -** it is by default) and so the methods are automatically serialized. -** However, if [SQLITE_CONFIG_MEMSTATUS] is disabled, then the other -** methods must be threadsafe or else make their own arrangements for -** serialization. -** -** SQLite will never invoke xInit() more than once without an intervening -** call to xShutdown(). -*/ -typedef struct sqlite3_mem_methods sqlite3_mem_methods; -struct sqlite3_mem_methods { - void *(*xMalloc)(int); /* Memory allocation function */ - void (*xFree)(void*); /* Free a prior allocation */ - void *(*xRealloc)(void*,int); /* Resize an allocation */ - int (*xSize)(void*); /* Return the size of an allocation */ - int (*xRoundup)(int); /* Round up request size to allocation size */ - int (*xInit)(void*); /* Initialize the memory allocator */ - void (*xShutdown)(void*); /* Deinitialize the memory allocator */ - void *pAppData; /* Argument to xInit() and xShutdown() */ -}; + /* + ** CAPI3REF: Initialize The SQLite Library + ** + ** ^The sqlite3_initialize() routine initializes the + ** SQLite library. ^The sqlite3_shutdown() routine + ** deallocates any resources that were allocated by sqlite3_initialize(). + ** These routines are designed to aid in process initialization and + ** shutdown on embedded systems. Workstation applications using + ** SQLite normally do not need to invoke either of these routines. + ** + ** A call to sqlite3_initialize() is an "effective" call if it is + ** the first time sqlite3_initialize() is invoked during the lifetime of + ** the process, or if it is the first time sqlite3_initialize() is invoked + ** following a call to sqlite3_shutdown(). ^(Only an effective call + ** of sqlite3_initialize() does any initialization. All other calls + ** are harmless no-ops.)^ + ** + ** A call to sqlite3_shutdown() is an "effective" call if it is the first + ** call to sqlite3_shutdown() since the last sqlite3_initialize(). ^(Only + ** an effective call to sqlite3_shutdown() does any deinitialization. + ** All other valid calls to sqlite3_shutdown() are harmless no-ops.)^ + ** + ** The sqlite3_initialize() interface is threadsafe, but sqlite3_shutdown() + ** is not. The sqlite3_shutdown() interface must only be called from a + ** single thread. All open [database connections] must be closed and all + ** other SQLite resources must be deallocated prior to invoking + ** sqlite3_shutdown(). + ** + ** Among other things, ^sqlite3_initialize() will invoke + ** sqlite3_os_init(). Similarly, ^sqlite3_shutdown() + ** will invoke sqlite3_os_end(). + ** + ** ^The sqlite3_initialize() routine returns [SQLITE_OK] on success. + ** ^If for some reason, sqlite3_initialize() is unable to initialize + ** the library (perhaps it is unable to allocate a needed resource such + ** as a mutex) it returns an [error code] other than [SQLITE_OK]. + ** + ** ^The sqlite3_initialize() routine is called internally by many other + ** SQLite interfaces so that an application usually does not need to + ** invoke sqlite3_initialize() directly. For example, [sqlite3_open()] + ** calls sqlite3_initialize() so the SQLite library will be automatically + ** initialized when [sqlite3_open()] is called if it has not be initialized + ** already. ^However, if SQLite is compiled with the [SQLITE_OMIT_AUTOINIT] + ** compile-time option, then the automatic calls to sqlite3_initialize() + ** are omitted and the application must call sqlite3_initialize() directly + ** prior to using any other SQLite interface. For maximum portability, + ** it is recommended that applications always invoke sqlite3_initialize() + ** directly prior to using any other SQLite interface. Future releases + ** of SQLite may require this. In other words, the behavior exhibited + ** when SQLite is compiled with [SQLITE_OMIT_AUTOINIT] might become the + ** default behavior in some future release of SQLite. + ** + ** The sqlite3_os_init() routine does operating-system specific + ** initialization of the SQLite library. The sqlite3_os_end() + ** routine undoes the effect of sqlite3_os_init(). Typical tasks + ** performed by these routines include allocation or deallocation + ** of static resources, initialization of global variables, + ** setting up a default [sqlite3_vfs] module, or setting up + ** a default configuration using [sqlite3_config()]. + ** + ** The application should never invoke either sqlite3_os_init() + ** or sqlite3_os_end() directly. The application should only invoke + ** sqlite3_initialize() and sqlite3_shutdown(). The sqlite3_os_init() + ** interface is called automatically by sqlite3_initialize() and + ** sqlite3_os_end() is called by sqlite3_shutdown(). Appropriate + ** implementations for sqlite3_os_init() and sqlite3_os_end() + ** are built into SQLite when it is compiled for Unix, Windows, or OS/2. + ** When [custom builds | built for other platforms] + ** (using the [SQLITE_OS_OTHER=1] compile-time + ** option) the application must supply a suitable implementation for + ** sqlite3_os_init() and sqlite3_os_end(). An application-supplied + ** implementation of sqlite3_os_init() or sqlite3_os_end() + ** must return [SQLITE_OK] on success and some other [error code] upon + ** failure. + */ + SQLITE_API int sqlite3_initialize(void); + SQLITE_API int sqlite3_shutdown(void); + SQLITE_API int sqlite3_os_init(void); + SQLITE_API int sqlite3_os_end(void); + + /* + ** CAPI3REF: Configuring The SQLite Library + ** + ** The sqlite3_config() interface is used to make global configuration + ** changes to SQLite in order to tune SQLite to the specific needs of + ** the application. The default configuration is recommended for most + ** applications and so this routine is usually not necessary. It is + ** provided to support rare applications with unusual needs. + ** + ** The sqlite3_config() interface is not threadsafe. The application + ** 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. + ** + ** ^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]. + */ + SQLITE_API int sqlite3_config(int, ...); + + /* + ** CAPI3REF: Configure database connections + ** METHOD: sqlite3 + ** + ** The sqlite3_db_config() interface is used to make configuration + ** changes to a [database connection]. The interface is similar to + ** [sqlite3_config()] except that the changes apply to a single + ** [database connection] (specified in the first argument). + ** + ** The second argument to sqlite3_db_config(D,V,...) is the + ** [SQLITE_DBCONFIG_LOOKASIDE | configuration verb] - an integer code + ** that indicates what aspect of the [database connection] is being configured. + ** Subsequent arguments vary depending on the configuration verb. + ** + ** ^Calls to sqlite3_db_config() return SQLITE_OK if and only if + ** the call is considered successful. + */ + SQLITE_API int sqlite3_db_config(sqlite3*, int op, ...); + + /* + ** CAPI3REF: Memory Allocation Routines + ** + ** An instance of this object defines the interface between SQLite + ** and low-level memory allocation routines. + ** + ** This object is used in only one place in the SQLite interface. + ** A pointer to an instance of this object is the argument to + ** [sqlite3_config()] when the configuration option is + ** [SQLITE_CONFIG_MALLOC] or [SQLITE_CONFIG_GETMALLOC]. + ** By creating an instance of this object + ** and passing it to [sqlite3_config]([SQLITE_CONFIG_MALLOC]) + ** during configuration, an application can specify an alternative + ** memory allocation subsystem for SQLite to use for all of its + ** dynamic memory needs. + ** + ** Note that SQLite comes with several [built-in memory allocators] + ** that are perfectly adequate for the overwhelming majority of applications + ** and that this object is only useful to a tiny minority of applications + ** with specialized memory allocation requirements. This object is + ** also used during testing of SQLite in order to specify an alternative + ** memory allocator that simulates memory out-of-memory conditions in + ** order to verify that SQLite recovers gracefully from such + ** conditions. + ** + ** The xMalloc, xRealloc, and xFree methods must work like the + ** malloc(), realloc() and free() functions from the standard C library. + ** ^SQLite guarantees that the second argument to + ** xRealloc is always a value returned by a prior call to xRoundup. + ** + ** xSize should return the allocated size of a memory allocation + ** previously obtained from xMalloc or xRealloc. The allocated size + ** is always at least as big as the requested size but may be larger. + ** + ** The xRoundup method returns what would be the allocated size of + ** a memory allocation given a particular requested size. Most memory + ** allocators round up memory allocations at least to the next multiple + ** of 8. Some allocators round up to a larger multiple or to a power of 2. + ** Every memory allocation request coming in through [sqlite3_malloc()] + ** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0, + ** that causes the corresponding memory allocation to fail. + ** + ** The xInit method initializes the memory allocator. For example, + ** it might allocate any required mutexes or initialize internal data + ** structures. The xShutdown method is invoked (indirectly) by + ** [sqlite3_shutdown()] and should deallocate any resources acquired + ** by xInit. The pAppData pointer is used as the only parameter to + ** xInit and xShutdown. + ** + ** SQLite holds the [SQLITE_MUTEX_STATIC_MAIN] mutex when it invokes + ** the xInit method, so the xInit method need not be threadsafe. The + ** xShutdown method is only called from [sqlite3_shutdown()] so it does + ** not need to be threadsafe either. For all other methods, SQLite + ** holds the [SQLITE_MUTEX_STATIC_MEM] mutex as long as the + ** [SQLITE_CONFIG_MEMSTATUS] configuration option is turned on (which + ** it is by default) and so the methods are automatically serialized. + ** However, if [SQLITE_CONFIG_MEMSTATUS] is disabled, then the other + ** methods must be threadsafe or else make their own arrangements for + ** serialization. + ** + ** SQLite will never invoke xInit() more than once without an intervening + ** call to xShutdown(). + */ + typedef struct sqlite3_mem_methods sqlite3_mem_methods; + struct sqlite3_mem_methods + { + void* (*xMalloc)(int); /* Memory allocation function */ + void (*xFree)(void*); /* Free a prior allocation */ + void* (*xRealloc)(void*, int); /* Resize an allocation */ + int (*xSize)(void*); /* Return the size of an allocation */ + int (*xRoundup)(int); /* Round up request size to allocation size */ + int (*xInit)(void*); /* Initialize the memory allocator */ + void (*xShutdown)(void*); /* Deinitialize the memory allocator */ + void* pAppData; /* Argument to xInit() and xShutdown() */ + }; /* ** CAPI3REF: Configuration Options @@ -2027,35 +2084,35 @@ ** compile-time option is not set, then the default maximum is 1073741824. ** */ -#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* */ +#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_WIN32_HEAPSIZE 23 /* int nByte */ -#define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ -#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ -#define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ -#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_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_WIN32_HEAPSIZE 23 /* int nByte */ +#define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ +#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ +#define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ +#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ +#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ +#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ /* ** CAPI3REF: Database Connection Configuration Options @@ -2114,7 +2171,13 @@ ** 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
    @@ -2125,7 +2188,13 @@ ** 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
    @@ -2326,713 +2395,719 @@ ** ** */ -#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ -#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ -#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ -#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ +#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ +#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ +#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ +#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */ -#define SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE 1006 /* int int* */ -#define SQLITE_DBCONFIG_ENABLE_QPSG 1007 /* int int* */ -#define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */ -#define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */ -#define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */ -#define SQLITE_DBCONFIG_WRITABLE_SCHEMA 1011 /* int int* */ -#define SQLITE_DBCONFIG_LEGACY_ALTER_TABLE 1012 /* int int* */ -#define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */ -#define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */ -#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 */ - -/* -** CAPI3REF: Enable Or Disable Extended Result Codes -** METHOD: sqlite3 -** -** ^The sqlite3_extended_result_codes() routine enables or disables the -** [extended result codes] feature of SQLite. ^The extended result -** codes are disabled by default for historical compatibility. -*/ -SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); - -/* -** CAPI3REF: Last Insert Rowid -** METHOD: sqlite3 -** -** ^Each entry in most SQLite tables (except for [WITHOUT ROWID] tables) -** has a unique 64-bit signed -** integer key called the [ROWID | "rowid"]. ^The rowid is always available -** as an undeclared column named ROWID, OID, or _ROWID_ as long as those -** names are not also used by explicitly declared columns. ^If -** the table has a column of type [INTEGER PRIMARY KEY] then that column -** is another alias for the rowid. -** -** ^The sqlite3_last_insert_rowid(D) interface usually returns the [rowid] of -** the most recent successful [INSERT] into a rowid table or [virtual table] -** on database connection D. ^Inserts into [WITHOUT ROWID] tables are not -** recorded. ^If no successful [INSERT]s into rowid tables have ever occurred -** on the database connection D, then sqlite3_last_insert_rowid(D) returns -** zero. -** -** As well as being set automatically as rows are inserted into database -** tables, the value returned by this function may be set explicitly by -** [sqlite3_set_last_insert_rowid()] -** -** Some virtual table implementations may INSERT rows into rowid tables as -** part of committing a transaction (e.g. to flush data accumulated in memory -** to disk). In this case subsequent calls to this function return the rowid -** associated with these internal INSERT operations, which leads to -** unintuitive results. Virtual table implementations that do write to rowid -** tables in this way can avoid this problem by restoring the original -** rowid value using [sqlite3_set_last_insert_rowid()] before returning -** control to the user. -** -** ^(If an [INSERT] occurs within a trigger then this routine will -** return the [rowid] of the inserted row as long as the trigger is -** running. Once the trigger program ends, the value returned -** by this routine reverts to what it was before the trigger was fired.)^ -** -** ^An [INSERT] that fails due to a constraint violation is not a -** successful [INSERT] and does not change the value returned by this -** routine. ^Thus INSERT OR FAIL, INSERT OR IGNORE, INSERT OR ROLLBACK, -** and INSERT OR ABORT make no changes to the return value of this -** routine when their insertion fails. ^(When INSERT OR REPLACE -** encounters a constraint violation, it does not fail. The -** INSERT continues to completion after deleting rows that caused -** the constraint problem so INSERT OR REPLACE will always change -** the return value of this interface.)^ -** -** ^For the purposes of this routine, an [INSERT] is considered to -** be successful even if it is subsequently rolled back. -** -** This function is accessible to SQL statements via the -** [last_insert_rowid() SQL function]. -** -** If a separate thread performs a new [INSERT] on the same -** database connection while the [sqlite3_last_insert_rowid()] -** function is running and thus changes the last insert [rowid], -** then the value returned by [sqlite3_last_insert_rowid()] is -** unpredictable and might not equal either the old or the new -** last insert [rowid]. -*/ -SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); - -/* -** CAPI3REF: Set the Last Insert Rowid value. -** METHOD: sqlite3 -** -** The sqlite3_set_last_insert_rowid(D, R) method allows the application to -** set the value returned by calling sqlite3_last_insert_rowid(D) to R -** without inserting a row into the database. -*/ -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 -** 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. -** -** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are -** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], -** [foreign key actions] or [REPLACE] constraint resolution are not counted. -** -** Changes to a view that are intercepted by -** [INSTEAD OF trigger | INSTEAD OF triggers] are not counted. ^The value -** returned by sqlite3_changes() immediately after an INSERT, UPDATE or -** DELETE statement run on a view is always zero. Only changes made to real -** tables are counted. -** -** Things are more complicated if the sqlite3_changes() function is -** executed while a trigger program is running. This may happen if the -** program uses the [changes() SQL function], or if some other callback -** function invokes sqlite3_changes() directly. Essentially: -** -**
      -**
    • ^(Before entering a trigger program the value returned by -** sqlite3_changes() function is saved. After the trigger program -** has finished, the original value is restored.)^ -** -**
    • ^(Within a trigger program each INSERT, UPDATE and DELETE -** statement sets the value returned by sqlite3_changes() -** upon completion as normal. Of course, this value will not include -** any changes performed by sub-triggers, as the sqlite3_changes() -** value will be saved and restored after each sub-trigger has run.)^ -**
    -** -** ^This means that if the changes() SQL function (or similar) is used -** by the first INSERT, UPDATE or DELETE statement within a trigger, it -** returns the value as set when the calling statement began executing. -** ^If it is used by the second or subsequent such statement within a trigger -** program, the value returned reflects the number of rows modified by the -** previous INSERT, UPDATE or DELETE statement within the same trigger. -** -** If a separate thread makes changes on the same database connection -** while [sqlite3_changes()] is running then the value returned -** is unpredictable and not meaningful. -** -** See also: -**
      -**
    • the [sqlite3_total_changes()] interface -**
    • the [count_changes pragma] -**
    • the [changes() SQL function] -**
    • the [data_version pragma] -**
    -*/ -SQLITE_API int sqlite3_changes(sqlite3*); - -/* -** CAPI3REF: Total Number Of Rows Modified -** METHOD: sqlite3 -** -** ^This function returns 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(). -** -** ^Changes made as part of [foreign key actions] are included in the -** count, but those made as part of REPLACE constraint resolution are -** not. ^Changes to a view that are intercepted by INSTEAD OF triggers -** are not counted. -** -** The [sqlite3_total_changes(D)] interface only reports the number -** of rows that changed due to SQL statement run against database -** connection D. Any changes by other database connections are ignored. -** To detect changes against a database file from other database -** connections use the [PRAGMA data_version] command or the -** [SQLITE_FCNTL_DATA_VERSION] [file control]. -** -** If a separate thread makes changes on the same database connection -** while [sqlite3_total_changes()] is running then the value -** returned is unpredictable and not meaningful. -** -** See also: -**
      -**
    • the [sqlite3_changes()] interface -**
    • the [count_changes pragma] -**
    • the [changes() SQL function] -**
    • the [data_version pragma] -**
    • the [SQLITE_FCNTL_DATA_VERSION] [file control] -**
    -*/ -SQLITE_API int sqlite3_total_changes(sqlite3*); - -/* -** CAPI3REF: Interrupt A Long-Running Query -** METHOD: sqlite3 -** -** ^This function causes any pending database operation to abort and -** return at its earliest opportunity. This routine is typically -** called in response to a user action such as pressing "Cancel" -** or Ctrl-C where the user wants a long query operation to halt -** immediately. -** -** ^It is safe to call this routine from a thread different from the -** thread that is currently running the database operation. But it -** is not safe to call this routine with a [database connection] that -** is closed or might close before sqlite3_interrupt() returns. -** -** ^If an SQL operation is very nearly finished at the time when -** sqlite3_interrupt() is called, then it might not have an opportunity -** to be interrupted and might continue to completion. -** -** ^An SQL operation that is interrupted will return [SQLITE_INTERRUPT]. -** ^If the interrupted SQL operation is an INSERT, UPDATE, or DELETE -** that is inside an explicit transaction, then the entire transaction -** will be rolled back automatically. -** -** ^The sqlite3_interrupt(D) call is in effect until all currently running -** SQL statements on [database connection] D complete. ^Any new SQL statements -** that are started after the sqlite3_interrupt() call and before the -** running statement count reaches zero are interrupted as if they had been -** running prior to the sqlite3_interrupt() call. ^New SQL statements -** that are started after the running statement count reaches zero are -** not effected by the sqlite3_interrupt(). -** ^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. -*/ -SQLITE_API void sqlite3_interrupt(sqlite3*); - -/* -** CAPI3REF: Determine If An SQL Statement Is Complete -** -** These routines are useful during command-line input to determine if the -** currently entered text seems to form a complete SQL statement or -** if additional input is needed before sending the text into -** SQLite for parsing. ^These routines return 1 if the input string -** appears to be a complete SQL statement. ^A statement is judged to be -** complete if it ends with a semicolon token and is not a prefix of a -** well-formed CREATE TRIGGER statement. ^Semicolons that are embedded within -** string literals or quoted identifier names or comments are not -** independent tokens (they are part of the token in which they are -** embedded) and thus do not count as a statement terminator. ^Whitespace -** and comments that follow the final semicolon are ignored. -** -** ^These routines return 0 if the statement is incomplete. ^If a -** memory allocation fails, then SQLITE_NOMEM is returned. -** -** ^These routines do not parse the SQL statements thus -** will not detect syntactically incorrect SQL. -** -** ^(If SQLite has not been initialized using [sqlite3_initialize()] prior -** to invoking sqlite3_complete16() then sqlite3_initialize() is invoked -** automatically by sqlite3_complete16(). If that initialization fails, -** then the return value from sqlite3_complete16() will be non-zero -** regardless of whether or not the input SQL is complete.)^ -** -** The input to [sqlite3_complete()] must be a zero-terminated -** UTF-8 string. -** -** The input to [sqlite3_complete16()] must be a zero-terminated -** UTF-16 string in native byte order. -*/ -SQLITE_API int sqlite3_complete(const char *sql); -SQLITE_API int sqlite3_complete16(const void *sql); - -/* -** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors -** KEYWORDS: {busy-handler callback} {busy handler} -** METHOD: sqlite3 -** -** ^The sqlite3_busy_handler(D,X,P) routine sets a callback function X -** that might be invoked with argument P whenever -** an attempt is made to access a database table associated with -** [database connection] D when another thread -** or process has the table locked. -** The sqlite3_busy_handler() interface is used to implement -** [sqlite3_busy_timeout()] and [PRAGMA busy_timeout]. -** -** ^If the busy callback is NULL, then [SQLITE_BUSY] -** is returned immediately upon encountering the lock. ^If the busy callback -** is not NULL, then the callback might be invoked with two arguments. -** -** ^The first argument to the busy handler is a copy of the void* pointer which -** is the third argument to sqlite3_busy_handler(). ^The second argument to -** the busy handler callback is the number of times that the busy handler has -** been invoked previously for the same locking event. ^If the -** busy callback returns 0, then no additional attempts are made to -** access the database and [SQLITE_BUSY] is returned -** to the application. -** ^If the callback returns non-zero, then another attempt -** is made to access the database and the cycle repeats. -** -** The presence of a busy handler does not guarantee that it will be invoked -** when there is lock contention. ^If SQLite determines that invoking the busy -** handler could result in a deadlock, it will go ahead and return [SQLITE_BUSY] -** to the application instead of invoking the -** busy handler. -** Consider a scenario where one process is holding a read lock that -** it is trying to promote to a reserved lock and -** a second process is holding a reserved lock that it is trying -** to promote to an exclusive lock. The first process cannot proceed -** because it is blocked by the second and the second process cannot -** proceed because it is blocked by the first. If both processes -** invoke the busy handlers, neither will make any progress. Therefore, -** SQLite returns [SQLITE_BUSY] for the first process, hoping that this -** will induce the first process to release its read lock and allow -** the second process to proceed. -** -** ^The default busy callback is NULL. -** -** ^(There can only be a single busy handler defined for each -** [database connection]. Setting a new busy handler clears any -** previously set handler.)^ ^Note that calling [sqlite3_busy_timeout()] -** or evaluating [PRAGMA busy_timeout=N] will change the -** busy handler and thus clear any previously set busy handler. -** -** The busy callback should not take any actions which modify the -** database connection that invoked the busy handler. In other words, -** the busy handler is not reentrant. Any such actions -** result in undefined behavior. -** -** A busy handler must not close the database connection -** or [prepared statement] that invoked the busy handler. -*/ -SQLITE_API int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*); - -/* -** CAPI3REF: Set A Busy Timeout -** METHOD: sqlite3 -** -** ^This routine sets a [sqlite3_busy_handler | busy handler] that sleeps -** for a specified amount of time when a table is locked. ^The handler -** will sleep multiple times until at least "ms" milliseconds of sleeping -** have accumulated. ^After at least "ms" milliseconds of sleeping, -** the handler returns 0 which causes [sqlite3_step()] to return -** [SQLITE_BUSY]. -** -** ^Calling this routine with an argument less than or equal to zero -** turns off all busy handlers. -** -** ^(There can only be a single busy handler for a particular -** [database connection] at any given moment. If another busy handler -** was defined (using [sqlite3_busy_handler()]) prior to calling -** this routine, that other busy handler is cleared.)^ -** -** See also: [PRAGMA busy_timeout] -*/ -SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); - -/* -** CAPI3REF: Convenience Routines For Running Queries -** METHOD: sqlite3 -** -** This is a legacy interface that is preserved for backwards compatibility. -** Use of this interface is not recommended. -** -** Definition: A result table is memory data structure created by the -** [sqlite3_get_table()] interface. A result table records the -** complete query results from one or more queries. -** -** The table conceptually has a number of rows and columns. But -** these numbers are not part of the result table itself. These -** numbers are obtained separately. Let N be the number of rows -** and M be the number of columns. -** -** A result table is an array of pointers to zero-terminated UTF-8 strings. -** There are (N+1)*M elements in the array. The first M pointers point -** to zero-terminated strings that contain the names of the columns. -** The remaining entries all point to query results. NULL values result -** in NULL pointers. All other values are in their UTF-8 zero-terminated -** string representation as returned by [sqlite3_column_text()]. -** -** A result table might consist of one or more memory allocations. -** It is not safe to pass a result table directly to [sqlite3_free()]. -** A result table should be deallocated using [sqlite3_free_table()]. -** -** ^(As an example of the result table format, suppose a query result -** is as follows: -** -**
    -**        Name        | Age
    -**        -----------------------
    -**        Alice       | 43
    -**        Bob         | 28
    -**        Cindy       | 21
    -** 
    -** -** There are two columns (M==2) and three rows (N==3). Thus the -** result table has 8 entries. Suppose the result table is stored -** in an array named azResult. Then azResult holds this content: -** -**
    -**        azResult[0] = "Name";
    -**        azResult[1] = "Age";
    -**        azResult[2] = "Alice";
    -**        azResult[3] = "43";
    -**        azResult[4] = "Bob";
    -**        azResult[5] = "28";
    -**        azResult[6] = "Cindy";
    -**        azResult[7] = "21";
    -** 
    )^ -** -** ^The sqlite3_get_table() function evaluates one or more -** semicolon-separated SQL statements in the zero-terminated UTF-8 -** string of its 2nd parameter and returns a result table to the -** pointer given in its 3rd parameter. -** -** After the application has finished with the result from sqlite3_get_table(), -** it must pass the result table pointer to sqlite3_free_table() in order to -** release the memory that was malloced. Because of the way the -** [sqlite3_malloc()] happens within sqlite3_get_table(), the calling -** function must not try to call [sqlite3_free()] directly. Only -** [sqlite3_free_table()] is able to release the memory properly and safely. -** -** The sqlite3_get_table() interface is implemented as a wrapper around -** [sqlite3_exec()]. The sqlite3_get_table() routine does not have access -** to any internal data structures of SQLite. It uses only the public -** interface defined here. As a consequence, errors that occur in the -** wrapper layer outside of the internal [sqlite3_exec()] call are not -** reflected in subsequent calls to [sqlite3_errcode()] or -** [sqlite3_errmsg()]. -*/ -SQLITE_API int sqlite3_get_table( - sqlite3 *db, /* An open database */ - const char *zSql, /* SQL to be evaluated */ - char ***pazResult, /* Results of the query */ - int *pnRow, /* Number of result rows written here */ - int *pnColumn, /* Number of result columns written here */ - char **pzErrmsg /* Error msg written here */ -); -SQLITE_API void sqlite3_free_table(char **result); - -/* -** CAPI3REF: Formatted String Printing Functions -** -** These routines are work-alikes of the "printf()" family of functions -** from the standard C library. -** These routines understand most of the common formatting options from -** the standard library printf() -** plus some additional non-standard formats ([%q], [%Q], [%w], and [%z]). -** See the [built-in printf()] documentation for details. -** -** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their -** results into memory obtained from [sqlite3_malloc64()]. -** The strings returned by these two routines should be -** released by [sqlite3_free()]. ^Both routines return a -** NULL pointer if [sqlite3_malloc64()] is unable to allocate enough -** memory to hold the resulting string. -** -** ^(The sqlite3_snprintf() routine is similar to "snprintf()" from -** the standard C library. The result is written into the -** buffer supplied as the second parameter whose size is given by -** the first parameter. Note that the order of the -** first two parameters is reversed from snprintf().)^ This is an -** historical accident that cannot be fixed without breaking -** backwards compatibility. ^(Note also that sqlite3_snprintf() -** returns a pointer to its buffer instead of the number of -** characters actually written into the buffer.)^ We admit that -** the number of characters written would be a more useful return -** value but we cannot change the implementation of sqlite3_snprintf() -** now without breaking compatibility. -** -** ^As long as the buffer size is greater than zero, sqlite3_snprintf() -** guarantees that the buffer is always zero-terminated. ^The first -** parameter "n" is the total size of the buffer, including space for -** the zero terminator. So the longest string that can be completely -** written will be n-1 characters. -** -** ^The sqlite3_vsnprintf() routine is a varargs version of sqlite3_snprintf(). -** -** See also: [built-in printf()], [printf() SQL function] -*/ -SQLITE_API char *sqlite3_mprintf(const char*,...); -SQLITE_API char *sqlite3_vmprintf(const char*, va_list); -SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...); -SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list); - -/* -** CAPI3REF: Memory Allocation Subsystem -** -** The SQLite core uses these three routines for all of its own -** internal memory allocation needs. "Core" in the previous sentence -** does not include operating-system specific [VFS] implementation. The -** Windows VFS uses native malloc() and free() for some operations. -** -** ^The sqlite3_malloc() routine returns a pointer to a block -** of memory at least N bytes in length, where N is the parameter. -** ^If sqlite3_malloc() is unable to obtain sufficient free -** memory, it returns a NULL pointer. ^If the parameter N to -** sqlite3_malloc() is zero or negative then sqlite3_malloc() returns -** a NULL pointer. -** -** ^The sqlite3_malloc64(N) routine works just like -** sqlite3_malloc(N) except that N is an unsigned 64-bit integer instead -** of a signed 32-bit integer. -** -** ^Calling sqlite3_free() with a pointer previously returned -** by sqlite3_malloc() or sqlite3_realloc() releases that memory so -** that it might be reused. ^The sqlite3_free() routine is -** a no-op if is called with a NULL pointer. Passing a NULL pointer -** to sqlite3_free() is harmless. After being freed, memory -** should neither be read nor written. Even reading previously freed -** memory might result in a segmentation fault or other severe error. -** Memory corruption, a segmentation fault, or other severe error -** might result if sqlite3_free() is called with a non-NULL pointer that -** was not obtained from sqlite3_malloc() or sqlite3_realloc(). -** -** ^The sqlite3_realloc(X,N) interface attempts to resize a -** prior memory allocation X to be at least N bytes. -** ^If the X parameter to sqlite3_realloc(X,N) -** is a NULL pointer then its behavior is identical to calling -** sqlite3_malloc(N). -** ^If the N parameter to sqlite3_realloc(X,N) is zero or -** negative then the behavior is exactly the same as calling -** sqlite3_free(X). -** ^sqlite3_realloc(X,N) returns a pointer to a memory allocation -** of at least N bytes in size or NULL if insufficient memory is available. -** ^If M is the size of the prior allocation, then min(N,M) bytes -** of the prior allocation are copied into the beginning of buffer returned -** by sqlite3_realloc(X,N) and the prior allocation is freed. -** ^If sqlite3_realloc(X,N) returns NULL and N is positive, then the -** prior allocation is not freed. -** -** ^The sqlite3_realloc64(X,N) interfaces works the same as -** sqlite3_realloc(X,N) except that N is a 64-bit unsigned integer instead -** of a 32-bit signed integer. -** -** ^If X is a memory allocation previously obtained from sqlite3_malloc(), -** sqlite3_malloc64(), sqlite3_realloc(), or sqlite3_realloc64(), then -** sqlite3_msize(X) returns the size of that memory allocation in bytes. -** ^The value returned by sqlite3_msize(X) might be larger than the number -** of bytes requested when X was allocated. ^If X is a NULL pointer then -** sqlite3_msize(X) returns zero. If X points to something that is not -** the beginning of memory allocation, or if it points to a formerly -** valid memory allocation that has now been freed, then the behavior -** of sqlite3_msize(X) is undefined and possibly harmful. -** -** ^The memory returned by sqlite3_malloc(), sqlite3_realloc(), -** sqlite3_malloc64(), and sqlite3_realloc64() -** is always aligned to at least an 8 byte boundary, or to a -** 4 byte boundary if the [SQLITE_4_BYTE_ALIGNED_MALLOC] compile-time -** option is used. -** -** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()] -** must be either NULL or else pointers obtained from a prior -** invocation of [sqlite3_malloc()] or [sqlite3_realloc()] that have -** not yet been released. -** -** The application must not read or write any part of -** a block of memory after it has been released using -** [sqlite3_free()] or [sqlite3_realloc()]. -*/ -SQLITE_API void *sqlite3_malloc(int); -SQLITE_API void *sqlite3_malloc64(sqlite3_uint64); -SQLITE_API void *sqlite3_realloc(void*, int); -SQLITE_API void *sqlite3_realloc64(void*, sqlite3_uint64); -SQLITE_API void sqlite3_free(void*); -SQLITE_API sqlite3_uint64 sqlite3_msize(void*); - -/* -** CAPI3REF: Memory Allocator Statistics -** -** SQLite provides these two interfaces for reporting on the status -** of the [sqlite3_malloc()], [sqlite3_free()], and [sqlite3_realloc()] -** routines, which form the built-in memory allocation subsystem. -** -** ^The [sqlite3_memory_used()] routine returns the number of bytes -** of memory currently outstanding (malloced but not freed). -** ^The [sqlite3_memory_highwater()] routine returns the maximum -** value of [sqlite3_memory_used()] since the high-water mark -** was last reset. ^The values returned by [sqlite3_memory_used()] and -** [sqlite3_memory_highwater()] include any overhead -** added by SQLite in its implementation of [sqlite3_malloc()], -** but not overhead added by the any underlying system library -** routines that [sqlite3_malloc()] may call. -** -** ^The memory high-water mark is reset to the current value of -** [sqlite3_memory_used()] if and only if the parameter to -** [sqlite3_memory_highwater()] is true. ^The value returned -** by [sqlite3_memory_highwater(1)] is the high-water mark -** prior to the reset. -*/ -SQLITE_API sqlite3_int64 sqlite3_memory_used(void); -SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag); - -/* -** CAPI3REF: Pseudo-Random Number Generator -** -** SQLite contains a high-quality pseudo-random number generator (PRNG) used to -** select random [ROWID | ROWIDs] when inserting new records into a table that -** already uses the largest possible [ROWID]. The PRNG is also used for -** the built-in random() and randomblob() SQL functions. This interface allows -** applications to access the same PRNG for other purposes. -** -** ^A call to this routine stores N bytes of randomness into buffer P. -** ^The P parameter can be a NULL pointer. -** -** ^If this routine has not been previously called or if the previous -** call had N less than one or a NULL pointer for P, then the PRNG is -** seeded using randomness obtained from the xRandomness method of -** the default [sqlite3_vfs] object. -** ^If the previous call to this routine had an N of 1 or more and a -** non-NULL P then the pseudo-randomness is generated -** internally and without recourse to the [sqlite3_vfs] xRandomness -** method. -*/ -SQLITE_API void sqlite3_randomness(int N, void *P); - -/* -** CAPI3REF: Compile-Time Authorization Callbacks -** METHOD: sqlite3 -** KEYWORDS: {authorizer callback} -** -** ^This routine registers an authorizer callback with a particular -** [database connection], supplied in the first argument. -** ^The authorizer callback is invoked as SQL statements are being compiled -** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()], -** [sqlite3_prepare_v3()], [sqlite3_prepare16()], [sqlite3_prepare16_v2()], -** and [sqlite3_prepare16_v3()]. ^At various -** points during the compilation process, as logic is being created -** to perform various actions, the authorizer callback is invoked to -** see if those actions are allowed. ^The authorizer callback should -** return [SQLITE_OK] to allow the action, [SQLITE_IGNORE] to disallow the -** specific action but allow the SQL statement to continue to be -** compiled, or [SQLITE_DENY] to cause the entire SQL statement to be -** rejected with an error. ^If the authorizer callback returns -** any value other than [SQLITE_IGNORE], [SQLITE_OK], or [SQLITE_DENY] -** then the [sqlite3_prepare_v2()] or equivalent call that triggered -** the authorizer will fail with an error message. -** -** When the callback returns [SQLITE_OK], that means the operation -** requested is ok. ^When the callback returns [SQLITE_DENY], the -** [sqlite3_prepare_v2()] or equivalent call that triggered the -** authorizer will fail with an error message explaining that -** access is denied. -** -** ^The first parameter to the authorizer callback is a copy of the third -** parameter to the sqlite3_set_authorizer() interface. ^The second parameter -** to the callback is an integer [SQLITE_COPY | action code] that specifies -** the particular action to be authorized. ^The third through sixth parameters -** to the callback are either NULL pointers or zero-terminated strings -** that contain additional details about the action to be authorized. -** Applications must always be prepared to encounter a NULL pointer in any -** of the third through the sixth parameters of the authorization callback. -** -** ^If the action code is [SQLITE_READ] -** and the callback returns [SQLITE_IGNORE] then the -** [prepared statement] statement is constructed to substitute -** a NULL value in place of the table column that would have -** been read if [SQLITE_OK] had been returned. The [SQLITE_IGNORE] -** return can be used to deny an untrusted user access to individual -** columns of a table. -** ^When a table is referenced by a [SELECT] but no column values are -** extracted from that table (for example in a query like -** "SELECT count(*) FROM tab") then the [SQLITE_READ] authorizer callback -** is invoked once for that table with a column name that is an empty string. -** ^If the action code is [SQLITE_DELETE] and the callback returns -** [SQLITE_IGNORE] then the [DELETE] operation proceeds but the -** [truncate optimization] is disabled and all rows are deleted individually. -** -** An authorizer is used when [sqlite3_prepare | preparing] -** SQL statements from an untrusted source, to ensure that the SQL statements -** do not try to access data they are not allowed to see, or that they do not -** try to execute malicious statements that damage the database. For -** example, an application may allow a user to enter arbitrary -** SQL queries for evaluation by a database. But the application does -** not want the user to be able to make arbitrary changes to the -** database. An authorizer could then be put in place while the -** user-entered SQL is being [sqlite3_prepare | prepared] that -** disallows everything except [SELECT] statements. -** -** Applications that need to process SQL from untrusted sources -** might also consider lowering resource limits using [sqlite3_limit()] -** and limiting database size using the [max_page_count] [PRAGMA] -** in addition to using an authorizer. -** -** ^(Only a single authorizer can be in place on a database connection -** at a time. Each call to sqlite3_set_authorizer overrides the -** previous call.)^ ^Disable the authorizer by installing a NULL callback. -** The authorizer is disabled by default. -** -** The authorizer callback must not do anything that will modify -** the database connection that invoked the authorizer callback. -** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their -** database connections for the meaning of "modify" in this paragraph. -** -** ^When [sqlite3_prepare_v2()] is used to prepare a statement, the -** statement might be re-prepared during [sqlite3_step()] due to a -** schema change. Hence, the application should ensure that the -** correct authorizer callback remains in place during the [sqlite3_step()]. -** -** ^Note that the authorizer callback is invoked only during -** [sqlite3_prepare()] or its variants. Authorization is not -** performed during statement evaluation in [sqlite3_step()], unless -** as stated in the previous paragraph, sqlite3_step() invokes -** sqlite3_prepare_v2() to reprepare a statement after a schema change. -*/ -SQLITE_API int sqlite3_set_authorizer( - sqlite3*, - int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), - void *pUserData -); +#define SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE 1006 /* int int* */ +#define SQLITE_DBCONFIG_ENABLE_QPSG 1007 /* int int* */ +#define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */ +#define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */ +#define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */ +#define SQLITE_DBCONFIG_WRITABLE_SCHEMA 1011 /* int int* */ +#define SQLITE_DBCONFIG_LEGACY_ALTER_TABLE 1012 /* int int* */ +#define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */ +#define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */ +#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 */ + + /* + ** CAPI3REF: Enable Or Disable Extended Result Codes + ** METHOD: sqlite3 + ** + ** ^The sqlite3_extended_result_codes() routine enables or disables the + ** [extended result codes] feature of SQLite. ^The extended result + ** codes are disabled by default for historical compatibility. + */ + SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); + + /* + ** CAPI3REF: Last Insert Rowid + ** METHOD: sqlite3 + ** + ** ^Each entry in most SQLite tables (except for [WITHOUT ROWID] tables) + ** has a unique 64-bit signed + ** integer key called the [ROWID | "rowid"]. ^The rowid is always available + ** as an undeclared column named ROWID, OID, or _ROWID_ as long as those + ** names are not also used by explicitly declared columns. ^If + ** the table has a column of type [INTEGER PRIMARY KEY] then that column + ** is another alias for the rowid. + ** + ** ^The sqlite3_last_insert_rowid(D) interface usually returns the [rowid] of + ** the most recent successful [INSERT] into a rowid table or [virtual table] + ** on database connection D. ^Inserts into [WITHOUT ROWID] tables are not + ** recorded. ^If no successful [INSERT]s into rowid tables have ever occurred + ** on the database connection D, then sqlite3_last_insert_rowid(D) returns + ** zero. + ** + ** As well as being set automatically as rows are inserted into database + ** tables, the value returned by this function may be set explicitly by + ** [sqlite3_set_last_insert_rowid()] + ** + ** Some virtual table implementations may INSERT rows into rowid tables as + ** part of committing a transaction (e.g. to flush data accumulated in memory + ** to disk). In this case subsequent calls to this function return the rowid + ** associated with these internal INSERT operations, which leads to + ** unintuitive results. Virtual table implementations that do write to rowid + ** tables in this way can avoid this problem by restoring the original + ** rowid value using [sqlite3_set_last_insert_rowid()] before returning + ** control to the user. + ** + ** ^(If an [INSERT] occurs within a trigger then this routine will + ** return the [rowid] of the inserted row as long as the trigger is + ** running. Once the trigger program ends, the value returned + ** by this routine reverts to what it was before the trigger was fired.)^ + ** + ** ^An [INSERT] that fails due to a constraint violation is not a + ** successful [INSERT] and does not change the value returned by this + ** routine. ^Thus INSERT OR FAIL, INSERT OR IGNORE, INSERT OR ROLLBACK, + ** and INSERT OR ABORT make no changes to the return value of this + ** routine when their insertion fails. ^(When INSERT OR REPLACE + ** encounters a constraint violation, it does not fail. The + ** INSERT continues to completion after deleting rows that caused + ** the constraint problem so INSERT OR REPLACE will always change + ** the return value of this interface.)^ + ** + ** ^For the purposes of this routine, an [INSERT] is considered to + ** be successful even if it is subsequently rolled back. + ** + ** This function is accessible to SQL statements via the + ** [last_insert_rowid() SQL function]. + ** + ** If a separate thread performs a new [INSERT] on the same + ** database connection while the [sqlite3_last_insert_rowid()] + ** function is running and thus changes the last insert [rowid], + ** then the value returned by [sqlite3_last_insert_rowid()] is + ** unpredictable and might not equal either the old or the new + ** last insert [rowid]. + */ + SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); + + /* + ** CAPI3REF: Set the Last Insert Rowid value. + ** METHOD: sqlite3 + ** + ** The sqlite3_set_last_insert_rowid(D, R) method allows the application to + ** set the value returned by calling sqlite3_last_insert_rowid(D) to R + ** without inserting a row into the database. + */ + SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*, sqlite3_int64); + + /* + ** CAPI3REF: Count The Number Of Rows Modified + ** METHOD: sqlite3 + ** + ** ^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. + ** 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. + ** + ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are + ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], + ** [foreign key actions] or [REPLACE] constraint resolution are not counted. + ** + ** Changes to a view that are intercepted by + ** [INSTEAD OF trigger | INSTEAD OF triggers] are not counted. ^The value + ** returned by sqlite3_changes() immediately after an INSERT, UPDATE or + ** DELETE statement run on a view is always zero. Only changes made to real + ** tables are counted. + ** + ** Things are more complicated if the sqlite3_changes() function is + ** executed while a trigger program is running. This may happen if the + ** program uses the [changes() SQL function], or if some other callback + ** function invokes sqlite3_changes() directly. Essentially: + ** + **
      + **
    • ^(Before entering a trigger program the value returned by + ** sqlite3_changes() function is saved. After the trigger program + ** has finished, the original value is restored.)^ + ** + **
    • ^(Within a trigger program each INSERT, UPDATE and DELETE + ** statement sets the value returned by sqlite3_changes() + ** upon completion as normal. Of course, this value will not include + ** any changes performed by sub-triggers, as the sqlite3_changes() + ** value will be saved and restored after each sub-trigger has run.)^ + **
    + ** + ** ^This means that if the changes() SQL function (or similar) is used + ** by the first INSERT, UPDATE or DELETE statement within a trigger, it + ** returns the value as set when the calling statement began executing. + ** ^If it is used by the second or subsequent such statement within a trigger + ** program, the value returned reflects the number of rows modified by the + ** previous INSERT, UPDATE or DELETE statement within the same trigger. + ** + ** If a separate thread makes changes on the same database connection + ** while [sqlite3_changes()] is running then the value returned + ** is unpredictable and not meaningful. + ** + ** See also: + **
      + **
    • the [sqlite3_total_changes()] interface + **
    • the [count_changes pragma] + **
    • the [changes() SQL function] + **
    • the [data_version pragma] + **
    + */ + SQLITE_API int sqlite3_changes(sqlite3*); + SQLITE_API sqlite3_int64 sqlite3_changes64(sqlite3*); + + /* + ** CAPI3REF: Total Number Of Rows Modified + ** METHOD: sqlite3 + ** + ** ^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. 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 + ** not. ^Changes to a view that are intercepted by INSTEAD OF triggers + ** are not counted. + ** + ** The [sqlite3_total_changes(D)] interface only reports the number + ** of rows that changed due to SQL statement run against database + ** connection D. Any changes by other database connections are ignored. + ** To detect changes against a database file from other database + ** connections use the [PRAGMA data_version] command or the + ** [SQLITE_FCNTL_DATA_VERSION] [file control]. + ** + ** If a separate thread makes changes on the same database connection + ** while [sqlite3_total_changes()] is running then the value + ** returned is unpredictable and not meaningful. + ** + ** See also: + **
      + **
    • the [sqlite3_changes()] interface + **
    • the [count_changes pragma] + **
    • the [changes() SQL function] + **
    • the [data_version pragma] + **
    • the [SQLITE_FCNTL_DATA_VERSION] [file control] + **
    + */ + SQLITE_API int sqlite3_total_changes(sqlite3*); + SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3*); + + /* + ** CAPI3REF: Interrupt A Long-Running Query + ** METHOD: sqlite3 + ** + ** ^This function causes any pending database operation to abort and + ** return at its earliest opportunity. This routine is typically + ** called in response to a user action such as pressing "Cancel" + ** or Ctrl-C where the user wants a long query operation to halt + ** immediately. + ** + ** ^It is safe to call this routine from a thread different from the + ** thread that is currently running the database operation. But it + ** is not safe to call this routine with a [database connection] that + ** is closed or might close before sqlite3_interrupt() returns. + ** + ** ^If an SQL operation is very nearly finished at the time when + ** sqlite3_interrupt() is called, then it might not have an opportunity + ** to be interrupted and might continue to completion. + ** + ** ^An SQL operation that is interrupted will return [SQLITE_INTERRUPT]. + ** ^If the interrupted SQL operation is an INSERT, UPDATE, or DELETE + ** that is inside an explicit transaction, then the entire transaction + ** will be rolled back automatically. + ** + ** ^The sqlite3_interrupt(D) call is in effect until all currently running + ** SQL statements on [database connection] D complete. ^Any new SQL statements + ** that are started after the sqlite3_interrupt() call and before the + ** running statement count reaches zero are interrupted as if they had been + ** running prior to the sqlite3_interrupt() call. ^New SQL statements + ** that are started after the running statement count reaches zero are + ** not effected by the sqlite3_interrupt(). + ** ^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. + */ + SQLITE_API void sqlite3_interrupt(sqlite3*); + + /* + ** CAPI3REF: Determine If An SQL Statement Is Complete + ** + ** These routines are useful during command-line input to determine if the + ** currently entered text seems to form a complete SQL statement or + ** if additional input is needed before sending the text into + ** SQLite for parsing. ^These routines return 1 if the input string + ** appears to be a complete SQL statement. ^A statement is judged to be + ** complete if it ends with a semicolon token and is not a prefix of a + ** well-formed CREATE TRIGGER statement. ^Semicolons that are embedded within + ** string literals or quoted identifier names or comments are not + ** independent tokens (they are part of the token in which they are + ** embedded) and thus do not count as a statement terminator. ^Whitespace + ** and comments that follow the final semicolon are ignored. + ** + ** ^These routines return 0 if the statement is incomplete. ^If a + ** memory allocation fails, then SQLITE_NOMEM is returned. + ** + ** ^These routines do not parse the SQL statements thus + ** will not detect syntactically incorrect SQL. + ** + ** ^(If SQLite has not been initialized using [sqlite3_initialize()] prior + ** to invoking sqlite3_complete16() then sqlite3_initialize() is invoked + ** automatically by sqlite3_complete16(). If that initialization fails, + ** then the return value from sqlite3_complete16() will be non-zero + ** regardless of whether or not the input SQL is complete.)^ + ** + ** The input to [sqlite3_complete()] must be a zero-terminated + ** UTF-8 string. + ** + ** The input to [sqlite3_complete16()] must be a zero-terminated + ** UTF-16 string in native byte order. + */ + SQLITE_API int sqlite3_complete(const char* sql); + SQLITE_API int sqlite3_complete16(const void* sql); + + /* + ** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors + ** KEYWORDS: {busy-handler callback} {busy handler} + ** METHOD: sqlite3 + ** + ** ^The sqlite3_busy_handler(D,X,P) routine sets a callback function X + ** that might be invoked with argument P whenever + ** an attempt is made to access a database table associated with + ** [database connection] D when another thread + ** or process has the table locked. + ** The sqlite3_busy_handler() interface is used to implement + ** [sqlite3_busy_timeout()] and [PRAGMA busy_timeout]. + ** + ** ^If the busy callback is NULL, then [SQLITE_BUSY] + ** is returned immediately upon encountering the lock. ^If the busy callback + ** is not NULL, then the callback might be invoked with two arguments. + ** + ** ^The first argument to the busy handler is a copy of the void* pointer which + ** is the third argument to sqlite3_busy_handler(). ^The second argument to + ** the busy handler callback is the number of times that the busy handler has + ** been invoked previously for the same locking event. ^If the + ** busy callback returns 0, then no additional attempts are made to + ** access the database and [SQLITE_BUSY] is returned + ** to the application. + ** ^If the callback returns non-zero, then another attempt + ** is made to access the database and the cycle repeats. + ** + ** The presence of a busy handler does not guarantee that it will be invoked + ** when there is lock contention. ^If SQLite determines that invoking the busy + ** handler could result in a deadlock, it will go ahead and return [SQLITE_BUSY] + ** to the application instead of invoking the + ** busy handler. + ** Consider a scenario where one process is holding a read lock that + ** it is trying to promote to a reserved lock and + ** a second process is holding a reserved lock that it is trying + ** to promote to an exclusive lock. The first process cannot proceed + ** because it is blocked by the second and the second process cannot + ** proceed because it is blocked by the first. If both processes + ** invoke the busy handlers, neither will make any progress. Therefore, + ** SQLite returns [SQLITE_BUSY] for the first process, hoping that this + ** will induce the first process to release its read lock and allow + ** the second process to proceed. + ** + ** ^The default busy callback is NULL. + ** + ** ^(There can only be a single busy handler defined for each + ** [database connection]. Setting a new busy handler clears any + ** previously set handler.)^ ^Note that calling [sqlite3_busy_timeout()] + ** or evaluating [PRAGMA busy_timeout=N] will change the + ** busy handler and thus clear any previously set busy handler. + ** + ** The busy callback should not take any actions which modify the + ** database connection that invoked the busy handler. In other words, + ** the busy handler is not reentrant. Any such actions + ** result in undefined behavior. + ** + ** A busy handler must not close the database connection + ** or [prepared statement] that invoked the busy handler. + */ + SQLITE_API int sqlite3_busy_handler(sqlite3*, int (*)(void*, int), void*); + + /* + ** CAPI3REF: Set A Busy Timeout + ** METHOD: sqlite3 + ** + ** ^This routine sets a [sqlite3_busy_handler | busy handler] that sleeps + ** for a specified amount of time when a table is locked. ^The handler + ** will sleep multiple times until at least "ms" milliseconds of sleeping + ** have accumulated. ^After at least "ms" milliseconds of sleeping, + ** the handler returns 0 which causes [sqlite3_step()] to return + ** [SQLITE_BUSY]. + ** + ** ^Calling this routine with an argument less than or equal to zero + ** turns off all busy handlers. + ** + ** ^(There can only be a single busy handler for a particular + ** [database connection] at any given moment. If another busy handler + ** was defined (using [sqlite3_busy_handler()]) prior to calling + ** this routine, that other busy handler is cleared.)^ + ** + ** See also: [PRAGMA busy_timeout] + */ + SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); + + /* + ** CAPI3REF: Convenience Routines For Running Queries + ** METHOD: sqlite3 + ** + ** This is a legacy interface that is preserved for backwards compatibility. + ** Use of this interface is not recommended. + ** + ** Definition: A result table is memory data structure created by the + ** [sqlite3_get_table()] interface. A result table records the + ** complete query results from one or more queries. + ** + ** The table conceptually has a number of rows and columns. But + ** these numbers are not part of the result table itself. These + ** numbers are obtained separately. Let N be the number of rows + ** and M be the number of columns. + ** + ** A result table is an array of pointers to zero-terminated UTF-8 strings. + ** There are (N+1)*M elements in the array. The first M pointers point + ** to zero-terminated strings that contain the names of the columns. + ** The remaining entries all point to query results. NULL values result + ** in NULL pointers. All other values are in their UTF-8 zero-terminated + ** string representation as returned by [sqlite3_column_text()]. + ** + ** A result table might consist of one or more memory allocations. + ** It is not safe to pass a result table directly to [sqlite3_free()]. + ** A result table should be deallocated using [sqlite3_free_table()]. + ** + ** ^(As an example of the result table format, suppose a query result + ** is as follows: + ** + **
    +    **        Name        | Age
    +    **        -----------------------
    +    **        Alice       | 43
    +    **        Bob         | 28
    +    **        Cindy       | 21
    +    ** 
    + ** + ** There are two columns (M==2) and three rows (N==3). Thus the + ** result table has 8 entries. Suppose the result table is stored + ** in an array named azResult. Then azResult holds this content: + ** + **
    +    **        azResult[0] = "Name";
    +    **        azResult[1] = "Age";
    +    **        azResult[2] = "Alice";
    +    **        azResult[3] = "43";
    +    **        azResult[4] = "Bob";
    +    **        azResult[5] = "28";
    +    **        azResult[6] = "Cindy";
    +    **        azResult[7] = "21";
    +    ** 
    )^ + ** + ** ^The sqlite3_get_table() function evaluates one or more + ** semicolon-separated SQL statements in the zero-terminated UTF-8 + ** string of its 2nd parameter and returns a result table to the + ** pointer given in its 3rd parameter. + ** + ** After the application has finished with the result from sqlite3_get_table(), + ** it must pass the result table pointer to sqlite3_free_table() in order to + ** release the memory that was malloced. Because of the way the + ** [sqlite3_malloc()] happens within sqlite3_get_table(), the calling + ** function must not try to call [sqlite3_free()] directly. Only + ** [sqlite3_free_table()] is able to release the memory properly and safely. + ** + ** The sqlite3_get_table() interface is implemented as a wrapper around + ** [sqlite3_exec()]. The sqlite3_get_table() routine does not have access + ** to any internal data structures of SQLite. It uses only the public + ** interface defined here. As a consequence, errors that occur in the + ** wrapper layer outside of the internal [sqlite3_exec()] call are not + ** reflected in subsequent calls to [sqlite3_errcode()] or + ** [sqlite3_errmsg()]. + */ + SQLITE_API int sqlite3_get_table(sqlite3* db, /* An open database */ + const char* zSql, /* SQL to be evaluated */ + char*** pazResult, /* Results of the query */ + int* pnRow, /* Number of result rows written here */ + int* pnColumn, /* Number of result columns written here */ + char** pzErrmsg /* Error msg written here */ + ); + SQLITE_API void sqlite3_free_table(char** result); + + /* + ** CAPI3REF: Formatted String Printing Functions + ** + ** These routines are work-alikes of the "printf()" family of functions + ** from the standard C library. + ** These routines understand most of the common formatting options from + ** the standard library printf() + ** plus some additional non-standard formats ([%q], [%Q], [%w], and [%z]). + ** See the [built-in printf()] documentation for details. + ** + ** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their + ** results into memory obtained from [sqlite3_malloc64()]. + ** The strings returned by these two routines should be + ** released by [sqlite3_free()]. ^Both routines return a + ** NULL pointer if [sqlite3_malloc64()] is unable to allocate enough + ** memory to hold the resulting string. + ** + ** ^(The sqlite3_snprintf() routine is similar to "snprintf()" from + ** the standard C library. The result is written into the + ** buffer supplied as the second parameter whose size is given by + ** the first parameter. Note that the order of the + ** first two parameters is reversed from snprintf().)^ This is an + ** historical accident that cannot be fixed without breaking + ** backwards compatibility. ^(Note also that sqlite3_snprintf() + ** returns a pointer to its buffer instead of the number of + ** characters actually written into the buffer.)^ We admit that + ** the number of characters written would be a more useful return + ** value but we cannot change the implementation of sqlite3_snprintf() + ** now without breaking compatibility. + ** + ** ^As long as the buffer size is greater than zero, sqlite3_snprintf() + ** guarantees that the buffer is always zero-terminated. ^The first + ** parameter "n" is the total size of the buffer, including space for + ** the zero terminator. So the longest string that can be completely + ** written will be n-1 characters. + ** + ** ^The sqlite3_vsnprintf() routine is a varargs version of sqlite3_snprintf(). + ** + ** See also: [built-in printf()], [printf() SQL function] + */ + SQLITE_API char* sqlite3_mprintf(const char*, ...); + SQLITE_API char* sqlite3_vmprintf(const char*, va_list); + SQLITE_API char* sqlite3_snprintf(int, char*, const char*, ...); + SQLITE_API char* sqlite3_vsnprintf(int, char*, const char*, va_list); + + /* + ** CAPI3REF: Memory Allocation Subsystem + ** + ** The SQLite core uses these three routines for all of its own + ** internal memory allocation needs. "Core" in the previous sentence + ** does not include operating-system specific [VFS] implementation. The + ** Windows VFS uses native malloc() and free() for some operations. + ** + ** ^The sqlite3_malloc() routine returns a pointer to a block + ** of memory at least N bytes in length, where N is the parameter. + ** ^If sqlite3_malloc() is unable to obtain sufficient free + ** memory, it returns a NULL pointer. ^If the parameter N to + ** sqlite3_malloc() is zero or negative then sqlite3_malloc() returns + ** a NULL pointer. + ** + ** ^The sqlite3_malloc64(N) routine works just like + ** sqlite3_malloc(N) except that N is an unsigned 64-bit integer instead + ** of a signed 32-bit integer. + ** + ** ^Calling sqlite3_free() with a pointer previously returned + ** by sqlite3_malloc() or sqlite3_realloc() releases that memory so + ** that it might be reused. ^The sqlite3_free() routine is + ** a no-op if is called with a NULL pointer. Passing a NULL pointer + ** to sqlite3_free() is harmless. After being freed, memory + ** should neither be read nor written. Even reading previously freed + ** memory might result in a segmentation fault or other severe error. + ** Memory corruption, a segmentation fault, or other severe error + ** might result if sqlite3_free() is called with a non-NULL pointer that + ** was not obtained from sqlite3_malloc() or sqlite3_realloc(). + ** + ** ^The sqlite3_realloc(X,N) interface attempts to resize a + ** prior memory allocation X to be at least N bytes. + ** ^If the X parameter to sqlite3_realloc(X,N) + ** is a NULL pointer then its behavior is identical to calling + ** sqlite3_malloc(N). + ** ^If the N parameter to sqlite3_realloc(X,N) is zero or + ** negative then the behavior is exactly the same as calling + ** sqlite3_free(X). + ** ^sqlite3_realloc(X,N) returns a pointer to a memory allocation + ** of at least N bytes in size or NULL if insufficient memory is available. + ** ^If M is the size of the prior allocation, then min(N,M) bytes + ** of the prior allocation are copied into the beginning of buffer returned + ** by sqlite3_realloc(X,N) and the prior allocation is freed. + ** ^If sqlite3_realloc(X,N) returns NULL and N is positive, then the + ** prior allocation is not freed. + ** + ** ^The sqlite3_realloc64(X,N) interfaces works the same as + ** sqlite3_realloc(X,N) except that N is a 64-bit unsigned integer instead + ** of a 32-bit signed integer. + ** + ** ^If X is a memory allocation previously obtained from sqlite3_malloc(), + ** sqlite3_malloc64(), sqlite3_realloc(), or sqlite3_realloc64(), then + ** sqlite3_msize(X) returns the size of that memory allocation in bytes. + ** ^The value returned by sqlite3_msize(X) might be larger than the number + ** of bytes requested when X was allocated. ^If X is a NULL pointer then + ** sqlite3_msize(X) returns zero. If X points to something that is not + ** the beginning of memory allocation, or if it points to a formerly + ** valid memory allocation that has now been freed, then the behavior + ** of sqlite3_msize(X) is undefined and possibly harmful. + ** + ** ^The memory returned by sqlite3_malloc(), sqlite3_realloc(), + ** sqlite3_malloc64(), and sqlite3_realloc64() + ** is always aligned to at least an 8 byte boundary, or to a + ** 4 byte boundary if the [SQLITE_4_BYTE_ALIGNED_MALLOC] compile-time + ** option is used. + ** + ** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()] + ** must be either NULL or else pointers obtained from a prior + ** invocation of [sqlite3_malloc()] or [sqlite3_realloc()] that have + ** not yet been released. + ** + ** The application must not read or write any part of + ** a block of memory after it has been released using + ** [sqlite3_free()] or [sqlite3_realloc()]. + */ + SQLITE_API void* sqlite3_malloc(int); + SQLITE_API void* sqlite3_malloc64(sqlite3_uint64); + SQLITE_API void* sqlite3_realloc(void*, int); + SQLITE_API void* sqlite3_realloc64(void*, sqlite3_uint64); + SQLITE_API void sqlite3_free(void*); + SQLITE_API sqlite3_uint64 sqlite3_msize(void*); + + /* + ** CAPI3REF: Memory Allocator Statistics + ** + ** SQLite provides these two interfaces for reporting on the status + ** of the [sqlite3_malloc()], [sqlite3_free()], and [sqlite3_realloc()] + ** routines, which form the built-in memory allocation subsystem. + ** + ** ^The [sqlite3_memory_used()] routine returns the number of bytes + ** of memory currently outstanding (malloced but not freed). + ** ^The [sqlite3_memory_highwater()] routine returns the maximum + ** value of [sqlite3_memory_used()] since the high-water mark + ** was last reset. ^The values returned by [sqlite3_memory_used()] and + ** [sqlite3_memory_highwater()] include any overhead + ** added by SQLite in its implementation of [sqlite3_malloc()], + ** but not overhead added by the any underlying system library + ** routines that [sqlite3_malloc()] may call. + ** + ** ^The memory high-water mark is reset to the current value of + ** [sqlite3_memory_used()] if and only if the parameter to + ** [sqlite3_memory_highwater()] is true. ^The value returned + ** by [sqlite3_memory_highwater(1)] is the high-water mark + ** prior to the reset. + */ + SQLITE_API sqlite3_int64 sqlite3_memory_used(void); + SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag); + + /* + ** CAPI3REF: Pseudo-Random Number Generator + ** + ** SQLite contains a high-quality pseudo-random number generator (PRNG) used to + ** select random [ROWID | ROWIDs] when inserting new records into a table that + ** already uses the largest possible [ROWID]. The PRNG is also used for + ** the built-in random() and randomblob() SQL functions. This interface allows + ** applications to access the same PRNG for other purposes. + ** + ** ^A call to this routine stores N bytes of randomness into buffer P. + ** ^The P parameter can be a NULL pointer. + ** + ** ^If this routine has not been previously called or if the previous + ** call had N less than one or a NULL pointer for P, then the PRNG is + ** seeded using randomness obtained from the xRandomness method of + ** the default [sqlite3_vfs] object. + ** ^If the previous call to this routine had an N of 1 or more and a + ** non-NULL P then the pseudo-randomness is generated + ** internally and without recourse to the [sqlite3_vfs] xRandomness + ** method. + */ + SQLITE_API void sqlite3_randomness(int N, void* P); + + /* + ** CAPI3REF: Compile-Time Authorization Callbacks + ** METHOD: sqlite3 + ** KEYWORDS: {authorizer callback} + ** + ** ^This routine registers an authorizer callback with a particular + ** [database connection], supplied in the first argument. + ** ^The authorizer callback is invoked as SQL statements are being compiled + ** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()], + ** [sqlite3_prepare_v3()], [sqlite3_prepare16()], [sqlite3_prepare16_v2()], + ** and [sqlite3_prepare16_v3()]. ^At various + ** points during the compilation process, as logic is being created + ** to perform various actions, the authorizer callback is invoked to + ** see if those actions are allowed. ^The authorizer callback should + ** return [SQLITE_OK] to allow the action, [SQLITE_IGNORE] to disallow the + ** specific action but allow the SQL statement to continue to be + ** compiled, or [SQLITE_DENY] to cause the entire SQL statement to be + ** rejected with an error. ^If the authorizer callback returns + ** any value other than [SQLITE_IGNORE], [SQLITE_OK], or [SQLITE_DENY] + ** then the [sqlite3_prepare_v2()] or equivalent call that triggered + ** the authorizer will fail with an error message. + ** + ** When the callback returns [SQLITE_OK], that means the operation + ** requested is ok. ^When the callback returns [SQLITE_DENY], the + ** [sqlite3_prepare_v2()] or equivalent call that triggered the + ** authorizer will fail with an error message explaining that + ** access is denied. + ** + ** ^The first parameter to the authorizer callback is a copy of the third + ** parameter to the sqlite3_set_authorizer() interface. ^The second parameter + ** to the callback is an integer [SQLITE_COPY | action code] that specifies + ** the particular action to be authorized. ^The third through sixth parameters + ** to the callback are either NULL pointers or zero-terminated strings + ** that contain additional details about the action to be authorized. + ** Applications must always be prepared to encounter a NULL pointer in any + ** of the third through the sixth parameters of the authorization callback. + ** + ** ^If the action code is [SQLITE_READ] + ** and the callback returns [SQLITE_IGNORE] then the + ** [prepared statement] statement is constructed to substitute + ** a NULL value in place of the table column that would have + ** been read if [SQLITE_OK] had been returned. The [SQLITE_IGNORE] + ** return can be used to deny an untrusted user access to individual + ** columns of a table. + ** ^When a table is referenced by a [SELECT] but no column values are + ** extracted from that table (for example in a query like + ** "SELECT count(*) FROM tab") then the [SQLITE_READ] authorizer callback + ** is invoked once for that table with a column name that is an empty string. + ** ^If the action code is [SQLITE_DELETE] and the callback returns + ** [SQLITE_IGNORE] then the [DELETE] operation proceeds but the + ** [truncate optimization] is disabled and all rows are deleted individually. + ** + ** An authorizer is used when [sqlite3_prepare | preparing] + ** SQL statements from an untrusted source, to ensure that the SQL statements + ** do not try to access data they are not allowed to see, or that they do not + ** try to execute malicious statements that damage the database. For + ** example, an application may allow a user to enter arbitrary + ** SQL queries for evaluation by a database. But the application does + ** not want the user to be able to make arbitrary changes to the + ** database. An authorizer could then be put in place while the + ** user-entered SQL is being [sqlite3_prepare | prepared] that + ** disallows everything except [SELECT] statements. + ** + ** Applications that need to process SQL from untrusted sources + ** might also consider lowering resource limits using [sqlite3_limit()] + ** and limiting database size using the [max_page_count] [PRAGMA] + ** in addition to using an authorizer. + ** + ** ^(Only a single authorizer can be in place on a database connection + ** at a time. Each call to sqlite3_set_authorizer overrides the + ** previous call.)^ ^Disable the authorizer by installing a NULL callback. + ** The authorizer is disabled by default. + ** + ** The authorizer callback must not do anything that will modify + ** the database connection that invoked the authorizer callback. + ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their + ** database connections for the meaning of "modify" in this paragraph. + ** + ** ^When [sqlite3_prepare_v2()] is used to prepare a statement, the + ** statement might be re-prepared during [sqlite3_step()] due to a + ** schema change. Hence, the application should ensure that the + ** correct authorizer callback remains in place during the [sqlite3_step()]. + ** + ** ^Note that the authorizer callback is invoked only during + ** [sqlite3_prepare()] or its variants. Authorization is not + ** performed during statement evaluation in [sqlite3_step()], unless + ** as stated in the previous paragraph, sqlite3_step() invokes + ** sqlite3_prepare_v2() to reprepare a statement after a schema change. + */ + SQLITE_API int sqlite3_set_authorizer(sqlite3*, + int (*xAuth)(void*, int, const char*, const char*, const char*, const char*), + void* pUserData); /* ** CAPI3REF: Authorizer Return Codes @@ -3046,8 +3121,8 @@ ** Note that SQLITE_IGNORE is also used as a [conflict resolution mode] ** returned from the [sqlite3_vtab_on_conflict()] interface. */ -#define SQLITE_DENY 1 /* Abort the SQL statement with an error */ -#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */ +#define SQLITE_DENY 1 /* Abort the SQL statement with an error */ +#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */ /* ** CAPI3REF: Authorizer Action Codes @@ -3069,77 +3144,76 @@ ** top-level SQL code. */ /******************************************* 3rd ************ 4th ***********/ -#define SQLITE_CREATE_INDEX 1 /* Index Name Table Name */ -#define SQLITE_CREATE_TABLE 2 /* Table Name NULL */ -#define SQLITE_CREATE_TEMP_INDEX 3 /* Index Name Table Name */ -#define SQLITE_CREATE_TEMP_TABLE 4 /* Table Name NULL */ -#define SQLITE_CREATE_TEMP_TRIGGER 5 /* Trigger Name Table Name */ -#define SQLITE_CREATE_TEMP_VIEW 6 /* View Name NULL */ -#define SQLITE_CREATE_TRIGGER 7 /* Trigger Name Table Name */ -#define SQLITE_CREATE_VIEW 8 /* View Name NULL */ -#define SQLITE_DELETE 9 /* Table Name NULL */ -#define SQLITE_DROP_INDEX 10 /* Index Name Table Name */ -#define SQLITE_DROP_TABLE 11 /* Table Name NULL */ -#define SQLITE_DROP_TEMP_INDEX 12 /* Index Name Table Name */ -#define SQLITE_DROP_TEMP_TABLE 13 /* Table Name NULL */ -#define SQLITE_DROP_TEMP_TRIGGER 14 /* Trigger Name Table Name */ -#define SQLITE_DROP_TEMP_VIEW 15 /* View Name NULL */ -#define SQLITE_DROP_TRIGGER 16 /* Trigger Name Table Name */ -#define SQLITE_DROP_VIEW 17 /* View Name NULL */ -#define SQLITE_INSERT 18 /* Table Name NULL */ -#define SQLITE_PRAGMA 19 /* Pragma Name 1st arg or NULL */ -#define SQLITE_READ 20 /* Table Name Column Name */ -#define SQLITE_SELECT 21 /* NULL NULL */ -#define SQLITE_TRANSACTION 22 /* Operation NULL */ -#define SQLITE_UPDATE 23 /* Table Name Column Name */ -#define SQLITE_ATTACH 24 /* Filename NULL */ -#define SQLITE_DETACH 25 /* Database Name NULL */ -#define SQLITE_ALTER_TABLE 26 /* Database Name Table Name */ -#define SQLITE_REINDEX 27 /* Index Name NULL */ -#define SQLITE_ANALYZE 28 /* Table Name NULL */ -#define SQLITE_CREATE_VTABLE 29 /* Table Name Module Name */ -#define SQLITE_DROP_VTABLE 30 /* Table Name Module Name */ -#define SQLITE_FUNCTION 31 /* NULL Function Name */ -#define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */ -#define SQLITE_COPY 0 /* No longer used */ -#define SQLITE_RECURSIVE 33 /* NULL NULL */ - -/* -** CAPI3REF: Tracing And Profiling Functions -** METHOD: sqlite3 -** -** These routines are deprecated. Use the [sqlite3_trace_v2()] interface -** instead of the routines described here. -** -** These routines register callback functions that can be used for -** tracing and profiling the execution of SQL statements. -** -** ^The callback function registered by sqlite3_trace() is invoked at -** various times when an SQL statement is being run by [sqlite3_step()]. -** ^The sqlite3_trace() callback is invoked with a UTF-8 rendering of the -** SQL statement text as the statement first begins executing. -** ^(Additional sqlite3_trace() callbacks might occur -** as each triggered subprogram is entered. The callbacks for triggers -** contain a UTF-8 SQL comment that identifies the trigger.)^ -** -** The [SQLITE_TRACE_SIZE_LIMIT] compile-time option can be used to limit -** the length of [bound parameter] expansion in the output of sqlite3_trace(). -** -** ^The callback function registered by sqlite3_profile() is invoked -** as each SQL statement finishes. ^The profile callback contains -** the original statement text and an estimate of wall-clock time -** of how long that statement took to run. ^The profile callback -** time is in units of nanoseconds, however the current implementation -** is only capable of millisecond resolution so the six least significant -** digits in the time are meaningless. Future versions of SQLite -** might provide greater resolution on the profiler callback. Invoking -** either [sqlite3_trace()] or [sqlite3_trace_v2()] will cancel the -** profile callback. -*/ -SQLITE_API SQLITE_DEPRECATED void *sqlite3_trace(sqlite3*, - void(*xTrace)(void*,const char*), void*); -SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*, - void(*xProfile)(void*,const char*,sqlite3_uint64), void*); +#define SQLITE_CREATE_INDEX 1 /* Index Name Table Name */ +#define SQLITE_CREATE_TABLE 2 /* Table Name NULL */ +#define SQLITE_CREATE_TEMP_INDEX 3 /* Index Name Table Name */ +#define SQLITE_CREATE_TEMP_TABLE 4 /* Table Name NULL */ +#define SQLITE_CREATE_TEMP_TRIGGER 5 /* Trigger Name Table Name */ +#define SQLITE_CREATE_TEMP_VIEW 6 /* View Name NULL */ +#define SQLITE_CREATE_TRIGGER 7 /* Trigger Name Table Name */ +#define SQLITE_CREATE_VIEW 8 /* View Name NULL */ +#define SQLITE_DELETE 9 /* Table Name NULL */ +#define SQLITE_DROP_INDEX 10 /* Index Name Table Name */ +#define SQLITE_DROP_TABLE 11 /* Table Name NULL */ +#define SQLITE_DROP_TEMP_INDEX 12 /* Index Name Table Name */ +#define SQLITE_DROP_TEMP_TABLE 13 /* Table Name NULL */ +#define SQLITE_DROP_TEMP_TRIGGER 14 /* Trigger Name Table Name */ +#define SQLITE_DROP_TEMP_VIEW 15 /* View Name NULL */ +#define SQLITE_DROP_TRIGGER 16 /* Trigger Name Table Name */ +#define SQLITE_DROP_VIEW 17 /* View Name NULL */ +#define SQLITE_INSERT 18 /* Table Name NULL */ +#define SQLITE_PRAGMA 19 /* Pragma Name 1st arg or NULL */ +#define SQLITE_READ 20 /* Table Name Column Name */ +#define SQLITE_SELECT 21 /* NULL NULL */ +#define SQLITE_TRANSACTION 22 /* Operation NULL */ +#define SQLITE_UPDATE 23 /* Table Name Column Name */ +#define SQLITE_ATTACH 24 /* Filename NULL */ +#define SQLITE_DETACH 25 /* Database Name NULL */ +#define SQLITE_ALTER_TABLE 26 /* Database Name Table Name */ +#define SQLITE_REINDEX 27 /* Index Name NULL */ +#define SQLITE_ANALYZE 28 /* Table Name NULL */ +#define SQLITE_CREATE_VTABLE 29 /* Table Name Module Name */ +#define SQLITE_DROP_VTABLE 30 /* Table Name Module Name */ +#define SQLITE_FUNCTION 31 /* NULL Function Name */ +#define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */ +#define SQLITE_COPY 0 /* No longer used */ +#define SQLITE_RECURSIVE 33 /* NULL NULL */ + + /* + ** CAPI3REF: Tracing And Profiling Functions + ** METHOD: sqlite3 + ** + ** These routines are deprecated. Use the [sqlite3_trace_v2()] interface + ** instead of the routines described here. + ** + ** These routines register callback functions that can be used for + ** tracing and profiling the execution of SQL statements. + ** + ** ^The callback function registered by sqlite3_trace() is invoked at + ** various times when an SQL statement is being run by [sqlite3_step()]. + ** ^The sqlite3_trace() callback is invoked with a UTF-8 rendering of the + ** SQL statement text as the statement first begins executing. + ** ^(Additional sqlite3_trace() callbacks might occur + ** as each triggered subprogram is entered. The callbacks for triggers + ** contain a UTF-8 SQL comment that identifies the trigger.)^ + ** + ** The [SQLITE_TRACE_SIZE_LIMIT] compile-time option can be used to limit + ** the length of [bound parameter] expansion in the output of sqlite3_trace(). + ** + ** ^The callback function registered by sqlite3_profile() is invoked + ** as each SQL statement finishes. ^The profile callback contains + ** the original statement text and an estimate of wall-clock time + ** of how long that statement took to run. ^The profile callback + ** time is in units of nanoseconds, however the current implementation + ** is only capable of millisecond resolution so the six least significant + ** digits in the time are meaningless. Future versions of SQLite + ** might provide greater resolution on the profiler callback. Invoking + ** either [sqlite3_trace()] or [sqlite3_trace_v2()] will cancel the + ** profile callback. + */ + SQLITE_API SQLITE_DEPRECATED void* sqlite3_trace(sqlite3*, void (*xTrace)(void*, const char*), void*); + SQLITE_API SQLITE_DEPRECATED void* + sqlite3_profile(sqlite3*, void (*xProfile)(void*, const char*, sqlite3_uint64), void*); /* ** CAPI3REF: SQL Trace Event Codes @@ -3193,649 +3267,666 @@ ** and the X argument is unused. ** */ -#define SQLITE_TRACE_STMT 0x01 -#define SQLITE_TRACE_PROFILE 0x02 -#define SQLITE_TRACE_ROW 0x04 -#define SQLITE_TRACE_CLOSE 0x08 - -/* -** CAPI3REF: SQL Trace Hook -** METHOD: sqlite3 -** -** ^The sqlite3_trace_v2(D,M,X,P) interface registers a trace callback -** function X against [database connection] D, using property mask M -** and context pointer P. ^If the X callback is -** NULL or if the M mask is zero, then tracing is disabled. The -** 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(). -** -** ^The X callback is invoked whenever any of the events identified by -** mask M occur. ^The integer return value from the callback is currently -** ignored, though this may change in future releases. Callback -** implementations should return zero to ensure future compatibility. -** -** ^A trace callback is invoked with four arguments: callback(T,C,P,X). -** ^The T argument is one of the [SQLITE_TRACE] -** constants to indicate why the callback was invoked. -** ^The C argument is a copy of the context pointer. -** The P and X arguments are pointers whose meanings depend on T. -** -** The sqlite3_trace_v2() interface is intended to replace the legacy -** interfaces [sqlite3_trace()] and [sqlite3_profile()], both of which -** are deprecated. -*/ -SQLITE_API int sqlite3_trace_v2( - sqlite3*, - unsigned uMask, - int(*xCallback)(unsigned,void*,void*,void*), - void *pCtx -); - -/* -** CAPI3REF: Query Progress Callbacks -** METHOD: sqlite3 -** -** ^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 -** database connection D. An example use for this -** interface is to keep a GUI updated during a large query. -** -** ^The parameter P is passed through as the only parameter to the -** callback function X. ^The parameter N is the approximate number of -** [virtual machine instructions] that are evaluated between successive -** invocations of the callback X. ^If N is less than one then the progress -** handler is disabled. -** -** ^Only a single progress handler may be defined at one time per -** [database connection]; setting a new progress handler cancels the -** old one. ^Setting parameter X to NULL disables the progress handler. -** ^The progress handler is also disabled by setting N to a value less -** than 1. -** -** ^If the progress callback returns non-zero, the operation is -** interrupted. This feature can be used to implement a -** "Cancel" button on a GUI progress dialog box. -** -** The progress handler callback must not do anything that will modify -** the database connection that invoked the progress handler. -** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their -** database connections for the meaning of "modify" in this paragraph. -** -*/ -SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); - -/* -** CAPI3REF: Opening A New Database Connection -** CONSTRUCTOR: sqlite3 -** -** ^These routines open an SQLite database file as specified by the -** filename argument. ^The filename argument is interpreted as UTF-8 for -** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte -** order for sqlite3_open16(). ^(A [database connection] handle is usually -** returned in *ppDb, even if an error occurs. The only exception is that -** if SQLite is unable to allocate memory to hold the [sqlite3] object, -** a NULL will be written into *ppDb instead of a pointer to the [sqlite3] -** object.)^ ^(If the database is opened (and/or created) successfully, then -** [SQLITE_OK] is returned. Otherwise an [error code] is returned.)^ ^The -** [sqlite3_errmsg()] or [sqlite3_errmsg16()] routines can be used to obtain -** an English language description of the error following a failure of any -** of the sqlite3_open() routines. -** -** ^The default encoding will be UTF-8 for databases created using -** sqlite3_open() or sqlite3_open_v2(). ^The default encoding for databases -** created using sqlite3_open16() will be UTF-16 in the native byte order. -** -** Whether or not an error occurs when it is opened, resources -** associated with the [database connection] handle should be released by -** passing it to [sqlite3_close()] when it is no longer required. -** -** The sqlite3_open_v2() interface works like sqlite3_open() -** except that it accepts two additional parameters for additional control -** over the new database connection. ^(The flags parameter to -** sqlite3_open_v2() must include, at a minimum, one of the following -** three flag combinations:)^ -** -**
    -** ^(
    [SQLITE_OPEN_READONLY]
    -**
    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.
    )^ -** -** ^(
    [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
    -**
    The database is opened for reading and writing, and is created if -** it does not already exist. This is the behavior that is always used for -** sqlite3_open() and sqlite3_open16().
    )^ -**
    -** -** In addition to the required flags, the following optional flags are -** also supported: -** -**
    -** ^(
    [SQLITE_OPEN_URI]
    -**
    The filename can be interpreted as a URI if this flag is set.
    )^ -** -** ^(
    [SQLITE_OPEN_MEMORY]
    -**
    The database will be opened as an in-memory database. The database -** is named by the "filename" argument for the purposes of cache-sharing, -** if shared cache mode is enabled, but the "filename" is otherwise ignored. -**
    )^ -** -** ^(
    [SQLITE_OPEN_NOMUTEX]
    -**
    The new database connection will use the "multi-thread" -** [threading mode].)^ This means that separate threads are allowed -** to use SQLite at the same time, as long as each thread is using -** a different [database connection]. -** -** ^(
    [SQLITE_OPEN_FULLMUTEX]
    -**
    The new database connection will use the "serialized" -** [threading mode].)^ This means the multiple threads can safely -** attempt to use the same database connection at the same time. -** (Mutexes will block any actual concurrency, but in this mode -** there is no harm in trying.) -** -** ^(
    [SQLITE_OPEN_SHAREDCACHE]
    -**
    The database is opened [shared cache] enabled, overriding -** the default shared cache setting provided by -** [sqlite3_enable_shared_cache()].)^ -** -** ^(
    [SQLITE_OPEN_PRIVATECACHE]
    -**
    The database is opened [shared cache] disabled, overriding -** the default shared cache setting provided by -** [sqlite3_enable_shared_cache()].)^ -** -** [[OPEN_NOFOLLOW]] ^(
    [SQLITE_OPEN_NOFOLLOW]
    -**
    The database filename is not allowed to be 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. -** -** ^The fourth parameter to sqlite3_open_v2() is the name of the -** [sqlite3_vfs] object that defines the operating system interface that -** the new database connection should use. ^If the fourth parameter is -** a NULL pointer then the default [sqlite3_vfs] object is used. -** -** ^If the filename is ":memory:", then a private, temporary in-memory database -** is created for the connection. ^This in-memory database will vanish when -** the database connection is closed. Future versions of SQLite might -** make use of additional special filenames that begin with the ":" character. -** It is recommended that when a database filename actually does begin with -** a ":" character you should prefix the filename with a pathname such as -** "./" to avoid ambiguity. -** -** ^If the filename is an empty string, then a private, temporary -** on-disk database will be created. ^This private database will be -** automatically deleted as soon as the database connection is closed. -** -** [[URI filenames in sqlite3_open()]]

    URI Filenames

    -** -** ^If [URI filename] interpretation is enabled, and the filename argument -** begins with "file:", then the filename is interpreted as a URI. ^URI -** filename interpretation is enabled if the [SQLITE_OPEN_URI] flag is -** set in the third argument to sqlite3_open_v2(), or if it has -** been enabled globally using the [SQLITE_CONFIG_URI] option with the -** [sqlite3_config()] method or by the [SQLITE_USE_URI] compile-time option. -** URI filename interpretation is turned off -** by default, but future releases of SQLite might enable URI filename -** interpretation by default. See "[URI filenames]" for additional -** information. -** -** URI filenames are parsed according to RFC 3986. ^If the URI contains an -** authority, then it must be either an empty string or the string -** "localhost". ^If the authority is not an empty string or "localhost", an -** error is returned to the caller. ^The fragment component of a URI, if -** present, is ignored. -** -** ^SQLite uses the path component of the URI as the name of the disk file -** which contains the database. ^If the path begins with a '/' character, -** then it is interpreted as an absolute path. ^If the path does not begin -** with a '/' (meaning that the authority section is omitted from the URI) -** then the path is interpreted as a relative path. -** ^(On windows, the first component of an absolute path -** is a drive specification (e.g. "C:").)^ -** -** [[core URI query parameters]] -** The query component of a URI may contain parameters that are interpreted -** either by SQLite itself, or by a [VFS | custom VFS implementation]. -** SQLite and its built-in [VFSes] interpret the -** following query parameters: -** -**
      -**
    • vfs: ^The "vfs" parameter may be used to specify the name of -** a VFS object that provides the operating system interface that should -** be used to access the database file on disk. ^If this option is set to -** an empty string the default VFS object is used. ^Specifying an unknown -** VFS is an error. ^If sqlite3_open_v2() is used and the vfs option is -** present, then the VFS specified by the option takes precedence over -** the value passed as the fourth parameter to sqlite3_open_v2(). -** -**
    • mode: ^(The mode parameter may be set to either "ro", "rw", -** "rwc", or "memory". Attempting to set it to any other value is -** an error)^. -** ^If "ro" is specified, then the database is opened for read-only -** access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the -** third argument to sqlite3_open_v2(). ^If the mode option is set to -** "rw", then the database is opened for read-write (but not create) -** access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had -** been set. ^Value "rwc" is equivalent to setting both -** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If the mode option is -** set to "memory" then a pure [in-memory database] that never reads -** or writes from disk is used. ^It is an error to specify a value for -** the mode parameter that is less restrictive than that specified by -** the flags passed in the third parameter to sqlite3_open_v2(). -** -**
    • cache: ^The cache parameter may be set to either "shared" or -** "private". ^Setting it to "shared" is equivalent to setting the -** SQLITE_OPEN_SHAREDCACHE bit in the flags argument passed to -** sqlite3_open_v2(). ^Setting the cache parameter to "private" is -** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit. -** ^If sqlite3_open_v2() is used and the "cache" parameter is present in -** a URI filename, its value overrides any behavior requested by setting -** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag. -** -**
    • psow: ^The psow parameter indicates whether or not the -** [powersafe overwrite] property does or does not apply to the -** storage media on which the database file resides. -** -**
    • nolock: ^The nolock parameter is a boolean query parameter -** which if set disables file locking in rollback journal modes. This -** is useful for accessing a database on a filesystem that does not -** support locking. Caution: Database corruption might result if two -** or more processes write to the same database and any one of those -** processes uses nolock=1. -** -**
    • immutable: ^The immutable parameter is a boolean query -** parameter that indicates that the database file is stored on -** read-only media. ^When immutable is set, SQLite assumes that the -** database file cannot be changed, even by a process with higher -** privilege, and so the database is opened read-only and all locking -** and change detection is disabled. Caution: Setting the immutable -** property on a database file that does in fact change can result -** in incorrect query results and/or [SQLITE_CORRUPT] errors. -** See also: [SQLITE_IOCAP_IMMUTABLE]. -** -**
    -** -** ^Specifying an unknown parameter in the query component of a URI is not an -** error. Future versions of SQLite might understand additional query -** parameters. See "[query parameters with special meaning to SQLite]" for -** additional information. -** -** [[URI filename examples]]

    URI filename examples

    -** -**
  • -**
    URI filenames Results -**
    file:data.db -** Open the file "data.db" in the current directory. -**
    file:/home/fred/data.db
    -** file:///home/fred/data.db
    -** file://localhost/home/fred/data.db
    -** Open the database file "/home/fred/data.db". -**
    file://darkstar/home/fred/data.db -** An error. "darkstar" is not a recognized authority. -**
    -** file:///C:/Documents%20and%20Settings/fred/Desktop/data.db -** Windows only: Open the file "data.db" on fred's desktop on drive -** C:. Note that the %20 escaping in this example is not strictly -** necessary - space characters can be used literally -** in URI filenames. -**
    file:data.db?mode=ro&cache=private -** Open file "data.db" in the current directory for read-only access. -** Regardless of whether or not shared-cache mode is enabled by -** default, use a private cache. -**
    file:/home/fred/data.db?vfs=unix-dotfile -** Open file "/home/fred/data.db". Use the special VFS "unix-dotfile" -** 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. -**
    -** -** ^URI hexadecimal escape sequences (%HH) are supported within the path and -** query components of a URI. A hexadecimal escape sequence consists of a -** percent sign - "%" - followed by exactly two hexadecimal digits -** specifying an octet value. ^Before the path or query components of a -** URI filename are interpreted, they are encoded using UTF-8 and all -** hexadecimal escape sequences replaced by a single byte containing the -** corresponding octet. If this process generates an invalid UTF-8 encoding, -** the results are undefined. -** -** Note to Windows users: The encoding used for the filename argument -** of sqlite3_open() and sqlite3_open_v2() must be UTF-8, not whatever -** codepage is currently defined. Filenames containing international -** characters must be converted to UTF-8 prior to passing them into -** sqlite3_open() or sqlite3_open_v2(). -** -** Note to Windows Runtime users: The temporary directory must be set -** prior to calling sqlite3_open() or sqlite3_open_v2(). Otherwise, various -** features that require the use of temporary files may fail. -** -** See also: [sqlite3_temp_directory] -*/ -SQLITE_API int sqlite3_open( - const char *filename, /* Database filename (UTF-8) */ - sqlite3 **ppDb /* OUT: SQLite db handle */ -); -SQLITE_API int sqlite3_open16( - const void *filename, /* Database filename (UTF-16) */ - sqlite3 **ppDb /* OUT: SQLite db handle */ -); -SQLITE_API int sqlite3_open_v2( - const char *filename, /* Database filename (UTF-8) */ - sqlite3 **ppDb, /* OUT: SQLite db handle */ - int flags, /* Flags */ - const char *zVfs /* Name of VFS module to use */ -); - -/* -** CAPI3REF: Obtain Values For URI Parameters -** -** These are utility routines, useful to [VFS|custom VFS implementations], -** that check if a database file was a URI that contained a specific query -** parameter, and if so obtains the value of that query parameter. -** -** The first parameter to these interfaces (hereafter referred to -** 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 -**
    • A filename obtained from [sqlite3_db_filename()], or -**
    • A new filename constructed using [sqlite3_create_filename()]. -**
    -** If the F parameter is not one of the above, then the behavior is -** undefined and probably undesirable. Older versions of SQLite were -** more tolerant of invalid F parameters than newer versions. -** -** If F is a suitable filename (as described in the previous paragraph) -** and if P is the name of the query parameter, then -** sqlite3_uri_parameter(F,P) returns the value of the P -** parameter if it exists or a NULL pointer if P does not appear as a -** query parameter on F. If P is a query parameter of F and it -** has no explicit value, then sqlite3_uri_parameter(F,P) returns -** a pointer to an empty string. -** -** The sqlite3_uri_boolean(F,P,B) routine assumes that P is a boolean -** parameter and returns true (1) or false (0) according to the value -** of P. The sqlite3_uri_boolean(F,P,B) routine returns true (1) if the -** value of query parameter P is one of "yes", "true", or "on" in any -** case or if the value begins with a non-zero number. The -** sqlite3_uri_boolean(F,P,B) routines returns false (0) if the value of -** query parameter P is one of "no", "false", or "off" in any case or -** if the value begins with a numeric zero. If P is not a query -** parameter on F or if the value of P does not match any of the -** above, then sqlite3_uri_boolean(F,P,B) returns (B!=0). -** -** The sqlite3_uri_int64(F,P,D) routine converts the value of P into a -** 64-bit signed integer and returns that integer, or D if P does not -** exist. If the value of P is something other than an integer, then -** zero is returned. -** -** The sqlite3_uri_key(F,N) returns a pointer to the name (not -** the value) of the N-th query parameter for filename F, or a NULL -** pointer if N is less than zero or greater than the number of query -** parameters minus 1. The N value is zero-based so N should be 0 to obtain -** the name of the first query parameter, 1 for the second parameter, and -** so forth. -** -** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and -** sqlite3_uri_boolean(F,P,B) returns B. If F is not a NULL pointer and -** is not a database file pathname pointer that the SQLite core passed -** into the xOpen VFS method, then the behavior of this routine is undefined -** and probably undesirable. -** -** Beginning with SQLite [version 3.31.0] ([dateof:3.31.0]) the input F -** parameter can also be the name of a rollback journal file or WAL file -** in addition to the main database file. Prior to version 3.31.0, these -** routines would only work if F was the name of the main database file. -** When the F parameter is the name of the rollback journal or WAL file, -** it has access to all the same query parameters as were found on the -** main database file. -** -** 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); - -/* -** CAPI3REF: Translate filenames -** -** These routines are available to [VFS|custom VFS implementations] for -** translating filenames between the main database file, the journal file, -** and the WAL file. -** -** If F is the name of an sqlite database file, journal file, or WAL file -** passed by the SQLite core into the VFS, then sqlite3_filename_database(F) -** returns the name of the corresponding database file. -** -** If F is the name of an sqlite database file, journal file, or WAL file -** passed by the SQLite core into the VFS, or if F is a database filename -** obtained from [sqlite3_db_filename()], then sqlite3_filename_journal(F) -** returns the name of the corresponding rollback journal file. -** -** If F is the name of an sqlite database file, journal file, or WAL file -** that was passed by the SQLite core into the VFS, or if F is a database -** filename obtained from [sqlite3_db_filename()], then -** sqlite3_filename_wal(F) returns the name of the corresponding -** WAL file. -** -** In all of the above, if F is not the name of a database, journal or WAL -** filename passed into the VFS from the SQLite core and F is not the -** 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*); - -/* -** CAPI3REF: Database File Corresponding To A Journal -** -** ^If X is the name of a rollback or WAL-mode journal file that is -** passed into the xOpen method of [sqlite3_vfs], then -** sqlite3_database_file_object(X) returns a pointer to the [sqlite3_file] -** object that represents the main database file. -** -** This routine is intended for use in custom [VFS] implementations -** only. It is not a general-purpose interface. -** The argument sqlite3_file_object(X) must be a filename pointer that -** has been passed into [sqlite3_vfs].xOpen method where the -** flags parameter to xOpen contains one of the bits -** [SQLITE_OPEN_MAIN_JOURNAL] or [SQLITE_OPEN_WAL]. Any other use -** of this routine results in undefined and probably undesirable -** behavior. -*/ -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 -** 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 -** sqlite3_create_filename(D,J,W,N,P) is a pointer to a database filename that -** is safe to pass to routines like: -**
      -**
    • [sqlite3_uri_parameter()], -**
    • [sqlite3_uri_boolean()], -**
    • [sqlite3_uri_int64()], -**
    • [sqlite3_uri_key()], -**
    • [sqlite3_filename_database()], -**
    • [sqlite3_filename_journal()], or -**
    • [sqlite3_filename_wal()]. -**
    -** If a memory allocation error occurs, sqlite3_create_filename() might -** return a NULL pointer. The memory obtained from sqlite3_create_filename(X) -** must be released by a corresponding call to sqlite3_free_filename(Y). -** -** The P parameter in sqlite3_create_filename(D,J,W,N,P) should be an array -** of 2*N pointers to strings. Each pair of pointers in this array corresponds -** to a key and value for a query parameter. The P parameter may be a NULL -** pointer if N is zero. None of the 2*N pointers in the P array may be -** NULL pointers and key pointers should not be empty strings. -** None of the D, J, or W parameters to sqlite3_create_filename(D,J,W,N,P) may -** be NULL pointers, though they can be empty strings. -** -** The sqlite3_free_filename(Y) routine releases a memory allocation -** previously obtained from sqlite3_create_filename(). Invoking -** sqlite3_free_filename(Y) where Y is a NULL pointer is a harmless no-op. -** -** 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 -** 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( - const char *zDatabase, - const char *zJournal, - const char *zWal, - int nParam, - const char **azParam -); -SQLITE_API void sqlite3_free_filename(char*); - -/* -** CAPI3REF: Error Codes And Messages -** METHOD: sqlite3 -** -** ^If the most recent sqlite3_* API call associated with -** [database connection] D failed, then the sqlite3_errcode(D) interface -** returns the numeric [result code] or [extended result code] for that -** API call. -** ^The sqlite3_extended_errcode() -** interface is the same except that it always returns the -** [extended result code] even when extended result codes are -** disabled. -** -** The values returned by sqlite3_errcode() and/or -** 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: -** -**
      -**
    • sqlite3_errcode() -**
    • sqlite3_extended_errcode() -**
    • sqlite3_errmsg() -**
    • sqlite3_errmsg16() -**
    -** -** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language -** text that describes the error, as either UTF-8 or UTF-16 respectively. -** ^(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. -** ^(Memory to hold the error message string is managed internally -** and must not be freed by the application)^. -** -** 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. -** When that happens, the second error will be reported since these -** interfaces always report the most recent result. To avoid -** this, each thread can obtain exclusive use of the [database connection] D -** by invoking [sqlite3_mutex_enter]([sqlite3_db_mutex](D)) before beginning -** to use D and invoking [sqlite3_mutex_leave]([sqlite3_db_mutex](D)) after -** all calls to the interfaces listed here are completed. -** -** If an interface fails with SQLITE_MISUSE, that means the interface -** was invoked incorrectly by the application. In that case, the -** error code and message may or may not be set. -*/ -SQLITE_API int sqlite3_errcode(sqlite3 *db); -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); - -/* -** CAPI3REF: Prepared Statement Object -** KEYWORDS: {prepared statement} {prepared statements} -** -** An instance of this object represents a single SQL statement that -** has been compiled into binary form and is ready to be evaluated. -** -** Think of each SQL statement as a separate computer program. The -** original SQL text is source code. A prepared statement object -** is the compiled object code. All SQL must be converted into a -** prepared statement before it can be run. -** -** The life-cycle of a prepared statement object usually goes like this: -** -**
      -**
    1. Create the prepared statement object using [sqlite3_prepare_v2()]. -**
    2. Bind values to [parameters] using the sqlite3_bind_*() -** interfaces. -**
    3. Run the SQL by calling [sqlite3_step()] one or more times. -**
    4. Reset the prepared statement using [sqlite3_reset()] then go back -** to step 2. Do this zero or more times. -**
    5. Destroy the object using [sqlite3_finalize()]. -**
    -*/ -typedef struct sqlite3_stmt sqlite3_stmt; - -/* -** CAPI3REF: Run-time Limits -** METHOD: sqlite3 -** -** ^(This interface allows the size of various constructs to be limited -** on a connection by connection basis. The first parameter is the -** [database connection] whose limit is to be set or queried. The -** second parameter is one of the [limit categories] that define a -** class of constructs to be size limited. The third parameter is the -** new limit for that construct.)^ -** -** ^If the new limit is a negative number, the limit is unchanged. -** ^(For each limit category SQLITE_LIMIT_NAME there is a -** [limits | hard upper bound] -** set at compile-time by a C preprocessor macro called -** [limits | SQLITE_MAX_NAME]. -** (The "_LIMIT_" in the name is changed to "_MAX_".))^ -** ^Attempts to increase a limit above its hard upper bound are -** silently truncated to the hard upper bound. -** -** ^Regardless of whether or not the limit was changed, the -** [sqlite3_limit()] interface returns the prior value of the limit. -** ^Hence, to find the current value of a limit without changing it, -** simply invoke this interface with the third parameter set to -1. -** -** Run-time limits are intended for use in applications that manage -** both their own internal database and also databases that are controlled -** by untrusted external sources. An example application might be a -** web browser that has its own databases for storing history and -** separate databases controlled by JavaScript applications downloaded -** off the Internet. The internal databases can be given the -** large, default limits. Databases managed by external sources can -** be given much smaller limits designed to prevent a denial of service -** attack. Developers might also want to use the [sqlite3_set_authorizer()] -** interface to further control untrusted SQL. The size of the database -** created by an untrusted script can be contained using the -** [max_page_count] [PRAGMA]. -** -** New run-time limit categories may be added in future releases. -*/ -SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); +#define SQLITE_TRACE_STMT 0x01 +#define SQLITE_TRACE_PROFILE 0x02 +#define SQLITE_TRACE_ROW 0x04 +#define SQLITE_TRACE_CLOSE 0x08 + + /* + ** CAPI3REF: SQL Trace Hook + ** METHOD: sqlite3 + ** + ** ^The sqlite3_trace_v2(D,M,X,P) interface registers a trace callback + ** function X against [database connection] D, using property mask M + ** and context pointer P. ^If the X callback is + ** NULL or if the M mask is zero, then tracing is disabled. The + ** 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(). + ** + ** ^The X callback is invoked whenever any of the events identified by + ** mask M occur. ^The integer return value from the callback is currently + ** ignored, though this may change in future releases. Callback + ** implementations should return zero to ensure future compatibility. + ** + ** ^A trace callback is invoked with four arguments: callback(T,C,P,X). + ** ^The T argument is one of the [SQLITE_TRACE] + ** constants to indicate why the callback was invoked. + ** ^The C argument is a copy of the context pointer. + ** The P and X arguments are pointers whose meanings depend on T. + ** + ** The sqlite3_trace_v2() interface is intended to replace the legacy + ** interfaces [sqlite3_trace()] and [sqlite3_profile()], both of which + ** are deprecated. + */ + SQLITE_API int + sqlite3_trace_v2(sqlite3*, unsigned uMask, int (*xCallback)(unsigned, void*, void*, void*), void* pCtx); + + /* + ** CAPI3REF: Query Progress Callbacks + ** METHOD: sqlite3 + ** + ** ^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 + ** database connection D. An example use for this + ** interface is to keep a GUI updated during a large query. + ** + ** ^The parameter P is passed through as the only parameter to the + ** callback function X. ^The parameter N is the approximate number of + ** [virtual machine instructions] that are evaluated between successive + ** invocations of the callback X. ^If N is less than one then the progress + ** handler is disabled. + ** + ** ^Only a single progress handler may be defined at one time per + ** [database connection]; setting a new progress handler cancels the + ** old one. ^Setting parameter X to NULL disables the progress handler. + ** ^The progress handler is also disabled by setting N to a value less + ** than 1. + ** + ** ^If the progress callback returns non-zero, the operation is + ** interrupted. This feature can be used to implement a + ** "Cancel" button on a GUI progress dialog box. + ** + ** The progress handler callback must not do anything that will modify + ** the database connection that invoked the progress handler. + ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their + ** database connections for the meaning of "modify" in this paragraph. + ** + */ + SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int (*)(void*), void*); + + /* + ** CAPI3REF: Opening A New Database Connection + ** CONSTRUCTOR: sqlite3 + ** + ** ^These routines open an SQLite database file as specified by the + ** filename argument. ^The filename argument is interpreted as UTF-8 for + ** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte + ** order for sqlite3_open16(). ^(A [database connection] handle is usually + ** returned in *ppDb, even if an error occurs. The only exception is that + ** if SQLite is unable to allocate memory to hold the [sqlite3] object, + ** a NULL will be written into *ppDb instead of a pointer to the [sqlite3] + ** object.)^ ^(If the database is opened (and/or created) successfully, then + ** [SQLITE_OK] is returned. Otherwise an [error code] is returned.)^ ^The + ** [sqlite3_errmsg()] or [sqlite3_errmsg16()] routines can be used to obtain + ** an English language description of the error following a failure of any + ** of the sqlite3_open() routines. + ** + ** ^The default encoding will be UTF-8 for databases created using + ** sqlite3_open() or sqlite3_open_v2(). ^The default encoding for databases + ** created using sqlite3_open16() will be UTF-16 in the native byte order. + ** + ** Whether or not an error occurs when it is opened, resources + ** associated with the [database connection] handle should be released by + ** passing it to [sqlite3_close()] when it is no longer required. + ** + ** The sqlite3_open_v2() interface works like sqlite3_open() + ** except that it accepts two additional parameters for additional control + ** over the new database connection. ^(The flags parameter to + ** sqlite3_open_v2() must include, at a minimum, one of the following + ** three flag combinations:)^ + ** + **
    + ** ^(
    [SQLITE_OPEN_READONLY]
    + **
    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.
    )^ + ** + ** ^(
    [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
    + **
    The database is opened for reading and writing, and is created if + ** it does not already exist. This is the behavior that is always used for + ** sqlite3_open() and sqlite3_open16().
    )^ + **
    + ** + ** In addition to the required flags, the following optional flags are + ** also supported: + ** + **
    + ** ^(
    [SQLITE_OPEN_URI]
    + **
    The filename can be interpreted as a URI if this flag is set.
    )^ + ** + ** ^(
    [SQLITE_OPEN_MEMORY]
    + **
    The database will be opened as an in-memory database. The database + ** is named by the "filename" argument for the purposes of cache-sharing, + ** if shared cache mode is enabled, but the "filename" is otherwise ignored. + **
    )^ + ** + ** ^(
    [SQLITE_OPEN_NOMUTEX]
    + **
    The new database connection will use the "multi-thread" + ** [threading mode].)^ This means that separate threads are allowed + ** to use SQLite at the same time, as long as each thread is using + ** a different [database connection]. + ** + ** ^(
    [SQLITE_OPEN_FULLMUTEX]
    + **
    The new database connection will use the "serialized" + ** [threading mode].)^ This means the multiple threads can safely + ** attempt to use the same database connection at the same time. + ** (Mutexes will block any actual concurrency, but in this mode + ** there is no harm in trying.) + ** + ** ^(
    [SQLITE_OPEN_SHAREDCACHE]
    + **
    The database is opened [shared cache] enabled, overriding + ** the default shared cache setting provided by + ** [sqlite3_enable_shared_cache()].)^ + ** + ** ^(
    [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 has if + ** [sqlite3_extended_result_codes(db,1)] where 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
    + **
    )^ + ** + ** 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. 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 + ** the new database connection should use. ^If the fourth parameter is + ** a NULL pointer then the default [sqlite3_vfs] object is used. + ** + ** ^If the filename is ":memory:", then a private, temporary in-memory database + ** is created for the connection. ^This in-memory database will vanish when + ** the database connection is closed. Future versions of SQLite might + ** make use of additional special filenames that begin with the ":" character. + ** It is recommended that when a database filename actually does begin with + ** a ":" character you should prefix the filename with a pathname such as + ** "./" to avoid ambiguity. + ** + ** ^If the filename is an empty string, then a private, temporary + ** on-disk database will be created. ^This private database will be + ** automatically deleted as soon as the database connection is closed. + ** + ** [[URI filenames in sqlite3_open()]]

    URI Filenames

    + ** + ** ^If [URI filename] interpretation is enabled, and the filename argument + ** begins with "file:", then the filename is interpreted as a URI. ^URI + ** filename interpretation is enabled if the [SQLITE_OPEN_URI] flag is + ** set in the third argument to sqlite3_open_v2(), or if it has + ** been enabled globally using the [SQLITE_CONFIG_URI] option with the + ** [sqlite3_config()] method or by the [SQLITE_USE_URI] compile-time option. + ** URI filename interpretation is turned off + ** by default, but future releases of SQLite might enable URI filename + ** interpretation by default. See "[URI filenames]" for additional + ** information. + ** + ** URI filenames are parsed according to RFC 3986. ^If the URI contains an + ** authority, then it must be either an empty string or the string + ** "localhost". ^If the authority is not an empty string or "localhost", an + ** error is returned to the caller. ^The fragment component of a URI, if + ** present, is ignored. + ** + ** ^SQLite uses the path component of the URI as the name of the disk file + ** which contains the database. ^If the path begins with a '/' character, + ** then it is interpreted as an absolute path. ^If the path does not begin + ** with a '/' (meaning that the authority section is omitted from the URI) + ** then the path is interpreted as a relative path. + ** ^(On windows, the first component of an absolute path + ** is a drive specification (e.g. "C:").)^ + ** + ** [[core URI query parameters]] + ** The query component of a URI may contain parameters that are interpreted + ** either by SQLite itself, or by a [VFS | custom VFS implementation]. + ** SQLite and its built-in [VFSes] interpret the + ** following query parameters: + ** + **
      + **
    • vfs: ^The "vfs" parameter may be used to specify the name of + ** a VFS object that provides the operating system interface that should + ** be used to access the database file on disk. ^If this option is set to + ** an empty string the default VFS object is used. ^Specifying an unknown + ** VFS is an error. ^If sqlite3_open_v2() is used and the vfs option is + ** present, then the VFS specified by the option takes precedence over + ** the value passed as the fourth parameter to sqlite3_open_v2(). + ** + **
    • mode: ^(The mode parameter may be set to either "ro", "rw", + ** "rwc", or "memory". Attempting to set it to any other value is + ** an error)^. + ** ^If "ro" is specified, then the database is opened for read-only + ** access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the + ** third argument to sqlite3_open_v2(). ^If the mode option is set to + ** "rw", then the database is opened for read-write (but not create) + ** access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had + ** been set. ^Value "rwc" is equivalent to setting both + ** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If the mode option is + ** set to "memory" then a pure [in-memory database] that never reads + ** or writes from disk is used. ^It is an error to specify a value for + ** the mode parameter that is less restrictive than that specified by + ** the flags passed in the third parameter to sqlite3_open_v2(). + ** + **
    • cache: ^The cache parameter may be set to either "shared" or + ** "private". ^Setting it to "shared" is equivalent to setting the + ** SQLITE_OPEN_SHAREDCACHE bit in the flags argument passed to + ** sqlite3_open_v2(). ^Setting the cache parameter to "private" is + ** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit. + ** ^If sqlite3_open_v2() is used and the "cache" parameter is present in + ** a URI filename, its value overrides any behavior requested by setting + ** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag. + ** + **
    • psow: ^The psow parameter indicates whether or not the + ** [powersafe overwrite] property does or does not apply to the + ** storage media on which the database file resides. + ** + **
    • nolock: ^The nolock parameter is a boolean query parameter + ** which if set disables file locking in rollback journal modes. This + ** is useful for accessing a database on a filesystem that does not + ** support locking. Caution: Database corruption might result if two + ** or more processes write to the same database and any one of those + ** processes uses nolock=1. + ** + **
    • immutable: ^The immutable parameter is a boolean query + ** parameter that indicates that the database file is stored on + ** read-only media. ^When immutable is set, SQLite assumes that the + ** database file cannot be changed, even by a process with higher + ** privilege, and so the database is opened read-only and all locking + ** and change detection is disabled. Caution: Setting the immutable + ** property on a database file that does in fact change can result + ** in incorrect query results and/or [SQLITE_CORRUPT] errors. + ** See also: [SQLITE_IOCAP_IMMUTABLE]. + ** + **
    + ** + ** ^Specifying an unknown parameter in the query component of a URI is not an + ** error. Future versions of SQLite might understand additional query + ** parameters. See "[query parameters with special meaning to SQLite]" for + ** additional information. + ** + ** [[URI filename examples]]

    URI filename examples

    + ** + ** + **
    URI filenames Results + **
    file:data.db + ** Open the file "data.db" in the current directory. + **
    file:/home/fred/data.db
    + ** file:///home/fred/data.db
    + ** file://localhost/home/fred/data.db
    + ** Open the database file "/home/fred/data.db". + **
    file://darkstar/home/fred/data.db + ** An error. "darkstar" is not a recognized authority. + **
    + ** file:///C:/Documents%20and%20Settings/fred/Desktop/data.db + ** Windows only: Open the file "data.db" on fred's desktop on drive + ** C:. Note that the %20 escaping in this example is not strictly + ** necessary - space characters can be used literally + ** in URI filenames. + **
    file:data.db?mode=ro&cache=private + ** Open file "data.db" in the current directory for read-only access. + ** Regardless of whether or not shared-cache mode is enabled by + ** default, use a private cache. + **
    file:/home/fred/data.db?vfs=unix-dotfile + ** Open file "/home/fred/data.db". Use the special VFS "unix-dotfile" + ** 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 + ** query components of a URI. A hexadecimal escape sequence consists of a + ** percent sign - "%" - followed by exactly two hexadecimal digits + ** specifying an octet value. ^Before the path or query components of a + ** URI filename are interpreted, they are encoded using UTF-8 and all + ** hexadecimal escape sequences replaced by a single byte containing the + ** corresponding octet. If this process generates an invalid UTF-8 encoding, + ** the results are undefined. + ** + ** Note to Windows users: The encoding used for the filename argument + ** of sqlite3_open() and sqlite3_open_v2() must be UTF-8, not whatever + ** codepage is currently defined. Filenames containing international + ** characters must be converted to UTF-8 prior to passing them into + ** sqlite3_open() or sqlite3_open_v2(). + ** + ** Note to Windows Runtime users: The temporary directory must be set + ** prior to calling sqlite3_open() or sqlite3_open_v2(). Otherwise, various + ** features that require the use of temporary files may fail. + ** + ** See also: [sqlite3_temp_directory] + */ + SQLITE_API int sqlite3_open(const char* filename, /* Database filename (UTF-8) */ + sqlite3** ppDb /* OUT: SQLite db handle */ + ); + SQLITE_API int sqlite3_open16(const void* filename, /* Database filename (UTF-16) */ + sqlite3** ppDb /* OUT: SQLite db handle */ + ); + SQLITE_API int sqlite3_open_v2(const char* filename, /* Database filename (UTF-8) */ + sqlite3** ppDb, /* OUT: SQLite db handle */ + int flags, /* Flags */ + const char* zVfs /* Name of VFS module to use */ + ); + + /* + ** CAPI3REF: Obtain Values For URI Parameters + ** + ** These are utility routines, useful to [VFS|custom VFS implementations], + ** that check if a database file was a URI that contained a specific query + ** parameter, and if so obtains the value of that query parameter. + ** + ** The first parameter to these interfaces (hereafter referred to + ** 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 + **
    • A filename obtained from [sqlite3_db_filename()], or + **
    • A new filename constructed using [sqlite3_create_filename()]. + **
    + ** If the F parameter is not one of the above, then the behavior is + ** undefined and probably undesirable. Older versions of SQLite were + ** more tolerant of invalid F parameters than newer versions. + ** + ** If F is a suitable filename (as described in the previous paragraph) + ** and if P is the name of the query parameter, then + ** sqlite3_uri_parameter(F,P) returns the value of the P + ** parameter if it exists or a NULL pointer if P does not appear as a + ** query parameter on F. If P is a query parameter of F and it + ** has no explicit value, then sqlite3_uri_parameter(F,P) returns + ** a pointer to an empty string. + ** + ** The sqlite3_uri_boolean(F,P,B) routine assumes that P is a boolean + ** parameter and returns true (1) or false (0) according to the value + ** of P. The sqlite3_uri_boolean(F,P,B) routine returns true (1) if the + ** value of query parameter P is one of "yes", "true", or "on" in any + ** case or if the value begins with a non-zero number. The + ** sqlite3_uri_boolean(F,P,B) routines returns false (0) if the value of + ** query parameter P is one of "no", "false", or "off" in any case or + ** if the value begins with a numeric zero. If P is not a query + ** parameter on F or if the value of P does not match any of the + ** above, then sqlite3_uri_boolean(F,P,B) returns (B!=0). + ** + ** The sqlite3_uri_int64(F,P,D) routine converts the value of P into a + ** 64-bit signed integer and returns that integer, or D if P does not + ** exist. If the value of P is something other than an integer, then + ** zero is returned. + ** + ** The sqlite3_uri_key(F,N) returns a pointer to the name (not + ** the value) of the N-th query parameter for filename F, or a NULL + ** pointer if N is less than zero or greater than the number of query + ** parameters minus 1. The N value is zero-based so N should be 0 to obtain + ** the name of the first query parameter, 1 for the second parameter, and + ** so forth. + ** + ** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and + ** sqlite3_uri_boolean(F,P,B) returns B. If F is not a NULL pointer and + ** is not a database file pathname pointer that the SQLite core passed + ** into the xOpen VFS method, then the behavior of this routine is undefined + ** and probably undesirable. + ** + ** Beginning with SQLite [version 3.31.0] ([dateof:3.31.0]) the input F + ** parameter can also be the name of a rollback journal file or WAL file + ** in addition to the main database file. Prior to version 3.31.0, these + ** routines would only work if F was the name of the main database file. + ** When the F parameter is the name of the rollback journal or WAL file, + ** it has access to all the same query parameters as were found on the + ** main database file. + ** + ** 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); + + /* + ** CAPI3REF: Translate filenames + ** + ** These routines are available to [VFS|custom VFS implementations] for + ** translating filenames between the main database file, the journal file, + ** and the WAL file. + ** + ** If F is the name of an sqlite database file, journal file, or WAL file + ** passed by the SQLite core into the VFS, then sqlite3_filename_database(F) + ** returns the name of the corresponding database file. + ** + ** If F is the name of an sqlite database file, journal file, or WAL file + ** passed by the SQLite core into the VFS, or if F is a database filename + ** obtained from [sqlite3_db_filename()], then sqlite3_filename_journal(F) + ** returns the name of the corresponding rollback journal file. + ** + ** If F is the name of an sqlite database file, journal file, or WAL file + ** that was passed by the SQLite core into the VFS, or if F is a database + ** filename obtained from [sqlite3_db_filename()], then + ** sqlite3_filename_wal(F) returns the name of the corresponding + ** WAL file. + ** + ** In all of the above, if F is not the name of a database, journal or WAL + ** filename passed into the VFS from the SQLite core and F is not the + ** 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*); + + /* + ** CAPI3REF: Database File Corresponding To A Journal + ** + ** ^If X is the name of a rollback or WAL-mode journal file that is + ** passed into the xOpen method of [sqlite3_vfs], then + ** sqlite3_database_file_object(X) returns a pointer to the [sqlite3_file] + ** object that represents the main database file. + ** + ** This routine is intended for use in custom [VFS] implementations + ** only. It is not a general-purpose interface. + ** The argument sqlite3_file_object(X) must be a filename pointer that + ** has been passed into [sqlite3_vfs].xOpen method where the + ** flags parameter to xOpen contains one of the bits + ** [SQLITE_OPEN_MAIN_JOURNAL] or [SQLITE_OPEN_WAL]. Any other use + ** of this routine results in undefined and probably undesirable + ** behavior. + */ + 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 + ** 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 + ** sqlite3_create_filename(D,J,W,N,P) is a pointer to a database filename that + ** is safe to pass to routines like: + **
      + **
    • [sqlite3_uri_parameter()], + **
    • [sqlite3_uri_boolean()], + **
    • [sqlite3_uri_int64()], + **
    • [sqlite3_uri_key()], + **
    • [sqlite3_filename_database()], + **
    • [sqlite3_filename_journal()], or + **
    • [sqlite3_filename_wal()]. + **
    + ** If a memory allocation error occurs, sqlite3_create_filename() might + ** return a NULL pointer. The memory obtained from sqlite3_create_filename(X) + ** must be released by a corresponding call to sqlite3_free_filename(Y). + ** + ** The P parameter in sqlite3_create_filename(D,J,W,N,P) should be an array + ** of 2*N pointers to strings. Each pair of pointers in this array corresponds + ** to a key and value for a query parameter. The P parameter may be a NULL + ** pointer if N is zero. None of the 2*N pointers in the P array may be + ** NULL pointers and key pointers should not be empty strings. + ** None of the D, J, or W parameters to sqlite3_create_filename(D,J,W,N,P) may + ** be NULL pointers, though they can be empty strings. + ** + ** The sqlite3_free_filename(Y) routine releases a memory allocation + ** previously obtained from sqlite3_create_filename(). Invoking + ** sqlite3_free_filename(Y) where Y is a NULL pointer is a harmless no-op. + ** + ** 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 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(const char* zDatabase, + const char* zJournal, + const char* zWal, + int nParam, + const char** azParam); + SQLITE_API void sqlite3_free_filename(char*); + + /* + ** CAPI3REF: Error Codes And Messages + ** METHOD: sqlite3 + ** + ** ^If the most recent sqlite3_* API call associated with + ** [database connection] D failed, then the sqlite3_errcode(D) interface + ** returns the numeric [result code] or [extended result code] for that + ** API call. + ** ^The sqlite3_extended_errcode() + ** interface is the same except that it always returns the + ** [extended result code] even when extended result codes are + ** disabled. + ** + ** The values returned by sqlite3_errcode() and/or + ** 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 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. + ** ^(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. + ** ^(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. + ** When that happens, the second error will be reported since these + ** interfaces always report the most recent result. To avoid + ** this, each thread can obtain exclusive use of the [database connection] D + ** by invoking [sqlite3_mutex_enter]([sqlite3_db_mutex](D)) before beginning + ** to use D and invoking [sqlite3_mutex_leave]([sqlite3_db_mutex](D)) after + ** all calls to the interfaces listed here are completed. + ** + ** If an interface fails with SQLITE_MISUSE, that means the interface + ** was invoked incorrectly by the application. In that case, the + ** error code and message may or may not be set. + */ + SQLITE_API int sqlite3_errcode(sqlite3* db); + 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 + ** KEYWORDS: {prepared statement} {prepared statements} + ** + ** An instance of this object represents a single SQL statement that + ** has been compiled into binary form and is ready to be evaluated. + ** + ** Think of each SQL statement as a separate computer program. The + ** original SQL text is source code. A prepared statement object + ** is the compiled object code. All SQL must be converted into a + ** prepared statement before it can be run. + ** + ** The life-cycle of a prepared statement object usually goes like this: + ** + **
      + **
    1. Create the prepared statement object using [sqlite3_prepare_v2()]. + **
    2. Bind values to [parameters] using the sqlite3_bind_*() + ** interfaces. + **
    3. Run the SQL by calling [sqlite3_step()] one or more times. + **
    4. Reset the prepared statement using [sqlite3_reset()] then go back + ** to step 2. Do this zero or more times. + **
    5. Destroy the object using [sqlite3_finalize()]. + **
    + */ + typedef struct sqlite3_stmt sqlite3_stmt; + + /* + ** CAPI3REF: Run-time Limits + ** METHOD: sqlite3 + ** + ** ^(This interface allows the size of various constructs to be limited + ** on a connection by connection basis. The first parameter is the + ** [database connection] whose limit is to be set or queried. The + ** second parameter is one of the [limit categories] that define a + ** class of constructs to be size limited. The third parameter is the + ** new limit for that construct.)^ + ** + ** ^If the new limit is a negative number, the limit is unchanged. + ** ^(For each limit category SQLITE_LIMIT_NAME there is a + ** [limits | hard upper bound] + ** set at compile-time by a C preprocessor macro called + ** [limits | SQLITE_MAX_NAME]. + ** (The "_LIMIT_" in the name is changed to "_MAX_".))^ + ** ^Attempts to increase a limit above its hard upper bound are + ** silently truncated to the hard upper bound. + ** + ** ^Regardless of whether or not the limit was changed, the + ** [sqlite3_limit()] interface returns the prior value of the limit. + ** ^Hence, to find the current value of a limit without changing it, + ** simply invoke this interface with the third parameter set to -1. + ** + ** Run-time limits are intended for use in applications that manage + ** both their own internal database and also databases that are controlled + ** by untrusted external sources. An example application might be a + ** web browser that has its own databases for storing history and + ** separate databases controlled by JavaScript applications downloaded + ** off the Internet. The internal databases can be given the + ** large, default limits. Databases managed by external sources can + ** be given much smaller limits designed to prevent a denial of service + ** attack. Developers might also want to use the [sqlite3_set_authorizer()] + ** interface to further control untrusted SQL. The size of the database + ** created by an untrusted script can be contained using the + ** [max_page_count] [PRAGMA]. + ** + ** New run-time limit categories may be added in future releases. + */ + SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); /* ** CAPI3REF: Run-Time Limit Categories @@ -3893,18 +3984,18 @@ ** [prepared statement] may start.
    )^ **
    */ -#define SQLITE_LIMIT_LENGTH 0 -#define SQLITE_LIMIT_SQL_LENGTH 1 -#define SQLITE_LIMIT_COLUMN 2 -#define SQLITE_LIMIT_EXPR_DEPTH 3 -#define SQLITE_LIMIT_COMPOUND_SELECT 4 -#define SQLITE_LIMIT_VDBE_OP 5 -#define SQLITE_LIMIT_FUNCTION_ARG 6 -#define SQLITE_LIMIT_ATTACHED 7 -#define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8 -#define SQLITE_LIMIT_VARIABLE_NUMBER 9 -#define SQLITE_LIMIT_TRIGGER_DEPTH 10 -#define SQLITE_LIMIT_WORKER_THREADS 11 +#define SQLITE_LIMIT_LENGTH 0 +#define SQLITE_LIMIT_SQL_LENGTH 1 +#define SQLITE_LIMIT_COLUMN 2 +#define SQLITE_LIMIT_EXPR_DEPTH 3 +#define SQLITE_LIMIT_COMPOUND_SELECT 4 +#define SQLITE_LIMIT_VDBE_OP 5 +#define SQLITE_LIMIT_FUNCTION_ARG 6 +#define SQLITE_LIMIT_ATTACHED 7 +#define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8 +#define SQLITE_LIMIT_VARIABLE_NUMBER 9 +#define SQLITE_LIMIT_TRIGGER_DEPTH 10 +#define SQLITE_LIMIT_WORKER_THREADS 11 /* ** CAPI3REF: Prepare Flags @@ -3941,783 +4032,800 @@ ** any virtual tables. ** */ -#define SQLITE_PREPARE_PERSISTENT 0x01 -#define SQLITE_PREPARE_NORMALIZE 0x02 -#define SQLITE_PREPARE_NO_VTAB 0x04 +#define SQLITE_PREPARE_PERSISTENT 0x01 +#define SQLITE_PREPARE_NORMALIZE 0x02 +#define SQLITE_PREPARE_NO_VTAB 0x04 + + /* + ** CAPI3REF: Compiling An SQL Statement + ** KEYWORDS: {SQL statement compiler} + ** METHOD: sqlite3 + ** CONSTRUCTOR: sqlite3_stmt + ** + ** To execute an SQL statement, it must first be compiled into a byte-code + ** program using one of these routines. Or, in other words, these routines + ** are constructors for the [prepared statement] object. + ** + ** The preferred routine to use is [sqlite3_prepare_v2()]. The + ** [sqlite3_prepare()] interface is legacy and should be avoided. + ** [sqlite3_prepare_v3()] has an extra "prepFlags" option that is used + ** for special purposes. + ** + ** The use of the UTF-8 interfaces is preferred, as SQLite currently + ** does all parsing using UTF-8. The UTF-16 interfaces are provided + ** as a convenience. The UTF-16 interfaces work by converting the + ** input text into UTF-8, then invoking the corresponding UTF-8 interface. + ** + ** The first argument, "db", is a [database connection] obtained from a + ** prior successful call to [sqlite3_open()], [sqlite3_open_v2()] or + ** [sqlite3_open16()]. The database connection must not have been closed. + ** + ** The second argument, "zSql", is the statement to be compiled, encoded + ** as either UTF-8 or UTF-16. The sqlite3_prepare(), sqlite3_prepare_v2(), + ** and sqlite3_prepare_v3() + ** interfaces use UTF-8, and sqlite3_prepare16(), sqlite3_prepare16_v2(), + ** 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 + ** 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. + ** + ** ^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 + ** compile the first statement in zSql, so *pzTail is left pointing to + ** what remains uncompiled. + ** + ** ^*ppStmt is left pointing to a compiled [prepared statement] that can be + ** executed using [sqlite3_step()]. ^If there is an error, *ppStmt is set + ** to NULL. ^If the input text contains no SQL (if the input is an empty + ** string or a comment) then *ppStmt is set to NULL. + ** The calling procedure is responsible for deleting the compiled + ** SQL statement using [sqlite3_finalize()] after it has finished with it. + ** ppStmt may not be NULL. + ** + ** ^On success, the sqlite3_prepare() family of routines return [SQLITE_OK]; + ** otherwise an [error code] is returned. + ** + ** The sqlite3_prepare_v2(), sqlite3_prepare_v3(), sqlite3_prepare16_v2(), + ** and sqlite3_prepare16_v3() interfaces are recommended for all new programs. + ** The older interfaces (sqlite3_prepare() and sqlite3_prepare16()) + ** are retained for backwards compatibility, but their use is discouraged. + ** ^In the "vX" interfaces, the prepared statement + ** that is returned (the [sqlite3_stmt] object) contains a copy of the + ** original SQL text. This causes the [sqlite3_step()] interface to + ** behave differently in three ways: + ** + **
      + **
    1. + ** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it + ** always used to do, [sqlite3_step()] will automatically recompile the SQL + ** statement and try to run it again. As many as [SQLITE_MAX_SCHEMA_RETRY] + ** retries will occur before sqlite3_step() gives up and returns an error. + **
    2. + ** + **
    3. + ** ^When an error occurs, [sqlite3_step()] will return one of the detailed + ** [error codes] or [extended error codes]. ^The legacy behavior was that + ** [sqlite3_step()] would only return a generic [SQLITE_ERROR] result code + ** and the application would have to make a second call to [sqlite3_reset()] + ** in order to find the underlying cause of the problem. With the "v2" prepare + ** interfaces, the underlying reason for the error is returned immediately. + **
    4. + ** + **
    5. + ** ^If the specific value bound to a [parameter | host parameter] in the + ** WHERE clause might influence the choice of query plan for a statement, + ** then the statement will be automatically recompiled, as if there had been + ** a schema change, on the first [sqlite3_step()] call following any change + ** to the [sqlite3_bind_text | bindings] of that [parameter]. + ** ^The specific value of a WHERE-clause [parameter] might influence the + ** choice of query plan if the parameter is the left-hand side of a [LIKE] + ** or [GLOB] operator or if the parameter is compared to an indexed column + ** and the [SQLITE_ENABLE_STAT4] compile-time option is enabled. + **
    6. + **
    + ** + **

    ^sqlite3_prepare_v3() differs from sqlite3_prepare_v2() only in having + ** the extra prepFlags parameter, which is a bit array consisting of zero or + ** more of the [SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_*] flags. ^The + ** sqlite3_prepare_v2() interface works exactly the same as + ** sqlite3_prepare_v3() with a zero prepFlags parameter. + */ + SQLITE_API int sqlite3_prepare(sqlite3* db, /* Database handle */ + const char* zSql, /* SQL statement, UTF-8 encoded */ + int nByte, /* Maximum length of zSql in bytes. */ + sqlite3_stmt** ppStmt, /* OUT: Statement handle */ + const char** pzTail /* OUT: Pointer to unused portion of zSql */ + ); + SQLITE_API int sqlite3_prepare_v2(sqlite3* db, /* Database handle */ + const char* zSql, /* SQL statement, UTF-8 encoded */ + int nByte, /* Maximum length of zSql in bytes. */ + sqlite3_stmt** ppStmt, /* OUT: Statement handle */ + const char** pzTail /* OUT: Pointer to unused portion of zSql */ + ); + SQLITE_API int sqlite3_prepare_v3(sqlite3* db, /* Database handle */ + const char* zSql, /* SQL statement, UTF-8 encoded */ + int nByte, /* Maximum length of zSql in bytes. */ + unsigned int prepFlags, /* Zero or more SQLITE_PREPARE_ flags */ + sqlite3_stmt** ppStmt, /* OUT: Statement handle */ + const char** pzTail /* OUT: Pointer to unused portion of zSql */ + ); + SQLITE_API int sqlite3_prepare16(sqlite3* db, /* Database handle */ + const void* zSql, /* SQL statement, UTF-16 encoded */ + int nByte, /* Maximum length of zSql in bytes. */ + sqlite3_stmt** ppStmt, /* OUT: Statement handle */ + const void** pzTail /* OUT: Pointer to unused portion of zSql */ + ); + SQLITE_API int sqlite3_prepare16_v2(sqlite3* db, /* Database handle */ + const void* zSql, /* SQL statement, UTF-16 encoded */ + int nByte, /* Maximum length of zSql in bytes. */ + sqlite3_stmt** ppStmt, /* OUT: Statement handle */ + const void** pzTail /* OUT: Pointer to unused portion of zSql */ + ); + SQLITE_API int sqlite3_prepare16_v3(sqlite3* db, /* Database handle */ + const void* zSql, /* SQL statement, UTF-16 encoded */ + int nByte, /* Maximum length of zSql in bytes. */ + unsigned int prepFlags, /* Zero or more SQLITE_PREPARE_ flags */ + sqlite3_stmt** ppStmt, /* OUT: Statement handle */ + const void** pzTail /* OUT: Pointer to unused portion of zSql */ + ); + + /* + ** CAPI3REF: Retrieving Statement SQL + ** METHOD: sqlite3_stmt + ** + ** ^The sqlite3_sql(P) interface returns a pointer to a copy of the UTF-8 + ** SQL text used to create [prepared statement] P if P was + ** created by [sqlite3_prepare_v2()], [sqlite3_prepare_v3()], + ** [sqlite3_prepare16_v2()], or [sqlite3_prepare16_v3()]. + ** ^The sqlite3_expanded_sql(P) interface returns a pointer to a UTF-8 + ** string containing the SQL text of prepared statement P with + ** [bound parameters] expanded. + ** ^The sqlite3_normalized_sql(P) interface returns a pointer to a UTF-8 + ** string containing the normalized SQL text of prepared statement P. The + ** semantics used to normalize a SQL statement are unspecified and subject + ** to change. At a minimum, literal values will be replaced with suitable + ** placeholders. + ** + ** ^(For example, if a prepared statement is created using the SQL + ** text "SELECT $abc,:xyz" and if parameter $abc is bound to integer 2345 + ** and parameter :xyz is unbound, then sqlite3_sql() will return + ** the original string, "SELECT $abc,:xyz" but sqlite3_expanded_sql() + ** will return "SELECT 2345,NULL".)^ + ** + ** ^The sqlite3_expanded_sql() interface returns NULL if insufficient memory + ** is available to hold the result, or if the result would exceed the + ** the maximum string length determined by the [SQLITE_LIMIT_LENGTH]. + ** + ** ^The [SQLITE_TRACE_SIZE_LIMIT] compile-time option limits the size of + ** bound parameter expansions. ^The [SQLITE_OMIT_TRACE] compile-time + ** option causes sqlite3_expanded_sql() to always return NULL. + ** + ** ^The strings returned by sqlite3_sql(P) and sqlite3_normalized_sql(P) + ** 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 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: Compiling An SQL Statement -** KEYWORDS: {SQL statement compiler} -** METHOD: sqlite3 -** CONSTRUCTOR: sqlite3_stmt -** -** To execute an SQL statement, it must first be compiled into a byte-code -** program using one of these routines. Or, in other words, these routines -** are constructors for the [prepared statement] object. -** -** The preferred routine to use is [sqlite3_prepare_v2()]. The -** [sqlite3_prepare()] interface is legacy and should be avoided. -** [sqlite3_prepare_v3()] has an extra "prepFlags" option that is used -** for special purposes. -** -** The use of the UTF-8 interfaces is preferred, as SQLite currently -** does all parsing using UTF-8. The UTF-16 interfaces are provided -** as a convenience. The UTF-16 interfaces work by converting the -** input text into UTF-8, then invoking the corresponding UTF-8 interface. -** -** The first argument, "db", is a [database connection] obtained from a -** prior successful call to [sqlite3_open()], [sqlite3_open_v2()] or -** [sqlite3_open16()]. The database connection must not have been closed. -** -** The second argument, "zSql", is the statement to be compiled, encoded -** as either UTF-8 or UTF-16. The sqlite3_prepare(), sqlite3_prepare_v2(), -** and sqlite3_prepare_v3() -** interfaces use UTF-8, and sqlite3_prepare16(), sqlite3_prepare16_v2(), -** 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 -** 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. -** -** ^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 -** compile the first statement in zSql, so *pzTail is left pointing to -** what remains uncompiled. -** -** ^*ppStmt is left pointing to a compiled [prepared statement] that can be -** executed using [sqlite3_step()]. ^If there is an error, *ppStmt is set -** to NULL. ^If the input text contains no SQL (if the input is an empty -** string or a comment) then *ppStmt is set to NULL. -** The calling procedure is responsible for deleting the compiled -** SQL statement using [sqlite3_finalize()] after it has finished with it. -** ppStmt may not be NULL. -** -** ^On success, the sqlite3_prepare() family of routines return [SQLITE_OK]; -** otherwise an [error code] is returned. -** -** The sqlite3_prepare_v2(), sqlite3_prepare_v3(), sqlite3_prepare16_v2(), -** and sqlite3_prepare16_v3() interfaces are recommended for all new programs. -** The older interfaces (sqlite3_prepare() and sqlite3_prepare16()) -** are retained for backwards compatibility, but their use is discouraged. -** ^In the "vX" interfaces, the prepared statement -** that is returned (the [sqlite3_stmt] object) contains a copy of the -** original SQL text. This causes the [sqlite3_step()] interface to -** behave differently in three ways: -** -**

      -**
    1. -** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it -** always used to do, [sqlite3_step()] will automatically recompile the SQL -** statement and try to run it again. As many as [SQLITE_MAX_SCHEMA_RETRY] -** retries will occur before sqlite3_step() gives up and returns an error. -**
    2. -** -**
    3. -** ^When an error occurs, [sqlite3_step()] will return one of the detailed -** [error codes] or [extended error codes]. ^The legacy behavior was that -** [sqlite3_step()] would only return a generic [SQLITE_ERROR] result code -** and the application would have to make a second call to [sqlite3_reset()] -** in order to find the underlying cause of the problem. With the "v2" prepare -** interfaces, the underlying reason for the error is returned immediately. -**
    4. -** -**
    5. -** ^If the specific value bound to a [parameter | host parameter] in the -** WHERE clause might influence the choice of query plan for a statement, -** then the statement will be automatically recompiled, as if there had been -** a schema change, on the first [sqlite3_step()] call following any change -** to the [sqlite3_bind_text | bindings] of that [parameter]. -** ^The specific value of a WHERE-clause [parameter] might influence the -** choice of query plan if the parameter is the left-hand side of a [LIKE] -** or [GLOB] operator or if the parameter is compared to an indexed column -** and the [SQLITE_ENABLE_STAT4] compile-time option is enabled. -**
    6. -**
    -** -**

    ^sqlite3_prepare_v3() differs from sqlite3_prepare_v2() only in having -** the extra prepFlags parameter, which is a bit array consisting of zero or -** more of the [SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_*] flags. ^The -** sqlite3_prepare_v2() interface works exactly the same as -** sqlite3_prepare_v3() with a zero prepFlags parameter. -*/ -SQLITE_API int sqlite3_prepare( - sqlite3 *db, /* Database handle */ - const char *zSql, /* SQL statement, UTF-8 encoded */ - int nByte, /* Maximum length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: Statement handle */ - const char **pzTail /* OUT: Pointer to unused portion of zSql */ -); -SQLITE_API int sqlite3_prepare_v2( - sqlite3 *db, /* Database handle */ - const char *zSql, /* SQL statement, UTF-8 encoded */ - int nByte, /* Maximum length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: Statement handle */ - const char **pzTail /* OUT: Pointer to unused portion of zSql */ -); -SQLITE_API int sqlite3_prepare_v3( - sqlite3 *db, /* Database handle */ - const char *zSql, /* SQL statement, UTF-8 encoded */ - int nByte, /* Maximum length of zSql in bytes. */ - unsigned int prepFlags, /* Zero or more SQLITE_PREPARE_ flags */ - sqlite3_stmt **ppStmt, /* OUT: Statement handle */ - const char **pzTail /* OUT: Pointer to unused portion of zSql */ -); -SQLITE_API int sqlite3_prepare16( - sqlite3 *db, /* Database handle */ - const void *zSql, /* SQL statement, UTF-16 encoded */ - int nByte, /* Maximum length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: Statement handle */ - const void **pzTail /* OUT: Pointer to unused portion of zSql */ -); -SQLITE_API int sqlite3_prepare16_v2( - sqlite3 *db, /* Database handle */ - const void *zSql, /* SQL statement, UTF-16 encoded */ - int nByte, /* Maximum length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: Statement handle */ - const void **pzTail /* OUT: Pointer to unused portion of zSql */ -); -SQLITE_API int sqlite3_prepare16_v3( - sqlite3 *db, /* Database handle */ - const void *zSql, /* SQL statement, UTF-16 encoded */ - int nByte, /* Maximum length of zSql in bytes. */ - unsigned int prepFlags, /* Zero or more SQLITE_PREPARE_ flags */ - sqlite3_stmt **ppStmt, /* OUT: Statement handle */ - const void **pzTail /* OUT: Pointer to unused portion of zSql */ -); - -/* -** CAPI3REF: Retrieving Statement SQL -** METHOD: sqlite3_stmt -** -** ^The sqlite3_sql(P) interface returns a pointer to a copy of the UTF-8 -** SQL text used to create [prepared statement] P if P was -** created by [sqlite3_prepare_v2()], [sqlite3_prepare_v3()], -** [sqlite3_prepare16_v2()], or [sqlite3_prepare16_v3()]. -** ^The sqlite3_expanded_sql(P) interface returns a pointer to a UTF-8 -** string containing the SQL text of prepared statement P with -** [bound parameters] expanded. -** ^The sqlite3_normalized_sql(P) interface returns a pointer to a UTF-8 -** string containing the normalized SQL text of prepared statement P. The -** semantics used to normalize a SQL statement are unspecified and subject -** to change. At a minimum, literal values will be replaced with suitable -** placeholders. -** -** ^(For example, if a prepared statement is created using the SQL -** text "SELECT $abc,:xyz" and if parameter $abc is bound to integer 2345 -** and parameter :xyz is unbound, then sqlite3_sql() will return -** the original string, "SELECT $abc,:xyz" but sqlite3_expanded_sql() -** will return "SELECT 2345,NULL".)^ -** -** ^The sqlite3_expanded_sql() interface returns NULL if insufficient memory -** is available to hold the result, or if the result would exceed the -** the maximum string length determined by the [SQLITE_LIMIT_LENGTH]. -** -** ^The [SQLITE_TRACE_SIZE_LIMIT] compile-time option limits the size of -** bound parameter expansions. ^The [SQLITE_OMIT_TRACE] compile-time -** option causes sqlite3_expanded_sql() to always return NULL. -** -** ^The strings returned by sqlite3_sql(P) and sqlite3_normalized_sql(P) -** 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 -** by passing it to [sqlite3_free()]. -*/ -SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); -SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt); -SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt); - -/* -** CAPI3REF: Determine If An SQL Statement Writes The Database -** METHOD: sqlite3_stmt -** -** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if -** and only if the [prepared statement] X makes no direct changes to -** the content of the database file. -** -** Note that [application-defined SQL functions] or -** [virtual tables] might change the database indirectly as a side effect. -** ^(For example, if an application defines a function "eval()" that -** calls [sqlite3_exec()], then the following SQL statement would -** change the database file through side-effects: -** -**

    -**    SELECT eval('DELETE FROM t1') FROM t2;
    -** 
    -** -** But because the [SELECT] statement does not change the database file -** directly, sqlite3_stmt_readonly() would still return true.)^ -** -** ^Transaction control statements such as [BEGIN], [COMMIT], [ROLLBACK], -** [SAVEPOINT], and [RELEASE] cause sqlite3_stmt_readonly() to return true, -** since the statements themselves do not actually modify the database but -** rather they control the timing of when other statements modify the -** database. ^The [ATTACH] and [DETACH] statements also cause -** sqlite3_stmt_readonly() to return true since, while those statements -** change the configuration of a database connection, they do not make -** changes to the content of the database files on disk. -** ^The sqlite3_stmt_readonly() interface returns true for [BEGIN] since -** [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. -*/ -SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); - -/* -** CAPI3REF: Query The EXPLAIN Setting For A Prepared Statement -** METHOD: sqlite3_stmt -** -** ^The sqlite3_stmt_isexplain(S) interface returns 1 if the -** prepared statement S is an EXPLAIN statement, or 2 if the -** statement S is an EXPLAIN QUERY PLAN. -** ^The sqlite3_stmt_isexplain(S) interface returns 0 if S is -** an ordinary statement or a NULL pointer. -*/ -SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt); - -/* -** CAPI3REF: Determine If A Prepared Statement Has Been Reset -** METHOD: sqlite3_stmt -** -** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the -** [prepared statement] S has been stepped at least once using -** [sqlite3_step(S)] but has neither run to completion (returned -** [SQLITE_DONE] from [sqlite3_step(S)]) nor -** been reset using [sqlite3_reset(S)]. ^The sqlite3_stmt_busy(S) -** interface returns false if S is a NULL pointer. If S is not a -** NULL pointer and is not a pointer to a valid [prepared statement] -** object, then the behavior is undefined and probably undesirable. -** -** This interface can be used in combination [sqlite3_next_stmt()] -** to locate all prepared statements associated with a database -** connection that are in need of being reset. This can be used, -** for example, in diagnostic routines to search for prepared -** statements that are holding a transaction open. -*/ -SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*); - -/* -** CAPI3REF: Dynamically Typed Value Object -** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value} -** -** SQLite uses the sqlite3_value object to represent all values -** that can be stored in a database table. SQLite uses dynamic typing -** for the values it stores. ^Values stored in sqlite3_value objects -** can be integers, floating point values, strings, BLOBs, or NULL. -** -** An sqlite3_value object may be either "protected" or "unprotected". -** Some interfaces require a protected sqlite3_value. Other interfaces -** will accept either a protected or an unprotected sqlite3_value. -** Every interface that accepts sqlite3_value arguments specifies -** whether or not it requires a protected sqlite3_value. The -** [sqlite3_value_dup()] interface can be used to construct a new -** protected sqlite3_value from an unprotected sqlite3_value. -** -** The terms "protected" and "unprotected" refer to whether or not -** a mutex is held. An internal mutex is held for a protected -** sqlite3_value object but no mutex is held for an unprotected -** sqlite3_value object. If SQLite is compiled to be single-threaded -** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0) -** or if SQLite is run in one of reduced mutex modes -** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD] -** then there is no distinction between protected and unprotected -** sqlite3_value objects and they can be used interchangeably. However, -** for maximum code portability it is recommended that applications -** still make the distinction between protected and unprotected -** sqlite3_value objects even when not strictly required. -** -** ^The sqlite3_value objects that are passed as parameters into the -** implementation of [application-defined SQL functions] are protected. -** ^The sqlite3_value object returned by -** [sqlite3_column_value()] is unprotected. -** Unprotected sqlite3_value objects may only be used as arguments -** to [sqlite3_result_value()], [sqlite3_bind_value()], and -** [sqlite3_value_dup()]. -** The [sqlite3_value_blob | sqlite3_value_type()] family of -** interfaces require protected sqlite3_value objects. -*/ -typedef struct sqlite3_value sqlite3_value; - -/* -** CAPI3REF: SQL Function Context Object -** -** The context in which an SQL function executes is stored in an -** sqlite3_context object. ^A pointer to an sqlite3_context object -** is always first parameter to [application-defined SQL functions]. -** The application-defined SQL function implementation will pass this -** pointer through into calls to [sqlite3_result_int | sqlite3_result()], -** [sqlite3_aggregate_context()], [sqlite3_user_data()], -** [sqlite3_context_db_handle()], [sqlite3_get_auxdata()], -** and/or [sqlite3_set_auxdata()]. -*/ -typedef struct sqlite3_context sqlite3_context; - -/* -** CAPI3REF: Binding Values To Prepared Statements -** KEYWORDS: {host parameter} {host parameters} {host parameter name} -** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding} -** 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 -** templates: -** -**
      -**
    • ? -**
    • ?NNN -**
    • :VVV -**
    • @VVV -**
    • $VVV -**
    -** -** In the templates above, NNN represents an integer literal, -** and VVV represents an alphanumeric identifier.)^ ^The values of these -** parameters (also called "host parameter names" or "SQL parameters") -** can be set using the sqlite3_bind_*() routines defined here. -** -** ^The first argument to the sqlite3_bind_*() routines is always -** a pointer to the [sqlite3_stmt] object returned from -** [sqlite3_prepare_v2()] or its variants. -** -** ^The second argument is the index of the SQL parameter to be set. -** ^The leftmost SQL parameter has an index of 1. ^When the same named -** SQL parameter is used more than once, second and subsequent -** occurrences have the same index as the first occurrence. -** ^The index for named parameters can be looked up using the -** [sqlite3_bind_parameter_index()] API if desired. ^The index -** for "?NNN" parameters is the value of NNN. -** ^The NNN value must be between 1 and the [sqlite3_limit()] -** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 32766). -** -** ^The third argument is the value to bind to the parameter. -** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16() -** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter -** is ignored and the end result is the same as sqlite3_bind_null(). -** ^If the third parameter to sqlite3_bind_text() is not NULL, then -** it should be a pointer to well-formed UTF8 text. -** ^If the third parameter to sqlite3_bind_text16() is not NULL, then -** it should be a pointer to well-formed UTF16 text. -** ^If the third parameter to sqlite3_bind_text64() is not NULL, then -** it should be a pointer to a well-formed unicode string that is -** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16 -** otherwise. -** -** [[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 -** 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().)^ -** ^If UTF16 input text contains invalid unicode -** characters, then SQLite might change those invalid characters -** into the unicode replacement character: U+FFFD. -** -** ^(In those routines that have a fourth argument, its value is the -** number of bytes in the parameter. To be clear: the value is the -** number of bytes in the value, not the number of characters.)^ -** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16() -** is negative, then the length of the string is -** the number of bytes up to the first zero terminator. -** If the fourth parameter to sqlite3_bind_blob() is negative, then -** the behavior is undefined. -** If a non-negative fourth parameter is provided to sqlite3_bind_text() -** 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 -** 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 sixth argument to sqlite3_bind_text64() must be one of -** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE] -** to specify the encoding of the text in the third parameter. If -** the sixth argument to sqlite3_bind_text64() is not one of the -** allowed values shown above, or if the text encoding is different -** from the encoding specified by the sixth parameter, then the behavior -** is undefined. -** -** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that -** is filled with zeroes. ^A zeroblob uses a fixed amount of memory -** (just an integer to hold its size) while it is being processed. -** Zeroblobs are intended to serve as placeholders for BLOBs whose -** content is later written using -** [sqlite3_blob_open | incremental BLOB I/O] routines. -** ^A negative value for the zeroblob results in a zero-length BLOB. -** -** ^The sqlite3_bind_pointer(S,I,P,T,D) routine causes the I-th parameter in -** [prepared statement] S to have an SQL value of NULL, but to also be -** associated with the pointer P of type T. ^D is either a NULL pointer or -** a pointer to a destructor function for P. ^SQLite will invoke the -** destructor D with a single argument of P when it is finished using -** P. The T parameter should be a static string, preferably a string -** literal. The sqlite3_bind_pointer() routine is part of the -** [pointer passing interface] added for SQLite 3.20.0. -** -** ^If any of the sqlite3_bind_*() routines are called with a NULL pointer -** for the [prepared statement] or with a prepared statement for which -** [sqlite3_step()] has been called more recently than [sqlite3_reset()], -** then the call will return [SQLITE_MISUSE]. If any sqlite3_bind_() -** routine is passed a [prepared statement] that has been finalized, the -** result is undefined and probably harmful. -** -** ^Bindings are not cleared by the [sqlite3_reset()] routine. -** ^Unbound parameters are interpreted as NULL. -** -** ^The sqlite3_bind_* routines return [SQLITE_OK] on success or an -** [error code] if anything goes wrong. -** ^[SQLITE_TOOBIG] might be returned if the size of a string or BLOB -** exceeds limits imposed by [sqlite3_limit]([SQLITE_LIMIT_LENGTH]) or -** [SQLITE_MAX_LENGTH]. -** ^[SQLITE_RANGE] is returned if the parameter -** index is out of range. ^[SQLITE_NOMEM] is returned if malloc() fails. -** -** See also: [sqlite3_bind_parameter_count()], -** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()]. -*/ -SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); -SQLITE_API int sqlite3_bind_blob64(sqlite3_stmt*, int, const void*, sqlite3_uint64, - void(*)(void*)); -SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double); -SQLITE_API int sqlite3_bind_int(sqlite3_stmt*, int, int); -SQLITE_API int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); -SQLITE_API int sqlite3_bind_null(sqlite3_stmt*, int); -SQLITE_API int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*)); -SQLITE_API int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); -SQLITE_API int sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64, - void(*)(void*), unsigned char encoding); -SQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); -SQLITE_API int sqlite3_bind_pointer(sqlite3_stmt*, int, void*, const char*,void(*)(void*)); -SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); -SQLITE_API int sqlite3_bind_zeroblob64(sqlite3_stmt*, int, sqlite3_uint64); - -/* -** CAPI3REF: Number Of SQL Parameters -** METHOD: sqlite3_stmt -** -** ^This routine can be used to find the number of [SQL parameters] -** in a [prepared statement]. SQL parameters are tokens of the -** form "?", "?NNN", ":AAA", "$AAA", or "@AAA" that serve as -** placeholders for values that are [sqlite3_bind_blob | bound] -** to the parameters at a later time. -** -** ^(This routine actually returns the index of the largest (rightmost) -** parameter. For all forms except ?NNN, this will correspond to the -** number of unique parameters. If parameters of the ?NNN form are used, -** there may be gaps in the list.)^ -** -** See also: [sqlite3_bind_blob|sqlite3_bind()], -** [sqlite3_bind_parameter_name()], and -** [sqlite3_bind_parameter_index()]. -*/ -SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*); - -/* -** CAPI3REF: Name Of A Host Parameter -** METHOD: sqlite3_stmt -** -** ^The sqlite3_bind_parameter_name(P,N) interface returns -** the name of the N-th [SQL parameter] in the [prepared statement] P. -** ^(SQL parameters of the form "?NNN" or ":AAA" or "@AAA" or "$AAA" -** have a name which is the string "?NNN" or ":AAA" or "@AAA" or "$AAA" -** respectively. -** In other words, the initial ":" or "$" or "@" or "?" -** is included as part of the name.)^ -** ^Parameters of the form "?" without a following integer have no name -** and are referred to as "nameless" or "anonymous parameters". -** -** ^The first host parameter has an index of 1, not 0. -** -** ^If the value N is out of range or if the N-th parameter is -** nameless, then NULL is returned. ^The returned string is -** always in UTF-8 encoding even if the named parameter was -** originally specified as UTF-16 in [sqlite3_prepare16()], -** [sqlite3_prepare16_v2()], or [sqlite3_prepare16_v3()]. -** -** See also: [sqlite3_bind_blob|sqlite3_bind()], -** [sqlite3_bind_parameter_count()], and -** [sqlite3_bind_parameter_index()]. -*/ -SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); - -/* -** CAPI3REF: Index Of A Parameter With A Given Name -** METHOD: sqlite3_stmt -** -** ^Return the index of an SQL parameter given its name. ^The -** index value returned is suitable for use as the second -** parameter to [sqlite3_bind_blob|sqlite3_bind()]. ^A zero -** is returned if no matching parameter is found. ^The parameter -** name must be given in UTF-8 even if the original statement -** was prepared from UTF-16 text using [sqlite3_prepare16_v2()] or -** [sqlite3_prepare16_v3()]. -** -** See also: [sqlite3_bind_blob|sqlite3_bind()], -** [sqlite3_bind_parameter_count()], and -** [sqlite3_bind_parameter_name()]. -*/ -SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); - -/* -** CAPI3REF: Reset All Bindings On A Prepared Statement -** METHOD: sqlite3_stmt -** -** ^Contrary to the intuition of many, [sqlite3_reset()] does not reset -** the [sqlite3_bind_blob | bindings] on a [prepared statement]. -** ^Use this routine to reset all host parameters to NULL. -*/ -SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*); - -/* -** CAPI3REF: Number Of Columns In A Result Set -** METHOD: sqlite3_stmt -** -** ^Return the number of columns in the result set returned by the -** [prepared statement]. ^If this routine returns 0, that means the -** [prepared statement] returns no data (for example an [UPDATE]). -** ^However, just because this routine returns a positive number does not -** mean that one or more rows of data will be returned. ^A SELECT statement -** will always have a positive sqlite3_column_count() but depending on the -** WHERE clause constraints and the table content, it might return no rows. -** -** See also: [sqlite3_data_count()] -*/ -SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); - -/* -** CAPI3REF: Column Names In A Result Set -** METHOD: sqlite3_stmt -** -** ^These routines return the name assigned to a particular column -** in the result set of a [SELECT] statement. ^The sqlite3_column_name() -** interface returns a pointer to a zero-terminated UTF-8 string -** and sqlite3_column_name16() returns a pointer to a zero-terminated -** UTF-16 string. ^The first parameter is the [prepared statement] -** that implements the [SELECT] statement. ^The second parameter is the -** column number. ^The leftmost column is number 0. -** -** ^The returned string pointer is valid until either the [prepared statement] -** is destroyed by [sqlite3_finalize()] or until the statement is automatically -** reprepared by the first call to [sqlite3_step()] for a particular run -** or until the next call to -** sqlite3_column_name() or sqlite3_column_name16() on the same column. -** -** ^If sqlite3_malloc() fails during the processing of either routine -** (for example during a conversion from UTF-8 to UTF-16) then a -** NULL pointer is returned. -** -** ^The name of a result column is the value of the "AS" clause for -** that column, if there is an AS clause. If there is no AS clause -** then the name of the column is unspecified and may change from -** one release of SQLite to the next. -*/ -SQLITE_API const char *sqlite3_column_name(sqlite3_stmt*, int N); -SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); - -/* -** CAPI3REF: Source Of Data In A Query Result -** 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 -** [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 -** the database name, the _table_ routines return the table name, and -** the origin_ routines return the column name. -** ^The returned string is valid until the [prepared statement] is destroyed -** using [sqlite3_finalize()] or until the statement is automatically -** reprepared by the first call to [sqlite3_step()] for a particular run -** or until the same information is requested -** again in a different encoding. -** -** ^The names returned are the original un-aliased names of the -** database, table, and column. -** -** ^The first argument to these interfaces is a [prepared statement]. -** ^These functions return information about the Nth result column returned by -** the statement, where N is the second function argument. -** ^The left-most column is column 0 for these routines. -** -** ^If the Nth column returned by the statement is an expression or -** subquery and is not a column value, then all of these functions return -** NULL. ^These routines might also return NULL if a memory allocation error -** occurs. ^Otherwise, they return the name of the attached database, table, -** or column that query result column was extracted from. -** -** ^As with all other SQLite APIs, those whose names end with "16" return -** UTF-16 encoded strings and the other functions return UTF-8. -** -** ^These APIs are only available if the library was compiled with the -** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol. -** -** If two or more threads call one or more -** [sqlite3_column_database_name | column metadata interfaces] -** for the same [prepared statement] and result column -** at the same time then the results are undefined. -*/ -SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt*,int); -SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt*,int); -SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); - -/* -** CAPI3REF: Declared Datatype Of A Query Result -** METHOD: sqlite3_stmt -** -** ^(The first parameter is a [prepared statement]. -** If this statement is a [SELECT] statement and the Nth column of the -** returned result set of that [SELECT] is a table column (not an -** expression or subquery) then the declared type of the table -** column is returned.)^ ^If the Nth column of the result set is an -** expression or subquery, then a NULL pointer is returned. -** ^The returned string is always UTF-8 encoded. -** -** ^(For example, given the database schema: -** -** CREATE TABLE t1(c1 VARIANT); -** -** and the following statement to be compiled: -** -** SELECT c1 + 1, c1 FROM t1; -** -** this routine would return the string "VARIANT" for the second result -** column (i==1), and a NULL pointer for the first result column (i==0).)^ -** -** ^SQLite uses dynamic run-time typing. ^So just because a column -** is declared to contain a particular type does not mean that the -** data stored in that column is of the declared type. SQLite is -** strongly typed, but the typing is dynamic not static. ^Type -** is associated with individual values, not with the containers -** used to hold those values. -*/ -SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); - -/* -** CAPI3REF: Evaluate An SQL Statement -** METHOD: sqlite3_stmt -** -** After a [prepared statement] has been prepared using any of -** [sqlite3_prepare_v2()], [sqlite3_prepare_v3()], [sqlite3_prepare16_v2()], -** or [sqlite3_prepare16_v3()] or one of the legacy -** interfaces [sqlite3_prepare()] or [sqlite3_prepare16()], this function -** must be called one or more times to evaluate the statement. -** -** The details of the behavior of the sqlite3_step() interface depend -** on whether the statement was prepared using the newer "vX" interfaces -** [sqlite3_prepare_v3()], [sqlite3_prepare_v2()], [sqlite3_prepare16_v3()], -** [sqlite3_prepare16_v2()] or the older legacy -** interfaces [sqlite3_prepare()] and [sqlite3_prepare16()]. The use of the -** new "vX" interface is recommended for new applications but the legacy -** interface will continue to be supported. -** -** ^In the legacy interface, the return value will be either [SQLITE_BUSY], -** [SQLITE_DONE], [SQLITE_ROW], [SQLITE_ERROR], or [SQLITE_MISUSE]. -** ^With the "v2" interface, any of the other [result codes] or -** [extended result codes] might be returned as well. -** -** ^[SQLITE_BUSY] means that the database engine was unable to acquire the -** database locks it needs to do its job. ^If the statement is a [COMMIT] -** or occurs outside of an explicit transaction, then you can retry the -** statement. If the statement is not a [COMMIT] and occurs within an -** explicit transaction then you should rollback the transaction before -** continuing. -** -** ^[SQLITE_DONE] means that the statement has finished executing -** successfully. sqlite3_step() should not be called again on this virtual -** machine without first calling [sqlite3_reset()] to reset the virtual -** machine back to its initial state. -** -** ^If the SQL statement being executed returns any data, then [SQLITE_ROW] -** is returned each time a new row of data is ready for processing by the -** caller. The values may be accessed using the [column access functions]. -** sqlite3_step() is called again to retrieve the next row of data. -** -** ^[SQLITE_ERROR] means that a run-time error (such as a constraint -** violation) has occurred. sqlite3_step() should not be called again on -** the VM. More information may be found by calling [sqlite3_errmsg()]. -** ^With the legacy interface, a more specific error code (for example, -** [SQLITE_INTERRUPT], [SQLITE_SCHEMA], [SQLITE_CORRUPT], and so forth) -** can be obtained by calling [sqlite3_reset()] on the -** [prepared statement]. ^In the "v2" interface, -** the more specific error code is returned directly by sqlite3_step(). -** -** [SQLITE_MISUSE] means that the this routine was called inappropriately. -** Perhaps it was called on a [prepared statement] that has -** already been [sqlite3_finalize | finalized] or on one that had -** previously returned [SQLITE_ERROR] or [SQLITE_DONE]. Or it could -** be the case that the same database connection is being used by two or -** more threads at the same moment in time. -** -** For all versions of SQLite up to and including 3.6.23.1, a call to -** [sqlite3_reset()] was required after sqlite3_step() returned anything -** 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() began -** calling [sqlite3_reset()] automatically in this circumstance rather -** than returning [SQLITE_MISUSE]. This is not considered a compatibility -** break because any application that ever receives an SQLITE_MISUSE error -** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option -** can be used to restore the legacy behavior. -** -** Goofy Interface Alert: In the legacy interface, the sqlite3_step() -** API always returns a generic error code, [SQLITE_ERROR], following any -** error other than [SQLITE_BUSY] and [SQLITE_MISUSE]. You must call -** [sqlite3_reset()] or [sqlite3_finalize()] in order to find one of the -** specific [error codes] that better describes the error. -** We admit that this is a goofy design. The problem has been fixed -** with the "v2" interface. If you prepare all of your SQL statements -** using [sqlite3_prepare_v3()] or [sqlite3_prepare_v2()] -** or [sqlite3_prepare16_v2()] or [sqlite3_prepare16_v3()] instead -** of the legacy [sqlite3_prepare()] and [sqlite3_prepare16()] interfaces, -** then the more specific [error codes] are returned directly -** by sqlite3_step(). The use of the "vX" interfaces is recommended. -*/ -SQLITE_API int sqlite3_step(sqlite3_stmt*); - -/* -** CAPI3REF: Number of columns in a result set -** METHOD: sqlite3_stmt -** -** ^The sqlite3_data_count(P) interface returns the number of columns in the -** current row of the result set of [prepared statement] P. -** ^If prepared statement P does not have results ready to return -** (via calls to the [sqlite3_column_int | sqlite3_column()] family of -** interfaces) then sqlite3_data_count(P) returns 0. -** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer. -** ^The sqlite3_data_count(P) routine returns 0 if the previous call to -** [sqlite3_step](P) returned [SQLITE_DONE]. ^The sqlite3_data_count(P) -** will return non-zero if previous call to [sqlite3_step](P) returned -** [SQLITE_ROW], except in the case of the [PRAGMA incremental_vacuum] -** where it always returns zero since each step of that multi-step -** pragma returns 0 columns of data. -** -** See also: [sqlite3_column_count()] -*/ -SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); + /* + ** CAPI3REF: Determine If An SQL Statement Writes The Database + ** METHOD: sqlite3_stmt + ** + ** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if + ** and only if the [prepared statement] X makes no direct changes to + ** the content of the database file. + ** + ** Note that [application-defined SQL functions] or + ** [virtual tables] might change the database indirectly as a side effect. + ** ^(For example, if an application defines a function "eval()" that + ** calls [sqlite3_exec()], then the following SQL statement would + ** change the database file through side-effects: + ** + **
    +    **    SELECT eval('DELETE FROM t1') FROM t2;
    +    ** 
    + ** + ** But because the [SELECT] statement does not change the database file + ** directly, sqlite3_stmt_readonly() would still return true.)^ + ** + ** ^Transaction control statements such as [BEGIN], [COMMIT], [ROLLBACK], + ** [SAVEPOINT], and [RELEASE] cause sqlite3_stmt_readonly() to return true, + ** since the statements themselves do not actually modify the database but + ** rather they control the timing of when other statements modify the + ** database. ^The [ATTACH] and [DETACH] statements also cause + ** sqlite3_stmt_readonly() to return true since, while those statements + ** change the configuration of a database connection, they do not make + ** changes to the content of the database files on disk. + ** ^The sqlite3_stmt_readonly() interface returns true for [BEGIN] since + ** [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); + + /* + ** CAPI3REF: Query The EXPLAIN Setting For A Prepared Statement + ** METHOD: sqlite3_stmt + ** + ** ^The sqlite3_stmt_isexplain(S) interface returns 1 if the + ** prepared statement S is an EXPLAIN statement, or 2 if the + ** statement S is an EXPLAIN QUERY PLAN. + ** ^The sqlite3_stmt_isexplain(S) interface returns 0 if S is + ** an ordinary statement or a NULL pointer. + */ + SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt* pStmt); + + /* + ** CAPI3REF: Determine If A Prepared Statement Has Been Reset + ** METHOD: sqlite3_stmt + ** + ** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the + ** [prepared statement] S has been stepped at least once using + ** [sqlite3_step(S)] but has neither run to completion (returned + ** [SQLITE_DONE] from [sqlite3_step(S)]) nor + ** been reset using [sqlite3_reset(S)]. ^The sqlite3_stmt_busy(S) + ** interface returns false if S is a NULL pointer. If S is not a + ** NULL pointer and is not a pointer to a valid [prepared statement] + ** object, then the behavior is undefined and probably undesirable. + ** + ** This interface can be used in combination [sqlite3_next_stmt()] + ** to locate all prepared statements associated with a database + ** connection that are in need of being reset. This can be used, + ** for example, in diagnostic routines to search for prepared + ** statements that are holding a transaction open. + */ + SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*); + + /* + ** CAPI3REF: Dynamically Typed Value Object + ** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value} + ** + ** SQLite uses the sqlite3_value object to represent all values + ** that can be stored in a database table. SQLite uses dynamic typing + ** for the values it stores. ^Values stored in sqlite3_value objects + ** can be integers, floating point values, strings, BLOBs, or NULL. + ** + ** An sqlite3_value object may be either "protected" or "unprotected". + ** Some interfaces require a protected sqlite3_value. Other interfaces + ** will accept either a protected or an unprotected sqlite3_value. + ** Every interface that accepts sqlite3_value arguments specifies + ** whether or not it requires a protected sqlite3_value. The + ** [sqlite3_value_dup()] interface can be used to construct a new + ** protected sqlite3_value from an unprotected sqlite3_value. + ** + ** The terms "protected" and "unprotected" refer to whether or not + ** a mutex is held. An internal mutex is held for a protected + ** sqlite3_value object but no mutex is held for an unprotected + ** sqlite3_value object. If SQLite is compiled to be single-threaded + ** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0) + ** or if SQLite is run in one of reduced mutex modes + ** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD] + ** then there is no distinction between protected and unprotected + ** sqlite3_value objects and they can be used interchangeably. However, + ** for maximum code portability it is recommended that applications + ** still make the distinction between protected and unprotected + ** sqlite3_value objects even when not strictly required. + ** + ** ^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 + ** to [sqlite3_result_value()], [sqlite3_bind_value()], and + ** [sqlite3_value_dup()]. + ** The [sqlite3_value_blob | sqlite3_value_type()] family of + ** interfaces require protected sqlite3_value objects. + */ + typedef struct sqlite3_value sqlite3_value; + + /* + ** CAPI3REF: SQL Function Context Object + ** + ** The context in which an SQL function executes is stored in an + ** sqlite3_context object. ^A pointer to an sqlite3_context object + ** is always first parameter to [application-defined SQL functions]. + ** The application-defined SQL function implementation will pass this + ** pointer through into calls to [sqlite3_result_int | sqlite3_result()], + ** [sqlite3_aggregate_context()], [sqlite3_user_data()], + ** [sqlite3_context_db_handle()], [sqlite3_get_auxdata()], + ** and/or [sqlite3_set_auxdata()]. + */ + typedef struct sqlite3_context sqlite3_context; + + /* + ** CAPI3REF: Binding Values To Prepared Statements + ** KEYWORDS: {host parameter} {host parameters} {host parameter name} + ** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding} + ** 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 + ** templates: + ** + **
      + **
    • ? + **
    • ?NNN + **
    • :VVV + **
    • @VVV + **
    • $VVV + **
    + ** + ** In the templates above, NNN represents an integer literal, + ** and VVV represents an alphanumeric identifier.)^ ^The values of these + ** parameters (also called "host parameter names" or "SQL parameters") + ** can be set using the sqlite3_bind_*() routines defined here. + ** + ** ^The first argument to the sqlite3_bind_*() routines is always + ** a pointer to the [sqlite3_stmt] object returned from + ** [sqlite3_prepare_v2()] or its variants. + ** + ** ^The second argument is the index of the SQL parameter to be set. + ** ^The leftmost SQL parameter has an index of 1. ^When the same named + ** SQL parameter is used more than once, second and subsequent + ** occurrences have the same index as the first occurrence. + ** ^The index for named parameters can be looked up using the + ** [sqlite3_bind_parameter_index()] API if desired. ^The index + ** for "?NNN" parameters is the value of NNN. + ** ^The NNN value must be between 1 and the [sqlite3_limit()] + ** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 32766). + ** + ** ^The third argument is the value to bind to the parameter. + ** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16() + ** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter + ** is ignored and the end result is the same as sqlite3_bind_null(). + ** ^If the third parameter to sqlite3_bind_text() is not NULL, then + ** it should be a pointer to well-formed UTF8 text. + ** ^If the third parameter to sqlite3_bind_text16() is not NULL, then + ** it should be a pointer to well-formed UTF16 text. + ** ^If the third parameter to sqlite3_bind_text64() is not NULL, then + ** it should be a pointer to a well-formed unicode string that is + ** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16 + ** otherwise. + ** + ** [[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 + ** 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().)^ + ** ^If UTF16 input text contains invalid unicode + ** characters, then SQLite might change those invalid characters + ** into the unicode replacement character: U+FFFD. + ** + ** ^(In those routines that have a fourth argument, its value is the + ** number of bytes in the parameter. To be clear: the value is the + ** number of bytes in the value, not the number of characters.)^ + ** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16() + ** is negative, then the length of the string is + ** the number of bytes up to the first zero terminator. + ** If the fourth parameter to sqlite3_bind_blob() is negative, then + ** the behavior is undefined. + ** If a non-negative fourth parameter is provided to sqlite3_bind_text() + ** 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 + ** 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 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 passsed 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] + ** to specify the encoding of the text in the third parameter. If + ** the sixth argument to sqlite3_bind_text64() is not one of the + ** allowed values shown above, or if the text encoding is different + ** from the encoding specified by the sixth parameter, then the behavior + ** is undefined. + ** + ** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that + ** is filled with zeroes. ^A zeroblob uses a fixed amount of memory + ** (just an integer to hold its size) while it is being processed. + ** Zeroblobs are intended to serve as placeholders for BLOBs whose + ** content is later written using + ** [sqlite3_blob_open | incremental BLOB I/O] routines. + ** ^A negative value for the zeroblob results in a zero-length BLOB. + ** + ** ^The sqlite3_bind_pointer(S,I,P,T,D) routine causes the I-th parameter in + ** [prepared statement] S to have an SQL value of NULL, but to also be + ** associated with the pointer P of type T. ^D is either a NULL pointer or + ** a pointer to a destructor function for P. ^SQLite will invoke the + ** destructor D with a single argument of P when it is finished using + ** P. The T parameter should be a static string, preferably a string + ** literal. The sqlite3_bind_pointer() routine is part of the + ** [pointer passing interface] added for SQLite 3.20.0. + ** + ** ^If any of the sqlite3_bind_*() routines are called with a NULL pointer + ** for the [prepared statement] or with a prepared statement for which + ** [sqlite3_step()] has been called more recently than [sqlite3_reset()], + ** then the call will return [SQLITE_MISUSE]. If any sqlite3_bind_() + ** routine is passed a [prepared statement] that has been finalized, the + ** result is undefined and probably harmful. + ** + ** ^Bindings are not cleared by the [sqlite3_reset()] routine. + ** ^Unbound parameters are interpreted as NULL. + ** + ** ^The sqlite3_bind_* routines return [SQLITE_OK] on success or an + ** [error code] if anything goes wrong. + ** ^[SQLITE_TOOBIG] might be returned if the size of a string or BLOB + ** exceeds limits imposed by [sqlite3_limit]([SQLITE_LIMIT_LENGTH]) or + ** [SQLITE_MAX_LENGTH]. + ** ^[SQLITE_RANGE] is returned if the parameter + ** index is out of range. ^[SQLITE_NOMEM] is returned if malloc() fails. + ** + ** See also: [sqlite3_bind_parameter_count()], + ** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()]. + */ + SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void (*)(void*)); + SQLITE_API int sqlite3_bind_blob64(sqlite3_stmt*, int, const void*, sqlite3_uint64, void (*)(void*)); + SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double); + SQLITE_API int sqlite3_bind_int(sqlite3_stmt*, int, int); + SQLITE_API int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); + SQLITE_API int sqlite3_bind_null(sqlite3_stmt*, int); + SQLITE_API int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int, void (*)(void*)); + SQLITE_API int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void (*)(void*)); + SQLITE_API int + sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64, void (*)(void*), unsigned char encoding); + SQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); + SQLITE_API int sqlite3_bind_pointer(sqlite3_stmt*, int, void*, const char*, void (*)(void*)); + SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); + SQLITE_API int sqlite3_bind_zeroblob64(sqlite3_stmt*, int, sqlite3_uint64); + + /* + ** CAPI3REF: Number Of SQL Parameters + ** METHOD: sqlite3_stmt + ** + ** ^This routine can be used to find the number of [SQL parameters] + ** in a [prepared statement]. SQL parameters are tokens of the + ** form "?", "?NNN", ":AAA", "$AAA", or "@AAA" that serve as + ** placeholders for values that are [sqlite3_bind_blob | bound] + ** to the parameters at a later time. + ** + ** ^(This routine actually returns the index of the largest (rightmost) + ** parameter. For all forms except ?NNN, this will correspond to the + ** number of unique parameters. If parameters of the ?NNN form are used, + ** there may be gaps in the list.)^ + ** + ** See also: [sqlite3_bind_blob|sqlite3_bind()], + ** [sqlite3_bind_parameter_name()], and + ** [sqlite3_bind_parameter_index()]. + */ + SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*); + + /* + ** CAPI3REF: Name Of A Host Parameter + ** METHOD: sqlite3_stmt + ** + ** ^The sqlite3_bind_parameter_name(P,N) interface returns + ** the name of the N-th [SQL parameter] in the [prepared statement] P. + ** ^(SQL parameters of the form "?NNN" or ":AAA" or "@AAA" or "$AAA" + ** have a name which is the string "?NNN" or ":AAA" or "@AAA" or "$AAA" + ** respectively. + ** In other words, the initial ":" or "$" or "@" or "?" + ** is included as part of the name.)^ + ** ^Parameters of the form "?" without a following integer have no name + ** and are referred to as "nameless" or "anonymous parameters". + ** + ** ^The first host parameter has an index of 1, not 0. + ** + ** ^If the value N is out of range or if the N-th parameter is + ** nameless, then NULL is returned. ^The returned string is + ** always in UTF-8 encoding even if the named parameter was + ** originally specified as UTF-16 in [sqlite3_prepare16()], + ** [sqlite3_prepare16_v2()], or [sqlite3_prepare16_v3()]. + ** + ** See also: [sqlite3_bind_blob|sqlite3_bind()], + ** [sqlite3_bind_parameter_count()], and + ** [sqlite3_bind_parameter_index()]. + */ + SQLITE_API const char* sqlite3_bind_parameter_name(sqlite3_stmt*, int); + + /* + ** CAPI3REF: Index Of A Parameter With A Given Name + ** METHOD: sqlite3_stmt + ** + ** ^Return the index of an SQL parameter given its name. ^The + ** index value returned is suitable for use as the second + ** parameter to [sqlite3_bind_blob|sqlite3_bind()]. ^A zero + ** is returned if no matching parameter is found. ^The parameter + ** name must be given in UTF-8 even if the original statement + ** was prepared from UTF-16 text using [sqlite3_prepare16_v2()] or + ** [sqlite3_prepare16_v3()]. + ** + ** See also: [sqlite3_bind_blob|sqlite3_bind()], + ** [sqlite3_bind_parameter_count()], and + ** [sqlite3_bind_parameter_name()]. + */ + SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt*, const char* zName); + + /* + ** CAPI3REF: Reset All Bindings On A Prepared Statement + ** METHOD: sqlite3_stmt + ** + ** ^Contrary to the intuition of many, [sqlite3_reset()] does not reset + ** the [sqlite3_bind_blob | bindings] on a [prepared statement]. + ** ^Use this routine to reset all host parameters to NULL. + */ + SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*); + + /* + ** CAPI3REF: Number Of Columns In A Result Set + ** METHOD: sqlite3_stmt + ** + ** ^Return the number of columns in the result set returned by the + ** [prepared statement]. ^If this routine returns 0, that means the + ** [prepared statement] returns no data (for example an [UPDATE]). + ** ^However, just because this routine returns a positive number does not + ** mean that one or more rows of data will be returned. ^A SELECT statement + ** will always have a positive sqlite3_column_count() but depending on the + ** WHERE clause constraints and the table content, it might return no rows. + ** + ** See also: [sqlite3_data_count()] + */ + SQLITE_API int sqlite3_column_count(sqlite3_stmt* pStmt); + + /* + ** CAPI3REF: Column Names In A Result Set + ** METHOD: sqlite3_stmt + ** + ** ^These routines return the name assigned to a particular column + ** in the result set of a [SELECT] statement. ^The sqlite3_column_name() + ** interface returns a pointer to a zero-terminated UTF-8 string + ** and sqlite3_column_name16() returns a pointer to a zero-terminated + ** UTF-16 string. ^The first parameter is the [prepared statement] + ** that implements the [SELECT] statement. ^The second parameter is the + ** column number. ^The leftmost column is number 0. + ** + ** ^The returned string pointer is valid until either the [prepared statement] + ** is destroyed by [sqlite3_finalize()] or until the statement is automatically + ** reprepared by the first call to [sqlite3_step()] for a particular run + ** or until the next call to + ** sqlite3_column_name() or sqlite3_column_name16() on the same column. + ** + ** ^If sqlite3_malloc() fails during the processing of either routine + ** (for example during a conversion from UTF-8 to UTF-16) then a + ** NULL pointer is returned. + ** + ** ^The name of a result column is the value of the "AS" clause for + ** that column, if there is an AS clause. If there is no AS clause + ** then the name of the column is unspecified and may change from + ** one release of SQLite to the next. + */ + SQLITE_API const char* sqlite3_column_name(sqlite3_stmt*, int N); + SQLITE_API const void* sqlite3_column_name16(sqlite3_stmt*, int N); + + /* + ** CAPI3REF: Source Of Data In A Query Result + ** 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 + ** [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 + ** the database name, the _table_ routines return the table name, and + ** the origin_ routines return the column name. + ** ^The returned string is valid until the [prepared statement] is destroyed + ** using [sqlite3_finalize()] or until the statement is automatically + ** reprepared by the first call to [sqlite3_step()] for a particular run + ** or until the same information is requested + ** again in a different encoding. + ** + ** ^The names returned are the original un-aliased names of the + ** database, table, and column. + ** + ** ^The first argument to these interfaces is a [prepared statement]. + ** ^These functions return information about the Nth result column returned by + ** the statement, where N is the second function argument. + ** ^The left-most column is column 0 for these routines. + ** + ** ^If the Nth column returned by the statement is an expression or + ** subquery and is not a column value, then all of these functions return + ** NULL. ^These routines might also return NULL if a memory allocation error + ** occurs. ^Otherwise, they return the name of the attached database, table, + ** or column that query result column was extracted from. + ** + ** ^As with all other SQLite APIs, those whose names end with "16" return + ** UTF-16 encoded strings and the other functions return UTF-8. + ** + ** ^These APIs are only available if the library was compiled with the + ** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol. + ** + ** If two or more threads call one or more + ** [sqlite3_column_database_name | column metadata interfaces] + ** for the same [prepared statement] and result column + ** at the same time then the results are undefined. + */ + SQLITE_API const char* sqlite3_column_database_name(sqlite3_stmt*, int); + SQLITE_API const void* sqlite3_column_database_name16(sqlite3_stmt*, int); + SQLITE_API const char* sqlite3_column_table_name(sqlite3_stmt*, int); + SQLITE_API const void* sqlite3_column_table_name16(sqlite3_stmt*, int); + SQLITE_API const char* sqlite3_column_origin_name(sqlite3_stmt*, int); + SQLITE_API const void* sqlite3_column_origin_name16(sqlite3_stmt*, int); + + /* + ** CAPI3REF: Declared Datatype Of A Query Result + ** METHOD: sqlite3_stmt + ** + ** ^(The first parameter is a [prepared statement]. + ** If this statement is a [SELECT] statement and the Nth column of the + ** returned result set of that [SELECT] is a table column (not an + ** expression or subquery) then the declared type of the table + ** column is returned.)^ ^If the Nth column of the result set is an + ** expression or subquery, then a NULL pointer is returned. + ** ^The returned string is always UTF-8 encoded. + ** + ** ^(For example, given the database schema: + ** + ** CREATE TABLE t1(c1 VARIANT); + ** + ** and the following statement to be compiled: + ** + ** SELECT c1 + 1, c1 FROM t1; + ** + ** this routine would return the string "VARIANT" for the second result + ** column (i==1), and a NULL pointer for the first result column (i==0).)^ + ** + ** ^SQLite uses dynamic run-time typing. ^So just because a column + ** is declared to contain a particular type does not mean that the + ** data stored in that column is of the declared type. SQLite is + ** strongly typed, but the typing is dynamic not static. ^Type + ** is associated with individual values, not with the containers + ** used to hold those values. + */ + SQLITE_API const char* sqlite3_column_decltype(sqlite3_stmt*, int); + SQLITE_API const void* sqlite3_column_decltype16(sqlite3_stmt*, int); + + /* + ** CAPI3REF: Evaluate An SQL Statement + ** METHOD: sqlite3_stmt + ** + ** After a [prepared statement] has been prepared using any of + ** [sqlite3_prepare_v2()], [sqlite3_prepare_v3()], [sqlite3_prepare16_v2()], + ** or [sqlite3_prepare16_v3()] or one of the legacy + ** interfaces [sqlite3_prepare()] or [sqlite3_prepare16()], this function + ** must be called one or more times to evaluate the statement. + ** + ** The details of the behavior of the sqlite3_step() interface depend + ** on whether the statement was prepared using the newer "vX" interfaces + ** [sqlite3_prepare_v3()], [sqlite3_prepare_v2()], [sqlite3_prepare16_v3()], + ** [sqlite3_prepare16_v2()] or the older legacy + ** interfaces [sqlite3_prepare()] and [sqlite3_prepare16()]. The use of the + ** new "vX" interface is recommended for new applications but the legacy + ** interface will continue to be supported. + ** + ** ^In the legacy interface, the return value will be either [SQLITE_BUSY], + ** [SQLITE_DONE], [SQLITE_ROW], [SQLITE_ERROR], or [SQLITE_MISUSE]. + ** ^With the "v2" interface, any of the other [result codes] or + ** [extended result codes] might be returned as well. + ** + ** ^[SQLITE_BUSY] means that the database engine was unable to acquire the + ** database locks it needs to do its job. ^If the statement is a [COMMIT] + ** or occurs outside of an explicit transaction, then you can retry the + ** statement. If the statement is not a [COMMIT] and occurs within an + ** explicit transaction then you should rollback the transaction before + ** continuing. + ** + ** ^[SQLITE_DONE] means that the statement has finished executing + ** successfully. sqlite3_step() should not be called again on this virtual + ** machine without first calling [sqlite3_reset()] to reset the virtual + ** machine back to its initial state. + ** + ** ^If the SQL statement being executed returns any data, then [SQLITE_ROW] + ** is returned each time a new row of data is ready for processing by the + ** caller. The values may be accessed using the [column access functions]. + ** sqlite3_step() is called again to retrieve the next row of data. + ** + ** ^[SQLITE_ERROR] means that a run-time error (such as a constraint + ** violation) has occurred. sqlite3_step() should not be called again on + ** the VM. More information may be found by calling [sqlite3_errmsg()]. + ** ^With the legacy interface, a more specific error code (for example, + ** [SQLITE_INTERRUPT], [SQLITE_SCHEMA], [SQLITE_CORRUPT], and so forth) + ** can be obtained by calling [sqlite3_reset()] on the + ** [prepared statement]. ^In the "v2" interface, + ** the more specific error code is returned directly by sqlite3_step(). + ** + ** [SQLITE_MISUSE] means that the this routine was called inappropriately. + ** Perhaps it was called on a [prepared statement] that has + ** already been [sqlite3_finalize | finalized] or on one that had + ** previously returned [SQLITE_ERROR] or [SQLITE_DONE]. Or it could + ** be the case that the same database connection is being used by two or + ** more threads at the same moment in time. + ** + ** For all versions of SQLite up to and including 3.6.23.1, a call to + ** [sqlite3_reset()] was required after sqlite3_step() returned anything + ** 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() began + ** calling [sqlite3_reset()] automatically in this circumstance rather + ** than returning [SQLITE_MISUSE]. This is not considered a compatibility + ** break because any application that ever receives an SQLITE_MISUSE error + ** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option + ** can be used to restore the legacy behavior. + ** + ** Goofy Interface Alert: In the legacy interface, the sqlite3_step() + ** API always returns a generic error code, [SQLITE_ERROR], following any + ** error other than [SQLITE_BUSY] and [SQLITE_MISUSE]. You must call + ** [sqlite3_reset()] or [sqlite3_finalize()] in order to find one of the + ** specific [error codes] that better describes the error. + ** We admit that this is a goofy design. The problem has been fixed + ** with the "v2" interface. If you prepare all of your SQL statements + ** using [sqlite3_prepare_v3()] or [sqlite3_prepare_v2()] + ** or [sqlite3_prepare16_v2()] or [sqlite3_prepare16_v3()] instead + ** of the legacy [sqlite3_prepare()] and [sqlite3_prepare16()] interfaces, + ** then the more specific [error codes] are returned directly + ** by sqlite3_step(). The use of the "vX" interfaces is recommended. + */ + SQLITE_API int sqlite3_step(sqlite3_stmt*); + + /* + ** CAPI3REF: Number of columns in a result set + ** METHOD: sqlite3_stmt + ** + ** ^The sqlite3_data_count(P) interface returns the number of columns in the + ** current row of the result set of [prepared statement] P. + ** ^If prepared statement P does not have results ready to return + ** (via calls to the [sqlite3_column_int | sqlite3_column()] family of + ** interfaces) then sqlite3_data_count(P) returns 0. + ** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer. + ** ^The sqlite3_data_count(P) routine returns 0 if the previous call to + ** [sqlite3_step](P) returned [SQLITE_DONE]. ^The sqlite3_data_count(P) + ** will return non-zero if previous call to [sqlite3_step](P) returned + ** [SQLITE_ROW], except in the case of the [PRAGMA incremental_vacuum] + ** where it always returns zero since each step of that multi-step + ** pragma returns 0 columns of data. + ** + ** See also: [sqlite3_column_count()] + */ + SQLITE_API int sqlite3_data_count(sqlite3_stmt* pStmt); /* ** CAPI3REF: Fundamental Datatypes @@ -4740,460 +4848,450 @@ ** SQLite version 2 and SQLite version 3 should use SQLITE3_TEXT, not ** SQLITE_TEXT. */ -#define SQLITE_INTEGER 1 -#define SQLITE_FLOAT 2 -#define SQLITE_BLOB 4 -#define SQLITE_NULL 5 +#define SQLITE_INTEGER 1 +#define SQLITE_FLOAT 2 +#define SQLITE_BLOB 4 +#define SQLITE_NULL 5 #ifdef SQLITE_TEXT -# undef SQLITE_TEXT +#undef SQLITE_TEXT #else -# define SQLITE_TEXT 3 +#define SQLITE_TEXT 3 #endif -#define SQLITE3_TEXT 3 - -/* -** CAPI3REF: Result Values From A Query -** KEYWORDS: {column access functions} -** METHOD: sqlite3_stmt -** -** Summary: -**
    -**
    sqlite3_column_blobBLOB result -**
    sqlite3_column_doubleREAL result -**
    sqlite3_column_int32-bit INTEGER result -**
    sqlite3_column_int6464-bit INTEGER result -**
    sqlite3_column_textUTF-8 TEXT result -**
    sqlite3_column_text16UTF-16 TEXT result -**
    sqlite3_column_valueThe result as an -** [sqlite3_value|unprotected sqlite3_value] object. -**
        -**
    sqlite3_column_bytesSize of a BLOB -** or a UTF-8 TEXT result in bytes -**
    sqlite3_column_bytes16   -** →  Size of UTF-16 -** TEXT in bytes -**
    sqlite3_column_typeDefault -** datatype of the result -**
    -** -** Details: -** -** ^These routines return information about a single column of the current -** result row of a query. ^In every case the first argument is a pointer -** to the [prepared statement] that is being evaluated (the [sqlite3_stmt*] -** that was returned from [sqlite3_prepare_v2()] or one of its variants) -** and the second argument is the index of the column for which information -** should be returned. ^The leftmost column of the result set has the index 0. -** ^The number of columns in the result can be determined using -** [sqlite3_column_count()]. -** -** If the SQL statement does not currently point to a valid row, or if the -** column index is out of range, the result is undefined. -** These routines may only be called when the most recent call to -** [sqlite3_step()] has returned [SQLITE_ROW] and neither -** [sqlite3_reset()] nor [sqlite3_finalize()] have been called subsequently. -** If any of these routines are called after [sqlite3_reset()] or -** [sqlite3_finalize()] or after [sqlite3_step()] has returned -** something other than [SQLITE_ROW], the results are undefined. -** If [sqlite3_step()] or [sqlite3_reset()] or [sqlite3_finalize()] -** are called from a different thread while any of these routines -** are pending, then the results are undefined. -** -** The first six interfaces (_blob, _double, _int, _int64, _text, and _text16) -** each return the value of a result column in a specific data format. If -** the result column is not initially in the requested format (for example, -** if the query returns an integer but the sqlite3_column_text() interface -** is used to extract the value) then an automatic type conversion is performed. -** -** ^The sqlite3_column_type() routine returns the -** [SQLITE_INTEGER | datatype code] for the initial data type -** of the result column. ^The returned value is one of [SQLITE_INTEGER], -** [SQLITE_FLOAT], [SQLITE_TEXT], [SQLITE_BLOB], or [SQLITE_NULL]. -** The return value of sqlite3_column_type() can be used to decide which -** of the first six interface should be used to extract the column value. -** The value returned by sqlite3_column_type() is only meaningful if no -** automatic type conversions have occurred for the value in question. -** After a type conversion, the result of calling sqlite3_column_type() -** is undefined, though harmless. Future -** versions of SQLite may change the behavior of sqlite3_column_type() -** following a type conversion. -** -** If the result is a BLOB or a TEXT string, then the sqlite3_column_bytes() -** or sqlite3_column_bytes16() interfaces can be used to determine the size -** of that BLOB or string. -** -** ^If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes() -** routine returns the number of bytes in that BLOB or string. -** ^If the result is a UTF-16 string, then sqlite3_column_bytes() converts -** the string to UTF-8 and then returns the number of bytes. -** ^If the result is a numeric value then sqlite3_column_bytes() uses -** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns -** the number of bytes in that string. -** ^If the result is NULL, then sqlite3_column_bytes() returns zero. -** -** ^If the result is a BLOB or UTF-16 string then the sqlite3_column_bytes16() -** routine returns the number of bytes in that BLOB or string. -** ^If the result is a UTF-8 string, then sqlite3_column_bytes16() converts -** the string to UTF-16 and then returns the number of bytes. -** ^If the result is a numeric value then sqlite3_column_bytes16() uses -** [sqlite3_snprintf()] to convert that value to a UTF-16 string and returns -** the number of bytes in that string. -** ^If the result is NULL, then sqlite3_column_bytes16() returns zero. -** -** ^The values returned by [sqlite3_column_bytes()] and -** [sqlite3_column_bytes16()] do not include the zero terminators at the end -** of the string. ^For clarity: the values returned by -** [sqlite3_column_bytes()] and [sqlite3_column_bytes16()] are the number of -** bytes in the string, not the number of characters. -** -** ^Strings returned by sqlite3_column_text() and sqlite3_column_text16(), -** even empty strings, are always zero-terminated. ^The return -** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. -** -** 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 -** [sqlite3_bind_value()] and [sqlite3_result_value()]. -** If the [unprotected sqlite3_value] object returned by -** [sqlite3_column_value()] is used in any other way, including calls -** to routines like [sqlite3_value_int()], [sqlite3_value_text()], -** or [sqlite3_value_bytes()], the behavior is not threadsafe. -** Hence, the sqlite3_column_value() interface -** is normally only useful within the implementation of -** [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. -** ^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 -** that are applied: -** -**
    -** -**
    Internal
    Type
    Requested
    Type
    Conversion -** -**
    NULL INTEGER Result is 0 -**
    NULL FLOAT Result is 0.0 -**
    NULL TEXT Result is a NULL pointer -**
    NULL BLOB Result is a NULL pointer -**
    INTEGER FLOAT Convert from integer to float -**
    INTEGER TEXT ASCII rendering of the integer -**
    INTEGER BLOB Same as INTEGER->TEXT -**
    FLOAT INTEGER [CAST] to INTEGER -**
    FLOAT TEXT ASCII rendering of the float -**
    FLOAT BLOB [CAST] to BLOB -**
    TEXT INTEGER [CAST] to INTEGER -**
    TEXT FLOAT [CAST] to REAL -**
    TEXT BLOB No change -**
    BLOB INTEGER [CAST] to INTEGER -**
    BLOB FLOAT [CAST] to REAL -**
    BLOB TEXT Add a zero terminator if needed -**
    -**
    )^ -** -** Note that when type conversions occur, pointers returned by prior -** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or -** sqlite3_column_text16() may be invalidated. -** Type conversions and pointer invalidations might occur -** in the following cases: -** -**
      -**
    • The initial content is a BLOB and sqlite3_column_text() or -** sqlite3_column_text16() is called. A zero-terminator might -** need to be added to the string.
    • -**
    • The initial content is UTF-8 text and sqlite3_column_bytes16() or -** sqlite3_column_text16() is called. The content must be converted -** to UTF-16.
    • -**
    • The initial content is UTF-16 text and sqlite3_column_bytes() or -** sqlite3_column_text() is called. The content must be converted -** to UTF-8.
    • -**
    -** -** ^Conversions between UTF-16be and UTF-16le are always done in place and do -** not invalidate a prior pointer, though of course the content of the buffer -** that the prior pointer references will have been modified. Other kinds -** of conversion are done in place when it is possible, but sometimes they -** are not possible and in those cases prior pointers are invalidated. -** -** The safest policy is to invoke these routines -** in one of the following ways: -** -**
      -**
    • sqlite3_column_text() followed by sqlite3_column_bytes()
    • -**
    • sqlite3_column_blob() followed by sqlite3_column_bytes()
    • -**
    • sqlite3_column_text16() followed by sqlite3_column_bytes16()
    • -**
    -** -** In other words, you should call sqlite3_column_text(), -** sqlite3_column_blob(), or sqlite3_column_text16() first to force the result -** into the desired format, then invoke sqlite3_column_bytes() or -** sqlite3_column_bytes16() to find the size of the result. Do not mix calls -** to sqlite3_column_text() or sqlite3_column_blob() with calls to -** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16() -** with calls to sqlite3_column_bytes(). -** -** ^The pointers returned are valid until a type conversion occurs as -** described above, or until [sqlite3_step()] or [sqlite3_reset()] or -** [sqlite3_finalize()] is called. ^The memory space used to hold strings -** and BLOBs is freed automatically. Do not pass the pointers returned -** from [sqlite3_column_blob()], [sqlite3_column_text()], etc. into -** [sqlite3_free()]. -** -** As long as the input parameters are correct, these routines will only -** fail if an out-of-memory error occurs during a format conversion. -** Only the following subset of interfaces are subject to out-of-memory -** errors: -** -**
      -**
    • sqlite3_column_blob() -**
    • sqlite3_column_text() -**
    • sqlite3_column_text16() -**
    • sqlite3_column_bytes() -**
    • sqlite3_column_bytes16() -**
    -** -** If an out-of-memory error occurs, then the return value from these -** routines is the same as if the column had contained an SQL NULL value. -** Valid SQL NULL returns can be distinguished from out-of-memory errors -** by invoking the [sqlite3_errcode()] immediately after the suspect -** return value is obtained and before any -** other SQLite interface is called on the same [database connection]. -*/ -SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); -SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol); -SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); -SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); -SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt*, int iCol); -SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol); - -/* -** CAPI3REF: Destroy A Prepared Statement Object -** DESTRUCTOR: sqlite3_stmt -** -** ^The sqlite3_finalize() function is called to delete a [prepared statement]. -** ^If the most recent evaluation of the statement encountered no errors -** or if the statement is never been evaluated, then sqlite3_finalize() returns -** SQLITE_OK. ^If the most recent evaluation of statement S failed, then -** sqlite3_finalize(S) returns the appropriate [error code] or -** [extended error code]. -** -** ^The sqlite3_finalize(S) routine can be called at any point during -** the life cycle of [prepared statement] S: -** before statement S is ever evaluated, after -** one or more calls to [sqlite3_reset()], or after any call -** to [sqlite3_step()] regardless of whether or not the statement has -** completed execution. -** -** ^Invoking sqlite3_finalize() on a NULL pointer is a harmless no-op. -** -** The application must finalize every [prepared statement] in order to avoid -** resource leaks. It is a grievous error for the application to try to use -** a prepared statement after it has been finalized. Any use of a prepared -** statement after it has been finalized can result in undefined and -** undesirable behavior such as segfaults and heap corruption. -*/ -SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); - -/* -** CAPI3REF: Reset A Prepared Statement Object -** METHOD: sqlite3_stmt -** -** The sqlite3_reset() function is called to reset a [prepared statement] -** object back to its initial state, ready to be re-executed. -** ^Any SQL statement variables that had values bound to them using -** the [sqlite3_bind_blob | sqlite3_bind_*() API] retain their values. -** Use [sqlite3_clear_bindings()] to reset the bindings. -** -** ^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]. -** -** ^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 does not change the values -** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. -*/ -SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); +#define SQLITE3_TEXT 3 -/* -** CAPI3REF: Create Or Redefine SQL Functions -** KEYWORDS: {function creation routines} -** METHOD: sqlite3 -** -** ^These functions (collectively known as "function creation routines") -** are used to add SQL functions or aggregates or to redefine the behavior -** of existing SQL functions or aggregates. The only differences between -** the three "sqlite3_create_function*" routines are the text encoding -** expected for the second parameter (the name of the function being -** created) and the presence or absence of a destructor callback for -** the application data pointer. Function sqlite3_create_window_function() -** is similar, but allows the user to supply the extra callback functions -** needed by [aggregate window functions]. -** -** ^The first parameter is the [database connection] to which the SQL -** function is to be added. ^If an application uses more than one database -** connection then application-defined SQL functions must be added -** to each database connection separately. -** -** ^The second parameter is the name of the SQL function to be created or -** redefined. ^The length of the name is limited to 255 bytes in a UTF-8 -** representation, exclusive of the zero-terminator. ^Note that the name -** length limit is in UTF-8 bytes, not characters nor UTF-16 bytes. -** ^Any attempt to create a function with a longer name -** will result in [SQLITE_MISUSE] being returned. -** -** ^The third parameter (nArg) -** is the number of arguments that the SQL function or -** aggregate takes. ^If this parameter is -1, then the SQL function or -** aggregate may take any number of arguments between 0 and the limit -** set by [sqlite3_limit]([SQLITE_LIMIT_FUNCTION_ARG]). If the third -** parameter is less than -1 or greater than 127 then the behavior is -** undefined. -** -** ^The fourth parameter, eTextRep, specifies what -** [SQLITE_UTF8 | text encoding] this SQL function prefers for -** its parameters. The application should set this parameter to -** [SQLITE_UTF16LE] if the function implementation invokes -** [sqlite3_value_text16le()] on an input, or [SQLITE_UTF16BE] if the -** implementation invokes [sqlite3_value_text16be()] on an input, or -** [SQLITE_UTF16] if [sqlite3_value_text16()] is used, or [SQLITE_UTF8] -** otherwise. ^The same SQL function may be registered multiple times using -** different preferred text encodings, with different implementations for -** each encoding. -** ^When multiple implementations of the same function are available, SQLite -** will pick the one that involves the least amount of data conversion. -** -** ^The fourth parameter may optionally be ORed with [SQLITE_DETERMINISTIC] -** to signal that the function will always return the same result given -** the same inputs within a single SQL statement. Most SQL functions are -** deterministic. The built-in [random()] SQL function is an example of a -** function that is not deterministic. The SQLite query planner is able to -** perform additional optimizations on deterministic functions, so use -** of the [SQLITE_DETERMINISTIC] flag is recommended where possible. -** -** ^The fourth parameter may also optionally include the [SQLITE_DIRECTONLY] -** flag, which if present prevents the function from being invoked from -** 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 -** 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()].)^ -** -** ^The sixth, seventh and eighth parameters passed to the three -** "sqlite3_create_function*" functions, xFunc, xStep and xFinal, are -** pointers to C-language functions that implement the SQL function or -** aggregate. ^A scalar SQL function requires an implementation of the xFunc -** callback only; NULL pointers must be passed as the xStep and xFinal -** parameters. ^An aggregate SQL function requires an implementation of xStep -** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing -** SQL function or aggregate, pass NULL pointers for all three function -** callbacks. -** -** ^The sixth, seventh, eighth and ninth parameters (xStep, xFinal, xValue -** and xInverse) passed to sqlite3_create_window_function are pointers to -** C-language callbacks that implement the new function. xStep and xFinal -** must both be non-NULL. xValue and xInverse may either both be NULL, in -** which case a regular aggregate function is created, or must both be -** non-NULL, in which case the new function may be used as either an aggregate -** or aggregate window function. More details regarding the implementation -** of aggregate window functions are -** [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 -** 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 -** sqlite3_create_function_v2() fails. ^When the destructor callback is -** invoked, it is passed a single argument which is a copy of the application -** data pointer which was the fifth parameter to sqlite3_create_function_v2(). -** -** ^It is permitted to register multiple implementations of the same -** functions with the same name but with either differing numbers of -** arguments or differing preferred text encodings. ^SQLite will use -** the implementation that most closely matches the way in which the -** SQL function is used. ^A function implementation with a non-negative -** nArg parameter is a better match than a function implementation with -** a negative nArg. ^A function where the preferred text encoding -** matches the database encoding is a better -** match than a function where the encoding is different. -** ^A function where the encoding difference is between UTF16le and UTF16be -** is a closer match than a function where the encoding difference is -** between UTF8 and UTF16. -** -** ^Built-in functions may be overloaded by new application-defined functions. -** -** ^An application-defined function is permitted to call other -** SQLite interfaces. However, such calls must not -** close the database connection nor finalize or reset the prepared -** statement in which the function is running. -*/ -SQLITE_API int sqlite3_create_function( - sqlite3 *db, - const char *zFunctionName, - int nArg, - int eTextRep, - void *pApp, - void (*xFunc)(sqlite3_context*,int,sqlite3_value**), - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*) -); -SQLITE_API int sqlite3_create_function16( - sqlite3 *db, - const void *zFunctionName, - int nArg, - int eTextRep, - void *pApp, - void (*xFunc)(sqlite3_context*,int,sqlite3_value**), - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*) -); -SQLITE_API int sqlite3_create_function_v2( - sqlite3 *db, - const char *zFunctionName, - int nArg, - int eTextRep, - void *pApp, - void (*xFunc)(sqlite3_context*,int,sqlite3_value**), - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*), - void(*xDestroy)(void*) -); -SQLITE_API int sqlite3_create_window_function( - sqlite3 *db, - const char *zFunctionName, - int nArg, - int eTextRep, - void *pApp, - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*), - void (*xValue)(sqlite3_context*), - void (*xInverse)(sqlite3_context*,int,sqlite3_value**), - void(*xDestroy)(void*) -); + /* + ** CAPI3REF: Result Values From A Query + ** KEYWORDS: {column access functions} + ** METHOD: sqlite3_stmt + ** + ** Summary: + **
    + **
    sqlite3_column_blobBLOB result + **
    sqlite3_column_doubleREAL result + **
    sqlite3_column_int32-bit INTEGER result + **
    sqlite3_column_int6464-bit INTEGER result + **
    sqlite3_column_textUTF-8 TEXT result + **
    sqlite3_column_text16UTF-16 TEXT result + **
    sqlite3_column_valueThe result as an + ** [sqlite3_value|unprotected sqlite3_value] object. + **
        + **
    sqlite3_column_bytesSize of a BLOB + ** or a UTF-8 TEXT result in bytes + **
    sqlite3_column_bytes16   + ** →  Size of UTF-16 + ** TEXT in bytes + **
    sqlite3_column_typeDefault + ** datatype of the result + **
    + ** + ** Details: + ** + ** ^These routines return information about a single column of the current + ** result row of a query. ^In every case the first argument is a pointer + ** to the [prepared statement] that is being evaluated (the [sqlite3_stmt*] + ** that was returned from [sqlite3_prepare_v2()] or one of its variants) + ** and the second argument is the index of the column for which information + ** should be returned. ^The leftmost column of the result set has the index 0. + ** ^The number of columns in the result can be determined using + ** [sqlite3_column_count()]. + ** + ** If the SQL statement does not currently point to a valid row, or if the + ** column index is out of range, the result is undefined. + ** These routines may only be called when the most recent call to + ** [sqlite3_step()] has returned [SQLITE_ROW] and neither + ** [sqlite3_reset()] nor [sqlite3_finalize()] have been called subsequently. + ** If any of these routines are called after [sqlite3_reset()] or + ** [sqlite3_finalize()] or after [sqlite3_step()] has returned + ** something other than [SQLITE_ROW], the results are undefined. + ** If [sqlite3_step()] or [sqlite3_reset()] or [sqlite3_finalize()] + ** are called from a different thread while any of these routines + ** are pending, then the results are undefined. + ** + ** The first six interfaces (_blob, _double, _int, _int64, _text, and _text16) + ** each return the value of a result column in a specific data format. If + ** the result column is not initially in the requested format (for example, + ** if the query returns an integer but the sqlite3_column_text() interface + ** is used to extract the value) then an automatic type conversion is performed. + ** + ** ^The sqlite3_column_type() routine returns the + ** [SQLITE_INTEGER | datatype code] for the initial data type + ** of the result column. ^The returned value is one of [SQLITE_INTEGER], + ** [SQLITE_FLOAT], [SQLITE_TEXT], [SQLITE_BLOB], or [SQLITE_NULL]. + ** The return value of sqlite3_column_type() can be used to decide which + ** of the first six interface should be used to extract the column value. + ** The value returned by sqlite3_column_type() is only meaningful if no + ** automatic type conversions have occurred for the value in question. + ** After a type conversion, the result of calling sqlite3_column_type() + ** is undefined, though harmless. Future + ** versions of SQLite may change the behavior of sqlite3_column_type() + ** following a type conversion. + ** + ** If the result is a BLOB or a TEXT string, then the sqlite3_column_bytes() + ** or sqlite3_column_bytes16() interfaces can be used to determine the size + ** of that BLOB or string. + ** + ** ^If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes() + ** routine returns the number of bytes in that BLOB or string. + ** ^If the result is a UTF-16 string, then sqlite3_column_bytes() converts + ** the string to UTF-8 and then returns the number of bytes. + ** ^If the result is a numeric value then sqlite3_column_bytes() uses + ** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns + ** the number of bytes in that string. + ** ^If the result is NULL, then sqlite3_column_bytes() returns zero. + ** + ** ^If the result is a BLOB or UTF-16 string then the sqlite3_column_bytes16() + ** routine returns the number of bytes in that BLOB or string. + ** ^If the result is a UTF-8 string, then sqlite3_column_bytes16() converts + ** the string to UTF-16 and then returns the number of bytes. + ** ^If the result is a numeric value then sqlite3_column_bytes16() uses + ** [sqlite3_snprintf()] to convert that value to a UTF-16 string and returns + ** the number of bytes in that string. + ** ^If the result is NULL, then sqlite3_column_bytes16() returns zero. + ** + ** ^The values returned by [sqlite3_column_bytes()] and + ** [sqlite3_column_bytes16()] do not include the zero terminators at the end + ** of the string. ^For clarity: the values returned by + ** [sqlite3_column_bytes()] and [sqlite3_column_bytes16()] are the number of + ** bytes in the string, not the number of characters. + ** + ** ^Strings returned by sqlite3_column_text() and sqlite3_column_text16(), + ** even empty strings, are always zero-terminated. ^The return + ** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. + ** + ** 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 + ** [sqlite3_bind_value()] and [sqlite3_result_value()]. + ** If the [unprotected sqlite3_value] object returned by + ** [sqlite3_column_value()] is used in any other way, including calls + ** to routines like [sqlite3_value_int()], [sqlite3_value_text()], + ** or [sqlite3_value_bytes()], the behavior is not threadsafe. + ** Hence, the sqlite3_column_value() interface + ** is normally only useful within the implementation of + ** [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. + ** ^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 + ** that are applied: + ** + **
    + ** + **
    Internal
    Type
    Requested
    Type
    Conversion + ** + **
    NULL INTEGER Result is 0 + **
    NULL FLOAT Result is 0.0 + **
    NULL TEXT Result is a NULL pointer + **
    NULL BLOB Result is a NULL pointer + **
    INTEGER FLOAT Convert from integer to float + **
    INTEGER TEXT ASCII rendering of the integer + **
    INTEGER BLOB Same as INTEGER->TEXT + **
    FLOAT INTEGER [CAST] to INTEGER + **
    FLOAT TEXT ASCII rendering of the float + **
    FLOAT BLOB [CAST] to BLOB + **
    TEXT INTEGER [CAST] to INTEGER + **
    TEXT FLOAT [CAST] to REAL + **
    TEXT BLOB No change + **
    BLOB INTEGER [CAST] to INTEGER + **
    BLOB FLOAT [CAST] to REAL + **
    BLOB TEXT Add a zero terminator if needed + **
    + **
    )^ + ** + ** Note that when type conversions occur, pointers returned by prior + ** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or + ** sqlite3_column_text16() may be invalidated. + ** Type conversions and pointer invalidations might occur + ** in the following cases: + ** + **
      + **
    • The initial content is a BLOB and sqlite3_column_text() or + ** sqlite3_column_text16() is called. A zero-terminator might + ** need to be added to the string.
    • + **
    • The initial content is UTF-8 text and sqlite3_column_bytes16() or + ** sqlite3_column_text16() is called. The content must be converted + ** to UTF-16.
    • + **
    • The initial content is UTF-16 text and sqlite3_column_bytes() or + ** sqlite3_column_text() is called. The content must be converted + ** to UTF-8.
    • + **
    + ** + ** ^Conversions between UTF-16be and UTF-16le are always done in place and do + ** not invalidate a prior pointer, though of course the content of the buffer + ** that the prior pointer references will have been modified. Other kinds + ** of conversion are done in place when it is possible, but sometimes they + ** are not possible and in those cases prior pointers are invalidated. + ** + ** The safest policy is to invoke these routines + ** in one of the following ways: + ** + **
      + **
    • sqlite3_column_text() followed by sqlite3_column_bytes()
    • + **
    • sqlite3_column_blob() followed by sqlite3_column_bytes()
    • + **
    • sqlite3_column_text16() followed by sqlite3_column_bytes16()
    • + **
    + ** + ** In other words, you should call sqlite3_column_text(), + ** sqlite3_column_blob(), or sqlite3_column_text16() first to force the result + ** into the desired format, then invoke sqlite3_column_bytes() or + ** sqlite3_column_bytes16() to find the size of the result. Do not mix calls + ** to sqlite3_column_text() or sqlite3_column_blob() with calls to + ** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16() + ** with calls to sqlite3_column_bytes(). + ** + ** ^The pointers returned are valid until a type conversion occurs as + ** described above, or until [sqlite3_step()] or [sqlite3_reset()] or + ** [sqlite3_finalize()] is called. ^The memory space used to hold strings + ** and BLOBs is freed automatically. Do not pass the pointers returned + ** from [sqlite3_column_blob()], [sqlite3_column_text()], etc. into + ** [sqlite3_free()]. + ** + ** As long as the input parameters are correct, these routines will only + ** fail if an out-of-memory error occurs during a format conversion. + ** Only the following subset of interfaces are subject to out-of-memory + ** errors: + ** + **
      + **
    • sqlite3_column_blob() + **
    • sqlite3_column_text() + **
    • sqlite3_column_text16() + **
    • sqlite3_column_bytes() + **
    • sqlite3_column_bytes16() + **
    + ** + ** If an out-of-memory error occurs, then the return value from these + ** routines is the same as if the column had contained an SQL NULL value. + ** Valid SQL NULL returns can be distinguished from out-of-memory errors + ** by invoking the [sqlite3_errcode()] immediately after the suspect + ** return value is obtained and before any + ** other SQLite interface is called on the same [database connection]. + */ + SQLITE_API const void* sqlite3_column_blob(sqlite3_stmt*, int iCol); + SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol); + SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol); + SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); + SQLITE_API const unsigned char* sqlite3_column_text(sqlite3_stmt*, int iCol); + SQLITE_API const void* sqlite3_column_text16(sqlite3_stmt*, int iCol); + SQLITE_API sqlite3_value* sqlite3_column_value(sqlite3_stmt*, int iCol); + SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol); + SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt*, int iCol); + SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol); + + /* + ** CAPI3REF: Destroy A Prepared Statement Object + ** DESTRUCTOR: sqlite3_stmt + ** + ** ^The sqlite3_finalize() function is called to delete a [prepared statement]. + ** ^If the most recent evaluation of the statement encountered no errors + ** or if the statement is never been evaluated, then sqlite3_finalize() returns + ** SQLITE_OK. ^If the most recent evaluation of statement S failed, then + ** sqlite3_finalize(S) returns the appropriate [error code] or + ** [extended error code]. + ** + ** ^The sqlite3_finalize(S) routine can be called at any point during + ** the life cycle of [prepared statement] S: + ** before statement S is ever evaluated, after + ** one or more calls to [sqlite3_reset()], or after any call + ** to [sqlite3_step()] regardless of whether or not the statement has + ** completed execution. + ** + ** ^Invoking sqlite3_finalize() on a NULL pointer is a harmless no-op. + ** + ** The application must finalize every [prepared statement] in order to avoid + ** resource leaks. It is a grievous error for the application to try to use + ** a prepared statement after it has been finalized. Any use of a prepared + ** statement after it has been finalized can result in undefined and + ** undesirable behavior such as segfaults and heap corruption. + */ + SQLITE_API int sqlite3_finalize(sqlite3_stmt* pStmt); + + /* + ** CAPI3REF: Reset A Prepared Statement Object + ** METHOD: sqlite3_stmt + ** + ** The sqlite3_reset() function is called to reset a [prepared statement] + ** object back to its initial state, ready to be re-executed. + ** ^Any SQL statement variables that had values bound to them using + ** the [sqlite3_bind_blob | sqlite3_bind_*() API] retain their values. + ** Use [sqlite3_clear_bindings()] to reset the bindings. + ** + ** ^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]. + ** + ** ^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 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} + ** METHOD: sqlite3 + ** + ** ^These functions (collectively known as "function creation routines") + ** are used to add SQL functions or aggregates or to redefine the behavior + ** of existing SQL functions or aggregates. The only differences between + ** the three "sqlite3_create_function*" routines are the text encoding + ** expected for the second parameter (the name of the function being + ** created) and the presence or absence of a destructor callback for + ** the application data pointer. Function sqlite3_create_window_function() + ** is similar, but allows the user to supply the extra callback functions + ** needed by [aggregate window functions]. + ** + ** ^The first parameter is the [database connection] to which the SQL + ** function is to be added. ^If an application uses more than one database + ** connection then application-defined SQL functions must be added + ** to each database connection separately. + ** + ** ^The second parameter is the name of the SQL function to be created or + ** redefined. ^The length of the name is limited to 255 bytes in a UTF-8 + ** representation, exclusive of the zero-terminator. ^Note that the name + ** length limit is in UTF-8 bytes, not characters nor UTF-16 bytes. + ** ^Any attempt to create a function with a longer name + ** will result in [SQLITE_MISUSE] being returned. + ** + ** ^The third parameter (nArg) + ** is the number of arguments that the SQL function or + ** aggregate takes. ^If this parameter is -1, then the SQL function or + ** aggregate may take any number of arguments between 0 and the limit + ** set by [sqlite3_limit]([SQLITE_LIMIT_FUNCTION_ARG]). If the third + ** parameter is less than -1 or greater than 127 then the behavior is + ** undefined. + ** + ** ^The fourth parameter, eTextRep, specifies what + ** [SQLITE_UTF8 | text encoding] this SQL function prefers for + ** its parameters. The application should set this parameter to + ** [SQLITE_UTF16LE] if the function implementation invokes + ** [sqlite3_value_text16le()] on an input, or [SQLITE_UTF16BE] if the + ** implementation invokes [sqlite3_value_text16be()] on an input, or + ** [SQLITE_UTF16] if [sqlite3_value_text16()] is used, or [SQLITE_UTF8] + ** otherwise. ^The same SQL function may be registered multiple times using + ** different preferred text encodings, with different implementations for + ** each encoding. + ** ^When multiple implementations of the same function are available, SQLite + ** will pick the one that involves the least amount of data conversion. + ** + ** ^The fourth parameter may optionally be ORed with [SQLITE_DETERMINISTIC] + ** to signal that the function will always return the same result given + ** the same inputs within a single SQL statement. Most SQL functions are + ** deterministic. The built-in [random()] SQL function is an example of a + ** function that is not deterministic. The SQLite query planner is able to + ** perform additional optimizations on deterministic functions, so use + ** of the [SQLITE_DETERMINISTIC] flag is recommended where possible. + ** + ** ^The fourth parameter may also optionally include the [SQLITE_DIRECTONLY] + ** flag, which if present prevents the function from being invoked from + ** 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 + ** 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()].)^ + ** + ** ^The sixth, seventh and eighth parameters passed to the three + ** "sqlite3_create_function*" functions, xFunc, xStep and xFinal, are + ** pointers to C-language functions that implement the SQL function or + ** aggregate. ^A scalar SQL function requires an implementation of the xFunc + ** callback only; NULL pointers must be passed as the xStep and xFinal + ** parameters. ^An aggregate SQL function requires an implementation of xStep + ** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing + ** SQL function or aggregate, pass NULL pointers for all three function + ** callbacks. + ** + ** ^The sixth, seventh, eighth and ninth parameters (xStep, xFinal, xValue + ** and xInverse) passed to sqlite3_create_window_function are pointers to + ** C-language callbacks that implement the new function. xStep and xFinal + ** must both be non-NULL. xValue and xInverse may either both be NULL, in + ** which case a regular aggregate function is created, or must both be + ** non-NULL, in which case the new function may be used as either an aggregate + ** or aggregate window function. More details regarding the implementation + ** of aggregate window functions are + ** [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 + ** 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 + ** sqlite3_create_function_v2() fails. ^When the destructor callback is + ** invoked, it is passed a single argument which is a copy of the application + ** data pointer which was the fifth parameter to sqlite3_create_function_v2(). + ** + ** ^It is permitted to register multiple implementations of the same + ** functions with the same name but with either differing numbers of + ** arguments or differing preferred text encodings. ^SQLite will use + ** the implementation that most closely matches the way in which the + ** SQL function is used. ^A function implementation with a non-negative + ** nArg parameter is a better match than a function implementation with + ** a negative nArg. ^A function where the preferred text encoding + ** matches the database encoding is a better + ** match than a function where the encoding is different. + ** ^A function where the encoding difference is between UTF16le and UTF16be + ** is a closer match than a function where the encoding difference is + ** between UTF8 and UTF16. + ** + ** ^Built-in functions may be overloaded by new application-defined functions. + ** + ** ^An application-defined function is permitted to call other + ** SQLite interfaces. However, such calls must not + ** close the database connection nor finalize or reset the prepared + ** statement in which the function is running. + */ + SQLITE_API int sqlite3_create_function(sqlite3* db, + const char* zFunctionName, + int nArg, + int eTextRep, + void* pApp, + void (*xFunc)(sqlite3_context*, int, sqlite3_value**), + void (*xStep)(sqlite3_context*, int, sqlite3_value**), + void (*xFinal)(sqlite3_context*)); + SQLITE_API int sqlite3_create_function16(sqlite3* db, + const void* zFunctionName, + int nArg, + int eTextRep, + void* pApp, + void (*xFunc)(sqlite3_context*, int, sqlite3_value**), + void (*xStep)(sqlite3_context*, int, sqlite3_value**), + void (*xFinal)(sqlite3_context*)); + SQLITE_API int sqlite3_create_function_v2(sqlite3* db, + const char* zFunctionName, + int nArg, + int eTextRep, + void* pApp, + void (*xFunc)(sqlite3_context*, int, sqlite3_value**), + void (*xStep)(sqlite3_context*, int, sqlite3_value**), + void (*xFinal)(sqlite3_context*), + void (*xDestroy)(void*)); + SQLITE_API int sqlite3_create_window_function(sqlite3* db, + const char* zFunctionName, + int nArg, + int eTextRep, + void* pApp, + void (*xStep)(sqlite3_context*, int, sqlite3_value**), + void (*xFinal)(sqlite3_context*), + void (*xValue)(sqlite3_context*), + void (*xInverse)(sqlite3_context*, int, sqlite3_value**), + void (*xDestroy)(void*)); /* ** CAPI3REF: Text Encodings @@ -5201,12 +5299,12 @@ ** These constant define integer codes that represent the various ** text encodings supported by SQLite. */ -#define SQLITE_UTF8 1 /* IMP: R-37514-35566 */ -#define SQLITE_UTF16LE 2 /* IMP: R-03371-37637 */ -#define SQLITE_UTF16BE 3 /* IMP: R-51971-34154 */ -#define SQLITE_UTF16 4 /* Use native byte order */ -#define SQLITE_ANY 5 /* Deprecated */ -#define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */ +#define SQLITE_UTF8 1 /* IMP: R-37514-35566 */ +#define SQLITE_UTF16LE 2 /* IMP: R-03371-37637 */ +#define SQLITE_UTF16BE 3 /* IMP: R-51971-34154 */ +#define SQLITE_UTF16 4 /* Use native byte order */ +#define SQLITE_ANY 5 /* Deprecated */ +#define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */ /* ** CAPI3REF: Function Flags @@ -5273,10 +5371,10 @@ ** ** */ -#define SQLITE_DETERMINISTIC 0x000000800 -#define SQLITE_DIRECTONLY 0x000080000 -#define SQLITE_SUBTYPE 0x000100000 -#define SQLITE_INNOCUOUS 0x000200000 +#define SQLITE_DETERMINISTIC 0x000000800 +#define SQLITE_DIRECTONLY 0x000080000 +#define SQLITE_SUBTYPE 0x000100000 +#define SQLITE_INNOCUOUS 0x000200000 /* ** CAPI3REF: Deprecated Functions @@ -5289,814 +5387,794 @@ ** these functions, we will not explain what they do. */ #ifndef SQLITE_OMIT_DEPRECATED -SQLITE_API SQLITE_DEPRECATED int sqlite3_aggregate_count(sqlite3_context*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void); -SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void); -SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int), - void*,sqlite3_int64); + SQLITE_API SQLITE_DEPRECATED int sqlite3_aggregate_count(sqlite3_context*); + SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*); + SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); + SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void); + SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void); + SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void (*)(void*, sqlite3_int64, int), void*, sqlite3_int64); #endif -/* -** CAPI3REF: Obtaining SQL Values -** METHOD: sqlite3_value -** -** Summary: -**
    -**
    sqlite3_value_blobBLOB value -**
    sqlite3_value_doubleREAL value -**
    sqlite3_value_int32-bit INTEGER value -**
    sqlite3_value_int6464-bit INTEGER value -**
    sqlite3_value_pointerPointer value -**
    sqlite3_value_textUTF-8 TEXT value -**
    sqlite3_value_text16UTF-16 TEXT value in -** the native byteorder -**
    sqlite3_value_text16beUTF-16be TEXT value -**
    sqlite3_value_text16leUTF-16le TEXT value -**
        -**
    sqlite3_value_bytesSize of a BLOB -** or a UTF-8 TEXT in bytes -**
    sqlite3_value_bytes16   -** →  Size of UTF-16 -** TEXT in bytes -**
    sqlite3_value_typeDefault -** datatype of the value -**
    sqlite3_value_numeric_type   -** →  Best numeric datatype of the value -**
    sqlite3_value_nochange   -** →  True if the column is unchanged in an UPDATE -** against a virtual table. -**
    sqlite3_value_frombind   -** →  True if value originated from a [bound parameter] -**
    -** -** Details: -** -** These routines extract type, size, and content information from -** [protected sqlite3_value] objects. Protected sqlite3_value objects -** are used to pass parameter information into the functions that -** implement [application-defined SQL functions] and [virtual tables]. -** -** These routines work only with [protected sqlite3_value] objects. -** Any attempt to use these routines on an [unprotected sqlite3_value] -** is not threadsafe. -** -** ^These routines work just like the corresponding [column access functions] -** except that these routines take a single [protected sqlite3_value] object -** pointer instead of a [sqlite3_stmt*] pointer and an integer column number. -** -** ^The sqlite3_value_text16() interface extracts a UTF-16 string -** in the native byte-order of the host machine. ^The -** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces -** extract UTF-16 strings as big-endian and little-endian respectively. -** -** ^If [sqlite3_value] object V was initialized -** using [sqlite3_bind_pointer(S,I,P,X,D)] or [sqlite3_result_pointer(C,P,X,D)] -** and if X and Y are strings that compare equal according to strcmp(X,Y), -** then sqlite3_value_pointer(V,Y) will return the pointer P. ^Otherwise, -** sqlite3_value_pointer(V,Y) returns a NULL. The sqlite3_bind_pointer() -** routine is part of the [pointer passing interface] added for SQLite 3.20.0. -** -** ^(The sqlite3_value_type(V) interface returns the -** [SQLITE_INTEGER | datatype code] for the initial datatype of the -** [sqlite3_value] object V. The returned value is one of [SQLITE_INTEGER], -** [SQLITE_FLOAT], [SQLITE_TEXT], [SQLITE_BLOB], or [SQLITE_NULL].)^ -** Other interfaces might change the datatype for an sqlite3_value object. -** For example, if the datatype is initially SQLITE_INTEGER and -** sqlite3_value_text(V) is called to extract a text value for that -** integer, then subsequent calls to sqlite3_value_type(V) might return -** SQLITE_TEXT. Whether or not a persistent internal datatype conversion -** occurs is undefined and may change from one release of SQLite to the next. -** -** ^(The sqlite3_value_numeric_type() interface attempts to apply -** numeric affinity to the value. This means that an attempt is -** made to convert the value to an integer or floating point. If -** such a conversion is possible without loss of information (in other -** words, if the value is a string that looks like a number) -** then the conversion is performed. Otherwise no conversion occurs. -** The [SQLITE_INTEGER | datatype] after conversion is returned.)^ -** -** ^Within the [xUpdate] method of a [virtual table], the -** sqlite3_value_nochange(X) interface returns true if and only if -** the column corresponding to X is unchanged by the UPDATE operation -** that the xUpdate method call was invoked to implement and if -** and the prior [xColumn] method call that was invoked to extracted -** the value for that column returned without setting a result (probably -** because it queried [sqlite3_vtab_nochange()] and found that the column -** was unchanging). ^Within an [xUpdate] method, any value for which -** sqlite3_value_nochange(X) is true will in all other respects appear -** to be a NULL value. If sqlite3_value_nochange(X) is invoked anywhere other -** than within an [xUpdate] method call for an UPDATE statement, then -** the return value is arbitrary and meaningless. -** -** ^The sqlite3_value_frombind(X) interface returns non-zero if the -** value X originated from one of the [sqlite3_bind_int|sqlite3_bind()] -** interfaces. ^If X comes from an SQL literal value, or a table column, -** or an expression, then sqlite3_value_frombind(X) returns zero. -** -** Please pay particular attention to the fact that the pointer returned -** from [sqlite3_value_blob()], [sqlite3_value_text()], or -** [sqlite3_value_text16()] can be invalidated by a subsequent call to -** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()], -** or [sqlite3_value_text16()]. -** -** These routines must be called from the same thread as -** the SQL function that supplied the [sqlite3_value*] parameters. -** -** As long as the input parameter is correct, these routines can only -** fail if an out-of-memory error occurs during a format conversion. -** Only the following subset of interfaces are subject to out-of-memory -** errors: -** -**
      -**
    • sqlite3_value_blob() -**
    • sqlite3_value_text() -**
    • sqlite3_value_text16() -**
    • sqlite3_value_text16le() -**
    • sqlite3_value_text16be() -**
    • sqlite3_value_bytes() -**
    • sqlite3_value_bytes16() -**
    -** -** If an out-of-memory error occurs, then the return value from these -** routines is the same as if the column had contained an SQL NULL value. -** Valid SQL NULL returns can be distinguished from out-of-memory errors -** by invoking the [sqlite3_errcode()] immediately after the suspect -** return value is obtained and before any -** other SQLite interface is called on the same [database connection]. -*/ -SQLITE_API const void *sqlite3_value_blob(sqlite3_value*); -SQLITE_API double sqlite3_value_double(sqlite3_value*); -SQLITE_API int sqlite3_value_int(sqlite3_value*); -SQLITE_API sqlite3_int64 sqlite3_value_int64(sqlite3_value*); -SQLITE_API void *sqlite3_value_pointer(sqlite3_value*, const char*); -SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); -SQLITE_API int sqlite3_value_bytes(sqlite3_value*); -SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); -SQLITE_API int sqlite3_value_type(sqlite3_value*); -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: Finding The Subtype Of SQL Values -** METHOD: sqlite3_value -** -** The sqlite3_value_subtype(V) function returns the subtype for -** an [application-defined SQL function] argument V. The subtype -** 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. -*/ -SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*); - -/* -** CAPI3REF: Copy And Free SQL Values -** 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 -** 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. -** -** ^The sqlite3_value_free(V) interface frees an [sqlite3_value] object -** previously obtained from [sqlite3_value_dup()]. ^If V is a NULL pointer -** then sqlite3_value_free(V) is a harmless no-op. -*/ -SQLITE_API sqlite3_value *sqlite3_value_dup(const sqlite3_value*); -SQLITE_API void sqlite3_value_free(sqlite3_value*); - -/* -** CAPI3REF: Obtain Aggregate Function Context -** METHOD: sqlite3_context -** -** Implementations of aggregate SQL functions use this -** routine to allocate memory for storing their state. -** -** ^The first time the sqlite3_aggregate_context(C,N) routine is called -** for a particular aggregate function, SQLite allocates -** N bytes of memory, zeroes out that memory, and returns a pointer -** to the new memory. ^On second and subsequent calls to -** sqlite3_aggregate_context() for the same aggregate function instance, -** the same buffer is returned. Sqlite3_aggregate_context() is normally -** called once for each invocation of the xStep callback and then one -** last time when the xFinal callback is invoked. ^(When no rows match -** an aggregate query, the xStep() callback of the aggregate function -** implementation is never called and xFinal() is called exactly once. -** In those cases, sqlite3_aggregate_context() might be called for the -** first time from within xFinal().)^ -** -** ^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. -** -** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is -** determined by the N parameter on 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 -** N=0 in calls to sqlite3_aggregate_context(C,N) so that no -** pointless memory allocations occur. -** -** ^SQLite automatically frees the memory allocated by -** sqlite3_aggregate_context() when the aggregate query concludes. -** -** The first parameter must be a copy of the -** [sqlite3_context | SQL function context] that is the first parameter -** to the xStep or xFinal callback routine that implements the aggregate -** function. -** -** This routine must be called from the same thread in which -** the aggregate SQL function is running. -*/ -SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); - -/* -** CAPI3REF: User Data For Functions -** METHOD: sqlite3_context -** -** ^The sqlite3_user_data() interface returns a copy of -** the pointer that was the pUserData parameter (the 5th parameter) -** of the [sqlite3_create_function()] -** and [sqlite3_create_function16()] routines that originally -** registered the application defined function. -** -** This routine must be called from the same thread in which -** the application-defined function is running. -*/ -SQLITE_API void *sqlite3_user_data(sqlite3_context*); - -/* -** CAPI3REF: Database Connection For Functions -** METHOD: sqlite3_context -** -** ^The sqlite3_context_db_handle() interface returns a copy of -** the pointer to the [database connection] (the 1st parameter) -** of the [sqlite3_create_function()] -** and [sqlite3_create_function16()] routines that originally -** registered the application defined function. -*/ -SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); - -/* -** CAPI3REF: Function Auxiliary Data -** 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. -** 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 -** 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 -** 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 -** 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. -** ^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:
      -**
    • ^(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.)^
    -** -** Note the last bullet 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 -** function parameters that are compile-time constants, including literal -** values and [parameters] and expressions composed from the same.)^ -** -** The value of the N parameter to these interfaces should be non-negative. -** Future enhancements may make use of negative N values to define new -** kinds of function caching behavior. -** -** These routines must be called from the same thread in which -** the SQL function is running. -*/ -SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); -SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); - - -/* -** CAPI3REF: Constants Defining Special Destructor Behavior -** -** These are special values for the destructor that is passed in as the -** final argument to routines like [sqlite3_result_blob()]. ^If the destructor -** argument is SQLITE_STATIC, it means that the content pointer is constant -** and will never change. It does not need to be destroyed. ^The -** SQLITE_TRANSIENT value means that the content will likely change in -** the near future and that SQLite should make its own private copy of -** the content before returning. -** -** The typedef is necessary to work around problems in certain -** C++ compilers. -*/ -typedef void (*sqlite3_destructor_type)(void*); -#define SQLITE_STATIC ((sqlite3_destructor_type)0) -#define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1) - -/* -** CAPI3REF: Setting The Result Of An SQL Function -** METHOD: sqlite3_context -** -** These routines are used by the xFunc or xFinal callbacks that -** implement SQL functions and aggregates. See -** [sqlite3_create_function()] and [sqlite3_create_function16()] -** for additional information. -** -** These functions work very much like the [parameter binding] family of -** functions used to bind values to host parameters in prepared statements. -** Refer to the [SQL parameter] documentation for additional information. -** -** ^The sqlite3_result_blob() interface sets the result from -** an application-defined function to be the BLOB whose content is pointed -** to by the second parameter and which is N bytes long where N is the -** third parameter. -** -** ^The sqlite3_result_zeroblob(C,N) and sqlite3_result_zeroblob64(C,N) -** interfaces set the result of the application-defined function to be -** a BLOB containing all zero bytes and N bytes in size. -** -** ^The sqlite3_result_double() interface sets the result from -** an application-defined function to be a floating point value specified -** by its 2nd argument. -** -** ^The sqlite3_result_error() and sqlite3_result_error16() functions -** cause the implemented SQL function to throw an exception. -** ^SQLite uses the string pointed to by the -** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16() -** as the text of an error message. ^SQLite interprets the error -** message string from sqlite3_result_error() as UTF-8. ^SQLite -** interprets the string from sqlite3_result_error16() as UTF-16 using -** the same [byte-order determination rules] as [sqlite3_bind_text16()]. -** ^If the third parameter to sqlite3_result_error() -** or sqlite3_result_error16() is negative then SQLite takes as the error -** message all text up through the first zero character. -** ^If the third parameter to sqlite3_result_error() or -** sqlite3_result_error16() is non-negative then SQLite takes that many -** bytes (not characters) from the 2nd parameter as the error message. -** ^The sqlite3_result_error() and sqlite3_result_error16() -** routines make a private copy of the error message text before -** they return. Hence, the calling function can deallocate or -** modify the text after they return without harm. -** ^The sqlite3_result_error_code() function changes the error code -** returned by SQLite as a result of an error in a function. ^By default, -** the error code is SQLITE_ERROR. ^A subsequent call to sqlite3_result_error() -** or sqlite3_result_error16() resets the error code to SQLITE_ERROR. -** -** ^The sqlite3_result_error_toobig() interface causes SQLite to throw an -** error indicating that a string or BLOB is too long to represent. -** -** ^The sqlite3_result_error_nomem() interface causes SQLite to throw an -** error indicating that a memory allocation failed. -** -** ^The sqlite3_result_int() interface sets the return value -** of the application-defined function to be the 32-bit signed integer -** value given in the 2nd argument. -** ^The sqlite3_result_int64() interface sets the return value -** of the application-defined function to be the 64-bit signed integer -** value given in the 2nd argument. -** -** ^The sqlite3_result_null() interface sets the return value -** of the application-defined function to be NULL. -** -** ^The sqlite3_result_text(), sqlite3_result_text16(), -** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces -** set the return value of the application-defined function to be -** a text string which is represented as UTF-8, UTF-16 native byte order, -** UTF-16 little endian, or UTF-16 big endian, respectively. -** ^The sqlite3_result_text64() interface sets the return value of an -** application-defined function to be a text string in an encoding -** specified by the fifth (and last) parameter, which must be one -** 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 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 -** 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. -** ^If the 4th parameter to the sqlite3_result_text* interfaces -** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that -** function as the destructor on the text or BLOB result when it has -** finished using that result. -** ^If the 4th parameter to the sqlite3_result_text* interfaces or to -** sqlite3_result_blob is the special constant SQLITE_STATIC, then SQLite -** assumes that the text or BLOB result is in constant space and does not -** copy the content of the parameter nor call a destructor on the content -** when it has finished using that result. -** ^If the 4th parameter to the sqlite3_result_text* interfaces -** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT -** then SQLite makes a copy of the result into space obtained -** from [sqlite3_malloc()] before it returns. -** -** ^For the sqlite3_result_text16(), sqlite3_result_text16le(), and -** sqlite3_result_text16be() routines, and for sqlite3_result_text64() -** when the encoding is not UTF8, if the input UTF16 begins with a -** byte-order mark (BOM, U+FEFF) then the BOM is removed from the -** string and the rest of the string is interpreted according to the -** byte-order specified by the BOM. ^The byte-order specified by -** the BOM at the beginning of the text overrides the byte-order -** specified by the interface procedure. ^So, for example, if -** sqlite3_result_text16le() is invoked with text that begins -** with bytes 0xfe, 0xff (a big-endian byte-order mark) then the -** first two bytes of input are skipped and the remaining input -** is interpreted as UTF16BE text. -** -** ^For UTF16 input text to the sqlite3_result_text16(), -** sqlite3_result_text16be(), sqlite3_result_text16le(), and -** sqlite3_result_text64() routines, if the text contains invalid -** UTF16 characters, the invalid characters might be converted -** into the unicode replacement character, U+FFFD. -** -** ^The sqlite3_result_value() interface sets the result of -** the application-defined function to be a copy of the -** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The -** sqlite3_result_value() interface makes a copy of the [sqlite3_value] -** so that the [sqlite3_value] specified in the parameter may change or -** be deallocated after sqlite3_result_value() returns without harm. -** ^A [protected sqlite3_value] object may always be used where an -** [unprotected sqlite3_value] object is required, so either -** kind of [sqlite3_value] object can be used with this interface. -** -** ^The sqlite3_result_pointer(C,P,T,D) interface sets the result to an -** SQL NULL value, just like [sqlite3_result_null(C)], except that it -** also associates the host-language pointer P or type T with that -** NULL value such that the pointer can be retrieved within an -** [application-defined SQL function] using [sqlite3_value_pointer()]. -** ^If the D parameter is not NULL, then it is a pointer to a destructor -** for the P parameter. ^SQLite invokes D with P as its only argument -** when SQLite is finished with P. The T parameter should be a static -** 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 -** than the one containing the application-defined function that received -** the [sqlite3_context] pointer, the results are undefined. -*/ -SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); -SQLITE_API void sqlite3_result_blob64(sqlite3_context*,const void*, - sqlite3_uint64,void(*)(void*)); -SQLITE_API void sqlite3_result_double(sqlite3_context*, double); -SQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int); -SQLITE_API void sqlite3_result_error16(sqlite3_context*, const void*, int); -SQLITE_API void sqlite3_result_error_toobig(sqlite3_context*); -SQLITE_API void sqlite3_result_error_nomem(sqlite3_context*); -SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); -SQLITE_API void sqlite3_result_int(sqlite3_context*, int); -SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); -SQLITE_API void sqlite3_result_null(sqlite3_context*); -SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); -SQLITE_API void sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64, - void(*)(void*), unsigned char encoding); -SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); -SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); -SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); -SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); -SQLITE_API void sqlite3_result_pointer(sqlite3_context*, void*,const char*,void(*)(void*)); -SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); -SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n); - - -/* -** CAPI3REF: Setting The Subtype Of An SQL Function -** METHOD: sqlite3_context -** -** The sqlite3_result_subtype(C,T) function causes the subtype of -** the result from the [application-defined SQL function] with -** [sqlite3_context] C to be the value T. Only the lower 8 bits -** of the subtype T are preserved in current versions of SQLite; -** higher order bits are discarded. -** The number of subtype bytes preserved by SQLite might increase -** in future releases of SQLite. -*/ -SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int); - -/* -** CAPI3REF: Define New Collating Sequences -** METHOD: sqlite3 -** -** ^These functions add, remove, or modify a [collation] associated -** with the [database connection] specified as the first argument. -** -** ^The name of the collation is a UTF-8 string -** for sqlite3_create_collation() and sqlite3_create_collation_v2() -** and a UTF-16 string in native byte order for sqlite3_create_collation16(). -** ^Collation names that compare equal according to [sqlite3_strnicmp()] are -** considered to be the same name. -** -** ^(The third argument (eTextRep) must be one of the constants: -**
      -**
    • [SQLITE_UTF8], -**
    • [SQLITE_UTF16LE], -**
    • [SQLITE_UTF16BE], -**
    • [SQLITE_UTF16], or -**
    • [SQLITE_UTF16_ALIGNED]. -**
    )^ -** ^The eTextRep argument determines the encoding of strings passed -** to the collating function callback, xCompare. -** ^The [SQLITE_UTF16] and [SQLITE_UTF16_ALIGNED] values for eTextRep -** force strings to be UTF16 with native byte order. -** ^The [SQLITE_UTF16_ALIGNED] value for eTextRep forces strings to begin -** on an even byte address. -** -** ^The fourth argument, pArg, is an application data pointer that is passed -** through as the first argument to the collating function callback. -** -** ^The fifth argument, xCompare, is a pointer to the collating function. -** ^Multiple collating functions can be registered using the same name but -** with different eTextRep parameters and SQLite will use whichever -** function requires the least amount of data transformation. -** ^If the xCompare argument is NULL then the collating function is -** deleted. ^When all collating functions having the same name are deleted, -** that collation is no longer usable. -** -** ^The collating function callback is invoked with a copy of the pArg -** application data pointer and with two strings in the encoding specified -** by the eTextRep argument. The two integer parameters to the collating -** function callback are the length of the two strings, in bytes. The collating -** function must return an integer that is negative, zero, or positive -** if the first string is less than, equal to, or greater than the second, -** respectively. A collating function must always return the same answer -** given the same inputs. If two or more collating functions are registered -** to the same collation name (using different eTextRep values) then all -** must give an equivalent answer when invoked with equivalent strings. -** The collating function must obey the following properties for all -** strings A, B, and C: -** -**
      -**
    1. If A==B then B==A. -**
    2. If A==B and B==C then A==C. -**
    3. If A<B THEN B>A. -**
    4. If A<B and B<C then A<C. -**
    -** -** If a collating function fails any of the above constraints and that -** collating function is registered and used, then the behavior of SQLite -** is undefined. -** -** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation() -** with the addition that the xDestroy callback is invoked on pArg when -** the collating function is deleted. -** ^Collating functions are deleted when they are overridden by later -** calls to the collation creation functions or when the -** [database connection] is closed using [sqlite3_close()]. -** -** ^The xDestroy callback is not called if the -** sqlite3_create_collation_v2() function fails. Applications that invoke -** sqlite3_create_collation_v2() with a non-NULL xDestroy argument should -** check the return code and dispose of the application data pointer -** themselves rather than expecting SQLite to deal with it for them. -** This is different from every other SQLite interface. The inconsistency -** is unfortunate but cannot be changed without breaking backwards -** compatibility. -** -** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. -*/ -SQLITE_API int sqlite3_create_collation( - sqlite3*, - const char *zName, - int eTextRep, - void *pArg, - int(*xCompare)(void*,int,const void*,int,const void*) -); -SQLITE_API int sqlite3_create_collation_v2( - sqlite3*, - const char *zName, - int eTextRep, - void *pArg, - int(*xCompare)(void*,int,const void*,int,const void*), - void(*xDestroy)(void*) -); -SQLITE_API int sqlite3_create_collation16( - sqlite3*, - const void *zName, - int eTextRep, - void *pArg, - int(*xCompare)(void*,int,const void*,int,const void*) -); - -/* -** CAPI3REF: Collation Needed Callbacks -** METHOD: sqlite3 -** -** ^To avoid having to register all collation sequences before a database -** can be used, a single callback function may be registered with the -** [database connection] to be invoked whenever an undefined collation -** sequence is required. -** -** ^If the function is registered using the sqlite3_collation_needed() API, -** then it is passed the names of undefined collation sequences as strings -** encoded in UTF-8. ^If sqlite3_collation_needed16() is used, -** the names are passed as UTF-16 in machine native byte order. -** ^A call to either function replaces the existing collation-needed callback. -** -** ^(When the callback is invoked, the first argument passed is a copy -** of the second argument to sqlite3_collation_needed() or -** sqlite3_collation_needed16(). The second argument is the database -** connection. The third argument is one of [SQLITE_UTF8], [SQLITE_UTF16BE], -** or [SQLITE_UTF16LE], indicating the most desirable form of the collation -** sequence function required. The fourth parameter is the name of the -** required collation sequence.)^ -** -** The callback function should register the desired collation using -** [sqlite3_create_collation()], [sqlite3_create_collation16()], or -** [sqlite3_create_collation_v2()]. -*/ -SQLITE_API int sqlite3_collation_needed( - sqlite3*, - void*, - void(*)(void*,sqlite3*,int eTextRep,const char*) -); -SQLITE_API int sqlite3_collation_needed16( - sqlite3*, - void*, - void(*)(void*,sqlite3*,int eTextRep,const void*) -); + /* + ** CAPI3REF: Obtaining SQL Values + ** METHOD: sqlite3_value + ** + ** Summary: + **
    + **
    sqlite3_value_blobBLOB value + **
    sqlite3_value_doubleREAL value + **
    sqlite3_value_int32-bit INTEGER value + **
    sqlite3_value_int6464-bit INTEGER value + **
    sqlite3_value_pointerPointer value + **
    sqlite3_value_textUTF-8 TEXT value + **
    sqlite3_value_text16UTF-16 TEXT value in + ** the native byteorder + **
    sqlite3_value_text16beUTF-16be TEXT value + **
    sqlite3_value_text16leUTF-16le TEXT value + **
        + **
    sqlite3_value_bytesSize of a BLOB + ** or a UTF-8 TEXT in bytes + **
    sqlite3_value_bytes16   + ** →  Size of UTF-16 + ** TEXT in bytes + **
    sqlite3_value_typeDefault + ** datatype of the value + **
    sqlite3_value_numeric_type   + ** →  Best numeric datatype of the value + **
    sqlite3_value_nochange   + ** →  True if the column is unchanged in an UPDATE + ** against a virtual table. + **
    sqlite3_value_frombind   + ** →  True if value originated from a [bound parameter] + **
    + ** + ** Details: + ** + ** These routines extract type, size, and content information from + ** [protected sqlite3_value] objects. Protected sqlite3_value objects + ** are used to pass parameter information into the functions that + ** implement [application-defined SQL functions] and [virtual tables]. + ** + ** These routines work only with [protected sqlite3_value] objects. + ** Any attempt to use these routines on an [unprotected sqlite3_value] + ** is not threadsafe. + ** + ** ^These routines work just like the corresponding [column access functions] + ** except that these routines take a single [protected sqlite3_value] object + ** pointer instead of a [sqlite3_stmt*] pointer and an integer column number. + ** + ** ^The sqlite3_value_text16() interface extracts a UTF-16 string + ** in the native byte-order of the host machine. ^The + ** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces + ** extract UTF-16 strings as big-endian and little-endian respectively. + ** + ** ^If [sqlite3_value] object V was initialized + ** using [sqlite3_bind_pointer(S,I,P,X,D)] or [sqlite3_result_pointer(C,P,X,D)] + ** and if X and Y are strings that compare equal according to strcmp(X,Y), + ** then sqlite3_value_pointer(V,Y) will return the pointer P. ^Otherwise, + ** sqlite3_value_pointer(V,Y) returns a NULL. The sqlite3_bind_pointer() + ** routine is part of the [pointer passing interface] added for SQLite 3.20.0. + ** + ** ^(The sqlite3_value_type(V) interface returns the + ** [SQLITE_INTEGER | datatype code] for the initial datatype of the + ** [sqlite3_value] object V. The returned value is one of [SQLITE_INTEGER], + ** [SQLITE_FLOAT], [SQLITE_TEXT], [SQLITE_BLOB], or [SQLITE_NULL].)^ + ** Other interfaces might change the datatype for an sqlite3_value object. + ** For example, if the datatype is initially SQLITE_INTEGER and + ** sqlite3_value_text(V) is called to extract a text value for that + ** integer, then subsequent calls to sqlite3_value_type(V) might return + ** SQLITE_TEXT. Whether or not a persistent internal datatype conversion + ** occurs is undefined and may change from one release of SQLite to the next. + ** + ** ^(The sqlite3_value_numeric_type() interface attempts to apply + ** numeric affinity to the value. This means that an attempt is + ** made to convert the value to an integer or floating point. If + ** such a conversion is possible without loss of information (in other + ** words, if the value is a string that looks like a number) + ** then the conversion is performed. Otherwise no conversion occurs. + ** The [SQLITE_INTEGER | datatype] after conversion is returned.)^ + ** + ** ^Within the [xUpdate] method of a [virtual table], the + ** sqlite3_value_nochange(X) interface returns true if and only if + ** the column corresponding to X is unchanged by the UPDATE operation + ** that the xUpdate method call was invoked to implement and if + ** and the prior [xColumn] method call that was invoked to extracted + ** the value for that column returned without setting a result (probably + ** because it queried [sqlite3_vtab_nochange()] and found that the column + ** was unchanging). ^Within an [xUpdate] method, any value for which + ** sqlite3_value_nochange(X) is true will in all other respects appear + ** to be a NULL value. If sqlite3_value_nochange(X) is invoked anywhere other + ** than within an [xUpdate] method call for an UPDATE statement, then + ** the return value is arbitrary and meaningless. + ** + ** ^The sqlite3_value_frombind(X) interface returns non-zero if the + ** value X originated from one of the [sqlite3_bind_int|sqlite3_bind()] + ** interfaces. ^If X comes from an SQL literal value, or a table column, + ** or an expression, then sqlite3_value_frombind(X) returns zero. + ** + ** Please pay particular attention to the fact that the pointer returned + ** from [sqlite3_value_blob()], [sqlite3_value_text()], or + ** [sqlite3_value_text16()] can be invalidated by a subsequent call to + ** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()], + ** or [sqlite3_value_text16()]. + ** + ** These routines must be called from the same thread as + ** the SQL function that supplied the [sqlite3_value*] parameters. + ** + ** As long as the input parameter is correct, these routines can only + ** fail if an out-of-memory error occurs during a format conversion. + ** Only the following subset of interfaces are subject to out-of-memory + ** errors: + ** + **
      + **
    • sqlite3_value_blob() + **
    • sqlite3_value_text() + **
    • sqlite3_value_text16() + **
    • sqlite3_value_text16le() + **
    • sqlite3_value_text16be() + **
    • sqlite3_value_bytes() + **
    • sqlite3_value_bytes16() + **
    + ** + ** If an out-of-memory error occurs, then the return value from these + ** routines is the same as if the column had contained an SQL NULL value. + ** Valid SQL NULL returns can be distinguished from out-of-memory errors + ** by invoking the [sqlite3_errcode()] immediately after the suspect + ** return value is obtained and before any + ** other SQLite interface is called on the same [database connection]. + */ + SQLITE_API const void* sqlite3_value_blob(sqlite3_value*); + SQLITE_API double sqlite3_value_double(sqlite3_value*); + SQLITE_API int sqlite3_value_int(sqlite3_value*); + SQLITE_API sqlite3_int64 sqlite3_value_int64(sqlite3_value*); + SQLITE_API void* sqlite3_value_pointer(sqlite3_value*, const char*); + SQLITE_API const unsigned char* sqlite3_value_text(sqlite3_value*); + SQLITE_API const void* sqlite3_value_text16(sqlite3_value*); + SQLITE_API const void* sqlite3_value_text16le(sqlite3_value*); + SQLITE_API const void* sqlite3_value_text16be(sqlite3_value*); + SQLITE_API int sqlite3_value_bytes(sqlite3_value*); + SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); + SQLITE_API int sqlite3_value_type(sqlite3_value*); + 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: Finding The Subtype Of SQL Values + ** METHOD: sqlite3_value + ** + ** The sqlite3_value_subtype(V) function returns the subtype for + ** an [application-defined SQL function] argument V. The subtype + ** 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. + */ + SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*); + + /* + ** CAPI3REF: Copy And Free SQL Values + ** 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 + ** 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. + ** + ** ^The sqlite3_value_free(V) interface frees an [sqlite3_value] object + ** previously obtained from [sqlite3_value_dup()]. ^If V is a NULL pointer + ** then sqlite3_value_free(V) is a harmless no-op. + */ + SQLITE_API sqlite3_value* sqlite3_value_dup(const sqlite3_value*); + SQLITE_API void sqlite3_value_free(sqlite3_value*); + + /* + ** CAPI3REF: Obtain Aggregate Function Context + ** METHOD: sqlite3_context + ** + ** Implementations of aggregate SQL functions use this + ** routine to allocate memory for storing their state. + ** + ** ^The first time the sqlite3_aggregate_context(C,N) routine is called + ** for a particular aggregate function, SQLite allocates + ** N bytes of memory, zeroes out that memory, and returns a pointer + ** to the new memory. ^On second and subsequent calls to + ** sqlite3_aggregate_context() for the same aggregate function instance, + ** the same buffer is returned. Sqlite3_aggregate_context() is normally + ** called once for each invocation of the xStep callback and then one + ** last time when the xFinal callback is invoked. ^(When no rows match + ** an aggregate query, the xStep() callback of the aggregate function + ** implementation is never called and xFinal() is called exactly once. + ** In those cases, sqlite3_aggregate_context() might be called for the + ** first time from within xFinal().)^ + ** + ** ^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. + ** + ** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is + ** determined by the N parameter on 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 + ** N=0 in calls to sqlite3_aggregate_context(C,N) so that no + ** pointless memory allocations occur. + ** + ** ^SQLite automatically frees the memory allocated by + ** sqlite3_aggregate_context() when the aggregate query concludes. + ** + ** The first parameter must be a copy of the + ** [sqlite3_context | SQL function context] that is the first parameter + ** to the xStep or xFinal callback routine that implements the aggregate + ** function. + ** + ** This routine must be called from the same thread in which + ** the aggregate SQL function is running. + */ + SQLITE_API void* sqlite3_aggregate_context(sqlite3_context*, int nBytes); + + /* + ** CAPI3REF: User Data For Functions + ** METHOD: sqlite3_context + ** + ** ^The sqlite3_user_data() interface returns a copy of + ** the pointer that was the pUserData parameter (the 5th parameter) + ** of the [sqlite3_create_function()] + ** and [sqlite3_create_function16()] routines that originally + ** registered the application defined function. + ** + ** This routine must be called from the same thread in which + ** the application-defined function is running. + */ + SQLITE_API void* sqlite3_user_data(sqlite3_context*); + + /* + ** CAPI3REF: Database Connection For Functions + ** METHOD: sqlite3_context + ** + ** ^The sqlite3_context_db_handle() interface returns a copy of + ** the pointer to the [database connection] (the 1st parameter) + ** of the [sqlite3_create_function()] + ** and [sqlite3_create_function16()] routines that originally + ** registered the application defined function. + */ + SQLITE_API sqlite3* sqlite3_context_db_handle(sqlite3_context*); + + /* + ** CAPI3REF: Function Auxiliary Data + ** 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. + ** 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 + ** 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 + ** 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 + ** 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. + ** ^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:
      + **
    • ^(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.)^
    + ** + ** Note the last bullet 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 + ** function parameters that are compile-time constants, including literal + ** values and [parameters] and expressions composed from the same.)^ + ** + ** The value of the N parameter to these interfaces should be non-negative. + ** Future enhancements may make use of negative N values to define new + ** kinds of function caching behavior. + ** + ** These routines must be called from the same thread in which + ** the SQL function is running. + */ + SQLITE_API void* sqlite3_get_auxdata(sqlite3_context*, int N); + SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); + + /* + ** CAPI3REF: Constants Defining Special Destructor Behavior + ** + ** These are special values for the destructor that is passed in as the + ** final argument to routines like [sqlite3_result_blob()]. ^If the destructor + ** argument is SQLITE_STATIC, it means that the content pointer is constant + ** and will never change. It does not need to be destroyed. ^The + ** SQLITE_TRANSIENT value means that the content will likely change in + ** the near future and that SQLite should make its own private copy of + ** the content before returning. + ** + ** The typedef is necessary to work around problems in certain + ** C++ compilers. + */ + typedef void (*sqlite3_destructor_type)(void*); +#define SQLITE_STATIC ((sqlite3_destructor_type)0) +#define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1) + + /* + ** CAPI3REF: Setting The Result Of An SQL Function + ** METHOD: sqlite3_context + ** + ** These routines are used by the xFunc or xFinal callbacks that + ** implement SQL functions and aggregates. See + ** [sqlite3_create_function()] and [sqlite3_create_function16()] + ** for additional information. + ** + ** These functions work very much like the [parameter binding] family of + ** functions used to bind values to host parameters in prepared statements. + ** Refer to the [SQL parameter] documentation for additional information. + ** + ** ^The sqlite3_result_blob() interface sets the result from + ** an application-defined function to be the BLOB whose content is pointed + ** to by the second parameter and which is N bytes long where N is the + ** third parameter. + ** + ** ^The sqlite3_result_zeroblob(C,N) and sqlite3_result_zeroblob64(C,N) + ** interfaces set the result of the application-defined function to be + ** a BLOB containing all zero bytes and N bytes in size. + ** + ** ^The sqlite3_result_double() interface sets the result from + ** an application-defined function to be a floating point value specified + ** by its 2nd argument. + ** + ** ^The sqlite3_result_error() and sqlite3_result_error16() functions + ** cause the implemented SQL function to throw an exception. + ** ^SQLite uses the string pointed to by the + ** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16() + ** as the text of an error message. ^SQLite interprets the error + ** message string from sqlite3_result_error() as UTF-8. ^SQLite + ** interprets the string from sqlite3_result_error16() as UTF-16 using + ** the same [byte-order determination rules] as [sqlite3_bind_text16()]. + ** ^If the third parameter to sqlite3_result_error() + ** or sqlite3_result_error16() is negative then SQLite takes as the error + ** message all text up through the first zero character. + ** ^If the third parameter to sqlite3_result_error() or + ** sqlite3_result_error16() is non-negative then SQLite takes that many + ** bytes (not characters) from the 2nd parameter as the error message. + ** ^The sqlite3_result_error() and sqlite3_result_error16() + ** routines make a private copy of the error message text before + ** they return. Hence, the calling function can deallocate or + ** modify the text after they return without harm. + ** ^The sqlite3_result_error_code() function changes the error code + ** returned by SQLite as a result of an error in a function. ^By default, + ** the error code is SQLITE_ERROR. ^A subsequent call to sqlite3_result_error() + ** or sqlite3_result_error16() resets the error code to SQLITE_ERROR. + ** + ** ^The sqlite3_result_error_toobig() interface causes SQLite to throw an + ** error indicating that a string or BLOB is too long to represent. + ** + ** ^The sqlite3_result_error_nomem() interface causes SQLite to throw an + ** error indicating that a memory allocation failed. + ** + ** ^The sqlite3_result_int() interface sets the return value + ** of the application-defined function to be the 32-bit signed integer + ** value given in the 2nd argument. + ** ^The sqlite3_result_int64() interface sets the return value + ** of the application-defined function to be the 64-bit signed integer + ** value given in the 2nd argument. + ** + ** ^The sqlite3_result_null() interface sets the return value + ** of the application-defined function to be NULL. + ** + ** ^The sqlite3_result_text(), sqlite3_result_text16(), + ** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces + ** set the return value of the application-defined function to be + ** a text string which is represented as UTF-8, UTF-16 native byte order, + ** UTF-16 little endian, or UTF-16 big endian, respectively. + ** ^The sqlite3_result_text64() interface sets the return value of an + ** application-defined function to be a text string in an encoding + ** specified by the fifth (and last) parameter, which must be one + ** 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 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 + ** 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. + ** ^If the 4th parameter to the sqlite3_result_text* interfaces + ** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that + ** function as the destructor on the text or BLOB result when it has + ** finished using that result. + ** ^If the 4th parameter to the sqlite3_result_text* interfaces or to + ** sqlite3_result_blob is the special constant SQLITE_STATIC, then SQLite + ** assumes that the text or BLOB result is in constant space and does not + ** copy the content of the parameter nor call a destructor on the content + ** when it has finished using that result. + ** ^If the 4th parameter to the sqlite3_result_text* interfaces + ** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT + ** then SQLite makes a copy of the result into space obtained + ** from [sqlite3_malloc()] before it returns. + ** + ** ^For the sqlite3_result_text16(), sqlite3_result_text16le(), and + ** sqlite3_result_text16be() routines, and for sqlite3_result_text64() + ** when the encoding is not UTF8, if the input UTF16 begins with a + ** byte-order mark (BOM, U+FEFF) then the BOM is removed from the + ** string and the rest of the string is interpreted according to the + ** byte-order specified by the BOM. ^The byte-order specified by + ** the BOM at the beginning of the text overrides the byte-order + ** specified by the interface procedure. ^So, for example, if + ** sqlite3_result_text16le() is invoked with text that begins + ** with bytes 0xfe, 0xff (a big-endian byte-order mark) then the + ** first two bytes of input are skipped and the remaining input + ** is interpreted as UTF16BE text. + ** + ** ^For UTF16 input text to the sqlite3_result_text16(), + ** sqlite3_result_text16be(), sqlite3_result_text16le(), and + ** sqlite3_result_text64() routines, if the text contains invalid + ** UTF16 characters, the invalid characters might be converted + ** into the unicode replacement character, U+FFFD. + ** + ** ^The sqlite3_result_value() interface sets the result of + ** the application-defined function to be a copy of the + ** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The + ** sqlite3_result_value() interface makes a copy of the [sqlite3_value] + ** so that the [sqlite3_value] specified in the parameter may change or + ** be deallocated after sqlite3_result_value() returns without harm. + ** ^A [protected sqlite3_value] object may always be used where an + ** [unprotected sqlite3_value] object is required, so either + ** kind of [sqlite3_value] object can be used with this interface. + ** + ** ^The sqlite3_result_pointer(C,P,T,D) interface sets the result to an + ** SQL NULL value, just like [sqlite3_result_null(C)], except that it + ** also associates the host-language pointer P or type T with that + ** NULL value such that the pointer can be retrieved within an + ** [application-defined SQL function] using [sqlite3_value_pointer()]. + ** ^If the D parameter is not NULL, then it is a pointer to a destructor + ** for the P parameter. ^SQLite invokes D with P as its only argument + ** when SQLite is finished with P. The T parameter should be a static + ** 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 + ** than the one containing the application-defined function that received + ** the [sqlite3_context] pointer, the results are undefined. + */ + SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void (*)(void*)); + SQLITE_API void sqlite3_result_blob64(sqlite3_context*, const void*, sqlite3_uint64, void (*)(void*)); + SQLITE_API void sqlite3_result_double(sqlite3_context*, double); + SQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int); + SQLITE_API void sqlite3_result_error16(sqlite3_context*, const void*, int); + SQLITE_API void sqlite3_result_error_toobig(sqlite3_context*); + SQLITE_API void sqlite3_result_error_nomem(sqlite3_context*); + SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); + SQLITE_API void sqlite3_result_int(sqlite3_context*, int); + SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); + SQLITE_API void sqlite3_result_null(sqlite3_context*); + SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void (*)(void*)); + SQLITE_API void + sqlite3_result_text64(sqlite3_context*, const char*, sqlite3_uint64, void (*)(void*), unsigned char encoding); + SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void (*)(void*)); + SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int, void (*)(void*)); + SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int, void (*)(void*)); + SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); + SQLITE_API void sqlite3_result_pointer(sqlite3_context*, void*, const char*, void (*)(void*)); + SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); + SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n); + + /* + ** CAPI3REF: Setting The Subtype Of An SQL Function + ** METHOD: sqlite3_context + ** + ** The sqlite3_result_subtype(C,T) function causes the subtype of + ** the result from the [application-defined SQL function] with + ** [sqlite3_context] C to be the value T. Only the lower 8 bits + ** of the subtype T are preserved in current versions of SQLite; + ** higher order bits are discarded. + ** The number of subtype bytes preserved by SQLite might increase + ** in future releases of SQLite. + */ + SQLITE_API void sqlite3_result_subtype(sqlite3_context*, unsigned int); + + /* + ** CAPI3REF: Define New Collating Sequences + ** METHOD: sqlite3 + ** + ** ^These functions add, remove, or modify a [collation] associated + ** with the [database connection] specified as the first argument. + ** + ** ^The name of the collation is a UTF-8 string + ** for sqlite3_create_collation() and sqlite3_create_collation_v2() + ** and a UTF-16 string in native byte order for sqlite3_create_collation16(). + ** ^Collation names that compare equal according to [sqlite3_strnicmp()] are + ** considered to be the same name. + ** + ** ^(The third argument (eTextRep) must be one of the constants: + **
      + **
    • [SQLITE_UTF8], + **
    • [SQLITE_UTF16LE], + **
    • [SQLITE_UTF16BE], + **
    • [SQLITE_UTF16], or + **
    • [SQLITE_UTF16_ALIGNED]. + **
    )^ + ** ^The eTextRep argument determines the encoding of strings passed + ** to the collating function callback, xCompare. + ** ^The [SQLITE_UTF16] and [SQLITE_UTF16_ALIGNED] values for eTextRep + ** force strings to be UTF16 with native byte order. + ** ^The [SQLITE_UTF16_ALIGNED] value for eTextRep forces strings to begin + ** on an even byte address. + ** + ** ^The fourth argument, pArg, is an application data pointer that is passed + ** through as the first argument to the collating function callback. + ** + ** ^The fifth argument, xCompare, is a pointer to the collating function. + ** ^Multiple collating functions can be registered using the same name but + ** with different eTextRep parameters and SQLite will use whichever + ** function requires the least amount of data transformation. + ** ^If the xCompare argument is NULL then the collating function is + ** deleted. ^When all collating functions having the same name are deleted, + ** that collation is no longer usable. + ** + ** ^The collating function callback is invoked with a copy of the pArg + ** application data pointer and with two strings in the encoding specified + ** by the eTextRep argument. The two integer parameters to the collating + ** function callback are the length of the two strings, in bytes. The collating + ** function must return an integer that is negative, zero, or positive + ** if the first string is less than, equal to, or greater than the second, + ** respectively. A collating function must always return the same answer + ** given the same inputs. If two or more collating functions are registered + ** to the same collation name (using different eTextRep values) then all + ** must give an equivalent answer when invoked with equivalent strings. + ** The collating function must obey the following properties for all + ** strings A, B, and C: + ** + **
      + **
    1. If A==B then B==A. + **
    2. If A==B and B==C then A==C. + **
    3. If A<B THEN B>A. + **
    4. If A<B and B<C then A<C. + **
    + ** + ** If a collating function fails any of the above constraints and that + ** collating function is registered and used, then the behavior of SQLite + ** is undefined. + ** + ** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation() + ** with the addition that the xDestroy callback is invoked on pArg when + ** the collating function is deleted. + ** ^Collating functions are deleted when they are overridden by later + ** calls to the collation creation functions or when the + ** [database connection] is closed using [sqlite3_close()]. + ** + ** ^The xDestroy callback is not called if the + ** sqlite3_create_collation_v2() function fails. Applications that invoke + ** sqlite3_create_collation_v2() with a non-NULL xDestroy argument should + ** check the return code and dispose of the application data pointer + ** themselves rather than expecting SQLite to deal with it for them. + ** This is different from every other SQLite interface. The inconsistency + ** is unfortunate but cannot be changed without breaking backwards + ** compatibility. + ** + ** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. + */ + SQLITE_API int sqlite3_create_collation(sqlite3*, + const char* zName, + int eTextRep, + void* pArg, + int (*xCompare)(void*, int, const void*, int, const void*)); + SQLITE_API int sqlite3_create_collation_v2(sqlite3*, + const char* zName, + int eTextRep, + void* pArg, + int (*xCompare)(void*, int, const void*, int, const void*), + void (*xDestroy)(void*)); + SQLITE_API int sqlite3_create_collation16(sqlite3*, + const void* zName, + int eTextRep, + void* pArg, + int (*xCompare)(void*, int, const void*, int, const void*)); + + /* + ** CAPI3REF: Collation Needed Callbacks + ** METHOD: sqlite3 + ** + ** ^To avoid having to register all collation sequences before a database + ** can be used, a single callback function may be registered with the + ** [database connection] to be invoked whenever an undefined collation + ** sequence is required. + ** + ** ^If the function is registered using the sqlite3_collation_needed() API, + ** then it is passed the names of undefined collation sequences as strings + ** encoded in UTF-8. ^If sqlite3_collation_needed16() is used, + ** the names are passed as UTF-16 in machine native byte order. + ** ^A call to either function replaces the existing collation-needed callback. + ** + ** ^(When the callback is invoked, the first argument passed is a copy + ** of the second argument to sqlite3_collation_needed() or + ** sqlite3_collation_needed16(). The second argument is the database + ** connection. The third argument is one of [SQLITE_UTF8], [SQLITE_UTF16BE], + ** or [SQLITE_UTF16LE], indicating the most desirable form of the collation + ** sequence function required. The fourth parameter is the name of the + ** required collation sequence.)^ + ** + ** The callback function should register the desired collation using + ** [sqlite3_create_collation()], [sqlite3_create_collation16()], or + ** [sqlite3_create_collation_v2()]. + */ + SQLITE_API int sqlite3_collation_needed(sqlite3*, void*, void (*)(void*, sqlite3*, int eTextRep, const char*)); + SQLITE_API int sqlite3_collation_needed16(sqlite3*, void*, void (*)(void*, sqlite3*, int eTextRep, const void*)); #ifdef SQLITE_ENABLE_CEROD -/* -** Specify the activation key for a CEROD database. Unless -** activated, none of the CEROD routines will work. -*/ -SQLITE_API void sqlite3_activate_cerod( - const char *zPassPhrase /* Activation phrase */ -); + /* + ** Specify the activation key for a CEROD database. Unless + ** activated, none of the CEROD routines will work. + */ + SQLITE_API void sqlite3_activate_cerod(const char* zPassPhrase /* Activation phrase */ + ); #endif -/* -** CAPI3REF: Suspend Execution For A Short Time -** -** The sqlite3_sleep() function causes the current thread to suspend execution -** for at least a number of milliseconds specified in its parameter. -** -** If the operating system does not support sleep requests with -** millisecond time resolution, then the time will be rounded up to -** the nearest second. The number of milliseconds of sleep actually -** requested from the operating system is returned. -** -** ^SQLite implements this interface by calling the xSleep() -** method of the default [sqlite3_vfs] object. If the xSleep() method -** 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. -*/ -SQLITE_API int sqlite3_sleep(int); - -/* -** CAPI3REF: Name Of The Folder Holding Temporary Files -** -** ^(If this global variable is made to point to a string which is -** the name of a folder (a.k.a. directory), then all temporary files -** created by SQLite when using a built-in [sqlite3_vfs | VFS] -** will be placed in that directory.)^ ^If this variable -** is a NULL pointer, then SQLite performs a search for an appropriate -** temporary file directory. -** -** Applications are strongly discouraged from using this global variable. -** It is required to set a temporary folder on Windows Runtime (WinRT). -** But for all other platforms, it is highly recommended that applications -** neither read nor write this variable. This global variable is a relic -** that exists for backwards compatibility of legacy applications and should -** be avoided in new projects. -** -** It is not safe to read or modify this variable in more than one -** thread at a time. It is not safe to read or modify this variable -** if a [database connection] is being used at the same time in a separate -** thread. -** It is intended that this variable be set once -** as part of process initialization and before any SQLite interface -** routines have been called and that this variable remain unchanged -** thereafter. -** -** ^The [temp_store_directory pragma] may modify this variable and cause -** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore, -** the [temp_store_directory pragma] always assumes that any string -** that this variable points to is held in memory obtained from -** [sqlite3_malloc] and the pragma may attempt to free that memory -** using [sqlite3_free]. -** Hence, if this variable is modified directly, either it should be -** made NULL or made to point to memory obtained from [sqlite3_malloc] -** or else the use of the [temp_store_directory pragma] should be avoided. -** Except when requested by the [temp_store_directory pragma], SQLite -** does not free the memory that sqlite3_temp_directory points to. If -** the application wants that memory to be freed, it must do -** so itself, taking care to only do so after all [database connection] -** objects have been destroyed. -** -** Note to Windows Runtime users: The temporary directory must be set -** prior to calling [sqlite3_open] or [sqlite3_open_v2]. Otherwise, various -** features that require the use of temporary files may fail. Here is an -** example of how to do this using C++ with the Windows Runtime: -** -**
    -** LPCWSTR zPath = Windows::Storage::ApplicationData::Current->
    -**       TemporaryFolder->Path->Data();
    -** char zPathBuf[MAX_PATH + 1];
    -** memset(zPathBuf, 0, sizeof(zPathBuf));
    -** WideCharToMultiByte(CP_UTF8, 0, zPath, -1, zPathBuf, sizeof(zPathBuf),
    -**       NULL, NULL);
    -** sqlite3_temp_directory = sqlite3_mprintf("%s", zPathBuf);
    -** 
    -*/ -SQLITE_API SQLITE_EXTERN char *sqlite3_temp_directory; - -/* -** CAPI3REF: Name Of The Folder Holding Database Files -** -** ^(If this global variable is made to point to a string which is -** the name of a folder (a.k.a. directory), then all database files -** specified with a relative pathname and created or accessed by -** SQLite when using a built-in windows [sqlite3_vfs | VFS] will be assumed -** to be relative to that directory.)^ ^If this variable is a NULL -** pointer, then SQLite assumes that all database files specified -** with a relative pathname are relative to the current directory -** for the process. Only the windows VFS makes use of this global -** variable; it is ignored by the unix VFS. -** -** Changing the value of this variable while a database connection is -** open can result in a corrupt database. -** -** It is not safe to read or modify this variable in more than one -** thread at a time. It is not safe to read or modify this variable -** if a [database connection] is being used at the same time in a separate -** thread. -** It is intended that this variable be set once -** as part of process initialization and before any SQLite interface -** routines have been called and that this variable remain unchanged -** thereafter. -** -** ^The [data_store_directory pragma] may modify this variable and cause -** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore, -** the [data_store_directory pragma] always assumes that any string -** that this variable points to is held in memory obtained from -** [sqlite3_malloc] and the pragma may attempt to free that memory -** using [sqlite3_free]. -** Hence, if this variable is modified directly, either it should be -** made NULL or made to point to memory obtained from [sqlite3_malloc] -** or else the use of the [data_store_directory pragma] should be avoided. -*/ -SQLITE_API SQLITE_EXTERN char *sqlite3_data_directory; - -/* -** CAPI3REF: Win32 Specific Interface -** -** These interfaces are available only on Windows. The -** [sqlite3_win32_set_directory] interface is used to set the value associated -** with the [sqlite3_temp_directory] or [sqlite3_data_directory] variable, to -** zValue, depending on the value of the type parameter. The zValue parameter -** should be NULL to cause the previous value to be freed via [sqlite3_free]; -** a non-NULL value will be copied into memory obtained from [sqlite3_malloc] -** prior to being used. The [sqlite3_win32_set_directory] interface returns -** [SQLITE_OK] to indicate success, [SQLITE_ERROR] if the type is unsupported, -** or [SQLITE_NOMEM] if memory could not be allocated. The value of the -** [sqlite3_data_directory] variable is intended to act as a replacement for -** the current directory on the sub-platforms of Win32 where that concept is -** not present, e.g. WinRT and UWP. The [sqlite3_win32_set_directory8] and -** [sqlite3_win32_set_directory16] interfaces behave exactly the same as the -** sqlite3_win32_set_directory interface except the string parameter must be -** UTF-8 or UTF-16, respectively. -*/ -SQLITE_API int sqlite3_win32_set_directory( - unsigned long type, /* Identifier for directory being set or reset */ - void *zValue /* New value for directory being set or reset */ -); -SQLITE_API int sqlite3_win32_set_directory8(unsigned long type, const char *zValue); -SQLITE_API int sqlite3_win32_set_directory16(unsigned long type, const void *zValue); + /* + ** CAPI3REF: Suspend Execution For A Short Time + ** + ** The sqlite3_sleep() function causes the current thread to suspend execution + ** for at least a number of milliseconds specified in its parameter. + ** + ** If the operating system does not support sleep requests with + ** millisecond time resolution, then the time will be rounded up to + ** the nearest second. The number of milliseconds of sleep actually + ** requested from the operating system is returned. + ** + ** ^SQLite implements this interface by calling the xSleep() + ** method of the default [sqlite3_vfs] object. If the xSleep() method + ** 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. + */ + SQLITE_API int sqlite3_sleep(int); + + /* + ** CAPI3REF: Name Of The Folder Holding Temporary Files + ** + ** ^(If this global variable is made to point to a string which is + ** the name of a folder (a.k.a. directory), then all temporary files + ** created by SQLite when using a built-in [sqlite3_vfs | VFS] + ** will be placed in that directory.)^ ^If this variable + ** is a NULL pointer, then SQLite performs a search for an appropriate + ** temporary file directory. + ** + ** Applications are strongly discouraged from using this global variable. + ** It is required to set a temporary folder on Windows Runtime (WinRT). + ** But for all other platforms, it is highly recommended that applications + ** neither read nor write this variable. This global variable is a relic + ** that exists for backwards compatibility of legacy applications and should + ** be avoided in new projects. + ** + ** It is not safe to read or modify this variable in more than one + ** thread at a time. It is not safe to read or modify this variable + ** if a [database connection] is being used at the same time in a separate + ** thread. + ** It is intended that this variable be set once + ** as part of process initialization and before any SQLite interface + ** routines have been called and that this variable remain unchanged + ** thereafter. + ** + ** ^The [temp_store_directory pragma] may modify this variable and cause + ** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore, + ** the [temp_store_directory pragma] always assumes that any string + ** that this variable points to is held in memory obtained from + ** [sqlite3_malloc] and the pragma may attempt to free that memory + ** using [sqlite3_free]. + ** Hence, if this variable is modified directly, either it should be + ** made NULL or made to point to memory obtained from [sqlite3_malloc] + ** or else the use of the [temp_store_directory pragma] should be avoided. + ** Except when requested by the [temp_store_directory pragma], SQLite + ** does not free the memory that sqlite3_temp_directory points to. If + ** the application wants that memory to be freed, it must do + ** so itself, taking care to only do so after all [database connection] + ** objects have been destroyed. + ** + ** Note to Windows Runtime users: The temporary directory must be set + ** prior to calling [sqlite3_open] or [sqlite3_open_v2]. Otherwise, various + ** features that require the use of temporary files may fail. Here is an + ** example of how to do this using C++ with the Windows Runtime: + ** + **
    +    ** LPCWSTR zPath = Windows::Storage::ApplicationData::Current->
    +    **       TemporaryFolder->Path->Data();
    +    ** char zPathBuf[MAX_PATH + 1];
    +    ** memset(zPathBuf, 0, sizeof(zPathBuf));
    +    ** WideCharToMultiByte(CP_UTF8, 0, zPath, -1, zPathBuf, sizeof(zPathBuf),
    +    **       NULL, NULL);
    +    ** sqlite3_temp_directory = sqlite3_mprintf("%s", zPathBuf);
    +    ** 
    + */ + SQLITE_API SQLITE_EXTERN char* sqlite3_temp_directory; + + /* + ** CAPI3REF: Name Of The Folder Holding Database Files + ** + ** ^(If this global variable is made to point to a string which is + ** the name of a folder (a.k.a. directory), then all database files + ** specified with a relative pathname and created or accessed by + ** SQLite when using a built-in windows [sqlite3_vfs | VFS] will be assumed + ** to be relative to that directory.)^ ^If this variable is a NULL + ** pointer, then SQLite assumes that all database files specified + ** with a relative pathname are relative to the current directory + ** for the process. Only the windows VFS makes use of this global + ** variable; it is ignored by the unix VFS. + ** + ** Changing the value of this variable while a database connection is + ** open can result in a corrupt database. + ** + ** It is not safe to read or modify this variable in more than one + ** thread at a time. It is not safe to read or modify this variable + ** if a [database connection] is being used at the same time in a separate + ** thread. + ** It is intended that this variable be set once + ** as part of process initialization and before any SQLite interface + ** routines have been called and that this variable remain unchanged + ** thereafter. + ** + ** ^The [data_store_directory pragma] may modify this variable and cause + ** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore, + ** the [data_store_directory pragma] always assumes that any string + ** that this variable points to is held in memory obtained from + ** [sqlite3_malloc] and the pragma may attempt to free that memory + ** using [sqlite3_free]. + ** Hence, if this variable is modified directly, either it should be + ** made NULL or made to point to memory obtained from [sqlite3_malloc] + ** or else the use of the [data_store_directory pragma] should be avoided. + */ + SQLITE_API SQLITE_EXTERN char* sqlite3_data_directory; + + /* + ** CAPI3REF: Win32 Specific Interface + ** + ** These interfaces are available only on Windows. The + ** [sqlite3_win32_set_directory] interface is used to set the value associated + ** with the [sqlite3_temp_directory] or [sqlite3_data_directory] variable, to + ** zValue, depending on the value of the type parameter. The zValue parameter + ** should be NULL to cause the previous value to be freed via [sqlite3_free]; + ** a non-NULL value will be copied into memory obtained from [sqlite3_malloc] + ** prior to being used. The [sqlite3_win32_set_directory] interface returns + ** [SQLITE_OK] to indicate success, [SQLITE_ERROR] if the type is unsupported, + ** or [SQLITE_NOMEM] if memory could not be allocated. The value of the + ** [sqlite3_data_directory] variable is intended to act as a replacement for + ** the current directory on the sub-platforms of Win32 where that concept is + ** not present, e.g. WinRT and UWP. The [sqlite3_win32_set_directory8] and + ** [sqlite3_win32_set_directory16] interfaces behave exactly the same as the + ** sqlite3_win32_set_directory interface except the string parameter must be + ** UTF-8 or UTF-16, respectively. + */ + SQLITE_API int sqlite3_win32_set_directory(unsigned long type, /* Identifier for directory being set or reset */ + void* zValue /* New value for directory being set or reset */ + ); + SQLITE_API int sqlite3_win32_set_directory8(unsigned long type, const char* zValue); + SQLITE_API int sqlite3_win32_set_directory16(unsigned long type, const void* zValue); /* ** CAPI3REF: Win32 Directory Types @@ -6104,780 +6182,890 @@ ** These macros are only available on Windows. They define the allowed values ** for the type argument to the [sqlite3_win32_set_directory] interface. */ -#define SQLITE_WIN32_DATA_DIRECTORY_TYPE 1 -#define SQLITE_WIN32_TEMP_DIRECTORY_TYPE 2 +#define SQLITE_WIN32_DATA_DIRECTORY_TYPE 1 +#define SQLITE_WIN32_TEMP_DIRECTORY_TYPE 2 -/* -** CAPI3REF: Test For Auto-Commit Mode -** KEYWORDS: {autocommit mode} -** METHOD: sqlite3 -** -** ^The sqlite3_get_autocommit() interface returns non-zero or -** zero if the given database connection is or is not in autocommit mode, -** respectively. ^Autocommit mode is on by default. -** ^Autocommit mode is disabled by a [BEGIN] statement. -** ^Autocommit mode is re-enabled by a [COMMIT] or [ROLLBACK]. -** -** If certain kinds of errors occur on a statement within a multi-statement -** transaction (errors including [SQLITE_FULL], [SQLITE_IOERR], -** [SQLITE_NOMEM], [SQLITE_BUSY], and [SQLITE_INTERRUPT]) then the -** transaction might be rolled back automatically. The only way to -** find out whether SQLite automatically rolled back the transaction after -** an error is to use this function. -** -** If another thread changes the autocommit status of the database -** connection while this routine is running, then the return value -** is undefined. -*/ -SQLITE_API int sqlite3_get_autocommit(sqlite3*); - -/* -** CAPI3REF: Find The Database Handle Of A Prepared Statement -** METHOD: sqlite3_stmt -** -** ^The sqlite3_db_handle interface returns the [database connection] handle -** to which a [prepared statement] belongs. ^The [database connection] -** returned by sqlite3_db_handle is the same [database connection] -** that was the first argument -** to the [sqlite3_prepare_v2()] call (or its variants) that was used to -** create the statement in the first place. -*/ -SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); - -/* -** CAPI3REF: Return The Filename For A Database Connection -** METHOD: sqlite3 -** -** ^The sqlite3_db_filename(D,N) interface returns a pointer to the filename -** associated with database N of connection D. -** ^If there is no attached database N on the database -** connection D, or if database N is a temporary or in-memory database, then -** this function will return either a NULL pointer or an empty string. -** -** ^The string value returned by this routine is owned and managed by -** the database connection. ^The value will be valid until the database N -** is [DETACH]-ed or until the database connection closes. -** -** ^The filename returned by this function is the output of the -** xFullPathname method of the [VFS]. ^In other words, the filename -** will be an absolute pathname, even if the filename used -** to open the database originally was a URI or relative pathname. -** -** If the filename pointer returned by this routine is not NULL, then it -** can be used as the filename input parameter to these routines: -**
      -**
    • [sqlite3_uri_parameter()] -**
    • [sqlite3_uri_boolean()] -**
    • [sqlite3_uri_int64()] -**
    • [sqlite3_filename_database()] -**
    • [sqlite3_filename_journal()] -**
    • [sqlite3_filename_wal()] -**
    -*/ -SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); - -/* -** CAPI3REF: Determine if a database is read-only -** METHOD: sqlite3 -** -** ^The sqlite3_db_readonly(D,N) interface returns 1 if the database N -** of connection D is read-only, 0 if it is read/write, or -1 if N is not -** the name of a database on connection D. -*/ -SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); - -/* -** CAPI3REF: Find the next prepared statement -** METHOD: sqlite3 -** -** ^This interface returns a pointer to the next [prepared statement] after -** pStmt associated with the [database connection] pDb. ^If pStmt is NULL -** then this interface returns a pointer to the first prepared statement -** associated with the database connection pDb. ^If no prepared statement -** satisfies the conditions of this routine, it returns NULL. -** -** The [database connection] pointer D in a call to -** [sqlite3_next_stmt(D,S)] must refer to an open database -** connection and in particular must not be a NULL pointer. -*/ -SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); - -/* -** CAPI3REF: Commit And Rollback Notification Callbacks -** METHOD: sqlite3 -** -** ^The sqlite3_commit_hook() interface registers a callback -** function to be invoked whenever a transaction is [COMMIT | committed]. -** ^Any callback set by a previous call to sqlite3_commit_hook() -** for the same database connection is overridden. -** ^The sqlite3_rollback_hook() interface registers a callback -** function to be invoked whenever a transaction is [ROLLBACK | rolled back]. -** ^Any callback set by a previous call to sqlite3_rollback_hook() -** for the same database connection is overridden. -** ^The pArg argument is passed through to the callback. -** ^If the callback on a commit hook function returns non-zero, -** then the commit is converted into a rollback. -** -** ^The sqlite3_commit_hook(D,C,P) and sqlite3_rollback_hook(D,C,P) functions -** return the P argument from the previous call of the same function -** on the same [database connection] D, or NULL for -** the first call for each function on D. -** -** The commit and rollback hook callbacks are not reentrant. -** The callback implementation must not do anything that will modify -** the database connection that invoked the callback. Any actions -** to modify the database connection must be deferred until after the -** completion of the [sqlite3_step()] call that triggered the commit -** or rollback hook in the first place. -** Note that running any other SQL statements, including SELECT statements, -** or merely calling [sqlite3_prepare_v2()] and [sqlite3_step()] will modify -** the database connections for the meaning of "modify" in this paragraph. -** -** ^Registering a NULL function disables the callback. -** -** ^When the commit hook callback routine returns zero, the [COMMIT] -** operation is allowed to continue normally. ^If the commit hook -** returns non-zero, then the [COMMIT] is converted into a [ROLLBACK]. -** ^The rollback hook is invoked on a rollback that results from a commit -** hook returning non-zero, just as it would be with any other rollback. -** -** ^For the purposes of this API, a transaction is said to have been -** rolled back if an explicit "ROLLBACK" statement is executed, or -** an error or constraint causes an implicit rollback to occur. -** ^The rollback callback is not invoked if a transaction is -** automatically rolled back because the database connection is closed. -** -** See also the [sqlite3_update_hook()] interface. -*/ -SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); -SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); - -/* -** CAPI3REF: Data Change Notification Callbacks -** METHOD: sqlite3 -** -** ^The sqlite3_update_hook() interface registers a callback function -** with the [database connection] identified by the first argument -** to be invoked whenever a row is updated, inserted or deleted in -** a [rowid table]. -** ^Any callback set by a previous call to this function -** for the same database connection is overridden. -** -** ^The second argument is a pointer to the function to invoke when a -** row is updated, inserted or deleted in a rowid table. -** ^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], -** or [SQLITE_UPDATE], depending on the operation that caused the callback -** to be invoked. -** ^The third and fourth arguments to the callback contain pointers to the -** database and table name containing the affected row. -** ^The final callback parameter is the [rowid] of the row. -** ^In the case of an update, this is the [rowid] after the update takes place. -** -** ^(The update hook is not invoked when internal system tables are -** modified (i.e. sqlite_sequence).)^ -** ^The update hook is not invoked when [WITHOUT ROWID] tables are modified. -** -** ^In the current implementation, the update hook -** is not invoked when conflicting rows are deleted because of an -** [ON CONFLICT | ON CONFLICT REPLACE] clause. ^Nor is the update hook -** invoked when rows are deleted using the [truncate optimization]. -** The exceptions defined in this paragraph might change in a future -** release of SQLite. -** -** 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 -** completion of the [sqlite3_step()] call that triggered the update hook. -** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their -** database connections for the meaning of "modify" in this paragraph. -** -** ^The sqlite3_update_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. -** -** See also the [sqlite3_commit_hook()], [sqlite3_rollback_hook()], -** and [sqlite3_preupdate_hook()] interfaces. -*/ -SQLITE_API void *sqlite3_update_hook( - sqlite3*, - void(*)(void *,int ,char const *,char const *,sqlite3_int64), - void* -); - -/* -** CAPI3REF: Enable Or Disable Shared Pager Cache -** -** ^(This routine enables or disables the sharing of the database cache -** and schema data structures between [database connection | connections] -** to the same database. Sharing is enabled if the argument is true -** and disabled if the argument is false.)^ -** -** ^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, -** sharing was enabled or disabled for each thread separately. -** -** ^(The cache sharing mode set by this interface effects all subsequent -** calls to [sqlite3_open()], [sqlite3_open_v2()], and [sqlite3_open16()]. -** Existing database connections continue to use the sharing mode -** that was in effect at the time they were opened.)^ -** -** ^(This routine returns [SQLITE_OK] if shared cache was enabled or disabled -** successfully. An [error code] is returned otherwise.)^ -** -** ^Shared cache is disabled by default. It is recommended that it stay -** that way. In other words, do not use this routine. This interface -** continues to be provided for historical compatibility, but its use is -** discouraged. Any use of shared cache is discouraged. If shared cache -** must be used, it is recommended that shared cache only be enabled for -** individual database connections using the [sqlite3_open_v2()] interface -** with the [SQLITE_OPEN_SHAREDCACHE] flag. + /* + ** CAPI3REF: Test For Auto-Commit Mode + ** KEYWORDS: {autocommit mode} + ** METHOD: sqlite3 + ** + ** ^The sqlite3_get_autocommit() interface returns non-zero or + ** zero if the given database connection is or is not in autocommit mode, + ** respectively. ^Autocommit mode is on by default. + ** ^Autocommit mode is disabled by a [BEGIN] statement. + ** ^Autocommit mode is re-enabled by a [COMMIT] or [ROLLBACK]. + ** + ** If certain kinds of errors occur on a statement within a multi-statement + ** transaction (errors including [SQLITE_FULL], [SQLITE_IOERR], + ** [SQLITE_NOMEM], [SQLITE_BUSY], and [SQLITE_INTERRUPT]) then the + ** transaction might be rolled back automatically. The only way to + ** find out whether SQLite automatically rolled back the transaction after + ** an error is to use this function. + ** + ** If another thread changes the autocommit status of the database + ** connection while this routine is running, then the return value + ** is undefined. + */ + SQLITE_API int sqlite3_get_autocommit(sqlite3*); + + /* + ** CAPI3REF: Find The Database Handle Of A Prepared Statement + ** METHOD: sqlite3_stmt + ** + ** ^The sqlite3_db_handle interface returns the [database connection] handle + ** to which a [prepared statement] belongs. ^The [database connection] + ** returned by sqlite3_db_handle is the same [database connection] + ** that was the first argument + ** to the [sqlite3_prepare_v2()] call (or its variants) that was used to + ** create the statement in the first place. + */ + SQLITE_API sqlite3* sqlite3_db_handle(sqlite3_stmt*); + + /* + ** CAPI3REF: Return The Filename For A Database Connection + ** METHOD: sqlite3 + ** + ** ^The sqlite3_db_filename(D,N) interface returns a pointer to the filename + ** associated with database N of connection D. + ** ^If there is no attached database N on the database + ** connection D, or if database N is a temporary or in-memory database, then + ** this function will return either a NULL pointer or an empty string. + ** + ** ^The string value returned by this routine is owned and managed by + ** the database connection. ^The value will be valid until the database N + ** is [DETACH]-ed or until the database connection closes. + ** + ** ^The filename returned by this function is the output of the + ** xFullPathname method of the [VFS]. ^In other words, the filename + ** will be an absolute pathname, even if the filename used + ** to open the database originally was a URI or relative pathname. + ** + ** If the filename pointer returned by this routine is not NULL, then it + ** can be used as the filename input parameter to these routines: + **
      + **
    • [sqlite3_uri_parameter()] + **
    • [sqlite3_uri_boolean()] + **
    • [sqlite3_uri_int64()] + **
    • [sqlite3_filename_database()] + **
    • [sqlite3_filename_journal()] + **
    • [sqlite3_filename_wal()] + **
    + */ + SQLITE_API const char* sqlite3_db_filename(sqlite3* db, const char* zDbName); + + /* + ** CAPI3REF: Determine if a database is read-only + ** METHOD: sqlite3 + ** + ** ^The sqlite3_db_readonly(D,N) interface returns 1 if the database N + ** of connection D is read-only, 0 if it is read/write, or -1 if N is not + ** the name of a database on connection D. + */ + 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. ** -** Note: This method is disabled on MacOS X 10.7 and iOS version 5.0 -** and will always return SQLITE_MISUSE. On those systems, -** shared cache mode should be enabled per-database connection via -** [sqlite3_open_v2()] with [SQLITE_OPEN_SHAREDCACHE]. -** -** This interface is threadsafe on processors where writing a -** 32-bit integer is atomic. -** -** See Also: [SQLite Shared-Cache Mode] -*/ -SQLITE_API int sqlite3_enable_shared_cache(int); - -/* -** CAPI3REF: Attempt To Free Heap Memory -** -** ^The sqlite3_release_memory() interface attempts to free N bytes -** of heap memory by deallocating non-essential memory allocations -** held by the database library. Memory used to cache database -** pages to improve performance is an example of non-essential memory. -** ^sqlite3_release_memory() returns the number of bytes actually freed, -** which might be more or less than the amount requested. -** ^The sqlite3_release_memory() routine is a no-op returning zero -** if SQLite is not compiled with [SQLITE_ENABLE_MEMORY_MANAGEMENT]. -** -** See also: [sqlite3_db_release_memory()] -*/ -SQLITE_API int sqlite3_release_memory(int); - -/* -** CAPI3REF: Free Memory Used By A Database Connection -** METHOD: sqlite3 -** -** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap -** memory as possible from database connection D. Unlike the -** [sqlite3_release_memory()] interface, this interface is in effect even -** when the [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is -** omitted. -** -** See also: [sqlite3_release_memory()] -*/ -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. -** -** ^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. -** ^SQLite strives to keep heap memory utilization below the soft heap -** limit by reducing the number of pages held in the page cache -** as heap memory usages approaches the limit. -** ^The soft heap limit is "soft" because even though SQLite strives to stay -** below the limit, it will exceed the limit rather than generate -** an [SQLITE_NOMEM] error. In other words, the soft heap limit -** is advisory only. -** -** ^The sqlite3_hard_heap_limit64(N) interface sets a hard upper bound of -** N bytes on the amount of memory that will be allocated. ^The -** sqlite3_hard_heap_limit64(N) interface is similar to -** sqlite3_soft_heap_limit64(N) except that memory allocations will fail -** when the hard heap limit is reached. -** -** ^The return value from both sqlite3_soft_heap_limit64() and -** sqlite3_hard_heap_limit64() is the size of -** the heap limit prior to the call, or negative in the case of an -** error. ^If the argument N is negative -** then no change is made to the heap limit. Hence, the current -** size of heap limits can be determined by invoking -** sqlite3_soft_heap_limit64(-1) or sqlite3_hard_heap_limit(-1). -** -** ^Setting the heap limits to zero disables the heap limiter mechanism. -** -** ^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 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 -** limit is set to N. ^Invoking sqlite3_soft_heap_limit64(0) when the -** hard heap limit is enabled makes the soft heap limit equal to the -** hard heap limit. -** -** The memory allocation limits can also be adjusted using -** [PRAGMA soft_heap_limit] and [PRAGMA hard_heap_limit]. -** -** ^(The heap limits are not enforced in the current implementation -** if one or more of following conditions are true: -** -**
      -**
    • The limit value is set to zero. -**
    • Memory accounting is disabled using a combination of the -** [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and -** the [SQLITE_DEFAULT_MEMSTATUS] compile-time option. -**
    • An alternative page cache implementation is specified using -** [sqlite3_config]([SQLITE_CONFIG_PCACHE2],...). -**
    • The page cache allocates from its own memory pool supplied -** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than -** from the heap. -**
    )^ -** -** The circumstances under which SQLite will enforce the heap limits may -** changes 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); - -/* -** CAPI3REF: Deprecated Soft Heap Limit Interface -** DEPRECATED -** -** This is a deprecated version of the [sqlite3_soft_heap_limit64()] -** interface. This routine is provided for historical compatibility -** only. All new applications should use the -** [sqlite3_soft_heap_limit64()] interface rather than this one. -*/ -SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); - - -/* -** CAPI3REF: Extract Metadata About A Column Of A Table -** METHOD: sqlite3 -** -** ^(The sqlite3_table_column_metadata(X,D,T,C,....) routine returns -** information about column C of table T in database D -** on [database connection] X.)^ ^The sqlite3_table_column_metadata() -** interface returns SQLITE_OK and fills in the non-NULL pointers in -** the final five arguments with appropriate values if the specified -** column exists. ^The sqlite3_table_column_metadata() interface returns -** SQLITE_ERROR if the specified column does not exist. -** ^If the column-name parameter to sqlite3_table_column_metadata() is a -** NULL pointer, then this routine simply checks for the existence of the -** table and returns SQLITE_OK if the table exists and SQLITE_ERROR if it -** does not. If the table name parameter T in a call to -** sqlite3_table_column_metadata(X,D,T,C,...) is NULL then the result is -** undefined behavior. -** -** ^The column is identified by the second, third and fourth parameters to -** this function. ^(The second parameter is either the name of the database -** (i.e. "main", "temp", or an attached database) containing the specified -** table or NULL.)^ ^If it is NULL, then all attached databases are searched -** for the table using the same algorithm used by the database engine to -** resolve unqualified table references. -** -** ^The third and fourth parameters to this function are the table and column -** name of the desired column, respectively. -** -** ^Metadata is returned by writing to the memory locations passed as the 5th -** and subsequent parameters to this function. ^Any of these arguments may be -** NULL, in which case the corresponding element of metadata is omitted. -** -** ^(
    -** -**
    Parameter Output
    Type
    Description -** -**
    5th const char* Data type -**
    6th const char* Name of default collation sequence -**
    7th int True if column has a NOT NULL constraint -**
    8th int True if column is part of the PRIMARY KEY -**
    9th int True if column is [AUTOINCREMENT] -**
    -**
    )^ -** -** ^The memory pointed to by the character pointers returned for the -** declaration type and collation sequence is valid until the next -** call to any SQLite API function. -** -** ^If the specified table is actually a view, an [error code] is returned. -** -** ^If the specified column is "rowid", "oid" or "_rowid_" and the table -** is not a [WITHOUT ROWID] table and an -** [INTEGER PRIMARY KEY] column has been explicitly declared, then the output -** parameters are set for the explicitly declared column. ^(If there is no -** [INTEGER PRIMARY KEY] column, then the outputs -** for the [rowid] are set as follows: -** -**
    -**     data type: "INTEGER"
    -**     collation sequence: "BINARY"
    -**     not null: 0
    -**     primary key: 1
    -**     auto increment: 0
    -** 
    )^ -** -** ^This function causes all database schemas to be read from disk and -** parsed, if that has not already been done, and returns an error if -** any errors are encountered while loading the schema. -*/ -SQLITE_API int sqlite3_table_column_metadata( - sqlite3 *db, /* Connection handle */ - const char *zDbName, /* Database name or NULL */ - const char *zTableName, /* Table name */ - const char *zColumnName, /* Column name */ - char const **pzDataType, /* OUTPUT: Declared data type */ - char const **pzCollSeq, /* OUTPUT: Collation sequence name */ - int *pNotNull, /* OUTPUT: True if NOT NULL constraint exists */ - int *pPrimaryKey, /* OUTPUT: True if column part of PK */ - int *pAutoinc /* OUTPUT: True if column is auto-increment */ -); - -/* -** CAPI3REF: Load An Extension -** METHOD: sqlite3 -** -** ^This interface loads an SQLite extension library from the named file. -** -** ^The sqlite3_load_extension() interface attempts to load an -** [SQLite extension] library contained in the file zFile. If -** the file cannot be loaded directly, attempts are made to load -** with various operating-system specific extensions added. -** So for example, if "samplelib" cannot be loaded, then names like -** "samplelib.so" or "samplelib.dylib" or "samplelib.dll" might -** be tried also. -** -** ^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 -** characters in the filename from the last "/" to the first following -** "." and omitting any initial "lib".)^ -** ^The sqlite3_load_extension() interface returns -** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. -** ^If an error occurs and pzErrMsg is not 0, then the -** [sqlite3_load_extension()] interface shall attempt to -** fill *pzErrMsg with error message text stored in memory -** obtained from [sqlite3_malloc()]. The calling function -** should free this memory by calling [sqlite3_free()]. -** -** ^Extension loading must be enabled using -** [sqlite3_enable_load_extension()] or -** [sqlite3_db_config](db,[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION],1,NULL) -** prior to calling this API, -** otherwise an error will be returned. -** -** Security warning: It is recommended that the -** [SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION] method be used to enable only this -** interface. The use of the [sqlite3_enable_load_extension()] interface -** should be avoided. This will keep the SQL function [load_extension()] -** disabled and prevent SQL injections from giving attackers -** access to extension loading capabilities. -** -** See also the [load_extension() SQL function]. -*/ -SQLITE_API int sqlite3_load_extension( - sqlite3 *db, /* Load the extension into this database connection */ - const char *zFile, /* Name of the shared library containing extension */ - const char *zProc, /* Entry point. Derived from zFile if 0 */ - char **pzErrMsg /* Put error message here if not 0 */ -); - -/* -** CAPI3REF: Enable Or Disable Extension Loading -** METHOD: sqlite3 -** -** ^So as not to open security holes in older applications that are -** unprepared to deal with [extension loading], and as a means of disabling -** [extension loading] while evaluating user-entered SQL, the following API -** is provided to turn the [sqlite3_load_extension()] mechanism on and off. -** -** ^Extension loading is off by default. -** ^Call the sqlite3_enable_load_extension() routine with onoff==1 -** to turn extension loading on and call it with onoff==0 to turn -** it back off again. -** -** ^This interface enables or disables both the C-API -** [sqlite3_load_extension()] and the SQL function [load_extension()]. -** ^(Use [sqlite3_db_config](db,[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION],..) -** to enable or disable only the C-API.)^ -** -** Security warning: It is recommended that extension loading -** be enabled using the [SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION] method -** rather than this interface, so the [load_extension()] SQL function -** remains disabled. This will prevent SQL injections from giving attackers -** access to extension loading capabilities. -*/ -SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); - -/* -** CAPI3REF: Automatically Load Statically Linked Extensions -** -** ^This interface causes the xEntryPoint() function to be invoked for -** each new [database connection] that is created. The idea here is that -** xEntryPoint() is the entry point for a statically linked [SQLite extension] -** that is to be automatically loaded into all new database connections. -** -** ^(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: -** -**
    -**    int xEntryPoint(
    -**      sqlite3 *db,
    -**      const char **pzErrMsg,
    -**      const struct sqlite3_api_routines *pThunk
    -**    );
    -** 
    )^ -** -** If the xEntryPoint routine encounters an error, it should make *pzErrMsg -** point to an appropriate error message (obtained from [sqlite3_mprintf()]) -** and return an appropriate [error code]. ^SQLite ensures that *pzErrMsg -** is NULL before calling the xEntryPoint(). ^SQLite will invoke -** [sqlite3_free()] on *pzErrMsg after xEntryPoint() returns. ^If any -** xEntryPoint() returns an error, the [sqlite3_open()], [sqlite3_open16()], -** or [sqlite3_open_v2()] call that provoked the xEntryPoint() will fail. -** -** ^Calling sqlite3_auto_extension(X) with an entry point X that is already -** on the list of automatic extensions is a harmless no-op. ^No entry point -** will be called more than once for each database connection that is opened. -** -** See also: [sqlite3_reset_auto_extension()] -** and [sqlite3_cancel_auto_extension()] -*/ -SQLITE_API int sqlite3_auto_extension(void(*xEntryPoint)(void)); - -/* -** CAPI3REF: Cancel Automatic Extension Loading -** -** ^The [sqlite3_cancel_auto_extension(X)] interface unregisters the -** initialization routine X that was registered using a prior call to -** [sqlite3_auto_extension(X)]. ^The [sqlite3_cancel_auto_extension(X)] -** routine returns 1 if initialization routine X was successfully -** unregistered and it returns 0 if X was not on the list of initialization -** routines. -*/ -SQLITE_API int sqlite3_cancel_auto_extension(void(*xEntryPoint)(void)); - -/* -** CAPI3REF: Reset Automatic Extension Loading -** -** ^This interface disables all automatic extensions previously -** registered using [sqlite3_auto_extension()]. -*/ -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 -*/ -typedef struct sqlite3_vtab sqlite3_vtab; -typedef struct sqlite3_index_info sqlite3_index_info; -typedef struct sqlite3_vtab_cursor sqlite3_vtab_cursor; -typedef struct sqlite3_module sqlite3_module; - -/* -** CAPI3REF: Virtual Table Object -** KEYWORDS: sqlite3_module {virtual table module} -** -** This structure, sometimes called a "virtual table module", -** defines the implementation of a [virtual table]. -** This structure consists mostly of methods for the module. -** -** ^A virtual table module is created by filling in a persistent -** instance of this structure and passing a pointer to that instance -** to [sqlite3_create_module()] or [sqlite3_create_module_v2()]. -** ^The registration remains valid until it is replaced by a different -** module or until the [database connection] closes. The content -** of this structure must not change while it is registered with -** any database connection. -*/ -struct sqlite3_module { - int iVersion; - int (*xCreate)(sqlite3*, void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVTab, char**); - int (*xConnect)(sqlite3*, void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVTab, char**); - int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*); - int (*xDisconnect)(sqlite3_vtab *pVTab); - int (*xDestroy)(sqlite3_vtab *pVTab); - int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor); - int (*xClose)(sqlite3_vtab_cursor*); - int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr, - int argc, sqlite3_value **argv); - int (*xNext)(sqlite3_vtab_cursor*); - int (*xEof)(sqlite3_vtab_cursor*); - int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int); - int (*xRowid)(sqlite3_vtab_cursor*, sqlite3_int64 *pRowid); - int (*xUpdate)(sqlite3_vtab *, int, sqlite3_value **, sqlite3_int64 *); - int (*xBegin)(sqlite3_vtab *pVTab); - int (*xSync)(sqlite3_vtab *pVTab); - int (*xCommit)(sqlite3_vtab *pVTab); - int (*xRollback)(sqlite3_vtab *pVTab); - int (*xFindFunction)(sqlite3_vtab *pVtab, int nArg, const char *zName, - void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), - void **ppArg); - int (*xRename)(sqlite3_vtab *pVtab, const char *zNew); - /* The methods above are in version 1 of the sqlite_module object. Those - ** below are for version 2 and greater. */ - int (*xSavepoint)(sqlite3_vtab *pVTab, int); - int (*xRelease)(sqlite3_vtab *pVTab, int); - int (*xRollbackTo)(sqlite3_vtab *pVTab, int); - /* 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*); -}; - -/* -** CAPI3REF: Virtual Table Indexing Information -** KEYWORDS: sqlite3_index_info -** -** The sqlite3_index_info structure and its substructures is used as part -** of the [virtual table] interface to -** pass information into and receive the reply from the [xBestIndex] -** method of a [virtual table module]. The fields under **Inputs** are the -** inputs to xBestIndex and are read-only. xBestIndex inserts its -** results into the **Outputs** fields. -** -** ^(The aConstraint[] array records WHERE clause constraints of the form: -** -**
    column OP expr
    -** -** where OP is =, <, <=, >, or >=.)^ ^(The particular operator is -** stored in aConstraint[].op using one of the -** [SQLITE_INDEX_CONSTRAINT_EQ | SQLITE_INDEX_CONSTRAINT_ values].)^ -** ^(The index of the column is stored in -** aConstraint[].iColumn.)^ ^(aConstraint[].usable is TRUE if the -** expr on the right-hand side can be evaluated (and thus the constraint -** is usable) and false if it cannot.)^ -** -** ^The optimizer automatically inverts terms of the form "expr OP column" -** and makes other simplifications to the WHERE clause in an attempt to -** get as many WHERE clause terms into the form shown above as possible. -** ^The aConstraint[] array only reports WHERE clause terms that are -** relevant to the particular virtual table being queried. -** -** ^Information about the ORDER BY clause is stored in aOrderBy[]. -** ^Each term of aOrderBy records a column of the ORDER BY clause. -** -** The colUsed field indicates which columns of the virtual table may be -** required by the current scan. Virtual table columns are numbered from -** zero in the order in which they appear within the CREATE TABLE statement -** passed to sqlite3_declare_vtab(). For the first 63 columns (columns 0-62), -** the corresponding bit is set within the colUsed mask if the column may be -** required by SQLite. If the table has at least 64 columns and any column -** to the right of the first 63 is required, then bit 63 of colUsed is also -** set. In other words, column iCol may be required if the expression -** (colUsed & ((sqlite3_uint64)1 << (iCol>=63 ? 63 : iCol))) evaluates to -** non-zero. -** -** The [xBestIndex] method must fill aConstraintUsage[] with information -** about what parameters to pass to xFilter. ^If argvIndex>0 then -** the right-hand side of the corresponding aConstraint[] is evaluated -** and becomes the argvIndex-th entry in argv. ^(If aConstraintUsage[].omit -** is true, then the constraint is assumed to be fully handled by the -** 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 -** 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 -** [xFilter] method. -** ^[sqlite3_free()] is used to free idxPtr if and only if -** needToFreeIdxPtr 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 -** sorting step is required. -** -** ^The estimatedCost value is an estimate of the cost of a particular -** strategy. A cost of N indicates that the cost of the strategy is similar -** to a linear scan of an SQLite table with N rows. A cost of log(N) -** indicates that the expense of the operation is similar to that of a -** binary search on a unique indexed field of an SQLite table with N rows. -** -** ^The estimatedRows value is an estimate of the number of rows that -** 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. -** -** 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 -** part of the same statement to delete or update a virtual table row and the -** implementation returns SQLITE_CONSTRAINT, then there is no need to rollback -** any database changes. In other words, if the xUpdate() returns -** SQLITE_CONSTRAINT, the database contents must be exactly as they were -** before xUpdate was called. By contrast, if SQLITE_INDEX_SCAN_UNIQUE is not -** set and xUpdate returns SQLITE_CONSTRAINT, any database changes made by -** the xUpdate method are automatically rolled back by SQLite. -** -** IMPORTANT: The estimatedRows field was added to the sqlite3_index_info -** structure for SQLite [version 3.8.2] ([dateof:3.8.2]). -** If a virtual table extension is -** used with an SQLite version earlier than 3.8.2, the results of attempting -** to read or write the estimatedRows field are undefined (but are likely -** to include crashing the application). The estimatedRows field should -** therefore only be used if [sqlite3_libversion_number()] returns a -** value greater than or equal to 3008002. Similarly, the idxFlags field -** was added for [version 3.9.0] ([dateof:3.9.0]). -** It may therefore only be used if -** sqlite3_libversion_number() returns a value greater than or equal to -** 3009000. -*/ -struct sqlite3_index_info { - /* Inputs */ - int nConstraint; /* Number of entries in aConstraint */ - struct sqlite3_index_constraint { - int iColumn; /* Column constrained. -1 for ROWID */ - unsigned char op; /* Constraint operator */ - unsigned char usable; /* True if this constraint is usable */ - int iTermOffset; /* Used internally - xBestIndex should ignore */ - } *aConstraint; /* Table of WHERE clause constraints */ - int nOrderBy; /* Number of terms in the ORDER BY clause */ - struct sqlite3_index_orderby { - int iColumn; /* Column number */ - unsigned char desc; /* True for DESC. False for ASC. */ - } *aOrderBy; /* The ORDER BY clause */ - /* Outputs */ - struct sqlite3_index_constraint_usage { - int argvIndex; /* if >0, constraint is part of argv to xFilter */ - unsigned char omit; /* Do not code a test for this constraint */ - } *aConstraintUsage; - int idxNum; /* Number used to identify the index */ - char *idxStr; /* String, possibly obtained from sqlite3_malloc */ - int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */ - int orderByConsumed; /* True if output is already ordered */ - double estimatedCost; /* Estimated cost of using this index */ - /* Fields below are only available in SQLite 3.8.2 and later */ - sqlite3_int64 estimatedRows; /* Estimated number of rows returned */ - /* Fields below are only available in SQLite 3.9.0 and later */ - int idxFlags; /* Mask of SQLITE_INDEX_SCAN_* flags */ - /* Fields below are only available in SQLite 3.10.0 and later */ - sqlite3_uint64 colUsed; /* Input: Mask of columns used by statement */ -}; +**
    +** [[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 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 +** 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 + ** + ** ^This interface returns a pointer to the next [prepared statement] after + ** pStmt associated with the [database connection] pDb. ^If pStmt is NULL + ** then this interface returns a pointer to the first prepared statement + ** associated with the database connection pDb. ^If no prepared statement + ** satisfies the conditions of this routine, it returns NULL. + ** + ** The [database connection] pointer D in a call to + ** [sqlite3_next_stmt(D,S)] must refer to an open database + ** connection and in particular must not be a NULL pointer. + */ + SQLITE_API sqlite3_stmt* sqlite3_next_stmt(sqlite3* pDb, sqlite3_stmt* pStmt); + + /* + ** CAPI3REF: Commit And Rollback Notification Callbacks + ** METHOD: sqlite3 + ** + ** ^The sqlite3_commit_hook() interface registers a callback + ** function to be invoked whenever a transaction is [COMMIT | committed]. + ** ^Any callback set by a previous call to sqlite3_commit_hook() + ** for the same database connection is overridden. + ** ^The sqlite3_rollback_hook() interface registers a callback + ** function to be invoked whenever a transaction is [ROLLBACK | rolled back]. + ** ^Any callback set by a previous call to sqlite3_rollback_hook() + ** for the same database connection is overridden. + ** ^The pArg argument is passed through to the callback. + ** ^If the callback on a commit hook function returns non-zero, + ** then the commit is converted into a rollback. + ** + ** ^The sqlite3_commit_hook(D,C,P) and sqlite3_rollback_hook(D,C,P) functions + ** return the P argument from the previous call of the same function + ** on the same [database connection] D, or NULL for + ** the first call for each function on D. + ** + ** The commit and rollback hook callbacks are not reentrant. + ** The callback implementation must not do anything that will modify + ** the database connection that invoked the callback. Any actions + ** to modify the database connection must be deferred until after the + ** completion of the [sqlite3_step()] call that triggered the commit + ** or rollback hook in the first place. + ** Note that running any other SQL statements, including SELECT statements, + ** or merely calling [sqlite3_prepare_v2()] and [sqlite3_step()] will modify + ** the database connections for the meaning of "modify" in this paragraph. + ** + ** ^Registering a NULL function disables the callback. + ** + ** ^When the commit hook callback routine returns zero, the [COMMIT] + ** operation is allowed to continue normally. ^If the commit hook + ** returns non-zero, then the [COMMIT] is converted into a [ROLLBACK]. + ** ^The rollback hook is invoked on a rollback that results from a commit + ** hook returning non-zero, just as it would be with any other rollback. + ** + ** ^For the purposes of this API, a transaction is said to have been + ** rolled back if an explicit "ROLLBACK" statement is executed, or + ** an error or constraint causes an implicit rollback to occur. + ** ^The rollback callback is not invoked if a transaction is + ** automatically rolled back because the database connection is closed. + ** + ** See also the [sqlite3_update_hook()] interface. + */ + 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 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 cancelled. 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 + ** + ** ^The sqlite3_update_hook() interface registers a callback function + ** with the [database connection] identified by the first argument + ** to be invoked whenever a row is updated, inserted or deleted in + ** a [rowid table]. + ** ^Any callback set by a previous call to this function + ** for the same database connection is overridden. + ** + ** ^The second argument is a pointer to the function to invoke when a + ** row is updated, inserted or deleted in a rowid table. + ** ^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], + ** or [SQLITE_UPDATE], depending on the operation that caused the callback + ** to be invoked. + ** ^The third and fourth arguments to the callback contain pointers to the + ** database and table name containing the affected row. + ** ^The final callback parameter is the [rowid] of the row. + ** ^In the case of an update, this is the [rowid] after the update takes place. + ** + ** ^(The update hook is not invoked when internal system tables are + ** modified (i.e. sqlite_sequence).)^ + ** ^The update hook is not invoked when [WITHOUT ROWID] tables are modified. + ** + ** ^In the current implementation, the update hook + ** is not invoked when conflicting rows are deleted because of an + ** [ON CONFLICT | ON CONFLICT REPLACE] clause. ^Nor is the update hook + ** invoked when rows are deleted using the [truncate optimization]. + ** The exceptions defined in this paragraph might change in a future + ** release of SQLite. + ** + ** 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 + ** completion of the [sqlite3_step()] call that triggered the update hook. + ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their + ** database connections for the meaning of "modify" in this paragraph. + ** + ** ^The sqlite3_update_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. + ** + ** See also the [sqlite3_commit_hook()], [sqlite3_rollback_hook()], + ** and [sqlite3_preupdate_hook()] interfaces. + */ + SQLITE_API void* sqlite3_update_hook(sqlite3*, void (*)(void*, int, char const*, char const*, sqlite3_int64), void*); + + /* + ** CAPI3REF: Enable Or Disable Shared Pager Cache + ** + ** ^(This routine enables or disables the sharing of the database cache + ** and schema data structures between [database connection | connections] + ** to the same database. Sharing is enabled if the argument is true + ** and disabled if the argument is false.)^ + ** + ** ^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, + ** sharing was enabled or disabled for each thread separately. + ** + ** ^(The cache sharing mode set by this interface effects all subsequent + ** calls to [sqlite3_open()], [sqlite3_open_v2()], and [sqlite3_open16()]. + ** Existing database connections continue to use the sharing mode + ** that was in effect at the time they were opened.)^ + ** + ** ^(This routine returns [SQLITE_OK] if shared cache was enabled or disabled + ** successfully. An [error code] is returned otherwise.)^ + ** + ** ^Shared cache is disabled by default. It is recommended that it stay + ** that way. In other words, do not use this routine. This interface + ** continues to be provided for historical compatibility, but its use is + ** discouraged. Any use of shared cache is discouraged. If shared cache + ** must be used, it is recommended that shared cache only be enabled for + ** individual database connections using the [sqlite3_open_v2()] interface + ** with the [SQLITE_OPEN_SHAREDCACHE] flag. + ** + ** Note: This method is disabled on MacOS X 10.7 and iOS version 5.0 + ** and will always return SQLITE_MISUSE. On those systems, + ** shared cache mode should be enabled per-database connection via + ** [sqlite3_open_v2()] with [SQLITE_OPEN_SHAREDCACHE]. + ** + ** This interface is threadsafe on processors where writing a + ** 32-bit integer is atomic. + ** + ** See Also: [SQLite Shared-Cache Mode] + */ + SQLITE_API int sqlite3_enable_shared_cache(int); + + /* + ** CAPI3REF: Attempt To Free Heap Memory + ** + ** ^The sqlite3_release_memory() interface attempts to free N bytes + ** of heap memory by deallocating non-essential memory allocations + ** held by the database library. Memory used to cache database + ** pages to improve performance is an example of non-essential memory. + ** ^sqlite3_release_memory() returns the number of bytes actually freed, + ** which might be more or less than the amount requested. + ** ^The sqlite3_release_memory() routine is a no-op returning zero + ** if SQLite is not compiled with [SQLITE_ENABLE_MEMORY_MANAGEMENT]. + ** + ** See also: [sqlite3_db_release_memory()] + */ + SQLITE_API int sqlite3_release_memory(int); + + /* + ** CAPI3REF: Free Memory Used By A Database Connection + ** METHOD: sqlite3 + ** + ** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap + ** memory as possible from database connection D. Unlike the + ** [sqlite3_release_memory()] interface, this interface is in effect even + ** when the [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is + ** omitted. + ** + ** See also: [sqlite3_release_memory()] + */ + 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. + ** + ** ^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. + ** ^SQLite strives to keep heap memory utilization below the soft heap + ** limit by reducing the number of pages held in the page cache + ** as heap memory usages approaches the limit. + ** ^The soft heap limit is "soft" because even though SQLite strives to stay + ** below the limit, it will exceed the limit rather than generate + ** an [SQLITE_NOMEM] error. In other words, the soft heap limit + ** is advisory only. + ** + ** ^The sqlite3_hard_heap_limit64(N) interface sets a hard upper bound of + ** N bytes on the amount of memory that will be allocated. ^The + ** sqlite3_hard_heap_limit64(N) interface is similar to + ** sqlite3_soft_heap_limit64(N) except that memory allocations will fail + ** when the hard heap limit is reached. + ** + ** ^The return value from both sqlite3_soft_heap_limit64() and + ** sqlite3_hard_heap_limit64() is the size of + ** the heap limit prior to the call, or negative in the case of an + ** error. ^If the argument N is negative + ** then no change is made to the heap limit. Hence, the current + ** size of heap limits can be determined by invoking + ** sqlite3_soft_heap_limit64(-1) or sqlite3_hard_heap_limit(-1). + ** + ** ^Setting the heap limits to zero disables the heap limiter mechanism. + ** + ** ^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 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 + ** limit is set to N. ^Invoking sqlite3_soft_heap_limit64(0) when the + ** hard heap limit is enabled makes the soft heap limit equal to the + ** hard heap limit. + ** + ** The memory allocation limits can also be adjusted using + ** [PRAGMA soft_heap_limit] and [PRAGMA hard_heap_limit]. + ** + ** ^(The heap limits are not enforced in the current implementation + ** if one or more of following conditions are true: + ** + **
      + **
    • The limit value is set to zero. + **
    • Memory accounting is disabled using a combination of the + ** [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and + ** the [SQLITE_DEFAULT_MEMSTATUS] compile-time option. + **
    • An alternative page cache implementation is specified using + ** [sqlite3_config]([SQLITE_CONFIG_PCACHE2],...). + **
    • The page cache allocates from its own memory pool supplied + ** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than + ** from the heap. + **
    )^ + ** + ** The circumstances under which SQLite will enforce the heap limits may + ** changes 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); + + /* + ** CAPI3REF: Deprecated Soft Heap Limit Interface + ** DEPRECATED + ** + ** This is a deprecated version of the [sqlite3_soft_heap_limit64()] + ** interface. This routine is provided for historical compatibility + ** only. All new applications should use the + ** [sqlite3_soft_heap_limit64()] interface rather than this one. + */ + SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); + + /* + ** CAPI3REF: Extract Metadata About A Column Of A Table + ** METHOD: sqlite3 + ** + ** ^(The sqlite3_table_column_metadata(X,D,T,C,....) routine returns + ** information about column C of table T in database D + ** on [database connection] X.)^ ^The sqlite3_table_column_metadata() + ** interface returns SQLITE_OK and fills in the non-NULL pointers in + ** the final five arguments with appropriate values if the specified + ** column exists. ^The sqlite3_table_column_metadata() interface returns + ** SQLITE_ERROR if the specified column does not exist. + ** ^If the column-name parameter to sqlite3_table_column_metadata() is a + ** NULL pointer, then this routine simply checks for the existence of the + ** table and returns SQLITE_OK if the table exists and SQLITE_ERROR if it + ** does not. If the table name parameter T in a call to + ** sqlite3_table_column_metadata(X,D,T,C,...) is NULL then the result is + ** undefined behavior. + ** + ** ^The column is identified by the second, third and fourth parameters to + ** this function. ^(The second parameter is either the name of the database + ** (i.e. "main", "temp", or an attached database) containing the specified + ** table or NULL.)^ ^If it is NULL, then all attached databases are searched + ** for the table using the same algorithm used by the database engine to + ** resolve unqualified table references. + ** + ** ^The third and fourth parameters to this function are the table and column + ** name of the desired column, respectively. + ** + ** ^Metadata is returned by writing to the memory locations passed as the 5th + ** and subsequent parameters to this function. ^Any of these arguments may be + ** NULL, in which case the corresponding element of metadata is omitted. + ** + ** ^(
    + ** + **
    Parameter Output
    Type
    Description + ** + **
    5th const char* Data type + **
    6th const char* Name of default collation sequence + **
    7th int True if column has a NOT NULL constraint + **
    8th int True if column is part of the PRIMARY KEY + **
    9th int True if column is [AUTOINCREMENT] + **
    + **
    )^ + ** + ** ^The memory pointed to by the character pointers returned for the + ** declaration type and collation sequence is valid until the next + ** call to any SQLite API function. + ** + ** ^If the specified table is actually a view, an [error code] is returned. + ** + ** ^If the specified column is "rowid", "oid" or "_rowid_" and the table + ** is not a [WITHOUT ROWID] table and an + ** [INTEGER PRIMARY KEY] column has been explicitly declared, then the output + ** parameters are set for the explicitly declared column. ^(If there is no + ** [INTEGER PRIMARY KEY] column, then the outputs + ** for the [rowid] are set as follows: + ** + **
    +    **     data type: "INTEGER"
    +    **     collation sequence: "BINARY"
    +    **     not null: 0
    +    **     primary key: 1
    +    **     auto increment: 0
    +    ** 
    )^ + ** + ** ^This function causes all database schemas to be read from disk and + ** parsed, if that has not already been done, and returns an error if + ** any errors are encountered while loading the schema. + */ + SQLITE_API int sqlite3_table_column_metadata(sqlite3* db, /* Connection handle */ + const char* zDbName, /* Database name or NULL */ + const char* zTableName, /* Table name */ + const char* zColumnName, /* Column name */ + char const** pzDataType, /* OUTPUT: Declared data type */ + char const** pzCollSeq, /* OUTPUT: Collation sequence name */ + int* pNotNull, /* OUTPUT: True if NOT NULL constraint exists */ + int* pPrimaryKey, /* OUTPUT: True if column part of PK */ + int* pAutoinc /* OUTPUT: True if column is auto-increment */ + ); + + /* + ** CAPI3REF: Load An Extension + ** METHOD: sqlite3 + ** + ** ^This interface loads an SQLite extension library from the named file. + ** + ** ^The sqlite3_load_extension() interface attempts to load an + ** [SQLite extension] library contained in the file zFile. If + ** the file cannot be loaded directly, attempts are made to load + ** with various operating-system specific extensions added. + ** So for example, if "samplelib" cannot be loaded, then names like + ** "samplelib.so" or "samplelib.dylib" or "samplelib.dll" might + ** be tried also. + ** + ** ^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 + ** characters in the filename from the last "/" to the first following + ** "." and omitting any initial "lib".)^ + ** ^The sqlite3_load_extension() interface returns + ** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. + ** ^If an error occurs and pzErrMsg is not 0, then the + ** [sqlite3_load_extension()] interface shall attempt to + ** fill *pzErrMsg with error message text stored in memory + ** obtained from [sqlite3_malloc()]. The calling function + ** should free this memory by calling [sqlite3_free()]. + ** + ** ^Extension loading must be enabled using + ** [sqlite3_enable_load_extension()] or + ** [sqlite3_db_config](db,[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION],1,NULL) + ** prior to calling this API, + ** otherwise an error will be returned. + ** + ** Security warning: It is recommended that the + ** [SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION] method be used to enable only this + ** interface. The use of the [sqlite3_enable_load_extension()] interface + ** should be avoided. This will keep the SQL function [load_extension()] + ** disabled and prevent SQL injections from giving attackers + ** access to extension loading capabilities. + ** + ** See also the [load_extension() SQL function]. + */ + SQLITE_API int sqlite3_load_extension(sqlite3* db, /* Load the extension into this database connection */ + const char* zFile, /* Name of the shared library containing extension */ + const char* zProc, /* Entry point. Derived from zFile if 0 */ + char** pzErrMsg /* Put error message here if not 0 */ + ); + + /* + ** CAPI3REF: Enable Or Disable Extension Loading + ** METHOD: sqlite3 + ** + ** ^So as not to open security holes in older applications that are + ** unprepared to deal with [extension loading], and as a means of disabling + ** [extension loading] while evaluating user-entered SQL, the following API + ** is provided to turn the [sqlite3_load_extension()] mechanism on and off. + ** + ** ^Extension loading is off by default. + ** ^Call the sqlite3_enable_load_extension() routine with onoff==1 + ** to turn extension loading on and call it with onoff==0 to turn + ** it back off again. + ** + ** ^This interface enables or disables both the C-API + ** [sqlite3_load_extension()] and the SQL function [load_extension()]. + ** ^(Use [sqlite3_db_config](db,[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION],..) + ** to enable or disable only the C-API.)^ + ** + ** Security warning: It is recommended that extension loading + ** be enabled using the [SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION] method + ** rather than this interface, so the [load_extension()] SQL function + ** remains disabled. This will prevent SQL injections from giving attackers + ** access to extension loading capabilities. + */ + SQLITE_API int sqlite3_enable_load_extension(sqlite3* db, int onoff); + + /* + ** CAPI3REF: Automatically Load Statically Linked Extensions + ** + ** ^This interface causes the xEntryPoint() function to be invoked for + ** each new [database connection] that is created. The idea here is that + ** xEntryPoint() is the entry point for a statically linked [SQLite extension] + ** that is to be automatically loaded into all new database connections. + ** + ** ^(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: + ** + **
    +    **    int xEntryPoint(
    +    **      sqlite3 *db,
    +    **      const char **pzErrMsg,
    +    **      const struct sqlite3_api_routines *pThunk
    +    **    );
    +    ** 
    )^ + ** + ** If the xEntryPoint routine encounters an error, it should make *pzErrMsg + ** point to an appropriate error message (obtained from [sqlite3_mprintf()]) + ** and return an appropriate [error code]. ^SQLite ensures that *pzErrMsg + ** is NULL before calling the xEntryPoint(). ^SQLite will invoke + ** [sqlite3_free()] on *pzErrMsg after xEntryPoint() returns. ^If any + ** xEntryPoint() returns an error, the [sqlite3_open()], [sqlite3_open16()], + ** or [sqlite3_open_v2()] call that provoked the xEntryPoint() will fail. + ** + ** ^Calling sqlite3_auto_extension(X) with an entry point X that is already + ** on the list of automatic extensions is a harmless no-op. ^No entry point + ** will be called more than once for each database connection that is opened. + ** + ** See also: [sqlite3_reset_auto_extension()] + ** and [sqlite3_cancel_auto_extension()] + */ + SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void)); + + /* + ** CAPI3REF: Cancel Automatic Extension Loading + ** + ** ^The [sqlite3_cancel_auto_extension(X)] interface unregisters the + ** initialization routine X that was registered using a prior call to + ** [sqlite3_auto_extension(X)]. ^The [sqlite3_cancel_auto_extension(X)] + ** routine returns 1 if initialization routine X was successfully + ** unregistered and it returns 0 if X was not on the list of initialization + ** routines. + */ + SQLITE_API int sqlite3_cancel_auto_extension(void (*xEntryPoint)(void)); + + /* + ** CAPI3REF: Reset Automatic Extension Loading + ** + ** ^This interface disables all automatic extensions previously + ** registered using [sqlite3_auto_extension()]. + */ + 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 + */ + typedef struct sqlite3_vtab sqlite3_vtab; + typedef struct sqlite3_index_info sqlite3_index_info; + typedef struct sqlite3_vtab_cursor sqlite3_vtab_cursor; + typedef struct sqlite3_module sqlite3_module; + + /* + ** CAPI3REF: Virtual Table Object + ** KEYWORDS: sqlite3_module {virtual table module} + ** + ** This structure, sometimes called a "virtual table module", + ** defines the implementation of a [virtual table]. + ** This structure consists mostly of methods for the module. + ** + ** ^A virtual table module is created by filling in a persistent + ** instance of this structure and passing a pointer to that instance + ** to [sqlite3_create_module()] or [sqlite3_create_module_v2()]. + ** ^The registration remains valid until it is replaced by a different + ** module or until the [database connection] closes. The content + ** of this structure must not change while it is registered with + ** any database connection. + */ + struct sqlite3_module + { + int iVersion; + int (*xCreate)(sqlite3*, void* pAux, int argc, const char* const* argv, sqlite3_vtab** ppVTab, char**); + int (*xConnect)(sqlite3*, void* pAux, int argc, const char* const* argv, sqlite3_vtab** ppVTab, char**); + int (*xBestIndex)(sqlite3_vtab* pVTab, sqlite3_index_info*); + int (*xDisconnect)(sqlite3_vtab* pVTab); + int (*xDestroy)(sqlite3_vtab* pVTab); + int (*xOpen)(sqlite3_vtab* pVTab, sqlite3_vtab_cursor** ppCursor); + int (*xClose)(sqlite3_vtab_cursor*); + int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char* idxStr, int argc, sqlite3_value** argv); + int (*xNext)(sqlite3_vtab_cursor*); + int (*xEof)(sqlite3_vtab_cursor*); + int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int); + int (*xRowid)(sqlite3_vtab_cursor*, sqlite3_int64* pRowid); + int (*xUpdate)(sqlite3_vtab*, int, sqlite3_value**, sqlite3_int64*); + int (*xBegin)(sqlite3_vtab* pVTab); + int (*xSync)(sqlite3_vtab* pVTab); + int (*xCommit)(sqlite3_vtab* pVTab); + int (*xRollback)(sqlite3_vtab* pVTab); + int (*xFindFunction)(sqlite3_vtab* pVtab, + int nArg, + const char* zName, + void (**pxFunc)(sqlite3_context*, int, sqlite3_value**), + void** ppArg); + int (*xRename)(sqlite3_vtab* pVtab, const char* zNew); + /* The methods above are in version 1 of the sqlite_module object. Those + ** below are for version 2 and greater. */ + int (*xSavepoint)(sqlite3_vtab* pVTab, int); + int (*xRelease)(sqlite3_vtab* pVTab, int); + int (*xRollbackTo)(sqlite3_vtab* pVTab, int); + /* 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*); + }; + + /* + ** CAPI3REF: Virtual Table Indexing Information + ** KEYWORDS: sqlite3_index_info + ** + ** The sqlite3_index_info structure and its substructures is used as part + ** of the [virtual table] interface to + ** pass information into and receive the reply from the [xBestIndex] + ** method of a [virtual table module]. The fields under **Inputs** are the + ** inputs to xBestIndex and are read-only. xBestIndex inserts its + ** results into the **Outputs** fields. + ** + ** ^(The aConstraint[] array records WHERE clause constraints of the form: + ** + **
    column OP expr
    + ** + ** where OP is =, <, <=, >, or >=.)^ ^(The particular operator is + ** stored in aConstraint[].op using one of the + ** [SQLITE_INDEX_CONSTRAINT_EQ | SQLITE_INDEX_CONSTRAINT_ values].)^ + ** ^(The index of the column is stored in + ** aConstraint[].iColumn.)^ ^(aConstraint[].usable is TRUE if the + ** expr on the right-hand side can be evaluated (and thus the constraint + ** is usable) and false if it cannot.)^ + ** + ** ^The optimizer automatically inverts terms of the form "expr OP column" + ** and makes other simplifications to the WHERE clause in an attempt to + ** get as many WHERE clause terms into the form shown above as possible. + ** ^The aConstraint[] array only reports WHERE clause terms that are + ** relevant to the particular virtual table being queried. + ** + ** ^Information about the ORDER BY clause is stored in aOrderBy[]. + ** ^Each term of aOrderBy records a column of the ORDER BY clause. + ** + ** The colUsed field indicates which columns of the virtual table may be + ** required by the current scan. Virtual table columns are numbered from + ** zero in the order in which they appear within the CREATE TABLE statement + ** passed to sqlite3_declare_vtab(). For the first 63 columns (columns 0-62), + ** the corresponding bit is set within the colUsed mask if the column may be + ** required by SQLite. If the table has at least 64 columns and any column + ** to the right of the first 63 is required, then bit 63 of colUsed is also + ** set. In other words, column iCol may be required if the expression + ** (colUsed & ((sqlite3_uint64)1 << (iCol>=63 ? 63 : iCol))) evaluates to + ** non-zero. + ** + ** The [xBestIndex] method must fill aConstraintUsage[] with information + ** about what parameters to pass to xFilter. ^If argvIndex>0 then + ** the right-hand side of the corresponding aConstraint[] is evaluated + ** and becomes the argvIndex-th entry in argv. ^(If aConstraintUsage[].omit + ** is true, then the constraint is assumed to be fully handled by the + ** 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 + ** 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 + ** [xFilter] method. + ** ^[sqlite3_free()] is used to free idxPtr if and only if + ** needToFreeIdxPtr 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 + ** sorting step is required. + ** + ** ^The estimatedCost value is an estimate of the cost of a particular + ** strategy. A cost of N indicates that the cost of the strategy is similar + ** to a linear scan of an SQLite table with N rows. A cost of log(N) + ** indicates that the expense of the operation is similar to that of a + ** binary search on a unique indexed field of an SQLite table with N rows. + ** + ** ^The estimatedRows value is an estimate of the number of rows that + ** 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. + ** + ** 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 + ** part of the same statement to delete or update a virtual table row and the + ** implementation returns SQLITE_CONSTRAINT, then there is no need to rollback + ** any database changes. In other words, if the xUpdate() returns + ** SQLITE_CONSTRAINT, the database contents must be exactly as they were + ** before xUpdate was called. By contrast, if SQLITE_INDEX_SCAN_UNIQUE is not + ** set and xUpdate returns SQLITE_CONSTRAINT, any database changes made by + ** the xUpdate method are automatically rolled back by SQLite. + ** + ** IMPORTANT: The estimatedRows field was added to the sqlite3_index_info + ** structure for SQLite [version 3.8.2] ([dateof:3.8.2]). + ** If a virtual table extension is + ** used with an SQLite version earlier than 3.8.2, the results of attempting + ** to read or write the estimatedRows field are undefined (but are likely + ** to include crashing the application). The estimatedRows field should + ** therefore only be used if [sqlite3_libversion_number()] returns a + ** value greater than or equal to 3008002. Similarly, the idxFlags field + ** was added for [version 3.9.0] ([dateof:3.9.0]). + ** It may therefore only be used if + ** sqlite3_libversion_number() returns a value greater than or equal to + ** 3009000. + */ + struct sqlite3_index_info + { + /* Inputs */ + int nConstraint; /* Number of entries in aConstraint */ + struct sqlite3_index_constraint + { + int iColumn; /* Column constrained. -1 for ROWID */ + unsigned char op; /* Constraint operator */ + unsigned char usable; /* True if this constraint is usable */ + int iTermOffset; /* Used internally - xBestIndex should ignore */ + } * aConstraint; /* Table of WHERE clause constraints */ + int nOrderBy; /* Number of terms in the ORDER BY clause */ + struct sqlite3_index_orderby + { + int iColumn; /* Column number */ + unsigned char desc; /* True for DESC. False for ASC. */ + } * aOrderBy; /* The ORDER BY clause */ + /* Outputs */ + struct sqlite3_index_constraint_usage + { + int argvIndex; /* if >0, constraint is part of argv to xFilter */ + unsigned char omit; /* Do not code a test for this constraint */ + } * aConstraintUsage; + int idxNum; /* Number used to identify the index */ + char* idxStr; /* String, possibly obtained from sqlite3_malloc */ + int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */ + int orderByConsumed; /* True if output is already ordered */ + double estimatedCost; /* Estimated cost of using this index */ + /* Fields below are only available in SQLite 3.8.2 and later */ + sqlite3_int64 estimatedRows; /* Estimated number of rows returned */ + /* Fields below are only available in SQLite 3.9.0 and later */ + int idxFlags; /* Mask of SQLITE_INDEX_SCAN_* flags */ + /* Fields below are only available in SQLite 3.10.0 and later */ + sqlite3_uint64 colUsed; /* Input: Mask of columns used by statement */ + }; /* ** CAPI3REF: Virtual Table Scan Flags @@ -6886,653 +7074,683 @@ ** [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 1 /* Scan visits at most 1 row */ /* ** 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 +** +** ^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 no 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_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 -** METHOD: sqlite3 -** -** ^These routines are used to register a new [virtual table module] name. -** ^Module names must be registered before -** creating a new [virtual table] using the module and before using a -** preexisting [virtual table] for the module. -** -** ^The module name is registered on the [database connection] specified -** by the first parameter. ^The name of the module is given by the -** second parameter. ^The third parameter is a pointer to -** 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. -** -** ^The sqlite3_create_module_v2() interface has a fifth parameter which -** is a pointer to a destructor for the pClientData. ^SQLite will -** invoke the destructor function (if it is not NULL) when SQLite -** no longer needs the pClientData pointer. ^The destructor will also -** be invoked if the call to sqlite3_create_module_v2() fails. -** ^The sqlite3_create_module() -** interface is equivalent to sqlite3_create_module_v2() with a NULL -** 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 -** same name are dropped. -** -** See also: [sqlite3_drop_modules()] -*/ -SQLITE_API int sqlite3_create_module( - sqlite3 *db, /* SQLite connection to register module with */ - const char *zName, /* Name of the module */ - const sqlite3_module *p, /* Methods for the module */ - void *pClientData /* Client data for xCreate/xConnect */ -); -SQLITE_API int sqlite3_create_module_v2( - sqlite3 *db, /* SQLite connection to register module with */ - const char *zName, /* Name of the module */ - const sqlite3_module *p, /* Methods for the module */ - void *pClientData, /* Client data for xCreate/xConnect */ - void(*xDestroy)(void*) /* Module destructor function */ -); - -/* -** CAPI3REF: Remove Unnecessary Virtual Table Implementations -** METHOD: sqlite3 -** -** ^The sqlite3_drop_modules(D,L) interface removes all virtual -** table modules from database connection D except those named on list L. -** The L parameter must be either NULL or a pointer to an array of pointers -** to strings where the array is terminated by a single NULL pointer. -** ^If the L parameter is NULL, then all virtual table modules are removed. -** -** See also: [sqlite3_create_module()] -*/ -SQLITE_API int sqlite3_drop_modules( - sqlite3 *db, /* Remove modules from this connection */ - const char **azKeep /* Except, do not remove the ones named here */ -); - -/* -** CAPI3REF: Virtual Table Instance Object -** KEYWORDS: sqlite3_vtab -** -** Every [virtual table module] implementation uses a subclass -** of this object to describe a particular instance -** of the [virtual table]. Each subclass will -** be tailored to the specific needs of the module implementation. -** The purpose of this superclass is to define certain fields that are -** common to all module implementations. -** -** ^Virtual tables methods can set an error message by assigning a -** string obtained from [sqlite3_mprintf()] to zErrMsg. The method should -** take care that any prior string is freed by a call to [sqlite3_free()] -** prior to assigning a new string to zErrMsg. ^After the error message -** is delivered up to the client application, the string will be automatically -** freed by sqlite3_free() and the zErrMsg field will be zeroed. -*/ -struct sqlite3_vtab { - const sqlite3_module *pModule; /* The module for this virtual table */ - int nRef; /* Number of open cursors */ - char *zErrMsg; /* Error message from sqlite3_mprintf() */ - /* Virtual table implementations will typically add additional fields */ -}; - -/* -** CAPI3REF: Virtual Table Cursor Object -** KEYWORDS: sqlite3_vtab_cursor {virtual table cursor} -** -** Every [virtual table module] implementation uses a subclass of the -** following structure to describe cursors that point into the -** [virtual table] and are used -** to loop through the virtual table. Cursors are created using the -** [sqlite3_module.xOpen | xOpen] method of the module and are destroyed -** by the [sqlite3_module.xClose | xClose] method. Cursors are used -** by the [xFilter], [xNext], [xEof], [xColumn], and [xRowid] methods -** of the module. Each module implementation will define -** the content of a cursor structure to suit its own needs. -** -** This superclass exists in order to define fields of the cursor that -** are common to all implementations. -*/ -struct sqlite3_vtab_cursor { - sqlite3_vtab *pVtab; /* Virtual table of this cursor */ - /* Virtual table implementations will typically add additional fields */ -}; - -/* -** CAPI3REF: Declare The Schema Of A Virtual Table -** -** ^The [xCreate] and [xConnect] methods of a -** [virtual table module] call this interface -** to declare the format (the names and datatypes of the columns) of -** the virtual tables they implement. -*/ -SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL); - -/* -** CAPI3REF: Overload A Function For A Virtual Table -** METHOD: sqlite3 -** -** ^(Virtual tables can provide alternative implementations of functions -** using the [xFindFunction] method of the [virtual table module]. -** But global versions of those functions -** must exist in order to be overloaded.)^ -** -** ^(This API makes sure a global version of a function with a particular -** name and number of parameters exists. If no such function exists -** before this API is called, a new function is created.)^ ^The implementation -** of the new function always causes an exception to be thrown. So -** the new function is not good for anything by itself. Its only -** purpose is to be a placeholder function that can be overloaded -** by a [virtual table]. -*/ -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} -** -** An instance of this object represents an open BLOB on which -** [sqlite3_blob_open | incremental BLOB I/O] can be performed. -** ^Objects of this type are created by [sqlite3_blob_open()] -** and destroyed by [sqlite3_blob_close()]. -** ^The [sqlite3_blob_read()] and [sqlite3_blob_write()] interfaces -** can be used to read or write small subsections of the BLOB. -** ^The [sqlite3_blob_bytes()] interface returns the size of the BLOB in bytes. -*/ -typedef struct sqlite3_blob sqlite3_blob; - -/* -** CAPI3REF: Open A BLOB For Incremental I/O -** METHOD: sqlite3 -** CONSTRUCTOR: sqlite3_blob -** -** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located -** in row iRow, column zColumn, table zTable in database zDb; -** in other words, the same BLOB that would be selected by: -** -**
    -**     SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow;
    -** 
    )^ -** -** ^(Parameter zDb is not the filename that contains the database, but -** rather the symbolic name of the database. For attached databases, this is -** the name that appears after the AS keyword in the [ATTACH] statement. -** For the main database file, the database name is "main". For TEMP -** tables, the database name is "temp".)^ -** -** ^If the flags parameter is non-zero, then the BLOB is opened for read -** and write access. ^If the flags parameter is zero, the BLOB is opened for -** read-only access. -** -** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is stored -** 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. -** -** This function fails with SQLITE_ERROR if any of the following are true: -**
      -**
    • ^(Database zDb does not exist)^, -**
    • ^(Table zTable does not exist within database zDb)^, -**
    • ^(Table zTable is a WITHOUT ROWID table)^, -**
    • ^(Column zColumn does not exist)^, -**
    • ^(Row iRow is not present in the table)^, -**
    • ^(The specified column of row iRow contains a value that is not -** a TEXT or BLOB value)^, -**
    • ^(Column zColumn is part of an index, PRIMARY KEY or UNIQUE -** constraint and the blob is being opened for read/write access)^, -**
    • ^([foreign key constraints | Foreign key constraints] are enabled, -** column zColumn is part of a [child key] definition and the blob is -** being opened for read/write access)^. -**
    -** -** ^Unless it returns SQLITE_MISUSE, this function sets the -** [database connection] error code and message accessible via -** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions. -** -** A BLOB referenced by sqlite3_blob_open() may be read using the -** [sqlite3_blob_read()] interface and modified by using -** [sqlite3_blob_write()]. The [BLOB handle] can be moved to a -** different row of the same table using the [sqlite3_blob_reopen()] -** interface. However, the column, table, or database of a [BLOB handle] -** cannot be changed after the [BLOB handle] is opened. -** -** ^(If the row that a BLOB handle points to is modified by an -** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects -** then the BLOB handle is marked as "expired". -** This is true if any column of the row is changed, even a column -** other than the one the BLOB handle is open on.)^ -** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for -** an expired BLOB handle fail with a return code of [SQLITE_ABORT]. -** ^(Changes written into a BLOB prior to the BLOB expiring are not -** rolled back by the expiration of the BLOB. Such changes will eventually -** commit if the transaction continues to completion.)^ -** -** ^Use the [sqlite3_blob_bytes()] interface to determine the size of -** the opened blob. ^The size of a blob may not be changed by this -** interface. Use the [UPDATE] SQL command to change the size of a -** blob. -** -** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces -** and the built-in [zeroblob] SQL function may be used to create a -** zero-filled blob to read or write using the incremental-blob interface. -** -** To avoid a resource leak, every open [BLOB handle] should eventually -** be released by a call to [sqlite3_blob_close()]. -** -** See also: [sqlite3_blob_close()], -** [sqlite3_blob_reopen()], [sqlite3_blob_read()], -** [sqlite3_blob_bytes()], [sqlite3_blob_write()]. -*/ -SQLITE_API int sqlite3_blob_open( - sqlite3*, - const char *zDb, - const char *zTable, - const char *zColumn, - sqlite3_int64 iRow, - int flags, - sqlite3_blob **ppBlob -); - -/* -** CAPI3REF: Move a BLOB Handle to a New Row -** METHOD: sqlite3_blob -** -** ^This function is used to move an existing [BLOB handle] so that it points -** to a different row of the same database table. ^The new row is identified -** by the rowid value passed as the second argument. Only the row can be -** changed. ^The database, table and column on which the blob handle is open -** remain the same. Moving an existing [BLOB handle] to a new row is -** faster than closing the existing handle and opening a new one. -** -** ^(The new row must meet the same criteria as for [sqlite3_blob_open()] - -** it must exist and there must be either a blob or text value stored in -** the nominated column.)^ ^If the new row is not present in the table, or if -** it does not contain a blob or text value, or if another error occurs, an -** SQLite error code is returned and the blob handle is considered aborted. -** ^All subsequent calls to [sqlite3_blob_read()], [sqlite3_blob_write()] or -** [sqlite3_blob_reopen()] on an aborted blob handle immediately return -** SQLITE_ABORT. ^Calling [sqlite3_blob_bytes()] on an aborted blob handle -** always returns zero. -** -** ^This function sets the database handle error code and message. -*/ -SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); - -/* -** CAPI3REF: Close A BLOB Handle -** DESTRUCTOR: sqlite3_blob -** -** ^This function closes an open [BLOB handle]. ^(The BLOB handle is closed -** unconditionally. Even if this routine returns an error code, the -** handle is still closed.)^ -** -** ^If the blob handle being closed was opened for read-write access, and if -** the database is in auto-commit mode and there are no other open read-write -** blob handles or active write statements, the current transaction is -** committed. ^If an error occurs while committing the transaction, an error -** 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 -** 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 -** sqlite3_errcode() and sqlite3_errmsg() functions are set before returning. -*/ -SQLITE_API int sqlite3_blob_close(sqlite3_blob *); - -/* -** CAPI3REF: Return The Size Of An Open BLOB -** METHOD: 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 -** blob content; they cannot change the size of a blob. -** -** This routine only works on a [BLOB handle] which has been created -** by a prior successful call to [sqlite3_blob_open()] and which has not -** been closed by [sqlite3_blob_close()]. Passing any other pointer in -** to this routine results in undefined and probably undesirable behavior. -*/ -SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *); - -/* -** CAPI3REF: Read Data From A BLOB Incrementally -** METHOD: sqlite3_blob -** -** ^(This function is used to read data from an open [BLOB handle] into a -** caller-supplied buffer. N bytes of data are copied into buffer Z -** from the open BLOB, starting at offset iOffset.)^ -** -** ^If offset iOffset is less than N bytes from the end of the BLOB, -** [SQLITE_ERROR] is returned and no data is read. ^If N or iOffset is -** less than zero, [SQLITE_ERROR] is returned and no data is read. -** ^The size of the blob (and hence the maximum value of N+iOffset) -** can be determined using the [sqlite3_blob_bytes()] interface. -** -** ^An attempt to read from an expired [BLOB handle] fails with an -** error code of [SQLITE_ABORT]. -** -** ^(On success, sqlite3_blob_read() returns SQLITE_OK. -** Otherwise, an [error code] or an [extended error code] is returned.)^ -** -** This routine only works on a [BLOB handle] which has been created -** by a prior successful call to [sqlite3_blob_open()] and which has not -** been closed by [sqlite3_blob_close()]. Passing any other pointer in -** to this routine results in undefined and probably undesirable behavior. -** -** See also: [sqlite3_blob_write()]. -*/ -SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); - -/* -** CAPI3REF: Write Data Into A BLOB Incrementally -** METHOD: sqlite3_blob -** -** ^(This function is used to write data into an open [BLOB handle] from a -** caller-supplied buffer. N bytes of data are copied from the buffer Z -** into the open BLOB, starting at offset iOffset.)^ -** -** ^(On success, sqlite3_blob_write() returns SQLITE_OK. -** Otherwise, an [error code] or an [extended error code] is returned.)^ -** ^Unless SQLITE_MISUSE is returned, this function sets the -** [database connection] error code and message accessible via -** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions. -** -** ^If the [BLOB handle] passed as the first argument was not opened for -** writing (the flags parameter to [sqlite3_blob_open()] was zero), -** this function returns [SQLITE_READONLY]. -** -** This function may only modify the contents of the BLOB; it is -** not possible to increase the size of a BLOB using this API. -** ^If offset iOffset is less than N bytes from the end of the BLOB, -** [SQLITE_ERROR] is returned and no data is written. The size of the -** BLOB (and hence the maximum value of N+iOffset) can be determined -** using the [sqlite3_blob_bytes()] interface. ^If N or iOffset are less -** than zero [SQLITE_ERROR] is returned and no data is written. -** -** ^An attempt to write to an expired [BLOB handle] fails with an -** error code of [SQLITE_ABORT]. ^Writes to the BLOB that occurred -** before the [BLOB handle] expired are not rolled back by the -** expiration of the handle, though of course those changes might -** have been overwritten by the statement that expired the BLOB handle -** or by other independent statements. -** -** This routine only works on a [BLOB handle] which has been created -** by a prior successful call to [sqlite3_blob_open()] and which has not -** been closed by [sqlite3_blob_close()]. Passing any other pointer in -** to this routine results in undefined and probably undesirable behavior. -** -** See also: [sqlite3_blob_read()]. -*/ -SQLITE_API int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); - -/* -** CAPI3REF: Virtual File System Objects -** -** A virtual filesystem (VFS) is an [sqlite3_vfs] object -** that SQLite uses to interact -** with the underlying operating system. Most SQLite builds come with a -** single default VFS that is appropriate for the host computer. -** New VFSes can be registered and existing VFSes can be unregistered. -** The following interfaces are provided. -** -** ^The sqlite3_vfs_find() interface returns a pointer to a VFS given its name. -** ^Names are case sensitive. -** ^Names are zero-terminated UTF-8 strings. -** ^If there is no match, a NULL pointer is returned. -** ^If zVfsName is NULL then the default VFS is returned. -** -** ^New VFSes are registered with sqlite3_vfs_register(). -** ^Each new VFS becomes the default VFS if the makeDflt flag is set. -** ^The same VFS can be registered multiple times without injury. -** ^To make an existing VFS into the default VFS, register it again -** with the makeDflt flag set. If two different VFSes with the -** same name are registered, the behavior is undefined. If a -** VFS is registered with a name that is NULL or an empty string, -** then the behavior is undefined. -** -** ^Unregister a VFS with the sqlite3_vfs_unregister() interface. -** ^(If the default VFS is unregistered, another VFS is chosen as -** the default. The choice for the new VFS is arbitrary.)^ -*/ -SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName); -SQLITE_API int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); -SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); - -/* -** CAPI3REF: Mutexes -** -** The SQLite core uses these routines for thread -** synchronization. Though they are intended for internal -** use by SQLite, code that links against SQLite is -** permitted to use any of these routines. -** -** The SQLite source code contains multiple implementations -** of these mutex routines. An appropriate implementation -** is selected automatically at compile-time. The following -** implementations are available in the SQLite core: -** -**
      -**
    • SQLITE_MUTEX_PTHREADS -**
    • SQLITE_MUTEX_W32 -**
    • SQLITE_MUTEX_NOOP -**
    -** -** The SQLITE_MUTEX_NOOP implementation is a set of routines -** that does no real locking and is appropriate for use in -** a single-threaded application. The SQLITE_MUTEX_PTHREADS and -** SQLITE_MUTEX_W32 implementations are appropriate for use on Unix -** and Windows. -** -** If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor -** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex -** implementation is included with the library. In this case the -** application must supply a custom mutex implementation using the -** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function -** before calling sqlite3_initialize() or any other public sqlite3_ -** function that calls sqlite3_initialize(). -** -** ^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 -** integer constants: -** -**
      -**
    • SQLITE_MUTEX_FAST -**
    • SQLITE_MUTEX_RECURSIVE -**
    • SQLITE_MUTEX_STATIC_MAIN -**
    • SQLITE_MUTEX_STATIC_MEM -**
    • SQLITE_MUTEX_STATIC_OPEN -**
    • SQLITE_MUTEX_STATIC_PRNG -**
    • SQLITE_MUTEX_STATIC_LRU -**
    • SQLITE_MUTEX_STATIC_PMEM -**
    • SQLITE_MUTEX_STATIC_APP1 -**
    • SQLITE_MUTEX_STATIC_APP2 -**
    • SQLITE_MUTEX_STATIC_APP3 -**
    • SQLITE_MUTEX_STATIC_VFS1 -**
    • SQLITE_MUTEX_STATIC_VFS2 -**
    • SQLITE_MUTEX_STATIC_VFS3 -**
    -** -** ^The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) -** cause sqlite3_mutex_alloc() to create -** a new mutex. ^The new mutex is recursive when SQLITE_MUTEX_RECURSIVE -** is used but not necessarily so when SQLITE_MUTEX_FAST is used. -** The mutex implementation does not need to make a distinction -** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does -** not want to. SQLite will only request a recursive mutex in -** cases where it really needs one. If a faster non-recursive mutex -** implementation is available on the host platform, the mutex subsystem -** might return such a mutex in response to SQLITE_MUTEX_FAST. -** -** ^The other allowed parameters to sqlite3_mutex_alloc() (anything other -** than SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) each return -** a pointer to a static preexisting mutex. ^Nine static mutexes are -** used by the current version of SQLite. Future versions of SQLite -** may add additional static mutexes. Static mutexes are for internal -** use by SQLite only. Applications that use SQLite mutexes should -** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or -** SQLITE_MUTEX_RECURSIVE. -** -** ^Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST -** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc() -** returns a different mutex on every call. ^For the static -** mutex types, the same mutex is returned on every call that has -** the same type number. -** -** ^The sqlite3_mutex_free() routine deallocates a previously -** allocated dynamic mutex. Attempting to deallocate a static -** mutex results in undefined behavior. -** -** ^The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt -** to enter a mutex. ^If another thread is already within the mutex, -** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return -** SQLITE_BUSY. ^The sqlite3_mutex_try() interface returns [SQLITE_OK] -** upon successful entry. ^(Mutexes created using -** SQLITE_MUTEX_RECURSIVE can be entered multiple times by the same thread. -** In such cases, the -** mutex must be exited an equal number of times before another thread -** can enter.)^ If the same thread tries to enter any mutex other -** than an SQLITE_MUTEX_RECURSIVE more than once, the behavior is undefined. -** -** ^(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.)^ -** -** ^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. -** -** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. -*/ -SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); -SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); -SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex*); -SQLITE_API int sqlite3_mutex_try(sqlite3_mutex*); -SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*); - -/* -** CAPI3REF: Mutex Methods Object -** -** An instance of this structure defines the low-level routines -** used to allocate and use mutexes. -** -** Usually, the default mutex implementations provided by SQLite are -** sufficient, however the application has the option of substituting a custom -** implementation for specialized deployments or systems for which SQLite -** does not provide a suitable implementation. In this case, the application -** creates and populates an instance of this structure to pass -** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option. -** Additionally, an instance of this structure can be used as an -** output variable when querying the system for the current mutex -** implementation, using the [SQLITE_CONFIG_GETMUTEX] option. -** -** ^The xMutexInit method defined by this structure is invoked as -** part of system initialization by the sqlite3_initialize() function. -** ^The xMutexInit routine is called by SQLite exactly once for each -** effective call to [sqlite3_initialize()]. -** -** ^The xMutexEnd method defined by this structure is invoked as -** part of system shutdown by the sqlite3_shutdown() function. The -** implementation of this method is expected to release all outstanding -** resources obtained by the mutex methods implementation, especially -** those obtained by the xMutexInit method. ^The xMutexEnd() -** interface is invoked exactly once for each call to [sqlite3_shutdown()]. -** -** ^(The remaining seven methods defined by this structure (xMutexAlloc, -** xMutexFree, xMutexEnter, xMutexTry, xMutexLeave, xMutexHeld and -** xMutexNotheld) implement the following interfaces (respectively): -** -**
      -**
    • [sqlite3_mutex_alloc()]
    • -**
    • [sqlite3_mutex_free()]
    • -**
    • [sqlite3_mutex_enter()]
    • -**
    • [sqlite3_mutex_try()]
    • -**
    • [sqlite3_mutex_leave()]
    • -**
    • [sqlite3_mutex_held()]
    • -**
    • [sqlite3_mutex_notheld()]
    • -**
    )^ -** -** The only difference is that the public sqlite3_XXX functions enumerated -** above silently ignore any invocations that pass a NULL pointer instead -** of a valid mutex handle. The implementations of the methods defined -** by this structure are not required to handle this case. The results -** of passing a NULL pointer instead of a valid mutex handle are undefined -** (i.e. it is acceptable to provide an implementation that segfaults if -** it is passed a NULL pointer). -** -** The xMutexInit() method must be threadsafe. It must be harmless to -** invoke xMutexInit() multiple times within the same process and without -** intervening calls to xMutexEnd(). Second and subsequent calls to -** xMutexInit() must be no-ops. -** -** xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()] -** and its associates). Similarly, xMutexAlloc() must not use SQLite memory -** allocation for a static mutex. ^However xMutexAlloc() may use SQLite -** memory allocation for a fast or recursive mutex. -** -** ^SQLite will invoke the xMutexEnd() method when [sqlite3_shutdown()] is -** called, but only if the prior call to xMutexInit returned SQLITE_OK. -** If xMutexInit fails in any way, it is expected to clean up after itself -** prior to returning. -*/ -typedef struct sqlite3_mutex_methods sqlite3_mutex_methods; -struct sqlite3_mutex_methods { - int (*xMutexInit)(void); - int (*xMutexEnd)(void); - sqlite3_mutex *(*xMutexAlloc)(int); - void (*xMutexFree)(sqlite3_mutex *); - void (*xMutexEnter)(sqlite3_mutex *); - int (*xMutexTry)(sqlite3_mutex *); - void (*xMutexLeave)(sqlite3_mutex *); - int (*xMutexHeld)(sqlite3_mutex *); - int (*xMutexNotheld)(sqlite3_mutex *); -}; + /* + ** CAPI3REF: Register A Virtual Table Implementation + ** METHOD: sqlite3 + ** + ** ^These routines are used to register a new [virtual table module] name. + ** ^Module names must be registered before + ** creating a new [virtual table] using the module and before using a + ** preexisting [virtual table] for the module. + ** + ** ^The module name is registered on the [database connection] specified + ** by the first parameter. ^The name of the module is given by the + ** second parameter. ^The third parameter is a pointer to + ** 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. + ** + ** ^The sqlite3_create_module_v2() interface has a fifth parameter which + ** is a pointer to a destructor for the pClientData. ^SQLite will + ** invoke the destructor function (if it is not NULL) when SQLite + ** no longer needs the pClientData pointer. ^The destructor will also + ** be invoked if the call to sqlite3_create_module_v2() fails. + ** ^The sqlite3_create_module() + ** interface is equivalent to sqlite3_create_module_v2() with a NULL + ** destructor. + ** + ** ^If the third parameter (the pointer to the sqlite3_module object) is + ** NULL then no new module is created and any existing modules with the + ** same name are dropped. + ** + ** See also: [sqlite3_drop_modules()] + */ + SQLITE_API int sqlite3_create_module(sqlite3* db, /* SQLite connection to register module with */ + const char* zName, /* Name of the module */ + const sqlite3_module* p, /* Methods for the module */ + void* pClientData /* Client data for xCreate/xConnect */ + ); + SQLITE_API int sqlite3_create_module_v2(sqlite3* db, /* SQLite connection to register module with */ + const char* zName, /* Name of the module */ + const sqlite3_module* p, /* Methods for the module */ + void* pClientData, /* Client data for xCreate/xConnect */ + void (*xDestroy)(void*) /* Module destructor function */ + ); + + /* + ** CAPI3REF: Remove Unnecessary Virtual Table Implementations + ** METHOD: sqlite3 + ** + ** ^The sqlite3_drop_modules(D,L) interface removes all virtual + ** table modules from database connection D except those named on list L. + ** The L parameter must be either NULL or a pointer to an array of pointers + ** to strings where the array is terminated by a single NULL pointer. + ** ^If the L parameter is NULL, then all virtual table modules are removed. + ** + ** See also: [sqlite3_create_module()] + */ + SQLITE_API int sqlite3_drop_modules(sqlite3* db, /* Remove modules from this connection */ + const char** azKeep /* Except, do not remove the ones named here */ + ); + + /* + ** CAPI3REF: Virtual Table Instance Object + ** KEYWORDS: sqlite3_vtab + ** + ** Every [virtual table module] implementation uses a subclass + ** of this object to describe a particular instance + ** of the [virtual table]. Each subclass will + ** be tailored to the specific needs of the module implementation. + ** The purpose of this superclass is to define certain fields that are + ** common to all module implementations. + ** + ** ^Virtual tables methods can set an error message by assigning a + ** string obtained from [sqlite3_mprintf()] to zErrMsg. The method should + ** take care that any prior string is freed by a call to [sqlite3_free()] + ** prior to assigning a new string to zErrMsg. ^After the error message + ** is delivered up to the client application, the string will be automatically + ** freed by sqlite3_free() and the zErrMsg field will be zeroed. + */ + struct sqlite3_vtab + { + const sqlite3_module* pModule; /* The module for this virtual table */ + int nRef; /* Number of open cursors */ + char* zErrMsg; /* Error message from sqlite3_mprintf() */ + /* Virtual table implementations will typically add additional fields */ + }; + + /* + ** CAPI3REF: Virtual Table Cursor Object + ** KEYWORDS: sqlite3_vtab_cursor {virtual table cursor} + ** + ** Every [virtual table module] implementation uses a subclass of the + ** following structure to describe cursors that point into the + ** [virtual table] and are used + ** to loop through the virtual table. Cursors are created using the + ** [sqlite3_module.xOpen | xOpen] method of the module and are destroyed + ** by the [sqlite3_module.xClose | xClose] method. Cursors are used + ** by the [xFilter], [xNext], [xEof], [xColumn], and [xRowid] methods + ** of the module. Each module implementation will define + ** the content of a cursor structure to suit its own needs. + ** + ** This superclass exists in order to define fields of the cursor that + ** are common to all implementations. + */ + struct sqlite3_vtab_cursor + { + sqlite3_vtab* pVtab; /* Virtual table of this cursor */ + /* Virtual table implementations will typically add additional fields */ + }; + + /* + ** CAPI3REF: Declare The Schema Of A Virtual Table + ** + ** ^The [xCreate] and [xConnect] methods of a + ** [virtual table module] call this interface + ** to declare the format (the names and datatypes of the columns) of + ** the virtual tables they implement. + */ + SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char* zSQL); + + /* + ** CAPI3REF: Overload A Function For A Virtual Table + ** METHOD: sqlite3 + ** + ** ^(Virtual tables can provide alternative implementations of functions + ** using the [xFindFunction] method of the [virtual table module]. + ** But global versions of those functions + ** must exist in order to be overloaded.)^ + ** + ** ^(This API makes sure a global version of a function with a particular + ** name and number of parameters exists. If no such function exists + ** before this API is called, a new function is created.)^ ^The implementation + ** of the new function always causes an exception to be thrown. So + ** the new function is not good for anything by itself. Its only + ** purpose is to be a placeholder function that can be overloaded + ** by a [virtual table]. + */ + 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} + ** + ** An instance of this object represents an open BLOB on which + ** [sqlite3_blob_open | incremental BLOB I/O] can be performed. + ** ^Objects of this type are created by [sqlite3_blob_open()] + ** and destroyed by [sqlite3_blob_close()]. + ** ^The [sqlite3_blob_read()] and [sqlite3_blob_write()] interfaces + ** can be used to read or write small subsections of the BLOB. + ** ^The [sqlite3_blob_bytes()] interface returns the size of the BLOB in bytes. + */ + typedef struct sqlite3_blob sqlite3_blob; + + /* + ** CAPI3REF: Open A BLOB For Incremental I/O + ** METHOD: sqlite3 + ** CONSTRUCTOR: sqlite3_blob + ** + ** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located + ** in row iRow, column zColumn, table zTable in database zDb; + ** in other words, the same BLOB that would be selected by: + ** + **
    +    **     SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow;
    +    ** 
    )^ + ** + ** ^(Parameter zDb is not the filename that contains the database, but + ** rather the symbolic name of the database. For attached databases, this is + ** the name that appears after the AS keyword in the [ATTACH] statement. + ** For the main database file, the database name is "main". For TEMP + ** tables, the database name is "temp".)^ + ** + ** ^If the flags parameter is non-zero, then the BLOB is opened for read + ** and write access. ^If the flags parameter is zero, the BLOB is opened for + ** read-only access. + ** + ** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is stored + ** 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. + ** + ** This function fails with SQLITE_ERROR if any of the following are true: + **
      + **
    • ^(Database zDb does not exist)^, + **
    • ^(Table zTable does not exist within database zDb)^, + **
    • ^(Table zTable is a WITHOUT ROWID table)^, + **
    • ^(Column zColumn does not exist)^, + **
    • ^(Row iRow is not present in the table)^, + **
    • ^(The specified column of row iRow contains a value that is not + ** a TEXT or BLOB value)^, + **
    • ^(Column zColumn is part of an index, PRIMARY KEY or UNIQUE + ** constraint and the blob is being opened for read/write access)^, + **
    • ^([foreign key constraints | Foreign key constraints] are enabled, + ** column zColumn is part of a [child key] definition and the blob is + ** being opened for read/write access)^. + **
    + ** + ** ^Unless it returns SQLITE_MISUSE, this function sets the + ** [database connection] error code and message accessible via + ** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions. + ** + ** A BLOB referenced by sqlite3_blob_open() may be read using the + ** [sqlite3_blob_read()] interface and modified by using + ** [sqlite3_blob_write()]. The [BLOB handle] can be moved to a + ** different row of the same table using the [sqlite3_blob_reopen()] + ** interface. However, the column, table, or database of a [BLOB handle] + ** cannot be changed after the [BLOB handle] is opened. + ** + ** ^(If the row that a BLOB handle points to is modified by an + ** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects + ** then the BLOB handle is marked as "expired". + ** This is true if any column of the row is changed, even a column + ** other than the one the BLOB handle is open on.)^ + ** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for + ** an expired BLOB handle fail with a return code of [SQLITE_ABORT]. + ** ^(Changes written into a BLOB prior to the BLOB expiring are not + ** rolled back by the expiration of the BLOB. Such changes will eventually + ** commit if the transaction continues to completion.)^ + ** + ** ^Use the [sqlite3_blob_bytes()] interface to determine the size of + ** the opened blob. ^The size of a blob may not be changed by this + ** interface. Use the [UPDATE] SQL command to change the size of a + ** blob. + ** + ** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces + ** and the built-in [zeroblob] SQL function may be used to create a + ** zero-filled blob to read or write using the incremental-blob interface. + ** + ** To avoid a resource leak, every open [BLOB handle] should eventually + ** be released by a call to [sqlite3_blob_close()]. + ** + ** See also: [sqlite3_blob_close()], + ** [sqlite3_blob_reopen()], [sqlite3_blob_read()], + ** [sqlite3_blob_bytes()], [sqlite3_blob_write()]. + */ + SQLITE_API int sqlite3_blob_open(sqlite3*, + const char* zDb, + const char* zTable, + const char* zColumn, + sqlite3_int64 iRow, + int flags, + sqlite3_blob** ppBlob); + + /* + ** CAPI3REF: Move a BLOB Handle to a New Row + ** METHOD: sqlite3_blob + ** + ** ^This function is used to move an existing [BLOB handle] so that it points + ** to a different row of the same database table. ^The new row is identified + ** by the rowid value passed as the second argument. Only the row can be + ** changed. ^The database, table and column on which the blob handle is open + ** remain the same. Moving an existing [BLOB handle] to a new row is + ** faster than closing the existing handle and opening a new one. + ** + ** ^(The new row must meet the same criteria as for [sqlite3_blob_open()] - + ** it must exist and there must be either a blob or text value stored in + ** the nominated column.)^ ^If the new row is not present in the table, or if + ** it does not contain a blob or text value, or if another error occurs, an + ** SQLite error code is returned and the blob handle is considered aborted. + ** ^All subsequent calls to [sqlite3_blob_read()], [sqlite3_blob_write()] or + ** [sqlite3_blob_reopen()] on an aborted blob handle immediately return + ** SQLITE_ABORT. ^Calling [sqlite3_blob_bytes()] on an aborted blob handle + ** always returns zero. + ** + ** ^This function sets the database handle error code and message. + */ + SQLITE_API int sqlite3_blob_reopen(sqlite3_blob*, sqlite3_int64); + + /* + ** CAPI3REF: Close A BLOB Handle + ** DESTRUCTOR: sqlite3_blob + ** + ** ^This function closes an open [BLOB handle]. ^(The BLOB handle is closed + ** unconditionally. Even if this routine returns an error code, the + ** handle is still closed.)^ + ** + ** ^If the blob handle being closed was opened for read-write access, and if + ** the database is in auto-commit mode and there are no other open read-write + ** blob handles or active write statements, the current transaction is + ** committed. ^If an error occurs while committing the transaction, an error + ** 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 + ** 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 + ** sqlite3_errcode() and sqlite3_errmsg() functions are set before returning. + */ + SQLITE_API int sqlite3_blob_close(sqlite3_blob*); + + /* + ** CAPI3REF: Return The Size Of An Open BLOB + ** METHOD: 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 + ** blob content; they cannot change the size of a blob. + ** + ** This routine only works on a [BLOB handle] which has been created + ** by a prior successful call to [sqlite3_blob_open()] and which has not + ** been closed by [sqlite3_blob_close()]. Passing any other pointer in + ** to this routine results in undefined and probably undesirable behavior. + */ + SQLITE_API int sqlite3_blob_bytes(sqlite3_blob*); + + /* + ** CAPI3REF: Read Data From A BLOB Incrementally + ** METHOD: sqlite3_blob + ** + ** ^(This function is used to read data from an open [BLOB handle] into a + ** caller-supplied buffer. N bytes of data are copied into buffer Z + ** from the open BLOB, starting at offset iOffset.)^ + ** + ** ^If offset iOffset is less than N bytes from the end of the BLOB, + ** [SQLITE_ERROR] is returned and no data is read. ^If N or iOffset is + ** less than zero, [SQLITE_ERROR] is returned and no data is read. + ** ^The size of the blob (and hence the maximum value of N+iOffset) + ** can be determined using the [sqlite3_blob_bytes()] interface. + ** + ** ^An attempt to read from an expired [BLOB handle] fails with an + ** error code of [SQLITE_ABORT]. + ** + ** ^(On success, sqlite3_blob_read() returns SQLITE_OK. + ** Otherwise, an [error code] or an [extended error code] is returned.)^ + ** + ** This routine only works on a [BLOB handle] which has been created + ** by a prior successful call to [sqlite3_blob_open()] and which has not + ** been closed by [sqlite3_blob_close()]. Passing any other pointer in + ** to this routine results in undefined and probably undesirable behavior. + ** + ** See also: [sqlite3_blob_write()]. + */ + SQLITE_API int sqlite3_blob_read(sqlite3_blob*, void* Z, int N, int iOffset); + + /* + ** CAPI3REF: Write Data Into A BLOB Incrementally + ** METHOD: sqlite3_blob + ** + ** ^(This function is used to write data into an open [BLOB handle] from a + ** caller-supplied buffer. N bytes of data are copied from the buffer Z + ** into the open BLOB, starting at offset iOffset.)^ + ** + ** ^(On success, sqlite3_blob_write() returns SQLITE_OK. + ** Otherwise, an [error code] or an [extended error code] is returned.)^ + ** ^Unless SQLITE_MISUSE is returned, this function sets the + ** [database connection] error code and message accessible via + ** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions. + ** + ** ^If the [BLOB handle] passed as the first argument was not opened for + ** writing (the flags parameter to [sqlite3_blob_open()] was zero), + ** this function returns [SQLITE_READONLY]. + ** + ** This function may only modify the contents of the BLOB; it is + ** not possible to increase the size of a BLOB using this API. + ** ^If offset iOffset is less than N bytes from the end of the BLOB, + ** [SQLITE_ERROR] is returned and no data is written. The size of the + ** BLOB (and hence the maximum value of N+iOffset) can be determined + ** using the [sqlite3_blob_bytes()] interface. ^If N or iOffset are less + ** than zero [SQLITE_ERROR] is returned and no data is written. + ** + ** ^An attempt to write to an expired [BLOB handle] fails with an + ** error code of [SQLITE_ABORT]. ^Writes to the BLOB that occurred + ** before the [BLOB handle] expired are not rolled back by the + ** expiration of the handle, though of course those changes might + ** have been overwritten by the statement that expired the BLOB handle + ** or by other independent statements. + ** + ** This routine only works on a [BLOB handle] which has been created + ** by a prior successful call to [sqlite3_blob_open()] and which has not + ** been closed by [sqlite3_blob_close()]. Passing any other pointer in + ** to this routine results in undefined and probably undesirable behavior. + ** + ** See also: [sqlite3_blob_read()]. + */ + SQLITE_API int sqlite3_blob_write(sqlite3_blob*, const void* z, int n, int iOffset); + + /* + ** CAPI3REF: Virtual File System Objects + ** + ** A virtual filesystem (VFS) is an [sqlite3_vfs] object + ** that SQLite uses to interact + ** with the underlying operating system. Most SQLite builds come with a + ** single default VFS that is appropriate for the host computer. + ** New VFSes can be registered and existing VFSes can be unregistered. + ** The following interfaces are provided. + ** + ** ^The sqlite3_vfs_find() interface returns a pointer to a VFS given its name. + ** ^Names are case sensitive. + ** ^Names are zero-terminated UTF-8 strings. + ** ^If there is no match, a NULL pointer is returned. + ** ^If zVfsName is NULL then the default VFS is returned. + ** + ** ^New VFSes are registered with sqlite3_vfs_register(). + ** ^Each new VFS becomes the default VFS if the makeDflt flag is set. + ** ^The same VFS can be registered multiple times without injury. + ** ^To make an existing VFS into the default VFS, register it again + ** with the makeDflt flag set. If two different VFSes with the + ** same name are registered, the behavior is undefined. If a + ** VFS is registered with a name that is NULL or an empty string, + ** then the behavior is undefined. + ** + ** ^Unregister a VFS with the sqlite3_vfs_unregister() interface. + ** ^(If the default VFS is unregistered, another VFS is chosen as + ** the default. The choice for the new VFS is arbitrary.)^ + */ + SQLITE_API sqlite3_vfs* sqlite3_vfs_find(const char* zVfsName); + SQLITE_API int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); + SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); + + /* + ** CAPI3REF: Mutexes + ** + ** The SQLite core uses these routines for thread + ** synchronization. Though they are intended for internal + ** use by SQLite, code that links against SQLite is + ** permitted to use any of these routines. + ** + ** The SQLite source code contains multiple implementations + ** of these mutex routines. An appropriate implementation + ** is selected automatically at compile-time. The following + ** implementations are available in the SQLite core: + ** + **
      + **
    • SQLITE_MUTEX_PTHREADS + **
    • SQLITE_MUTEX_W32 + **
    • SQLITE_MUTEX_NOOP + **
    + ** + ** The SQLITE_MUTEX_NOOP implementation is a set of routines + ** that does no real locking and is appropriate for use in + ** a single-threaded application. The SQLITE_MUTEX_PTHREADS and + ** SQLITE_MUTEX_W32 implementations are appropriate for use on Unix + ** and Windows. + ** + ** If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor + ** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex + ** implementation is included with the library. In this case the + ** application must supply a custom mutex implementation using the + ** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function + ** before calling sqlite3_initialize() or any other public sqlite3_ + ** function that calls sqlite3_initialize(). + ** + ** ^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 + ** integer constants: + ** + **
      + **
    • SQLITE_MUTEX_FAST + **
    • SQLITE_MUTEX_RECURSIVE + **
    • SQLITE_MUTEX_STATIC_MAIN + **
    • SQLITE_MUTEX_STATIC_MEM + **
    • SQLITE_MUTEX_STATIC_OPEN + **
    • SQLITE_MUTEX_STATIC_PRNG + **
    • SQLITE_MUTEX_STATIC_LRU + **
    • SQLITE_MUTEX_STATIC_PMEM + **
    • SQLITE_MUTEX_STATIC_APP1 + **
    • SQLITE_MUTEX_STATIC_APP2 + **
    • SQLITE_MUTEX_STATIC_APP3 + **
    • SQLITE_MUTEX_STATIC_VFS1 + **
    • SQLITE_MUTEX_STATIC_VFS2 + **
    • SQLITE_MUTEX_STATIC_VFS3 + **
    + ** + ** ^The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) + ** cause sqlite3_mutex_alloc() to create + ** a new mutex. ^The new mutex is recursive when SQLITE_MUTEX_RECURSIVE + ** is used but not necessarily so when SQLITE_MUTEX_FAST is used. + ** The mutex implementation does not need to make a distinction + ** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does + ** not want to. SQLite will only request a recursive mutex in + ** cases where it really needs one. If a faster non-recursive mutex + ** implementation is available on the host platform, the mutex subsystem + ** might return such a mutex in response to SQLITE_MUTEX_FAST. + ** + ** ^The other allowed parameters to sqlite3_mutex_alloc() (anything other + ** than SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) each return + ** a pointer to a static preexisting mutex. ^Nine static mutexes are + ** used by the current version of SQLite. Future versions of SQLite + ** may add additional static mutexes. Static mutexes are for internal + ** use by SQLite only. Applications that use SQLite mutexes should + ** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or + ** SQLITE_MUTEX_RECURSIVE. + ** + ** ^Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST + ** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc() + ** returns a different mutex on every call. ^For the static + ** mutex types, the same mutex is returned on every call that has + ** the same type number. + ** + ** ^The sqlite3_mutex_free() routine deallocates a previously + ** allocated dynamic mutex. Attempting to deallocate a static + ** mutex results in undefined behavior. + ** + ** ^The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt + ** to enter a mutex. ^If another thread is already within the mutex, + ** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return + ** SQLITE_BUSY. ^The sqlite3_mutex_try() interface returns [SQLITE_OK] + ** upon successful entry. ^(Mutexes created using + ** SQLITE_MUTEX_RECURSIVE can be entered multiple times by the same thread. + ** In such cases, the + ** mutex must be exited an equal number of times before another thread + ** can enter.)^ If the same thread tries to enter any mutex other + ** than an SQLITE_MUTEX_RECURSIVE more than once, the behavior is undefined. + ** + ** ^(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.)^ + ** + ** ^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. + ** + ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. + */ + SQLITE_API sqlite3_mutex* sqlite3_mutex_alloc(int); + SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); + SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex*); + SQLITE_API int sqlite3_mutex_try(sqlite3_mutex*); + SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*); + + /* + ** CAPI3REF: Mutex Methods Object + ** + ** An instance of this structure defines the low-level routines + ** used to allocate and use mutexes. + ** + ** Usually, the default mutex implementations provided by SQLite are + ** sufficient, however the application has the option of substituting a custom + ** implementation for specialized deployments or systems for which SQLite + ** does not provide a suitable implementation. In this case, the application + ** creates and populates an instance of this structure to pass + ** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option. + ** Additionally, an instance of this structure can be used as an + ** output variable when querying the system for the current mutex + ** implementation, using the [SQLITE_CONFIG_GETMUTEX] option. + ** + ** ^The xMutexInit method defined by this structure is invoked as + ** part of system initialization by the sqlite3_initialize() function. + ** ^The xMutexInit routine is called by SQLite exactly once for each + ** effective call to [sqlite3_initialize()]. + ** + ** ^The xMutexEnd method defined by this structure is invoked as + ** part of system shutdown by the sqlite3_shutdown() function. The + ** implementation of this method is expected to release all outstanding + ** resources obtained by the mutex methods implementation, especially + ** those obtained by the xMutexInit method. ^The xMutexEnd() + ** interface is invoked exactly once for each call to [sqlite3_shutdown()]. + ** + ** ^(The remaining seven methods defined by this structure (xMutexAlloc, + ** xMutexFree, xMutexEnter, xMutexTry, xMutexLeave, xMutexHeld and + ** xMutexNotheld) implement the following interfaces (respectively): + ** + **
      + **
    • [sqlite3_mutex_alloc()]
    • + **
    • [sqlite3_mutex_free()]
    • + **
    • [sqlite3_mutex_enter()]
    • + **
    • [sqlite3_mutex_try()]
    • + **
    • [sqlite3_mutex_leave()]
    • + **
    • [sqlite3_mutex_held()]
    • + **
    • [sqlite3_mutex_notheld()]
    • + **
    )^ + ** + ** The only difference is that the public sqlite3_XXX functions enumerated + ** above silently ignore any invocations that pass a NULL pointer instead + ** of a valid mutex handle. The implementations of the methods defined + ** by this structure are not required to handle this case. The results + ** of passing a NULL pointer instead of a valid mutex handle are undefined + ** (i.e. it is acceptable to provide an implementation that segfaults if + ** it is passed a NULL pointer). + ** + ** The xMutexInit() method must be threadsafe. It must be harmless to + ** invoke xMutexInit() multiple times within the same process and without + ** intervening calls to xMutexEnd(). Second and subsequent calls to + ** xMutexInit() must be no-ops. + ** + ** xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()] + ** and its associates). Similarly, xMutexAlloc() must not use SQLite memory + ** allocation for a static mutex. ^However xMutexAlloc() may use SQLite + ** memory allocation for a fast or recursive mutex. + ** + ** ^SQLite will invoke the xMutexEnd() method when [sqlite3_shutdown()] is + ** called, but only if the prior call to xMutexInit returned SQLITE_OK. + ** If xMutexInit fails in any way, it is expected to clean up after itself + ** prior to returning. + */ + typedef struct sqlite3_mutex_methods sqlite3_mutex_methods; + struct sqlite3_mutex_methods + { + int (*xMutexInit)(void); + int (*xMutexEnd)(void); + sqlite3_mutex* (*xMutexAlloc)(int); + void (*xMutexFree)(sqlite3_mutex*); + void (*xMutexEnter)(sqlite3_mutex*); + int (*xMutexTry)(sqlite3_mutex*); + void (*xMutexLeave)(sqlite3_mutex*); + int (*xMutexHeld)(sqlite3_mutex*); + int (*xMutexNotheld)(sqlite3_mutex*); + }; /* ** CAPI3REF: Mutex Verification Routines @@ -7564,8 +7782,8 @@ ** interface should also return 1 when given a NULL pointer. */ #ifndef NDEBUG -SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*); -SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); + SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*); + SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); #endif /* @@ -7578,100 +7796,99 @@ ** next. Applications that override the built-in mutex logic must be ** prepared to accommodate additional static mutexes. */ -#define SQLITE_MUTEX_FAST 0 -#define SQLITE_MUTEX_RECURSIVE 1 -#define SQLITE_MUTEX_STATIC_MAIN 2 -#define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */ -#define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */ -#define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */ -#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_randomness() */ -#define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */ -#define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */ -#define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */ -#define SQLITE_MUTEX_STATIC_APP1 8 /* For use by application */ -#define SQLITE_MUTEX_STATIC_APP2 9 /* For use by application */ -#define SQLITE_MUTEX_STATIC_APP3 10 /* For use by application */ -#define SQLITE_MUTEX_STATIC_VFS1 11 /* For use by built-in VFS */ -#define SQLITE_MUTEX_STATIC_VFS2 12 /* For use by extension VFS */ -#define SQLITE_MUTEX_STATIC_VFS3 13 /* For use by application VFS */ +#define SQLITE_MUTEX_FAST 0 +#define SQLITE_MUTEX_RECURSIVE 1 +#define SQLITE_MUTEX_STATIC_MAIN 2 +#define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */ +#define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */ +#define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */ +#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_randomness() */ +#define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */ +#define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */ +#define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */ +#define SQLITE_MUTEX_STATIC_APP1 8 /* For use by application */ +#define SQLITE_MUTEX_STATIC_APP2 9 /* For use by application */ +#define SQLITE_MUTEX_STATIC_APP3 10 /* For use by application */ +#define SQLITE_MUTEX_STATIC_VFS1 11 /* For use by built-in VFS */ +#define SQLITE_MUTEX_STATIC_VFS2 12 /* For use by extension VFS */ +#define SQLITE_MUTEX_STATIC_VFS3 13 /* For use by application VFS */ /* Legacy compatibility: */ -#define SQLITE_MUTEX_STATIC_MASTER 2 - - -/* -** CAPI3REF: Retrieve the mutex for a database connection -** METHOD: sqlite3 -** -** ^This interface returns a pointer 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 -** routine returns a NULL pointer. -*/ -SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); +#define SQLITE_MUTEX_STATIC_MASTER 2 -/* -** CAPI3REF: Low-Level Control Of Database Files -** METHOD: sqlite3 -** KEYWORDS: {file control} -** -** ^The [sqlite3_file_control()] interface makes a direct call to the -** xFileControl method for the [sqlite3_io_methods] object associated -** with a particular database identified by the second argument. ^The -** name of the database is "main" for the main database or "temp" for the -** TEMP database, or the name that appears after the AS keyword for -** databases that are added using the [ATTACH] SQL command. -** ^A NULL pointer can be used in place of "main" to refer to the -** main database file. -** ^The third and fourth parameters to this routine -** are passed directly through to the second and third parameters of -** the xFileControl method. ^The return value of the xFileControl -** method becomes the return value of this routine. -** -** A few opcodes for [sqlite3_file_control()] are handled directly -** by the SQLite core and never invoke the -** sqlite3_io_methods.xFileControl method. -** ^The [SQLITE_FCNTL_FILE_POINTER] value for the op parameter causes -** a pointer to the underlying [sqlite3_file] object to be written into -** the space pointed to by the 4th parameter. The -** [SQLITE_FCNTL_JOURNAL_POINTER] works similarly except that it returns -** the [sqlite3_file] object associated with the journal file instead of -** the main database. The [SQLITE_FCNTL_VFS_POINTER] opcode returns -** a pointer to the underlying [sqlite3_vfs] object for the file. -** The [SQLITE_FCNTL_DATA_VERSION] returns the data version counter -** from the pager. -** -** ^If the second parameter (zDbName) does not match the name of any -** open database file, then SQLITE_ERROR is returned. ^This error -** code is not remembered and will not be recalled by [sqlite3_errcode()] -** or [sqlite3_errmsg()]. The underlying xFileControl method might -** also return SQLITE_ERROR. There is no way to distinguish between -** an incorrect zDbName and an SQLITE_ERROR return from the underlying -** xFileControl method. -** -** See also: [file control opcodes] -*/ -SQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); - -/* -** CAPI3REF: Testing Interface -** -** ^The sqlite3_test_control() interface is used to read out internal -** state of SQLite and to inject faults into SQLite for testing -** purposes. ^The first parameter is an operation code that determines -** the number, meaning, and operation of all subsequent parameters. -** -** This interface is not for use by applications. It exists solely -** for verifying the correct operation of the SQLite library. Depending -** on how the SQLite library is compiled, this interface might not exist. -** -** The details of the operation codes, their meanings, the parameters -** they take, and what they do are all subject to change without notice. -** Unlike most of the SQLite API, this function is not guaranteed to -** operate consistently from one release to the next. -*/ -SQLITE_API int sqlite3_test_control(int op, ...); + /* + ** CAPI3REF: Retrieve the mutex for a database connection + ** METHOD: sqlite3 + ** + ** ^This interface returns a pointer 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 + ** routine returns a NULL pointer. + */ + SQLITE_API sqlite3_mutex* sqlite3_db_mutex(sqlite3*); + + /* + ** CAPI3REF: Low-Level Control Of Database Files + ** METHOD: sqlite3 + ** KEYWORDS: {file control} + ** + ** ^The [sqlite3_file_control()] interface makes a direct call to the + ** xFileControl method for the [sqlite3_io_methods] object associated + ** with a particular database identified by the second argument. ^The + ** name of the database is "main" for the main database or "temp" for the + ** TEMP database, or the name that appears after the AS keyword for + ** databases that are added using the [ATTACH] SQL command. + ** ^A NULL pointer can be used in place of "main" to refer to the + ** main database file. + ** ^The third and fourth parameters to this routine + ** are passed directly through to the second and third parameters of + ** the xFileControl method. ^The return value of the xFileControl + ** method becomes the return value of this routine. + ** + ** A few opcodes for [sqlite3_file_control()] are handled directly + ** by the SQLite core and never invoke the + ** sqlite3_io_methods.xFileControl method. + ** ^The [SQLITE_FCNTL_FILE_POINTER] value for the op parameter causes + ** a pointer to the underlying [sqlite3_file] object to be written into + ** the space pointed to by the 4th parameter. The + ** [SQLITE_FCNTL_JOURNAL_POINTER] works similarly except that it returns + ** the [sqlite3_file] object associated with the journal file instead of + ** the main database. The [SQLITE_FCNTL_VFS_POINTER] opcode returns + ** a pointer to the underlying [sqlite3_vfs] object for the file. + ** The [SQLITE_FCNTL_DATA_VERSION] returns the data version counter + ** from the pager. + ** + ** ^If the second parameter (zDbName) does not match the name of any + ** open database file, then SQLITE_ERROR is returned. ^This error + ** code is not remembered and will not be recalled by [sqlite3_errcode()] + ** or [sqlite3_errmsg()]. The underlying xFileControl method might + ** also return SQLITE_ERROR. There is no way to distinguish between + ** an incorrect zDbName and an SQLITE_ERROR return from the underlying + ** xFileControl method. + ** + ** See also: [file control opcodes] + */ + SQLITE_API int sqlite3_file_control(sqlite3*, const char* zDbName, int op, void*); + + /* + ** CAPI3REF: Testing Interface + ** + ** ^The sqlite3_test_control() interface is used to read out internal + ** state of SQLite and to inject faults into SQLite for testing + ** purposes. ^The first parameter is an operation code that determines + ** the number, meaning, and operation of all subsequent parameters. + ** + ** This interface is not for use by applications. It exists solely + ** for verifying the correct operation of the SQLite library. Depending + ** on how the SQLite library is compiled, this interface might not exist. + ** + ** The details of the operation codes, their meanings, the parameters + ** they take, and what they do are all subject to change without notice. + ** Unlike most of the SQLite API, this function is not guaranteed to + ** operate consistently from one release to the next. + */ + SQLITE_API int sqlite3_test_control(int op, ...); /* ** CAPI3REF: Testing Interface Operation Codes @@ -7684,253 +7901,251 @@ ** Applications should not use any of these parameters or the ** [sqlite3_test_control()] interface. */ -#define SQLITE_TESTCTRL_FIRST 5 -#define SQLITE_TESTCTRL_PRNG_SAVE 5 -#define SQLITE_TESTCTRL_PRNG_RESTORE 6 -#define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ -#define SQLITE_TESTCTRL_BITVEC_TEST 8 -#define SQLITE_TESTCTRL_FAULT_INSTALL 9 -#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 -#define SQLITE_TESTCTRL_PENDING_BYTE 11 -#define SQLITE_TESTCTRL_ASSERT 12 -#define SQLITE_TESTCTRL_ALWAYS 13 -#define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ -#define SQLITE_TESTCTRL_OPTIMIZATIONS 15 -#define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ -#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ -#define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 -#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 -#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ -#define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19 -#define SQLITE_TESTCTRL_NEVER_CORRUPT 20 -#define SQLITE_TESTCTRL_VDBE_COVERAGE 21 -#define SQLITE_TESTCTRL_BYTEORDER 22 -#define SQLITE_TESTCTRL_ISINIT 23 -#define SQLITE_TESTCTRL_SORTER_MMAP 24 -#define SQLITE_TESTCTRL_IMPOSTER 25 -#define SQLITE_TESTCTRL_PARSER_COVERAGE 26 -#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 */ - -/* -** 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 -** 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 -** 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 -** SQLITE_OK if N is within bounds and SQLITE_ERROR if not. If either Z -** or L are NULL or invalid pointers then calls to -** sqlite3_keyword_name(N,Z,L) result in undefined behavior. -** -** The sqlite3_keyword_check(Z,L) interface checks to see whether or not -** the L-byte UTF8 identifier that Z points to is a keyword, returning non-zero -** if it is and zero if not. -** -** The parser used by SQLite is forgiving. It is often possible to use -** a keyword as an identifier as long as such use does not result in a -** parsing ambiguity. For example, the statement -** "CREATE TABLE BEGIN(REPLACE,PRAGMA,END);" is accepted by SQLite, and -** creates a new table named "BEGIN" with three columns named -** "REPLACE", "PRAGMA", and "END". Nevertheless, best practice is to avoid -** using keywords as identifiers. Common techniques used to avoid keyword -** name collisions include: -**
      -**
    • Put all identifier names inside double-quotes. This is the official -** SQL way to escape identifier names. -**
    • Put identifier names inside [...]. This is not standard SQL, -** but it is what SQL Server does and so lots of programmers use this -** technique. -**
    • Begin every identifier with the letter "Z" as no SQL keywords start -** with "Z". -**
    • Include a digit somewhere in every identifier name. -**
    -** -** Note that the number of keywords understood by SQLite can depend on -** compile-time options. For example, "VACUUM" is not a keyword if -** SQLite is compiled with the [-DSQLITE_OMIT_VACUUM] option. Also, -** new keywords may be added to future releases of SQLite. -*/ -SQLITE_API int sqlite3_keyword_count(void); -SQLITE_API int sqlite3_keyword_name(int,const char**,int*); -SQLITE_API int sqlite3_keyword_check(const char*,int); - -/* -** CAPI3REF: Dynamic String Object -** KEYWORDS: {dynamic string} -** -** An instance of the sqlite3_str object contains a dynamically-sized -** string under construction. -** -** The lifecycle of an sqlite3_str object is as follows: -**
      -**
    1. ^The sqlite3_str object is created using [sqlite3_str_new()]. -**
    2. ^Text is appended to the sqlite3_str object using various -** methods, such as [sqlite3_str_appendf()]. -**
    3. ^The sqlite3_str object is destroyed and the string it created -** is returned using the [sqlite3_str_finish()] interface. -**
    -*/ -typedef struct sqlite3_str sqlite3_str; - -/* -** CAPI3REF: Create A New Dynamic String Object -** CONSTRUCTOR: sqlite3_str -** -** ^The [sqlite3_str_new(D)] interface allocates and initializes -** a new [sqlite3_str] object. To avoid memory leaks, the object returned by -** [sqlite3_str_new()] must be freed by a subsequent call to -** [sqlite3_str_finish(X)]. -** -** ^The [sqlite3_str_new(D)] interface always returns a pointer to a -** valid [sqlite3_str] object, though in the event of an out-of-memory -** error the returned object might be a special singleton that will -** silently reject new text, always return SQLITE_NOMEM from -** [sqlite3_str_errcode()], always return 0 for -** [sqlite3_str_length()], and always return NULL from -** [sqlite3_str_finish(X)]. It is always safe to use the value -** returned by [sqlite3_str_new(D)] as the sqlite3_str parameter -** to any of the other [sqlite3_str] methods. -** -** The D parameter to [sqlite3_str_new(D)] may be NULL. If the -** D parameter in [sqlite3_str_new(D)] is not NULL, then the maximum -** length of the string contained in the [sqlite3_str] object will be -** the value set for [sqlite3_limit](D,[SQLITE_LIMIT_LENGTH]) instead -** of [SQLITE_MAX_LENGTH]. -*/ -SQLITE_API sqlite3_str *sqlite3_str_new(sqlite3*); - -/* -** CAPI3REF: Finalize A Dynamic String -** DESTRUCTOR: sqlite3_str -** -** ^The [sqlite3_str_finish(X)] interface destroys the sqlite3_str object X -** and returns a pointer to a memory buffer obtained from [sqlite3_malloc64()] -** that contains the constructed string. The calling application should -** pass the returned value to [sqlite3_free()] to avoid a memory leak. -** ^The [sqlite3_str_finish(X)] interface may return a NULL pointer if any -** errors were encountered during construction of the string. ^The -** [sqlite3_str_finish(X)] interface will also return a NULL pointer if the -** string in [sqlite3_str] object X is zero bytes long. -*/ -SQLITE_API char *sqlite3_str_finish(sqlite3_str*); - -/* -** CAPI3REF: Add Content To A Dynamic String -** METHOD: sqlite3_str -** -** These interfaces add content to an sqlite3_str object previously obtained -** from [sqlite3_str_new()]. -** -** ^The [sqlite3_str_appendf(X,F,...)] and -** [sqlite3_str_vappendf(X,F,V)] interfaces uses the [built-in printf] -** functionality of SQLite to append formatted text onto the end of -** [sqlite3_str] object X. -** -** ^The [sqlite3_str_append(X,S,N)] method appends exactly N bytes from string S -** onto the end of the [sqlite3_str] object X. N must be non-negative. -** S must contain at least N non-zero bytes of content. To append a -** zero-terminated string in its entirety, use the [sqlite3_str_appendall()] -** method instead. -** -** ^The [sqlite3_str_appendall(X,S)] method appends the complete content of -** zero-terminated string S onto the end of [sqlite3_str] object X. -** -** ^The [sqlite3_str_appendchar(X,N,C)] method appends N copies of the -** single-byte character C onto the end of [sqlite3_str] object X. -** ^This method can be used, for example, to add whitespace indentation. -** -** ^The [sqlite3_str_reset(X)] method resets the string under construction -** inside [sqlite3_str] object X back to zero bytes in length. -** -** These methods do not return a result code. ^If an error occurs, that fact -** is recorded in the [sqlite3_str] object and can be recovered by a -** subsequent call to [sqlite3_str_errcode(X)]. -*/ -SQLITE_API void sqlite3_str_appendf(sqlite3_str*, const char *zFormat, ...); -SQLITE_API void sqlite3_str_vappendf(sqlite3_str*, const char *zFormat, va_list); -SQLITE_API void sqlite3_str_append(sqlite3_str*, const char *zIn, int N); -SQLITE_API void sqlite3_str_appendall(sqlite3_str*, const char *zIn); -SQLITE_API void sqlite3_str_appendchar(sqlite3_str*, int N, char C); -SQLITE_API void sqlite3_str_reset(sqlite3_str*); - -/* -** CAPI3REF: Status Of A Dynamic String -** METHOD: sqlite3_str -** -** These interfaces return the current status of an [sqlite3_str] object. -** -** ^If any prior errors have occurred while constructing the dynamic string -** in sqlite3_str X, then the [sqlite3_str_errcode(X)] method will return -** an appropriate error code. ^The [sqlite3_str_errcode(X)] method returns -** [SQLITE_NOMEM] following any out-of-memory error, or -** [SQLITE_TOOBIG] if the size of the dynamic string exceeds -** [SQLITE_MAX_LENGTH], or [SQLITE_OK] if there have been no errors. -** -** ^The [sqlite3_str_length(X)] method returns the current length, in bytes, -** of the dynamic string under construction in [sqlite3_str] object X. -** ^The length returned by [sqlite3_str_length(X)] does not include the -** zero-termination byte. -** -** ^The [sqlite3_str_value(X)] method returns a pointer to the current -** 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_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 -** outside the range of 0 to [sqlite3_str_length(X)] and do not read or -** write any byte after any subsequent sqlite3_str method call. -*/ -SQLITE_API int sqlite3_str_errcode(sqlite3_str*); -SQLITE_API int sqlite3_str_length(sqlite3_str*); -SQLITE_API char *sqlite3_str_value(sqlite3_str*); - -/* -** CAPI3REF: SQLite Runtime Status -** -** ^These interfaces are used to retrieve runtime status information -** about the performance of SQLite, and optionally to reset various -** highwater marks. ^The first argument is an integer code for -** the specific parameter to measure. ^(Recognized integer codes -** are of the form [status parameters | SQLITE_STATUS_...].)^ -** ^The current value of the parameter is returned into *pCurrent. -** ^The highest recorded value is returned in *pHighwater. ^If the -** resetFlag is true, then the highest record value is reset after -** *pHighwater is written. ^(Some parameters do not record the highest -** value. For those parameters -** nothing is written into *pHighwater and the resetFlag is ignored.)^ -** ^(Other parameters record only the highwater mark and not the current -** value. For these latter parameters nothing is written into *pCurrent.)^ -** -** ^The sqlite3_status() and sqlite3_status64() routines return -** SQLITE_OK on success and a non-zero [error code] on failure. -** -** If either the current value or the highwater mark is too large to -** be represented by a 32-bit integer, then the values returned by -** sqlite3_status() are undefined. -** -** See also: [sqlite3_db_status()] -*/ -SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); -SQLITE_API int sqlite3_status64( - int op, - sqlite3_int64 *pCurrent, - sqlite3_int64 *pHighwater, - int resetFlag -); - +#define SQLITE_TESTCTRL_FIRST 5 +#define SQLITE_TESTCTRL_PRNG_SAVE 5 +#define SQLITE_TESTCTRL_PRNG_RESTORE 6 +#define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ +#define SQLITE_TESTCTRL_BITVEC_TEST 8 +#define SQLITE_TESTCTRL_FAULT_INSTALL 9 +#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 +#define SQLITE_TESTCTRL_PENDING_BYTE 11 +#define SQLITE_TESTCTRL_ASSERT 12 +#define SQLITE_TESTCTRL_ALWAYS 13 +#define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ +#define SQLITE_TESTCTRL_OPTIMIZATIONS 15 +#define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ +#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ +#define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 +#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 +#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ +#define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19 +#define SQLITE_TESTCTRL_NEVER_CORRUPT 20 +#define SQLITE_TESTCTRL_VDBE_COVERAGE 21 +#define SQLITE_TESTCTRL_BYTEORDER 22 +#define SQLITE_TESTCTRL_ISINIT 23 +#define SQLITE_TESTCTRL_SORTER_MMAP 24 +#define SQLITE_TESTCTRL_IMPOSTER 25 +#define SQLITE_TESTCTRL_PARSER_COVERAGE 26 +#define SQLITE_TESTCTRL_RESULT_INTREAL 27 +#define SQLITE_TESTCTRL_PRNG_SEED 28 +#define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 +#define SQLITE_TESTCTRL_SEEK_COUNT 30 +#define SQLITE_TESTCTRL_TRACEFLAGS 31 +#define SQLITE_TESTCTRL_TUNE 32 +#define SQLITE_TESTCTRL_LOGEST 33 +#define SQLITE_TESTCTRL_LAST 33 /* 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 + ** 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 + ** 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 + ** SQLITE_OK if N is within bounds and SQLITE_ERROR if not. If either Z + ** or L are NULL or invalid pointers then calls to + ** sqlite3_keyword_name(N,Z,L) result in undefined behavior. + ** + ** The sqlite3_keyword_check(Z,L) interface checks to see whether or not + ** the L-byte UTF8 identifier that Z points to is a keyword, returning non-zero + ** if it is and zero if not. + ** + ** The parser used by SQLite is forgiving. It is often possible to use + ** a keyword as an identifier as long as such use does not result in a + ** parsing ambiguity. For example, the statement + ** "CREATE TABLE BEGIN(REPLACE,PRAGMA,END);" is accepted by SQLite, and + ** creates a new table named "BEGIN" with three columns named + ** "REPLACE", "PRAGMA", and "END". Nevertheless, best practice is to avoid + ** using keywords as identifiers. Common techniques used to avoid keyword + ** name collisions include: + **
      + **
    • Put all identifier names inside double-quotes. This is the official + ** SQL way to escape identifier names. + **
    • Put identifier names inside [...]. This is not standard SQL, + ** but it is what SQL Server does and so lots of programmers use this + ** technique. + **
    • Begin every identifier with the letter "Z" as no SQL keywords start + ** with "Z". + **
    • Include a digit somewhere in every identifier name. + **
    + ** + ** Note that the number of keywords understood by SQLite can depend on + ** compile-time options. For example, "VACUUM" is not a keyword if + ** SQLite is compiled with the [-DSQLITE_OMIT_VACUUM] option. Also, + ** new keywords may be added to future releases of SQLite. + */ + SQLITE_API int sqlite3_keyword_count(void); + SQLITE_API int sqlite3_keyword_name(int, const char**, int*); + SQLITE_API int sqlite3_keyword_check(const char*, int); + + /* + ** CAPI3REF: Dynamic String Object + ** KEYWORDS: {dynamic string} + ** + ** An instance of the sqlite3_str object contains a dynamically-sized + ** string under construction. + ** + ** The lifecycle of an sqlite3_str object is as follows: + **
      + **
    1. ^The sqlite3_str object is created using [sqlite3_str_new()]. + **
    2. ^Text is appended to the sqlite3_str object using various + ** methods, such as [sqlite3_str_appendf()]. + **
    3. ^The sqlite3_str object is destroyed and the string it created + ** is returned using the [sqlite3_str_finish()] interface. + **
    + */ + typedef struct sqlite3_str sqlite3_str; + + /* + ** CAPI3REF: Create A New Dynamic String Object + ** CONSTRUCTOR: sqlite3_str + ** + ** ^The [sqlite3_str_new(D)] interface allocates and initializes + ** a new [sqlite3_str] object. To avoid memory leaks, the object returned by + ** [sqlite3_str_new()] must be freed by a subsequent call to + ** [sqlite3_str_finish(X)]. + ** + ** ^The [sqlite3_str_new(D)] interface always returns a pointer to a + ** valid [sqlite3_str] object, though in the event of an out-of-memory + ** error the returned object might be a special singleton that will + ** silently reject new text, always return SQLITE_NOMEM from + ** [sqlite3_str_errcode()], always return 0 for + ** [sqlite3_str_length()], and always return NULL from + ** [sqlite3_str_finish(X)]. It is always safe to use the value + ** returned by [sqlite3_str_new(D)] as the sqlite3_str parameter + ** to any of the other [sqlite3_str] methods. + ** + ** The D parameter to [sqlite3_str_new(D)] may be NULL. If the + ** D parameter in [sqlite3_str_new(D)] is not NULL, then the maximum + ** length of the string contained in the [sqlite3_str] object will be + ** the value set for [sqlite3_limit](D,[SQLITE_LIMIT_LENGTH]) instead + ** of [SQLITE_MAX_LENGTH]. + */ + SQLITE_API sqlite3_str* sqlite3_str_new(sqlite3*); + + /* + ** CAPI3REF: Finalize A Dynamic String + ** DESTRUCTOR: sqlite3_str + ** + ** ^The [sqlite3_str_finish(X)] interface destroys the sqlite3_str object X + ** and returns a pointer to a memory buffer obtained from [sqlite3_malloc64()] + ** that contains the constructed string. The calling application should + ** pass the returned value to [sqlite3_free()] to avoid a memory leak. + ** ^The [sqlite3_str_finish(X)] interface may return a NULL pointer if any + ** errors were encountered during construction of the string. ^The + ** [sqlite3_str_finish(X)] interface will also return a NULL pointer if the + ** string in [sqlite3_str] object X is zero bytes long. + */ + SQLITE_API char* sqlite3_str_finish(sqlite3_str*); + + /* + ** CAPI3REF: Add Content To A Dynamic String + ** METHOD: sqlite3_str + ** + ** These interfaces add content to an sqlite3_str object previously obtained + ** from [sqlite3_str_new()]. + ** + ** ^The [sqlite3_str_appendf(X,F,...)] and + ** [sqlite3_str_vappendf(X,F,V)] interfaces uses the [built-in printf] + ** functionality of SQLite to append formatted text onto the end of + ** [sqlite3_str] object X. + ** + ** ^The [sqlite3_str_append(X,S,N)] method appends exactly N bytes from string S + ** onto the end of the [sqlite3_str] object X. N must be non-negative. + ** S must contain at least N non-zero bytes of content. To append a + ** zero-terminated string in its entirety, use the [sqlite3_str_appendall()] + ** method instead. + ** + ** ^The [sqlite3_str_appendall(X,S)] method appends the complete content of + ** zero-terminated string S onto the end of [sqlite3_str] object X. + ** + ** ^The [sqlite3_str_appendchar(X,N,C)] method appends N copies of the + ** single-byte character C onto the end of [sqlite3_str] object X. + ** ^This method can be used, for example, to add whitespace indentation. + ** + ** ^The [sqlite3_str_reset(X)] method resets the string under construction + ** inside [sqlite3_str] object X back to zero bytes in length. + ** + ** These methods do not return a result code. ^If an error occurs, that fact + ** is recorded in the [sqlite3_str] object and can be recovered by a + ** subsequent call to [sqlite3_str_errcode(X)]. + */ + SQLITE_API void sqlite3_str_appendf(sqlite3_str*, const char* zFormat, ...); + SQLITE_API void sqlite3_str_vappendf(sqlite3_str*, const char* zFormat, va_list); + SQLITE_API void sqlite3_str_append(sqlite3_str*, const char* zIn, int N); + SQLITE_API void sqlite3_str_appendall(sqlite3_str*, const char* zIn); + SQLITE_API void sqlite3_str_appendchar(sqlite3_str*, int N, char C); + SQLITE_API void sqlite3_str_reset(sqlite3_str*); + + /* + ** CAPI3REF: Status Of A Dynamic String + ** METHOD: sqlite3_str + ** + ** These interfaces return the current status of an [sqlite3_str] object. + ** + ** ^If any prior errors have occurred while constructing the dynamic string + ** in sqlite3_str X, then the [sqlite3_str_errcode(X)] method will return + ** an appropriate error code. ^The [sqlite3_str_errcode(X)] method returns + ** [SQLITE_NOMEM] following any out-of-memory error, or + ** [SQLITE_TOOBIG] if the size of the dynamic string exceeds + ** [SQLITE_MAX_LENGTH], or [SQLITE_OK] if there have been no errors. + ** + ** ^The [sqlite3_str_length(X)] method returns the current length, in bytes, + ** of the dynamic string under construction in [sqlite3_str] object X. + ** ^The length returned by [sqlite3_str_length(X)] does not include the + ** zero-termination byte. + ** + ** ^The [sqlite3_str_value(X)] method returns a pointer to the current + ** 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_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 + ** outside the range of 0 to [sqlite3_str_length(X)] and do not read or + ** write any byte after any subsequent sqlite3_str method call. + */ + SQLITE_API int sqlite3_str_errcode(sqlite3_str*); + SQLITE_API int sqlite3_str_length(sqlite3_str*); + SQLITE_API char* sqlite3_str_value(sqlite3_str*); + + /* + ** CAPI3REF: SQLite Runtime Status + ** + ** ^These interfaces are used to retrieve runtime status information + ** about the performance of SQLite, and optionally to reset various + ** highwater marks. ^The first argument is an integer code for + ** the specific parameter to measure. ^(Recognized integer codes + ** are of the form [status parameters | SQLITE_STATUS_...].)^ + ** ^The current value of the parameter is returned into *pCurrent. + ** ^The highest recorded value is returned in *pHighwater. ^If the + ** resetFlag is true, then the highest record value is reset after + ** *pHighwater is written. ^(Some parameters do not record the highest + ** value. For those parameters + ** nothing is written into *pHighwater and the resetFlag is ignored.)^ + ** ^(Other parameters record only the highwater mark and not the current + ** value. For these latter parameters nothing is written into *pCurrent.)^ + ** + ** ^The sqlite3_status() and sqlite3_status64() routines return + ** SQLITE_OK on success and a non-zero [error code] on failure. + ** + ** If either the current value or the highwater mark is too large to + ** be represented by a 32-bit integer, then the values returned by + ** sqlite3_status() are undefined. + ** + ** See also: [sqlite3_db_status()] + */ + SQLITE_API int sqlite3_status(int op, int* pCurrent, int* pHighwater, int resetFlag); + SQLITE_API int sqlite3_status64(int op, sqlite3_int64* pCurrent, sqlite3_int64* pHighwater, int resetFlag); /* ** CAPI3REF: Status Parameters @@ -7999,41 +8214,41 @@ ** ** New status parameters may be added from time to time. */ -#define SQLITE_STATUS_MEMORY_USED 0 -#define SQLITE_STATUS_PAGECACHE_USED 1 -#define SQLITE_STATUS_PAGECACHE_OVERFLOW 2 -#define SQLITE_STATUS_SCRATCH_USED 3 /* NOT USED */ -#define SQLITE_STATUS_SCRATCH_OVERFLOW 4 /* NOT USED */ -#define SQLITE_STATUS_MALLOC_SIZE 5 -#define SQLITE_STATUS_PARSER_STACK 6 -#define SQLITE_STATUS_PAGECACHE_SIZE 7 -#define SQLITE_STATUS_SCRATCH_SIZE 8 /* NOT USED */ -#define SQLITE_STATUS_MALLOC_COUNT 9 - -/* -** CAPI3REF: Database Connection Status -** METHOD: sqlite3 -** -** ^This interface is used to retrieve runtime status information -** about a single [database connection]. ^The first argument is the -** database connection object to be interrogated. ^The second argument -** is an integer constant, taken from the set of -** [SQLITE_DBSTATUS options], that -** determines the parameter to interrogate. The set of -** [SQLITE_DBSTATUS options] is likely -** to grow in future releases of SQLite. -** -** ^The current value of the requested parameter is written into *pCur -** and the highest instantaneous value is written into *pHiwtr. ^If -** the resetFlg is true, then the highest instantaneous value is -** reset back down to the current value. -** -** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a -** non-zero [error code] on failure. -** -** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. -*/ -SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); +#define SQLITE_STATUS_MEMORY_USED 0 +#define SQLITE_STATUS_PAGECACHE_USED 1 +#define SQLITE_STATUS_PAGECACHE_OVERFLOW 2 +#define SQLITE_STATUS_SCRATCH_USED 3 /* NOT USED */ +#define SQLITE_STATUS_SCRATCH_OVERFLOW 4 /* NOT USED */ +#define SQLITE_STATUS_MALLOC_SIZE 5 +#define SQLITE_STATUS_PARSER_STACK 6 +#define SQLITE_STATUS_PAGECACHE_SIZE 7 +#define SQLITE_STATUS_SCRATCH_SIZE 8 /* NOT USED */ +#define SQLITE_STATUS_MALLOC_COUNT 9 + + /* + ** CAPI3REF: Database Connection Status + ** METHOD: sqlite3 + ** + ** ^This interface is used to retrieve runtime status information + ** about a single [database connection]. ^The first argument is the + ** database connection object to be interrogated. ^The second argument + ** is an integer constant, taken from the set of + ** [SQLITE_DBSTATUS options], that + ** determines the parameter to interrogate. The set of + ** [SQLITE_DBSTATUS options] is likely + ** to grow in future releases of SQLite. + ** + ** ^The current value of the requested parameter is written into *pCur + ** and the highest instantaneous value is written into *pHiwtr. ^If + ** the resetFlg is true, then the highest instantaneous value is + ** reset back down to the current value. + ** + ** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a + ** non-zero [error code] on failure. + ** + ** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. + */ + SQLITE_API int sqlite3_db_status(sqlite3*, int op, int* pCur, int* pHiwtr, int resetFlg); /* ** CAPI3REF: Status Parameters for database connections @@ -8146,47 +8361,46 @@ ** **
    */ -#define SQLITE_DBSTATUS_LOOKASIDE_USED 0 -#define SQLITE_DBSTATUS_CACHE_USED 1 -#define SQLITE_DBSTATUS_SCHEMA_USED 2 -#define SQLITE_DBSTATUS_STMT_USED 3 -#define SQLITE_DBSTATUS_LOOKASIDE_HIT 4 -#define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE 5 -#define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6 -#define SQLITE_DBSTATUS_CACHE_HIT 7 -#define SQLITE_DBSTATUS_CACHE_MISS 8 -#define SQLITE_DBSTATUS_CACHE_WRITE 9 -#define SQLITE_DBSTATUS_DEFERRED_FKS 10 -#define SQLITE_DBSTATUS_CACHE_USED_SHARED 11 -#define SQLITE_DBSTATUS_CACHE_SPILL 12 -#define SQLITE_DBSTATUS_MAX 12 /* Largest defined DBSTATUS */ - - -/* -** CAPI3REF: Prepared Statement Status -** METHOD: sqlite3_stmt -** -** ^(Each prepared statement maintains various -** [SQLITE_STMTSTATUS counters] that measure the number -** of times it has performed specific operations.)^ These counters can -** be used to monitor the performance characteristics of the prepared -** statements. For example, if the number of table steps greatly exceeds -** the number of table searches or result rows, that would tend to indicate -** that the prepared statement is using a full table scan rather than -** an index. -** -** ^(This interface is used to retrieve and reset counter values from -** a [prepared statement]. The first argument is the prepared statement -** object to be interrogated. The second argument -** is an integer code for a specific [SQLITE_STMTSTATUS counter] -** to be interrogated.)^ -** ^The current value of the requested counter is returned. -** ^If the resetFlg is true, then the counter is reset to zero after this -** interface call returns. -** -** See also: [sqlite3_status()] and [sqlite3_db_status()]. -*/ -SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); +#define SQLITE_DBSTATUS_LOOKASIDE_USED 0 +#define SQLITE_DBSTATUS_CACHE_USED 1 +#define SQLITE_DBSTATUS_SCHEMA_USED 2 +#define SQLITE_DBSTATUS_STMT_USED 3 +#define SQLITE_DBSTATUS_LOOKASIDE_HIT 4 +#define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE 5 +#define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6 +#define SQLITE_DBSTATUS_CACHE_HIT 7 +#define SQLITE_DBSTATUS_CACHE_MISS 8 +#define SQLITE_DBSTATUS_CACHE_WRITE 9 +#define SQLITE_DBSTATUS_DEFERRED_FKS 10 +#define SQLITE_DBSTATUS_CACHE_USED_SHARED 11 +#define SQLITE_DBSTATUS_CACHE_SPILL 12 +#define SQLITE_DBSTATUS_MAX 12 /* Largest defined DBSTATUS */ + + /* + ** CAPI3REF: Prepared Statement Status + ** METHOD: sqlite3_stmt + ** + ** ^(Each prepared statement maintains various + ** [SQLITE_STMTSTATUS counters] that measure the number + ** of times it has performed specific operations.)^ These counters can + ** be used to monitor the performance characteristics of the prepared + ** statements. For example, if the number of table steps greatly exceeds + ** the number of table searches or result rows, that would tend to indicate + ** that the prepared statement is using a full table scan rather than + ** an index. + ** + ** ^(This interface is used to retrieve and reset counter values from + ** a [prepared statement]. The first argument is the prepared statement + ** object to be interrogated. The second argument + ** is an integer code for a specific [SQLITE_STMTSTATUS counter] + ** to be interrogated.)^ + ** ^The current value of the requested counter is returned. + ** ^If the resetFlg is true, then the counter is reset to zero after this + ** interface call returns. + ** + ** See also: [sqlite3_status()] and [sqlite3_db_status()]. + */ + SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op, int resetFlg); /* ** CAPI3REF: Status Parameters for prepared statements @@ -8235,6 +8449,16 @@ ** The counter is incremented on the first [sqlite3_step()] call of each ** 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 ** used to store the prepared statement. ^This value is not actually @@ -8243,838 +8467,834 @@ **
    ** */ -#define SQLITE_STMTSTATUS_FULLSCAN_STEP 1 -#define SQLITE_STMTSTATUS_SORT 2 -#define SQLITE_STMTSTATUS_AUTOINDEX 3 -#define SQLITE_STMTSTATUS_VM_STEP 4 -#define SQLITE_STMTSTATUS_REPREPARE 5 -#define SQLITE_STMTSTATUS_RUN 6 -#define SQLITE_STMTSTATUS_MEMUSED 99 - -/* -** CAPI3REF: Custom Page Cache Object -** -** The sqlite3_pcache type is opaque. It is implemented by -** the pluggable module. The SQLite core has no knowledge of -** its size or internal structure and never deals with the -** sqlite3_pcache object except by holding and passing pointers -** to the object. -** -** See [sqlite3_pcache_methods2] for additional information. -*/ -typedef struct sqlite3_pcache sqlite3_pcache; - -/* -** CAPI3REF: Custom Page Cache Object -** -** The sqlite3_pcache_page object represents a single page in the -** page cache. The page cache will allocate instances of this -** object. Various methods of the page cache use pointers to instances -** of this object as parameters or as their return value. -** -** See [sqlite3_pcache_methods2] for additional information. -*/ -typedef struct sqlite3_pcache_page sqlite3_pcache_page; -struct sqlite3_pcache_page { - void *pBuf; /* The content of the page */ - void *pExtra; /* Extra information associated with the page */ -}; - -/* -** CAPI3REF: Application Defined Page Cache. -** KEYWORDS: {page cache} -** -** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE2], ...) interface can -** register an alternative page cache implementation by passing in an -** instance of the sqlite3_pcache_methods2 structure.)^ -** In many applications, most of the heap memory allocated by -** SQLite is used for the page cache. -** By implementing a -** custom page cache using this API, an application can better control -** the amount of memory consumed by SQLite, the way in which -** that memory is allocated and released, and the policies used to -** determine exactly which parts of a database file are cached and for -** how long. -** -** The alternative page cache mechanism is an -** extreme measure that is only needed by the most demanding applications. -** The built-in page cache is recommended for most uses. -** -** ^(The contents of the sqlite3_pcache_methods2 structure are copied to an -** internal buffer by SQLite within the call to [sqlite3_config]. Hence -** the application may discard the parameter after the call to -** [sqlite3_config()] returns.)^ -** -** [[the xInit() page cache method]] -** ^(The xInit() method is called once for each effective -** call to [sqlite3_initialize()])^ -** (usually only once during the lifetime of the process). ^(The xInit() -** method is passed a copy of the sqlite3_pcache_methods2.pArg value.)^ -** The intent of the xInit() method is to set up global data structures -** required by the custom page cache implementation. -** ^(If the xInit() method is NULL, then the -** built-in default page cache is used instead of the application defined -** page cache.)^ -** -** [[the xShutdown() page cache method]] -** ^The xShutdown() method is called by [sqlite3_shutdown()]. -** It can be used to clean up -** any outstanding resources before process shutdown, if required. -** ^The xShutdown() method may be NULL. -** -** ^SQLite automatically serializes calls to the xInit method, -** so the xInit method need not be threadsafe. ^The -** xShutdown method is only called from [sqlite3_shutdown()] so it does -** not need to be threadsafe either. All other methods must be threadsafe -** in multithreaded applications. -** -** ^SQLite will never invoke xInit() more than once without an intervening -** call to xShutdown(). -** -** [[the xCreate() page cache methods]] -** ^SQLite invokes the xCreate() method to construct a new cache instance. -** 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 -** second parameter szExtra is a number of bytes of extra storage -** associated with each page cache entry. ^The szExtra parameter will -** 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 -** on the SQLite version, the target platform, and how SQLite was compiled. -** ^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; -** 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 -** 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 -** 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 -** value; it is advisory only. -** -** [[the xPagecount() page cache methods]] -** The xPagecount() method must return the number of pages currently -** stored in the cache, both pinned and unpinned. -** -** [[the xFetch() page cache methods]] -** The xFetch() method locates a page in the cache and returns a pointer to -** an sqlite3_pcache_page object associated with that page, or a NULL pointer. -** The pBuf element of the returned sqlite3_pcache_page object will be a -** pointer to a buffer of szPage bytes used to store the content of a -** single database page. The pExtra element of sqlite3_pcache_page will be -** a pointer to the szExtra bytes of extra storage that SQLite has requested -** for each entry in the page cache. -** -** The page to be fetched is determined by the key. ^The minimum key value -** is 1. After it has been retrieved using xFetch, the page is considered -** to be "pinned". -** -** If the requested page is already in the page cache, then the page cache -** 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: -** -** -**
    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. -** Otherwise return NULL. -**
    2 Make every effort to allocate a new page. Only return -** NULL if allocating a new page is effectively impossible. -**
    -** -** ^(SQLite will normally invoke xFetch() with a createFlag of 0 or 1. SQLite -** will only use a createFlag of 2 after a prior call with a createFlag of 1 -** failed.)^ In between the xFetch() calls, SQLite may -** attempt to unpin one or more cache pages by spilling the content of -** pinned pages to disk and synching the operating system disk cache. -** -** [[the xUnpin() page cache method]] -** ^xUnpin() is called by SQLite with a pointer to a currently pinned 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 -** page cache implementation. ^The page cache implementation -** may choose to evict unpinned pages at any time. -** -** The cache must not perform any reference counting. A single -** call to xUnpin() unpins the page regardless of the number of prior calls -** to xFetch(). -** -** [[the xRekey() page cache methods]] -** The xRekey() method is used to change the key value associated with the -** page passed as the second argument. If the cache -** previously contains an entry associated with newKey, it must be -** discarded. ^Any prior cache entry associated with newKey is guaranteed not -** to be pinned. -** -** 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 -** they can be safely discarded. -** -** [[the xDestroy() page cache method]] -** ^The xDestroy() method is used to delete a cache allocated by xCreate(). -** All resources associated with the specified cache should be freed. ^After -** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*] -** handle invalid, and will not use it with any other sqlite3_pcache_methods2 -** functions. -** -** [[the xShrink() page cache method]] -** ^SQLite invokes the xShrink() method when it wants the page cache to -** free up as much of heap memory as possible. The page cache implementation -** is not obligated to free any memory, but well-behaved implementations should -** do their best. -*/ -typedef struct sqlite3_pcache_methods2 sqlite3_pcache_methods2; -struct sqlite3_pcache_methods2 { - int iVersion; - void *pArg; - int (*xInit)(void*); - void (*xShutdown)(void*); - sqlite3_pcache *(*xCreate)(int szPage, int szExtra, int bPurgeable); - void (*xCachesize)(sqlite3_pcache*, int nCachesize); - int (*xPagecount)(sqlite3_pcache*); - sqlite3_pcache_page *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag); - void (*xUnpin)(sqlite3_pcache*, sqlite3_pcache_page*, int discard); - void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*, - unsigned oldKey, unsigned newKey); - void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); - void (*xDestroy)(sqlite3_pcache*); - void (*xShrink)(sqlite3_pcache*); -}; - -/* -** This is the obsolete pcache_methods object that has now been replaced -** by sqlite3_pcache_methods2. This object is not used by SQLite. It is -** retained in the header file for backwards compatibility only. -*/ -typedef struct sqlite3_pcache_methods sqlite3_pcache_methods; -struct sqlite3_pcache_methods { - void *pArg; - int (*xInit)(void*); - void (*xShutdown)(void*); - sqlite3_pcache *(*xCreate)(int szPage, int bPurgeable); - void (*xCachesize)(sqlite3_pcache*, int nCachesize); - int (*xPagecount)(sqlite3_pcache*); - void *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag); - void (*xUnpin)(sqlite3_pcache*, void*, int discard); - void (*xRekey)(sqlite3_pcache*, void*, unsigned oldKey, unsigned newKey); - void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); - void (*xDestroy)(sqlite3_pcache*); -}; - - -/* -** CAPI3REF: Online Backup Object -** -** The sqlite3_backup object records state information about an ongoing -** online backup operation. ^The sqlite3_backup object is created by -** a call to [sqlite3_backup_init()] and is destroyed by a call to -** [sqlite3_backup_finish()]. -** -** See Also: [Using the SQLite Online Backup API] -*/ -typedef struct sqlite3_backup sqlite3_backup; - -/* -** CAPI3REF: Online Backup API. -** -** The backup API copies the content of one database into another. -** It is useful either for creating backups of databases or -** for copying in-memory databases to or from persistent files. -** -** See Also: [Using the SQLite Online Backup API] -** -** ^SQLite holds a write transaction open on the destination database file -** for the duration of the backup operation. -** ^The source database is read-locked only while it is being read; -** it is not locked continuously for the entire backup operation. -** ^Thus, the backup may be performed on a live source database without -** preventing other database connections from -** reading or writing to the source database while the backup is underway. -** -** ^(To perform a backup operation: -**
      -**
    1. sqlite3_backup_init() is called once to initialize the -** backup, -**
    2. sqlite3_backup_step() is called one or more times to transfer -** the data between the two databases, and finally -**
    3. sqlite3_backup_finish() is called to release all resources -** associated with the backup operation. -**
    )^ -** There should be exactly one call to sqlite3_backup_finish() for each -** successful call to sqlite3_backup_init(). -** -** [[sqlite3_backup_init()]] sqlite3_backup_init() -** -** ^The D and N arguments to sqlite3_backup_init(D,N,S,M) are the -** [database connection] associated with the destination database -** and the database name, respectively. -** ^The database name is "main" for the main database, "temp" for the -** temporary database, or the name specified after the AS keyword in -** an [ATTACH] statement for an attached database. -** ^The S and M arguments passed to -** sqlite3_backup_init(D,N,S,M) identify the [database connection] -** and database name of the source database, respectively. -** ^The source and destination [database connections] (parameters S and D) -** must be different or else sqlite3_backup_init(D,N,S,M) will fail with -** an error. -** -** ^A call to sqlite3_backup_init() will fail, returning NULL, if -** there is already a read or read-write transaction open on the -** destination database. -** -** ^If an error occurs within sqlite3_backup_init(D,N,S,M), then NULL is -** returned and an error code and error message are stored in the -** destination [database connection] D. -** ^The error code and message for the failed call to sqlite3_backup_init() -** can be retrieved using the [sqlite3_errcode()], [sqlite3_errmsg()], and/or -** [sqlite3_errmsg16()] functions. -** ^A successful call to sqlite3_backup_init() returns a pointer to an -** [sqlite3_backup] object. -** ^The [sqlite3_backup] object may be used with the sqlite3_backup_step() and -** sqlite3_backup_finish() functions to perform the specified backup -** operation. -** -** [[sqlite3_backup_step()]] sqlite3_backup_step() -** -** ^Function sqlite3_backup_step(B,N) will copy up to N pages between -** the source and destination databases specified by [sqlite3_backup] object B. -** ^If N is negative, all remaining source pages are copied. -** ^If sqlite3_backup_step(B,N) successfully copies N pages and there -** are still more pages to be copied, then the function returns [SQLITE_OK]. -** ^If sqlite3_backup_step(B,N) successfully finishes copying all pages -** from source to destination, then it returns [SQLITE_DONE]. -** ^If an error occurs while running sqlite3_backup_step(B,N), -** then an [error code] is returned. ^As well as [SQLITE_OK] and -** [SQLITE_DONE], a call to sqlite3_backup_step() may return [SQLITE_READONLY], -** [SQLITE_NOMEM], [SQLITE_BUSY], [SQLITE_LOCKED], or an -** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] extended error code. -** -** ^(The sqlite3_backup_step() might return [SQLITE_READONLY] if -**
      -**
    1. the destination database was opened read-only, or -**
    2. the destination database is using write-ahead-log journaling -** and the destination and source page sizes differ, or -**
    3. the destination database is an in-memory database and the -** destination and source page sizes differ. -**
    )^ -** -** ^If sqlite3_backup_step() cannot obtain a required file-system lock, then -** the [sqlite3_busy_handler | busy-handler function] -** is invoked (if one is specified). ^If the -** busy-handler returns non-zero before the lock is available, then -** [SQLITE_BUSY] is returned to the caller. ^In this case the call to -** sqlite3_backup_step() can be retried later. ^If the source -** [database connection] -** is being used to write to the source database when sqlite3_backup_step() -** is called, then [SQLITE_LOCKED] is returned immediately. ^Again, in this -** case the call to sqlite3_backup_step() can be retried later on. ^(If -** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX], [SQLITE_NOMEM], or -** [SQLITE_READONLY] is returned, then -** there is no point in retrying the call to sqlite3_backup_step(). These -** errors are considered fatal.)^ The application must accept -** that the backup operation has failed and pass the backup operation handle -** to the sqlite3_backup_finish() to release associated resources. -** -** ^The first call to sqlite3_backup_step() obtains an exclusive lock -** on the destination file. ^The exclusive lock is not released until either -** sqlite3_backup_finish() is called or the backup operation is complete -** and sqlite3_backup_step() returns [SQLITE_DONE]. ^Every call to -** sqlite3_backup_step() obtains a [shared lock] on the source database that -** lasts for the duration of the sqlite3_backup_step() call. -** ^Because the source database is not locked between calls to -** sqlite3_backup_step(), the source database may be modified mid-way -** through the backup process. ^If the source database is modified by an -** 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 -** by the backup operation, then the backup database is automatically -** updated at the same time. -** -** [[sqlite3_backup_finish()]] sqlite3_backup_finish() -** -** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the -** application wishes to abandon the backup operation, the application -** should destroy the [sqlite3_backup] by passing it to sqlite3_backup_finish(). -** ^The sqlite3_backup_finish() interfaces releases all -** resources associated with the [sqlite3_backup] object. -** ^If sqlite3_backup_step() has not yet returned [SQLITE_DONE], then any -** active write-transaction on the destination database is rolled back. -** The [sqlite3_backup] object is invalid -** 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() 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 -** sqlite3_backup_finish() returns the corresponding [error code]. -** -** ^A return of [SQLITE_BUSY] or [SQLITE_LOCKED] from sqlite3_backup_step() -** is not a permanent error and does not affect the return value of -** sqlite3_backup_finish(). -** -** [[sqlite3_backup_remaining()]] [[sqlite3_backup_pagecount()]] -** sqlite3_backup_remaining() and sqlite3_backup_pagecount() -** -** ^The sqlite3_backup_remaining() routine returns the number of pages still -** to be backed up at the conclusion of the most recent sqlite3_backup_step(). -** ^The sqlite3_backup_pagecount() routine returns the total number of pages -** in the source database at the conclusion of the most recent -** sqlite3_backup_step(). -** ^(The values returned by these functions are only updated by -** sqlite3_backup_step(). If the source database is modified in a way that -** changes the size of the source database or the number of pages remaining, -** those changes are not reflected in the output of sqlite3_backup_pagecount() -** and sqlite3_backup_remaining() until after the next -** sqlite3_backup_step().)^ -** -** Concurrent Usage of Database Handles -** -** ^The source [database connection] may be used by the application for other -** purposes while a backup operation is underway or being initialized. -** ^If SQLite is compiled and configured to support threadsafe database -** connections, then the source database connection may be used concurrently -** from within other threads. -** -** However, the application must guarantee that the destination -** [database connection] is not passed to any other API (by any thread) after -** sqlite3_backup_init() is called and before the corresponding call to -** sqlite3_backup_finish(). SQLite does not currently check to see -** 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. -** -** If running in [shared cache mode], the application must -** guarantee that the shared cache used by the destination database -** is not accessed while the backup is running. In practice this means -** that the application must guarantee that the disk file being -** backed up to is not accessed by any connection within the process, -** not just the specific connection that was passed to sqlite3_backup_init(). -** -** The [sqlite3_backup] object itself is partially threadsafe. Multiple -** threads may safely make multiple concurrent calls to sqlite3_backup_step(). -** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() -** 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. -*/ -SQLITE_API sqlite3_backup *sqlite3_backup_init( - sqlite3 *pDest, /* Destination database handle */ - const char *zDestName, /* Destination database name */ - sqlite3 *pSource, /* Source database handle */ - const char *zSourceName /* Source database name */ -); -SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage); -SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p); -SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p); -SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); - -/* -** CAPI3REF: Unlock Notification -** METHOD: sqlite3 -** -** ^When running in shared-cache mode, a database operation may fail with -** an [SQLITE_LOCKED] error if the required locks on the shared-cache or -** individual tables within the shared-cache cannot be obtained. See -** [SQLite Shared-Cache Mode] for a description of shared-cache locking. -** ^This API may be used to register a callback that SQLite will invoke -** when the connection currently holding the required lock relinquishes it. -** ^This API is only available if the library was compiled with the -** [SQLITE_ENABLE_UNLOCK_NOTIFY] C-preprocessor symbol defined. -** -** See Also: [Using the SQLite Unlock Notification Feature]. -** -** ^Shared-cache locks are released when a database connection concludes -** its current transaction, either by committing it or rolling it back. -** -** ^When a connection (known as the blocked connection) fails to obtain a -** shared-cache lock and SQLITE_LOCKED is returned to the caller, the -** identity of the database connection (the blocking connection) that -** has locked the required resource is stored internally. ^After an -** 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 -** callback is invoked from within the [sqlite3_step] or [sqlite3_close] -** call that concludes the blocking connection's transaction. -** -** ^(If sqlite3_unlock_notify() is called in a multi-threaded application, -** there is a chance that the blocking connection will have already -** concluded its transaction by the time sqlite3_unlock_notify() is invoked. -** If this happens, then the specified callback is invoked immediately, -** from within the call to sqlite3_unlock_notify().)^ -** -** ^If the blocked connection is attempting to obtain a write-lock on a -** shared-cache table, and more than one other connection currently holds -** a read-lock on the same table, then SQLite arbitrarily selects one of -** the other connections to use as the blocking connection. -** -** ^(There may be at most one unlock-notify callback registered by a -** blocked connection. If sqlite3_unlock_notify() is called when the -** 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 may also be canceled by closing the blocked -** connection using [sqlite3_close()]. -** -** The unlock-notify callback is not reentrant. If an application invokes -** any sqlite3_xxx API functions from within an unlock-notify callback, a -** crash or deadlock may be the result. -** -** ^Unless deadlock is detected (see below), sqlite3_unlock_notify() always -** returns SQLITE_OK. -** -** Callback Invocation Details -** -** When an unlock-notify callback is registered, the application provides a -** single void* pointer that is passed to the callback when it is invoked. -** However, the signature of the callback function allows SQLite to pass -** it an array of void* context pointers. The first argument passed to -** an unlock-notify callback is a pointer to an array of void* pointers, -** and the second is the number of entries in the array. -** -** When a blocking connection's transaction is concluded, there may be -** more than one blocked connection that has registered for an unlock-notify -** callback. ^If two or more such blocked connections have specified the -** same callback function, then instead of invoking the callback function -** multiple times, it is invoked once with the set of void* context pointers -** specified by the blocked connections bundled together into an array. -** This gives the application an opportunity to prioritize any actions -** related to the set of unblocked database connections. -** -** Deadlock Detection -** -** Assuming that after registering for an unlock-notify callback a -** database waits for the callback to be issued before taking any further -** action (a reasonable assumption), then using this API may cause the -** application to deadlock. For example, if connection X is waiting for -** connection Y's transaction to be concluded, and similarly connection -** Y is waiting on connection X's transaction, then neither connection -** will proceed and the system may remain deadlocked indefinitely. -** -** To avoid this scenario, the sqlite3_unlock_notify() performs deadlock -** detection. ^If a given call to sqlite3_unlock_notify() would put the -** system in a deadlocked state, then SQLITE_LOCKED is returned and no -** unlock-notify callback is registered. The system is said to be in -** a deadlocked state if connection A has registered for an unlock-notify -** callback on the conclusion of connection B's transaction, and connection -** B has itself registered for an unlock-notify callback when connection -** A's transaction is concluded. ^Indirect deadlock is also detected, so -** the system is also considered to be deadlocked if connection B has -** registered for an unlock-notify callback on the conclusion of connection -** C's transaction, where connection C is waiting on connection A. ^Any -** number of levels of indirection are allowed. -** -** The "DROP TABLE" Exception -** -** When a call to [sqlite3_step()] returns SQLITE_LOCKED, it is almost -** always appropriate to call sqlite3_unlock_notify(). There is however, -** one exception. When executing a "DROP TABLE" or "DROP INDEX" statement, -** SQLite checks if there are any currently executing SELECT statements -** that belong to the same connection. If there are, SQLITE_LOCKED is -** returned. In this case there is no "blocking connection", so invoking -** sqlite3_unlock_notify() results in the unlock-notify callback being -** invoked immediately. If the application then re-attempts the "DROP TABLE" -** or "DROP INDEX" query, an infinite loop might be the result. -** -** One way around this problem is to check the extended error code returned -** by an sqlite3_step() call. ^(If there is a blocking connection, then the -** extended error code is set to SQLITE_LOCKED_SHAREDCACHE. Otherwise, in -** the special "DROP TABLE/INDEX" case, the extended error code is just -** SQLITE_LOCKED.)^ -*/ -SQLITE_API int sqlite3_unlock_notify( - sqlite3 *pBlocked, /* Waiting connection */ - void (*xNotify)(void **apArg, int nArg), /* Callback function to invoke */ - void *pNotifyArg /* Argument to pass to xNotify */ -); - - -/* -** CAPI3REF: String Comparison -** -** ^The [sqlite3_stricmp()] and [sqlite3_strnicmp()] APIs allow applications -** and extensions to compare the contents of two buffers containing UTF-8 -** strings in a case-independent fashion, using the same definition of "case -** independence" that SQLite uses internally when comparing identifiers. -*/ -SQLITE_API int sqlite3_stricmp(const char *, const char *); -SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); - -/* -** CAPI3REF: String Globbing -* -** ^The [sqlite3_strglob(P,X)] interface returns zero if and only if -** string X matches the [GLOB] pattern P. -** ^The definition of [GLOB] pattern matching used in -** [sqlite3_strglob(P,X)] is the same as for the "X GLOB P" operator in the -** SQL dialect understood by SQLite. ^The [sqlite3_strglob(P,X)] function -** is case sensitive. -** -** Note that this routine returns zero on a match and non-zero if the strings -** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()]. -** -** See also: [sqlite3_strlike()]. -*/ -SQLITE_API int sqlite3_strglob(const char *zGlob, const char *zStr); - -/* -** CAPI3REF: String LIKE Matching -* -** ^The [sqlite3_strlike(P,X,E)] interface returns zero if and only if -** string X matches the [LIKE] pattern P with escape character E. -** ^The definition of [LIKE] pattern matching used in -** [sqlite3_strlike(P,X,E)] is the same as for the "X LIKE P ESCAPE E" -** operator in the SQL dialect understood by SQLite. ^For "X LIKE P" without -** the ESCAPE clause, set the E parameter of [sqlite3_strlike(P,X,E)] to 0. -** ^As with the LIKE operator, the [sqlite3_strlike(P,X,E)] function is case -** insensitive - equivalent upper and lower case ASCII characters match -** one another. -** -** ^The [sqlite3_strlike(P,X,E)] function matches Unicode characters, though -** only ASCII characters are case folded. -** -** Note that this routine returns zero on a match and non-zero if the strings -** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()]. -** -** See also: [sqlite3_strglob()]. -*/ -SQLITE_API int sqlite3_strlike(const char *zGlob, const char *zStr, unsigned int cEsc); - -/* -** CAPI3REF: Error Logging Interface -** -** ^The [sqlite3_log()] interface writes a message into the [error log] -** established by the [SQLITE_CONFIG_LOG] option to [sqlite3_config()]. -** ^If logging is enabled, the zFormat string and subsequent arguments are -** used with [sqlite3_snprintf()] to generate the final output string. -** -** The sqlite3_log() interface is intended for use by extensions such as -** virtual tables, collating functions, and SQL functions. While there is -** nothing to prevent an application from calling sqlite3_log(), doing so -** is considered bad form. -** -** The zFormat string must not be NULL. -** -** To avoid deadlocks and other threading problems, the sqlite3_log() routine -** will not use dynamically allocated memory. The log message is stored in -** a fixed-length buffer on the stack. If the log message is longer than -** a few hundred characters, it will be truncated to the length of the -** buffer. -*/ -SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...); - -/* -** CAPI3REF: Write-Ahead Log Commit Hook -** METHOD: sqlite3 -** -** ^The [sqlite3_wal_hook()] function is used to register a callback that -** is invoked each time data is committed to a database in wal mode. -** -** ^(The callback is invoked by SQLite after the commit has taken place and -** the associated write-lock on the database released)^, so the implementation -** may read, write or [checkpoint] the database as required. -** -** ^The first parameter passed to the callback function when it is invoked -** is a copy of the third parameter passed to sqlite3_wal_hook() when -** registering the callback. ^The second is a copy of the database handle. -** ^The third parameter is the name of the database that was written to - -** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter -** is the number of pages currently in the write-ahead log file, -** including those that were just committed. -** -** The callback function should normally return [SQLITE_OK]. ^If an error -** code is returned, that error will propagate back up through the -** SQLite code base to cause the statement that provoked the callback -** to report an error, though the commit will have still occurred. If the -** callback returns [SQLITE_ROW] or [SQLITE_DONE], or if it returns a value -** that does not correspond to any valid SQLite error code, the results -** are undefined. -** -** 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 -** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will -** overwrite any prior [sqlite3_wal_hook()] settings. -*/ -SQLITE_API void *sqlite3_wal_hook( - sqlite3*, - int(*)(void *,sqlite3*,const char*,int), - void* -); - -/* -** CAPI3REF: Configure an auto-checkpoint -** METHOD: sqlite3 -** -** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around -** [sqlite3_wal_hook()] that causes any database on [database connection] D -** to automatically [checkpoint] -** after committing a transaction if there are N or -** more frames in the [write-ahead log] file. ^Passing zero or -** a negative value as the nFrame parameter disables automatic -** checkpoints entirely. -** -** ^The callback registered by this function replaces any existing callback -** registered using [sqlite3_wal_hook()]. ^Likewise, registering a callback -** using [sqlite3_wal_hook()] disables the automatic checkpoint mechanism -** configured by this function. -** -** ^The [wal_autocheckpoint pragma] can be used to invoke this interface -** from SQL. -** -** ^Checkpoints initiated by this mechanism are -** [sqlite3_wal_checkpoint_v2|PASSIVE]. -** -** ^Every new [database connection] defaults to having the auto-checkpoint -** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT] -** pages. The use of this interface -** is only necessary if the default setting is found to be suboptimal -** for a particular application. -*/ -SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); - -/* -** CAPI3REF: Checkpoint a database -** METHOD: sqlite3 -** -** ^(The sqlite3_wal_checkpoint(D,X) is equivalent to -** [sqlite3_wal_checkpoint_v2](D,X,[SQLITE_CHECKPOINT_PASSIVE],0,0).)^ -** -** In brief, sqlite3_wal_checkpoint(D,X) causes the content in the -** [write-ahead log] for database X on [database connection] D to be -** transferred into the database file and for the write-ahead log to -** be reset. See the [checkpointing] documentation for addition -** information. -** -** This interface used to be the only way to cause a checkpoint to -** occur. But then the newer and more powerful [sqlite3_wal_checkpoint_v2()] -** interface was added. This interface is retained for backwards -** compatibility and as a convenience for applications that need to manually -** start a callback but which do not need the full power (and corresponding -** complication) of [sqlite3_wal_checkpoint_v2()]. -*/ -SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); - -/* -** CAPI3REF: Checkpoint a database -** METHOD: sqlite3 -** -** ^(The sqlite3_wal_checkpoint_v2(D,X,M,L,C) interface runs a checkpoint -** operation on database X of [database connection] D in mode M. Status -** information is written back into integers pointed to by L and C.)^ -** ^(The M parameter must be a valid [checkpoint mode]:)^ -** -**
    -**
    SQLITE_CHECKPOINT_PASSIVE
    -** ^Checkpoint as many frames as possible without waiting for any database -** readers or writers to finish, then sync the database file if all frames -** in the log were checkpointed. ^The [busy-handler callback] -** is never invoked in the SQLITE_CHECKPOINT_PASSIVE mode. -** ^On the other hand, passive mode might leave the checkpoint unfinished -** if there are concurrent readers or writers. -** -**
    SQLITE_CHECKPOINT_FULL
    -** ^This mode blocks (it invokes the -** [sqlite3_busy_handler|busy-handler callback]) until there is no -** database writer and all readers are reading from the most recent database -** snapshot. ^It then checkpoints all frames in the log file and syncs the -** database file. ^This mode blocks new database writers while it is pending, -** but new database readers are allowed to continue unimpeded. -** -**
    SQLITE_CHECKPOINT_RESTART
    -** ^This mode works the same way as SQLITE_CHECKPOINT_FULL with the addition -** that after checkpointing the log file it blocks (calls the -** [busy-handler callback]) -** until all readers are reading from the database file only. ^This ensures -** that the next writer will restart the log file from the beginning. -** ^Like SQLITE_CHECKPOINT_FULL, this mode blocks new -** database writer attempts while it is pending, but does not impede readers. -** -**
    SQLITE_CHECKPOINT_TRUNCATE
    -** ^This mode works the same way as SQLITE_CHECKPOINT_RESTART with the -** addition that it also truncates the log file to zero bytes just prior -** to a successful return. -**
    -** -** ^If pnLog is not NULL, then *pnLog is set to the total number of frames in -** the log file or to -1 if the checkpoint could not run because -** of an error or because the database is not in [WAL mode]. ^If pnCkpt is not -** NULL,then *pnCkpt is set to the total number of checkpointed frames in the -** log file (including any that were already checkpointed before the function -** was called) or to -1 if the checkpoint could not run due to an error or -** because the database is not in WAL mode. ^Note that upon successful -** completion of an SQLITE_CHECKPOINT_TRUNCATE, the log file will have been -** truncated to zero bytes and so both *pnLog and *pnCkpt will be set to zero. -** -** ^All calls obtain an exclusive "checkpoint" lock on the database file. ^If -** any other process is running a checkpoint operation at the same time, the -** lock cannot be obtained and SQLITE_BUSY is returned. ^Even if there is a -** busy-handler configured, it will not be invoked in this case. -** -** ^The SQLITE_CHECKPOINT_FULL, RESTART and TRUNCATE modes also obtain the -** exclusive "writer" lock on the database file. ^If the writer lock cannot be -** obtained immediately, and a busy-handler is configured, it is invoked and -** the writer lock retried until either the busy-handler returns 0 or the lock -** is successfully obtained. ^The busy-handler is also invoked while waiting for -** database readers as described above. ^If the busy-handler returns 0 before -** the writer lock is obtained or while waiting for database readers, the -** checkpoint operation proceeds from that point in the same way as -** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible -** without blocking any further. ^SQLITE_BUSY is returned in this case. -** -** ^If parameter zDb is NULL or points to a zero length string, then the -** specified operation is attempted on all WAL databases [attached] to -** [database connection] db. In this case the -** values written to output parameters *pnLog and *pnCkpt are undefined. ^If -** an SQLITE_BUSY error is encountered when processing one or more of the -** attached WAL databases, the operation is still attempted on any remaining -** attached databases and SQLITE_BUSY is returned at the end. ^If any other -** error occurs while processing an attached database, processing is abandoned -** and the error code is returned to the caller immediately. ^If no error -** (SQLITE_BUSY or otherwise) is encountered while processing the attached -** databases, SQLITE_OK is returned. -** -** ^If database zDb is the name of an attached database that is not in WAL -** mode, SQLITE_OK is returned and both *pnLog and *pnCkpt set to -1. ^If -** zDb is not NULL (or a zero length string) and is not the name of any -** attached database, SQLITE_ERROR is returned to the caller. -** -** ^Unless it returns SQLITE_MISUSE, -** the sqlite3_wal_checkpoint_v2() interface -** sets the error information that is queried by -** [sqlite3_errcode()] and [sqlite3_errmsg()]. -** -** ^The [PRAGMA wal_checkpoint] command can be used to invoke this interface -** from SQL. -*/ -SQLITE_API int sqlite3_wal_checkpoint_v2( - sqlite3 *db, /* Database handle */ - const char *zDb, /* Name of attached database (or NULL) */ - int eMode, /* SQLITE_CHECKPOINT_* value */ - int *pnLog, /* OUT: Size of WAL log in frames */ - int *pnCkpt /* OUT: Total number of frames checkpointed */ -); +#define SQLITE_STMTSTATUS_FULLSCAN_STEP 1 +#define SQLITE_STMTSTATUS_SORT 2 +#define SQLITE_STMTSTATUS_AUTOINDEX 3 +#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 + + /* + ** CAPI3REF: Custom Page Cache Object + ** + ** The sqlite3_pcache type is opaque. It is implemented by + ** the pluggable module. The SQLite core has no knowledge of + ** its size or internal structure and never deals with the + ** sqlite3_pcache object except by holding and passing pointers + ** to the object. + ** + ** See [sqlite3_pcache_methods2] for additional information. + */ + typedef struct sqlite3_pcache sqlite3_pcache; + + /* + ** CAPI3REF: Custom Page Cache Object + ** + ** The sqlite3_pcache_page object represents a single page in the + ** page cache. The page cache will allocate instances of this + ** object. Various methods of the page cache use pointers to instances + ** of this object as parameters or as their return value. + ** + ** See [sqlite3_pcache_methods2] for additional information. + */ + typedef struct sqlite3_pcache_page sqlite3_pcache_page; + struct sqlite3_pcache_page + { + void* pBuf; /* The content of the page */ + void* pExtra; /* Extra information associated with the page */ + }; + + /* + ** CAPI3REF: Application Defined Page Cache. + ** KEYWORDS: {page cache} + ** + ** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE2], ...) interface can + ** register an alternative page cache implementation by passing in an + ** instance of the sqlite3_pcache_methods2 structure.)^ + ** In many applications, most of the heap memory allocated by + ** SQLite is used for the page cache. + ** By implementing a + ** custom page cache using this API, an application can better control + ** the amount of memory consumed by SQLite, the way in which + ** that memory is allocated and released, and the policies used to + ** determine exactly which parts of a database file are cached and for + ** how long. + ** + ** The alternative page cache mechanism is an + ** extreme measure that is only needed by the most demanding applications. + ** The built-in page cache is recommended for most uses. + ** + ** ^(The contents of the sqlite3_pcache_methods2 structure are copied to an + ** internal buffer by SQLite within the call to [sqlite3_config]. Hence + ** the application may discard the parameter after the call to + ** [sqlite3_config()] returns.)^ + ** + ** [[the xInit() page cache method]] + ** ^(The xInit() method is called once for each effective + ** call to [sqlite3_initialize()])^ + ** (usually only once during the lifetime of the process). ^(The xInit() + ** method is passed a copy of the sqlite3_pcache_methods2.pArg value.)^ + ** The intent of the xInit() method is to set up global data structures + ** required by the custom page cache implementation. + ** ^(If the xInit() method is NULL, then the + ** built-in default page cache is used instead of the application defined + ** page cache.)^ + ** + ** [[the xShutdown() page cache method]] + ** ^The xShutdown() method is called by [sqlite3_shutdown()]. + ** It can be used to clean up + ** any outstanding resources before process shutdown, if required. + ** ^The xShutdown() method may be NULL. + ** + ** ^SQLite automatically serializes calls to the xInit method, + ** so the xInit method need not be threadsafe. ^The + ** xShutdown method is only called from [sqlite3_shutdown()] so it does + ** not need to be threadsafe either. All other methods must be threadsafe + ** in multithreaded applications. + ** + ** ^SQLite will never invoke xInit() more than once without an intervening + ** call to xShutdown(). + ** + ** [[the xCreate() page cache methods]] + ** ^SQLite invokes the xCreate() method to construct a new cache instance. + ** 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 + ** second parameter szExtra is a number of bytes of extra storage + ** associated with each page cache entry. ^The szExtra parameter will + ** 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 + ** on the SQLite version, the target platform, and how SQLite was compiled. + ** ^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; + ** 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 + ** 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 + ** 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 + ** value; it is advisory only. + ** + ** [[the xPagecount() page cache methods]] + ** The xPagecount() method must return the number of pages currently + ** stored in the cache, both pinned and unpinned. + ** + ** [[the xFetch() page cache methods]] + ** The xFetch() method locates a page in the cache and returns a pointer to + ** an sqlite3_pcache_page object associated with that page, or a NULL pointer. + ** The pBuf element of the returned sqlite3_pcache_page object will be a + ** pointer to a buffer of szPage bytes used to store the content of a + ** single database page. The pExtra element of sqlite3_pcache_page will be + ** a pointer to the szExtra bytes of extra storage that SQLite has requested + ** for each entry in the page cache. + ** + ** The page to be fetched is determined by the key. ^The minimum key value + ** is 1. After it has been retrieved using xFetch, the page is considered + ** to be "pinned". + ** + ** If the requested page is already in the page cache, then the page cache + ** 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: + ** + ** + **
    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. + ** Otherwise return NULL. + **
    2 Make every effort to allocate a new page. Only return + ** NULL if allocating a new page is effectively impossible. + **
    + ** + ** ^(SQLite will normally invoke xFetch() with a createFlag of 0 or 1. SQLite + ** will only use a createFlag of 2 after a prior call with a createFlag of 1 + ** failed.)^ In between the xFetch() calls, SQLite may + ** attempt to unpin one or more cache pages by spilling the content of + ** pinned pages to disk and synching the operating system disk cache. + ** + ** [[the xUnpin() page cache method]] + ** ^xUnpin() is called by SQLite with a pointer to a currently pinned 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 + ** page cache implementation. ^The page cache implementation + ** may choose to evict unpinned pages at any time. + ** + ** The cache must not perform any reference counting. A single + ** call to xUnpin() unpins the page regardless of the number of prior calls + ** to xFetch(). + ** + ** [[the xRekey() page cache methods]] + ** The xRekey() method is used to change the key value associated with the + ** page passed as the second argument. If the cache + ** previously contains an entry associated with newKey, it must be + ** discarded. ^Any prior cache entry associated with newKey is guaranteed not + ** to be pinned. + ** + ** 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 + ** they can be safely discarded. + ** + ** [[the xDestroy() page cache method]] + ** ^The xDestroy() method is used to delete a cache allocated by xCreate(). + ** All resources associated with the specified cache should be freed. ^After + ** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*] + ** handle invalid, and will not use it with any other sqlite3_pcache_methods2 + ** functions. + ** + ** [[the xShrink() page cache method]] + ** ^SQLite invokes the xShrink() method when it wants the page cache to + ** free up as much of heap memory as possible. The page cache implementation + ** is not obligated to free any memory, but well-behaved implementations should + ** do their best. + */ + typedef struct sqlite3_pcache_methods2 sqlite3_pcache_methods2; + struct sqlite3_pcache_methods2 + { + int iVersion; + void* pArg; + int (*xInit)(void*); + void (*xShutdown)(void*); + sqlite3_pcache* (*xCreate)(int szPage, int szExtra, int bPurgeable); + void (*xCachesize)(sqlite3_pcache*, int nCachesize); + int (*xPagecount)(sqlite3_pcache*); + sqlite3_pcache_page* (*xFetch)(sqlite3_pcache*, unsigned key, int createFlag); + void (*xUnpin)(sqlite3_pcache*, sqlite3_pcache_page*, int discard); + void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*, unsigned oldKey, unsigned newKey); + void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); + void (*xDestroy)(sqlite3_pcache*); + void (*xShrink)(sqlite3_pcache*); + }; + + /* + ** This is the obsolete pcache_methods object that has now been replaced + ** by sqlite3_pcache_methods2. This object is not used by SQLite. It is + ** retained in the header file for backwards compatibility only. + */ + typedef struct sqlite3_pcache_methods sqlite3_pcache_methods; + struct sqlite3_pcache_methods + { + void* pArg; + int (*xInit)(void*); + void (*xShutdown)(void*); + sqlite3_pcache* (*xCreate)(int szPage, int bPurgeable); + void (*xCachesize)(sqlite3_pcache*, int nCachesize); + int (*xPagecount)(sqlite3_pcache*); + void* (*xFetch)(sqlite3_pcache*, unsigned key, int createFlag); + void (*xUnpin)(sqlite3_pcache*, void*, int discard); + void (*xRekey)(sqlite3_pcache*, void*, unsigned oldKey, unsigned newKey); + void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); + void (*xDestroy)(sqlite3_pcache*); + }; + + /* + ** CAPI3REF: Online Backup Object + ** + ** The sqlite3_backup object records state information about an ongoing + ** online backup operation. ^The sqlite3_backup object is created by + ** a call to [sqlite3_backup_init()] and is destroyed by a call to + ** [sqlite3_backup_finish()]. + ** + ** See Also: [Using the SQLite Online Backup API] + */ + typedef struct sqlite3_backup sqlite3_backup; + + /* + ** CAPI3REF: Online Backup API. + ** + ** The backup API copies the content of one database into another. + ** It is useful either for creating backups of databases or + ** for copying in-memory databases to or from persistent files. + ** + ** See Also: [Using the SQLite Online Backup API] + ** + ** ^SQLite holds a write transaction open on the destination database file + ** for the duration of the backup operation. + ** ^The source database is read-locked only while it is being read; + ** it is not locked continuously for the entire backup operation. + ** ^Thus, the backup may be performed on a live source database without + ** preventing other database connections from + ** reading or writing to the source database while the backup is underway. + ** + ** ^(To perform a backup operation: + **
      + **
    1. sqlite3_backup_init() is called once to initialize the + ** backup, + **
    2. sqlite3_backup_step() is called one or more times to transfer + ** the data between the two databases, and finally + **
    3. sqlite3_backup_finish() is called to release all resources + ** associated with the backup operation. + **
    )^ + ** There should be exactly one call to sqlite3_backup_finish() for each + ** successful call to sqlite3_backup_init(). + ** + ** [[sqlite3_backup_init()]] sqlite3_backup_init() + ** + ** ^The D and N arguments to sqlite3_backup_init(D,N,S,M) are the + ** [database connection] associated with the destination database + ** and the database name, respectively. + ** ^The database name is "main" for the main database, "temp" for the + ** temporary database, or the name specified after the AS keyword in + ** an [ATTACH] statement for an attached database. + ** ^The S and M arguments passed to + ** sqlite3_backup_init(D,N,S,M) identify the [database connection] + ** and database name of the source database, respectively. + ** ^The source and destination [database connections] (parameters S and D) + ** must be different or else sqlite3_backup_init(D,N,S,M) will fail with + ** an error. + ** + ** ^A call to sqlite3_backup_init() will fail, returning NULL, if + ** there is already a read or read-write transaction open on the + ** destination database. + ** + ** ^If an error occurs within sqlite3_backup_init(D,N,S,M), then NULL is + ** returned and an error code and error message are stored in the + ** destination [database connection] D. + ** ^The error code and message for the failed call to sqlite3_backup_init() + ** can be retrieved using the [sqlite3_errcode()], [sqlite3_errmsg()], and/or + ** [sqlite3_errmsg16()] functions. + ** ^A successful call to sqlite3_backup_init() returns a pointer to an + ** [sqlite3_backup] object. + ** ^The [sqlite3_backup] object may be used with the sqlite3_backup_step() and + ** sqlite3_backup_finish() functions to perform the specified backup + ** operation. + ** + ** [[sqlite3_backup_step()]] sqlite3_backup_step() + ** + ** ^Function sqlite3_backup_step(B,N) will copy up to N pages between + ** the source and destination databases specified by [sqlite3_backup] object B. + ** ^If N is negative, all remaining source pages are copied. + ** ^If sqlite3_backup_step(B,N) successfully copies N pages and there + ** are still more pages to be copied, then the function returns [SQLITE_OK]. + ** ^If sqlite3_backup_step(B,N) successfully finishes copying all pages + ** from source to destination, then it returns [SQLITE_DONE]. + ** ^If an error occurs while running sqlite3_backup_step(B,N), + ** then an [error code] is returned. ^As well as [SQLITE_OK] and + ** [SQLITE_DONE], a call to sqlite3_backup_step() may return [SQLITE_READONLY], + ** [SQLITE_NOMEM], [SQLITE_BUSY], [SQLITE_LOCKED], or an + ** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] extended error code. + ** + ** ^(The sqlite3_backup_step() might return [SQLITE_READONLY] if + **
      + **
    1. the destination database was opened read-only, or + **
    2. the destination database is using write-ahead-log journaling + ** and the destination and source page sizes differ, or + **
    3. the destination database is an in-memory database and the + ** destination and source page sizes differ. + **
    )^ + ** + ** ^If sqlite3_backup_step() cannot obtain a required file-system lock, then + ** the [sqlite3_busy_handler | busy-handler function] + ** is invoked (if one is specified). ^If the + ** busy-handler returns non-zero before the lock is available, then + ** [SQLITE_BUSY] is returned to the caller. ^In this case the call to + ** sqlite3_backup_step() can be retried later. ^If the source + ** [database connection] + ** is being used to write to the source database when sqlite3_backup_step() + ** is called, then [SQLITE_LOCKED] is returned immediately. ^Again, in this + ** case the call to sqlite3_backup_step() can be retried later on. ^(If + ** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX], [SQLITE_NOMEM], or + ** [SQLITE_READONLY] is returned, then + ** there is no point in retrying the call to sqlite3_backup_step(). These + ** errors are considered fatal.)^ The application must accept + ** that the backup operation has failed and pass the backup operation handle + ** to the sqlite3_backup_finish() to release associated resources. + ** + ** ^The first call to sqlite3_backup_step() obtains an exclusive lock + ** on the destination file. ^The exclusive lock is not released until either + ** sqlite3_backup_finish() is called or the backup operation is complete + ** and sqlite3_backup_step() returns [SQLITE_DONE]. ^Every call to + ** sqlite3_backup_step() obtains a [shared lock] on the source database that + ** lasts for the duration of the sqlite3_backup_step() call. + ** ^Because the source database is not locked between calls to + ** sqlite3_backup_step(), the source database may be modified mid-way + ** through the backup process. ^If the source database is modified by an + ** 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 + ** by the backup operation, then the backup database is automatically + ** updated at the same time. + ** + ** [[sqlite3_backup_finish()]] sqlite3_backup_finish() + ** + ** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the + ** application wishes to abandon the backup operation, the application + ** should destroy the [sqlite3_backup] by passing it to sqlite3_backup_finish(). + ** ^The sqlite3_backup_finish() interfaces releases all + ** resources associated with the [sqlite3_backup] object. + ** ^If sqlite3_backup_step() has not yet returned [SQLITE_DONE], then any + ** active write-transaction on the destination database is rolled back. + ** The [sqlite3_backup] object is invalid + ** 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() 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 + ** sqlite3_backup_finish() returns the corresponding [error code]. + ** + ** ^A return of [SQLITE_BUSY] or [SQLITE_LOCKED] from sqlite3_backup_step() + ** is not a permanent error and does not affect the return value of + ** sqlite3_backup_finish(). + ** + ** [[sqlite3_backup_remaining()]] [[sqlite3_backup_pagecount()]] + ** sqlite3_backup_remaining() and sqlite3_backup_pagecount() + ** + ** ^The sqlite3_backup_remaining() routine returns the number of pages still + ** to be backed up at the conclusion of the most recent sqlite3_backup_step(). + ** ^The sqlite3_backup_pagecount() routine returns the total number of pages + ** in the source database at the conclusion of the most recent + ** sqlite3_backup_step(). + ** ^(The values returned by these functions are only updated by + ** sqlite3_backup_step(). If the source database is modified in a way that + ** changes the size of the source database or the number of pages remaining, + ** those changes are not reflected in the output of sqlite3_backup_pagecount() + ** and sqlite3_backup_remaining() until after the next + ** sqlite3_backup_step().)^ + ** + ** Concurrent Usage of Database Handles + ** + ** ^The source [database connection] may be used by the application for other + ** purposes while a backup operation is underway or being initialized. + ** ^If SQLite is compiled and configured to support threadsafe database + ** connections, then the source database connection may be used concurrently + ** from within other threads. + ** + ** However, the application must guarantee that the destination + ** [database connection] is not passed to any other API (by any thread) after + ** sqlite3_backup_init() is called and before the corresponding call to + ** sqlite3_backup_finish(). SQLite does not currently check to see + ** 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. + ** + ** If running in [shared cache mode], the application must + ** guarantee that the shared cache used by the destination database + ** is not accessed while the backup is running. In practice this means + ** that the application must guarantee that the disk file being + ** backed up to is not accessed by any connection within the process, + ** not just the specific connection that was passed to sqlite3_backup_init(). + ** + ** The [sqlite3_backup] object itself is partially threadsafe. Multiple + ** threads may safely make multiple concurrent calls to sqlite3_backup_step(). + ** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() + ** 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. + */ + SQLITE_API sqlite3_backup* sqlite3_backup_init(sqlite3* pDest, /* Destination database handle */ + const char* zDestName, /* Destination database name */ + sqlite3* pSource, /* Source database handle */ + const char* zSourceName /* Source database name */ + ); + SQLITE_API int sqlite3_backup_step(sqlite3_backup* p, int nPage); + SQLITE_API int sqlite3_backup_finish(sqlite3_backup* p); + SQLITE_API int sqlite3_backup_remaining(sqlite3_backup* p); + SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup* p); + + /* + ** CAPI3REF: Unlock Notification + ** METHOD: sqlite3 + ** + ** ^When running in shared-cache mode, a database operation may fail with + ** an [SQLITE_LOCKED] error if the required locks on the shared-cache or + ** individual tables within the shared-cache cannot be obtained. See + ** [SQLite Shared-Cache Mode] for a description of shared-cache locking. + ** ^This API may be used to register a callback that SQLite will invoke + ** when the connection currently holding the required lock relinquishes it. + ** ^This API is only available if the library was compiled with the + ** [SQLITE_ENABLE_UNLOCK_NOTIFY] C-preprocessor symbol defined. + ** + ** See Also: [Using the SQLite Unlock Notification Feature]. + ** + ** ^Shared-cache locks are released when a database connection concludes + ** its current transaction, either by committing it or rolling it back. + ** + ** ^When a connection (known as the blocked connection) fails to obtain a + ** shared-cache lock and SQLITE_LOCKED is returned to the caller, the + ** identity of the database connection (the blocking connection) that + ** has locked the required resource is stored internally. ^After an + ** 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 + ** callback is invoked from within the [sqlite3_step] or [sqlite3_close] + ** call that concludes the blocking connection's transaction. + ** + ** ^(If sqlite3_unlock_notify() is called in a multi-threaded application, + ** there is a chance that the blocking connection will have already + ** concluded its transaction by the time sqlite3_unlock_notify() is invoked. + ** If this happens, then the specified callback is invoked immediately, + ** from within the call to sqlite3_unlock_notify().)^ + ** + ** ^If the blocked connection is attempting to obtain a write-lock on a + ** shared-cache table, and more than one other connection currently holds + ** a read-lock on the same table, then SQLite arbitrarily selects one of + ** the other connections to use as the blocking connection. + ** + ** ^(There may be at most one unlock-notify callback registered by a + ** blocked connection. If sqlite3_unlock_notify() is called when the + ** 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 may also be canceled by closing the blocked + ** connection using [sqlite3_close()]. + ** + ** The unlock-notify callback is not reentrant. If an application invokes + ** any sqlite3_xxx API functions from within an unlock-notify callback, a + ** crash or deadlock may be the result. + ** + ** ^Unless deadlock is detected (see below), sqlite3_unlock_notify() always + ** returns SQLITE_OK. + ** + ** Callback Invocation Details + ** + ** When an unlock-notify callback is registered, the application provides a + ** single void* pointer that is passed to the callback when it is invoked. + ** However, the signature of the callback function allows SQLite to pass + ** it an array of void* context pointers. The first argument passed to + ** an unlock-notify callback is a pointer to an array of void* pointers, + ** and the second is the number of entries in the array. + ** + ** When a blocking connection's transaction is concluded, there may be + ** more than one blocked connection that has registered for an unlock-notify + ** callback. ^If two or more such blocked connections have specified the + ** same callback function, then instead of invoking the callback function + ** multiple times, it is invoked once with the set of void* context pointers + ** specified by the blocked connections bundled together into an array. + ** This gives the application an opportunity to prioritize any actions + ** related to the set of unblocked database connections. + ** + ** Deadlock Detection + ** + ** Assuming that after registering for an unlock-notify callback a + ** database waits for the callback to be issued before taking any further + ** action (a reasonable assumption), then using this API may cause the + ** application to deadlock. For example, if connection X is waiting for + ** connection Y's transaction to be concluded, and similarly connection + ** Y is waiting on connection X's transaction, then neither connection + ** will proceed and the system may remain deadlocked indefinitely. + ** + ** To avoid this scenario, the sqlite3_unlock_notify() performs deadlock + ** detection. ^If a given call to sqlite3_unlock_notify() would put the + ** system in a deadlocked state, then SQLITE_LOCKED is returned and no + ** unlock-notify callback is registered. The system is said to be in + ** a deadlocked state if connection A has registered for an unlock-notify + ** callback on the conclusion of connection B's transaction, and connection + ** B has itself registered for an unlock-notify callback when connection + ** A's transaction is concluded. ^Indirect deadlock is also detected, so + ** the system is also considered to be deadlocked if connection B has + ** registered for an unlock-notify callback on the conclusion of connection + ** C's transaction, where connection C is waiting on connection A. ^Any + ** number of levels of indirection are allowed. + ** + ** The "DROP TABLE" Exception + ** + ** When a call to [sqlite3_step()] returns SQLITE_LOCKED, it is almost + ** always appropriate to call sqlite3_unlock_notify(). There is however, + ** one exception. When executing a "DROP TABLE" or "DROP INDEX" statement, + ** SQLite checks if there are any currently executing SELECT statements + ** that belong to the same connection. If there are, SQLITE_LOCKED is + ** returned. In this case there is no "blocking connection", so invoking + ** sqlite3_unlock_notify() results in the unlock-notify callback being + ** invoked immediately. If the application then re-attempts the "DROP TABLE" + ** or "DROP INDEX" query, an infinite loop might be the result. + ** + ** One way around this problem is to check the extended error code returned + ** by an sqlite3_step() call. ^(If there is a blocking connection, then the + ** extended error code is set to SQLITE_LOCKED_SHAREDCACHE. Otherwise, in + ** the special "DROP TABLE/INDEX" case, the extended error code is just + ** SQLITE_LOCKED.)^ + */ + SQLITE_API int sqlite3_unlock_notify(sqlite3* pBlocked, /* Waiting connection */ + void (*xNotify)(void** apArg, int nArg), /* Callback function to invoke */ + void* pNotifyArg /* Argument to pass to xNotify */ + ); + + /* + ** CAPI3REF: String Comparison + ** + ** ^The [sqlite3_stricmp()] and [sqlite3_strnicmp()] APIs allow applications + ** and extensions to compare the contents of two buffers containing UTF-8 + ** strings in a case-independent fashion, using the same definition of "case + ** independence" that SQLite uses internally when comparing identifiers. + */ + SQLITE_API int sqlite3_stricmp(const char*, const char*); + SQLITE_API int sqlite3_strnicmp(const char*, const char*, int); + + /* + ** CAPI3REF: String Globbing + * + ** ^The [sqlite3_strglob(P,X)] interface returns zero if and only if + ** string X matches the [GLOB] pattern P. + ** ^The definition of [GLOB] pattern matching used in + ** [sqlite3_strglob(P,X)] is the same as for the "X GLOB P" operator in the + ** SQL dialect understood by SQLite. ^The [sqlite3_strglob(P,X)] function + ** is case sensitive. + ** + ** Note that this routine returns zero on a match and non-zero if the strings + ** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()]. + ** + ** See also: [sqlite3_strlike()]. + */ + SQLITE_API int sqlite3_strglob(const char* zGlob, const char* zStr); + + /* + ** CAPI3REF: String LIKE Matching + * + ** ^The [sqlite3_strlike(P,X,E)] interface returns zero if and only if + ** string X matches the [LIKE] pattern P with escape character E. + ** ^The definition of [LIKE] pattern matching used in + ** [sqlite3_strlike(P,X,E)] is the same as for the "X LIKE P ESCAPE E" + ** operator in the SQL dialect understood by SQLite. ^For "X LIKE P" without + ** the ESCAPE clause, set the E parameter of [sqlite3_strlike(P,X,E)] to 0. + ** ^As with the LIKE operator, the [sqlite3_strlike(P,X,E)] function is case + ** insensitive - equivalent upper and lower case ASCII characters match + ** one another. + ** + ** ^The [sqlite3_strlike(P,X,E)] function matches Unicode characters, though + ** only ASCII characters are case folded. + ** + ** Note that this routine returns zero on a match and non-zero if the strings + ** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()]. + ** + ** See also: [sqlite3_strglob()]. + */ + SQLITE_API int sqlite3_strlike(const char* zGlob, const char* zStr, unsigned int cEsc); + + /* + ** CAPI3REF: Error Logging Interface + ** + ** ^The [sqlite3_log()] interface writes a message into the [error log] + ** established by the [SQLITE_CONFIG_LOG] option to [sqlite3_config()]. + ** ^If logging is enabled, the zFormat string and subsequent arguments are + ** used with [sqlite3_snprintf()] to generate the final output string. + ** + ** The sqlite3_log() interface is intended for use by extensions such as + ** virtual tables, collating functions, and SQL functions. While there is + ** nothing to prevent an application from calling sqlite3_log(), doing so + ** is considered bad form. + ** + ** The zFormat string must not be NULL. + ** + ** To avoid deadlocks and other threading problems, the sqlite3_log() routine + ** will not use dynamically allocated memory. The log message is stored in + ** a fixed-length buffer on the stack. If the log message is longer than + ** a few hundred characters, it will be truncated to the length of the + ** buffer. + */ + SQLITE_API void sqlite3_log(int iErrCode, const char* zFormat, ...); + + /* + ** CAPI3REF: Write-Ahead Log Commit Hook + ** METHOD: sqlite3 + ** + ** ^The [sqlite3_wal_hook()] function is used to register a callback that + ** is invoked each time data is committed to a database in wal mode. + ** + ** ^(The callback is invoked by SQLite after the commit has taken place and + ** the associated write-lock on the database released)^, so the implementation + ** may read, write or [checkpoint] the database as required. + ** + ** ^The first parameter passed to the callback function when it is invoked + ** is a copy of the third parameter passed to sqlite3_wal_hook() when + ** registering the callback. ^The second is a copy of the database handle. + ** ^The third parameter is the name of the database that was written to - + ** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter + ** is the number of pages currently in the write-ahead log file, + ** including those that were just committed. + ** + ** The callback function should normally return [SQLITE_OK]. ^If an error + ** code is returned, that error will propagate back up through the + ** SQLite code base to cause the statement that provoked the callback + ** to report an error, though the commit will have still occurred. If the + ** callback returns [SQLITE_ROW] or [SQLITE_DONE], or if it returns a value + ** that does not correspond to any valid SQLite error code, the results + ** are undefined. + ** + ** 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. ^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. + */ + SQLITE_API void* sqlite3_wal_hook(sqlite3*, int (*)(void*, sqlite3*, const char*, int), void*); + + /* + ** CAPI3REF: Configure an auto-checkpoint + ** METHOD: sqlite3 + ** + ** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around + ** [sqlite3_wal_hook()] that causes any database on [database connection] D + ** to automatically [checkpoint] + ** after committing a transaction if there are N or + ** more frames in the [write-ahead log] file. ^Passing zero or + ** a negative value as the nFrame parameter disables automatic + ** checkpoints entirely. + ** + ** ^The callback registered by this function replaces any existing callback + ** registered using [sqlite3_wal_hook()]. ^Likewise, registering a callback + ** using [sqlite3_wal_hook()] disables the automatic checkpoint mechanism + ** configured by this function. + ** + ** ^The [wal_autocheckpoint pragma] can be used to invoke this interface + ** from SQL. + ** + ** ^Checkpoints initiated by this mechanism are + ** [sqlite3_wal_checkpoint_v2|PASSIVE]. + ** + ** ^Every new [database connection] defaults to having the auto-checkpoint + ** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT] + ** pages. The use of this interface + ** is only necessary if the default setting is found to be suboptimal + ** for a particular application. + */ + SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3* db, int N); + + /* + ** CAPI3REF: Checkpoint a database + ** METHOD: sqlite3 + ** + ** ^(The sqlite3_wal_checkpoint(D,X) is equivalent to + ** [sqlite3_wal_checkpoint_v2](D,X,[SQLITE_CHECKPOINT_PASSIVE],0,0).)^ + ** + ** In brief, sqlite3_wal_checkpoint(D,X) causes the content in the + ** [write-ahead log] for database X on [database connection] D to be + ** transferred into the database file and for the write-ahead log to + ** be reset. See the [checkpointing] documentation for addition + ** information. + ** + ** This interface used to be the only way to cause a checkpoint to + ** occur. But then the newer and more powerful [sqlite3_wal_checkpoint_v2()] + ** interface was added. This interface is retained for backwards + ** compatibility and as a convenience for applications that need to manually + ** start a callback but which do not need the full power (and corresponding + ** complication) of [sqlite3_wal_checkpoint_v2()]. + */ + SQLITE_API int sqlite3_wal_checkpoint(sqlite3* db, const char* zDb); + + /* + ** CAPI3REF: Checkpoint a database + ** METHOD: sqlite3 + ** + ** ^(The sqlite3_wal_checkpoint_v2(D,X,M,L,C) interface runs a checkpoint + ** operation on database X of [database connection] D in mode M. Status + ** information is written back into integers pointed to by L and C.)^ + ** ^(The M parameter must be a valid [checkpoint mode]:)^ + ** + **
    + **
    SQLITE_CHECKPOINT_PASSIVE
    + ** ^Checkpoint as many frames as possible without waiting for any database + ** readers or writers to finish, then sync the database file if all frames + ** in the log were checkpointed. ^The [busy-handler callback] + ** is never invoked in the SQLITE_CHECKPOINT_PASSIVE mode. + ** ^On the other hand, passive mode might leave the checkpoint unfinished + ** if there are concurrent readers or writers. + ** + **
    SQLITE_CHECKPOINT_FULL
    + ** ^This mode blocks (it invokes the + ** [sqlite3_busy_handler|busy-handler callback]) until there is no + ** database writer and all readers are reading from the most recent database + ** snapshot. ^It then checkpoints all frames in the log file and syncs the + ** database file. ^This mode blocks new database writers while it is pending, + ** but new database readers are allowed to continue unimpeded. + ** + **
    SQLITE_CHECKPOINT_RESTART
    + ** ^This mode works the same way as SQLITE_CHECKPOINT_FULL with the addition + ** that after checkpointing the log file it blocks (calls the + ** [busy-handler callback]) + ** until all readers are reading from the database file only. ^This ensures + ** that the next writer will restart the log file from the beginning. + ** ^Like SQLITE_CHECKPOINT_FULL, this mode blocks new + ** database writer attempts while it is pending, but does not impede readers. + ** + **
    SQLITE_CHECKPOINT_TRUNCATE
    + ** ^This mode works the same way as SQLITE_CHECKPOINT_RESTART with the + ** addition that it also truncates the log file to zero bytes just prior + ** to a successful return. + **
    + ** + ** ^If pnLog is not NULL, then *pnLog is set to the total number of frames in + ** the log file or to -1 if the checkpoint could not run because + ** of an error or because the database is not in [WAL mode]. ^If pnCkpt is not + ** NULL,then *pnCkpt is set to the total number of checkpointed frames in the + ** log file (including any that were already checkpointed before the function + ** was called) or to -1 if the checkpoint could not run due to an error or + ** because the database is not in WAL mode. ^Note that upon successful + ** completion of an SQLITE_CHECKPOINT_TRUNCATE, the log file will have been + ** truncated to zero bytes and so both *pnLog and *pnCkpt will be set to zero. + ** + ** ^All calls obtain an exclusive "checkpoint" lock on the database file. ^If + ** any other process is running a checkpoint operation at the same time, the + ** lock cannot be obtained and SQLITE_BUSY is returned. ^Even if there is a + ** busy-handler configured, it will not be invoked in this case. + ** + ** ^The SQLITE_CHECKPOINT_FULL, RESTART and TRUNCATE modes also obtain the + ** exclusive "writer" lock on the database file. ^If the writer lock cannot be + ** obtained immediately, and a busy-handler is configured, it is invoked and + ** the writer lock retried until either the busy-handler returns 0 or the lock + ** is successfully obtained. ^The busy-handler is also invoked while waiting for + ** database readers as described above. ^If the busy-handler returns 0 before + ** the writer lock is obtained or while waiting for database readers, the + ** checkpoint operation proceeds from that point in the same way as + ** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible + ** without blocking any further. ^SQLITE_BUSY is returned in this case. + ** + ** ^If parameter zDb is NULL or points to a zero length string, then the + ** specified operation is attempted on all WAL databases [attached] to + ** [database connection] db. In this case the + ** values written to output parameters *pnLog and *pnCkpt are undefined. ^If + ** an SQLITE_BUSY error is encountered when processing one or more of the + ** attached WAL databases, the operation is still attempted on any remaining + ** attached databases and SQLITE_BUSY is returned at the end. ^If any other + ** error occurs while processing an attached database, processing is abandoned + ** and the error code is returned to the caller immediately. ^If no error + ** (SQLITE_BUSY or otherwise) is encountered while processing the attached + ** databases, SQLITE_OK is returned. + ** + ** ^If database zDb is the name of an attached database that is not in WAL + ** mode, SQLITE_OK is returned and both *pnLog and *pnCkpt set to -1. ^If + ** zDb is not NULL (or a zero length string) and is not the name of any + ** attached database, SQLITE_ERROR is returned to the caller. + ** + ** ^Unless it returns SQLITE_MISUSE, + ** the sqlite3_wal_checkpoint_v2() interface + ** sets the error information that is queried by + ** [sqlite3_errcode()] and [sqlite3_errmsg()]. + ** + ** ^The [PRAGMA wal_checkpoint] command can be used to invoke this interface + ** from SQL. + */ + SQLITE_API int sqlite3_wal_checkpoint_v2(sqlite3* db, /* Database handle */ + const char* zDb, /* Name of attached database (or NULL) */ + int eMode, /* SQLITE_CHECKPOINT_* value */ + int* pnLog, /* OUT: Size of WAL log in frames */ + int* pnCkpt /* OUT: Total number of frames checkpointed */ + ); /* ** CAPI3REF: Checkpoint Mode Values @@ -9085,30 +9305,30 @@ ** See the [sqlite3_wal_checkpoint_v2()] documentation for details on the ** meaning of each of these checkpoint modes. */ -#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_TRUNCATE 3 /* Like RESTART but also truncate WAL */ - -/* -** CAPI3REF: Virtual Table Interface Configuration -** -** This function may be called by either the [xConnect] or [xCreate] method -** of a [virtual table] implementation to configure -** various facets of the virtual table interface. -** -** If this interface is invoked outside the context of an xConnect or -** xCreate virtual table method then the behavior is undefined. -** -** In the call sqlite3_vtab_config(D,C,...) the D parameter is the -** [database connection] in which the virtual table is being created and -** which is passed in as the first argument to the [xConnect] or [xCreate] -** method that is invoking sqlite3_vtab_config(). The C parameter is one -** of the [virtual table configuration options]. The presence and meaning -** of parameters after C depend on which [virtual table configuration option] -** is used. -*/ -SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); +#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_TRUNCATE 3 /* Like RESTART but also truncate WAL */ + + /* + ** CAPI3REF: Virtual Table Interface Configuration + ** + ** This function may be called by either the [xConnect] or [xCreate] method + ** of a [virtual table] implementation to configure + ** various facets of the virtual table interface. + ** + ** If this interface is invoked outside the context of an xConnect or + ** xCreate virtual table method then the behavior is undefined. + ** + ** In the call sqlite3_vtab_config(D,C,...) the D parameter is the + ** [database connection] in which the virtual table is being created and + ** which is passed in as the first argument to the [xConnect] or [xCreate] + ** method that is invoking sqlite3_vtab_config(). The C parameter is one + ** of the [virtual table configuration options]. The presence and meaning + ** of parameters after C depend on which [virtual table configuration option] + ** is used. + */ + SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); /* ** CAPI3REF: Virtual Table Configuration Options @@ -9173,54 +9393,311 @@ ** */ #define SQLITE_VTAB_CONSTRAINT_SUPPORT 1 -#define SQLITE_VTAB_INNOCUOUS 2 -#define SQLITE_VTAB_DIRECTONLY 3 +#define SQLITE_VTAB_INNOCUOUS 2 +#define SQLITE_VTAB_DIRECTONLY 3 -/* -** CAPI3REF: Determine The Virtual Table Conflict Policy -** -** This function may only be called from within a call to the [xUpdate] method -** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The -** value returned is one of [SQLITE_ROLLBACK], [SQLITE_IGNORE], [SQLITE_FAIL], -** [SQLITE_ABORT], or [SQLITE_REPLACE], according to the [ON CONFLICT] mode -** of the SQL statement that triggered the call to the [xUpdate] method of the -** [virtual table]. -*/ -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 -** 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 -** [xUpdate] method understands as a "no-change" value. -** -** If the [xColumn] method calls sqlite3_vtab_nochange() and finds that -** the column is not changed by the UPDATE statement, then the xColumn -** method can optionally return without setting a result, without calling -** 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. -*/ -SQLITE_API int sqlite3_vtab_nochange(sqlite3_context*); - -/* -** CAPI3REF: Determine The Collation For a Virtual Table Constraint -** -** This function may only be called from within a call to the [xBestIndex] -** method of a [virtual table]. -** -** 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. -*/ -SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int); + /* + ** CAPI3REF: Determine The Virtual Table Conflict Policy + ** + ** This function may only be called from within a call to the [xUpdate] method + ** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The + ** value returned is one of [SQLITE_ROLLBACK], [SQLITE_IGNORE], [SQLITE_FAIL], + ** [SQLITE_ABORT], or [SQLITE_REPLACE], according to the [ON CONFLICT] mode + ** of the SQL statement that triggered the call to the [xUpdate] method of the + ** [virtual table]. + */ + 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 might return true if the + ** column is being fetched as part of an UPDATE operation during which the + ** 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 + ** the column is not changed by the UPDATE statement, then the xColumn + ** method can optionally return without setting a result, without calling + ** 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]. 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 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); + + /* + ** 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 that is + ** either 0, 1, or 2. 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 "aOrderBy" columns + ** are adjacent.)^ ^(Furthermore, only a single row for each particular + ** combination of values in the columns identified by the "aOrderBy" field + ** needs to be returned.)^ ^It is always ok for two or more rows with the same + ** values in all "aOrderBy" columns to be returned, as long as all such rows + ** are adjacent. ^The virtual table may, if it chooses, omit extra rows + ** that have the same value for all columns identified by "aOrderBy". + ** ^However omitting the extra rows is optional. + ** This mode is used for a DISTINCT query. + **

    + ** + ** ^For the purposes of comparing virtual table output values to see if the + ** values are 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 + ** 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 postive 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) must 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 use 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_MISUSE])^ or perhaps + ** exhibit some other undefined or harmful behavior. + ** + ** ^(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 an 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 appreviation for + ** "Right-Hand Side". + */ + SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value** ppVal); /* ** CAPI3REF: Conflict resolution modes @@ -9236,9 +9713,9 @@ */ #define SQLITE_ROLLBACK 1 /* #define SQLITE_IGNORE 2 // Also used by sqlite3_authorizer() callback */ -#define SQLITE_FAIL 3 +#define SQLITE_FAIL 3 /* #define SQLITE_ABORT 4 // Also an error code */ -#define SQLITE_REPLACE 5 +#define SQLITE_REPLACE 5 /* ** CAPI3REF: Prepared Statement Scan Status Opcodes @@ -9287,96 +9764,97 @@ ** of an [EXPLAIN QUERY PLAN] query. ** */ -#define SQLITE_SCANSTAT_NLOOP 0 -#define SQLITE_SCANSTAT_NVISIT 1 -#define SQLITE_SCANSTAT_EST 2 -#define SQLITE_SCANSTAT_NAME 3 -#define SQLITE_SCANSTAT_EXPLAIN 4 +#define SQLITE_SCANSTAT_NLOOP 0 +#define SQLITE_SCANSTAT_NVISIT 1 +#define SQLITE_SCANSTAT_EST 2 +#define SQLITE_SCANSTAT_NAME 3 +#define SQLITE_SCANSTAT_EXPLAIN 4 #define SQLITE_SCANSTAT_SELECTID 5 -/* -** CAPI3REF: Prepared Statement Scan Status -** METHOD: sqlite3_stmt -** -** This interface returns 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. -** -** Since this interface is expected to be rarely used, it is only -** available if SQLite is compiled using the [SQLITE_ENABLE_STMT_SCANSTATUS] -** compile-time option. -** -** 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. -** -** See also: [sqlite3_stmt_scanstatus_reset()] -*/ -SQLITE_API int sqlite3_stmt_scanstatus( - sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ - int idx, /* Index of loop to report on */ - int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ - void *pOut /* Result written here */ -); - -/* -** CAPI3REF: Zero Scan-Status Counters -** METHOD: sqlite3_stmt -** -** ^Zero all [sqlite3_stmt_scanstatus()] related event counters. -** -** This API is only available if the library is built with pre-processor -** symbol [SQLITE_ENABLE_STMT_SCANSTATUS] defined. -*/ -SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*); - -/* -** CAPI3REF: Flush caches to disk mid-transaction -** -** ^If a write-transaction is open on [database connection] D when the -** [sqlite3_db_cacheflush(D)] interface 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 -** file (page 1 is always "in use"). ^The [sqlite3_db_cacheflush(D)] -** interface flushes caches for all schemas - "main", "temp", and -** any [attached] databases. -** -** ^If this function needs to obtain extra database locks before dirty pages -** can be flushed to disk, it does so. ^If those locks cannot be obtained -** immediately and there is a busy-handler callback configured, it is invoked -** in the usual manner. ^If the required lock still cannot be obtained, then -** the database is skipped and an attempt made to flush any dirty pages -** belonging to the next (if any) database. ^If any databases are skipped -** because locks cannot be obtained, but no other error occurs, this -** function returns SQLITE_BUSY. -** -** ^If any other error occurs while flushing dirty pages to disk (for -** example an IO error or out-of-memory condition), then processing is -** abandoned and an SQLite [error code] is returned to the caller immediately. -** -** ^Otherwise, if no error occurs, [sqlite3_db_cacheflush()] returns SQLITE_OK. -** -** ^This function does not set the database handle error code or message -** returned by the [sqlite3_errcode()] and [sqlite3_errmsg()] functions. -*/ -SQLITE_API int sqlite3_db_cacheflush(sqlite3*); + /* + ** CAPI3REF: Prepared Statement Scan Status + ** METHOD: sqlite3_stmt + ** + ** This interface returns 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. + ** + ** Since this interface is expected to be rarely used, it is only + ** available if SQLite is compiled using the [SQLITE_ENABLE_STMT_SCANSTATUS] + ** compile-time option. + ** + ** 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. + ** + ** See also: [sqlite3_stmt_scanstatus_reset()] + */ + SQLITE_API int sqlite3_stmt_scanstatus(sqlite3_stmt* pStmt, /* Prepared statement for which info desired */ + int idx, /* Index of loop to report on */ + int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ + void* pOut /* Result written here */ + ); + + /* + ** CAPI3REF: Zero Scan-Status Counters + ** METHOD: sqlite3_stmt + ** + ** ^Zero all [sqlite3_stmt_scanstatus()] related event counters. + ** + ** This API is only available if the library is built with pre-processor + ** symbol [SQLITE_ENABLE_STMT_SCANSTATUS] defined. + */ + 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 + ** 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 + ** file (page 1 is always "in use"). ^The [sqlite3_db_cacheflush(D)] + ** interface flushes caches for all schemas - "main", "temp", and + ** any [attached] databases. + ** + ** ^If this function needs to obtain extra database locks before dirty pages + ** can be flushed to disk, it does so. ^If those locks cannot be obtained + ** immediately and there is a busy-handler callback configured, it is invoked + ** in the usual manner. ^If the required lock still cannot be obtained, then + ** the database is skipped and an attempt made to flush any dirty pages + ** belonging to the next (if any) database. ^If any databases are skipped + ** because locks cannot be obtained, but no other error occurs, this + ** function returns SQLITE_BUSY. + ** + ** ^If any other error occurs while flushing dirty pages to disk (for + ** example an IO error or out-of-memory condition), then processing is + ** abandoned and an SQLite [error code] is returned to the caller immediately. + ** + ** ^Otherwise, if no error occurs, [sqlite3_db_cacheflush()] returns SQLITE_OK. + ** + ** ^This function does not set the database handle error code or message + ** returned by the [sqlite3_errcode()] and [sqlite3_errmsg()] functions. + */ + 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 +9895,7 @@ ** 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_old()], [sqlite3_preupdate_new()], ** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces @@ -9455,272 +9933,273 @@ ** 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. This is because the +** in this case the new values are not available. In this case, when a +** callback made with op==SQLITE_DELETE is actuall 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) -SQLITE_API void *sqlite3_preupdate_hook( - sqlite3 *db, - void(*xPreUpdate)( - void *pCtx, /* Copy of third arg to preupdate_hook() */ - sqlite3 *db, /* Database handle */ - int op, /* SQLITE_UPDATE, DELETE or INSERT */ - char const *zDb, /* Database name */ - char const *zName, /* Table name */ - sqlite3_int64 iKey1, /* Rowid of row about to be deleted/updated */ - sqlite3_int64 iKey2 /* New rowid value (for a rowid UPDATE) */ - ), - void* -); -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 void* + sqlite3_preupdate_hook(sqlite3* db, + void (*xPreUpdate)(void* pCtx, /* Copy of third arg to preupdate_hook() */ + sqlite3* db, /* Database handle */ + int op, /* SQLITE_UPDATE, DELETE or INSERT */ + char const* zDb, /* Database name */ + char const* zName, /* Table name */ + sqlite3_int64 iKey1, /* Rowid of row about to be deleted/updated */ + sqlite3_int64 iKey2 /* New rowid value (for a rowid UPDATE) */ + ), + void*); + 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 -** -** ^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. -** The return value is OS-dependent. For example, on unix systems, after -** [sqlite3_open_v2()] returns [SQLITE_CANTOPEN], this interface could be -** called to get back the underlying "errno" that caused the problem, such -** as ENOSPC, EAUTH, EISDIR, and so forth. -*/ -SQLITE_API int sqlite3_system_errno(sqlite3*); - -/* -** CAPI3REF: Database Snapshot -** KEYWORDS: {snapshot} {sqlite3_snapshot} -** -** An instance of the snapshot object records the state of a [WAL mode] -** database for some specific point in history. -** -** In [WAL mode], multiple [database connections] that are open on the -** same database file can each be reading a different historical version -** of the database file. When a [database connection] begins a read -** transaction, that connection sees an unchanging copy of the database -** as it existed for the point in time when the transaction first started. -** Subsequent changes to the database from other connections are not seen -** by the reader until a new read transaction is started. -** -** The sqlite3_snapshot object records state information about an historical -** version of the database file so that it is possible to later open a new read -** transaction that sees that historical version of the database rather than -** the most recent version. -*/ -typedef struct sqlite3_snapshot { - unsigned char hidden[48]; -} sqlite3_snapshot; - -/* -** CAPI3REF: Record A Database Snapshot -** CONSTRUCTOR: sqlite3_snapshot -** -** ^The [sqlite3_snapshot_get(D,S,P)] interface attempts to make a -** new [sqlite3_snapshot] object that records the current state of -** schema S in database connection D. ^On success, the -** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly -** created [sqlite3_snapshot] object into *P and returns SQLITE_OK. -** If there is not already a read-transaction open on schema S when -** this function is called, one is opened automatically. -** -** 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 -** in this case. -** -**
      -**
    • The database handle must not be in [autocommit mode]. -** -**
    • Schema S of [database connection] D must be a [WAL mode] database. -** -**
    • There must not be a write transaction open on schema S of database -** connection D. -** -**
    • One or more transactions must have been written to the current wal -** file since it was created on disk (by any connection). This means -** that a snapshot cannot be taken on a wal mode database with no wal -** file immediately after it is first opened. At least one transaction -** must be written to it first. -**
    -** -** This function may also return SQLITE_NOMEM. If it is called with the -** database handle in autocommit mode but fails for some other reason, -** whether or not a read transaction is opened on schema S is undefined. -** -** The [sqlite3_snapshot] object returned from a successful call to -** [sqlite3_snapshot_get()] must be freed using [sqlite3_snapshot_free()] -** to avoid a memory leak. -** -** The [sqlite3_snapshot_get()] interface is only available when the -** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used. -*/ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_get( - sqlite3 *db, - const char *zSchema, - sqlite3_snapshot **ppSnapshot -); - -/* -** CAPI3REF: Start a read transaction on an historical snapshot -** METHOD: sqlite3_snapshot -** -** ^The [sqlite3_snapshot_open(D,S,P)] interface either starts a new read -** transaction or upgrades an existing one for schema S of -** [database connection] D such that the read transaction refers to -** historical [snapshot] P, rather than the most recent change to the -** database. ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK -** on success or an appropriate [error code] if it fails. -** -** ^In order to succeed, the database connection must not be in -** [autocommit mode] when [sqlite3_snapshot_open(D,S,P)] is called. If there -** is already a read transaction open on schema S, then the database handle -** must have no active statements (SELECT statements that have been passed -** to sqlite3_step() but not sqlite3_reset() or sqlite3_finalize()). -** SQLITE_ERROR is returned if either of these conditions is violated, or -** if schema S does not exist, or if the snapshot object is invalid. -** -** ^A call to sqlite3_snapshot_open() will fail to open if the specified -** snapshot has been overwritten by a [checkpoint]. In this case -** SQLITE_ERROR_SNAPSHOT is returned. -** -** If there is already a read transaction open when this function is -** invoked, then the same read transaction remains open (on the same -** database snapshot) if SQLITE_ERROR, SQLITE_BUSY or SQLITE_ERROR_SNAPSHOT -** is returned. If another error code - for example SQLITE_PROTOCOL or an -** SQLITE_IOERR error code - is returned, then the final state of the -** read transaction is undefined. If SQLITE_OK is returned, then the -** read transaction is now open on database snapshot P. -** -** ^(A call to [sqlite3_snapshot_open(D,S,P)] will fail if the -** database connection D does not know that the database file for -** schema S is in [WAL mode]. A database connection might not know -** that the database file is in [WAL mode] if there has been no prior -** I/O on that database connection, or if the database entered [WAL mode] -** after the most recent I/O on the database connection.)^ -** (Hint: Run "[PRAGMA application_id]" against a newly opened -** database connection in order to make it ready to use snapshots.) -** -** The [sqlite3_snapshot_open()] interface is only available when the -** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used. -*/ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_open( - sqlite3 *db, - const char *zSchema, - sqlite3_snapshot *pSnapshot -); - -/* -** CAPI3REF: Destroy a snapshot -** DESTRUCTOR: sqlite3_snapshot -** -** ^The [sqlite3_snapshot_free(P)] interface destroys [sqlite3_snapshot] P. -** The application must eventually free every [sqlite3_snapshot] object -** using this routine to avoid a memory leak. -** -** The [sqlite3_snapshot_free()] interface is only available when the -** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used. -*/ -SQLITE_API SQLITE_EXPERIMENTAL void sqlite3_snapshot_free(sqlite3_snapshot*); - -/* -** CAPI3REF: Compare the ages of two snapshot handles. -** METHOD: sqlite3_snapshot -** -** The sqlite3_snapshot_cmp(P1, P2) interface is used to compare the ages -** of two valid snapshot handles. -** -** If the two snapshot handles are not associated with the same database -** file, the result of the comparison is undefined. -** -** Additionally, the result of the comparison is only valid if both of the -** snapshot handles were obtained by calling sqlite3_snapshot_get() since the -** last time the wal file was deleted. The wal file is deleted when the -** database is changed back to rollback mode or when the number of database -** clients drops to zero. If either snapshot handle was obtained before the -** wal file was last deleted, the value returned by this function -** is undefined. -** -** Otherwise, this API returns a negative value if P1 refers to an older -** snapshot than P2, zero if the two handles refer to the same database -** snapshot, and a positive value if P1 is a newer snapshot than P2. -** -** This interface is only available if SQLite is compiled with the -** [SQLITE_ENABLE_SNAPSHOT] option. -*/ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp( - sqlite3_snapshot *p1, - sqlite3_snapshot *p2 -); - -/* -** CAPI3REF: Recover snapshots from a wal file -** METHOD: sqlite3_snapshot -** -** If a [WAL file] remains on disk after all database connections close -** (either through the use of the [SQLITE_FCNTL_PERSIST_WAL] [file control] -** or because the last process to have the database opened exited without -** calling [sqlite3_close()]) and a new connection is subsequently opened -** on that database and [WAL file], the [sqlite3_snapshot_open()] interface -** will only be able to open the last transaction added to the WAL file -** even though the WAL file contains other valid transactions. -** -** This function attempts to scan the WAL file associated with database zDb -** of database handle db and make all valid snapshots available to -** sqlite3_snapshot_open(). It is an error if there is already a read -** transaction open on the database, or if the database is not a WAL mode -** database. -** -** SQLITE_OK is returned if successful, or an SQLite error code otherwise. -** -** This interface is only available if SQLite is compiled with the -** [SQLITE_ENABLE_SNAPSHOT] option. -*/ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb); - -/* -** 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. -** 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. -** -** The usual case is that sqlite3_serialize() copies the serialization of -** the database into memory obtained from [sqlite3_malloc64()] and returns -** a pointer to that memory. The caller is responsible for freeing the -** returned value to avoid a memory leak. However, if the F argument -** 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 -** 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 -** values of D and S. -** The size of the database is written into *P even if the -** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy -** of the database exists. -** -** 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. -*/ -SQLITE_API unsigned char *sqlite3_serialize( - sqlite3 *db, /* The database connection */ - const char *zSchema, /* Which DB to serialize. ex: "main", "temp", ... */ - sqlite3_int64 *piSize, /* Write size of the DB here, if not NULL */ - unsigned int mFlags /* Zero or more SQLITE_SERIALIZE_* flags */ -); + /* + ** 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. + ** The return value is OS-dependent. For example, on unix systems, after + ** [sqlite3_open_v2()] returns [SQLITE_CANTOPEN], this interface could be + ** called to get back the underlying "errno" that caused the problem, such + ** as ENOSPC, EAUTH, EISDIR, and so forth. + */ + SQLITE_API int sqlite3_system_errno(sqlite3*); + + /* + ** CAPI3REF: Database Snapshot + ** KEYWORDS: {snapshot} {sqlite3_snapshot} + ** + ** An instance of the snapshot object records the state of a [WAL mode] + ** database for some specific point in history. + ** + ** In [WAL mode], multiple [database connections] that are open on the + ** same database file can each be reading a different historical version + ** of the database file. When a [database connection] begins a read + ** transaction, that connection sees an unchanging copy of the database + ** as it existed for the point in time when the transaction first started. + ** Subsequent changes to the database from other connections are not seen + ** by the reader until a new read transaction is started. + ** + ** The sqlite3_snapshot object records state information about an historical + ** version of the database file so that it is possible to later open a new read + ** transaction that sees that historical version of the database rather than + ** the most recent version. + */ + typedef struct sqlite3_snapshot + { + unsigned char hidden[48]; + } sqlite3_snapshot; + + /* + ** CAPI3REF: Record A Database Snapshot + ** CONSTRUCTOR: sqlite3_snapshot + ** + ** ^The [sqlite3_snapshot_get(D,S,P)] interface attempts to make a + ** new [sqlite3_snapshot] object that records the current state of + ** schema S in database connection D. ^On success, the + ** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly + ** created [sqlite3_snapshot] object into *P and returns SQLITE_OK. + ** If there is not already a read-transaction open on schema S when + ** this function is called, one is opened automatically. + ** + ** 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 + ** in this case. + ** + **
      + **
    • The database handle must not be in [autocommit mode]. + ** + **
    • Schema S of [database connection] D must be a [WAL mode] database. + ** + **
    • There must not be a write transaction open on schema S of database + ** connection D. + ** + **
    • One or more transactions must have been written to the current wal + ** file since it was created on disk (by any connection). This means + ** that a snapshot cannot be taken on a wal mode database with no wal + ** file immediately after it is first opened. At least one transaction + ** must be written to it first. + **
    + ** + ** This function may also return SQLITE_NOMEM. If it is called with the + ** database handle in autocommit mode but fails for some other reason, + ** whether or not a read transaction is opened on schema S is undefined. + ** + ** The [sqlite3_snapshot] object returned from a successful call to + ** [sqlite3_snapshot_get()] must be freed using [sqlite3_snapshot_free()] + ** to avoid a memory leak. + ** + ** The [sqlite3_snapshot_get()] interface is only available when the + ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used. + */ + SQLITE_API SQLITE_EXPERIMENTAL int + sqlite3_snapshot_get(sqlite3* db, const char* zSchema, sqlite3_snapshot** ppSnapshot); + + /* + ** CAPI3REF: Start a read transaction on an historical snapshot + ** METHOD: sqlite3_snapshot + ** + ** ^The [sqlite3_snapshot_open(D,S,P)] interface either starts a new read + ** transaction or upgrades an existing one for schema S of + ** [database connection] D such that the read transaction refers to + ** historical [snapshot] P, rather than the most recent change to the + ** database. ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK + ** on success or an appropriate [error code] if it fails. + ** + ** ^In order to succeed, the database connection must not be in + ** [autocommit mode] when [sqlite3_snapshot_open(D,S,P)] is called. If there + ** is already a read transaction open on schema S, then the database handle + ** must have no active statements (SELECT statements that have been passed + ** to sqlite3_step() but not sqlite3_reset() or sqlite3_finalize()). + ** SQLITE_ERROR is returned if either of these conditions is violated, or + ** if schema S does not exist, or if the snapshot object is invalid. + ** + ** ^A call to sqlite3_snapshot_open() will fail to open if the specified + ** snapshot has been overwritten by a [checkpoint]. In this case + ** SQLITE_ERROR_SNAPSHOT is returned. + ** + ** If there is already a read transaction open when this function is + ** invoked, then the same read transaction remains open (on the same + ** database snapshot) if SQLITE_ERROR, SQLITE_BUSY or SQLITE_ERROR_SNAPSHOT + ** is returned. If another error code - for example SQLITE_PROTOCOL or an + ** SQLITE_IOERR error code - is returned, then the final state of the + ** read transaction is undefined. If SQLITE_OK is returned, then the + ** read transaction is now open on database snapshot P. + ** + ** ^(A call to [sqlite3_snapshot_open(D,S,P)] will fail if the + ** database connection D does not know that the database file for + ** schema S is in [WAL mode]. A database connection might not know + ** that the database file is in [WAL mode] if there has been no prior + ** I/O on that database connection, or if the database entered [WAL mode] + ** after the most recent I/O on the database connection.)^ + ** (Hint: Run "[PRAGMA application_id]" against a newly opened + ** database connection in order to make it ready to use snapshots.) + ** + ** The [sqlite3_snapshot_open()] interface is only available when the + ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used. + */ + SQLITE_API SQLITE_EXPERIMENTAL int + sqlite3_snapshot_open(sqlite3* db, const char* zSchema, sqlite3_snapshot* pSnapshot); + + /* + ** CAPI3REF: Destroy a snapshot + ** DESTRUCTOR: sqlite3_snapshot + ** + ** ^The [sqlite3_snapshot_free(P)] interface destroys [sqlite3_snapshot] P. + ** The application must eventually free every [sqlite3_snapshot] object + ** using this routine to avoid a memory leak. + ** + ** The [sqlite3_snapshot_free()] interface is only available when the + ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used. + */ + SQLITE_API SQLITE_EXPERIMENTAL void sqlite3_snapshot_free(sqlite3_snapshot*); + + /* + ** CAPI3REF: Compare the ages of two snapshot handles. + ** METHOD: sqlite3_snapshot + ** + ** The sqlite3_snapshot_cmp(P1, P2) interface is used to compare the ages + ** of two valid snapshot handles. + ** + ** If the two snapshot handles are not associated with the same database + ** file, the result of the comparison is undefined. + ** + ** Additionally, the result of the comparison is only valid if both of the + ** snapshot handles were obtained by calling sqlite3_snapshot_get() since the + ** last time the wal file was deleted. The wal file is deleted when the + ** database is changed back to rollback mode or when the number of database + ** clients drops to zero. If either snapshot handle was obtained before the + ** wal file was last deleted, the value returned by this function + ** is undefined. + ** + ** Otherwise, this API returns a negative value if P1 refers to an older + ** snapshot than P2, zero if the two handles refer to the same database + ** snapshot, and a positive value if P1 is a newer snapshot than P2. + ** + ** This interface is only available if SQLite is compiled with the + ** [SQLITE_ENABLE_SNAPSHOT] option. + */ + SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(sqlite3_snapshot* p1, sqlite3_snapshot* p2); + + /* + ** CAPI3REF: Recover snapshots from a wal file + ** METHOD: sqlite3_snapshot + ** + ** If a [WAL file] remains on disk after all database connections close + ** (either through the use of the [SQLITE_FCNTL_PERSIST_WAL] [file control] + ** or because the last process to have the database opened exited without + ** calling [sqlite3_close()]) and a new connection is subsequently opened + ** on that database and [WAL file], the [sqlite3_snapshot_open()] interface + ** will only be able to open the last transaction added to the WAL file + ** even though the WAL file contains other valid transactions. + ** + ** This function attempts to scan the WAL file associated with database zDb + ** of database handle db and make all valid snapshots available to + ** sqlite3_snapshot_open(). It is an error if there is already a read + ** transaction open on the database, or if the database is not a WAL mode + ** database. + ** + ** SQLITE_OK is returned if successful, or an SQLite error code otherwise. + ** + ** This interface is only available if SQLite is compiled with the + ** [SQLITE_ENABLE_SNAPSHOT] option. + */ + SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3* db, const char* zDb); + + /* + ** 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. + ** 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. + ** + ** The usual case is that sqlite3_serialize() copies the serialization of + ** the database into memory obtained from [sqlite3_malloc64()] and returns + ** a pointer to that memory. The caller is responsible for freeing the + ** returned value to avoid a memory leak. However, if the F argument + ** 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 + ** 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 + ** values of D and S. + ** The size of the database is written into *P even if the + ** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy + ** of the database exists. + ** + ** 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 omitted if SQLite is compiled with the + ** [SQLITE_OMIT_DESERIALIZE] option. + */ + SQLITE_API unsigned char* sqlite3_serialize(sqlite3* db, /* The database connection */ + const char* zSchema, /* Which DB to serialize. ex: "main", "temp", ... + */ + sqlite3_int64* piSize, /* Write size of the DB here, if not NULL */ + unsigned int mFlags /* Zero or more SQLITE_SERIALIZE_* flags */ + ); /* ** CAPI3REF: Flags for sqlite3_serialize @@ -9736,45 +10215,48 @@ ** using a contiguous in-memory database if it has been initialized by a ** prior call to [sqlite3_deserialize()]. */ -#define SQLITE_SERIALIZE_NOCOPY 0x001 /* Do no memory allocations */ +#define SQLITE_SERIALIZE_NOCOPY 0x001 /* Do no memory allocations */ -/* -** CAPI3REF: Deserialize a database -** -** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the -** [database connection] D to disconnect from database S and then -** reopen S as an in-memory database based on the serialization contained -** in P. The serialized database P is N bytes in size. M is the size of -** the buffer P, which might be larger than N. If M is larger than N, and -** the SQLITE_DESERIALIZE_READONLY bit is not set in F, then SQLite is -** permitted to add content to the in-memory database as long as the total -** size does not exceed M bytes. -** -** If the SQLITE_DESERIALIZE_FREEONCLOSE bit is set in F, then SQLite will -** invoke sqlite3_free() on the serialization buffer when the database -** connection closes. If the SQLITE_DESERIALIZE_RESIZEABLE bit is set, then -** SQLite will try to increase the buffer size using sqlite3_realloc64() -** if writes on the database cause it to grow larger than M bytes. -** -** 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. -** -** 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. -*/ -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 szBuf, /* Total size of buffer pData[] */ - unsigned mFlags /* Zero or more SQLITE_DESERIALIZE_* flags */ -); + /* + ** CAPI3REF: Deserialize a database + ** + ** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the + ** [database connection] D to disconnect from database S and then + ** reopen S as an in-memory database based on the serialization contained + ** in P. The serialized database P is N bytes in size. M is the size of + ** the buffer P, which might be larger than N. If M is larger than N, and + ** the SQLITE_DESERIALIZE_READONLY bit is not set in F, then SQLite is + ** permitted to add content to the in-memory database as long as the total + ** size does not exceed M bytes. + ** + ** If the SQLITE_DESERIALIZE_FREEONCLOSE bit is set in F, then SQLite will + ** invoke sqlite3_free() on the serialization buffer when the database + ** connection closes. If the SQLITE_DESERIALIZE_RESIZEABLE bit is set, then + ** SQLite will try to increase the buffer size using sqlite3_realloc64() + ** if writes on the database cause it to grow larger than M bytes. + ** + ** 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 deserialized 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. + ** + ** 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 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 szBuf, /* Total size of buffer pData[] */ + unsigned mFlags /* Zero or more SQLITE_DESERIALIZE_* flags */ + ); /* ** CAPI3REF: Flags for sqlite3_deserialize() @@ -9798,19 +10280,19 @@ ** should be treated as read-only. */ #define SQLITE_DESERIALIZE_FREEONCLOSE 1 /* Call sqlite3_free() on close */ -#define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */ -#define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */ +#define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */ +#define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */ /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT -# undef double +#undef double #endif #ifdef __cplusplus -} /* End of the 'extern "C"' block */ +} /* End of the 'extern "C"' block */ #endif #endif /* SQLITE3_H */ @@ -9831,106 +10313,101 @@ #ifndef _SQLITE3RTREE_H_ #define _SQLITE3RTREE_H_ - #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif -typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry; -typedef struct sqlite3_rtree_query_info sqlite3_rtree_query_info; + typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry; + typedef struct sqlite3_rtree_query_info sqlite3_rtree_query_info; /* The double-precision datatype used by RTree depends on the ** SQLITE_RTREE_INT_ONLY compile-time option. */ #ifdef SQLITE_RTREE_INT_ONLY - typedef sqlite3_int64 sqlite3_rtree_dbl; + typedef sqlite3_int64 sqlite3_rtree_dbl; #else - typedef double sqlite3_rtree_dbl; +typedef double sqlite3_rtree_dbl; #endif -/* -** Register a geometry callback named zGeom that can be used as part of an -** R-Tree geometry query as follows: -** -** SELECT ... FROM WHERE MATCH $zGeom(... params ...) -*/ -SQLITE_API int sqlite3_rtree_geometry_callback( - sqlite3 *db, - const char *zGeom, - int (*xGeom)(sqlite3_rtree_geometry*, int, sqlite3_rtree_dbl*,int*), - void *pContext -); - - -/* -** A pointer to a structure of the following type is passed as the first -** argument to callbacks registered using rtree_geometry_callback(). -*/ -struct sqlite3_rtree_geometry { - void *pContext; /* Copy of pContext passed to s_r_g_c() */ - int nParam; /* Size of array aParam[] */ - sqlite3_rtree_dbl *aParam; /* Parameters passed to SQL geom function */ - void *pUser; /* Callback implementation user data */ - void (*xDelUser)(void *); /* Called by SQLite to clean up pUser */ -}; - -/* -** Register a 2nd-generation geometry callback named zScore that can be -** used as part of an R-Tree geometry query as follows: -** -** SELECT ... FROM WHERE MATCH $zQueryFunc(... params ...) -*/ -SQLITE_API int sqlite3_rtree_query_callback( - sqlite3 *db, - const char *zQueryFunc, - int (*xQueryFunc)(sqlite3_rtree_query_info*), - void *pContext, - void (*xDestructor)(void*) -); - - -/* -** A pointer to a structure of the following type is passed as the -** argument to scored geometry callback registered using -** sqlite3_rtree_query_callback(). -** -** Note that the first 5 fields of this structure are identical to -** sqlite3_rtree_geometry. This structure is a subclass of -** sqlite3_rtree_geometry. -*/ -struct sqlite3_rtree_query_info { - void *pContext; /* pContext from when function registered */ - int nParam; /* Number of function parameters */ - sqlite3_rtree_dbl *aParam; /* value of function parameters */ - void *pUser; /* callback can use this, if desired */ - void (*xDelUser)(void*); /* function to free pUser */ - sqlite3_rtree_dbl *aCoord; /* Coordinates of node or entry to check */ - unsigned int *anQueue; /* Number of pending entries in the queue */ - int nCoord; /* Number of coordinates */ - int iLevel; /* Level of current node or entry */ - int mxLevel; /* The largest iLevel value in the tree */ - sqlite3_int64 iRowid; /* Rowid for current entry */ - sqlite3_rtree_dbl rParentScore; /* Score of parent node */ - int eParentWithin; /* Visibility of parent node */ - int eWithin; /* OUT: Visibility */ - sqlite3_rtree_dbl rScore; /* OUT: Write the score here */ - /* The following fields are only available in 3.8.11 and later */ - sqlite3_value **apSqlParam; /* Original SQL values of parameters */ -}; + /* + ** Register a geometry callback named zGeom that can be used as part of an + ** R-Tree geometry query as follows: + ** + ** SELECT ... FROM WHERE MATCH $zGeom(... params ...) + */ + SQLITE_API int sqlite3_rtree_geometry_callback(sqlite3* db, + const char* zGeom, + int (*xGeom)(sqlite3_rtree_geometry*, int, sqlite3_rtree_dbl*, int*), + void* pContext); + + /* + ** A pointer to a structure of the following type is passed as the first + ** argument to callbacks registered using rtree_geometry_callback(). + */ + struct sqlite3_rtree_geometry + { + void* pContext; /* Copy of pContext passed to s_r_g_c() */ + int nParam; /* Size of array aParam[] */ + sqlite3_rtree_dbl* aParam; /* Parameters passed to SQL geom function */ + void* pUser; /* Callback implementation user data */ + void (*xDelUser)(void*); /* Called by SQLite to clean up pUser */ + }; + + /* + ** Register a 2nd-generation geometry callback named zScore that can be + ** used as part of an R-Tree geometry query as follows: + ** + ** SELECT ... FROM WHERE MATCH $zQueryFunc(... params ...) + */ + SQLITE_API int sqlite3_rtree_query_callback(sqlite3* db, + const char* zQueryFunc, + int (*xQueryFunc)(sqlite3_rtree_query_info*), + void* pContext, + void (*xDestructor)(void*)); + + /* + ** A pointer to a structure of the following type is passed as the + ** argument to scored geometry callback registered using + ** sqlite3_rtree_query_callback(). + ** + ** Note that the first 5 fields of this structure are identical to + ** sqlite3_rtree_geometry. This structure is a subclass of + ** sqlite3_rtree_geometry. + */ + struct sqlite3_rtree_query_info + { + void* pContext; /* pContext from when function registered */ + int nParam; /* Number of function parameters */ + sqlite3_rtree_dbl* aParam; /* value of function parameters */ + void* pUser; /* callback can use this, if desired */ + void (*xDelUser)(void*); /* function to free pUser */ + sqlite3_rtree_dbl* aCoord; /* Coordinates of node or entry to check */ + unsigned int* anQueue; /* Number of pending entries in the queue */ + int nCoord; /* Number of coordinates */ + int iLevel; /* Level of current node or entry */ + int mxLevel; /* The largest iLevel value in the tree */ + sqlite3_int64 iRowid; /* Rowid for current entry */ + sqlite3_rtree_dbl rParentScore; /* Score of parent node */ + int eParentWithin; /* Visibility of parent node */ + int eWithin; /* OUT: Visibility */ + sqlite3_rtree_dbl rScore; /* OUT: Write the score here */ + /* The following fields are only available in 3.8.11 and later */ + sqlite3_value** apSqlParam; /* Original SQL values of parameters */ + }; /* ** Allowed values for sqlite3_rtree_query.eWithin and .eParentWithin. */ -#define NOT_WITHIN 0 /* Object completely outside of query region */ -#define PARTLY_WITHIN 1 /* Object partially overlaps query region */ -#define FULLY_WITHIN 2 /* Object fully contained within query region */ - +#define NOT_WITHIN 0 /* Object completely outside of query region */ +#define PARTLY_WITHIN 1 /* Object partially overlaps query region */ +#define FULLY_WITHIN 2 /* Object fully contained within query region */ #ifdef __cplusplus -} /* end of the 'extern "C"' block */ +} /* end of the 'extern "C"' block */ #endif -#endif /* ifndef _SQLITE3RTREE_H_ */ +#endif /* ifndef _SQLITE3RTREE_H_ */ /******** End of sqlite3rtree.h *********/ /******** Begin file sqlite3session.h *********/ @@ -9942,491 +10419,534 @@ ** Make sure we can call this stuff from C++. */ #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif - -/* -** CAPI3REF: Session Object Handle -** -** An instance of this object is a [session] that can be used to -** record changes to a database. -*/ -typedef struct sqlite3_session sqlite3_session; - -/* -** CAPI3REF: Changeset Iterator Handle -** -** An instance of this object acts as a cursor for iterating -** over the elements of a [changeset] or [patchset]. -*/ -typedef struct sqlite3_changeset_iter sqlite3_changeset_iter; - -/* -** CAPI3REF: Create A New Session Object -** CONSTRUCTOR: sqlite3_session -** -** Create a new session object attached to database handle db. If successful, -** a pointer to the new object is written to *ppSession and SQLITE_OK is -** returned. If an error occurs, *ppSession is set to NULL and an SQLite -** error code (e.g. SQLITE_NOMEM) is returned. -** -** It is possible to create multiple session objects attached to a single -** database handle. -** -** Session objects created using this function should be deleted using the -** [sqlite3session_delete()] function before the database handle that they -** are attached to is itself closed. If the database handle is closed before -** the session object is deleted, then the results of calling any session -** module function, including [sqlite3session_delete()] on the session object -** are undefined. -** -** Because the session module uses the [sqlite3_preupdate_hook()] API, it -** is not possible for an application to register a pre-update hook on a -** database handle that has one or more session objects attached. Nor is -** it possible to create a session object attached to a database handle for -** which a pre-update hook is already defined. The results of attempting -** either of these things are undefined. -** -** The session object will be used to create changesets for tables in -** database zDb, where zDb is either "main", or "temp", or the name of an -** attached database. It is not an error if database zDb is not attached -** to the database when the session object is created. -*/ -SQLITE_API int sqlite3session_create( - sqlite3 *db, /* Database handle */ - const char *zDb, /* Name of db (e.g. "main") */ - sqlite3_session **ppSession /* OUT: New session object */ -); - -/* -** CAPI3REF: Delete A Session Object -** DESTRUCTOR: sqlite3_session -** -** Delete a session object previously allocated using -** [sqlite3session_create()]. Once a session object has been deleted, the -** results of attempting to use pSession with any other session module -** function are undefined. -** -** Session objects must be deleted before the database handle to which they -** are attached is closed. Refer to the documentation for -** [sqlite3session_create()] for details. -*/ -SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); - - -/* -** CAPI3REF: Enable Or Disable A Session Object -** METHOD: sqlite3_session -** -** Enable or disable the recording of changes by a session object. When -** enabled, a session object records changes made to the database. When -** disabled - it does not. A newly created session object is enabled. -** Refer to the documentation for [sqlite3session_changeset()] for further -** details regarding how enabling and disabling a session object affects -** the eventual changesets. -** -** Passing zero to this function disables the session. Passing a value -** greater than zero enables it. Passing a value less than zero is a -** no-op, and may be used to query the current state of the session. -** -** The return value indicates the final state of the session object: 0 if -** the session is disabled, or 1 if it is enabled. -*/ -SQLITE_API int sqlite3session_enable(sqlite3_session *pSession, int bEnable); - -/* -** CAPI3REF: Set Or Clear the Indirect Change Flag -** METHOD: sqlite3_session -** -** Each change recorded by a session object is marked as either direct or -** indirect. A change is marked as indirect if either: -** -**
      -**
    • The session object "indirect" flag is set when the change is -** made, or -**
    • The change is made by an SQL trigger or foreign key action -** instead of directly as a result of a users SQL statement. -**
    -** -** If a single row is affected by more than one operation within a session, -** then the change is considered indirect if all operations meet the criteria -** for an indirect change above, or direct otherwise. -** -** This function is used to set, clear or query the session object indirect -** flag. If the second argument passed to this function is zero, then the -** indirect flag is cleared. If it is greater than zero, the indirect flag -** is set. Passing a value less than zero does not modify the current value -** of the indirect flag, and may be used to query the current state of the -** indirect flag for the specified session object. -** -** The return value indicates the final state of the indirect flag: 0 if -** it is clear, or 1 if it is set. -*/ -SQLITE_API int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect); - -/* -** CAPI3REF: Attach A Table To A Session Object -** METHOD: sqlite3_session -** -** If argument zTab is not NULL, then it is the name of a table to attach -** to the session object passed as the first argument. All subsequent changes -** made to the table while the session object is enabled will be recorded. See -** documentation for [sqlite3session_changeset()] for further details. -** -** Or, if argument zTab is NULL, then changes are recorded for all tables -** in the database. If additional tables are added to the database (by -** executing "CREATE TABLE" statements) after this call is made, changes for -** the new tables are also recorded. -** -** Changes can only be recorded for tables that have a PRIMARY KEY explicitly -** defined as part of their CREATE TABLE statement. It does not matter if the -** PRIMARY KEY is an "INTEGER PRIMARY KEY" (rowid alias) or not. The PRIMARY -** KEY may consist of a single column, or may be a composite key. -** -** It is not an error if the named table does not exist in the database. Nor -** is it an error if the named table does not have a PRIMARY KEY. However, -** no changes will be recorded in either of these scenarios. -** -** Changes are not recorded for individual rows that have NULL values stored -** in one or more of their PRIMARY KEY columns. -** -** SQLITE_OK is returned if the call completes without error. Or, if an error -** occurs, an SQLite error code (e.g. SQLITE_NOMEM) is returned. -** -**

    Special sqlite_stat1 Handling

    -** -** As of SQLite version 3.22.0, the "sqlite_stat1" table is an exception to -** some of the rules above. In SQLite, the schema of sqlite_stat1 is: -**
    -**        CREATE TABLE sqlite_stat1(tbl,idx,stat)
    -**  
    -** -** Even though sqlite_stat1 does not have a PRIMARY KEY, changes are -** recorded for it as if the PRIMARY KEY is (tbl,idx). Additionally, changes -** are recorded for rows for which (idx IS NULL) is true. However, for such -** rows a zero-length blob (SQL value X'') is stored in the changeset or -** patchset instead of a NULL value. This allows such changesets to be -** manipulated by legacy implementations of sqlite3changeset_invert(), -** concat() and similar. -** -** The sqlite3changeset_apply() function automatically converts the -** zero-length blob back to a NULL value when updating the sqlite_stat1 -** table. However, if the application calls sqlite3changeset_new(), -** sqlite3changeset_old() or sqlite3changeset_conflict on a changeset -** iterator directly (including on a changeset iterator passed to a -** conflict-handler callback) then the X'' value is returned. The application -** must translate X'' to NULL itself if required. -** -** Legacy (older than 3.22.0) versions of the sessions module cannot capture -** changes made to the sqlite_stat1 table. Legacy versions of the -** sqlite3changeset_apply() function silently ignore any modifications to the -** sqlite_stat1 table that are part of a changeset or patchset. -*/ -SQLITE_API int sqlite3session_attach( - sqlite3_session *pSession, /* Session object */ - const char *zTab /* Table name */ -); - -/* -** CAPI3REF: Set a table filter on a Session Object. -** METHOD: sqlite3_session -** -** The second argument (xFilter) is the "filter callback". For changes to rows -** in tables that are not attached to the Session object, the filter is called -** to determine whether changes to the table's rows should be tracked or not. -** If xFilter returns 0, changes are not tracked. Note that once a table is -** attached, xFilter will not be called again. -*/ -SQLITE_API void sqlite3session_table_filter( - sqlite3_session *pSession, /* Session object */ - int(*xFilter)( - void *pCtx, /* Copy of third arg to _filter_table() */ - const char *zTab /* Table name */ - ), - void *pCtx /* First argument passed to xFilter */ -); - -/* -** CAPI3REF: Generate A Changeset From A Session Object -** METHOD: sqlite3_session -** -** Obtain a changeset containing changes to the tables attached to the -** session object passed as the first argument. If successful, -** set *ppChangeset to point to a buffer containing the changeset -** and *pnChangeset to the size of the changeset in bytes before returning -** SQLITE_OK. If an error occurs, set both *ppChangeset and *pnChangeset to -** zero and return an SQLite error code. -** -** A changeset consists of zero or more INSERT, UPDATE and/or DELETE changes, -** each representing a change to a single row of an attached table. An INSERT -** change contains the values of each field of a new database row. A DELETE -** contains the original values of each field of a deleted database row. An -** UPDATE change contains the original values of each field of an updated -** database row along with the updated values for each updated non-primary-key -** column. It is not possible for an UPDATE change to represent a change that -** modifies the values of primary key columns. If such a change is made, it -** is represented in a changeset as a DELETE followed by an INSERT. -** -** Changes are not recorded for rows that have NULL values stored in one or -** more of their PRIMARY KEY columns. If such a row is inserted or deleted, -** no corresponding change is present in the changesets returned by this -** function. If an existing row with one or more NULL values stored in -** PRIMARY KEY columns is updated so that all PRIMARY KEY columns are non-NULL, -** only an INSERT is appears in the changeset. Similarly, if an existing row -** with non-NULL PRIMARY KEY values is updated so that one or more of its -** PRIMARY KEY columns are set to NULL, the resulting changeset contains a -** DELETE change only. -** -** The contents of a changeset may be traversed using an iterator created -** using the [sqlite3changeset_start()] API. A changeset may be applied to -** a database with a compatible schema using the [sqlite3changeset_apply()] -** API. -** -** Within a changeset generated by this function, all changes related to a -** single table are grouped together. In other words, when iterating through -** a changeset or when applying a changeset to a database, all changes related -** to a single table are processed before moving on to the next table. Tables -** are sorted in the same order in which they were attached (or auto-attached) -** to the sqlite3_session object. The order in which the changes related to -** a single table are stored is undefined. -** -** Following a successful call to this function, it is the responsibility of -** the caller to eventually free the buffer that *ppChangeset points to using -** [sqlite3_free()]. -** -**

    Changeset Generation

    -** -** Once a table has been attached to a session object, the session object -** records the primary key values of all new rows inserted into the table. -** It also records the original primary key and other column values of any -** deleted or updated rows. For each unique primary key value, data is only -** recorded once - the first time a row with said primary key is inserted, -** updated or deleted in the lifetime of the session. -** -** There is one exception to the previous paragraph: when a row is inserted, -** updated or deleted, if one or more of its primary key columns contain a -** NULL value, no record of the change is made. -** -** The session object therefore accumulates two types of records - those -** that consist of primary key values only (created when the user inserts -** a new record) and those that consist of the primary key values and the -** original values of other table columns (created when the users deletes -** or updates a record). -** -** When this function is called, the requested changeset is created using -** both the accumulated records and the current contents of the database -** file. Specifically: -** -**
      -**
    • For each record generated by an insert, the database is queried -** for a row with a matching primary key. If one is found, an INSERT -** change is added to the changeset. If no such row is found, no change -** is added to the changeset. -** -**
    • For each record generated by an update or delete, the database is -** queried for a row with a matching primary key. If such a row is -** found and one or more of the non-primary key fields have been -** modified from their original values, an UPDATE change is added to -** the changeset. Or, if no such row is found in the table, a DELETE -** change is added to the changeset. If there is a row with a matching -** primary key in the database, but all fields contain their original -** values, no change is added to the changeset. -**
    -** -** This means, amongst other things, that if a row is inserted and then later -** deleted while a session object is active, neither the insert nor the delete -** will be present in the changeset. Or if a row is deleted and then later a -** row with the same primary key values inserted while a session object is -** active, the resulting changeset will contain an UPDATE change instead of -** a DELETE and an INSERT. -** -** When a session object is disabled (see the [sqlite3session_enable()] API), -** it does not accumulate records when rows are inserted, updated or deleted. -** This may appear to have some counter-intuitive effects if a single row -** is written to more than once during a session. For example, if a row -** 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. -*/ -SQLITE_API int sqlite3session_changeset( - sqlite3_session *pSession, /* Session object */ - int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */ - void **ppChangeset /* OUT: Buffer containing changeset */ -); - -/* -** CAPI3REF: Load The Difference Between Tables Into A Session -** METHOD: sqlite3_session -** -** If it is not already attached to the session object passed as the first -** argument, this function attaches table zTbl in the same manner as the -** [sqlite3session_attach()] function. If zTbl does not exist, or if it -** does not have a primary key, this function is a no-op (but does not return -** an error). -** -** Argument zFromDb must be the name of a database ("main", "temp" etc.) -** attached to the same database handle as the session object that contains -** a table compatible with the table attached to the session by this function. -** A table is considered compatible if it: -** -**
      -**
    • Has the same name, -**
    • Has the same set of columns declared in the same order, and -**
    • Has the same PRIMARY KEY definition. -**
    -** -** If the tables are not compatible, SQLITE_SCHEMA is returned. If the tables -** are compatible but do not have any PRIMARY KEY columns, it is not an error -** but no changes are added to the session object. As with other session -** APIs, tables without PRIMARY KEYs are simply ignored. -** -** This function adds a set of changes to the session object that could be -** used to update the table in database zFrom (call this the "from-table") -** so that its content is the same as the table attached to the session -** object (call this the "to-table"). Specifically: -** -**
      -**
    • For each row (primary key) that exists in the to-table but not in -** the from-table, an INSERT record is added to the session object. -** -**
    • For each row (primary key) that exists in the to-table but not in -** the from-table, a DELETE record is added to the session object. -** -**
    • For each row (primary key) that exists in both tables, but features -** different non-PK values in each, an UPDATE record is added to the -** session. -**
    -** -** To clarify, if this function is called and then a changeset constructed -** using [sqlite3session_changeset()], then after applying that changeset to -** 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. -** -** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite -** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg -** may be set to point to a buffer containing an English language error -** message. It is the responsibility of the caller to free this buffer using -** sqlite3_free(). -*/ -SQLITE_API int sqlite3session_diff( - sqlite3_session *pSession, - const char *zFromDb, - const char *zTbl, - char **pzErrMsg -); - - -/* -** CAPI3REF: Generate A Patchset From A Session Object -** METHOD: sqlite3_session -** -** The differences between a patchset and a changeset are that: -** -**
      -**
    • DELETE records consist of the primary key fields only. The -** original values of other fields are omitted. -**
    • The original values of any modified fields are omitted from -** UPDATE records. -**
    -** -** A patchset blob may be used with up to date versions of all -** sqlite3changeset_xxx API functions except for sqlite3changeset_invert(), -** which returns SQLITE_CORRUPT if it is passed a patchset. Similarly, -** attempting to use a patchset blob with old versions of the -** sqlite3changeset_xxx APIs also provokes an SQLITE_CORRUPT error. -** -** Because the non-primary key "old.*" fields are omitted, no -** SQLITE_CHANGESET_DATA conflicts can be detected or reported if a patchset -** is passed to the sqlite3changeset_apply() API. Other conflict types work -** in the same way as for changesets. -** -** Changes within a patchset are ordered in the same way as for changesets -** generated by the sqlite3session_changeset() function (i.e. all changes for -** a single table are grouped together, tables appear in the order in which -** they were attached to the session object). -*/ -SQLITE_API int sqlite3session_patchset( - sqlite3_session *pSession, /* Session object */ - int *pnPatchset, /* OUT: Size of buffer at *ppPatchset */ - void **ppPatchset /* OUT: Buffer containing patchset */ -); - -/* -** CAPI3REF: Test if a changeset has recorded any changes. -** -** Return non-zero if no changes to attached tables have been recorded by -** the session object passed as the first argument. Otherwise, if one or -** more changes have been recorded, return zero. -** -** Even if this function returns zero, it is possible that calling -** [sqlite3session_changeset()] on the session handle may still return a -** changeset that contains no changes. This can happen when a row in -** an attached table is modified and then later on the original values -** are restored. However, if this function returns non-zero, then it is -** guaranteed that a call to sqlite3session_changeset() will return a -** changeset containing zero changes. -*/ -SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession); - -/* -** CAPI3REF: Create An Iterator To Traverse A Changeset -** CONSTRUCTOR: sqlite3_changeset_iter -** -** Create an iterator used to iterate through the contents of a changeset. -** If successful, *pp is set to point to the iterator handle and SQLITE_OK -** is returned. Otherwise, if an error occurs, *pp is set to zero and an -** SQLite error code is returned. -** -** The following functions can be used to advance and query a changeset -** iterator created by this function: -** -**
      -**
    • [sqlite3changeset_next()] -**
    • [sqlite3changeset_op()] -**
    • [sqlite3changeset_new()] -**
    • [sqlite3changeset_old()] -**
    -** -** It is the responsibility of the caller to eventually destroy the iterator -** by passing it to [sqlite3changeset_finalize()]. The buffer containing the -** changeset (pChangeset) must remain valid until after the iterator is -** destroyed. -** -** Assuming the changeset blob was created by one of the -** [sqlite3session_changeset()], [sqlite3changeset_concat()] or -** [sqlite3changeset_invert()] functions, all changes within the changeset -** that apply to a single table are grouped together. This means that when -** an application iterates through a changeset using an iterator created by -** this function, all changes that relate to a single table are visited -** consecutively. There is no chance that the iterator will visit a change -** the applies to table X, then one for table Y, and then later on visit -** another change for table X. -** -** The behavior of sqlite3changeset_start_v2() and its streaming equivalent -** may be modified by passing a combination of -** [SQLITE_CHANGESETSTART_INVERT | supported flags] as the 4th parameter. -** -** Note that the sqlite3changeset_start_v2() API is still experimental -** and therefore subject to change. -*/ -SQLITE_API int sqlite3changeset_start( - sqlite3_changeset_iter **pp, /* OUT: New changeset iterator handle */ - int nChangeset, /* Size of changeset blob in bytes */ - void *pChangeset /* Pointer to blob containing changeset */ -); -SQLITE_API int sqlite3changeset_start_v2( - sqlite3_changeset_iter **pp, /* OUT: New changeset iterator handle */ - int nChangeset, /* Size of changeset blob in bytes */ - void *pChangeset, /* Pointer to blob containing changeset */ - int flags /* SESSION_CHANGESETSTART_* flags */ -); + /* + ** CAPI3REF: Session Object Handle + ** + ** An instance of this object is a [session] that can be used to + ** record changes to a database. + */ + typedef struct sqlite3_session sqlite3_session; + + /* + ** CAPI3REF: Changeset Iterator Handle + ** + ** An instance of this object acts as a cursor for iterating + ** over the elements of a [changeset] or [patchset]. + */ + typedef struct sqlite3_changeset_iter sqlite3_changeset_iter; + + /* + ** CAPI3REF: Create A New Session Object + ** CONSTRUCTOR: sqlite3_session + ** + ** Create a new session object attached to database handle db. If successful, + ** a pointer to the new object is written to *ppSession and SQLITE_OK is + ** returned. If an error occurs, *ppSession is set to NULL and an SQLite + ** error code (e.g. SQLITE_NOMEM) is returned. + ** + ** It is possible to create multiple session objects attached to a single + ** database handle. + ** + ** Session objects created using this function should be deleted using the + ** [sqlite3session_delete()] function before the database handle that they + ** are attached to is itself closed. If the database handle is closed before + ** the session object is deleted, then the results of calling any session + ** module function, including [sqlite3session_delete()] on the session object + ** are undefined. + ** + ** Because the session module uses the [sqlite3_preupdate_hook()] API, it + ** is not possible for an application to register a pre-update hook on a + ** database handle that has one or more session objects attached. Nor is + ** it possible to create a session object attached to a database handle for + ** which a pre-update hook is already defined. The results of attempting + ** either of these things are undefined. + ** + ** The session object will be used to create changesets for tables in + ** database zDb, where zDb is either "main", or "temp", or the name of an + ** attached database. It is not an error if database zDb is not attached + ** to the database when the session object is created. + */ + SQLITE_API int sqlite3session_create(sqlite3* db, /* Database handle */ + const char* zDb, /* Name of db (e.g. "main") */ + sqlite3_session** ppSession /* OUT: New session object */ + ); + + /* + ** CAPI3REF: Delete A Session Object + ** DESTRUCTOR: sqlite3_session + ** + ** Delete a session object previously allocated using + ** [sqlite3session_create()]. Once a session object has been deleted, the + ** results of attempting to use pSession with any other session module + ** function are undefined. + ** + ** Session objects must be deleted before the database handle to which they + ** are attached is closed. Refer to the documentation for + ** [sqlite3session_create()] for details. + */ + SQLITE_API void sqlite3session_delete(sqlite3_session* pSession); + + /* + ** CAPIREF: Conigure 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 value for the second parameter is + ** [SQLITE_SESSION_OBJCONFIG_SIZE]. + ** + ** Arguments for sqlite3session_object_config() + ** + ** The following values may passed as the the 4th 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_API int sqlite3session_object_config(sqlite3_session*, int op, void* pArg); + +/* + */ +#define SQLITE_SESSION_OBJCONFIG_SIZE 1 + + /* + ** CAPI3REF: Enable Or Disable A Session Object + ** METHOD: sqlite3_session + ** + ** Enable or disable the recording of changes by a session object. When + ** enabled, a session object records changes made to the database. When + ** disabled - it does not. A newly created session object is enabled. + ** Refer to the documentation for [sqlite3session_changeset()] for further + ** details regarding how enabling and disabling a session object affects + ** the eventual changesets. + ** + ** Passing zero to this function disables the session. Passing a value + ** greater than zero enables it. Passing a value less than zero is a + ** no-op, and may be used to query the current state of the session. + ** + ** The return value indicates the final state of the session object: 0 if + ** the session is disabled, or 1 if it is enabled. + */ + SQLITE_API int sqlite3session_enable(sqlite3_session* pSession, int bEnable); + + /* + ** CAPI3REF: Set Or Clear the Indirect Change Flag + ** METHOD: sqlite3_session + ** + ** Each change recorded by a session object is marked as either direct or + ** indirect. A change is marked as indirect if either: + ** + **
      + **
    • The session object "indirect" flag is set when the change is + ** made, or + **
    • The change is made by an SQL trigger or foreign key action + ** instead of directly as a result of a users SQL statement. + **
    + ** + ** If a single row is affected by more than one operation within a session, + ** then the change is considered indirect if all operations meet the criteria + ** for an indirect change above, or direct otherwise. + ** + ** This function is used to set, clear or query the session object indirect + ** flag. If the second argument passed to this function is zero, then the + ** indirect flag is cleared. If it is greater than zero, the indirect flag + ** is set. Passing a value less than zero does not modify the current value + ** of the indirect flag, and may be used to query the current state of the + ** indirect flag for the specified session object. + ** + ** The return value indicates the final state of the indirect flag: 0 if + ** it is clear, or 1 if it is set. + */ + SQLITE_API int sqlite3session_indirect(sqlite3_session* pSession, int bIndirect); + + /* + ** CAPI3REF: Attach A Table To A Session Object + ** METHOD: sqlite3_session + ** + ** If argument zTab is not NULL, then it is the name of a table to attach + ** to the session object passed as the first argument. All subsequent changes + ** made to the table while the session object is enabled will be recorded. See + ** documentation for [sqlite3session_changeset()] for further details. + ** + ** Or, if argument zTab is NULL, then changes are recorded for all tables + ** in the database. If additional tables are added to the database (by + ** executing "CREATE TABLE" statements) after this call is made, changes for + ** the new tables are also recorded. + ** + ** Changes can only be recorded for tables that have a PRIMARY KEY explicitly + ** defined as part of their CREATE TABLE statement. It does not matter if the + ** PRIMARY KEY is an "INTEGER PRIMARY KEY" (rowid alias) or not. The PRIMARY + ** KEY may consist of a single column, or may be a composite key. + ** + ** It is not an error if the named table does not exist in the database. Nor + ** is it an error if the named table does not have a PRIMARY KEY. However, + ** no changes will be recorded in either of these scenarios. + ** + ** Changes are not recorded for individual rows that have NULL values stored + ** in one or more of their PRIMARY KEY columns. + ** + ** SQLITE_OK is returned if the call completes without error. Or, if an error + ** occurs, an SQLite error code (e.g. SQLITE_NOMEM) is returned. + ** + **

    Special sqlite_stat1 Handling

    + ** + ** As of SQLite version 3.22.0, the "sqlite_stat1" table is an exception to + ** some of the rules above. In SQLite, the schema of sqlite_stat1 is: + **
    +    **        CREATE TABLE sqlite_stat1(tbl,idx,stat)
    +    **  
    + ** + ** Even though sqlite_stat1 does not have a PRIMARY KEY, changes are + ** recorded for it as if the PRIMARY KEY is (tbl,idx). Additionally, changes + ** are recorded for rows for which (idx IS NULL) is true. However, for such + ** rows a zero-length blob (SQL value X'') is stored in the changeset or + ** patchset instead of a NULL value. This allows such changesets to be + ** manipulated by legacy implementations of sqlite3changeset_invert(), + ** concat() and similar. + ** + ** The sqlite3changeset_apply() function automatically converts the + ** zero-length blob back to a NULL value when updating the sqlite_stat1 + ** table. However, if the application calls sqlite3changeset_new(), + ** sqlite3changeset_old() or sqlite3changeset_conflict on a changeset + ** iterator directly (including on a changeset iterator passed to a + ** conflict-handler callback) then the X'' value is returned. The application + ** must translate X'' to NULL itself if required. + ** + ** Legacy (older than 3.22.0) versions of the sessions module cannot capture + ** changes made to the sqlite_stat1 table. Legacy versions of the + ** sqlite3changeset_apply() function silently ignore any modifications to the + ** sqlite_stat1 table that are part of a changeset or patchset. + */ + SQLITE_API int sqlite3session_attach(sqlite3_session* pSession, /* Session object */ + const char* zTab /* Table name */ + ); + + /* + ** CAPI3REF: Set a table filter on a Session Object. + ** METHOD: sqlite3_session + ** + ** The second argument (xFilter) is the "filter callback". For changes to rows + ** in tables that are not attached to the Session object, the filter is called + ** to determine whether changes to the table's rows should be tracked or not. + ** If xFilter returns 0, changes are not tracked. Note that once a table is + ** attached, xFilter will not be called again. + */ + SQLITE_API void sqlite3session_table_filter(sqlite3_session* pSession, /* Session object */ + int (*xFilter)(void* pCtx, /* Copy of third arg to _filter_table() */ + const char* zTab /* Table name */ + ), + void* pCtx /* First argument passed to xFilter */ + ); + + /* + ** CAPI3REF: Generate A Changeset From A Session Object + ** METHOD: sqlite3_session + ** + ** Obtain a changeset containing changes to the tables attached to the + ** session object passed as the first argument. If successful, + ** set *ppChangeset to point to a buffer containing the changeset + ** and *pnChangeset to the size of the changeset in bytes before returning + ** SQLITE_OK. If an error occurs, set both *ppChangeset and *pnChangeset to + ** zero and return an SQLite error code. + ** + ** A changeset consists of zero or more INSERT, UPDATE and/or DELETE changes, + ** each representing a change to a single row of an attached table. An INSERT + ** change contains the values of each field of a new database row. A DELETE + ** contains the original values of each field of a deleted database row. An + ** UPDATE change contains the original values of each field of an updated + ** database row along with the updated values for each updated non-primary-key + ** column. It is not possible for an UPDATE change to represent a change that + ** modifies the values of primary key columns. If such a change is made, it + ** is represented in a changeset as a DELETE followed by an INSERT. + ** + ** Changes are not recorded for rows that have NULL values stored in one or + ** more of their PRIMARY KEY columns. If such a row is inserted or deleted, + ** no corresponding change is present in the changesets returned by this + ** function. If an existing row with one or more NULL values stored in + ** PRIMARY KEY columns is updated so that all PRIMARY KEY columns are non-NULL, + ** only an INSERT is appears in the changeset. Similarly, if an existing row + ** with non-NULL PRIMARY KEY values is updated so that one or more of its + ** PRIMARY KEY columns are set to NULL, the resulting changeset contains a + ** DELETE change only. + ** + ** The contents of a changeset may be traversed using an iterator created + ** using the [sqlite3changeset_start()] API. A changeset may be applied to + ** a database with a compatible schema using the [sqlite3changeset_apply()] + ** API. + ** + ** Within a changeset generated by this function, all changes related to a + ** single table are grouped together. In other words, when iterating through + ** a changeset or when applying a changeset to a database, all changes related + ** to a single table are processed before moving on to the next table. Tables + ** are sorted in the same order in which they were attached (or auto-attached) + ** to the sqlite3_session object. The order in which the changes related to + ** a single table are stored is undefined. + ** + ** Following a successful call to this function, it is the responsibility of + ** the caller to eventually free the buffer that *ppChangeset points to using + ** [sqlite3_free()]. + ** + **

    Changeset Generation

    + ** + ** Once a table has been attached to a session object, the session object + ** records the primary key values of all new rows inserted into the table. + ** It also records the original primary key and other column values of any + ** deleted or updated rows. For each unique primary key value, data is only + ** recorded once - the first time a row with said primary key is inserted, + ** updated or deleted in the lifetime of the session. + ** + ** There is one exception to the previous paragraph: when a row is inserted, + ** updated or deleted, if one or more of its primary key columns contain a + ** NULL value, no record of the change is made. + ** + ** The session object therefore accumulates two types of records - those + ** that consist of primary key values only (created when the user inserts + ** a new record) and those that consist of the primary key values and the + ** original values of other table columns (created when the users deletes + ** or updates a record). + ** + ** When this function is called, the requested changeset is created using + ** both the accumulated records and the current contents of the database + ** file. Specifically: + ** + **
      + **
    • For each record generated by an insert, the database is queried + ** for a row with a matching primary key. If one is found, an INSERT + ** change is added to the changeset. If no such row is found, no change + ** is added to the changeset. + ** + **
    • For each record generated by an update or delete, the database is + ** queried for a row with a matching primary key. If such a row is + ** found and one or more of the non-primary key fields have been + ** modified from their original values, an UPDATE change is added to + ** the changeset. Or, if no such row is found in the table, a DELETE + ** change is added to the changeset. If there is a row with a matching + ** primary key in the database, but all fields contain their original + ** values, no change is added to the changeset. + **
    + ** + ** This means, amongst other things, that if a row is inserted and then later + ** deleted while a session object is active, neither the insert nor the delete + ** will be present in the changeset. Or if a row is deleted and then later a + ** row with the same primary key values inserted while a session object is + ** active, the resulting changeset will contain an UPDATE change instead of + ** a DELETE and an INSERT. + ** + ** When a session object is disabled (see the [sqlite3session_enable()] API), + ** it does not accumulate records when rows are inserted, updated or deleted. + ** This may appear to have some counter-intuitive effects if a single row + ** is written to more than once during a session. For example, if a row + ** 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. + */ + SQLITE_API int sqlite3session_changeset(sqlite3_session* pSession, /* Session object */ + int* pnChangeset, /* OUT: Size of buffer at *ppChangeset */ + 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 + ** + ** If it is not already attached to the session object passed as the first + ** argument, this function attaches table zTbl in the same manner as the + ** [sqlite3session_attach()] function. If zTbl does not exist, or if it + ** does not have a primary key, this function is a no-op (but does not return + ** an error). + ** + ** Argument zFromDb must be the name of a database ("main", "temp" etc.) + ** attached to the same database handle as the session object that contains + ** a table compatible with the table attached to the session by this function. + ** A table is considered compatible if it: + ** + **
      + **
    • Has the same name, + **
    • Has the same set of columns declared in the same order, and + **
    • Has the same PRIMARY KEY definition. + **
    + ** + ** If the tables are not compatible, SQLITE_SCHEMA is returned. If the tables + ** are compatible but do not have any PRIMARY KEY columns, it is not an error + ** but no changes are added to the session object. As with other session + ** APIs, tables without PRIMARY KEYs are simply ignored. + ** + ** This function adds a set of changes to the session object that could be + ** used to update the table in database zFrom (call this the "from-table") + ** so that its content is the same as the table attached to the session + ** object (call this the "to-table"). Specifically: + ** + **
      + **
    • For each row (primary key) that exists in the to-table but not in + ** the from-table, an INSERT record is added to the session object. + ** + **
    • For each row (primary key) that exists in the to-table but not in + ** the from-table, a DELETE record is added to the session object. + ** + **
    • For each row (primary key) that exists in both tables, but features + ** different non-PK values in each, an UPDATE record is added to the + ** session. + **
    + ** + ** To clarify, if this function is called and then a changeset constructed + ** using [sqlite3session_changeset()], then after applying that changeset to + ** 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. + ** + ** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite + ** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg + ** may be set to point to a buffer containing an English language error + ** message. It is the responsibility of the caller to free this buffer using + ** sqlite3_free(). + */ + SQLITE_API int + sqlite3session_diff(sqlite3_session* pSession, const char* zFromDb, const char* zTbl, char** pzErrMsg); + + /* + ** CAPI3REF: Generate A Patchset From A Session Object + ** METHOD: sqlite3_session + ** + ** The differences between a patchset and a changeset are that: + ** + **
      + **
    • DELETE records consist of the primary key fields only. The + ** original values of other fields are omitted. + **
    • The original values of any modified fields are omitted from + ** UPDATE records. + **
    + ** + ** A patchset blob may be used with up to date versions of all + ** sqlite3changeset_xxx API functions except for sqlite3changeset_invert(), + ** which returns SQLITE_CORRUPT if it is passed a patchset. Similarly, + ** attempting to use a patchset blob with old versions of the + ** sqlite3changeset_xxx APIs also provokes an SQLITE_CORRUPT error. + ** + ** Because the non-primary key "old.*" fields are omitted, no + ** SQLITE_CHANGESET_DATA conflicts can be detected or reported if a patchset + ** is passed to the sqlite3changeset_apply() API. Other conflict types work + ** in the same way as for changesets. + ** + ** Changes within a patchset are ordered in the same way as for changesets + ** generated by the sqlite3session_changeset() function (i.e. all changes for + ** a single table are grouped together, tables appear in the order in which + ** they were attached to the session object). + */ + SQLITE_API int sqlite3session_patchset(sqlite3_session* pSession, /* Session object */ + int* pnPatchset, /* OUT: Size of buffer at *ppPatchset */ + void** ppPatchset /* OUT: Buffer containing patchset */ + ); + + /* + ** CAPI3REF: Test if a changeset has recorded any changes. + ** + ** Return non-zero if no changes to attached tables have been recorded by + ** the session object passed as the first argument. Otherwise, if one or + ** more changes have been recorded, return zero. + ** + ** Even if this function returns zero, it is possible that calling + ** [sqlite3session_changeset()] on the session handle may still return a + ** changeset that contains no changes. This can happen when a row in + ** an attached table is modified and then later on the original values + ** are restored. However, if this function returns non-zero, then it is + ** guaranteed that a call to sqlite3session_changeset() will return a + ** changeset containing zero changes. + */ + 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 + ** + ** Create an iterator used to iterate through the contents of a changeset. + ** If successful, *pp is set to point to the iterator handle and SQLITE_OK + ** is returned. Otherwise, if an error occurs, *pp is set to zero and an + ** SQLite error code is returned. + ** + ** The following functions can be used to advance and query a changeset + ** iterator created by this function: + ** + **
      + **
    • [sqlite3changeset_next()] + **
    • [sqlite3changeset_op()] + **
    • [sqlite3changeset_new()] + **
    • [sqlite3changeset_old()] + **
    + ** + ** It is the responsibility of the caller to eventually destroy the iterator + ** by passing it to [sqlite3changeset_finalize()]. The buffer containing the + ** changeset (pChangeset) must remain valid until after the iterator is + ** destroyed. + ** + ** Assuming the changeset blob was created by one of the + ** [sqlite3session_changeset()], [sqlite3changeset_concat()] or + ** [sqlite3changeset_invert()] functions, all changes within the changeset + ** that apply to a single table are grouped together. This means that when + ** an application iterates through a changeset using an iterator created by + ** this function, all changes that relate to a single table are visited + ** consecutively. There is no chance that the iterator will visit a change + ** the applies to table X, then one for table Y, and then later on visit + ** another change for table X. + ** + ** The behavior of sqlite3changeset_start_v2() and its streaming equivalent + ** may be modified by passing a combination of + ** [SQLITE_CHANGESETSTART_INVERT | supported flags] as the 4th parameter. + ** + ** Note that the sqlite3changeset_start_v2() API is still experimental + ** and therefore subject to change. + */ + SQLITE_API int sqlite3changeset_start(sqlite3_changeset_iter** pp, /* OUT: New changeset iterator handle */ + int nChangeset, /* Size of changeset blob in bytes */ + void* pChangeset /* Pointer to blob containing changeset */ + ); + SQLITE_API int sqlite3changeset_start_v2(sqlite3_changeset_iter** pp, /* OUT: New changeset iterator handle */ + int nChangeset, /* Size of changeset blob in bytes */ + void* pChangeset, /* Pointer to blob containing changeset */ + int flags /* SESSION_CHANGESETSTART_* flags */ + ); /* ** CAPI3REF: Flags for sqlite3changeset_start_v2 @@ -10439,663 +10959,655 @@ ** inverting a changeset using sqlite3changeset_invert() before applying it. ** It is an error to specify this flag with a patchset. */ -#define SQLITE_CHANGESETSTART_INVERT 0x0002 - - -/* -** CAPI3REF: Advance A Changeset Iterator -** METHOD: sqlite3_changeset_iter -** -** This function may only be used with iterators created by the function -** [sqlite3changeset_start()]. If it is called on an iterator passed to -** a conflict-handler callback by [sqlite3changeset_apply()], SQLITE_MISUSE -** is returned and the call has no effect. -** -** Immediately after an iterator is created by sqlite3changeset_start(), it -** does not point to any change in the changeset. Assuming the changeset -** is not empty, the first call to this function advances the iterator to -** point to the first change in the changeset. Each subsequent call advances -** the iterator to point to the next change in the changeset (if any). If -** no error occurs and the iterator points to a valid change after a call -** to sqlite3changeset_next() has advanced it, SQLITE_ROW is returned. -** Otherwise, if all changes in the changeset have already been visited, -** SQLITE_DONE is returned. -** -** If an error occurs, an SQLite error code is returned. Possible error -** codes include SQLITE_CORRUPT (if the changeset buffer is corrupt) or -** SQLITE_NOMEM. -*/ -SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter *pIter); - -/* -** CAPI3REF: Obtain The Current Operation From A Changeset Iterator -** METHOD: sqlite3_changeset_iter -** -** The pIter argument passed to this function may either be an iterator -** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator -** created by [sqlite3changeset_start()]. In the latter case, the most recent -** 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 -** 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. -** -** 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 -** be trusted in this case. -*/ -SQLITE_API int sqlite3changeset_op( - sqlite3_changeset_iter *pIter, /* Iterator object */ - const char **pzTab, /* OUT: Pointer to table name */ - int *pnCol, /* OUT: Number of columns in table */ - int *pOp, /* OUT: SQLITE_INSERT, DELETE or UPDATE */ - int *pbIndirect /* OUT: True for an 'indirect' change */ -); +#define SQLITE_CHANGESETSTART_INVERT 0x0002 -/* -** CAPI3REF: Obtain The Primary Key Definition Of A Table -** METHOD: sqlite3_changeset_iter -** -** For each modified table, a changeset includes the following: -** -**
      -**
    • The number of columns in the table, and -**
    • Which of those columns make up the tables PRIMARY KEY. -**
    -** -** This function is used to find which columns comprise the PRIMARY KEY of -** the table modified by the change that iterator pIter currently points to. -** If successful, *pabPK is set to point to an array of nCol entries, where -** nCol is the number of columns in the table. Elements of *pabPK are set to -** 0x01 if the corresponding column is part of the tables primary key, or -** 0x00 if it is not. -** -** If argument pnCol is not NULL, then *pnCol is set to the number of columns -** in the table. -** -** If this function is called when the iterator does not point to a valid -** entry, SQLITE_MISUSE is returned and the output variables zeroed. Otherwise, -** SQLITE_OK is returned and the output variables populated as described -** above. -*/ -SQLITE_API int sqlite3changeset_pk( - sqlite3_changeset_iter *pIter, /* Iterator object */ - unsigned char **pabPK, /* OUT: Array of boolean - true for PK cols */ - int *pnCol /* OUT: Number of entries in output array */ -); - -/* -** CAPI3REF: Obtain old.* Values From A Changeset Iterator -** METHOD: sqlite3_changeset_iter -** -** The pIter argument passed to this function may either be an iterator -** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator -** created by [sqlite3changeset_start()]. In the latter case, the most recent -** call to [sqlite3changeset_next()] must have returned SQLITE_ROW. -** Furthermore, it may only be called if the type of change that the iterator -** currently points to is either [SQLITE_DELETE] or [SQLITE_UPDATE]. Otherwise, -** this function returns [SQLITE_MISUSE] and sets *ppValue to NULL. -** -** Argument iVal must be greater than or equal to 0, and less than the number -** of columns in the table affected by the current change. Otherwise, -** [SQLITE_RANGE] is returned and *ppValue is set to NULL. -** -** If successful, this function sets *ppValue to point to a protected -** sqlite3_value object containing the iVal'th value from the vector of -** original row values stored as part of the UPDATE or DELETE change and -** returns SQLITE_OK. The name of the function comes from the fact that this -** is similar to the "old.*" columns available to update or delete triggers. -** -** If some other error occurs (e.g. an OOM condition), an SQLite error code -** is returned and *ppValue is set to NULL. -*/ -SQLITE_API int sqlite3changeset_old( - sqlite3_changeset_iter *pIter, /* Changeset iterator */ - int iVal, /* Column number */ - sqlite3_value **ppValue /* OUT: Old value (or NULL pointer) */ -); - -/* -** CAPI3REF: Obtain new.* Values From A Changeset Iterator -** METHOD: sqlite3_changeset_iter -** -** The pIter argument passed to this function may either be an iterator -** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator -** created by [sqlite3changeset_start()]. In the latter case, the most recent -** call to [sqlite3changeset_next()] must have returned SQLITE_ROW. -** Furthermore, it may only be called if the type of change that the iterator -** currently points to is either [SQLITE_UPDATE] or [SQLITE_INSERT]. Otherwise, -** this function returns [SQLITE_MISUSE] and sets *ppValue to NULL. -** -** Argument iVal must be greater than or equal to 0, and less than the number -** of columns in the table affected by the current change. Otherwise, -** [SQLITE_RANGE] is returned and *ppValue is set to NULL. -** -** If successful, this function sets *ppValue to point to a protected -** sqlite3_value object containing the iVal'th value from the vector of -** new row values stored as part of the UPDATE or INSERT change and -** returns SQLITE_OK. If the change is an UPDATE and does not include -** a new value for the requested column, *ppValue is set to NULL and -** SQLITE_OK returned. The name of the function comes from the fact that -** this is similar to the "new.*" columns available to update or delete -** triggers. -** -** If some other error occurs (e.g. an OOM condition), an SQLite error code -** is returned and *ppValue is set to NULL. -*/ -SQLITE_API int sqlite3changeset_new( - sqlite3_changeset_iter *pIter, /* Changeset iterator */ - int iVal, /* Column number */ - sqlite3_value **ppValue /* OUT: New value (or NULL pointer) */ -); - -/* -** CAPI3REF: Obtain Conflicting Row Values From A Changeset Iterator -** METHOD: sqlite3_changeset_iter -** -** This function should only be used with iterator objects passed to a -** conflict-handler callback by [sqlite3changeset_apply()] with either -** [SQLITE_CHANGESET_DATA] or [SQLITE_CHANGESET_CONFLICT]. If this function -** is called on any other iterator, [SQLITE_MISUSE] is returned and *ppValue -** is set to NULL. -** -** Argument iVal must be greater than or equal to 0, and less than the number -** of columns in the table affected by the current change. Otherwise, -** [SQLITE_RANGE] is returned and *ppValue is set to NULL. -** -** If successful, this function sets *ppValue to point to a protected -** sqlite3_value object containing the iVal'th value from the -** "conflicting row" associated with the current conflict-handler callback -** and returns SQLITE_OK. -** -** If some other error occurs (e.g. an OOM condition), an SQLite error code -** is returned and *ppValue is set to NULL. -*/ -SQLITE_API int sqlite3changeset_conflict( - sqlite3_changeset_iter *pIter, /* Changeset iterator */ - int iVal, /* Column number */ - sqlite3_value **ppValue /* OUT: Value from conflicting row */ -); - -/* -** CAPI3REF: Determine The Number Of Foreign Key Constraint Violations -** METHOD: sqlite3_changeset_iter -** -** This function may only be called with an iterator passed to an -** SQLITE_CHANGESET_FOREIGN_KEY conflict handler callback. In this case -** it sets the output variable to the total number of known foreign key -** violations in the destination database and returns SQLITE_OK. -** -** In all other cases this function returns SQLITE_MISUSE. -*/ -SQLITE_API int sqlite3changeset_fk_conflicts( - sqlite3_changeset_iter *pIter, /* Changeset iterator */ - int *pnOut /* OUT: Number of FK violations */ -); - - -/* -** CAPI3REF: Finalize A Changeset Iterator -** METHOD: sqlite3_changeset_iter -** -** This function is used to finalize an iterator allocated with -** [sqlite3changeset_start()]. -** -** This function should only be called on iterators created using the -** [sqlite3changeset_start()] function. If an application calls this -** function with an iterator passed to a conflict-handler by -** [sqlite3changeset_apply()], [SQLITE_MISUSE] is immediately returned and the -** call has no effect. -** -** If an error was encountered within a call to an sqlite3changeset_xxx() -** function (for example an [SQLITE_CORRUPT] in [sqlite3changeset_next()] or an -** [SQLITE_NOMEM] in [sqlite3changeset_new()]) then an error code corresponding -** to that error is returned by this function. Otherwise, SQLITE_OK is -** returned. This is to allow the following pattern (pseudo-code): -** -**
    -**   sqlite3changeset_start();
    -**   while( SQLITE_ROW==sqlite3changeset_next() ){
    -**     // Do something with change.
    -**   }
    -**   rc = sqlite3changeset_finalize();
    -**   if( rc!=SQLITE_OK ){
    -**     // An error has occurred
    -**   }
    -** 
    -*/ -SQLITE_API int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter); - -/* -** CAPI3REF: Invert A Changeset -** -** This function is used to "invert" a changeset object. Applying an inverted -** changeset to a database reverses the effects of applying the uninverted -** changeset. Specifically: -** -**
      -**
    • Each DELETE change is changed to an INSERT, and -**
    • Each INSERT change is changed to a DELETE, and -**
    • For each UPDATE change, the old.* and new.* values are exchanged. -**
    -** -** This function does not change the order in which changes appear within -** the changeset. It merely reverses the sense of each individual change. -** -** If successful, a pointer to a buffer containing the inverted changeset -** is stored in *ppOut, the size of the same buffer is stored in *pnOut, and -** SQLITE_OK is returned. If an error occurs, both *pnOut and *ppOut are -** zeroed and an SQLite error code returned. -** -** It is the responsibility of the caller to eventually call sqlite3_free() -** on the *ppOut pointer to free the buffer allocation following a successful -** call to this function. -** -** WARNING/TODO: This function currently assumes that the input is a valid -** changeset. If it is not, the results are undefined. -*/ -SQLITE_API int sqlite3changeset_invert( - int nIn, const void *pIn, /* Input changeset */ - int *pnOut, void **ppOut /* OUT: Inverse of input */ -); - -/* -** CAPI3REF: Concatenate Two Changeset Objects -** -** This function is used to concatenate two changesets, A and B, into a -** single changeset. The result is a changeset equivalent to applying -** changeset A followed by changeset B. -** -** This function combines the two input changesets using an -** sqlite3_changegroup object. Calling it produces similar results as the -** following code fragment: -** -**
    -**   sqlite3_changegroup *pGrp;
    -**   rc = sqlite3_changegroup_new(&pGrp);
    -**   if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nA, pA);
    -**   if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nB, pB);
    -**   if( rc==SQLITE_OK ){
    -**     rc = sqlite3changegroup_output(pGrp, pnOut, ppOut);
    -**   }else{
    -**     *ppOut = 0;
    -**     *pnOut = 0;
    -**   }
    -** 
    -** -** Refer to the sqlite3_changegroup documentation below for details. -*/ -SQLITE_API int sqlite3changeset_concat( - int nA, /* Number of bytes in buffer pA */ - void *pA, /* Pointer to buffer containing changeset A */ - int nB, /* Number of bytes in buffer pB */ - void *pB, /* Pointer to buffer containing changeset B */ - int *pnOut, /* OUT: Number of bytes in output changeset */ - void **ppOut /* OUT: Buffer containing output changeset */ -); - - -/* -** CAPI3REF: Changegroup Handle -** -** A changegroup is an object used to combine two or more -** [changesets] or [patchsets] -*/ -typedef struct sqlite3_changegroup sqlite3_changegroup; - -/* -** CAPI3REF: Create A New Changegroup Object -** CONSTRUCTOR: sqlite3_changegroup -** -** An sqlite3_changegroup object is used to combine two or more changesets -** (or patchsets) into a single changeset (or patchset). A single changegroup -** object may combine changesets or patchsets, but not both. The output is -** always in the same format as the input. -** -** If successful, this function returns SQLITE_OK and populates (*pp) with -** a pointer to a new sqlite3_changegroup object before returning. The caller -** should eventually free the returned object using a call to -** sqlite3changegroup_delete(). If an error occurs, an SQLite error code -** (i.e. SQLITE_NOMEM) is returned and *pp is set to NULL. -** -** The usual usage pattern for an sqlite3_changegroup object is as follows: -** -**
      -**
    • It is created using a call to sqlite3changegroup_new(). -** -**
    • Zero or more changesets (or patchsets) are added to the object -** by calling sqlite3changegroup_add(). -** -**
    • The result of combining all input changesets together is obtained -** by the application via a call to sqlite3changegroup_output(). -** -**
    • The object is deleted using a call to sqlite3changegroup_delete(). -**
    -** -** Any number of calls to add() and output() may be made between the calls to -** new() and delete(), and in any order. -** -** As well as the regular sqlite3changegroup_add() and -** sqlite3changegroup_output() functions, also available are the streaming -** versions sqlite3changegroup_add_strm() and sqlite3changegroup_output_strm(). -*/ -SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp); - -/* -** CAPI3REF: Add A Changeset To A Changegroup -** METHOD: sqlite3_changegroup -** -** Add all changes within the changeset (or patchset) in buffer pData (size -** nData bytes) to the changegroup. -** -** If the buffer contains a patchset, then all prior calls to this function -** on the same changegroup object must also have specified patchsets. Or, if -** the buffer contains a changeset, so must have the earlier calls to this -** function. Otherwise, SQLITE_ERROR is returned and no changes are added -** to the changegroup. -** -** Rows within the changeset and changegroup are identified by the values in -** their PRIMARY KEY columns. A change in the changeset is considered to -** apply to the same row as a change already present in the changegroup if -** the two rows have the same primary key. -** -** Changes to rows that do not already appear in the changegroup are -** simply copied into it. Or, if both the new changeset and the changegroup -** contain changes that apply to a single row, the final contents of the -** changegroup depends on the type of each change, as follows: -** -** -** -** -**
    Existing Change New Change Output Change -**
    INSERT INSERT -** The new change is ignored. This case does not occur if the new -** changeset was recorded immediately after the changesets already -** added to the changegroup. -**
    INSERT UPDATE -** The INSERT change remains in the changegroup. The values in the -** INSERT change are modified as if the row was inserted by the -** existing change and then updated according to the new change. -**
    INSERT DELETE -** The existing INSERT is removed from the changegroup. The DELETE is -** not added. -**
    UPDATE INSERT -** The new change is ignored. This case does not occur if the new -** changeset was recorded immediately after the changesets already -** added to the changegroup. -**
    UPDATE UPDATE -** The existing UPDATE remains within the changegroup. It is amended -** so that the accompanying values are as if the row was updated once -** by the existing change and then again by the new change. -**
    UPDATE DELETE -** The existing UPDATE is replaced by the new DELETE within the -** changegroup. -**
    DELETE INSERT -** If one or more of the column values in the row inserted by the -** new change differ from those in the row deleted by the existing -** change, the existing DELETE is replaced by an UPDATE within the -** changegroup. Otherwise, if the inserted row is exactly the same -** as the deleted row, the existing DELETE is simply discarded. -**
    DELETE UPDATE -** The new change is ignored. This case does not occur if the new -** changeset was recorded immediately after the changesets already -** added to the changegroup. -**
    DELETE DELETE -** The new change is ignored. This case does not occur if the new -** changeset was recorded immediately after the changesets already -** added to the changegroup. -**
    -** -** 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. -** -** If no error occurs, SQLITE_OK is returned. -*/ -SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); - -/* -** CAPI3REF: Obtain A Composite Changeset From A Changegroup -** METHOD: sqlite3_changegroup -** -** Obtain a buffer containing a changeset (or patchset) representing the -** current contents of the changegroup. If the inputs to the changegroup -** were themselves changesets, the output is a changeset. Or, if the -** inputs were patchsets, the output is also a patchset. -** -** As with the output of the sqlite3session_changeset() and -** sqlite3session_patchset() functions, all changes related to a single -** table are grouped together in the output of this function. Tables appear -** in the same order as for the very first changeset added to the changegroup. -** If the second or subsequent changesets added to the changegroup contain -** changes for tables that do not appear in the first changeset, they are -** appended onto the end of the output changeset, again in the order in -** which they are first encountered. -** -** If an error occurs, an SQLite error code is returned and the output -** variables (*pnData) and (*ppData) are set to 0. Otherwise, SQLITE_OK -** is returned and the output variables are set to the size of and a -** pointer to the output buffer, respectively. In this case it is the -** responsibility of the caller to eventually free the buffer using a -** call to sqlite3_free(). -*/ -SQLITE_API int sqlite3changegroup_output( - sqlite3_changegroup*, - int *pnData, /* OUT: Size of output buffer in bytes */ - void **ppData /* OUT: Pointer to output buffer */ -); - -/* -** CAPI3REF: Delete A Changegroup Object -** DESTRUCTOR: sqlite3_changegroup -*/ -SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*); - -/* -** CAPI3REF: Apply A Changeset To A Database -** -** Apply a changeset or patchset to a database. These functions attempt to -** update the "main" database attached to handle db with the changes found in -** the changeset passed via the second and third arguments. -** -** The fourth argument (xFilter) passed to these functions is the "filter -** callback". If it is not NULL, then for each table affected by at least one -** change in the changeset, the filter callback is invoked with -** the table name as the second argument, and a copy of the context pointer -** passed as the sixth argument as the first. If the "filter callback" -** returns zero, then no attempt is made to apply any changes to the table. -** Otherwise, if the return value is non-zero or the xFilter argument to -** is NULL, all changes related to the table are attempted. -** -** For each table that is not excluded by the filter callback, this function -** tests that the target database contains a compatible table. A table is -** considered compatible if all of the following are true: -** -**
      -**
    • The table has the same name as the name recorded in the -** changeset, and -**
    • The table has at least as many columns as recorded in the -** changeset, and -**
    • The table has primary key columns in the same position as -** recorded in the changeset. -**
    -** -** If there is no compatible table, it is not an error, but none of the -** changes associated with the table are applied. A warning message is issued -** via the sqlite3_log() mechanism with the error code SQLITE_SCHEMA. At most -** one such warning is issued for each table in the changeset. -** -** For each change for which there is a compatible table, an attempt is made -** to modify the table contents according to the UPDATE, INSERT or DELETE -** change. If a change cannot be applied cleanly, the conflict handler -** function passed as the fifth argument to sqlite3changeset_apply() may be -** invoked. A description of exactly when the conflict handler is invoked for -** each type of change is below. -** -** Unlike the xFilter argument, xConflict may not be passed NULL. The results -** of passing anything other than a valid function pointer as the xConflict -** argument are undefined. -** -** Each time the conflict handler function is invoked, it must return one -** of [SQLITE_CHANGESET_OMIT], [SQLITE_CHANGESET_ABORT] or -** [SQLITE_CHANGESET_REPLACE]. SQLITE_CHANGESET_REPLACE may only be returned -** if the second argument passed to the conflict handler is either -** SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT. If the conflict-handler -** returns an illegal value, any changes already made are rolled back and -** the call to sqlite3changeset_apply() returns SQLITE_MISUSE. Different -** actions are taken by sqlite3changeset_apply() depending on the value -** returned by each invocation of the conflict-handler function. Refer to -** the documentation for the three -** [SQLITE_CHANGESET_OMIT|available return values] for details. -** -**
    -**
    DELETE Changes
    -** For each DELETE change, the function checks if the target database -** contains a row with the same primary key value (or values) as the -** original row values stored in the changeset. If it does, and the values -** stored in all non-primary key columns also match the values stored in -** the changeset the row is deleted from the target database. -** -** If a row with matching primary key values is found, but one or more of -** the non-primary key fields contains a value different from the original -** row value stored in the changeset, the conflict-handler function is -** invoked with [SQLITE_CHANGESET_DATA] as the second argument. If the -** database table has more columns than are recorded in the changeset, -** only the values of those non-primary key fields are compared against -** the current database contents - any trailing database table columns -** are ignored. -** -** If no row with matching primary key values is found in the database, -** the conflict-handler function is invoked with [SQLITE_CHANGESET_NOTFOUND] -** passed as the second argument. -** -** If the DELETE operation is attempted, but SQLite returns SQLITE_CONSTRAINT -** (which can only happen if a foreign key constraint is violated), the -** conflict-handler function is invoked with [SQLITE_CHANGESET_CONSTRAINT] -** passed as the second argument. This includes the case where the DELETE -** operation is attempted because an earlier call to the conflict handler -** function returned [SQLITE_CHANGESET_REPLACE]. -** -**
    INSERT Changes
    -** For each INSERT change, an attempt is made to insert the new row into -** the database. If the changeset row contains fewer fields than the -** database table, the trailing fields are populated with their default -** values. -** -** If the attempt to insert the row fails because the database already -** contains a row with the same primary key values, the conflict handler -** function is invoked with the second argument set to -** [SQLITE_CHANGESET_CONFLICT]. -** -** If the attempt to insert the row fails because of some other constraint -** violation (e.g. NOT NULL or UNIQUE), the conflict handler function is -** invoked with the second argument set to [SQLITE_CHANGESET_CONSTRAINT]. -** This includes the case where the INSERT operation is re-attempted because -** an earlier call to the conflict handler function returned -** [SQLITE_CHANGESET_REPLACE]. -** -**
    UPDATE Changes
    -** For each UPDATE change, the function checks if the target database -** contains a row with the same primary key value (or values) as the -** original row values stored in the changeset. If it does, and the values -** stored in all modified non-primary key columns also match the values -** stored in the changeset the row is updated within the target database. -** -** If a row with matching primary key values is found, but one or more of -** the modified non-primary key fields contains a value different from an -** original row value stored in the changeset, the conflict-handler function -** is invoked with [SQLITE_CHANGESET_DATA] as the second argument. Since -** UPDATE changes only contain values for non-primary key fields that are -** to be modified, only those fields need to match the original values to -** avoid the SQLITE_CHANGESET_DATA conflict-handler callback. -** -** If no row with matching primary key values is found in the database, -** the conflict-handler function is invoked with [SQLITE_CHANGESET_NOTFOUND] -** passed as the second argument. -** -** If the UPDATE operation is attempted, but SQLite returns -** SQLITE_CONSTRAINT, the conflict-handler function is invoked with -** [SQLITE_CHANGESET_CONSTRAINT] passed as the second argument. -** This includes the case where the UPDATE operation is attempted after -** an earlier call to the conflict handler function returned -** [SQLITE_CHANGESET_REPLACE]. -**
    -** -** It is safe to execute SQL statements, including those that write to the -** table that the callback related to, from within the xConflict callback. -** This can be used to further customize the application's conflict -** resolution strategy. -** -** All changes made by these functions are enclosed in a savepoint transaction. -** If any other error (aside from a constraint failure when attempting to -** write to the target database) occurs, then the savepoint transaction is -** rolled back, restoring the target database to its original state, and an -** SQLite error code returned. -** -** If the output parameters (ppRebase) and (pnRebase) are non-NULL and -** the input is a changeset (not a patchset), then sqlite3changeset_apply_v2() -** may set (*ppRebase) to point to a "rebase" that may be used with the -** sqlite3_rebaser APIs buffer before returning. In this case (*pnRebase) -** is set to the size of the buffer in bytes. It is the responsibility of the -** caller to eventually free any such buffer using sqlite3_free(). The buffer -** is only allocated and populated if one or more conflicts were encountered -** while applying the patchset. See comments surrounding the sqlite3_rebaser -** APIs for further details. -** -** The behavior of sqlite3changeset_apply_v2() and its streaming equivalent -** may be modified by passing a combination of -** [SQLITE_CHANGESETAPPLY_NOSAVEPOINT | supported flags] as the 9th parameter. -** -** Note that the sqlite3changeset_apply_v2() API is still experimental -** and therefore subject to change. -*/ -SQLITE_API int sqlite3changeset_apply( - sqlite3 *db, /* Apply change to "main" db of this handle */ - int nChangeset, /* Size of changeset in bytes */ - void *pChangeset, /* Changeset blob */ - int(*xFilter)( - void *pCtx, /* Copy of sixth arg to _apply() */ - const char *zTab /* Table name */ - ), - int(*xConflict)( - void *pCtx, /* Copy of sixth arg to _apply() */ - int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ - sqlite3_changeset_iter *p /* Handle describing change and conflict */ - ), - void *pCtx /* First argument passed to xConflict */ -); -SQLITE_API int sqlite3changeset_apply_v2( - sqlite3 *db, /* Apply change to "main" db of this handle */ - int nChangeset, /* Size of changeset in bytes */ - void *pChangeset, /* Changeset blob */ - int(*xFilter)( - void *pCtx, /* Copy of sixth arg to _apply() */ - const char *zTab /* Table name */ - ), - int(*xConflict)( - void *pCtx, /* Copy of sixth arg to _apply() */ - int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ - sqlite3_changeset_iter *p /* Handle describing change and conflict */ - ), - void *pCtx, /* First argument passed to xConflict */ - void **ppRebase, int *pnRebase, /* OUT: Rebase data */ - int flags /* SESSION_CHANGESETAPPLY_* flags */ -); + /* + ** CAPI3REF: Advance A Changeset Iterator + ** METHOD: sqlite3_changeset_iter + ** + ** This function may only be used with iterators created by the function + ** [sqlite3changeset_start()]. If it is called on an iterator passed to + ** a conflict-handler callback by [sqlite3changeset_apply()], SQLITE_MISUSE + ** is returned and the call has no effect. + ** + ** Immediately after an iterator is created by sqlite3changeset_start(), it + ** does not point to any change in the changeset. Assuming the changeset + ** is not empty, the first call to this function advances the iterator to + ** point to the first change in the changeset. Each subsequent call advances + ** the iterator to point to the next change in the changeset (if any). If + ** no error occurs and the iterator points to a valid change after a call + ** to sqlite3changeset_next() has advanced it, SQLITE_ROW is returned. + ** Otherwise, if all changes in the changeset have already been visited, + ** SQLITE_DONE is returned. + ** + ** If an error occurs, an SQLite error code is returned. Possible error + ** codes include SQLITE_CORRUPT (if the changeset buffer is corrupt) or + ** SQLITE_NOMEM. + */ + SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter* pIter); + + /* + ** CAPI3REF: Obtain The Current Operation From A Changeset Iterator + ** METHOD: sqlite3_changeset_iter + ** + ** The pIter argument passed to this function may either be an iterator + ** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator + ** created by [sqlite3changeset_start()]. In the latter case, the most recent + ** call to [sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this + ** is not the case, this function returns [SQLITE_MISUSE]. + ** + ** 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. + ** + ** 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 + ** be trusted in this case. + */ + SQLITE_API int sqlite3changeset_op(sqlite3_changeset_iter* pIter, /* Iterator object */ + const char** pzTab, /* OUT: Pointer to table name */ + int* pnCol, /* OUT: Number of columns in table */ + int* pOp, /* OUT: SQLITE_INSERT, DELETE or UPDATE */ + int* pbIndirect /* OUT: True for an 'indirect' change */ + ); + + /* + ** CAPI3REF: Obtain The Primary Key Definition Of A Table + ** METHOD: sqlite3_changeset_iter + ** + ** For each modified table, a changeset includes the following: + ** + **
      + **
    • The number of columns in the table, and + **
    • Which of those columns make up the tables PRIMARY KEY. + **
    + ** + ** This function is used to find which columns comprise the PRIMARY KEY of + ** the table modified by the change that iterator pIter currently points to. + ** If successful, *pabPK is set to point to an array of nCol entries, where + ** nCol is the number of columns in the table. Elements of *pabPK are set to + ** 0x01 if the corresponding column is part of the tables primary key, or + ** 0x00 if it is not. + ** + ** If argument pnCol is not NULL, then *pnCol is set to the number of columns + ** in the table. + ** + ** If this function is called when the iterator does not point to a valid + ** entry, SQLITE_MISUSE is returned and the output variables zeroed. Otherwise, + ** SQLITE_OK is returned and the output variables populated as described + ** above. + */ + SQLITE_API int sqlite3changeset_pk(sqlite3_changeset_iter* pIter, /* Iterator object */ + unsigned char** pabPK, /* OUT: Array of boolean - true for PK cols */ + int* pnCol /* OUT: Number of entries in output array */ + ); + + /* + ** CAPI3REF: Obtain old.* Values From A Changeset Iterator + ** METHOD: sqlite3_changeset_iter + ** + ** The pIter argument passed to this function may either be an iterator + ** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator + ** created by [sqlite3changeset_start()]. In the latter case, the most recent + ** call to [sqlite3changeset_next()] must have returned SQLITE_ROW. + ** Furthermore, it may only be called if the type of change that the iterator + ** currently points to is either [SQLITE_DELETE] or [SQLITE_UPDATE]. Otherwise, + ** this function returns [SQLITE_MISUSE] and sets *ppValue to NULL. + ** + ** Argument iVal must be greater than or equal to 0, and less than the number + ** of columns in the table affected by the current change. Otherwise, + ** [SQLITE_RANGE] is returned and *ppValue is set to NULL. + ** + ** If successful, this function sets *ppValue to point to a protected + ** sqlite3_value object containing the iVal'th value from the vector of + ** original row values stored as part of the UPDATE or DELETE change and + ** returns SQLITE_OK. The name of the function comes from the fact that this + ** is similar to the "old.*" columns available to update or delete triggers. + ** + ** If some other error occurs (e.g. an OOM condition), an SQLite error code + ** is returned and *ppValue is set to NULL. + */ + SQLITE_API int sqlite3changeset_old(sqlite3_changeset_iter* pIter, /* Changeset iterator */ + int iVal, /* Column number */ + sqlite3_value** ppValue /* OUT: Old value (or NULL pointer) */ + ); + + /* + ** CAPI3REF: Obtain new.* Values From A Changeset Iterator + ** METHOD: sqlite3_changeset_iter + ** + ** The pIter argument passed to this function may either be an iterator + ** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator + ** created by [sqlite3changeset_start()]. In the latter case, the most recent + ** call to [sqlite3changeset_next()] must have returned SQLITE_ROW. + ** Furthermore, it may only be called if the type of change that the iterator + ** currently points to is either [SQLITE_UPDATE] or [SQLITE_INSERT]. Otherwise, + ** this function returns [SQLITE_MISUSE] and sets *ppValue to NULL. + ** + ** Argument iVal must be greater than or equal to 0, and less than the number + ** of columns in the table affected by the current change. Otherwise, + ** [SQLITE_RANGE] is returned and *ppValue is set to NULL. + ** + ** If successful, this function sets *ppValue to point to a protected + ** sqlite3_value object containing the iVal'th value from the vector of + ** new row values stored as part of the UPDATE or INSERT change and + ** returns SQLITE_OK. If the change is an UPDATE and does not include + ** a new value for the requested column, *ppValue is set to NULL and + ** SQLITE_OK returned. The name of the function comes from the fact that + ** this is similar to the "new.*" columns available to update or delete + ** triggers. + ** + ** If some other error occurs (e.g. an OOM condition), an SQLite error code + ** is returned and *ppValue is set to NULL. + */ + SQLITE_API int sqlite3changeset_new(sqlite3_changeset_iter* pIter, /* Changeset iterator */ + int iVal, /* Column number */ + sqlite3_value** ppValue /* OUT: New value (or NULL pointer) */ + ); + + /* + ** CAPI3REF: Obtain Conflicting Row Values From A Changeset Iterator + ** METHOD: sqlite3_changeset_iter + ** + ** This function should only be used with iterator objects passed to a + ** conflict-handler callback by [sqlite3changeset_apply()] with either + ** [SQLITE_CHANGESET_DATA] or [SQLITE_CHANGESET_CONFLICT]. If this function + ** is called on any other iterator, [SQLITE_MISUSE] is returned and *ppValue + ** is set to NULL. + ** + ** Argument iVal must be greater than or equal to 0, and less than the number + ** of columns in the table affected by the current change. Otherwise, + ** [SQLITE_RANGE] is returned and *ppValue is set to NULL. + ** + ** If successful, this function sets *ppValue to point to a protected + ** sqlite3_value object containing the iVal'th value from the + ** "conflicting row" associated with the current conflict-handler callback + ** and returns SQLITE_OK. + ** + ** If some other error occurs (e.g. an OOM condition), an SQLite error code + ** is returned and *ppValue is set to NULL. + */ + SQLITE_API int sqlite3changeset_conflict(sqlite3_changeset_iter* pIter, /* Changeset iterator */ + int iVal, /* Column number */ + sqlite3_value** ppValue /* OUT: Value from conflicting row */ + ); + + /* + ** CAPI3REF: Determine The Number Of Foreign Key Constraint Violations + ** METHOD: sqlite3_changeset_iter + ** + ** This function may only be called with an iterator passed to an + ** SQLITE_CHANGESET_FOREIGN_KEY conflict handler callback. In this case + ** it sets the output variable to the total number of known foreign key + ** violations in the destination database and returns SQLITE_OK. + ** + ** In all other cases this function returns SQLITE_MISUSE. + */ + SQLITE_API int sqlite3changeset_fk_conflicts(sqlite3_changeset_iter* pIter, /* Changeset iterator */ + int* pnOut /* OUT: Number of FK violations */ + ); + + /* + ** CAPI3REF: Finalize A Changeset Iterator + ** METHOD: sqlite3_changeset_iter + ** + ** This function is used to finalize an iterator allocated with + ** [sqlite3changeset_start()]. + ** + ** This function should only be called on iterators created using the + ** [sqlite3changeset_start()] function. If an application calls this + ** function with an iterator passed to a conflict-handler by + ** [sqlite3changeset_apply()], [SQLITE_MISUSE] is immediately returned and the + ** call has no effect. + ** + ** If an error was encountered within a call to an sqlite3changeset_xxx() + ** function (for example an [SQLITE_CORRUPT] in [sqlite3changeset_next()] or an + ** [SQLITE_NOMEM] in [sqlite3changeset_new()]) then an error code corresponding + ** to that error is returned by this function. Otherwise, SQLITE_OK is + ** returned. This is to allow the following pattern (pseudo-code): + ** + **
    +    **   sqlite3changeset_start();
    +    **   while( SQLITE_ROW==sqlite3changeset_next() ){
    +    **     // Do something with change.
    +    **   }
    +    **   rc = sqlite3changeset_finalize();
    +    **   if( rc!=SQLITE_OK ){
    +    **     // An error has occurred
    +    **   }
    +    ** 
    + */ + SQLITE_API int sqlite3changeset_finalize(sqlite3_changeset_iter* pIter); + + /* + ** CAPI3REF: Invert A Changeset + ** + ** This function is used to "invert" a changeset object. Applying an inverted + ** changeset to a database reverses the effects of applying the uninverted + ** changeset. Specifically: + ** + **
      + **
    • Each DELETE change is changed to an INSERT, and + **
    • Each INSERT change is changed to a DELETE, and + **
    • For each UPDATE change, the old.* and new.* values are exchanged. + **
    + ** + ** This function does not change the order in which changes appear within + ** the changeset. It merely reverses the sense of each individual change. + ** + ** If successful, a pointer to a buffer containing the inverted changeset + ** is stored in *ppOut, the size of the same buffer is stored in *pnOut, and + ** SQLITE_OK is returned. If an error occurs, both *pnOut and *ppOut are + ** zeroed and an SQLite error code returned. + ** + ** It is the responsibility of the caller to eventually call sqlite3_free() + ** on the *ppOut pointer to free the buffer allocation following a successful + ** call to this function. + ** + ** WARNING/TODO: This function currently assumes that the input is a valid + ** changeset. If it is not, the results are undefined. + */ + SQLITE_API int sqlite3changeset_invert(int nIn, + const void* pIn, /* Input changeset */ + int* pnOut, + void** ppOut /* OUT: Inverse of input */ + ); + + /* + ** CAPI3REF: Concatenate Two Changeset Objects + ** + ** This function is used to concatenate two changesets, A and B, into a + ** single changeset. The result is a changeset equivalent to applying + ** changeset A followed by changeset B. + ** + ** This function combines the two input changesets using an + ** sqlite3_changegroup object. Calling it produces similar results as the + ** following code fragment: + ** + **
    +    **   sqlite3_changegroup *pGrp;
    +    **   rc = sqlite3_changegroup_new(&pGrp);
    +    **   if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nA, pA);
    +    **   if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nB, pB);
    +    **   if( rc==SQLITE_OK ){
    +    **     rc = sqlite3changegroup_output(pGrp, pnOut, ppOut);
    +    **   }else{
    +    **     *ppOut = 0;
    +    **     *pnOut = 0;
    +    **   }
    +    ** 
    + ** + ** Refer to the sqlite3_changegroup documentation below for details. + */ + SQLITE_API int sqlite3changeset_concat(int nA, /* Number of bytes in buffer pA */ + void* pA, /* Pointer to buffer containing changeset A */ + int nB, /* Number of bytes in buffer pB */ + void* pB, /* Pointer to buffer containing changeset B */ + int* pnOut, /* OUT: Number of bytes in output changeset */ + void** ppOut /* OUT: Buffer containing output changeset */ + ); + + /* + ** CAPI3REF: Changegroup Handle + ** + ** A changegroup is an object used to combine two or more + ** [changesets] or [patchsets] + */ + typedef struct sqlite3_changegroup sqlite3_changegroup; + + /* + ** CAPI3REF: Create A New Changegroup Object + ** CONSTRUCTOR: sqlite3_changegroup + ** + ** An sqlite3_changegroup object is used to combine two or more changesets + ** (or patchsets) into a single changeset (or patchset). A single changegroup + ** object may combine changesets or patchsets, but not both. The output is + ** always in the same format as the input. + ** + ** If successful, this function returns SQLITE_OK and populates (*pp) with + ** a pointer to a new sqlite3_changegroup object before returning. The caller + ** should eventually free the returned object using a call to + ** sqlite3changegroup_delete(). If an error occurs, an SQLite error code + ** (i.e. SQLITE_NOMEM) is returned and *pp is set to NULL. + ** + ** The usual usage pattern for an sqlite3_changegroup object is as follows: + ** + **
      + **
    • It is created using a call to sqlite3changegroup_new(). + ** + **
    • Zero or more changesets (or patchsets) are added to the object + ** by calling sqlite3changegroup_add(). + ** + **
    • The result of combining all input changesets together is obtained + ** by the application via a call to sqlite3changegroup_output(). + ** + **
    • The object is deleted using a call to sqlite3changegroup_delete(). + **
    + ** + ** Any number of calls to add() and output() may be made between the calls to + ** new() and delete(), and in any order. + ** + ** As well as the regular sqlite3changegroup_add() and + ** sqlite3changegroup_output() functions, also available are the streaming + ** versions sqlite3changegroup_add_strm() and sqlite3changegroup_output_strm(). + */ + SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup** pp); + + /* + ** CAPI3REF: Add A Changeset To A Changegroup + ** METHOD: sqlite3_changegroup + ** + ** Add all changes within the changeset (or patchset) in buffer pData (size + ** nData bytes) to the changegroup. + ** + ** If the buffer contains a patchset, then all prior calls to this function + ** on the same changegroup object must also have specified patchsets. Or, if + ** the buffer contains a changeset, so must have the earlier calls to this + ** function. Otherwise, SQLITE_ERROR is returned and no changes are added + ** to the changegroup. + ** + ** Rows within the changeset and changegroup are identified by the values in + ** their PRIMARY KEY columns. A change in the changeset is considered to + ** apply to the same row as a change already present in the changegroup if + ** the two rows have the same primary key. + ** + ** Changes to rows that do not already appear in the changegroup are + ** simply copied into it. Or, if both the new changeset and the changegroup + ** contain changes that apply to a single row, the final contents of the + ** changegroup depends on the type of each change, as follows: + ** + ** + ** + ** + **
    Existing Change New Change Output Change + **
    INSERT INSERT + ** The new change is ignored. This case does not occur if the new + ** changeset was recorded immediately after the changesets already + ** added to the changegroup. + **
    INSERT UPDATE + ** The INSERT change remains in the changegroup. The values in the + ** INSERT change are modified as if the row was inserted by the + ** existing change and then updated according to the new change. + **
    INSERT DELETE + ** The existing INSERT is removed from the changegroup. The DELETE is + ** not added. + **
    UPDATE INSERT + ** The new change is ignored. This case does not occur if the new + ** changeset was recorded immediately after the changesets already + ** added to the changegroup. + **
    UPDATE UPDATE + ** The existing UPDATE remains within the changegroup. It is amended + ** so that the accompanying values are as if the row was updated once + ** by the existing change and then again by the new change. + **
    UPDATE DELETE + ** The existing UPDATE is replaced by the new DELETE within the + ** changegroup. + **
    DELETE INSERT + ** If one or more of the column values in the row inserted by the + ** new change differ from those in the row deleted by the existing + ** change, the existing DELETE is replaced by an UPDATE within the + ** changegroup. Otherwise, if the inserted row is exactly the same + ** as the deleted row, the existing DELETE is simply discarded. + **
    DELETE UPDATE + ** The new change is ignored. This case does not occur if the new + ** changeset was recorded immediately after the changesets already + ** added to the changegroup. + **
    DELETE DELETE + ** The new change is ignored. This case does not occur if the new + ** changeset was recorded immediately after the changesets already + ** added to the changegroup. + **
    + ** + ** 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. + ** + ** If no error occurs, SQLITE_OK is returned. + */ + SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void* pData); + + /* + ** CAPI3REF: Obtain A Composite Changeset From A Changegroup + ** METHOD: sqlite3_changegroup + ** + ** Obtain a buffer containing a changeset (or patchset) representing the + ** current contents of the changegroup. If the inputs to the changegroup + ** were themselves changesets, the output is a changeset. Or, if the + ** inputs were patchsets, the output is also a patchset. + ** + ** As with the output of the sqlite3session_changeset() and + ** sqlite3session_patchset() functions, all changes related to a single + ** table are grouped together in the output of this function. Tables appear + ** in the same order as for the very first changeset added to the changegroup. + ** If the second or subsequent changesets added to the changegroup contain + ** changes for tables that do not appear in the first changeset, they are + ** appended onto the end of the output changeset, again in the order in + ** which they are first encountered. + ** + ** If an error occurs, an SQLite error code is returned and the output + ** variables (*pnData) and (*ppData) are set to 0. Otherwise, SQLITE_OK + ** is returned and the output variables are set to the size of and a + ** pointer to the output buffer, respectively. In this case it is the + ** responsibility of the caller to eventually free the buffer using a + ** call to sqlite3_free(). + */ + SQLITE_API int sqlite3changegroup_output(sqlite3_changegroup*, + int* pnData, /* OUT: Size of output buffer in bytes */ + void** ppData /* OUT: Pointer to output buffer */ + ); + + /* + ** CAPI3REF: Delete A Changegroup Object + ** DESTRUCTOR: sqlite3_changegroup + */ + SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*); + + /* + ** CAPI3REF: Apply A Changeset To A Database + ** + ** Apply a changeset or patchset to a database. These functions attempt to + ** update the "main" database attached to handle db with the changes found in + ** the changeset passed via the second and third arguments. + ** + ** The fourth argument (xFilter) passed to these functions is the "filter + ** callback". If it is not NULL, then for each table affected by at least one + ** change in the changeset, the filter callback is invoked with + ** the table name as the second argument, and a copy of the context pointer + ** passed as the sixth argument as the first. If the "filter callback" + ** returns zero, then no attempt is made to apply any changes to the table. + ** Otherwise, if the return value is non-zero or the xFilter argument to + ** is NULL, all changes related to the table are attempted. + ** + ** For each table that is not excluded by the filter callback, this function + ** tests that the target database contains a compatible table. A table is + ** considered compatible if all of the following are true: + ** + **
      + **
    • The table has the same name as the name recorded in the + ** changeset, and + **
    • The table has at least as many columns as recorded in the + ** changeset, and + **
    • The table has primary key columns in the same position as + ** recorded in the changeset. + **
    + ** + ** If there is no compatible table, it is not an error, but none of the + ** changes associated with the table are applied. A warning message is issued + ** via the sqlite3_log() mechanism with the error code SQLITE_SCHEMA. At most + ** one such warning is issued for each table in the changeset. + ** + ** For each change for which there is a compatible table, an attempt is made + ** to modify the table contents according to the UPDATE, INSERT or DELETE + ** change. If a change cannot be applied cleanly, the conflict handler + ** function passed as the fifth argument to sqlite3changeset_apply() may be + ** invoked. A description of exactly when the conflict handler is invoked for + ** each type of change is below. + ** + ** Unlike the xFilter argument, xConflict may not be passed NULL. The results + ** of passing anything other than a valid function pointer as the xConflict + ** argument are undefined. + ** + ** Each time the conflict handler function is invoked, it must return one + ** of [SQLITE_CHANGESET_OMIT], [SQLITE_CHANGESET_ABORT] or + ** [SQLITE_CHANGESET_REPLACE]. SQLITE_CHANGESET_REPLACE may only be returned + ** if the second argument passed to the conflict handler is either + ** SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT. If the conflict-handler + ** returns an illegal value, any changes already made are rolled back and + ** the call to sqlite3changeset_apply() returns SQLITE_MISUSE. Different + ** actions are taken by sqlite3changeset_apply() depending on the value + ** returned by each invocation of the conflict-handler function. Refer to + ** the documentation for the three + ** [SQLITE_CHANGESET_OMIT|available return values] for details. + ** + **
    + **
    DELETE Changes
    + ** For each DELETE change, the function checks if the target database + ** contains a row with the same primary key value (or values) as the + ** original row values stored in the changeset. If it does, and the values + ** stored in all non-primary key columns also match the values stored in + ** the changeset the row is deleted from the target database. + ** + ** If a row with matching primary key values is found, but one or more of + ** the non-primary key fields contains a value different from the original + ** row value stored in the changeset, the conflict-handler function is + ** invoked with [SQLITE_CHANGESET_DATA] as the second argument. If the + ** database table has more columns than are recorded in the changeset, + ** only the values of those non-primary key fields are compared against + ** the current database contents - any trailing database table columns + ** are ignored. + ** + ** If no row with matching primary key values is found in the database, + ** the conflict-handler function is invoked with [SQLITE_CHANGESET_NOTFOUND] + ** passed as the second argument. + ** + ** If the DELETE operation is attempted, but SQLite returns SQLITE_CONSTRAINT + ** (which can only happen if a foreign key constraint is violated), the + ** conflict-handler function is invoked with [SQLITE_CHANGESET_CONSTRAINT] + ** passed as the second argument. This includes the case where the DELETE + ** operation is attempted because an earlier call to the conflict handler + ** function returned [SQLITE_CHANGESET_REPLACE]. + ** + **
    INSERT Changes
    + ** For each INSERT change, an attempt is made to insert the new row into + ** the database. If the changeset row contains fewer fields than the + ** database table, the trailing fields are populated with their default + ** values. + ** + ** If the attempt to insert the row fails because the database already + ** contains a row with the same primary key values, the conflict handler + ** function is invoked with the second argument set to + ** [SQLITE_CHANGESET_CONFLICT]. + ** + ** If the attempt to insert the row fails because of some other constraint + ** violation (e.g. NOT NULL or UNIQUE), the conflict handler function is + ** invoked with the second argument set to [SQLITE_CHANGESET_CONSTRAINT]. + ** This includes the case where the INSERT operation is re-attempted because + ** an earlier call to the conflict handler function returned + ** [SQLITE_CHANGESET_REPLACE]. + ** + **
    UPDATE Changes
    + ** For each UPDATE change, the function checks if the target database + ** contains a row with the same primary key value (or values) as the + ** original row values stored in the changeset. If it does, and the values + ** stored in all modified non-primary key columns also match the values + ** stored in the changeset the row is updated within the target database. + ** + ** If a row with matching primary key values is found, but one or more of + ** the modified non-primary key fields contains a value different from an + ** original row value stored in the changeset, the conflict-handler function + ** is invoked with [SQLITE_CHANGESET_DATA] as the second argument. Since + ** UPDATE changes only contain values for non-primary key fields that are + ** to be modified, only those fields need to match the original values to + ** avoid the SQLITE_CHANGESET_DATA conflict-handler callback. + ** + ** If no row with matching primary key values is found in the database, + ** the conflict-handler function is invoked with [SQLITE_CHANGESET_NOTFOUND] + ** passed as the second argument. + ** + ** If the UPDATE operation is attempted, but SQLite returns + ** SQLITE_CONSTRAINT, the conflict-handler function is invoked with + ** [SQLITE_CHANGESET_CONSTRAINT] passed as the second argument. + ** This includes the case where the UPDATE operation is attempted after + ** an earlier call to the conflict handler function returned + ** [SQLITE_CHANGESET_REPLACE]. + **
    + ** + ** It is safe to execute SQL statements, including those that write to the + ** table that the callback related to, from within the xConflict callback. + ** This can be used to further customize the application's conflict + ** resolution strategy. + ** + ** All changes made by these functions are enclosed in a savepoint transaction. + ** If any other error (aside from a constraint failure when attempting to + ** write to the target database) occurs, then the savepoint transaction is + ** rolled back, restoring the target database to its original state, and an + ** SQLite error code returned. + ** + ** If the output parameters (ppRebase) and (pnRebase) are non-NULL and + ** the input is a changeset (not a patchset), then sqlite3changeset_apply_v2() + ** may set (*ppRebase) to point to a "rebase" that may be used with the + ** sqlite3_rebaser APIs buffer before returning. In this case (*pnRebase) + ** is set to the size of the buffer in bytes. It is the responsibility of the + ** caller to eventually free any such buffer using sqlite3_free(). The buffer + ** is only allocated and populated if one or more conflicts were encountered + ** while applying the patchset. See comments surrounding the sqlite3_rebaser + ** APIs for further details. + ** + ** The behavior of sqlite3changeset_apply_v2() and its streaming equivalent + ** may be modified by passing a combination of + ** [SQLITE_CHANGESETAPPLY_NOSAVEPOINT | supported flags] as the 9th parameter. + ** + ** Note that the sqlite3changeset_apply_v2() API is still experimental + ** and therefore subject to change. + */ + SQLITE_API int sqlite3changeset_apply(sqlite3* db, /* Apply change to "main" db of this handle */ + int nChangeset, /* Size of changeset in bytes */ + void* pChangeset, /* Changeset blob */ + int (*xFilter)(void* pCtx, /* Copy of sixth arg to _apply() */ + const char* zTab /* Table name */ + ), + int (*xConflict)(void* pCtx, /* Copy of sixth arg to _apply() */ + int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ + sqlite3_changeset_iter* p /* Handle describing change and + conflict */ + ), + void* pCtx /* First argument passed to xConflict */ + ); + SQLITE_API int sqlite3changeset_apply_v2(sqlite3* db, /* Apply change to "main" db of this handle */ + int nChangeset, /* Size of changeset in bytes */ + void* pChangeset, /* Changeset blob */ + int (*xFilter)(void* pCtx, /* Copy of sixth arg to _apply() */ + const char* zTab /* Table name */ + ), + int (*xConflict)(void* pCtx, /* Copy of sixth arg to _apply() */ + int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ + sqlite3_changeset_iter* p /* Handle describing change and + conflict */ + ), + void* pCtx, /* First argument passed to xConflict */ + void** ppRebase, + int* pnRebase, /* OUT: Rebase data */ + int flags /* SESSION_CHANGESETAPPLY_* flags */ + ); /* ** CAPI3REF: Flags for sqlite3changeset_apply_v2 @@ -11118,8 +11630,8 @@ ** a changeset using sqlite3changeset_invert() before applying it. It is ** an error to specify this flag with a patchset. */ -#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 -#define SQLITE_CHANGESETAPPLY_INVERT 0x0002 +#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 +#define SQLITE_CHANGESETAPPLY_INVERT 0x0002 /* ** CAPI3REF: Constants Passed To The Conflict Handler @@ -11176,10 +11688,10 @@ ** ** */ -#define SQLITE_CHANGESET_DATA 1 -#define SQLITE_CHANGESET_NOTFOUND 2 -#define SQLITE_CHANGESET_CONFLICT 3 -#define SQLITE_CHANGESET_CONSTRAINT 4 +#define SQLITE_CHANGESET_DATA 1 +#define SQLITE_CHANGESET_NOTFOUND 2 +#define SQLITE_CHANGESET_CONFLICT 3 +#define SQLITE_CHANGESET_CONSTRAINT 4 #define SQLITE_CHANGESET_FOREIGN_KEY 5 /* @@ -11213,372 +11725,345 @@ ** and the call to sqlite3changeset_apply() returns SQLITE_ABORT. ** */ -#define SQLITE_CHANGESET_OMIT 0 -#define SQLITE_CHANGESET_REPLACE 1 -#define SQLITE_CHANGESET_ABORT 2 - -/* -** CAPI3REF: Rebasing changesets -** EXPERIMENTAL -** -** Suppose there is a site hosting a database in state S0. And that -** modifications are made that move that database to state S1 and a -** changeset recorded (the "local" changeset). Then, a changeset based -** on S0 is received from another site (the "remote" changeset) and -** applied to the database. The database is then in state -** (S1+"remote"), where the exact state depends on any conflict -** resolution decisions (OMIT or REPLACE) made while applying "remote". -** Rebasing a changeset is to update it to take those conflict -** resolution decisions into account, so that the same conflicts -** do not have to be resolved elsewhere in the network. -** -** For example, if both the local and remote changesets contain an -** INSERT of the same key on "CREATE TABLE t1(a PRIMARY KEY, b)": -** -** local: INSERT INTO t1 VALUES(1, 'v1'); -** remote: INSERT INTO t1 VALUES(1, 'v2'); -** -** and the conflict resolution is REPLACE, then the INSERT change is -** removed from the local changeset (it was overridden). Or, if the -** conflict resolution was "OMIT", then the local changeset is modified -** to instead contain: -** -** UPDATE t1 SET b = 'v2' WHERE a=1; -** -** Changes within the local changeset are rebased as follows: -** -**
    -**
    Local INSERT
    -** This may only conflict with a remote INSERT. If the conflict -** resolution was OMIT, then add an UPDATE change to the rebased -** changeset. Or, if the conflict resolution was REPLACE, add -** nothing to the rebased changeset. -** -**
    Local DELETE
    -** This may conflict with a remote UPDATE or DELETE. In both cases the -** only possible resolution is OMIT. If the remote operation was a -** DELETE, then add no change to the rebased changeset. If the remote -** operation was an UPDATE, then the old.* fields of change are updated -** to reflect the new.* values in the UPDATE. -** -**
    Local UPDATE
    -** This may conflict with a remote UPDATE or DELETE. If it conflicts -** with a DELETE, and the conflict resolution was OMIT, then the update -** is changed into an INSERT. Any undefined values in the new.* record -** from the update change are filled in using the old.* values from -** the conflicting DELETE. Or, if the conflict resolution was REPLACE, -** the UPDATE change is simply omitted from the rebased changeset. -** -** If conflict is with a remote UPDATE and the resolution is OMIT, then -** the old.* values are rebased using the new.* values in the remote -** change. Or, if the resolution is REPLACE, then the change is copied -** into the rebased changeset with updates to columns also updated by -** the conflicting remote UPDATE removed. If this means no columns would -** be updated, the change is omitted. -**
    -** -** A local change may be rebased against multiple remote changes -** simultaneously. If a single key is modified by multiple remote -** changesets, they are combined as follows before the local changeset -** is rebased: -** -**
      -**
    • If there has been one or more REPLACE resolutions on a -** key, it is rebased according to a REPLACE. -** -**
    • If there have been no REPLACE resolutions on a key, then -** the local changeset is rebased according to the most recent -** of the OMIT resolutions. -**
    -** -** Note that conflict resolutions from multiple remote changesets are -** combined on a per-field basis, not per-row. This means that in the -** case of multiple remote UPDATE operations, some fields of a single -** local change may be rebased for REPLACE while others are rebased for -** OMIT. -** -** In order to rebase a local changeset, the remote changeset must first -** be applied to the local database using sqlite3changeset_apply_v2() and -** the buffer of rebase information captured. Then: -** -**
      -**
    1. An sqlite3_rebaser object is created by calling -** sqlite3rebaser_create(). -**
    2. The new object is configured with the rebase buffer obtained from -** sqlite3changeset_apply_v2() by calling sqlite3rebaser_configure(). -** If the local changeset is to be rebased against multiple remote -** changesets, then sqlite3rebaser_configure() should be called -** multiple times, in the same order that the multiple -** sqlite3changeset_apply_v2() calls were made. -**
    3. Each local changeset is rebased by calling sqlite3rebaser_rebase(). -**
    4. The sqlite3_rebaser object is deleted by calling -** sqlite3rebaser_delete(). -**
    -*/ -typedef struct sqlite3_rebaser sqlite3_rebaser; - -/* -** CAPI3REF: Create a changeset rebaser object. -** EXPERIMENTAL -** -** Allocate a new changeset rebaser object. If successful, set (*ppNew) to -** point to the new object and return SQLITE_OK. Otherwise, if an error -** occurs, return an SQLite error code (e.g. SQLITE_NOMEM) and set (*ppNew) -** to NULL. -*/ -SQLITE_API int sqlite3rebaser_create(sqlite3_rebaser **ppNew); - -/* -** CAPI3REF: Configure a changeset rebaser object. -** EXPERIMENTAL -** -** Configure the changeset rebaser object to rebase changesets according -** to the conflict resolutions described by buffer pRebase (size nRebase -** bytes), which must have been obtained from a previous call to -** sqlite3changeset_apply_v2(). -*/ -SQLITE_API int sqlite3rebaser_configure( - sqlite3_rebaser*, - int nRebase, const void *pRebase -); - -/* -** CAPI3REF: Rebase a changeset -** EXPERIMENTAL -** -** Argument pIn must point to a buffer containing a changeset nIn bytes -** in size. This function allocates and populates a buffer with a copy -** of the changeset rebased according to the configuration of the -** rebaser object passed as the first argument. If successful, (*ppOut) -** is set to point to the new buffer containing the rebased changeset and -** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the -** responsibility of the caller to eventually free the new buffer using -** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut) -** are set to zero and an SQLite error code returned. -*/ -SQLITE_API int sqlite3rebaser_rebase( - sqlite3_rebaser*, - int nIn, const void *pIn, - int *pnOut, void **ppOut -); - -/* -** CAPI3REF: Delete a changeset rebaser object. -** EXPERIMENTAL -** -** Delete the changeset rebaser object and all associated resources. There -** should be one call to this function for each successful invocation -** of sqlite3rebaser_create(). -*/ -SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p); - -/* -** CAPI3REF: Streaming Versions of API functions. -** -** The six streaming API xxx_strm() functions serve similar purposes to the -** corresponding non-streaming API functions: -** -** -** -**
    Streaming functionNon-streaming equivalent
    sqlite3changeset_apply_strm[sqlite3changeset_apply] -**
    sqlite3changeset_apply_strm_v2[sqlite3changeset_apply_v2] -**
    sqlite3changeset_concat_strm[sqlite3changeset_concat] -**
    sqlite3changeset_invert_strm[sqlite3changeset_invert] -**
    sqlite3changeset_start_strm[sqlite3changeset_start] -**
    sqlite3session_changeset_strm[sqlite3session_changeset] -**
    sqlite3session_patchset_strm[sqlite3session_patchset] -**
    -** -** Non-streaming functions that accept changesets (or patchsets) as input -** require that the entire changeset be stored in a single buffer in memory. -** Similarly, those that return a changeset or patchset do so by returning -** a pointer to a single large buffer allocated using sqlite3_malloc(). -** Normally this is convenient. However, if an application running in a -** low-memory environment is required to handle very large changesets, the -** large contiguous memory allocations required can become onerous. -** -** In order to avoid this problem, instead of a single large buffer, input -** is passed to a streaming API functions by way of a callback function that -** the sessions module invokes to incrementally request input data as it is -** required. In all cases, a pair of API function parameters such as -** -**
    -**        int nChangeset,
    -**        void *pChangeset,
    -**  
    -** -** Is replaced by: -** -**
    -**        int (*xInput)(void *pIn, void *pData, int *pnData),
    -**        void *pIn,
    -**  
    -** -** Each time the xInput callback is invoked by the sessions module, the first -** argument passed is a copy of the supplied pIn context pointer. The second -** argument, pData, points to a buffer (*pnData) bytes in size. Assuming no -** error occurs the xInput method should copy up to (*pnData) bytes of data -** into the buffer and set (*pnData) to the actual number of bytes copied -** before returning SQLITE_OK. If the input is completely exhausted, (*pnData) -** should be set to zero to indicate this. Or, if an error occurs, an SQLite -** error code should be returned. In all cases, if an xInput callback returns -** an error, all processing is abandoned and the streaming API function -** returns a copy of the error code to the caller. -** -** In the case of sqlite3changeset_start_strm(), the xInput callback may be -** invoked by the sessions module at any point during the lifetime of the -** iterator. If such an xInput callback returns an error, the iterator enters -** an error state, whereby all subsequent calls to iterator functions -** immediately fail with the same error code as returned by xInput. -** -** Similarly, streaming API functions that return changesets (or patchsets) -** return them in chunks by way of a callback function instead of via a -** pointer to a single large buffer. In this case, a pair of parameters such -** as: -** -**
    -**        int *pnChangeset,
    -**        void **ppChangeset,
    -**  
    -** -** Is replaced by: -** -**
    -**        int (*xOutput)(void *pOut, const void *pData, int nData),
    -**        void *pOut
    -**  
    -** -** The xOutput callback is invoked zero or more times to return data to -** the application. The first parameter passed to each call is a copy of the -** pOut pointer supplied by the application. The second parameter, pData, -** points to a buffer nData bytes in size containing the chunk of output -** data being returned. If the xOutput callback successfully processes the -** supplied data, it should return SQLITE_OK to indicate success. Otherwise, -** it should return some other SQLite error code. In this case processing -** is immediately abandoned and the streaming API function returns a copy -** of the xOutput error code to the application. -** -** The sessions module never invokes an xOutput callback with the third -** parameter set to a value less than or equal to zero. Other than this, -** no guarantees are made as to the size of the chunks of data returned. -*/ -SQLITE_API int sqlite3changeset_apply_strm( - sqlite3 *db, /* Apply change to "main" db of this handle */ - int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */ - void *pIn, /* First arg for xInput */ - int(*xFilter)( - void *pCtx, /* Copy of sixth arg to _apply() */ - const char *zTab /* Table name */ - ), - int(*xConflict)( - void *pCtx, /* Copy of sixth arg to _apply() */ - int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ - sqlite3_changeset_iter *p /* Handle describing change and conflict */ - ), - void *pCtx /* First argument passed to xConflict */ -); -SQLITE_API int sqlite3changeset_apply_v2_strm( - sqlite3 *db, /* Apply change to "main" db of this handle */ - int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */ - void *pIn, /* First arg for xInput */ - int(*xFilter)( - void *pCtx, /* Copy of sixth arg to _apply() */ - const char *zTab /* Table name */ - ), - int(*xConflict)( - void *pCtx, /* Copy of sixth arg to _apply() */ - int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ - sqlite3_changeset_iter *p /* Handle describing change and conflict */ - ), - void *pCtx, /* First argument passed to xConflict */ - void **ppRebase, int *pnRebase, - int flags -); -SQLITE_API int sqlite3changeset_concat_strm( - int (*xInputA)(void *pIn, void *pData, int *pnData), - void *pInA, - int (*xInputB)(void *pIn, void *pData, int *pnData), - void *pInB, - int (*xOutput)(void *pOut, const void *pData, int nData), - void *pOut -); -SQLITE_API int sqlite3changeset_invert_strm( - int (*xInput)(void *pIn, void *pData, int *pnData), - void *pIn, - int (*xOutput)(void *pOut, const void *pData, int nData), - void *pOut -); -SQLITE_API int sqlite3changeset_start_strm( - sqlite3_changeset_iter **pp, - int (*xInput)(void *pIn, void *pData, int *pnData), - void *pIn -); -SQLITE_API int sqlite3changeset_start_v2_strm( - sqlite3_changeset_iter **pp, - int (*xInput)(void *pIn, void *pData, int *pnData), - void *pIn, - int flags -); -SQLITE_API int sqlite3session_changeset_strm( - sqlite3_session *pSession, - int (*xOutput)(void *pOut, const void *pData, int nData), - void *pOut -); -SQLITE_API int sqlite3session_patchset_strm( - sqlite3_session *pSession, - int (*xOutput)(void *pOut, const void *pData, int nData), - void *pOut -); -SQLITE_API int sqlite3changegroup_add_strm(sqlite3_changegroup*, - int (*xInput)(void *pIn, void *pData, int *pnData), - void *pIn -); -SQLITE_API int sqlite3changegroup_output_strm(sqlite3_changegroup*, - int (*xOutput)(void *pOut, const void *pData, int nData), - void *pOut -); -SQLITE_API int sqlite3rebaser_rebase_strm( - sqlite3_rebaser *pRebaser, - int (*xInput)(void *pIn, void *pData, int *pnData), - void *pIn, - int (*xOutput)(void *pOut, const void *pData, int nData), - void *pOut -); - -/* -** CAPI3REF: Configure global parameters -** -** The sqlite3session_config() interface is used to make global configuration -** changes to the sessions module in order to tune it to the specific needs -** of the application. -** -** The sqlite3session_config() interface is not threadsafe. If it is invoked -** while any other thread is inside any other sessions method then the -** results are undefined. Furthermore, if it is invoked after any sessions -** related objects have been created, the results are also undefined. -** -** The first argument to the sqlite3session_config() function must be one -** of the SQLITE_SESSION_CONFIG_XXX constants defined below. The -** interpretation of the (void*) value passed as the second parameter and -** the effect of calling this function depends on the value of the first -** parameter. -** -**
    -**
    SQLITE_SESSION_CONFIG_STRMSIZE
    -** By default, the sessions module streaming interfaces attempt to input -** and output data in approximately 1 KiB chunks. This operand may be used -** to set and query the value of this configuration setting. The pointer -** passed as the second argument must point to a value of type (int). -** If this value is greater than 0, it is used as the new streaming data -** chunk size for both input and output. Before returning, the (int) value -** pointed to by pArg is set to the final value of the streaming interface -** chunk size. -**
    -** -** This function returns SQLITE_OK if successful, or an SQLite error code -** otherwise. -*/ -SQLITE_API int sqlite3session_config(int op, void *pArg); +#define SQLITE_CHANGESET_OMIT 0 +#define SQLITE_CHANGESET_REPLACE 1 +#define SQLITE_CHANGESET_ABORT 2 + + /* + ** CAPI3REF: Rebasing changesets + ** EXPERIMENTAL + ** + ** Suppose there is a site hosting a database in state S0. And that + ** modifications are made that move that database to state S1 and a + ** changeset recorded (the "local" changeset). Then, a changeset based + ** on S0 is received from another site (the "remote" changeset) and + ** applied to the database. The database is then in state + ** (S1+"remote"), where the exact state depends on any conflict + ** resolution decisions (OMIT or REPLACE) made while applying "remote". + ** Rebasing a changeset is to update it to take those conflict + ** resolution decisions into account, so that the same conflicts + ** do not have to be resolved elsewhere in the network. + ** + ** For example, if both the local and remote changesets contain an + ** INSERT of the same key on "CREATE TABLE t1(a PRIMARY KEY, b)": + ** + ** local: INSERT INTO t1 VALUES(1, 'v1'); + ** remote: INSERT INTO t1 VALUES(1, 'v2'); + ** + ** and the conflict resolution is REPLACE, then the INSERT change is + ** removed from the local changeset (it was overridden). Or, if the + ** conflict resolution was "OMIT", then the local changeset is modified + ** to instead contain: + ** + ** UPDATE t1 SET b = 'v2' WHERE a=1; + ** + ** Changes within the local changeset are rebased as follows: + ** + **
    + **
    Local INSERT
    + ** This may only conflict with a remote INSERT. If the conflict + ** resolution was OMIT, then add an UPDATE change to the rebased + ** changeset. Or, if the conflict resolution was REPLACE, add + ** nothing to the rebased changeset. + ** + **
    Local DELETE
    + ** This may conflict with a remote UPDATE or DELETE. In both cases the + ** only possible resolution is OMIT. If the remote operation was a + ** DELETE, then add no change to the rebased changeset. If the remote + ** operation was an UPDATE, then the old.* fields of change are updated + ** to reflect the new.* values in the UPDATE. + ** + **
    Local UPDATE
    + ** This may conflict with a remote UPDATE or DELETE. If it conflicts + ** with a DELETE, and the conflict resolution was OMIT, then the update + ** is changed into an INSERT. Any undefined values in the new.* record + ** from the update change are filled in using the old.* values from + ** the conflicting DELETE. Or, if the conflict resolution was REPLACE, + ** the UPDATE change is simply omitted from the rebased changeset. + ** + ** If conflict is with a remote UPDATE and the resolution is OMIT, then + ** the old.* values are rebased using the new.* values in the remote + ** change. Or, if the resolution is REPLACE, then the change is copied + ** into the rebased changeset with updates to columns also updated by + ** the conflicting remote UPDATE removed. If this means no columns would + ** be updated, the change is omitted. + **
    + ** + ** A local change may be rebased against multiple remote changes + ** simultaneously. If a single key is modified by multiple remote + ** changesets, they are combined as follows before the local changeset + ** is rebased: + ** + **
      + **
    • If there has been one or more REPLACE resolutions on a + ** key, it is rebased according to a REPLACE. + ** + **
    • If there have been no REPLACE resolutions on a key, then + ** the local changeset is rebased according to the most recent + ** of the OMIT resolutions. + **
    + ** + ** Note that conflict resolutions from multiple remote changesets are + ** combined on a per-field basis, not per-row. This means that in the + ** case of multiple remote UPDATE operations, some fields of a single + ** local change may be rebased for REPLACE while others are rebased for + ** OMIT. + ** + ** In order to rebase a local changeset, the remote changeset must first + ** be applied to the local database using sqlite3changeset_apply_v2() and + ** the buffer of rebase information captured. Then: + ** + **
      + **
    1. An sqlite3_rebaser object is created by calling + ** sqlite3rebaser_create(). + **
    2. The new object is configured with the rebase buffer obtained from + ** sqlite3changeset_apply_v2() by calling sqlite3rebaser_configure(). + ** If the local changeset is to be rebased against multiple remote + ** changesets, then sqlite3rebaser_configure() should be called + ** multiple times, in the same order that the multiple + ** sqlite3changeset_apply_v2() calls were made. + **
    3. Each local changeset is rebased by calling sqlite3rebaser_rebase(). + **
    4. The sqlite3_rebaser object is deleted by calling + ** sqlite3rebaser_delete(). + **
    + */ + typedef struct sqlite3_rebaser sqlite3_rebaser; + + /* + ** CAPI3REF: Create a changeset rebaser object. + ** EXPERIMENTAL + ** + ** Allocate a new changeset rebaser object. If successful, set (*ppNew) to + ** point to the new object and return SQLITE_OK. Otherwise, if an error + ** occurs, return an SQLite error code (e.g. SQLITE_NOMEM) and set (*ppNew) + ** to NULL. + */ + SQLITE_API int sqlite3rebaser_create(sqlite3_rebaser** ppNew); + + /* + ** CAPI3REF: Configure a changeset rebaser object. + ** EXPERIMENTAL + ** + ** Configure the changeset rebaser object to rebase changesets according + ** to the conflict resolutions described by buffer pRebase (size nRebase + ** bytes), which must have been obtained from a previous call to + ** sqlite3changeset_apply_v2(). + */ + SQLITE_API int sqlite3rebaser_configure(sqlite3_rebaser*, int nRebase, const void* pRebase); + + /* + ** CAPI3REF: Rebase a changeset + ** EXPERIMENTAL + ** + ** Argument pIn must point to a buffer containing a changeset nIn bytes + ** in size. This function allocates and populates a buffer with a copy + ** of the changeset rebased according to the configuration of the + ** rebaser object passed as the first argument. If successful, (*ppOut) + ** is set to point to the new buffer containing the rebased changeset and + ** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the + ** responsibility of the caller to eventually free the new buffer using + ** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut) + ** are set to zero and an SQLite error code returned. + */ + SQLITE_API int sqlite3rebaser_rebase(sqlite3_rebaser*, int nIn, const void* pIn, int* pnOut, void** ppOut); + + /* + ** CAPI3REF: Delete a changeset rebaser object. + ** EXPERIMENTAL + ** + ** Delete the changeset rebaser object and all associated resources. There + ** should be one call to this function for each successful invocation + ** of sqlite3rebaser_create(). + */ + SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser* p); + + /* + ** CAPI3REF: Streaming Versions of API functions. + ** + ** The six streaming API xxx_strm() functions serve similar purposes to the + ** corresponding non-streaming API functions: + ** + ** + ** + **
    Streaming functionNon-streaming equivalent
    sqlite3changeset_apply_strm[sqlite3changeset_apply] + **
    sqlite3changeset_apply_strm_v2[sqlite3changeset_apply_v2] + **
    sqlite3changeset_concat_strm[sqlite3changeset_concat] + **
    sqlite3changeset_invert_strm[sqlite3changeset_invert] + **
    sqlite3changeset_start_strm[sqlite3changeset_start] + **
    sqlite3session_changeset_strm[sqlite3session_changeset] + **
    sqlite3session_patchset_strm[sqlite3session_patchset] + **
    + ** + ** Non-streaming functions that accept changesets (or patchsets) as input + ** require that the entire changeset be stored in a single buffer in memory. + ** Similarly, those that return a changeset or patchset do so by returning + ** a pointer to a single large buffer allocated using sqlite3_malloc(). + ** Normally this is convenient. However, if an application running in a + ** low-memory environment is required to handle very large changesets, the + ** large contiguous memory allocations required can become onerous. + ** + ** In order to avoid this problem, instead of a single large buffer, input + ** is passed to a streaming API functions by way of a callback function that + ** the sessions module invokes to incrementally request input data as it is + ** required. In all cases, a pair of API function parameters such as + ** + **
    +    **        int nChangeset,
    +    **        void *pChangeset,
    +    **  
    + ** + ** Is replaced by: + ** + **
    +    **        int (*xInput)(void *pIn, void *pData, int *pnData),
    +    **        void *pIn,
    +    **  
    + ** + ** Each time the xInput callback is invoked by the sessions module, the first + ** argument passed is a copy of the supplied pIn context pointer. The second + ** argument, pData, points to a buffer (*pnData) bytes in size. Assuming no + ** error occurs the xInput method should copy up to (*pnData) bytes of data + ** into the buffer and set (*pnData) to the actual number of bytes copied + ** before returning SQLITE_OK. If the input is completely exhausted, (*pnData) + ** should be set to zero to indicate this. Or, if an error occurs, an SQLite + ** error code should be returned. In all cases, if an xInput callback returns + ** an error, all processing is abandoned and the streaming API function + ** returns a copy of the error code to the caller. + ** + ** In the case of sqlite3changeset_start_strm(), the xInput callback may be + ** invoked by the sessions module at any point during the lifetime of the + ** iterator. If such an xInput callback returns an error, the iterator enters + ** an error state, whereby all subsequent calls to iterator functions + ** immediately fail with the same error code as returned by xInput. + ** + ** Similarly, streaming API functions that return changesets (or patchsets) + ** return them in chunks by way of a callback function instead of via a + ** pointer to a single large buffer. In this case, a pair of parameters such + ** as: + ** + **
    +    **        int *pnChangeset,
    +    **        void **ppChangeset,
    +    **  
    + ** + ** Is replaced by: + ** + **
    +    **        int (*xOutput)(void *pOut, const void *pData, int nData),
    +    **        void *pOut
    +    **  
    + ** + ** The xOutput callback is invoked zero or more times to return data to + ** the application. The first parameter passed to each call is a copy of the + ** pOut pointer supplied by the application. The second parameter, pData, + ** points to a buffer nData bytes in size containing the chunk of output + ** data being returned. If the xOutput callback successfully processes the + ** supplied data, it should return SQLITE_OK to indicate success. Otherwise, + ** it should return some other SQLite error code. In this case processing + ** is immediately abandoned and the streaming API function returns a copy + ** of the xOutput error code to the application. + ** + ** The sessions module never invokes an xOutput callback with the third + ** parameter set to a value less than or equal to zero. Other than this, + ** no guarantees are made as to the size of the chunks of data returned. + */ + SQLITE_API int sqlite3changeset_apply_strm(sqlite3* db, /* Apply change to "main" db of this handle */ + int (*xInput)(void* pIn, void* pData, int* pnData), /* Input function */ + void* pIn, /* First arg for xInput */ + int (*xFilter)(void* pCtx, /* Copy of sixth arg to _apply() */ + const char* zTab /* Table name */ + ), + int (*xConflict)(void* pCtx, /* Copy of sixth arg to _apply() */ + int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ + sqlite3_changeset_iter* p /* Handle describing change + and conflict */ + ), + void* pCtx /* First argument passed to xConflict */ + ); + SQLITE_API int + sqlite3changeset_apply_v2_strm(sqlite3* db, /* Apply change to "main" db of this handle */ + int (*xInput)(void* pIn, void* pData, int* pnData), /* Input function */ + void* pIn, /* First arg for xInput */ + int (*xFilter)(void* pCtx, /* Copy of sixth arg to _apply() */ + const char* zTab /* Table name */ + ), + int (*xConflict)(void* pCtx, /* Copy of sixth arg to _apply() */ + int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ + sqlite3_changeset_iter* p /* Handle describing change and conflict + */ + ), + void* pCtx, /* First argument passed to xConflict */ + void** ppRebase, + int* pnRebase, + int flags); + SQLITE_API int sqlite3changeset_concat_strm(int (*xInputA)(void* pIn, void* pData, int* pnData), + void* pInA, + int (*xInputB)(void* pIn, void* pData, int* pnData), + void* pInB, + int (*xOutput)(void* pOut, const void* pData, int nData), + void* pOut); + SQLITE_API int sqlite3changeset_invert_strm(int (*xInput)(void* pIn, void* pData, int* pnData), + void* pIn, + int (*xOutput)(void* pOut, const void* pData, int nData), + void* pOut); + SQLITE_API int sqlite3changeset_start_strm(sqlite3_changeset_iter** pp, + int (*xInput)(void* pIn, void* pData, int* pnData), + void* pIn); + SQLITE_API int sqlite3changeset_start_v2_strm(sqlite3_changeset_iter** pp, + int (*xInput)(void* pIn, void* pData, int* pnData), + void* pIn, + int flags); + SQLITE_API int sqlite3session_changeset_strm(sqlite3_session* pSession, + int (*xOutput)(void* pOut, const void* pData, int nData), + void* pOut); + SQLITE_API int sqlite3session_patchset_strm(sqlite3_session* pSession, + int (*xOutput)(void* pOut, const void* pData, int nData), + void* pOut); + SQLITE_API int + sqlite3changegroup_add_strm(sqlite3_changegroup*, int (*xInput)(void* pIn, void* pData, int* pnData), void* pIn); + SQLITE_API int sqlite3changegroup_output_strm(sqlite3_changegroup*, + int (*xOutput)(void* pOut, const void* pData, int nData), + void* pOut); + SQLITE_API int sqlite3rebaser_rebase_strm(sqlite3_rebaser* pRebaser, + int (*xInput)(void* pIn, void* pData, int* pnData), + void* pIn, + int (*xOutput)(void* pOut, const void* pData, int nData), + void* pOut); + + /* + ** CAPI3REF: Configure global parameters + ** + ** The sqlite3session_config() interface is used to make global configuration + ** changes to the sessions module in order to tune it to the specific needs + ** of the application. + ** + ** The sqlite3session_config() interface is not threadsafe. If it is invoked + ** while any other thread is inside any other sessions method then the + ** results are undefined. Furthermore, if it is invoked after any sessions + ** related objects have been created, the results are also undefined. + ** + ** The first argument to the sqlite3session_config() function must be one + ** of the SQLITE_SESSION_CONFIG_XXX constants defined below. The + ** interpretation of the (void*) value passed as the second parameter and + ** the effect of calling this function depends on the value of the first + ** parameter. + ** + **
    + **
    SQLITE_SESSION_CONFIG_STRMSIZE
    + ** By default, the sessions module streaming interfaces attempt to input + ** and output data in approximately 1 KiB chunks. This operand may be used + ** to set and query the value of this configuration setting. The pointer + ** passed as the second argument must point to a value of type (int). + ** If this value is greater than 0, it is used as the new streaming data + ** chunk size for both input and output. Before returning, the (int) value + ** pointed to by pArg is set to the final value of the streaming interface + ** chunk size. + **
    + ** + ** This function returns SQLITE_OK if successful, or an SQLite error code + ** otherwise. + */ + SQLITE_API int sqlite3session_config(int op, void* pArg); /* ** CAPI3REF: Values for sqlite3session_config(). @@ -11592,7 +12077,7 @@ } #endif -#endif /* !defined(__SQLITESESSION_H_) && defined(SQLITE_ENABLE_SESSION) */ +#endif /* !defined(__SQLITESESSION_H_) && defined(SQLITE_ENABLE_SESSION) */ /******** End of sqlite3session.h *********/ /******** Begin file fts5.h *********/ @@ -11615,558 +12100,552 @@ ** * custom auxiliary functions. */ - #ifndef _FTS5_H #define _FTS5_H - #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif -/************************************************************************* -** CUSTOM AUXILIARY FUNCTIONS -** -** Virtual table implementations may overload SQL functions by implementing -** the sqlite3_module.xFindFunction() method. -*/ - -typedef struct Fts5ExtensionApi Fts5ExtensionApi; -typedef struct Fts5Context Fts5Context; -typedef struct Fts5PhraseIter Fts5PhraseIter; - -typedef void (*fts5_extension_function)( - 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 */ -); - -struct Fts5PhraseIter { - const unsigned char *a; - const unsigned char *b; -}; - -/* -** EXTENSION API FUNCTIONS -** -** xUserData(pFts): -** Return a copy of the context pointer the extension function was -** registered with. -** -** xColumnTotalSize(pFts, iCol, pnToken): -** If parameter iCol is less than zero, set output variable *pnToken -** to the total number of tokens in the FTS5 table. Or, if iCol is -** non-negative but less than the number of columns in the table, return -** the total number of tokens in column iCol, considering all rows in -** the FTS5 table. -** -** If parameter iCol is greater than or equal to the number of columns -** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g. -** an OOM condition or IO error), an appropriate SQLite error code is -** returned. -** -** xColumnCount(pFts): -** Return the number of columns in the table. -** -** xColumnSize(pFts, iCol, pnToken): -** If parameter iCol is less than zero, set output variable *pnToken -** to the total number of tokens in the current row. Or, if iCol is -** non-negative but less than the number of columns in the table, set -** *pnToken to the number of tokens in column iCol of the current row. -** -** If parameter iCol is greater than or equal to the number of columns -** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g. -** an OOM condition or IO error), an appropriate SQLite error code is -** returned. -** -** This function may be quite inefficient if used with an FTS5 table -** 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 -** 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 -** of (*pz) and (*pn) are undefined. -** -** xPhraseCount: -** 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. -** -** xInstCount: -** Set *pnInst to the total number of occurrences of all phrases within -** the query within the current row. Return SQLITE_OK 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. If the FTS5 table is created -** with either "detail=none" or "detail=column" and "content=" option -** (i.e. if it is a contentless table), then this API always returns 0. -** -** xInst: -** 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(). -** -** Usually, 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. -** -** This API can be quite slow if used with an FTS5 table created with the -** "detail=none" or "detail=column" option. -** -** xRowid: -** Returns the rowid of the current row. -** -** xTokenize: -** Tokenize text using the tokenizer belonging to the FTS5 table. -** -** xQueryPhrase(pFts5, iPhrase, pUserData, xCallback): -** This API function is used to query the FTS table for phrase iPhrase -** of the current query. Specifically, a query equivalent to: -** -** ... FROM ftstable WHERE ftstable MATCH $p ORDER BY rowid -** -** with $p set to a phrase equivalent to the phrase iPhrase of the -** current query is executed. Any column filter that applies to -** phrase iPhrase of the current query is included in $p. For each -** row visited, the callback function passed as the fourth argument -** is invoked. The context and API objects passed to the callback -** function may be used to access the properties of each matched row. -** Invoking Api.xUserData() returns a copy of the pointer passed as -** the third argument to pUserData. -** -** 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. -** Otherwise, the error code is propagated upwards. -** -** If the query runs to completion without incident, SQLITE_OK is returned. -** Or, if some error occurs before the query completes or is aborted by -** the callback, an SQLite error code is returned. -** -** -** xSetAuxdata(pFts5, pAux, xDelete) -** -** Save the pointer passed as the second argument as the extension function's -** "auxiliary data". The pointer may then be retrieved by the current or any -** future invocation of the same fts5 extension function made as part of -** the same MATCH query using the xGetAuxdata() API. -** -** Each extension function is allocated a single auxiliary data slot for -** each FTS query (MATCH expression). If the extension function is invoked -** more than once for a single FTS query, then all invocations share a -** single auxiliary data context. -** -** If there is already an auxiliary data pointer when this function is -** invoked, then it is replaced by the new pointer. If an xDelete callback -** was specified along with the original pointer, it is invoked at this -** point. -** -** The xDelete callback, if one is specified, is also invoked on the -** auxiliary data pointer after the FTS5 query has finished. -** -** If an error (e.g. an OOM condition) occurs within this function, -** the auxiliary data is set to NULL and an error code returned. If the -** xDelete parameter was not NULL, it is invoked on the auxiliary data -** pointer before returning. -** -** -** xGetAuxdata(pFts5, bClear) -** -** Returns the current auxiliary data pointer for the fts5 extension -** function. See the xSetAuxdata() method for details. -** -** If the bClear argument is non-zero, then the auxiliary data is cleared -** (set to NULL) before this function returns. In this case the xDelete, -** if any, is not invoked. -** -** -** xRowCount(pFts5, pnRow) -** -** This function is used to retrieve the total number of rows in the table. -** In other words, the same value that would be returned by: -** -** SELECT count(*) FROM ftstable; -** -** xPhraseFirst() -** This function is used, along with type Fts5PhraseIter and the xPhraseNext -** method, to iterate through all instances of a single query phrase within -** the current row. This is the same information as is accessible via the -** xInstCount/xInst APIs. While the xInstCount/xInst APIs are more convenient -** to use, this API may be faster under some circumstances. To iterate -** through instances of phrase iPhrase, use the following code: -** -** Fts5PhraseIter iter; -** int iCol, iOff; -** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff); -** iCol>=0; -** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff) -** ){ -** // An instance of phrase iPhrase at offset iOff of column iCol -** } -** -** The Fts5PhraseIter structure is defined above. Applications should not -** modify this structure directly - it should only be used as shown above -** with the xPhraseFirst() and xPhraseNext() API methods (and by -** xPhraseFirstColumn() and xPhraseNextColumn() as illustrated below). -** -** This API can be quite slow if used with an FTS5 table created with the -** "detail=none" or "detail=column" option. If the FTS5 table is created -** with either "detail=none" or "detail=column" and "content=" option -** (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). -** -** xPhraseNext() -** See xPhraseFirst above. -** -** xPhraseFirstColumn() -** This function and xPhraseNextColumn() are similar to the xPhraseFirst() -** and xPhraseNext() APIs described above. The difference is that instead -** of iterating through all instances of a phrase in the current row, these -** APIs are used to iterate through the set of columns in the current row -** that contain one or more instances of a specified phrase. For example: -** -** Fts5PhraseIter iter; -** int iCol; -** for(pApi->xPhraseFirstColumn(pFts, iPhrase, &iter, &iCol); -** iCol>=0; -** pApi->xPhraseNextColumn(pFts, &iter, &iCol) -** ){ -** // Column iCol contains at least one instance of phrase iPhrase -** } -** -** This API can be quite slow if used with an FTS5 table created with the -** "detail=none" option. If the FTS5 table is created with either -** "detail=none" "content=" option (i.e. if it is a contentless table), -** then this API always iterates through an empty set (all calls to -** xPhraseFirstColumn() set iCol to -1). -** -** The information accessed using this API and its companion -** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext -** (or xInst/xInstCount). The chief advantage of this API is that it is -** significantly more efficient than those alternatives when used with -** "detail=column" tables. -** -** xPhraseNextColumn() -** See xPhraseFirstColumn above. -*/ -struct Fts5ExtensionApi { - int iVersion; /* Currently always set to 3 */ - - void *(*xUserData)(Fts5Context*); - - int (*xColumnCount)(Fts5Context*); - int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow); - int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken); - - int (*xTokenize)(Fts5Context*, - const char *pText, int nText, /* Text to tokenize */ - void *pCtx, /* Context passed to xToken() */ - int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ - ); - - int (*xPhraseCount)(Fts5Context*); - int (*xPhraseSize)(Fts5Context*, int iPhrase); - - int (*xInstCount)(Fts5Context*, int *pnInst); - int (*xInst)(Fts5Context*, int iIdx, int *piPhrase, int *piCol, int *piOff); - - sqlite3_int64 (*xRowid)(Fts5Context*); - int (*xColumnText)(Fts5Context*, int iCol, const char **pz, int *pn); - int (*xColumnSize)(Fts5Context*, int iCol, int *pnToken); - - int (*xQueryPhrase)(Fts5Context*, int iPhrase, void *pUserData, - int(*)(const Fts5ExtensionApi*,Fts5Context*,void*) - ); - int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*)); - void *(*xGetAuxdata)(Fts5Context*, int bClear); - - int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); - void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff); - - int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); - void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); -}; - -/* -** CUSTOM AUXILIARY FUNCTIONS -*************************************************************************/ - -/************************************************************************* -** CUSTOM TOKENIZERS -** -** Applications may also register custom tokenizer types. A tokenizer -** is registered by providing fts5 with a populated instance of the -** following structure. All structure methods must be defined, setting -** any member of the fts5_tokenizer struct to NULL leads to undefined -** behaviour. The structure methods are expected to function as follows: -** -** xCreate: -** This function is used to allocate and initialize a tokenizer instance. -** 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 -** 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 -** tokenizer name as part of the CREATE VIRTUAL TABLE statement used -** to create the FTS5 table. -** -** The final argument is an output variable. If successful, (*ppOut) -** should be set to point to the new tokenizer handle and SQLITE_OK -** returned. If an error occurs, some value other than SQLITE_OK should -** be returned. In this case, fts5 assumes that the final value of *ppOut -** is undefined. -** -** xDelete: -** This function is invoked to delete a tokenizer handle previously -** allocated using xCreate(). Fts5 guarantees that this function will -** be invoked exactly once for each successful call to xCreate(). -** -** xTokenize: -** This function is expected to tokenize the nText byte string indicated -** by argument pText. pText may or may not be nul-terminated. The first -** 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 -** tokenization of the supplied text. This is always one of the following -** four values: -** -**
    • FTS5_TOKENIZE_DOCUMENT - A document is being inserted into -** or removed from the FTS table. The tokenizer is being invoked to -** determine the set of tokens to add to (or delete from) the -** FTS index. -** -**
    • FTS5_TOKENIZE_QUERY - A MATCH query is being executed -** against the FTS index. The tokenizer is being called to tokenize -** a bareword or quoted string specified as part of the query. -** -**
    • (FTS5_TOKENIZE_QUERY | FTS5_TOKENIZE_PREFIX) - Same as -** FTS5_TOKENIZE_QUERY, except that the bareword or quoted string is -** followed by a "*" character, indicating that the last token -** returned by the tokenizer will be treated as a token prefix. -** -**
    • FTS5_TOKENIZE_AUX - The tokenizer is being invoked to -** satisfy an fts5_api.xTokenize() request made by an auxiliary -** function. Or an fts5_api.xColumnSize() request made by the same -** on a columnsize=0 database. -**
    -** -** 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 -** arguments are a pointer to a buffer containing the token text, and the -** size of the token in bytes. The 4th and 5th arguments are the byte offsets -** of the first byte of and first byte immediately following the text from -** which the token is derived within the input. -** -** The second argument passed to the xToken() callback ("tflags") should -** normally be set to 0. The exception is if the tokenizer supports -** synonyms. In this case see the discussion below for details. -** -** FTS5 assumes the xToken() callback is invoked for each token in the -** order that they occur within the input text. -** -** If an xToken() callback returns any value other than SQLITE_OK, then -** the tokenization should be abandoned and the xTokenize() method should -** immediately return a copy of the xToken() return value. Or, if the -** input buffer is exhausted, xTokenize() should return SQLITE_OK. Finally, -** if an error occurs with the xTokenize() implementation itself, it -** may abandon the tokenization and return any error code other than -** SQLITE_OK or SQLITE_DONE. -** -** SYNONYM SUPPORT -** -** Custom tokenizers may also support synonyms. Consider a case in which a -** user wishes to query for a phrase such as "first place". Using the -** built-in tokenizers, the FTS5 query 'first + place' will match instances -** of "first place" within the document set, but not alternative forms -** such as "1st place". In some applications, it would be better to match -** all instances of "first place" or "1st place" regardless of which form -** the user specified in the MATCH query text. -** -** There are several ways to approach this in FTS5: -** -**
    1. By mapping all synonyms to a single token. In this case, using -** the above example, this means that the tokenizer returns the -** same token for inputs "first" and "1st". Say that token is in -** fact "first", so that when the user inserts the document "I won -** 1st place" entries are added to the index for tokens "i", "won", -** "first" and "place". If the user then queries for '1st + place', -** the tokenizer substitutes "first" for "1st" and the query works -** as expected. -** -**
    2. By querying the index for all synonyms of each query term -** separately. In this case, when tokenizing query text, the -** tokenizer may provide multiple synonyms for a single term -** within the document. FTS5 then queries the index for each -** synonym individually. For example, faced with the query: -** -** -** ... MATCH 'first place' -** -** the tokenizer offers both "1st" and "first" as synonyms for the -** first token in the MATCH query and FTS5 effectively runs a query -** similar to: -** -** -** ... MATCH '(first OR 1st) place' -** -** except that, for the purposes of auxiliary functions, the query -** still appears to contain just two phrases - "(first OR 1st)" -** being treated as a single phrase. -** -**
    3. By adding multiple synonyms for a single term to the FTS index. -** Using this method, when tokenizing document text, the tokenizer -** provides multiple synonyms for each token. So that when a -** document such as "I won first place" is tokenized, entries are -** added to the FTS index for "i", "won", "first", "1st" and -** "place". -** -** This way, even if the tokenizer does not provide synonyms -** when tokenizing query text (it should not - to do so would be -** inefficient), it doesn't matter if the user queries for -** 'first + place' or '1st + place', as there are entries in the -** FTS index corresponding to both forms of the first token. -**
    -** -** Whether it is parsing document or query text, any call to xToken that -** specifies a tflags argument with the FTS5_TOKEN_COLOCATED bit -** is considered to supply a synonym for the previous token. For example, -** when parsing the document "I won first place", a tokenizer that supports -** synonyms would call xToken() 5 times, as follows: -** -** -** xToken(pCtx, 0, "i", 1, 0, 1); -** xToken(pCtx, 0, "won", 3, 2, 5); -** xToken(pCtx, 0, "first", 5, 6, 11); -** xToken(pCtx, FTS5_TOKEN_COLOCATED, "1st", 3, 6, 11); -** xToken(pCtx, 0, "place", 5, 12, 17); -** -** -** It is an error to specify the FTS5_TOKEN_COLOCATED flag the first time -** xToken() is called. Multiple synonyms may be specified for a single token -** by making multiple calls to xToken(FTS5_TOKEN_COLOCATED) in sequence. -** There is no limit to the number of synonyms that may be provided for a -** single token. -** -** In many cases, method (1) above is the best approach. It does not add -** extra data to the FTS index or require FTS5 to query for multiple terms, -** so it is efficient in terms of disk space and query speed. However, it -** does not support prefix queries very well. If, as suggested above, the -** token "first" is substituted for "1st" by the tokenizer, then the query: -** -** -** ... MATCH '1s*' -** -** will not match documents that contain the token "1st" (as the tokenizer -** will probably not map "1s" to any prefix of "first"). -** -** For full prefix support, method (3) may be preferred. In this case, -** because the index contains entries for both "first" and "1st", prefix -** queries such as 'fi*' or '1s*' will match correctly. However, because -** extra entries are added to the FTS index, this method uses more space -** within the database. -** -** Method (2) offers a midpoint between (1) and (3). Using this method, -** a query such as '1s*' will match documents that contain the literal -** token "1st", but not "first" (assuming the tokenizer is not able to -** provide synonyms for prefixes). However, a non-prefix query like '1st' -** will match against "1st" and "first". This method does not require -** extra disk space, as no extra entries are added to the FTS index. -** On the other hand, it may require more CPU cycles to run MATCH queries, -** 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 -** inefficient. -*/ -typedef struct Fts5Tokenizer Fts5Tokenizer; -typedef struct fts5_tokenizer fts5_tokenizer; -struct fts5_tokenizer { - 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, - 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 */ - ) - ); -}; + /************************************************************************* + ** CUSTOM AUXILIARY FUNCTIONS + ** + ** Virtual table implementations may overload SQL functions by implementing + ** the sqlite3_module.xFindFunction() method. + */ + + typedef struct Fts5ExtensionApi Fts5ExtensionApi; + typedef struct Fts5Context Fts5Context; + typedef struct Fts5PhraseIter Fts5PhraseIter; + + typedef void (*fts5_extension_function)(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 */ + ); + + struct Fts5PhraseIter + { + const unsigned char* a; + const unsigned char* b; + }; + + /* + ** EXTENSION API FUNCTIONS + ** + ** xUserData(pFts): + ** Return a copy of the context pointer the extension function was + ** registered with. + ** + ** xColumnTotalSize(pFts, iCol, pnToken): + ** If parameter iCol is less than zero, set output variable *pnToken + ** to the total number of tokens in the FTS5 table. Or, if iCol is + ** non-negative but less than the number of columns in the table, return + ** the total number of tokens in column iCol, considering all rows in + ** the FTS5 table. + ** + ** If parameter iCol is greater than or equal to the number of columns + ** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g. + ** an OOM condition or IO error), an appropriate SQLite error code is + ** returned. + ** + ** xColumnCount(pFts): + ** Return the number of columns in the table. + ** + ** xColumnSize(pFts, iCol, pnToken): + ** If parameter iCol is less than zero, set output variable *pnToken + ** to the total number of tokens in the current row. Or, if iCol is + ** non-negative but less than the number of columns in the table, set + ** *pnToken to the number of tokens in column iCol of the current row. + ** + ** If parameter iCol is greater than or equal to the number of columns + ** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g. + ** an OOM condition or IO error), an appropriate SQLite error code is + ** returned. + ** + ** This function may be quite inefficient if used with an FTS5 table + ** 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 + ** 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 + ** of (*pz) and (*pn) are undefined. + ** + ** xPhraseCount: + ** 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. + ** + ** xInstCount: + ** Set *pnInst to the total number of occurrences of all phrases within + ** the query within the current row. Return SQLITE_OK 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. If the FTS5 table is created + ** with either "detail=none" or "detail=column" and "content=" option + ** (i.e. if it is a contentless table), then this API always returns 0. + ** + ** xInst: + ** 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(). + ** + ** Usually, 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. + ** + ** This API can be quite slow if used with an FTS5 table created with the + ** "detail=none" or "detail=column" option. + ** + ** xRowid: + ** Returns the rowid of the current row. + ** + ** xTokenize: + ** Tokenize text using the tokenizer belonging to the FTS5 table. + ** + ** xQueryPhrase(pFts5, iPhrase, pUserData, xCallback): + ** This API function is used to query the FTS table for phrase iPhrase + ** of the current query. Specifically, a query equivalent to: + ** + ** ... FROM ftstable WHERE ftstable MATCH $p ORDER BY rowid + ** + ** with $p set to a phrase equivalent to the phrase iPhrase of the + ** current query is executed. Any column filter that applies to + ** phrase iPhrase of the current query is included in $p. For each + ** row visited, the callback function passed as the fourth argument + ** is invoked. The context and API objects passed to the callback + ** function may be used to access the properties of each matched row. + ** Invoking Api.xUserData() returns a copy of the pointer passed as + ** the third argument to pUserData. + ** + ** 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. + ** Otherwise, the error code is propagated upwards. + ** + ** If the query runs to completion without incident, SQLITE_OK is returned. + ** Or, if some error occurs before the query completes or is aborted by + ** the callback, an SQLite error code is returned. + ** + ** + ** xSetAuxdata(pFts5, pAux, xDelete) + ** + ** Save the pointer passed as the second argument as the extension function's + ** "auxiliary data". The pointer may then be retrieved by the current or any + ** future invocation of the same fts5 extension function made as part of + ** the same MATCH query using the xGetAuxdata() API. + ** + ** Each extension function is allocated a single auxiliary data slot for + ** each FTS query (MATCH expression). If the extension function is invoked + ** more than once for a single FTS query, then all invocations share a + ** single auxiliary data context. + ** + ** If there is already an auxiliary data pointer when this function is + ** invoked, then it is replaced by the new pointer. If an xDelete callback + ** was specified along with the original pointer, it is invoked at this + ** point. + ** + ** The xDelete callback, if one is specified, is also invoked on the + ** auxiliary data pointer after the FTS5 query has finished. + ** + ** If an error (e.g. an OOM condition) occurs within this function, + ** the auxiliary data is set to NULL and an error code returned. If the + ** xDelete parameter was not NULL, it is invoked on the auxiliary data + ** pointer before returning. + ** + ** + ** xGetAuxdata(pFts5, bClear) + ** + ** Returns the current auxiliary data pointer for the fts5 extension + ** function. See the xSetAuxdata() method for details. + ** + ** If the bClear argument is non-zero, then the auxiliary data is cleared + ** (set to NULL) before this function returns. In this case the xDelete, + ** if any, is not invoked. + ** + ** + ** xRowCount(pFts5, pnRow) + ** + ** This function is used to retrieve the total number of rows in the table. + ** In other words, the same value that would be returned by: + ** + ** SELECT count(*) FROM ftstable; + ** + ** xPhraseFirst() + ** This function is used, along with type Fts5PhraseIter and the xPhraseNext + ** method, to iterate through all instances of a single query phrase within + ** the current row. This is the same information as is accessible via the + ** xInstCount/xInst APIs. While the xInstCount/xInst APIs are more convenient + ** to use, this API may be faster under some circumstances. To iterate + ** through instances of phrase iPhrase, use the following code: + ** + ** Fts5PhraseIter iter; + ** int iCol, iOff; + ** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff); + ** iCol>=0; + ** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff) + ** ){ + ** // An instance of phrase iPhrase at offset iOff of column iCol + ** } + ** + ** The Fts5PhraseIter structure is defined above. Applications should not + ** modify this structure directly - it should only be used as shown above + ** with the xPhraseFirst() and xPhraseNext() API methods (and by + ** xPhraseFirstColumn() and xPhraseNextColumn() as illustrated below). + ** + ** This API can be quite slow if used with an FTS5 table created with the + ** "detail=none" or "detail=column" option. If the FTS5 table is created + ** with either "detail=none" or "detail=column" and "content=" option + ** (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). + ** + ** xPhraseNext() + ** See xPhraseFirst above. + ** + ** xPhraseFirstColumn() + ** This function and xPhraseNextColumn() are similar to the xPhraseFirst() + ** and xPhraseNext() APIs described above. The difference is that instead + ** of iterating through all instances of a phrase in the current row, these + ** APIs are used to iterate through the set of columns in the current row + ** that contain one or more instances of a specified phrase. For example: + ** + ** Fts5PhraseIter iter; + ** int iCol; + ** for(pApi->xPhraseFirstColumn(pFts, iPhrase, &iter, &iCol); + ** iCol>=0; + ** pApi->xPhraseNextColumn(pFts, &iter, &iCol) + ** ){ + ** // Column iCol contains at least one instance of phrase iPhrase + ** } + ** + ** This API can be quite slow if used with an FTS5 table created with the + ** "detail=none" option. If the FTS5 table is created with either + ** "detail=none" "content=" option (i.e. if it is a contentless table), + ** then this API always iterates through an empty set (all calls to + ** xPhraseFirstColumn() set iCol to -1). + ** + ** The information accessed using this API and its companion + ** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext + ** (or xInst/xInstCount). The chief advantage of this API is that it is + ** significantly more efficient than those alternatives when used with + ** "detail=column" tables. + ** + ** xPhraseNextColumn() + ** See xPhraseFirstColumn above. + */ + struct Fts5ExtensionApi + { + int iVersion; /* Currently always set to 3 */ + + void* (*xUserData)(Fts5Context*); + + int (*xColumnCount)(Fts5Context*); + int (*xRowCount)(Fts5Context*, sqlite3_int64* pnRow); + int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64* pnToken); + + int (*xTokenize)(Fts5Context*, + const char* pText, + int nText, /* Text to tokenize */ + void* pCtx, /* Context passed to xToken() */ + int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ + ); + + int (*xPhraseCount)(Fts5Context*); + int (*xPhraseSize)(Fts5Context*, int iPhrase); + + int (*xInstCount)(Fts5Context*, int* pnInst); + int (*xInst)(Fts5Context*, int iIdx, int* piPhrase, int* piCol, int* piOff); + + sqlite3_int64 (*xRowid)(Fts5Context*); + int (*xColumnText)(Fts5Context*, int iCol, const char** pz, int* pn); + int (*xColumnSize)(Fts5Context*, int iCol, int* pnToken); + + int (*xQueryPhrase)(Fts5Context*, + int iPhrase, + void* pUserData, + int (*)(const Fts5ExtensionApi*, Fts5Context*, void*)); + int (*xSetAuxdata)(Fts5Context*, void* pAux, void (*xDelete)(void*)); + void* (*xGetAuxdata)(Fts5Context*, int bClear); + + int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); + void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int* piCol, int* piOff); + + int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); + void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int* piCol); + }; + + /* + ** CUSTOM AUXILIARY FUNCTIONS + *************************************************************************/ + + /************************************************************************* + ** CUSTOM TOKENIZERS + ** + ** Applications may also register custom tokenizer types. A tokenizer + ** is registered by providing fts5 with a populated instance of the + ** following structure. All structure methods must be defined, setting + ** any member of the fts5_tokenizer struct to NULL leads to undefined + ** behaviour. The structure methods are expected to function as follows: + ** + ** xCreate: + ** This function is used to allocate and initialize a tokenizer instance. + ** 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 + ** 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 + ** tokenizer name as part of the CREATE VIRTUAL TABLE statement used + ** to create the FTS5 table. + ** + ** The final argument is an output variable. If successful, (*ppOut) + ** should be set to point to the new tokenizer handle and SQLITE_OK + ** returned. If an error occurs, some value other than SQLITE_OK should + ** be returned. In this case, fts5 assumes that the final value of *ppOut + ** is undefined. + ** + ** xDelete: + ** This function is invoked to delete a tokenizer handle previously + ** allocated using xCreate(). Fts5 guarantees that this function will + ** be invoked exactly once for each successful call to xCreate(). + ** + ** xTokenize: + ** This function is expected to tokenize the nText byte string indicated + ** by argument pText. pText may or may not be nul-terminated. The first + ** 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 + ** tokenization of the supplied text. This is always one of the following + ** four values: + ** + **
    • FTS5_TOKENIZE_DOCUMENT - A document is being inserted into + ** or removed from the FTS table. The tokenizer is being invoked to + ** determine the set of tokens to add to (or delete from) the + ** FTS index. + ** + **
    • FTS5_TOKENIZE_QUERY - A MATCH query is being executed + ** against the FTS index. The tokenizer is being called to tokenize + ** a bareword or quoted string specified as part of the query. + ** + **
    • (FTS5_TOKENIZE_QUERY | FTS5_TOKENIZE_PREFIX) - Same as + ** FTS5_TOKENIZE_QUERY, except that the bareword or quoted string is + ** followed by a "*" character, indicating that the last token + ** returned by the tokenizer will be treated as a token prefix. + ** + **
    • FTS5_TOKENIZE_AUX - The tokenizer is being invoked to + ** satisfy an fts5_api.xTokenize() request made by an auxiliary + ** function. Or an fts5_api.xColumnSize() request made by the same + ** on a columnsize=0 database. + **
    + ** + ** 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 + ** arguments are a pointer to a buffer containing the token text, and the + ** size of the token in bytes. The 4th and 5th arguments are the byte offsets + ** of the first byte of and first byte immediately following the text from + ** which the token is derived within the input. + ** + ** The second argument passed to the xToken() callback ("tflags") should + ** normally be set to 0. The exception is if the tokenizer supports + ** synonyms. In this case see the discussion below for details. + ** + ** FTS5 assumes the xToken() callback is invoked for each token in the + ** order that they occur within the input text. + ** + ** If an xToken() callback returns any value other than SQLITE_OK, then + ** the tokenization should be abandoned and the xTokenize() method should + ** immediately return a copy of the xToken() return value. Or, if the + ** input buffer is exhausted, xTokenize() should return SQLITE_OK. Finally, + ** if an error occurs with the xTokenize() implementation itself, it + ** may abandon the tokenization and return any error code other than + ** SQLITE_OK or SQLITE_DONE. + ** + ** SYNONYM SUPPORT + ** + ** Custom tokenizers may also support synonyms. Consider a case in which a + ** user wishes to query for a phrase such as "first place". Using the + ** built-in tokenizers, the FTS5 query 'first + place' will match instances + ** of "first place" within the document set, but not alternative forms + ** such as "1st place". In some applications, it would be better to match + ** all instances of "first place" or "1st place" regardless of which form + ** the user specified in the MATCH query text. + ** + ** There are several ways to approach this in FTS5: + ** + **
    1. By mapping all synonyms to a single token. In this case, using + ** the above example, this means that the tokenizer returns the + ** same token for inputs "first" and "1st". Say that token is in + ** fact "first", so that when the user inserts the document "I won + ** 1st place" entries are added to the index for tokens "i", "won", + ** "first" and "place". If the user then queries for '1st + place', + ** the tokenizer substitutes "first" for "1st" and the query works + ** as expected. + ** + **
    2. By querying the index for all synonyms of each query term + ** separately. In this case, when tokenizing query text, the + ** tokenizer may provide multiple synonyms for a single term + ** within the document. FTS5 then queries the index for each + ** synonym individually. For example, faced with the query: + ** + ** + ** ... MATCH 'first place' + ** + ** the tokenizer offers both "1st" and "first" as synonyms for the + ** first token in the MATCH query and FTS5 effectively runs a query + ** similar to: + ** + ** + ** ... MATCH '(first OR 1st) place' + ** + ** except that, for the purposes of auxiliary functions, the query + ** still appears to contain just two phrases - "(first OR 1st)" + ** being treated as a single phrase. + ** + **
    3. By adding multiple synonyms for a single term to the FTS index. + ** Using this method, when tokenizing document text, the tokenizer + ** provides multiple synonyms for each token. So that when a + ** document such as "I won first place" is tokenized, entries are + ** added to the FTS index for "i", "won", "first", "1st" and + ** "place". + ** + ** This way, even if the tokenizer does not provide synonyms + ** when tokenizing query text (it should not - to do so would be + ** inefficient), it doesn't matter if the user queries for + ** 'first + place' or '1st + place', as there are entries in the + ** FTS index corresponding to both forms of the first token. + **
    + ** + ** Whether it is parsing document or query text, any call to xToken that + ** specifies a tflags argument with the FTS5_TOKEN_COLOCATED bit + ** is considered to supply a synonym for the previous token. For example, + ** when parsing the document "I won first place", a tokenizer that supports + ** synonyms would call xToken() 5 times, as follows: + ** + ** + ** xToken(pCtx, 0, "i", 1, 0, 1); + ** xToken(pCtx, 0, "won", 3, 2, 5); + ** xToken(pCtx, 0, "first", 5, 6, 11); + ** xToken(pCtx, FTS5_TOKEN_COLOCATED, "1st", 3, 6, 11); + ** xToken(pCtx, 0, "place", 5, 12, 17); + ** + ** + ** It is an error to specify the FTS5_TOKEN_COLOCATED flag the first time + ** xToken() is called. Multiple synonyms may be specified for a single token + ** by making multiple calls to xToken(FTS5_TOKEN_COLOCATED) in sequence. + ** There is no limit to the number of synonyms that may be provided for a + ** single token. + ** + ** In many cases, method (1) above is the best approach. It does not add + ** extra data to the FTS index or require FTS5 to query for multiple terms, + ** so it is efficient in terms of disk space and query speed. However, it + ** does not support prefix queries very well. If, as suggested above, the + ** token "first" is substituted for "1st" by the tokenizer, then the query: + ** + ** + ** ... MATCH '1s*' + ** + ** will not match documents that contain the token "1st" (as the tokenizer + ** will probably not map "1s" to any prefix of "first"). + ** + ** For full prefix support, method (3) may be preferred. In this case, + ** because the index contains entries for both "first" and "1st", prefix + ** queries such as 'fi*' or '1s*' will match correctly. However, because + ** extra entries are added to the FTS index, this method uses more space + ** within the database. + ** + ** Method (2) offers a midpoint between (1) and (3). Using this method, + ** a query such as '1s*' will match documents that contain the literal + ** token "1st", but not "first" (assuming the tokenizer is not able to + ** provide synonyms for prefixes). However, a non-prefix query like '1st' + ** will match against "1st" and "first". This method does not require + ** extra disk space, as no extra entries are added to the FTS index. + ** On the other hand, it may require more CPU cycles to run MATCH queries, + ** 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 + ** inefficient. + */ + typedef struct Fts5Tokenizer Fts5Tokenizer; + typedef struct fts5_tokenizer fts5_tokenizer; + struct fts5_tokenizer + { + 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, + 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 */ + )); + }; /* Flags that may be passed as the third argument to xTokenize() */ -#define FTS5_TOKENIZE_QUERY 0x0001 -#define FTS5_TOKENIZE_PREFIX 0x0002 -#define FTS5_TOKENIZE_DOCUMENT 0x0004 -#define FTS5_TOKENIZE_AUX 0x0008 +#define FTS5_TOKENIZE_QUERY 0x0001 +#define FTS5_TOKENIZE_PREFIX 0x0002 +#define FTS5_TOKENIZE_DOCUMENT 0x0004 +#define FTS5_TOKENIZE_AUX 0x0008 /* Flags that may be passed by the tokenizer implementation back to FTS5 ** as the third argument to the supplied xToken callback. */ -#define FTS5_TOKEN_COLOCATED 0x0001 /* Same position as prev. token */ - -/* -** END OF CUSTOM TOKENIZERS -*************************************************************************/ +#define FTS5_TOKEN_COLOCATED 0x0001 /* Same position as prev. token */ -/************************************************************************* -** FTS5 EXTENSION REGISTRATION API -*/ -typedef struct fts5_api fts5_api; -struct fts5_api { - int iVersion; /* Currently always set to 2 */ - - /* Create a new tokenizer */ - int (*xCreateTokenizer)( - fts5_api *pApi, - const char *zName, - void *pContext, - fts5_tokenizer *pTokenizer, - void (*xDestroy)(void*) - ); - - /* Find an existing tokenizer */ - int (*xFindTokenizer)( - fts5_api *pApi, - const char *zName, - void **ppContext, - fts5_tokenizer *pTokenizer - ); - - /* Create a new auxiliary function */ - int (*xCreateFunction)( - fts5_api *pApi, - const char *zName, - void *pContext, - fts5_extension_function xFunction, - void (*xDestroy)(void*) - ); -}; - -/* -** END OF REGISTRATION API -*************************************************************************/ + /* + ** END OF CUSTOM TOKENIZERS + *************************************************************************/ + + /************************************************************************* + ** FTS5 EXTENSION REGISTRATION API + */ + typedef struct fts5_api fts5_api; + struct fts5_api + { + int iVersion; /* Currently always set to 2 */ + + /* Create a new tokenizer */ + int (*xCreateTokenizer)(fts5_api* pApi, + const char* zName, + void* pContext, + fts5_tokenizer* pTokenizer, + void (*xDestroy)(void*)); + + /* Find an existing tokenizer */ + int (*xFindTokenizer)(fts5_api* pApi, const char* zName, void** ppContext, fts5_tokenizer* pTokenizer); + + /* Create a new auxiliary function */ + int (*xCreateFunction)(fts5_api* pApi, + const char* zName, + void* pContext, + fts5_extension_function xFunction, + void (*xDestroy)(void*)); + }; + + /* + ** END OF REGISTRATION API + *************************************************************************/ #ifdef __cplusplus -} /* end of the 'extern "C"' block */ +} /* end of the 'extern "C"' block */ #endif #endif /* _FTS5_H */ diff -Nru mstflint-4.17.0+1/flint/cmd_line_parser.cpp mstflint-4.21.0+1/flint/cmd_line_parser.cpp --- mstflint-4.17.0+1/flint/cmd_line_parser.cpp 2021-06-29 08:49:48.000000000 +0000 +++ mstflint-4.21.0+1/flint/cmd_line_parser.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -2,7 +2,7 @@ * * cmd_line_parser.cpp - FLash INTerface * - * Copyright (c) 2013 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2013-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -45,31 +45,34 @@ #include "mlxfwops/lib/flint_io.h" #ifndef FLINT_NAME - #ifdef __GNUC__ - #define FLINT_NAME "flint" - #else - #define FLINT_NAME "flint" - #endif +#ifdef __GNUC__ +#define FLINT_NAME "flint" +#else +#define FLINT_NAME "flint" +#endif #endif using namespace std; /****************************** -* Data structures containing -* Meta data about subcommands -* and flags -******************************/ -class SubCmdMetaData { + * Data structures containing + * Meta data about subcommands + * and flags + ******************************/ +class SubCmdMetaData +{ private: - class SubCmd { -public: - SubCmd(string s, string l, sub_cmd_t n) : shortf(s), longf(l), cmdNum(n) {} - ~SubCmd(){} + class SubCmd + { + public: + SubCmd(string s, string l, sub_cmd_t n) : shortf(s), longf(l), cmdNum(n) {} + ~SubCmd() {} string shortf; string longf; sub_cmd_t cmdNum; }; vector _sCmds; + public: SubCmdMetaData(); ~SubCmdMetaData(); @@ -92,6 +95,7 @@ #ifndef EXTERNAL _sCmds.push_back(new SubCmd("", "smg", SC_Smg)); _sCmds.push_back(new SubCmd("", "set_vpd", SC_Set_Vpd)); + _sCmds.push_back(new SubCmd("", "set_attestation_cert_chain", SC_Set_Cert_Chain)); #endif _sCmds.push_back(new SubCmd("", "sv", SC_Sv)); _sCmds.push_back(new SubCmd("", "ri", SC_Ri)); @@ -121,40 +125,43 @@ _sCmds.push_back(new SubCmd("bc", "binary_compare", SC_Binary_Compare)); _sCmds.push_back(new SubCmd("", "rsa_sign", SC_RSA_Sign)); _sCmds.push_back(new SubCmd("", "import_hsm_key", SC_Import_Hsm_Key)); -#if !defined(UEFI_BUILD) && !defined(NO_OPEN_SSL) _sCmds.push_back(new SubCmd("", "export_public_key", SC_Export_Public_Key)); -#endif } SubCmdMetaData::~SubCmdMetaData() { - for (vector::iterator it = _sCmds.begin(); it != _sCmds.end(); ++it) { + for (vector::iterator it = _sCmds.begin(); it != _sCmds.end(); ++it) + { delete *it; } } sub_cmd_t SubCmdMetaData::getCmdNum(string flag) { - for (vector::iterator it = _sCmds.begin(); it != _sCmds.end(); ++it) { - if (((*it)->shortf == flag) || ((*it)->longf == flag)) { + for (vector::iterator it = _sCmds.begin(); it != _sCmds.end(); ++it) + { + if (((*it)->shortf == flag) || ((*it)->longf == flag)) + { return (*it)->cmdNum; } } return SC_No_Cmd; } - -class FlagMetaData { +class FlagMetaData +{ private: - class Flag { -public: - Flag(string s, string l, int n) : shortf(s), longf(l), argNum(n) {} - ~Flag(){} + class Flag + { + public: + Flag(string s, string l, int n) : shortf(s), longf(l), argNum(n) {} + ~Flag() {} string shortf; string longf; int argNum; }; vector _flags; + public: FlagMetaData(); ~FlagMetaData(); @@ -196,11 +203,14 @@ _flags.push_back(new Flag("", "striped_image", 0)); _flags.push_back(new Flag("", "banks", 1)); _flags.push_back(new Flag("", "log", 1)); - _flags.push_back(new Flag("", "flash_params", 1)); //its actually 3 but separated by comma so we refer to them as one + _flags.push_back(new Flag("", "flash_params", 1)); // its actually 3 but separated by comma so we refer to them as + // one _flags.push_back(new Flag("v", "version", 0)); _flags.push_back(new Flag("", "no_devid_check", 0)); _flags.push_back(new Flag("", "use_fw", 0)); _flags.push_back(new Flag("", "use_dev_img_info", 0)); + _flags.push_back(new Flag("", "ignore_crc_check", 0)); + _flags.push_back(new Flag("", "hexdump_format", 0)); _flags.push_back(new Flag("", "skip_ci_req", 0)); _flags.push_back(new Flag("", "use_dev_rom", 0)); _flags.push_back(new Flag("", "private_key", 1)); @@ -227,24 +237,30 @@ _flags.push_back(new Flag("", "public_key_label", 1)); _flags.push_back(new Flag("", "hsm", 0)); #endif + _flags.push_back(new Flag("", "openssl_engine", 1)); + _flags.push_back(new Flag("", "openssl_key_id", 1)); #ifdef __WIN__ - _flags.push_back(new Flag("", "cpu_util", 1)); + _flags.push_back(new Flag("", "cpu_util", 1)); #endif _flags.push_back(new Flag("", "output_file", 1)); _flags.push_back(new Flag("", "user_password", 1)); + _flags.push_back(new Flag("", "cert_chain_index", 1)); } FlagMetaData::~FlagMetaData() { - for (vector::iterator it = _flags.begin(); it != _flags.end(); ++it) { + for (vector::iterator it = _flags.begin(); it != _flags.end(); ++it) + { delete *it; } } int FlagMetaData::getNumOfArgs(string flag) { - for (vector::iterator it = _flags.begin(); it != _flags.end(); ++it) { - if (((*it)->shortf == flag) || ((*it)->longf == flag)) { + for (vector::iterator it = _flags.begin(); it != _flags.end(); ++it) + { + if (((*it)->shortf == flag) || ((*it)->longf == flag)) + { return (*it)->argNum; } } @@ -256,16 +272,18 @@ *******************/ // NOTE : number of parameters extracted in the container should be checked. -static void splitByDelimiters(std::vector& container, string str, const char *delimiters ) +static void splitByDelimiters(std::vector& container, string str, const char* delimiters) { - if (str.size() == 0) { + if (str.size() == 0) + { return; } - char *cStr = strcpy(new char[str.size() + 1], str.c_str()); - char *ptr; + char* cStr = strcpy(new char[str.size() + 1], str.c_str()); + char* ptr; ptr = strtok(cStr, delimiters); - while (ptr != NULL) { + while (ptr != NULL) + { container.push_back(ptr); ptr = strtok(NULL, delimiters); } @@ -273,16 +291,19 @@ return; } -char* stripFlag(char *flag) +char* stripFlag(char* flag) { - char *strippedFlagStart = NULL; - if (flag[0] != '-') { // not a flag + char* strippedFlagStart = NULL; + if (flag[0] != '-') + { // not a flag return flag; - } - ; - if ((flag[0] == '-') && (flag[1] == '-')) { + }; + if ((flag[0] == '-') && (flag[1] == '-')) + { strippedFlagStart = &flag[2]; - } else { + } + else + { strippedFlagStart = &flag[1]; } return strippedFlagStart; @@ -290,12 +311,15 @@ int countArgs(string args) { - if (args.size() == 0) { + if (args.size() == 0) + { return 0; } int count = 1; - for (string::iterator it = args.begin(); it < args.end(); ++it) { - if (*it == ',') { + for (string::iterator it = args.begin(); it < args.end(); ++it) + { + if (*it == ',') + { count++; } } @@ -304,42 +328,56 @@ bool verifyNumOfArgs(string name, string value) { - //HACK : we don't check device numOfArgs because image device format might contain "," - if ((name == "device") || (name == "d") ) { + // HACK : we don't check device numOfArgs because image device format might contain "," + if ((name == "device") || (name == "d")) + { return true; } - if ((name == "image") || (name == "i") ) { + if ((name == "image") || (name == "i")) + { return true; } // Hack : VSD can be empty or contain "," so we shouldnt count its args - if (name == "vsd") { + if (name == "vsd") + { + return true; + } + if (name == "output_file") + { return true; } int expected = FlagMetaData().getNumOfArgs(name); - if (expected < 0) { + if (expected < 0) + { printf(FLINT_INVALID_FLAG_ERROR, name.c_str()); return false; } - //HACK : flash_params is 3 argument but given with comma separated instead of spaces like the rest + // HACK : flash_params is 3 argument but given with comma separated instead of spaces like the rest // so flag_arg_num gives a wrong value - if (name == "flash_params") { + if (name == "flash_params") + { expected = 3; } int actual = countArgs(value); - if (name == "downstream_device_ids") { - if (actual < 1) { + if (name == "downstream_device_ids") + { + if (actual < 1) + { printf(FLINT_TOO_FEW_MINIMUM_ARGS_ERROR, 1, actual); return false; } return true; } - if (actual < expected) { + if (actual < expected) + { printf(FLINT_TOO_FEW_ARGS_ERROR, expected, actual); return false; - } else if (actual > expected) { + } + else if (actual > expected) + { printf(FLINT_TOO_MANY_ARGS_ERROR, expected, actual); return false; } @@ -348,10 +386,11 @@ bool strToInt(string str, int& num, int base = 0) { - char *endp; - char *numStr = strcpy(new char[str.size() + 1], str.c_str()); + char* endp; + char* numStr = strcpy(new char[str.size() + 1], str.c_str()); num = strtol(numStr, &endp, base); - if (*endp) { + if (*endp) + { delete[] numStr; return false; } @@ -359,13 +398,13 @@ return true; } - bool strToNum(string str, u_int64_t& num, int base = 0) { - char *endp; - char *numStr = strcpy(new char[str.size() + 1], str.c_str()); + char* endp; + char* numStr = strcpy(new char[str.size() + 1], str.c_str()); num = strtoul(numStr, &endp, base); - if (*endp) { + if (*endp) + { delete[] numStr; return false; } @@ -373,16 +412,20 @@ return true; } -//this function is used in extracting both guids and macs from user +// this function is used in extracting both guids and macs from user bool getGUIDFromStr(string str, guid_t& guid, string prefixErr = "") { - char *endp; + char* endp; u_int64_t g; g = strtoull(str.c_str(), &endp, 16); - if (*endp || (g == 0xffffffffffffffffULL && errno == ERANGE)) { - if (prefixErr.size() == 0) { - printf("-E- Invalid Guid/Mac/Uid syntax (%s) %s \n", str.c_str(), errno ? strerror(errno) : "" ); - } else { + if (*endp || (g == 0xffffffffffffffffULL && errno == ERANGE)) + { + if (prefixErr.size() == 0) + { + printf("-E- Invalid Guid/Mac/Uid syntax (%s) %s \n", str.c_str(), errno ? strerror(errno) : ""); + } + else + { printf("%s\n", prefixErr.c_str()); } return false; @@ -394,37 +437,46 @@ bool isElementPresent(vector strv, string str) //* should be renamed to isStrPresent, since it's not generic { - for (vector::iterator it = strv.begin(); it < strv.end(); it++) { - if (*it == str) { + for (vector::iterator it = strv.begin(); it < strv.end(); it++) + { + if (*it == str) + { return true; } } return false; } -bool stringsCommaSplit(string str, std::vector &deviceIds) //* Should be renamed to something like +bool stringsCommaSplit(string str, std::vector& deviceIds) //* Should be renamed to something like { //* getIntsFromCommaSplitString size_t pos; std::vector strv; bool res = true; - while ((pos = str.find(',')) != string::npos) { + while ((pos = str.find(',')) != string::npos) + { string tmp = str.substr(0, pos); - if (!isElementPresent(strv, tmp)) { //* Can be changed to: std::find(strv.begin(), strv.end(), tmp) != strv.end() - strv.push_back((string)tmp); //* So we can remove isElementPresent function + if (!isElementPresent(strv, tmp)) + { //* Can be changed to: std::find(strv.begin(), strv.end(), tmp) != strv.end() + strv.push_back((string)tmp); //* So we can remove isElementPresent function } str = str.substr(pos + 1); } - if (str != "") { - if (!isElementPresent(strv, str)) { //* Same as above + if (str != "") + { + if (!isElementPresent(strv, str)) + { //* Same as above strv.push_back((string)str); } } - for (std::vector::iterator it = strv.begin(); it < strv.end(); it++) { + for (std::vector::iterator it = strv.begin(); it < strv.end(); it++) + { int num; - if (false == strToInt((*it).c_str(), num)) { + if (false == strToInt((*it).c_str(), num)) + { return false; } - if (num < 0 || num > 255) { + if (num < 0 || num > 255) + { printf("Index value should be between 0 and 255.\n"); return false; } @@ -433,7 +485,6 @@ return res; } - guid_t incrGuid(guid_t baseGuid, unsigned int incrVal) { u_int64_t g = baseGuid.h; @@ -458,7 +509,8 @@ // Step 1 split by "," std::vector paramVec; splitByDelimiters(paramVec, params, ","); - if (paramVec.size() != 3) { + if (paramVec.size() != 3) + { return false; } // Step 2 extract params @@ -467,11 +519,13 @@ memset(fp.type_name, 0, MAX_FLASH_NAME); strncpy(fp.type_name, paramStr, length); u_int64_t tmp; - if (!strToNum(paramVec[1], tmp)) { + if (!strToNum(paramVec[1], tmp)) + { return false; } fp.log2size = (int)tmp; - if (!strToNum(paramVec[2], tmp)) { + if (!strToNum(paramVec[2], tmp)) + { return false; } fp.num_of_flashes = (int)tmp; @@ -479,9 +533,9 @@ } /************************************ -* Implementation of the Command line -* Parsing part of the Flint class -************************************/ + * Implementation of the Command line + * Parsing part of the Flint class + ************************************/ #define FLASH_LIST_SZ 256 @@ -506,25 +560,13 @@ "Binary image file.\n" "Commands affected: burn, verify"); - AddOptions("latest_fw", - ' ', - "", - "Commands affected: burn"); - - AddOptions("ir", - ' ', - "", - "Commands affected: burn"); + AddOptions("latest_fw", ' ', "", "Commands affected: burn"); - AddOptions("help", - 'h', - "", - "Prints this message and exits"); + AddOptions("ir", ' ', "", "Commands affected: burn"); - AddOptions("hh", - ' ', - "", - "Prints extended command help"); + AddOptions("help", 'h', "", "Prints this message and exits"); + + AddOptions("hh", ' ', "", "Prints extended command help"); AddOptions("yes", 'y', @@ -624,16 +666,12 @@ "When specified, only flashed fw version is fetched\n" "Commands affected: query"); - AddOptions("nofs", - ' ', - "", - "Burn image in a non failsafe manner."); + AddOptions("nofs", ' ', "", "Burn image in a non failsafe manner."); - AddOptions("allow_psid_change", - ' ', - "", + AddOptions("allow_psid_change", ' ', "", "Allow burning a FW image with a different PSID (Parameter Set ID)than the one currently on flash. Note" - " that changing a PSID may cause the device to malfunction. Use only if you know what you are doing", isExternal); + " that changing a PSID may cause the device to malfunction. Use only if you know what you are doing", + isExternal); AddOptions("allow_rom_change", ' ', @@ -649,15 +687,9 @@ "NOTE: This flag is intended for advanced users only.\n" "Running in this mode may cause the firmware to hang.\n"); - AddOptions("no_flash_verify", - ' ', - "", - "Do not verify each write on the flash."); + AddOptions("no_flash_verify", ' ', "", "Do not verify each write on the flash."); - AddOptions("use_fw", - ' ', - "", - "Flash access will be done using FW (ConnectX-3/ConnectX-3Pro only)."); + AddOptions("use_fw", ' ', "", "Flash access will be done using FW (ConnectX-3/ConnectX-3Pro only)."); AddOptions("silent", 's', @@ -665,10 +697,7 @@ "Do not print burn progress flyer.\n" "Commands affected: burn"); - AddOptions("vsd", - ' ', - "", - "Write this string, of up to 208 characters, to VSD when burn."); + AddOptions("vsd", ' ', "", "Write this string, of up to 208 characters, to VSD when burn."); AddOptions("use_image_ps", ' ', @@ -701,10 +730,7 @@ " Only).\n" "Commands affected: burn"); - AddOptions("no_fw_ctrl", - ' ', - "", - "Do not attempt to work with the FW Ctrl update commands"); + AddOptions("no_fw_ctrl", ' ', "", "Do not attempt to work with the FW Ctrl update commands"); AddOptions("use_dev_img_info", ' ', @@ -713,6 +739,12 @@ "Commands affected: burn", true); + AddOptions("ignore_crc_check", ' ', "", "Prevents flint from failing due to CRC check", + true); // hidden + + AddOptions("hexdump_format", ' ', "", "Prints rb command output in hexdump format", + true); // hidden + AddOptions("dual_image", ' ', "", @@ -726,170 +758,147 @@ "Use this flag to indicate that the given image file is in a \"striped image\" format.\n" "Commands affected: query verify"); - AddOptions("banks", - ' ', - "", - "Set the number of attached flash devices (banks)"); + AddOptions("banks", ' ', "", "Set the number of attached flash devices (banks)"); - AddOptions("log", - ' ', - "", - "Print the burning status to the specified log file"); - - + AddOptions("log", ' ', "", "Print the burning status to the specified log file"); char flashList[FLASH_LIST_SZ] = {0}; char flashParDesc[FLASH_LIST_SZ * 2]; Flash::get_flash_list(flashList, FLASH_LIST_SZ); - snprintf(flashParDesc, FLASH_LIST_SZ * 2, "Use the given parameters to access the flash instead of reading them from " - "the flash.\n" \ - "Supported parameters:\n" \ - "Type: The type of the flash, such as:%s.\n" \ - "log2size: The log2 of the flash size." \ + snprintf(flashParDesc, FLASH_LIST_SZ * 2, + "Use the given parameters to access the flash instead of reading them from " + "the flash.\n" + "Supported parameters:\n" + "Type: The type of the flash, such as:%s.\n" + "log2size: The log2 of the flash size." "num_of_flashes: the number of the flashes connected to the device.", flashList); + AddOptions("flash_params", ' ', "", flashParDesc); - AddOptions("flash_params", - ' ', - "", flashParDesc); + AddOptions("version", 'v', "", "Version info."); - AddOptions("version", - 'v', - "", - "Version info."); + AddOptions("no_devid_check", ' ', "", "ignore device_id checks", true); - AddOptions("no_devid_check", - ' ', - "", - "ignore device_id checks", true); + AddOptions("skip_ci_req", ' ', "", "skip sending cache image request to driver(windows)", true); - AddOptions("skip_ci_req", - ' ', - "", - "skip sending cache image request to driver(windows)", true); + AddOptions("ocr", ' ', "", "another flag for override cache replacement", true); - AddOptions("ocr", - ' ', - "", - "another flag for override cache replacement", true); + AddOptions("hsm", ' ', "", "flag for the sign command", true); + + AddOptions("private_key", ' ', "", "path to PEM formatted private key to be used by the sign command"); + + AddOptions("public_key", ' ', "", "path to PEM formatted public key to be used by the sign command"); + AddOptions("key_uuid", ' ', "", "UUID matching the given private key to be used by the sign command"); + AddOptions("private_key2", ' ', "", "path to PEM formatted private key to be used by the sign command"); + AddOptions("hmac_key", ' ', "", "path to file containing key (For FS4 image only)."); + + AddOptions("key_uuid2", ' ', "", "UUID matching the given private key to be used by the sign command"); - AddOptions("hsm", - ' ', - "", - "flag for the sign command", true); - - AddOptions("private_key", - ' ', - "", - "path to PEM formatted private key to be used by the sign command"); - - AddOptions("public_key", - ' ', - "", - "path to PEM formatted public key to be used by the sign command"); - AddOptions("key_uuid", - ' ', - "", - "UUID matching the given private key to be used by the sign command"); - AddOptions("private_key2", - ' ', - "", - "path to PEM formatted private key to be used by the sign command"); - AddOptions("hmac_key", - ' ', - "", - "path to file containing key (For FS4 image only)."); - - AddOptions("key_uuid2", - ' ', - "", - "UUID matching the given private key to be used by the sign command"); - - AddOptions("psid", - ' ', - "", - "Use this PSID while burning livefish device using MFA2 archive"); - - AddOptions("cc", - ' ', - "", - "Use this flag while burning to device a Congestion Control Component"); - - AddOptions("linkx", - ' ', - "", - "Use this flag while burning to device a LinkX Component"); + AddOptions("psid", ' ', "", "Use this PSID while burning livefish device using MFA2 archive"); + + AddOptions("cc", ' ', "", + "Use this flag while burning to device a Congestion Control Component"); + + AddOptions("linkx", ' ', "", "Use this flag while burning to device a LinkX Component"); AddOptions("downstream_device_id_start_index", - ' ', - "", - "Use this flag while burning to device a LinkX Component. Begin from 0"); + ' ', + "", + "Use this flag while burning to device a LinkX Component. Begin from 0", + false, + false, + 1); AddOptions("num_of_downstream_devices", - ' ', - "", - "Use this flag while burning to device a LinkX Component to specify the number of devices to burn"); - - AddOptions("linkx_auto_update", - ' ', - "", - "Use this flag while burning all cable devices connected to host."); - - AddOptions("activate", - ' ', - "", - "Use this flag to apply the activation of all cable devices connected to host. By default, the activation is not performed."); - - /*AddOptions("activate_delay_sec", - ' ', - "", - "Use this flag to activate all cable devices connected to host with delay (default - 0, immediately). Important: 'activate' flag must be set. This flag is relevant only for cable components.");*/ - - AddOptions("download_transfer", - ' ', - "", - "Use this flag to perform the download and transfer of all cable data for cables. By default, the download and transfer are not performed . This flag is relevant only for cable components."); - - AddOptions("downstream_device_ids", - ' ', - "", - "Use this flag to specify the LNKX ports to perform query. List must be only comma-separated numbers, without spaces."); + ' ', + "", + "Use this flag while burning to device a LinkX Component to specify the number of devices to burn", + false, + false, + 1); + + AddOptions("linkx_auto_update", ' ', "", "Use this flag while burning all cable devices connected to host.", false, + false, 1); + + AddOptions( + "activate", + ' ', + "", + "Use this flag to apply the activation of all cable devices connected to host. By default, the activation is not performed.", + false, + false, + 1); + + AddOptions( + "activate_delay_sec", + ' ', + "", + "Use this flag to activate all cable devices connected to host with delay, acceptable values are between 0 and 255 (default - 1). Important: 'activate' flag must be set. This flag is relevant only for cable components.", + false, + false, + 1); + + AddOptions( + "download_transfer", + ' ', + "", + "Use this flag to perform the download and transfer of all cable data for cables. By default, the download and transfer are not performed . This flag is relevant only for cable components.", + false, + false, + 1); + + AddOptions( + "downstream_device_ids", + ' ', + "", + "Use this flag to specify the LNKX ports to perform query. List must be only comma-separated numbers, without spaces.", + false, + false, + 1); #ifndef __WIN__ - AddOptions("public_key_label", - ' ', - "", - "public key label to be used by the sign --hsm command"); - - AddOptions("private_key_label", - ' ', - "", - "private key label to be used by the sign --hsm command"); + AddOptions("public_key_label", ' ', "", "public key label to be used by the sign --hsm command"); + + AddOptions("private_key_label", ' ', "", "private key label to be used by the sign --hsm command"); #endif - AddOptions("output_file", - ' ', - "", - "output file name for exporting the public key from PEM/BIN"); - AddOptions("user_password", - ' ', - "", - "the HSM user password string in order to work with HSM device"); + AddOptions( + "openssl_engine", + ' ', + "", + "Name of the OpenSSL engine to used by the sign/rsa_sign commands to work with the HSM hardware via OpenSSL API"); + AddOptions( + "openssl_key_id", + ' ', + "", + "Key identification string to be used by the sign/rsa_sign commands to work with the HSM hardware via OpenSSL API"); + AddOptions("output_file", ' ', "", "output file name for exporting the public key from PEM/BIN"); + AddOptions("user_password", ' ', "", "the HSM user password string in order to work with HSM device"); #ifdef __WIN__ - AddOptions("cpu_util", - ' ', - "", - "Use this flag to reduce CPU utilization while burning, Windows only. Legal values are from 1 (lowest CPU) to 5 (highest CPU)"); + AddOptions( + "cpu_util", + ' ', + "", + "Use this flag to reduce CPU utilization while burning, Windows only. Legal values are from 1 (lowest CPU) to 5 (highest CPU)"); #endif - - for (map_sub_cmd_t_to_subcommand::iterator it = _subcommands.begin(); it != _subcommands.end(); it++) { - if (it->first == SC_ResetCfg) { + AddOptions( + "cert_chain_index", + ' ', + "", + "Use this flag to specify the certificate location, acceptable values are between 0 and 7 (default - 0). " + "In case index=0 certificate chain will be stored at CERT_CHAIN_0 section, otherwise at DIGITAL_CERT_RW " + "section according to given index.\n" + "This flag is relevant only for set_attestation_cert_chain command."); + + for (map_sub_cmd_t_to_subcommand::iterator it = _subcommands.begin(); it != _subcommands.end(); it++) + { + if (it->first == SC_ResetCfg) + { // hidden command so "forget" mentioning it continue; } - string str1 = it->second->getFlagL() + ((it->second->getFlagS() == "") ? (" ") : - ("|")) + it->second->getFlagS() \ - + " " + it->second->getParam(); + string str1 = it->second->getFlagL() + ((it->second->getFlagS() == "") ? (" ") : ("|")) + + it->second->getFlagS() + " " + it->second->getParam(); string str2 = it->second->getDesc(); AddOptionalSectionData("COMMANDS SUMMARY", str1, str2); @@ -897,304 +906,489 @@ AddOptionalSectionData("RETURN VALUES", "0", "Successful completion."); AddOptionalSectionData("RETURN VALUES", "1", "An error has occurred."); - AddOptionalSectionData("RETURN VALUES", "7", "For burn command - burning new firmware option was not chosen by the user when prompted, thus the firmware burning process was aborted."); - - - for (map_sub_cmd_t_to_subcommand::iterator it = _subcommands.begin(); it != _subcommands.end(); it++) { - if (it->first == SC_ResetCfg) { + AddOptionalSectionData( + "RETURN VALUES", "7", + "For burn command - burning new firmware option was not chosen by the user when prompted, thus the firmware burning process was aborted."); + + for (map_sub_cmd_t_to_subcommand::iterator it = _subcommands.begin(); it != _subcommands.end(); it++) + { + if (it->first == SC_ResetCfg) + { // hidden command so "forget" mentioning it continue; } - string str = "Name:\n" - "\t" + it->second->getName() + "\n" - + "Description:\n" - "\t" + it->second->getExtDesc() + "\n" - + "Command:\n" - "\t" + it->second->getFlagL() + - ((it->second->getFlagS() == "") ? (" ") : ("|")) + it->second->getFlagS() - + " " + it->second->getParam() + "\n" - + "Parameters:\n" - "\t" + it->second->getParamExp() + "\n" - + "Examples:\n" - "\t" + it->second->getExample() + "\n\n\n"; + string str = "Name:\n" + "\t" + + it->second->getName() + "\n" + + "Description:\n" + "\t" + + it->second->getExtDesc() + "\n" + + "Command:\n" + "\t" + + it->second->getFlagL() + ((it->second->getFlagS() == "") ? (" ") : ("|")) + + it->second->getFlagS() + " " + it->second->getParam() + "\n" + + "Parameters:\n" + "\t" + + it->second->getParamExp() + "\n" + + "Examples:\n" + "\t" + + it->second->getExample() + "\n\n\n"; AddOptionalSectionData("COMMANDS DESCRIPTION", str); } - _cmdParser.AddRequester(this); } ParseStatus Flint::HandleOption(string name, string value) { - //TODO: consider verifying num of args inside each if statements that needs its arg num verified + // TODO: consider verifying num of args inside each if statements that needs its arg num verified // thus we will be able to get rid of the hacks inside the function in the expense of a longer code. - if (!(verifyNumOfArgs(name, value))) { + if (!(verifyNumOfArgs(name, value))) + { return PARSE_ERROR; } int delta = 1; - if (name == "device" || name == "d") { + if (name == "device" || name == "d") + { _flintParams.device_specified = true; _flintParams.device = value; - } else if (name == "help" || name == "h") { + } + else if (name == "help" || name == "h") + { vector excluded_sections; excluded_sections.push_back("COMMANDS DESCRIPTION"); cout << _cmdParser.GetUsage(false, excluded_sections); return PARSE_OK_WITH_EXIT; - } else if (name == "version" || name == "v") { + } + else if (name == "version" || name == "v") + { #ifdef EXTERNAL print_version_string(FLINT_NAME, ""); #else print_version_string(FLINT_NAME "(oem)", ""); #endif return PARSE_OK_WITH_EXIT; - } else if (name == "hh") { + } + else if (name == "hh") + { cout << _cmdParser.GetUsage(); return PARSE_OK_WITH_EXIT; - } else if (name == "no_devid_check") { + } + else if (name == "no_devid_check") + { _flintParams.no_devid_check = true; - } else if (name == "skip_ci_req") { + } + else if (name == "skip_ci_req") + { _flintParams.skip_ci_req = true; - } else if (name == "guid") { + } + else if (name == "guid") + { _flintParams.guid_specified = true; guid_t g; - if (!getGUIDFromStr(value, g)) { + if (!getGUIDFromStr(value, g)) + { return PARSE_ERROR; } - for (int i = 0; i < GUIDS; i++) { + for (int i = 0; i < GUIDS; i++) + { _flintParams.user_guids.push_back(incrGuid(g, i)); } - // for (std::vector::iterator it=_flintParams.user_guids.begin();it != _flintParams.user_guids.end(); it++){ + // for (std::vector::iterator it=_flintParams.user_guids.begin();it != _flintParams.user_guids.end(); + // it++){ // printf("%8.8x%8.8x\n",it->h,it->l); // } - } else if (name == "guids") { + } + else if (name == "guids") + { _flintParams.guids_specified = true; std::vector strs; splitByDelimiters(strs, value, ","); - if (strs.size() != GUIDS) { + if (strs.size() != GUIDS) + { return PARSE_ERROR; } - for (int i = 0; i < GUIDS; i++) { + for (int i = 0; i < GUIDS; i++) + { guid_t g; - if (!getGUIDFromStr(strs[i], g)) { + if (!getGUIDFromStr(strs[i], g)) + { return PARSE_ERROR; - } else { + } + else + { _flintParams.user_guids.push_back(g); } } - } else if (name == "mac") { + } + else if (name == "mac") + { _flintParams.mac_specified = true; guid_t m; - if (!getGUIDFromStr(value, m)) { + if (!getGUIDFromStr(value, m)) + { return PARSE_ERROR; } - for (int i = 0; i < MACS; i++) { + for (int i = 0; i < MACS; i++) + { _flintParams.user_macs.push_back(incrGuid(m, i)); } - } else if (name == "macs") { + } + else if (name == "macs") + { _flintParams.macs_specified = true; std::vector strs; splitByDelimiters(strs, value, ","); - if (strs.size() != MACS) { + if (strs.size() != MACS) + { return PARSE_ERROR; } - for (int i = 0; i < MACS; i++) { + for (int i = 0; i < MACS; i++) + { guid_t m; - if (!getGUIDFromStr(strs[i], m)) { + if (!getGUIDFromStr(strs[i], m)) + { return PARSE_ERROR; - } else { + } + else + { _flintParams.user_macs.push_back(m); } } - } else if (name == "uid") { + } + else if (name == "uid") + { _flintParams.uid_specified = true; - if (!getGUIDFromStr(value, _flintParams.baseUid)) { + if (!getGUIDFromStr(value, _flintParams.baseUid)) + { return PARSE_ERROR; } - } else if (name == "blank_guids") { + } + else if (name == "blank_guids") + { _flintParams.blank_guids = true; - } else if (name == "clear_semaphore") { + } + else if (name == "clear_semaphore") + { _flintParams.clear_semaphore = true; - } else if (name == "image" || name == "i") { + } + else if (name == "image" || name == "i") + { _flintParams.image_specified = true; _flintParams.image = value; - } else if (name == "qq") { + } + else if (name == "qq") + { _flintParams.quick_query = true; _flintParams.skip_rom_query = true; delta = 2; - } else if (name == "low_cpu") { + } + else if (name == "low_cpu") + { _flintParams.low_cpu = true; - } else if (name == "next_boot_fw_ver" || name == "flashed_version") { + } + else if (name == "next_boot_fw_ver" || name == "flashed_version") + { _flintParams.next_boot_fw_ver = true; - } else if (name == "nofs") { + } + else if (name == "nofs") + { _flintParams.nofs = true; - } else if (name == "allow_psid_change") { + } + else if (name == "allow_psid_change") + { _flintParams.allow_psid_change = true; - } else if (name == "allow_rom_change") { + } + else if (name == "allow_rom_change") + { _flintParams.allow_rom_change = true; - } else if (name == "override_cache_replacement" || name == "ocr") { + } + else if (name == "override_cache_replacement" || name == "ocr") + { _flintParams.override_cache_replacement = true; - } else if (name == "use_fw") { + } + else if (name == "use_fw") + { _flintParams.use_fw = true; - } else if (name == "no_flash_verify") { + } + else if (name == "no_flash_verify") + { _flintParams.no_flash_verify = true; - } else if (name == "silent" || name == "s") { + } + else if (name == "silent" || name == "s") + { _flintParams.silent = true; - } else if (name == "yes" || name == "y") { + } + else if (name == "yes" || name == "y") + { _flintParams.yes = true; - } else if (name == "no") { + } + else if (name == "no") + { _flintParams.no = true; - } else if (name == "vsd") { + } + else if (name == "vsd") + { _flintParams.vsd_specified = true; _flintParams.vsd = value; - } else if (name == "use_image_ps") { + } + else if (name == "use_image_ps") + { _flintParams.use_image_ps = true; - } else if (name == "use_image_guids") { + } + else if (name == "use_image_guids") + { _flintParams.use_image_guids = true; - } else if (name == "use_image_rom") { + } + else if (name == "use_image_rom") + { _flintParams.use_image_rom = true; - } else if (name == "use_dev_rom") { + } + else if (name == "use_dev_rom") + { _flintParams.use_dev_rom = true; - } else if (name == "ignore_dev_data") { + } + else if (name == "ignore_dev_data") + { _flintParams.ignore_dev_data = true; - } else if (name == "no_fw_ctrl") { + } + else if (name == "no_fw_ctrl") + { _flintParams.no_fw_ctrl = true; - } else if (name == "dual_image") { + } + else if (name == "dual_image") + { _flintParams.dual_image = true; - } else if (name == "striped_image") { + } + else if (name == "striped_image") + { _flintParams.striped_image = true; - } else if (name == "use_dev_img_info") { + } + else if (name == "use_dev_img_info") + { _flintParams.use_dev_img_info = true; - } else if (name == "ir") { + } + else if (name == "ignore_crc_check") + { + _flintParams.ignore_crc_check = true; + } + else if (name == "hexdump_format") + { + _flintParams.hexdump_format = true; + } + else if (name == "ir") + { _flintParams.image_reactivation = true; - } else if (name == "banks") { + } + else if (name == "banks") + { _flintParams.banks_specified = true; u_int64_t banksNum; - if (!strToNum(value, banksNum)) { + if (!strToNum(value, banksNum)) + { return PARSE_ERROR; } _flintParams.banks = (int)banksNum; - } else if (name == "log") { + } + else if (name == "log") + { _flintParams.log_specified = true; _flintParams.log = value; - } else if (name == "flash_params") { + } + else if (name == "flash_params") + { _flintParams.flash_params_specified = true; - if (!parseFlashParams(value, _flintParams.flash_params)) { + if (!parseFlashParams(value, _flintParams.flash_params)) + { return PARSE_ERROR; } - //printf("-D- flashType=%s , log2size = %d , numOfBanks = %d\n", _flintParams.flash_params.type_name, _flintParams.flash_params.log2size, _flintParams.flash_params.num_of_flashes); - } else if (name == "private_key") { + // printf("-D- flashType=%s , log2size = %d , numOfBanks = %d\n", _flintParams.flash_params.type_name, + // _flintParams.flash_params.log2size, _flintParams.flash_params.num_of_flashes); + } + else if (name == "private_key") + { _flintParams.privkey_specified = true; _flintParams.privkey_file = value; - } else if (name == "public_key") { + } + else if (name == "public_key") + { _flintParams.pubkey_specified = true; _flintParams.pubkey_file = value; - } else if (name == "key_uuid") { + } + else if (name == "key_uuid") + { _flintParams.uuid_specified = true; _flintParams.privkey_uuid = value; - } else if (name == "hmac_key") { + } + else if (name == "hmac_key") + { _flintParams.key_specified = true; _flintParams.key = value; - } else if (name == "private_key2") { + } + else if (name == "private_key2") + { _flintParams.privkey2_specified = true; _flintParams.privkey2_file = value; - } else if (name == "key_uuid2") { + } + else if (name == "key_uuid2") + { _flintParams.uuid2_specified = true; _flintParams.privkey2_uuid = value; - } else if (name == "latest_fw") { + } + else if (name == "latest_fw") + { _flintParams.use_latest_fw_version = true; - } else if (name == "psid") { + } + else if (name == "psid") + { _flintParams.use_psid = true; _flintParams.psid = value; } - else if (name == "cc") { + else if (name == "cc") + { _flintParams.congestion_control = true; _flintParams.congestion_control_param = value; } - else if (name == "linkx") { + else if (name == "linkx") + { _flintParams.linkx_control = true; } - else if (name == "cpu_util") { - _flintParams.use_cpu_utilization = true; - u_int64_t cpu_percent = 0; - if (!strToNum(value, cpu_percent)) { - return PARSE_ERROR; - } - _flintParams.cpu_percent = (int)cpu_percent; - } else if (name == "hsm") { + else if (name == "cpu_util") + { + _flintParams.use_cpu_utilization = true; + u_int64_t cpu_percent = 0; + if (!strToNum(value, cpu_percent)) + { + return PARSE_ERROR; + } + _flintParams.cpu_percent = (int)cpu_percent; + } + else if (name == "hsm") + { _flintParams.hsm_specified = true; - } else if (name == "private_key_label") { + } + else if (name == "openssl_engine") + { + _flintParams.openssl_engine_usage_specified = true; + _flintParams.openssl_engine = value; + } + else if (name == "openssl_key_id") + { + _flintParams.openssl_engine_usage_specified = true; + _flintParams.openssl_key_id = value; + } + else if (name == "private_key_label") + { _flintParams.private_key_label_specified = true; _flintParams.private_key_label = value; - } else if (name == "public_key_label") { + } + else if (name == "public_key_label") + { _flintParams.public_key_label_specified = true; _flintParams.public_key_label = value; - } else if (name == "output_file") { + } + else if (name == "output_file") + { _flintParams.output_file_specified = true; _flintParams.output_file = value; - } else if (name == "user_password") { + } + else if (name == "user_password") + { _flintParams.hsm_password_specified = true; _flintParams.hsm_password = value; - } else if (name == "downstream_device_id_start_index") { + } + else if (name == "downstream_device_id_start_index") + { _flintParams.cable_device_index_specified = true; int device_index = 0; - if (!strToInt(value, device_index)) { + if (!strToInt(value, device_index)) + { return PARSE_ERROR; } - if (device_index < 0) { + if (device_index < 0) + { return PARSE_ERROR; } _flintParams.cableDeviceIndex = device_index; - } - - else if (name == "activate") { - _flintParams.activate = true; } - else if (name == "activate_delay_sec") { + else if (name == "activate") + { + _flintParams.activate = true; + } + + else if (name == "activate_delay_sec") + { u_int64_t activate_delay_sec = 0; - if (!strToNum(value, activate_delay_sec)) { + if (!strToNum(value, activate_delay_sec)) + { return PARSE_ERROR; } - if (activate_delay_sec > 255) { + if (activate_delay_sec > 255) + { printf("Activation_delay_sec should be between 0 and 255.\n"); return PARSE_ERROR; } _flintParams.activate_delay_sec = activate_delay_sec; } - else if (name == "linkx_auto_update") { + else if (name == "linkx_auto_update") + { _flintParams.linkx_auto_update = true; - } + } - else if (name == "download_transfer") { - _flintParams.download_transfer = true; + else if (name == "download_transfer") + { + _flintParams.download_transfer = true; } - - else if (name == "downstream_device_ids") { - if (value == "all") { + + else if (name == "downstream_device_ids") + { + if (value == "all") + { _flintParams.linkx_auto_update = true; } - else { + else + { _flintParams.downstream_device_ids_specified = true; std::vector deviceIds; - if (!stringsCommaSplit(value, deviceIds)) { + if (!stringsCommaSplit(value, deviceIds)) + { return PARSE_ERROR; } _flintParams.downstream_device_ids = deviceIds; } } - else if (name == "num_of_downstream_devices") { + else if (name == "num_of_downstream_devices") + { int cableDeviceSize = 0; - if (!strToInt(value, cableDeviceSize)) { + if (!strToInt(value, cableDeviceSize)) + { return PARSE_ERROR; } - if (cableDeviceSize <= 0 || cableDeviceSize > 255) { + if (cableDeviceSize <= 0 || cableDeviceSize > 255) + { printf("Cable size should be between 1 and 255.\n"); return PARSE_ERROR; } _flintParams.cableDeviceSize = cableDeviceSize; _flintParams.cable_device_size_specified = true; } - else { + else if (name == "cert_chain_index") + { + int cert_chain_index = 0; + if (!strToInt(value, cert_chain_index)) + { + return PARSE_ERROR; + } + if (cert_chain_index < 0 || cert_chain_index > 7) + { + printf("certificate chain index should be between 0 and 7.\n"); + return PARSE_ERROR; + } + _flintParams.cert_chain_index = cert_chain_index; + } + else + { cout << "Unknown Flag: " << name; cout << _cmdParser.GetSynopsis(); return PARSE_ERROR; @@ -1205,23 +1399,26 @@ #define IS_NUM(cha) (((cha) >= '0') && ((cha) <= '9')) -ParseStatus Flint::parseCmdLine(int argc, char *argv[]) +ParseStatus Flint::parseCmdLine(int argc, char* argv[]) { - //Step1 separate between option section and cmd section + // Step1 separate between option section and cmd section SubCmdMetaData subCmds; FlagMetaData flags; - char **argvCmd = NULL; - char **argvOpt = NULL; + char** argvCmd = NULL; + char** argvOpt = NULL; int argcCmd = 0, argcOpt = 0; bool foundOptionWhenLookingForCmd = false; ParseStatus rc; - char **newArgv = NULL; + char** newArgv = NULL; int newArgc = 0; int i = 1, j = 1, argStart = 1, argEnd = 1; - for (int k = (argc - 1); k > 0; k--) { // Parsing command line from end to start - if ((subCmds.getCmdNum(argv[k])) != SC_No_Cmd) { // i.e we found a subcommand - if (foundOptionWhenLookingForCmd) { + for (int k = (argc - 1); k > 0; k--) + { // Parsing command line from end to start + if ((subCmds.getCmdNum(argv[k])) != SC_No_Cmd) + { // i.e we found a subcommand + if (foundOptionWhenLookingForCmd) + { cout << "Specifying options flags after command is not allowed.\n\n"; rc = PARSE_ERROR_SHOW_USAGE; goto clean_up; @@ -1230,45 +1427,56 @@ argvOpt = argv; argcCmd = argc - k; argvCmd = &argv[k]; - } else if (argv[k][0] == '-' && !IS_NUM(argv[k][1])) { + } + else if (argv[k][0] == '-' && !IS_NUM(argv[k][1])) + { foundOptionWhenLookingForCmd = true; } } - if (argcCmd == 0) { - //no subcommand found + if (argcCmd == 0) + { + // no subcommand found argcOpt = argc; argvOpt = argv; } - //printf("-D- argcOpt:%d argvOpt:%s argcCmd:%d argvCmd:%s\n", argcOpt, argvOpt[0], argcCmd, argvCmd[0]); + // printf("-D- argcOpt:%d argvOpt:%s argcCmd:%d argvCmd:%s\n", argcOpt, argvOpt[0], argcCmd, argvCmd[0]); //_cmdparser should deal with the case of no arguments in argv except the program. - //Step2 unite with comma multiple args in the options section + // Step2 unite with comma multiple args in the options section newArgv = new char*[argcOpt]; - //first arg is the flint command we can copy as is + // first arg is the flint command we can copy as is newArgv[0] = strcpy(new char[strlen(argvOpt[0]) + 1], argvOpt[0]); - while (i < argcOpt) { - if (argvOpt[i][0] == '-' && !IS_NUM(argvOpt[i][1])) { //its a flag (and not a number) so we copy to new_argv as is - newArgv[j] = strcpy(new char[strlen(argvOpt[i]) + 1], - argvOpt[i]); + while (i < argcOpt) + { + if (argvOpt[i][0] == '-' && !IS_NUM(argvOpt[i][1])) + { // its a flag (and not a number) so we copy to new_argv as is + newArgv[j] = strcpy(new char[strlen(argvOpt[i]) + 1], argvOpt[i]); i++; j++; - } else { //its an argument - //find next flag if exsists + } + else + { // its an argument + // find next flag if exsists argStart = i; argEnd = i; int argsSize = 0; - while (argEnd < argcOpt && (argvOpt[argEnd][0] != '-' || IS_NUM(argvOpt[argEnd][1]))) { - argsSize += strlen(argvOpt[argEnd]) + 1; //for the comma + while (argEnd < argcOpt && (argvOpt[argEnd][0] != '-' || IS_NUM(argvOpt[argEnd][1]))) + { + argsSize += strlen(argvOpt[argEnd]) + 1; // for the comma argEnd++; } i = argEnd; - //concatenate all the args with comma between them to a single string + // concatenate all the args with comma between them to a single string newArgv[j] = new char[argsSize + 1]; newArgv[j][0] = '\0'; - while (argStart < argEnd) { - if (argStart == argEnd - 1) { - //no need to add comma to the last arg + while (argStart < argEnd) + { + if (argStart == argEnd - 1) + { + // no need to add comma to the last arg strcat(newArgv[j], argvOpt[argStart]); - } else { + } + else + { strcat(newArgv[j], argvOpt[argStart]); strcat(newArgv[j], ","); } @@ -1279,44 +1487,55 @@ } newArgc = j; - //Step3 set the command and its args in the FlintParams struct if present - if (argcCmd > 0) { + // Step3 set the command and its args in the FlintParams struct if present + if (argcCmd > 0) + { this->_flintParams.cmd = subCmds.getCmdNum(argvCmd[0]); - for (int i = 1; i < argcCmd; ++i) { + for (int i = 1; i < argcCmd; ++i) + { this->_flintParams.cmd_params.push_back(string(argvCmd[i])); } - } else { //no command found deal with either missing/invalid command - //find last flag + } + else + { // no command found deal with either missing/invalid command + // find last flag int lastFlagPos; - char *strippedFlag; - for (lastFlagPos = argc - 1; lastFlagPos > 0; lastFlagPos--) { - if (argv[lastFlagPos][0] == '-' && !IS_NUM(argv[lastFlagPos][1])) { + char* strippedFlag; + for (lastFlagPos = argc - 1; lastFlagPos > 0; lastFlagPos--) + { + if (argv[lastFlagPos][0] == '-' && !IS_NUM(argv[lastFlagPos][1])) + { break; } } - if (lastFlagPos == 0) { + if (lastFlagPos == 0) + { cout << FLINT_NO_OPTIONS_FOUND_ERROR << endl; rc = PARSE_ERROR; goto clean_up; } strippedFlag = stripFlag(argv[lastFlagPos]); - //check how many args it needs + // check how many args it needs int numOfArgs = flags.getNumOfArgs(strippedFlag); - //if too many args return arg in pos num_of_args+1 as the invalid command. - if ((argc - 1 - lastFlagPos) > numOfArgs) { + // if too many args return arg in pos num_of_args+1 as the invalid command. + if ((argc - 1 - lastFlagPos) > numOfArgs) + { printf(FLINT_INVALID_COMMAD_ERROR, argv[argc - 1]); rc = PARSE_ERROR_SHOW_USAGE; goto clean_up; } } - //Step5 parse option section using _cmdParser + // Step5 parse option section using _cmdParser rc = this->_cmdParser.ParseOptions(newArgc, newArgv); // Step 6 Delete allocated memory -clean_up: for (int i = 0; i < newArgc; i++) { +clean_up: + for (int i = 0; i < newArgc; i++) + { delete[] newArgv[i]; } delete[] newArgv; - if (rc == PARSE_ERROR_SHOW_USAGE) { + if (rc == PARSE_ERROR_SHOW_USAGE) + { cout << _cmdParser.GetSynopsis(); } return rc; diff -Nru mstflint-4.17.0+1/flint/err_msgs.h mstflint-4.21.0+1/flint/err_msgs.h --- mstflint-4.17.0+1/flint/err_msgs.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/flint/err_msgs.h 2022-07-31 09:33:57.000000000 +0000 @@ -3,6 +3,7 @@ * err_msgs.h - FLash INTerface * * Copyright (c) 2013 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -36,7 +37,6 @@ * */ - #ifndef __ERR_MSGS_H__ #define __ERR_MSGS_H__ @@ -44,7 +44,8 @@ * Flint Status Code *********************/ -typedef enum { +typedef enum +{ FLINT_SUCCESS = 0, FLINT_FAILED = 1, FLINT_QUERY_ERROR = 2, @@ -55,141 +56,150 @@ * Flint Error Messages **********************/ -#define FLINT_CLEAR_SEM_CMD_ERROR "No command is allowed when -clear_semaphore flag is given.\n" -#define FLINT_COMMAND_FLAGS_ERROR "For %s command, Please specify %s.\n" -#define FLINT_COMMAND_INCORRECT_FLAGS_ERROR "For %s command, %s.\n" -#define FLINT_PARSE_MEM_ERROR "Failed to allocate memory for parsing.\n " -#define FLINT_NO_OPTIONS_FOUND_ERROR "No options found. " -#define FLINT_INVALID_COMMAD_ERROR "Invalid command: %s\n" -#define FLINT_TOO_MANY_ARGS_ERROR "Too many arguments. Expected: %d , Received: %d\n" -#define FLINT_TOO_FEW_ARGS_ERROR "Too few arguments. Expected: %d , Received: %d\n" -#define FLINT_TOO_FEW_MINIMUM_ARGS_ERROR "Too few arguments. Expected at least: %d , Received: %d\n" -#define FLINT_NO_COMMAND_ERROR "No command found." -#define FLINT_OPEN_FWOPS_DEVICE_ERROR "Cannot open Device: %s. %s\n" -#define FLINT_OPEN_FWOPS_DEVICE_ERROR_1 "Cannot open Device: %s.\n" -#define FLINT_OPEN_FWOPS_IMAGE_ERROR "Cannot open Image: %s. %s\n" -#define FLINT_DEVICE_AND_IMAGE_ERROR "Please specify either Device or Image.\n" -#define FLINT_NO_DEVICE_ERROR "Please specify Device.\n" -#define FLINT_NO_IMAGE_ERROR "Please specify Image file.\n" -#define FLINT_MISSED_ARG_ERROR "Missed %s parameter after \"%s\" command.\n" -#define FLINT_CMD_ARGS_ERROR "Command \"%s\" requires %d arguments, but %d arguments were given\n" -#define FLINT_CMD_ARGS_ERROR2 "Command \"%s\" requires at most %d arguments, but %d arguments were given\n" -#define FLINT_CMD_ARGS_ERROR3 "Command \"%s\" requires at least %d arguments, but %d arguments were given\n" -#define FLINT_CMD_ARGS_ERROR4 "Command \"%s\" requires %d or %d arguments, but %d arguments were given\n" -#define FLINT_CMD_ARGS_ERROR5 "Command \"%s\" does not require arguments\n" -#define FLINT_INVALID_OPTION_ERROR "Unknown option \"%s\" for the \"%s\" command. you can use %s.\n" -#define FLINT_INVALID_FLAG_ERROR "Invalid switch \"%s\" is specified.\n" -#define FLINT_INVALID_FLAG_ERROR_5TH_GEN "Invalid switch \"%s\" is specified for 5th gen device.\n" -#define FLINT_INVALID_FLAG_WITH_FLAG_ERROR "Cannot specify \"%s\" flag with \"%s\" flag.\n" +#define FLINT_CLEAR_SEM_CMD_ERROR "No command is allowed when -clear_semaphore flag is given.\n" +#define FLINT_COMMAND_FLAGS_ERROR "For %s command, Please specify %s.\n" +#define FLINT_COMMAND_INCORRECT_FLAGS_ERROR "For %s command, %s.\n" +#define FLINT_PARSE_MEM_ERROR "Failed to allocate memory for parsing.\n " +#define FLINT_NO_OPTIONS_FOUND_ERROR "No options found. " +#define FLINT_INVALID_COMMAD_ERROR "Invalid command: %s\n" +#define FLINT_TOO_MANY_ARGS_ERROR "Too many arguments. Expected: %d , Received: %d\n" +#define FLINT_TOO_FEW_ARGS_ERROR "Too few arguments. Expected: %d , Received: %d\n" +#define FLINT_TOO_FEW_MINIMUM_ARGS_ERROR "Too few arguments. Expected at least: %d , Received: %d\n" +#define FLINT_NO_COMMAND_ERROR "No command found." +#define FLINT_OPEN_FWOPS_DEVICE_ERROR "Cannot open Device: %s. %s\n" +#define FLINT_OPEN_FWOPS_DEVICE_ERROR_1 "Cannot open Device: %s.\n" +#define FLINT_OPEN_FWOPS_IMAGE_ERROR "Cannot open Image: %s. %s\n" +#define FLINT_DEVICE_AND_IMAGE_ERROR "Please specify either Device or Image.\n" +#define FLINT_NO_DEVICE_ERROR "Please specify Device.\n" +#define FLINT_NO_IMAGE_ERROR "Please specify Image file.\n" +#define FLINT_MISSED_ARG_ERROR "Missed %s parameter after \"%s\" command.\n" +#define FLINT_CMD_ARGS_ERROR "Command \"%s\" requires %d arguments, but %d arguments were given\n" +#define FLINT_CMD_ARGS_ERROR2 "Command \"%s\" requires at most %d arguments, but %d arguments were given\n" +#define FLINT_CMD_ARGS_ERROR3 "Command \"%s\" requires at least %d arguments, but %d arguments were given\n" +#define FLINT_CMD_ARGS_ERROR4 "Command \"%s\" requires %d or %d arguments, but %d arguments were given\n" +#define FLINT_CMD_ARGS_ERROR5 "Command \"%s\" does not require arguments\n" +#define FLINT_INVALID_OPTION_ERROR "Unknown option \"%s\" for the \"%s\" command. you can use %s.\n" +#define FLINT_INVALID_FLAG_ERROR "Invalid switch \"%s\" is specified.\n" +#define FLINT_INVALID_FLAG_ERROR_5TH_GEN "Invalid switch \"%s\" is specified for 5th gen device.\n" +#define FLINT_INVALID_FLAG_WITH_FLAG_ERROR "Cannot specify \"%s\" flag with \"%s\" flag.\n" #define FLINT_INVALID_FLAG_WITHOUT_FLAG_ERROR "\"%s\" flag must be specified with \"%s\" flag.\n" -#define FLINT_INVALID_FLAG_WITH_CMD_ERROR "Cannot specify flag: %s with Command: %s\n" -#define FLINT_CMD_VERIFY_ERROR "FW image verification failed: %s. AN HCA DEVICE CAN NOT BOOT FROM THIS IMAGE.\n" -#define FLINT_CMD_VERIFY_ERROR_1 "FW image verification failed: No valid FS4 image found.Check the flash parameters, if specified..AN HCA DEVICE CAN NOT BOOT FROM THIS IMAGE" -#define FLINT_FAILED_QUERY_ERROR "Failed to query %s: %s.%s\n" -#define FLINT_COMMAND_DEVICE_IMAGE_ERROR "Command \"%s\" requires both image and device to be specified.\n" -#define FLINT_COMMAND_DEVICE_ERROR "Command \"%s\" requires device, but an image file was given.\n" -#define FLINT_COMMAND_IMAGE_ERROR "Command \"%s\" requires an image file, but device was given.\n" -#define FLINT_COMMAND_DEVICE_ERROR2 "Command \"%s\" requires device, but both an image file and a device are given.\n" -#define FLINT_COMMAND_IMAGE_ERROR2 "Command \"%s\" requires an image, but both an image file and a device are given.\n" -#define FLINT_HW_SET_ARGS_ERROR "bad argument of hw set command : %s, it should be in the format PARAM_NAME=VALUE\n" -#define FLINT_HW_COMMAND_ERROR "HW %s failed. %s\n" -#define FLINT_SWRESET_ERROR "Software reset failed. %s\n" -#define FLINT_ERASE_SEC_ERROR "Erase sector failed. %s\n" -#define FLINT_INVALID_ADDR_ERROR "Invalid address \"%s\"\n" -#define FLINT_FLASH_READ_ERROR "Flash read failed. %s\n" -#define FLINT_FLASH_WRITE_ERROR "Flash write failed. %s\n" -#define FLINT_INVALID_DATA_ERROR "Invalid data \"%s\"\n" -#define FLINT_INVALID_SIZE_ERROR "Invalid size \"%s\", Length should be 4-bytes aligned.\n" -#define FLINT_INVALID_ARG_ERROR "Invalid argument \"%s\"\n" -#define FLINT_OPEN_FILE_ERROR "Cannot open %s: %s\n" -#define FLINT_WRITE_FILE_ERROR "Failed to write to %s: %s\n" -#define FLINT_IO_OPEN_ERROR "Failed to open %s: %s\n" -#define FLINT_IMAGE_READ_ERROR "Failed to read image. %s\n" -#define FLINT_READ_ERROR "Failed to read from %s. %s\n" -#define FLINT_READ_FILE_ERROR "Failed to read from %s.\n" -#define FLINT_WIN_NOT_SUPP_ERROR "Command \"%s\" is not supported in windows.\n" -#define FLINT_WIN_ONLY_SUPP_ERROR "Command \"%s\" is supported only in windows.\n" -#define FLINT_GEN_COMMAND_ERROR "Failed to execute command %s. %s\n" -#define FLINT_FS3_BB_ERROR "bb command is not supported anymore in FS3/FS4 images, please use b for burning FS3/FS4 images.\n" -#define FLINT_FSX_BURN_ERROR "Burning %s image failed: %s\n" -#define FLINT_FS2_BURN_ERROR "Burning FS2 image failed: %s\n" -#define FLINT_PSID_ERROR "PSID mismatch. The PSID on flash (%s) differs from the PSID in the given image (%s).\n" -#define FLINT_FS2_STRIPED_ERROR "The -striped_image cannot be used with the burn command\n" -#define FLINT_IMG_DEV_COMPAT_ERROR "The given device requires an %s image type, but the given image file does not contain an %s FW image\n" -#define FLINT_UNKNOWN_FW_TYPE_ERROR "Unknown Firmware Type.\n" -#define FLINT_READ_ROM_ERROR "Read ROM failed. %s\n" -#define FLINT_SG_GUID_ERROR "Cannot set GUIDs: %s\n" -#define FLINT_SG_UID_ERROR "Failed to set UID: %s\n" -#define FLINT_MFG_ERROR "Failed to set manufacture guids: %s\n" -#define FLINT_VSD_ERROR "Failed to set the VSD: %s\n" -#define FLINT_VPD_ERROR "Failed to set VPD: %s\n" -#define FLINT_SET_KEY_ERROR "Failed to set the HW access key: %s\n" -#define FLINT_RESET_CFG_ERROR "Failed to reset Configuration: %s\n" -#define FLINT_FIX_IMG_ERROR "Failed to fix device image: %s\n" -#define FLINT_DROM_ERROR "Remove ROM failed: %s\n" -#define FLINT_BROM_ERROR "Burn ROM failed: %s\n" -#define FLINT_DUMP_ERROR "Failed dumping %s : %s\n" -#define FLINT_ROM_QUERY_ERROR "Image file rom (%s) query failed. %s\n" -#define FLINT_WB_FILE_ERROR "failed to open file: %s. %s\n" -#define FLINT_WB_ERROR "write Block Failed. %s\n" -#define FLINT_NO_ZLIB_ERROR "Executable was compiled with \"dump files\" option disabled.\n" -#define FLINT_FLAG_WITH_FLAG_ERROR "\"%s\" flag must be specified with \"%s\" flag.\n" -#define FLINT_INVALID_PASSWORD "Invalid Password.\n" -#define FLINT_NO_GUID_MAC_FLAGS_ERROR "Can not set GUIDs/MACs: please run with -uid/-guid/-mac flag.\n" -#define FLINT_NOT_SUPP_UID_FLAG_ERROR "Can not set GUIDs/MACs: %s flag is not supported for this device.\nPlease run with -uid/-guid/-mac flag.\n" -#define FLINT_NO_UID_FLAG_ERROR "Can not set GUIDs/MACs: uid is not specified, please run with -uid flag.\n" -#define FLINT_CHECKSUM_ERROR "Failed to calculate checksum on %s: %s\n" -#define FLINT_CHECKSUM_MISMATCH_ERROR "Given checksum: %s does not match the checksum calculated on device FW: %s.\n" -#define FLINT_CHECKSUM_PARSE_ERROR "Failed to parse given checksum.\n" -#define FLINT_CHECKSUM_LEN_ERROR "MD5 checksum should be exactly 16 bytes long.\n" -#define FLINT_CHECKSUM_HEX_ERROR "MD5 checksum should contain only hexadecimal digits.\n" -#define FLINT_CACHE_IMAGE_ERROR "Failed to issue image cache request to driver. %s. make sure Mellanox driver is loaded and working properly.\n" -#define FLINT_SET_PUBLIC_KEYS_ERROR "Failed to set the public keys: %s\n" -#define FLINT_SET_FORBIDDEN_VERSIONS_ERROR "Failed to set the forbidden versions: %s\n" -#define FLINT_SIGN_ERROR "Failed to sign the image: %s\n" -#define FLINT_HMAC_ERROR "Failed to add HMAC: %s\n" +#define FLINT_INVALID_FLAG_WITH_CMD_ERROR "Cannot specify flag: %s with Command: %s\n" +#define FLINT_CMD_VERIFY_ERROR "FW image verification failed: %s. AN HCA DEVICE CAN NOT BOOT FROM THIS IMAGE.\n" +#define FLINT_CMD_VERIFY_ERROR_1 \ + "FW image verification failed: No valid FS4 image found.Check the flash parameters, if specified..AN HCA DEVICE CAN NOT BOOT FROM THIS IMAGE" +#define FLINT_FAILED_QUERY_ERROR "Failed to query %s: %s. %s\n" +#define FLINT_COMMAND_DEVICE_IMAGE_ERROR "Command \"%s\" requires both image and device to be specified.\n" +#define FLINT_COMMAND_DEVICE_ERROR "Command \"%s\" requires device, but an image file was given.\n" +#define FLINT_COMMAND_IMAGE_ERROR "Command \"%s\" requires an image file, but device was given.\n" +#define FLINT_COMMAND_DEVICE_ERROR2 "Command \"%s\" requires device, but both an image file and a device are given.\n" +#define FLINT_COMMAND_IMAGE_ERROR2 "Command \"%s\" requires an image, but both an image file and a device are given.\n" +#define FLINT_HW_SET_ARGS_ERROR "bad argument of hw set command : %s, it should be in the format PARAM_NAME=VALUE\n" +#define FLINT_HW_COMMAND_ERROR "HW %s failed. %s\n" +#define FLINT_SWRESET_ERROR "Software reset failed. %s\n" +#define FLINT_ERASE_SEC_ERROR "Erase sector failed. %s\n" +#define FLINT_INVALID_ADDR_ERROR "Invalid address \"%s\"\n" +#define FLINT_FLASH_READ_ERROR "Flash read failed. %s\n" +#define FLINT_FLASH_WRITE_ERROR "Flash write failed. %s\n" +#define FLINT_INVALID_DATA_ERROR "Invalid data \"%s\"\n" +#define FLINT_INVALID_SIZE_ERROR "Invalid size \"%s\", Length should be 4-bytes aligned.\n" +#define FLINT_INVALID_ARG_ERROR "Invalid argument \"%s\"\n" +#define FLINT_OPEN_FILE_ERROR "Cannot open %s: %s\n" +#define FLINT_WRITE_FILE_ERROR "Failed to write to %s: %s\n" +#define FLINT_IO_OPEN_ERROR "Failed to open %s: %s\n" +#define FLINT_IMAGE_READ_ERROR "Failed to read image. %s\n" +#define FLINT_READ_ERROR "Failed to read from %s. %s\n" +#define FLINT_READ_FILE_ERROR "Failed to read from %s.\n" +#define FLINT_WIN_NOT_SUPP_ERROR "Command \"%s\" is not supported in windows.\n" +#define FLINT_WIN_ONLY_SUPP_ERROR "Command \"%s\" is supported only in windows.\n" +#define FLINT_GEN_COMMAND_ERROR "Failed to execute command %s. %s\n" +#define FLINT_FS3_BB_ERROR \ + "bb command is not supported anymore in FS3/FS4 images, please use b for burning FS3/FS4 images.\n" +#define FLINT_FSX_BURN_ERROR "Burning %s image failed: %s\n" +#define FLINT_FS2_BURN_ERROR "Burning FS2 image failed: %s\n" +#define FLINT_PSID_ERROR "PSID mismatch. The PSID on flash (%s) differs from the PSID in the given image (%s).\n" +#define FLINT_FS2_STRIPED_ERROR "The -striped_image cannot be used with the burn command\n" +#define FLINT_IMG_DEV_COMPAT_ERROR \ + "The given device requires an %s image type, but the given image file does not contain an %s FW image\n" +#define FLINT_UNKNOWN_FW_TYPE_ERROR "Unknown Firmware Type.\n" +#define FLINT_READ_ROM_ERROR "Read ROM failed. %s\n" +#define FLINT_SG_GUID_ERROR "Cannot set GUIDs: %s\n" +#define FLINT_SG_UID_ERROR "Failed to set UID: %s\n" +#define FLINT_MFG_ERROR "Failed to set manufacture guids: %s\n" +#define FLINT_VSD_ERROR "Failed to set the VSD: %s\n" +#define FLINT_VPD_ERROR "Failed to set VPD: %s\n" +#define FLINT_CERT_CHAIN_ERROR "Failed to set attestation certificate chain: %s\n" +#define FLINT_SET_KEY_ERROR "Failed to set the HW access key: %s\n" +#define FLINT_RESET_CFG_ERROR "Failed to reset Configuration: %s\n" +#define FLINT_FIX_IMG_ERROR "Failed to fix device image: %s\n" +#define FLINT_DROM_ERROR "Remove ROM failed: %s\n" +#define FLINT_BROM_ERROR "Burn ROM failed: %s\n" +#define FLINT_DUMP_ERROR "Failed dumping %s : %s\n" +#define FLINT_ROM_QUERY_ERROR "Image file rom (%s) query failed. %s\n" +#define FLINT_WB_FILE_ERROR "failed to open file: %s. %s\n" +#define FLINT_WB_ERROR "write Block Failed. %s\n" +#define FLINT_NO_ZLIB_ERROR "Executable was compiled with \"dump files\" option disabled.\n" +#define FLINT_FLAG_WITH_FLAG_ERROR "\"%s\" flag must be specified with \"%s\" flag.\n" +#define FLINT_INVALID_PASSWORD "Invalid Password.\n" +#define FLINT_NO_GUID_MAC_FLAGS_ERROR "Can not set GUIDs/MACs: please run with -uid/-guid/-mac flag.\n" +#define FLINT_NOT_SUPP_UID_FLAG_ERROR \ + "Can not set GUIDs/MACs: %s flag is not supported for this device.\nPlease run with -uid/-guid/-mac flag.\n" +#define FLINT_NO_UID_FLAG_ERROR "Can not set GUIDs/MACs: uid is not specified, please run with -uid flag.\n" +#define FLINT_CHECKSUM_ERROR "Failed to calculate checksum on %s: %s\n" +#define FLINT_CHECKSUM_MISMATCH_ERROR "Given checksum: %s does not match the checksum calculated on device FW: %s.\n" +#define FLINT_CHECKSUM_PARSE_ERROR "Failed to parse given checksum.\n" +#define FLINT_CHECKSUM_LEN_ERROR "MD5 checksum should be exactly 16 bytes long.\n" +#define FLINT_CHECKSUM_HEX_ERROR "MD5 checksum should contain only hexadecimal digits.\n" +#define FLINT_CACHE_IMAGE_ERROR \ + "Failed to issue image cache request to driver. %s. make sure Mellanox driver is loaded and working properly.\n" +#define FLINT_SET_PUBLIC_KEYS_ERROR "Failed to set the public keys: %s\n" +#define FLINT_SET_FORBIDDEN_VERSIONS_ERROR "Failed to set the forbidden versions: %s\n" +#define FLINT_SIGN_ERROR "Failed to sign the image: %s\n" +#define FLINT_HMAC_ERROR "Failed to add HMAC: %s\n" #define FLINT_FAILED_IMAGE_REACTIVATION_ERROR "Failed to execute image reactivation on device %s. Error: %s.\n" -#define FLINT_NO_MFA2 "MFA2 funcionality is not supported" -#define FLINT_NO_HSM "HSM funcionality is not supported" -#define FLINT_ILLEGAL_CPU_VALUE "Illegal value for CPU utilization. Values must be between 1 (low CPU) and 5 (high CPU).\n" -#define FLINT_FILE_SIZE_ERROR "Can not get file size for \"%s\".\n" -#define FAILED_TO_VERIFY_PARAMS "Failed to verify params(internal error)." -#define INVALID_GUID_SYNTAX "Invalid GUID syntax (%s) %s\n" -#define FAILED_GET_CONSOLE_MODE "Failed to get console mode.\n" -#define USER_ABORT "Aborted by user\n" -#define SECTION_NOT_FOUNT "%s section not found in the given image." -#define UNCOMPRESSS_ERROR "Failed uncompressing FW configuration section. uncompress returns %d" -#define OPEN_WRITE_FILE_ERROR "Can not open file %s for write: %s." -#define IMAGE_SIGN_TYPE_ERROR "Image signing is applicable only for selected FW images. Please check your image type.\n" -#define HSM_INIT_ERROR "HSM init has failed! Please check if the HSM card installed and configured properly.\n" -#define HSM_PRIVATE_KEY_DUPLICATE "Creating HSM signature has failed - the private key label is duplicated.\n" -#define HSM_PUBLIC_KEY_DUPLICATE "Creating HSM signature has failed - the public key label is duplicated.\n" -#define HSM_SIGNATURE_CREATION_FAILED "Creating HSM signature has failed\n" -#define HSM_UUID_MISSING "To Sign the image with RSA you must provide UUID with HSM sign.\n" -#define HSM_PRIVATE_KEY_LABEL_MISSING "Must supply private key label for sign with HSM sign.\n" -#define HSM_PASSWORD_MISSING "Must supply HSM user password for sign with HSM sign.\n" -#define SIGN_PRIVATE_KEY_NOT_FOUND "Can't find private key file %s \n" -#define SIGN_PUBLIC_KEY_NOT_FOUND "Can't find public key file %s \n" -#define HSM_BOOT_SIGNATURE_CREATION_FAILED "Creating HSM BOOT signature has failed\n" +#define FLINT_NO_MFA2 "MFA2 funcionality is not supported" +#define FLINT_NO_HSM "HSM funcionality is not supported" +#define FLINT_ILLEGAL_CPU_VALUE \ + "Illegal value for CPU utilization. Values must be between 1 (low CPU) and 5 (high CPU).\n" +#define FLINT_FILE_SIZE_ERROR "Can not get file size for \"%s\".\n" +#define FAILED_TO_VERIFY_PARAMS "Failed to verify params(internal error)." +#define INVALID_GUID_SYNTAX "Invalid GUID syntax (%s) %s\n" +#define FAILED_GET_CONSOLE_MODE "Failed to get console mode.\n" +#define USER_ABORT "Aborted by user\n" +#define SECTION_NOT_FOUNT "%s section not found in the given image." +#define UNCOMPRESSS_ERROR "Failed uncompressing FW configuration section. uncompress returns %d" +#define OPEN_WRITE_FILE_ERROR "Can not open file %s for write: %s." +#define IMAGE_SIGN_TYPE_ERROR "Image signing is applicable only for selected FW images. Please check your image type.\n" +#define HSM_INIT_ERROR "HSM init has failed! Please check if the HSM card installed and configured properly.\n" +#define HSM_PRIVATE_KEY_DUPLICATE "Creating HSM signature has failed - the private key label is duplicated.\n" +#define HSM_PUBLIC_KEY_DUPLICATE "Creating HSM signature has failed - the public key label is duplicated.\n" +#define HSM_SIGNATURE_CREATION_FAILED "Creating HSM signature has failed\n" +#define HSM_UUID_MISSING "To Sign the image with RSA you must provide UUID with HSM sign.\n" +#define HSM_PRIVATE_KEY_LABEL_MISSING "Must supply private key label for sign with HSM sign.\n" +#define HSM_PASSWORD_MISSING "Must supply HSM user password for sign with HSM sign.\n" +#define SIGN_PRIVATE_KEY_NOT_FOUND "Can't find private key file %s \n" +#define SIGN_PUBLIC_KEY_NOT_FOUND "Can't find public key file %s \n" +#define HSM_BOOT_SIGNATURE_CREATION_FAILED "Creating HSM BOOT signature has failed\n" #define HSM_CRITICAL_SIGNATURE_CREATION_FAILED "Creating HSM critical signature has failed\n" #define HSM_NON_CRITICAL_SIGNATURE_CREATION_FAILED "Creating HSM non-critical signature has failed\n" -#define HSM_SECURE_BOOT_SIGNATURE_FAILED "Inserting secure BOOT signatures has failed : %s.\n" -#define HSM_SECURE_FW_SIGNATURE_FAILED "Creation secured FW signatures has failed.\n" -#define LINKX_QUERY_DEVICE_NOT_SUPPORTED "Linkx query for device %s is not supported.\n" -#define LINKX_BURN_DEVICE_NOT_SUPPORTED "Linkx burn for device %s is not supported.\n" +#define HSM_SECURE_BOOT_SIGNATURE_FAILED "Inserting secure BOOT signatures has failed : %s.\n" +#define HSM_SECURE_FW_SIGNATURE_FAILED "Creation secured FW signatures has failed.\n" +#define LINKX_QUERY_DEVICE_NOT_SUPPORTED "Linkx query for device %s is not supported.\n" +#define LINKX_BURN_DEVICE_NOT_SUPPORTED "Linkx burn for device %s is not supported.\n" /************************** * Flint Warning Messages *************************/ -#define FLINT_QQ_WARRNING "-W- Running quick query - Skipping full image integrity checks.\n" -#define FLINT_NOT_MLNX_FW_WARNING "-W- Not a Mellanox FW image (vendor_id = 0x%04x). VSD and PSID are not displayed.\n" -#define FLINT_BLANK_GUIDS_WARNING "-W- GUIDs/MACs values and their CRC are not set.\n" -#define FLINT_MULTI_BIT_WARNING "Multicast bit (bit 40) is set." -#define FLINT_MORE_48_BITS_WARNING "More than 48 bits are used." -#define FLINT_BAD_MAC_ADRESS_WARNING "\n-W- Bad mac address ( %4.4x%8.8x ): %s\n" -#define FLINT_MAC_ENTRIES_WARNING "-W- Cannot get MAC address: Expecting %d entries in guid section, got %d. Probably an old FW image. Please update.\n" -#define FLINT_INTERRUPT_WARRNING "\n-W- An internal error occurred. This program cannot be interrupted.\n" -#define FLINT_SET_GUIDS_WARRNING "-W- GUIDs are already set, re-burning image with the new GUIDs ...\n" -#define FLINT_OCR_WARRNING "\n-W- Firmware flash cache access is enabled. Running in this mode may cause the firmware to hang.\n" -#define FLINT_OPEN_LOG_FILE_WARNING "-W- Failed to open log file \"%s\": %s. No logs will be saved\n" +#define FLINT_QQ_WARRNING "-W- Running quick query - Skipping full image integrity checks.\n" +#define FLINT_NOT_MLNX_FW_WARNING "-W- Not a Mellanox FW image (vendor_id = 0x%04x). VSD and PSID are not displayed.\n" +#define FLINT_BLANK_GUIDS_WARNING "-W- GUIDs/MACs values and their CRC are not set.\n" +#define FLINT_MULTI_BIT_WARNING "Multicast bit (bit 40) is set." +#define FLINT_MORE_48_BITS_WARNING "More than 48 bits are used." +#define FLINT_BAD_MAC_ADRESS_WARNING "\n-W- Bad mac address ( %4.4x%8.8x ): %s\n" +#define FLINT_MAC_ENTRIES_WARNING \ + "-W- Cannot get MAC address: Expecting %d entries in guid section, got %d. Probably an old FW image. Please update.\n" +#define FLINT_INTERRUPT_WARRNING "\n-W- An internal error occurred. This program cannot be interrupted.\n" +#define FLINT_SET_GUIDS_WARRNING "-W- GUIDs are already set, re-burning image with the new GUIDs ...\n" +#define FLINT_OCR_WARRNING \ + "\n-W- Firmware flash cache access is enabled. Running in this mode may cause the firmware to hang.\n" +#define FLINT_OPEN_LOG_FILE_WARNING "-W- Failed to open log file \"%s\": %s. No logs will be saved\n" #endif diff -Nru mstflint-4.17.0+1/flint/flint.cpp mstflint-4.21.0+1/flint/flint.cpp --- mstflint-4.17.0+1/flint/flint.cpp 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/flint/flint.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -3,6 +3,7 @@ * flint.cpp - FLash INTerface * * Copyright (c) 2013 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -41,10 +42,10 @@ #include "flint.h" -//Globals: -Flint *gFlint = NULL; -extern FILE *flint_log_fh; -//signal handler section +// Globals: +Flint* gFlint = NULL; +extern FILE* flint_log_fh; +// signal handler section #define BURN_INTERRUPTED 0x1234 @@ -53,44 +54,45 @@ #ifdef __WIN__ #include -static BOOL CtrlHandler( DWORD fdwCtrlType ) +static BOOL CtrlHandler(DWORD fdwCtrlType) { switch (fdwCtrlType) { - // Handle the CTRL-C signal. - case CTRL_C_EVENT: - // CTRL-CLOSE: confirm that the user wants to exit. - case CTRL_CLOSE_EVENT: - // Pass other signals to the next handler. - case CTRL_BREAK_EVENT: - case CTRL_LOGOFF_EVENT: - case CTRL_SHUTDOWN_EVENT: - TerminationHandler(SIGINT); - return TRUE; + // Handle the CTRL-C signal. + case CTRL_C_EVENT: + // CTRL-CLOSE: confirm that the user wants to exit. + case CTRL_CLOSE_EVENT: + // Pass other signals to the next handler. + case CTRL_BREAK_EVENT: + case CTRL_LOGOFF_EVENT: + case CTRL_SHUTDOWN_EVENT: + TerminationHandler(SIGINT); + return TRUE; - default: - return FALSE; + default: + return FALSE; } } #endif - void TerminationHandler(int signum) { static volatile sig_atomic_t fatal_error_in_progress = 0; - if (fatal_error_in_progress) { + if (fatal_error_in_progress) + { raise(signum); } fatal_error_in_progress = 1; write_result_to_log(BURN_INTERRUPTED, ""); close_log(); - if (gFlint != NULL) { + if (gFlint != NULL) + { printf("\n Received signal %d. Cleaning up ...\n", signum); fflush(stdout); - //sleep(1); // Legacy from the Old Flint + // sleep(1); // Legacy from the Old Flint gFlint->GetSubCommands()[gFlint->GetFlintParams().cmd]->cleanInterruptedCommand(); delete gFlint; gFlint = NULL; @@ -100,31 +102,42 @@ raise(signum); } - -void initHandler() -{ #ifdef __WIN__ #define SIGNAL_NUM 3 - int signalList[SIGNAL_NUM] = {SIGINT, SIGTERM, SIGABRT}; - SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, true ); +static int signalList[SIGNAL_NUM] = {SIGINT, SIGTERM, SIGABRT}; #else #define SIGNAL_NUM 4 - int signalList[SIGNAL_NUM] = {SIGINT, SIGTERM, SIGPIPE, SIGHUP}; +static int signalList[SIGNAL_NUM] = {SIGINT, SIGTERM, SIGPIPE, SIGHUP}; #endif - //set the signal handler - for (int i = 0; i < SIGNAL_NUM; i++) { +void initHandler() +{ +#ifdef __WIN__ + SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, true); +#endif + + // set the signal handler + for (int i = 0; i < SIGNAL_NUM; i++) + { void (*prevFunc)(int); prevFunc = signal(signalList[i], TerminationHandler); - if (prevFunc == SIG_ERR) { + if (prevFunc == SIG_ERR) + { printf("-E- failed to set signal Handler."); exit(FLINT_FAILED); } } } -//End of signal handler section. +void ignoreSignals() +{ + for (int i = 0; i < SIGNAL_NUM; i++) + { + signal(signalList[i], SIG_IGN); + } +} +// End of signal handler section. map_sub_cmd_t_to_subcommand Flint::initSubcommandMap() { @@ -143,6 +156,7 @@ #ifndef EXTERNAL cmdMap[SC_Smg] = new SmgSubCommand(); cmdMap[SC_Set_Vpd] = new SetVpdSubCommand(); + cmdMap[SC_Set_Cert_Chain] = new SetCertChainSubCommand(); cmdMap[SC_Fix_Img] = new FiSubCommand(); cmdMap[SC_Extract_4MB_Image] = new Extract4MBImageSubCommand(); #endif @@ -170,9 +184,9 @@ cmdMap[SC_Set_Forbidden_Versions] = new SetForbiddenVersionsSubCommand(); cmdMap[SC_Image_Reactivation] = new ImageReactivationSubCommand(); cmdMap[SC_RSA_Sign] = new SignRSASubCommand(); - cmdMap[SC_Binary_Compare] = new BinaryCompareSubCommand(); - cmdMap[SC_Import_Hsm_Key] = new ImportHsmKeySubCommand(); -#if !defined(UEFI_BUILD) && !defined(NO_OPEN_SSL) + cmdMap[SC_Binary_Compare] = new BinaryCompareSubCommand(); + cmdMap[SC_Import_Hsm_Key] = new ImportHsmKeySubCommand(); +#ifndef NO_OPEN_SSL cmdMap[SC_Export_Public_Key] = new ExportPublicSubCommand(); #endif return cmdMap; @@ -184,7 +198,6 @@ delete it->second; } - Flint::Flint() : CommandLineRequester(FLINT_DISPLAY_NAME " [OPTIONS] [Parameters]"), _flintParams(), @@ -199,51 +212,64 @@ deInitSubcommandMap(_subcommands); } -FlintStatus Flint::run(int argc, char *argv[]) +FlintStatus Flint::run(int argc, char* argv[]) { - //Step1 parse input - //There are some memory allocations parseCmdLine + // Step1 parse input + // There are some memory allocations parseCmdLine ParseStatus status; - try { + try + { status = this->parseCmdLine(argc, argv); - } catch (exception& e) { + } + catch (exception& e) + { cout << "-E- " << e.what() << endl; return FLINT_FAILED; } - if (status == PARSE_OK_WITH_EXIT) { + if (status == PARSE_OK_WITH_EXIT) + { return FLINT_SUCCESS; - } else if (status == PARSE_ERROR || status == PARSE_ERROR_SHOW_USAGE) { - if (string(_cmdParser.GetErrDesc()).length() > 0) { + } + else if (status == PARSE_ERROR || status == PARSE_ERROR_SHOW_USAGE) + { + if (string(_cmdParser.GetErrDesc()).length() > 0) + { cout << "-E- " << this->_cmdParser.GetErrDesc() << endl; } return FLINT_FAILED; } - if (_flintParams.cmd == SC_No_Cmd && !_flintParams.clear_semaphore) { + if (_flintParams.cmd == SC_No_Cmd && !_flintParams.clear_semaphore) + { cout << FLINT_NO_COMMAND_ERROR << endl; return FLINT_FAILED; } - if (_flintParams.clear_semaphore) { - if (_flintParams.cmd != SC_No_Cmd) { + if (_flintParams.clear_semaphore) + { + if (_flintParams.cmd != SC_No_Cmd) + { printf(FLINT_CLEAR_SEM_CMD_ERROR); return FLINT_FAILED; } _flintParams.cmd = SC_Clear_Sem; } - //TODO: adrianc: remove use_fw flag and this condition before MFT-4.1.0 - if (_flintParams.use_fw && _flintParams.override_cache_replacement) { + // TODO: adrianc: remove use_fw flag and this condition before MFT-4.1.0 + if (_flintParams.use_fw && _flintParams.override_cache_replacement) + { printf("-E- flags --use_fw and --override_cache_replacement/-ocr cannot be specified simultaneously"); return FLINT_FAILED; } - // Step 2 save argv as a single cmd string in flint params for the log functionallity - for (int i = 0; i < argc; i++) { + // Step 2 save argv as a single cmd string in flint params for the log functionality + for (int i = 0; i < argc; i++) + { _flintParams.fullCmd = _flintParams.fullCmd + argv[i] + " "; } - //TODO: Step 3 check flintParams for contradictions? - //Step 4 execute command from the correct subcommand class - if (_subcommands.count(_flintParams.cmd) == 0) { + // TODO: Step 3 check flintParams for contradictions? + // Step 4 execute command from the correct subcommand class + if (_subcommands.count(_flintParams.cmd) == 0) + { // should not be reached printf("-E- FATAL: command object not found."); return FLINT_FAILED; @@ -252,15 +278,16 @@ return _subcommands[_flintParams.cmd]->executeCommand(); } - -int main(int argc, char *argv[]) +int main(int argc, char* argv[]) { initHandler(); try { gFlint = new Flint(); FlintStatus rc = gFlint->run(argc, argv); - if (gFlint) { + if (gFlint) + { + ignoreSignals(); // clean-up should be signal free, prevent possible clean-up re-entry delete gFlint; gFlint = NULL; } diff -Nru mstflint-4.17.0+1/flint/flint.h mstflint-4.21.0+1/flint/flint.h --- mstflint-4.17.0+1/flint/flint.h 2021-04-06 13:00:20.000000000 +0000 +++ mstflint-4.21.0+1/flint/flint.h 2022-07-31 09:33:57.000000000 +0000 @@ -3,6 +3,7 @@ * flint2.h - FLash INTerface * * Copyright (c) 2013 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -47,9 +48,9 @@ using namespace std; -typedef map map_sub_cmd_t_to_subcommand; -typedef map map_string_to_string; -typedef map map_string_to_int; +typedef map map_sub_cmd_t_to_subcommand; +typedef map map_string_to_string; +typedef map map_string_to_int; map_string_to_string initShortToLongFlagMap(); map_string_to_int initLongFlagToNumOfArgsMap(); @@ -63,7 +64,7 @@ CommandLineParser _cmdParser; map_sub_cmd_t_to_subcommand _subcommands; - //methods + // methods map_sub_cmd_t_to_subcommand initSubcommandMap(); void deInitSubcommandMap(map_sub_cmd_t_to_subcommand cmdMap); @@ -72,8 +73,8 @@ ~Flint(); void initCmdParser(); virtual ParseStatus HandleOption(string name, string value); - ParseStatus parseCmdLine(int argc, char *argv[]); - FlintStatus run(int argc, char *argv[]); + ParseStatus parseCmdLine(int argc, char* argv[]); + FlintStatus run(int argc, char* argv[]); FlintParams& GetFlintParams() { return _flintParams; } map_sub_cmd_t_to_subcommand& GetSubCommands() { return _subcommands; } }; diff -Nru mstflint-4.17.0+1/flint/flint_params.cpp mstflint-4.21.0+1/flint/flint_params.cpp --- mstflint-4.17.0+1/flint/flint_params.cpp 2021-06-29 08:49:48.000000000 +0000 +++ mstflint-4.21.0+1/flint/flint_params.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -3,6 +3,7 @@ * flint_params.cpp - FLash INTerface * * Copyright (c) 2013 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -52,11 +53,12 @@ device_specified = false; blank_guids = false; clear_semaphore = false; - quick_query = true; //should now be true by default + quick_query = true; // should now be true by default next_boot_fw_ver = false; low_cpu = false; skip_rom_query = false; image_specified = false; + nonencrypted_image_specified = false; mfa2_specified = false; nofs = false; allow_psid_change = false; @@ -86,6 +88,8 @@ flash_params.num_of_flashes = 0; flash_params.log2size = 0; use_dev_img_info = false; + ignore_crc_check = false; + hexdump_format = false; skip_ci_req = false; use_dev_rom = false; privkey_specified = false; @@ -114,11 +118,8 @@ downstream_device_ids_specified = false; download_transfer = false; // if no delay specified, use minimal delay to avoid disconnection in case of activating the connect port - activate_delay_sec = 1; + activate_delay_sec = 1; openssl_engine_usage_specified = false; } -FlintParams::~FlintParams() -{ - -} +FlintParams::~FlintParams() {} diff -Nru mstflint-4.17.0+1/flint/flint_params.h mstflint-4.21.0+1/flint/flint_params.h --- mstflint-4.17.0+1/flint/flint_params.h 2021-06-29 08:49:48.000000000 +0000 +++ mstflint-4.21.0+1/flint/flint_params.h 2022-07-31 09:33:57.000000000 +0000 @@ -3,6 +3,7 @@ * flint_params.h - FLash INTerface * * Copyright (c) 2013 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -46,7 +47,8 @@ using namespace std; -typedef enum { +typedef enum +{ SC_No_Cmd = 0, SC_Burn, SC_Query, @@ -60,6 +62,7 @@ SC_Sg, SC_Smg, SC_Set_Vpd, + SC_Set_Cert_Chain, SC_Sv, SC_Ri, SC_Dc, @@ -89,14 +92,13 @@ SC_RSA_Sign, SC_Binary_Compare, SC_Import_Hsm_Key, -#if !defined(UEFI_BUILD) && !defined(NO_OPEN_SSL) SC_Export_Public_Key -#endif } sub_cmd_t; -class FlintParams { +class FlintParams +{ public: - //add more params + // add more params FlintParams(); ~FlintParams(); bool device_specified; @@ -119,8 +121,10 @@ bool next_boot_fw_ver; bool skip_rom_query; bool image_specified; + bool nonencrypted_image_specified; bool mfa2_specified; string image; + string nonencrypted_image; bool nofs; bool allow_psid_change; bool allow_rom_change; @@ -149,6 +153,8 @@ vector cmd_params; string fullCmd; bool use_dev_img_info; + bool ignore_crc_check; + bool hexdump_format; bool skip_ci_req; bool use_dev_rom; bool privkey_specified; @@ -184,7 +190,7 @@ bool hsm_password_specified; string hsm_password; bool linkx_control; - int cableDeviceIndex; + int cableDeviceIndex; int cableDeviceSize; bool cable_device_index_specified; bool cable_device_size_specified; @@ -192,12 +198,13 @@ bool activate; bool download_downstream_specified; bool downstream_device_ids_specified; - std::vector downstream_device_ids; + std::vector downstream_device_ids; bool download_transfer; u_int8_t activate_delay_sec; string openssl_engine; string openssl_key_id; bool openssl_engine_usage_specified; + u_int32_t cert_chain_index; }; #endif diff -Nru mstflint-4.17.0+1/flint/Makefile.am mstflint-4.21.0+1/flint/Makefile.am --- mstflint-4.17.0+1/flint/Makefile.am 2021-06-29 08:49:48.000000000 +0000 +++ mstflint-4.21.0+1/flint/Makefile.am 2022-04-28 11:49:49.000000000 +0000 @@ -1,5 +1,6 @@ #-- # Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved. +# Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU diff -Nru mstflint-4.17.0+1/flint/Makefile.in mstflint-4.21.0+1/flint/Makefile.in --- mstflint-4.17.0+1/flint/Makefile.in 2021-06-29 08:51:08.000000000 +0000 +++ mstflint-4.21.0+1/flint/Makefile.in 2022-07-31 09:34:41.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.3 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2020 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -16,6 +16,7 @@ #-- # Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved. +# Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # This software is available to you under a choice of one of two # licenses. You may choose to be licensed under the terms of the GNU diff -Nru mstflint-4.17.0+1/flint/subcommands.cpp mstflint-4.21.0+1/flint/subcommands.cpp --- mstflint-4.17.0+1/flint/subcommands.cpp 2021-06-29 08:49:48.000000000 +0000 +++ mstflint-4.21.0+1/flint/subcommands.cpp 2022-07-31 09:33:57.000000000 +0000 @@ -3,6 +3,7 @@ * subcommands.cpp - FLash INTerface * * Copyright (c) 2013 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -61,9 +62,7 @@ #define MODULUS_SIZE 512 #define TOTAL_PUBLIC_KEY_SIZE 532 #define MODULUS_OFFSET 38 -#ifndef __WIN__ -#include "hsmlunaclient.h" -#endif + #if !defined(__WIN__) && !defined(__DJGPP__) && !defined(UEFI_BUILD) && defined(HAVE_TERMIOS_H) // used in mygetchar #include @@ -85,14 +84,14 @@ * Log file writing implementation ************************************/ -//global log file header -FILE *flint_log_fh = NULL; +// global log file header +FILE* flint_log_fh = NULL; #define BURN_INTERRUPTED 0x1234 static int is_arm() { -#if defined (MST_CPU_arm64) +#if defined(MST_CPU_arm64) return 1; #else return 0; @@ -101,37 +100,22 @@ void close_log() { - if (flint_log_fh != NULL) { + if (flint_log_fh != NULL) + { fclose(flint_log_fh); flint_log_fh = NULL; } return; } -static const char* life_cycle_strings[NUM_OF_LIFE_CYCLES] = { - "PRODUCTION", - "GA SECURED", - "GA NON SECURED", - "RMA" -}; +static const char* life_cycle_strings[NUM_OF_LIFE_CYCLES] = {"PRODUCTION", "GA SECURED", "GA NON SECURED", "RMA"}; #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) -const char* month_2monstr(int month) +const char* month_2monstr(int month) { - static const char *month_2monstr_arr[] = { - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec", + static const char* month_2monstr_arr[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; int arr_size = (int)ARRAY_SIZE(month_2monstr_arr); return month < arr_size ? month_2monstr_arr[month] : "???"; @@ -140,14 +124,16 @@ void print_time_to_log() { time_t rawtime; - struct tm *timeinfo; + struct tm* timeinfo; - if (flint_log_fh == NULL) { + if (flint_log_fh == NULL) + { return; } time(&rawtime); timeinfo = localtime(&rawtime); - if (!timeinfo) { + if (!timeinfo) + { printf("localtime returned NULL. Can't print time.\n"); return; } @@ -157,10 +143,11 @@ return; } -int print_line_to_log(const char *format, ...) +int print_line_to_log(const char* format, ...) { va_list args; - if (flint_log_fh == NULL) { + if (flint_log_fh == NULL) + { return 0; } print_time_to_log(); @@ -172,16 +159,21 @@ int write_cmd_to_log(string fullCmd, sub_cmd_t cmd, bool write) { - if (!write) { + if (!write) + { return 0; } char pre_str[50]; - if (flint_log_fh == NULL) { + if (flint_log_fh == NULL) + { return 0; } - if (cmd == SC_Brom) { + if (cmd == SC_Brom) + { snprintf(pre_str, 50, "ROM"); - } else { + } + else + { snprintf(pre_str, 50, "FW"); } print_time_to_log(); @@ -191,25 +183,36 @@ return 0; } -int write_result_to_log(int is_failed, const char *err_msg, bool write) +int write_result_to_log(int is_failed, const char* err_msg, bool write) { - if (!write) { + if (!write) + { return 0; } - char msg[MAX_ERR_STR_LEN + 1] = { 0 }; + char msg[MAX_ERR_STR_LEN + 1] = {0}; strncpy(msg, err_msg, MAX_ERR_STR_LEN); - if (is_failed == 0) { + if (is_failed == 0) + { print_line_to_log("Burn completed successfully\n"); - } else if (is_failed == BURN_INTERRUPTED) { + } + else if (is_failed == BURN_INTERRUPTED) + { print_line_to_log("Burn interrupted by user\n"); - } else { + } + else + { int msg_len = strlen(msg); // cleanup the msg - for (int i = 0; i < msg_len; i++) { - if (msg[i] == '\n') { - if (i == msg_len - 1) { + for (int i = 0; i < msg_len; i++) + { + if (msg[i] == '\n') + { + if (i == msg_len - 1) + { msg[i] = '\0'; - } else { + } + else + { msg[i] = ' '; } } @@ -223,29 +226,33 @@ { /* Check for existence */ #ifdef __WIN__ - if (_access(filename.c_str(), F_OK) != -1) { + if (_access(filename.c_str(), F_OK) != -1) + { return true; } #else - if (access(filename.c_str(), F_OK) != -1) { + if (access(filename.c_str(), F_OK) != -1) + { return true; } #endif return false; } -bool is_file_exists (const char *filename) +bool is_file_exists(const char* filename) { /* Check for existence */ #ifdef __WIN__ - if(_access(filename, F_OK) != -1) { + if (_access(filename, F_OK) != -1) + { return true; } -#else - if(access(filename, F_OK) != -1) { +#else + if (access(filename, F_OK) != -1) + { return true; } -#endif +#endif return false; } @@ -253,16 +260,18 @@ * Static functions ******************/ -static bool str2Num(const char *str, u_int32_t& num) +static bool str2Num(const char* str, u_int32_t& num) { - char *endp; + char* endp; u_int32_t tempNum; - if (!str) { + if (!str) + { return false; } tempNum = strtoul(str, &endp, 0); - if (*endp) { + if (*endp) + { return false; } num = tempNum; @@ -273,7 +282,6 @@ * Class: Subcommand ******************/ - #define MAX_ERR_STR_LEN 1024 #define PRE_ERR_MSG "-E-" @@ -283,29 +291,32 @@ // A. it is either Burn, Burn Block or Burn ROM. // B. log flag was given in the cmd line. - switch (_cmdType) { - case SC_Burn: - case SC_Bb: - case SC_Brom: - return _flintParams.log_specified; + switch (_cmdType) + { + case SC_Burn: + case SC_Bb: + case SC_Brom: + return _flintParams.log_specified; - default: - return false; + default: + return false; } return false; } -void SubCommand::reportErr(bool shouldPrint, const char *format, ...) +void SubCommand::reportErr(bool shouldPrint, const char* format, ...) { va_list args; va_start(args, format); - if (vsnprintf(_errBuff, FLINT_ERR_LEN, format, args) >= FLINT_ERR_LEN) { + if (vsnprintf(_errBuff, FLINT_ERR_LEN, format, args) >= FLINT_ERR_LEN) + { strcpy(&_errBuff[FLINT_ERR_LEN - 5], "...\n"); } - //print to the user and to the log if needed - if (shouldPrint) { + // print to the user and to the log if needed + if (shouldPrint) + { fprintf(stdout, PRE_ERR_MSG " %s", _errBuff); } write_result_to_log(FLINT_FAILED, _errBuff, isCmdSupportLog()); @@ -316,18 +327,21 @@ bool SubCommand::getFileSize(const string& filePath, long& fileSize) { - FILE *fd = fopen(filePath.c_str(), "rb"); - if (fd == NULL) { + FILE* fd = fopen(filePath.c_str(), "rb"); + if (fd == NULL) + { reportErr(true, FLINT_OPEN_FILE_ERROR, filePath.c_str(), strerror(errno)); return false; } - if (fseek(fd, 0, SEEK_END) < 0) { + if (fseek(fd, 0, SEEK_END) < 0) + { fclose(fd); reportErr(true, FLINT_FILE_SIZE_ERROR, filePath.c_str()); return false; } long FileSize = ftell(fd); - if (FileSize < 0) { + if (FileSize < 0) + { fclose(fd); reportErr(true, FLINT_FILE_SIZE_ERROR, filePath.c_str()); return false; @@ -339,28 +353,32 @@ bool SubCommand::readFromFile(const string& filePath, std::vector& buff) { - FILE *fd = fopen(filePath.c_str(), "rb"); - if (fd == NULL) { + FILE* fd = fopen(filePath.c_str(), "rb"); + if (fd == NULL) + { reportErr(true, FLINT_OPEN_FILE_ERROR, filePath.c_str(), strerror(errno)); return false; } - if (fseek(fd, 0, SEEK_END) < 0) { + if (fseek(fd, 0, SEEK_END) < 0) + { fclose(fd); reportErr(true, FLINT_FILE_SIZE_ERROR, filePath.c_str()); return false; } long fileSize = ftell(fd); - if (fileSize < 0) { + if (fileSize < 0) + { fclose(fd); reportErr(true, FLINT_FILE_SIZE_ERROR, filePath.c_str()); return false; } rewind(fd); - int currentSize = buff.size();//default 0 + int currentSize = buff.size(); // default 0 buff.resize(currentSize + fileSize, 0xff); // Read long read_res = (long)fread(&buff[currentSize], 1, fileSize, fd); - if (read_res < 0) { + if (read_res < 0) + { fclose(fd); reportErr(true, FLINT_READ_FILE_ERROR, filePath.c_str()); return false; @@ -370,13 +388,15 @@ } bool SubCommand::writeToFile(string filePath, const std::vector& buff) { - FILE *fh = fopen(filePath.c_str(), "wb"); - if (fh == NULL) { + FILE* fh = fopen(filePath.c_str(), "wb"); + if (fh == NULL) + { reportErr(true, FLINT_OPEN_FILE_ERROR, filePath.c_str(), strerror(errno)); return false; } // Write - if (fwrite(&buff[0], 1, buff.size(), fh) != buff.size()) { + if (fwrite(&buff[0], 1, buff.size(), fh) != buff.size()) + { fclose(fh); reportErr(true, FLINT_WRITE_FILE_ERROR, filePath.c_str(), strerror(errno)); return false; @@ -385,16 +405,18 @@ return true; } -FlintStatus SubCommand::writeImageToFile(const char *file_name, u_int8_t *data, u_int32_t length) +FlintStatus SubCommand::writeImageToFile(const char* file_name, u_int8_t* data, u_int32_t length) { - FILE *fh = fopen(file_name, "wb"); - if (fh == NULL) { + FILE* fh = fopen(file_name, "wb"); + if (fh == NULL) + { reportErr(true, FLINT_OPEN_FILE_ERROR, file_name, strerror(errno)); return FLINT_FAILED; } // Write output - if (fwrite(data, 1, length, fh) != length) { + if (fwrite(data, 1, length, fh) != length) + { fclose(fh); reportErr(true, FLINT_WRITE_FILE_ERROR, file_name, strerror(errno)); return FLINT_FAILED; @@ -405,31 +427,37 @@ void SubCommand::openLog() { - if (isCmdSupportLog()) { + if (isCmdSupportLog()) + { flint_log_fh = fopen(_flintParams.log.c_str(), "a+"); - if (flint_log_fh == NULL) { - printf(FLINT_OPEN_LOG_FILE_WARNING, _flintParams.log.c_str() - , strerror(errno)); + if (flint_log_fh == NULL) + { + printf(FLINT_OPEN_LOG_FILE_WARNING, _flintParams.log.c_str(), strerror(errno)); } write_cmd_to_log(_flintParams.fullCmd, _flintParams.cmd, _flintParams.log_specified); } } -int SubCommand::verifyCbFunc(char *str) +int SubCommand::verifyCbFunc(char* str) { printf("%s", str); return 0; } - -int SubCommand::CbCommon(int completion, char *preStr, char *endStr) +int SubCommand::CbCommon(int completion, char* preStr, char* endStr) { - if (completion < 100) { + if (completion < 100) + { printf("\r%s%3d%%", preStr, completion); - } else if (completion == 100) { + } + else if (completion == 100) + { printf("\r%sOK \n", preStr); - } else { // printing endStr - if (endStr) { + } + else + { // printing endStr + if (endStr) + { printf("\r%s\n", endStr); } } @@ -441,34 +469,37 @@ // output. thus in subcommands that use these callbacks you will see we manually call them at the end with the 101 arg int SubCommand::burnCbFs3Func(int completion) { - char *message = (char*)"Burning FW image without signatures - "; - char *endStr = (char*)"Restoring signature - OK"; + char* message = (char*)"Burning FW image without signatures - "; + char* endStr = (char*)"Restoring signature - OK"; return CbCommon(completion, message, endStr); } -int SubCommand::advProgressFunc(int completion, const char *stage, prog_t type, int *unknownProgress) +int SubCommand::advProgressFunc(int completion, const char* stage, prog_t type, int* unknownProgress) { - switch (type) { - case PROG_WITH_PRECENTAGE: - printf("\r%s - %3d%%", stage, completion); - break; + switch (type) + { + case PROG_WITH_PRECENTAGE: + printf("\r%s - %3d%%", stage, completion); + break; - case PROG_OK: - printf("\r%s - OK\n", stage); - break; + case PROG_OK: + printf("\r%s - OK\n", stage); + break; - case PROG_STRING_ONLY: - printf("%s\n", stage); - break; + case PROG_STRING_ONLY: + printf("%s\n", stage); + break; - case PROG_WITHOUT_PRECENTAGE: - if (unknownProgress) { - static const char *progStr[] = { "[. ]", "[.. ]", "[... ]", "[.... ]", "[.....]", "[ ....]", "[ ...]", "[ ..]", "[ .]", "[ ]" }; - int size = sizeof(progStr) / sizeof(progStr[0]); - printf("\r%s - %s", stage, progStr[(*unknownProgress) % size]); - (*unknownProgress)++; - } - break; + case PROG_WITHOUT_PRECENTAGE: + if (unknownProgress) + { + static const char* progStr[] = {"[. ]", "[.. ]", "[... ]", "[.... ]", "[.....]", + "[ ....]", "[ ...]", "[ ..]", "[ .]", "[ ]"}; + int size = sizeof(progStr) / sizeof(progStr[0]); + printf("\r%s - %s", stage, progStr[(*unknownProgress) % size]); + (*unknownProgress)++; + } + break; } fflush(stdout); return 0; @@ -476,9 +507,10 @@ int SubCommand::burnCbFs2Func(int completion) { - char *message = (char*)"Burning FS2 FW image without signatures - "; - char *endStr = (char*)"Restoring signature - OK"; - if (completion == 102) { + char* message = (char*)"Burning FS2 FW image without signatures - "; + char* endStr = (char*)"Restoring signature - OK"; + if (completion == 102) + { endStr = (char*)"Image was successfully cached by driver."; } return CbCommon(completion, message, endStr); @@ -486,26 +518,25 @@ int SubCommand::bromCbFunc(int completion) { - char *message = (char*)"Burning ROM image - "; - char *endStr = (char*)"Restoring signature - OK"; + char* message = (char*)"Burning ROM image - "; + char* endStr = (char*)"Restoring signature - OK"; return CbCommon(completion, message, endStr); } int SubCommand::dromCbFunc(int completion) { - char *message = (char*)"Removing ROM image - "; - char *endStr = (char*)"Restoring signature - OK"; + char* message = (char*)"Removing ROM image - "; + char* endStr = (char*)"Restoring signature - OK"; return CbCommon(completion, message, endStr); } int SubCommand::resetCfgCbFunc(int completion) { - char *message = (char*)"Resetting NV configuration - "; - char *endStr = (char*)"Restoring signature - OK"; + char* message = (char*)"Resetting NV configuration - "; + char* endStr = (char*)"Restoring signature - OK"; return CbCommon(completion, message, endStr); } - int SubCommand::burnBCbFunc(int completion) { return CbCommon(completion, (char*)""); @@ -513,27 +544,27 @@ int SubCommand::vsdCbFunc(int completion) { - char *message = (char*)"Setting the VSD - "; - char *endStr = (char*)"Restoring signature - OK"; + char* message = (char*)"Setting the VSD - "; + char* endStr = (char*)"Restoring signature - OK"; return CbCommon(completion, message, endStr); } int SubCommand::setKeyCbFunc(int completion) { - char *message = (char*)"Setting the HW Key - "; - char *endStr = (char*)"Restoring signature - OK"; + char* message = (char*)"Setting the HW Key - "; + char* endStr = (char*)"Restoring signature - OK"; return CbCommon(completion, message, endStr); } int SubCommand::wbCbFunc(int completion) { - char *message = (char*)"Writing Block: - "; + char* message = (char*)"Writing Block: - "; return CbCommon(completion, message, NULL); } #define ERR_BUFF_SIZE 1024 -void SubCommand::initDeviceFwParams(char *errBuff, FwOperations::fw_ops_params_t& fwParams) +void SubCommand::initDeviceFwParams(char* errBuff, FwOperations::fw_ops_params_t& fwParams) { memset(&fwParams, 0, sizeof(FwOperations::fw_ops_params_t)); fwParams.errBuff = errBuff; @@ -549,16 +580,20 @@ fwParams.cx3FwAccess = _flintParams.use_fw; fwParams.noFwCtrl = _flintParams.no_fw_ctrl; fwParams.mccUnsupported = !_mccSupported; + fwParams.ignoreCrcCheck = _flintParams.ignore_crc_check; } FlintStatus SubCommand::openOps(bool ignoreSecurityAttributes, bool ignoreDToc) { - char errBuff[ERR_BUFF_SIZE] = { 0 }; - if (_flintParams.device_specified) { + DPRINTF(("SubCommand::openOps\n")); + char errBuff[ERR_BUFF_SIZE] = {0}; + if (_flintParams.device_specified) + { // fillup the fw_ops_params_t struct FwOperations::fw_ops_params_t fwParams; initDeviceFwParams(errBuff, fwParams); - if (_flintParams.image_specified) { + if (_flintParams.image_specified) + { FwOperations::fw_ops_params_t imgFwParams; memset(&imgFwParams, 0, sizeof(imgFwParams)); imgFwParams.psid = NULL; @@ -567,51 +602,66 @@ imgFwParams.errBuffSize = 1024; imgFwParams.shortErrors = true; imgFwParams.fileHndl = (char*)_flintParams.image.c_str(); - if (!FwOperations::imageDevOperationsCreate(fwParams, imgFwParams, &_fwOps, &_imgOps, ignoreSecurityAttributes, ignoreDToc)) { + imgFwParams.ignoreCrcCheck = _flintParams.ignore_crc_check; + if (!FwOperations::imageDevOperationsCreate(fwParams, imgFwParams, &_fwOps, &_imgOps, + ignoreSecurityAttributes, ignoreDToc)) + { /* * Error are being handled after */ } - } else { + } + else + { _fwOps = FwOperations::FwOperationsCreate(fwParams); } delete[] fwParams.mstHndl; } - if (_flintParams.image_specified && !_flintParams.device_specified) { - _imgOps = FwOperations::FwOperationsCreate((void*)_flintParams.image.c_str(), NULL, NULL, \ - FHT_FW_FILE, errBuff, 1024); + if (_flintParams.image_specified && !_flintParams.device_specified) + { + _imgOps = + FwOperations::FwOperationsCreate((void*)_flintParams.image.c_str(), NULL, NULL, FHT_FW_FILE, errBuff, 1024); } - if (_flintParams.image_specified && _imgOps == NULL) { + + if (_flintParams.image_specified && _imgOps == NULL) + { reportErr(true, FLINT_OPEN_FWOPS_IMAGE_ERROR, _flintParams.image.c_str(), strlen(errBuff) != 0 ? errBuff : ""); return FLINT_FAILED; } - if (_flintParams.device_specified && _fwOps == NULL) { - if (_flintParams.silent == false) { - reportErr(true, FLINT_OPEN_FWOPS_DEVICE_ERROR, _flintParams.device.c_str(), strlen(errBuff) != 0 ? errBuff : ""); + if (_flintParams.device_specified && _fwOps == NULL) + { + if (_flintParams.silent == false) + { + reportErr(true, FLINT_OPEN_FWOPS_DEVICE_ERROR, _flintParams.device.c_str(), + strlen(errBuff) != 0 ? errBuff : ""); } return FLINT_FAILED; } return FLINT_SUCCESS; } - - FlintStatus SubCommand::openIo() { - //TODO: consider adding a parameter for when image/device will be opened as "readOnly" in the open routine. - if (_flintParams.device_specified && _flintParams.image_specified) { - //should not arrive here as we verify params at each subcommand. + // TODO: consider adding a parameter for when image/device will be opened as "readOnly" in the open routine. + if (_flintParams.device_specified && _flintParams.image_specified) + { + // should not arrive here as we verify params at each subcommand. reportErr(true, FLINT_DEVICE_AND_IMAGE_ERROR); return FLINT_FAILED; } - if (_flintParams.device_specified) { + if (_flintParams.device_specified) + { _io = new Flash; - if (!((Flash*)_io)->open(_flintParams.device.c_str(), _flintParams.clear_semaphore, false, _flintParams.banks, \ - _flintParams.flash_params_specified ? &_flintParams.flash_params : NULL, _flintParams.override_cache_replacement, true, _flintParams.use_fw)) { + if (!((Flash*)_io) + ->open(_flintParams.device.c_str(), _flintParams.clear_semaphore, false, _flintParams.banks, + _flintParams.flash_params_specified ? &_flintParams.flash_params : NULL, + _flintParams.override_cache_replacement, true, _flintParams.use_fw)) + { // if we have Hw_Access command we dont fail straght away u_int8_t lockedCrSpace = ((Flash*)_io)->get_cr_space_locked(); - if (lockedCrSpace && (_flintParams.cmd == SC_Hw_Access || - (_flintParams.cmd == SC_Set_Key && ((Flash*)_io)->is_fifth_gen()))) { + if (lockedCrSpace && + (_flintParams.cmd == SC_Hw_Access || (_flintParams.cmd == SC_Set_Key && ((Flash*)_io)->is_fifth_gen()))) + { return FLINT_SUCCESS; } reportErr(true, FLINT_IO_OPEN_ERROR, "Device", (_io)->err()); @@ -620,11 +670,14 @@ return FLINT_FAILED; } // we have successfully opened a Flash Obj - //set no flash verify if needed (default =false) + // set no flash verify if needed (default =false) ((Flash*)_io)->set_no_flash_verify(_flintParams.no_flash_verify); - } else if (_flintParams.image_specified) { + } + else if (_flintParams.image_specified) + { _io = new FImage; - if (!((FImage*)_io)->open(_flintParams.image.c_str())) { + if (!((FImage*)_io)->open(_flintParams.image.c_str())) + { reportErr(true, FLINT_IO_OPEN_ERROR, "Image", (_io)->err()); delete _io; _io = NULL; @@ -636,93 +689,115 @@ bool SubCommand::basicVerifyParams() { - if (!_flintParams.log_specified) { - char *logFile; + if (!_flintParams.log_specified) + { + char* logFile; logFile = getenv(FLINT_LOG_ENV); - if (logFile) { + if (logFile) + { _flintParams.log = logFile; _flintParams.log_specified = true; } } - //open log if needed + // open log if needed openLog(); - if (_maxCmdParamNum == _minCmdParamNum && _maxCmdParamNum != -1 && (int)_flintParams.cmd_params.size() != _maxCmdParamNum) { + if (_maxCmdParamNum == _minCmdParamNum && _maxCmdParamNum != -1 && + (int)_flintParams.cmd_params.size() != _maxCmdParamNum) + { reportErr(true, FLINT_CMD_ARGS_ERROR, _name.c_str(), _maxCmdParamNum, _flintParams.cmd_params.size()); return false; - } else if (_maxCmdParamNum != -1 && (int)_flintParams.cmd_params.size() > _maxCmdParamNum) { + } + else if (_maxCmdParamNum != -1 && (int)_flintParams.cmd_params.size() > _maxCmdParamNum) + { // _maxCmdParamNum == -1 means ignore this check - if (_maxCmdParamNum) { - reportErr(true, FLINT_CMD_ARGS_ERROR2, _name.c_str(), _maxCmdParamNum, _flintParams.cmd_params.size()); - } else { + if (_maxCmdParamNum) + { + reportErr(true, FLINT_CMD_ARGS_ERROR2, _name.c_str(), _maxCmdParamNum, (int)_flintParams.cmd_params.size()); + } + else + { reportErr(true, FLINT_CMD_ARGS_ERROR5, _name.c_str()); } return false; - } else if (_minCmdParamNum != -1 && (int)_flintParams.cmd_params.size() < _minCmdParamNum) { + } + else if (_minCmdParamNum != -1 && (int)_flintParams.cmd_params.size() < _minCmdParamNum) + { // _minCmdParamNum == -1 means ignore this check reportErr(true, FLINT_CMD_ARGS_ERROR3, _name.c_str(), _minCmdParamNum, _flintParams.cmd_params.size()); return false; } - switch (_v) { - case Wtv_Img: - if (_flintParams.device_specified == true) { - _flintParams.image_specified ? reportErr(true, FLINT_COMMAND_IMAGE_ERROR2, _name.c_str()) : - reportErr(true, FLINT_COMMAND_IMAGE_ERROR, _name.c_str()); - return false; - } - if (_flintParams.image_specified == false) { - reportErr(true, FLINT_NO_IMAGE_ERROR); - return false; - } - break; - - case Wtv_Dev: - if (_flintParams.image_specified == true) { - _flintParams.device_specified ? reportErr(true, FLINT_COMMAND_DEVICE_ERROR2, _name.c_str()) : - reportErr(true, FLINT_COMMAND_DEVICE_ERROR, _name.c_str()); - return false; - } - if (_flintParams.device_specified == false) { - reportErr(true, FLINT_NO_DEVICE_ERROR); - return false; - } - break; + switch (_v) + { + case Wtv_Img: + if (_flintParams.device_specified == true) + { + _flintParams.image_specified ? reportErr(true, FLINT_COMMAND_IMAGE_ERROR2, _name.c_str()) : + reportErr(true, FLINT_COMMAND_IMAGE_ERROR, _name.c_str()); + return false; + } + if (_flintParams.image_specified == false) + { + reportErr(true, FLINT_NO_IMAGE_ERROR); + return false; + } + break; - case Wtv_Dev_And_Img: - if (_flintParams.linkx_control == false) { - if ((_flintParams.image_specified == false) || (_flintParams.device_specified == false)) { - reportErr(true, FLINT_COMMAND_DEVICE_IMAGE_ERROR, _name.c_str()); + case Wtv_Dev: + if (_flintParams.image_specified == true) + { + _flintParams.device_specified ? reportErr(true, FLINT_COMMAND_DEVICE_ERROR2, _name.c_str()) : + reportErr(true, FLINT_COMMAND_DEVICE_ERROR, _name.c_str()); return false; } - } - break; + if (_flintParams.device_specified == false) + { + reportErr(true, FLINT_NO_DEVICE_ERROR); + return false; + } + break; - case Wtv_Dev_Or_Img: - if (_flintParams.image_specified == true && _flintParams.device_specified == true) { - reportErr(true, FLINT_DEVICE_AND_IMAGE_ERROR); - return false; - } - if (_flintParams.device_specified == false && _flintParams.image_specified == false) { - reportErr(true, FLINT_DEVICE_AND_IMAGE_ERROR); - return false; - } - break; + case Wtv_Dev_And_Img: + if (_flintParams.linkx_control == false) + { + if ((_flintParams.image_specified == false) || (_flintParams.device_specified == false)) + { + reportErr(true, FLINT_COMMAND_DEVICE_IMAGE_ERROR, _name.c_str()); + return false; + } + } + break; + + case Wtv_Dev_Or_Img: + if (_flintParams.image_specified == true && _flintParams.device_specified == true) + { + reportErr(true, FLINT_DEVICE_AND_IMAGE_ERROR); + return false; + } + if (_flintParams.device_specified == false && _flintParams.image_specified == false) + { + reportErr(true, FLINT_DEVICE_AND_IMAGE_ERROR); + return false; + } + break; - case Wtv_Uninitilized: + case Wtv_Uninitilized: - return true; - default: - reportErr(true, FAILED_TO_VERIFY_PARAMS); - return false; + return true; + default: + reportErr(true, FAILED_TO_VERIFY_PARAMS); + return false; } - if (_flintParams.device_specified && _flintParams.striped_image) { + if (_flintParams.device_specified && _flintParams.striped_image) + { reportErr(true, FLINT_INVALID_FLAG_WITH_FLAG_ERROR, "-device", "-striped_image"); return false; } - if (_flintParams.override_cache_replacement) { + if (_flintParams.override_cache_replacement) + { printf(FLINT_OCR_WARRNING); } return true; @@ -730,32 +805,40 @@ FlintStatus SubCommand::preFwOps(bool ignoreSecurityAttributes, bool ignoreDToc) { - if (!basicVerifyParams()) { + DPRINTF(("SubCommand::preFwOps\n")); + if (!basicVerifyParams()) + { return FLINT_FAILED; } - if (_flintParams.linkx_control != true) { - if (!verifyParams()) { + if (_flintParams.linkx_control != true) + { + if (!verifyParams()) + { return FLINT_FAILED; } } - if (_flintParams.mfa2_specified || _flintParams.congestion_control || _flintParams.linkx_control == true) { + if (_flintParams.mfa2_specified || _flintParams.congestion_control || _flintParams.linkx_control == true) + { bool saved_value = _flintParams.image_specified; _flintParams.image_specified = false; FlintStatus result = openOps(true); _flintParams.image_specified = saved_value; return result; } - else { + else + { return openOps(ignoreSecurityAttributes, ignoreDToc); } } FlintStatus SubCommand::preFwAccess() { - if (!basicVerifyParams()) { + if (!basicVerifyParams()) + { return FLINT_FAILED; } - if (!verifyParams()) { + if (!verifyParams()) + { return FLINT_FAILED; } return openIo(); @@ -763,28 +846,31 @@ SubCommand::~SubCommand() { - if (_fwOps != NULL) { + if (_fwOps != NULL) + { _fwOps->FwCleanUp(); delete _fwOps; } - if (_imgOps != NULL) { + if (_imgOps != NULL) + { _imgOps->FwCleanUp(); delete _imgOps; } - if (_io != NULL) { + if (_io != NULL) + { _io->close(); delete _io; } - - } -bool SubCommand::getRomsInfo(FBase *io, roms_info_t& romsInfo) +// matanel - TODO: duplicated, use same function from fw_ops.cpp +bool SubCommand::getRomsInfo(FBase* io, roms_info_t& romsInfo) { std::vector romSector; romSector.clear(); - romSector.resize(io->get_size()); - if (!io->read(0, &romSector[0], io->get_size())) { + romSector.resize(io->get_effective_size()); + if (!io->read(0, &romSector[0], io->get_effective_size())) + { reportErr(true, FLINT_READ_ERROR, _flintParams.image.c_str(), io->err()); return false; } @@ -796,13 +882,17 @@ bool SubCommand::getGUIDFromStr(string str, guid_t& guid, string prefixErr) { - char *endp; + char* endp; u_int64_t g; g = strtoull(str.c_str(), &endp, 16); - if (*endp || (g == 0xffffffffffffffffULL && errno == ERANGE)) { - if (prefixErr.size() == 0) { + if (*endp || (g == 0xffffffffffffffffULL && errno == ERANGE)) + { + if (prefixErr.size() == 0) + { reportErr(true, INVALID_GUID_SYNTAX, str.c_str(), errno ? strerror(errno) : ""); - } else { + } + else + { reportErr(true, "%s\n", prefixErr.c_str()); } return false; @@ -812,12 +902,11 @@ return true; } - #if !defined(__WIN__) && !defined(__DJGPP__) && !defined(UEFI_BUILD) && defined(HAVE_TERMIOS_H) static int mygetch(void) { - struct termios oldt, - newt; + struct termios oldt; + struct termios newt; int ch; tcgetattr(STDIN_FILENO, &oldt); newt = oldt; @@ -828,24 +917,27 @@ return ch; } -bool SubCommand::getPasswordFromUser(const char *preStr, char buffer[MAX_PASSWORD_LEN + 1]) +bool SubCommand::getPasswordFromUser(const char* preStr, char buffer[MAX_PASSWORD_LEN + 1]) { char c; int pos = 0; printf("%s: ", preStr); - do { + do + { c = mygetch(); - if (((pos < MAX_PASSWORD_LEN)) && isprint(c)) { + if (((pos < MAX_PASSWORD_LEN)) && isprint(c)) + { buffer[pos++] = c; printf("%c", '*'); - } else if ((c == 8 || c == 127) && pos) { + } + else if ((c == 8 || c == 127) && pos) + { buffer[pos--] = '\0'; printf("%s", "\b \b"); } - } while (c != '\n'); printf("\n"); buffer[pos] = '\0'; @@ -853,7 +945,7 @@ } #else -bool SubCommand::getPasswordFromUser(const char *preStr, char buffer[MAX_PASSWORD_LEN + 1]) +bool SubCommand::getPasswordFromUser(const char* preStr, char buffer[MAX_PASSWORD_LEN + 1]) { static HANDLE stdinHndl = NULL; DWORD numOfBytesRead = 0; @@ -862,44 +954,54 @@ char ch; int i; - if (!stdinHndl) { + if (!stdinHndl) + { // adrianc: this might be problematic if called and stdout was alreading overridden use CIN$ instead stdinHndl = GetStdHandle(STD_INPUT_HANDLE); } printf("%s:", preStr); // disable console echoing - if (!GetConsoleMode(stdinHndl, &oldConsoleMode)) { + if (!GetConsoleMode(stdinHndl, &oldConsoleMode)) + { reportErr(true, FAILED_GET_CONSOLE_MODE); return false; } consoleMode = oldConsoleMode & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT); - if (!SetConsoleMode(stdinHndl, consoleMode)) { + if (!SetConsoleMode(stdinHndl, consoleMode)) + { reportErr(true, FAILED_GET_CONSOLE_MODE); return 1; } // read chars from stdin and print * to stdout using putchar - for (i = 0;;) { + for (i = 0;;) + { status = ReadFile(stdinHndl, &ch, sizeof(char), &numOfBytesRead, NULL); - if (!status || numOfBytesRead != sizeof(char) || ch == '\n' || ch == '\r' || i == (MAX_PASSWORD_LEN - 1)) { + if (!status || numOfBytesRead != sizeof(char) || ch == '\n' || ch == '\r' || i == (MAX_PASSWORD_LEN - 1)) + { // user finished giving the pw - if (!SetConsoleMode(stdinHndl, oldConsoleMode)) { + if (!SetConsoleMode(stdinHndl, oldConsoleMode)) + { reportErr(true, FAILED_GET_CONSOLE_MODE); return false; } - if (!status || numOfBytesRead != sizeof(char)) { + if (!status || numOfBytesRead != sizeof(char)) + { reportErr(true, FAILED_GET_CONSOLE_MODE); return false; } break; } - if (isalpha(ch) || isdigit(ch)) { + if (isalpha(ch) || isdigit(ch)) + { putchar('*'); buffer[i++] = ch; - } else if (ch == '\b' && i) { - //delete last astrix and set correct position + } + else if (ch == '\b' && i) + { + // delete last astrix and set correct position printf("\b \b"); i--; } @@ -916,19 +1018,25 @@ // Returns true if user chose Y, false if user chose N. // -bool SubCommand::askUser(const char *question, bool printAbrtMsg) +bool SubCommand::askUser(const char* question, bool printAbrtMsg) { - if (question == NULL) { + if (question == NULL) + { printf("\n Do you want to continue ? (y/n) [n] : "); - } else { + } + else + { printf("\n %s ? (y/n) [n] : ", question); - } - if (_flintParams.yes) { + if (_flintParams.yes) + { printf("y\n"); - } else { - if (_flintParams.no) { + } + else + { + if (_flintParams.no) + { printf("n\n"); reportErr(false, "-no flag is set\n"); return false; @@ -937,15 +1045,15 @@ fflush(stdout); std::string answer; std::getline(std::cin, answer); - //fgets(ansbuff, 30, stdin); - //if (!fscanf(stdin, "%[^\n]30s", ansbuff)) { + // fgets(ansbuff, 30, stdin); + // if (!fscanf(stdin, "%[^\n]30s", ansbuff)) { // return false; //} - if (strcasecmp(answer.c_str(), "y") && - strcasecmp(answer.c_str(), "yes")) { - - if (printAbrtMsg) { + if (strcasecmp(answer.c_str(), "y") && strcasecmp(answer.c_str(), "yes")) + { + if (printAbrtMsg) + { reportErr(true, USER_ABORT); } return false; @@ -958,49 +1066,50 @@ { string result; - switch (proto) { - case ER_IB: - result = "IB"; - break; + switch (proto) + { + case ER_IB: + result = "IB"; + break; - case ER_ETH: - result = "ETH"; - break; + case ER_ETH: + result = "ETH"; + break; - case ER_VPI: - result = "VPI"; - break; + case ER_VPI: + result = "VPI"; + break; - default: - result = "N/A"; + default: + result = "N/A"; } return result; } - string SubCommand::getRomSuppCpuStr(u_int8_t suppCpu) { string result; - switch (suppCpu) { - case ERC_AMD64: - result = "AMD64"; - break; + switch (suppCpu) + { + case ERC_AMD64: + result = "AMD64"; + break; - case ERC_AARCH64: - result = "AARCH64"; - break; + case ERC_AARCH64: + result = "AARCH64"; + break; - case ERC_AMD64_AARCH64: - result = "AMD64,AARCH64"; - break; + case ERC_AMD64_AARCH64: + result = "AMD64,AARCH64"; + break; - case ERC_IA32: - result = "IA32"; - break; + case ERC_IA32: + result = "IA32"; + break; - default: - result = "N/A"; + default: + result = "N/A"; } return result; } @@ -1008,10 +1117,13 @@ string SubCommand::getExpRomVerStr(const rom_info_t& info) { stringstream verStream; - if (info.exp_rom_num_ver_fields) { - for (int i = 0; i < info.exp_rom_num_ver_fields; i++) { + if (info.exp_rom_num_ver_fields) + { + for (int i = 0; i < info.exp_rom_num_ver_fields; i++) + { verStream << info.exp_rom_ver[i]; - if (i + 1 < info.exp_rom_num_ver_fields) { + if (i + 1 < info.exp_rom_num_ver_fields) + { verStream << "."; } } @@ -1021,28 +1133,38 @@ void SubCommand::displayOneExpRomInfo(const rom_info_t& info) { - const char *typeStr = FwOperations::expRomType2Str(info.exp_rom_product_id); - if (info.exp_rom_product_id == 0xf) { + const char* typeStr = FwOperations::expRomType2Str(info.exp_rom_product_id); + if (info.exp_rom_product_id == 0xf) + { // version id in this case is the freeStr that was moved to exp_rom_ver[0] in mlxfwops printf("version_id=%s type=%s ", getExpRomVerStr(info).c_str(), typeStr); - } else { - if (typeStr) { + } + else + { + if (typeStr) + { printf("type=%s ", typeStr); - } else { + } + else + { printf("0x%x - Unknown ROM product ID\n", info.exp_rom_product_id); return; } printf("version=%s", getExpRomVerStr(info).c_str()); - if (info.exp_rom_product_id >= 0x10) { - if (info.exp_rom_port) { + if (info.exp_rom_product_id >= 0x10) + { + if (info.exp_rom_port) + { // Do not display if 0 - port independent printf(" port=%d", info.exp_rom_port); } - if (info.exp_rom_product_id != 0x12 && info.exp_rom_proto != 0xff) { + if (info.exp_rom_product_id != 0x12 && info.exp_rom_proto != 0xff) + { // on CLP(0x12) there is no meaning to protocol printf(" proto=%s", getRomProtocolStr(info.exp_rom_proto).c_str()); } - if (info.exp_rom_supp_cpu_arch) { + if (info.exp_rom_supp_cpu_arch) + { printf(" cpu=%s", getRomSuppCpuStr(info.exp_rom_supp_cpu_arch).c_str()); } } @@ -1050,38 +1172,48 @@ return; } - -void SubCommand::displayExpRomInfo(const roms_info_t& romsInfo, const char *preStr) +void SubCommand::displayExpRomInfo(const roms_info_t& romsInfo, const char* preStr) { int i; int strLen = strlen(preStr); - if (romsInfo.num_of_exp_rom > 0) { - for (i = 0; i < romsInfo.num_of_exp_rom; i++) { + if (romsInfo.num_of_exp_rom > 0) + { + for (i = 0; i < romsInfo.num_of_exp_rom; i++) + { // Print the pre string or spaces - if (i == 0) { + if (i == 0) + { printf("%s", preStr); - } else { + } + else + { int j; - for (j = 0; j < strLen; j++) { + for (j = 0; j < strLen; j++) + { printf("%s", " "); } } // Display a ROM info displayOneExpRomInfo(romsInfo.rom_info[i]); - if (i != romsInfo.num_of_exp_rom - 1) { + if (i != romsInfo.num_of_exp_rom - 1) + { // Don't print new line after the info of the last ROM printf("\n"); } } - if (romsInfo.exp_rom_warning) { + if (romsInfo.exp_rom_warning) + { printf(" (-W- %s)", romsInfo.exp_rom_warning_msg); } printf("\n"); - } else { + } + else + { printf("%s", preStr); printf("N/A"); - if (romsInfo.exp_rom_err_msg_valid) { + if (romsInfo.exp_rom_err_msg_valid) + { printf(" (-E- %s)", romsInfo.exp_rom_err_msg); } printf("\n"); @@ -1089,52 +1221,68 @@ return; } -bool SubCommand::printGuidLine(guid_t *new_guids, guid_t *old_guids, int guid_index) +bool SubCommand::printGuidLine(guid_t* new_guids, guid_t* old_guids, int guid_index) { printf(GUID_FORMAT GUID_SPACES, new_guids[guid_index].h, new_guids[guid_index].l); - if (old_guids != NULL) { + if (old_guids != NULL) + { printf(GUID_FORMAT, old_guids[guid_index].h, old_guids[guid_index].l); - } else { + } + else + { printf(" N/A"); } printf("\n"); return true; } -bool SubCommand::printMacLine(guid_t *new_guids, guid_t *old_guids, int mac_index) +bool SubCommand::printMacLine(guid_t* new_guids, guid_t* old_guids, int mac_index) { printf(" " MAC_FORMAT MAC_SPACES, new_guids[mac_index].h, new_guids[mac_index].l); - if (old_guids != NULL) { + if (old_guids != NULL) + { printf(MAC_FORMAT, old_guids[mac_index].h, old_guids[mac_index].l); - } else { + } + else + { printf(" N/A"); } printf("\n"); return true; } -bool SubCommand::printGUIDsFunc(guid_t guids[GUIDS], guid_t macs[MACS], guid_t old_guids[GUIDS], \ - guid_t old_macs[MACS], bool print_guids, bool print_macs, int portNum, bool old_guid_fmt) +bool SubCommand::printGUIDsFunc(guid_t guids[GUIDS], + guid_t macs[MACS], + guid_t old_guids[GUIDS], + guid_t old_macs[MACS], + bool print_guids, + bool print_macs, + int portNum, + bool old_guid_fmt) { - - if (print_guids) { + if (print_guids) + { printf(" Node GUID: "); printGuidLine(guids, old_guids, 0); - if (portNum > 0) { + if (portNum > 0) + { printf(" Port1 GUID: "); printGuidLine(guids, old_guids, 1); } - if (portNum > 1) { + if (portNum > 1) + { printf(" Port2 GUID: "); printGuidLine(guids, old_guids, 2); } - if (!old_guid_fmt) { + if (!old_guid_fmt) + { printf(" Sys.Image GUID: "); printGuidLine(guids, old_guids, 3); } } - if (print_macs) { + if (print_macs) + { printf(" Port1 MAC: "); printMacLine(macs, old_macs, 0); printf(" Port2 MAC: "); @@ -1143,34 +1291,36 @@ return true; } -bool SubCommand::reportGuidChanges(guid_t *new_guids, guid_t *new_macs, \ - guid_t *old_guids, guid_t *old_macs, bool printGuids, \ - bool printMacs, int guidNum) -{ - //should be used ONLY on FS2 in current implementation - printf(" You are about to change the Guids/Macs/Uids on the %s:\n\n", _flintParams.device_specified ? "device" : "image"); +bool SubCommand::reportGuidChanges(guid_t* new_guids, + guid_t* new_macs, + guid_t* old_guids, + guid_t* old_macs, + bool printGuids, + bool printMacs, + int guidNum) +{ + // should be used ONLY on FS2 in current implementation + printf(" You are about to change the Guids/Macs/Uids on the %s:\n\n", + _flintParams.device_specified ? "device" : "image"); printf(" New Values " GUID_SPACES "Current Values\n"); - printGUIDsFunc(new_guids, new_macs, - old_guids, old_macs, - printGuids, - printMacs, - guidNum, - guidNum < GUIDS); + printGUIDsFunc(new_guids, new_macs, old_guids, old_macs, printGuids, printMacs, guidNum, guidNum < GUIDS); - if (!askUser()) { + if (!askUser()) + { return false; } return true; } -//used for dc and dh subcommands +// used for dc and dh subcommands -bool SubCommand::unzipDataFile(std::vector data, std::vector &newData, const char *sectionName) +bool SubCommand::unzipDataFile(std::vector data, std::vector& newData, const char* sectionName) { #ifndef NO_ZLIB int rc; - if (data.empty()) { + if (data.empty()) + { reportErr(true, SECTION_NOT_FOUNT, sectionName); return false; } @@ -1182,17 +1332,19 @@ destLen *= 40; // Assuming this is the best compression ratio vector dest(destLen); - for (int i = 0; i < 32; i++) { - rc = uncompress((Bytef*)&(dest[0]), &destLen, - (const Bytef*)&(data[0]), data.size()); - if (rc != Z_BUF_ERROR) { + for (int i = 0; i < 32; i++) + { + rc = uncompress((Bytef*)&(dest[0]), &destLen, (const Bytef*)&(data[0]), data.size()); + if (rc != Z_BUF_ERROR) + { break; } destLen *= 2; dest.resize(destLen); } - if (rc != Z_OK) { + if (rc != Z_OK) + { reportErr(true, UNCOMPRESSS_ERROR, rc); return false; } @@ -1212,44 +1364,60 @@ #endif } -bool SubCommand::dumpFile(const char *confFile, std::vector& data, const char *sectionName) +bool SubCommand::dumpFile(const char* confFile, std::vector& data, const char* sectionName) { - FILE *out; + FILE* out; vector dest; - if (confFile == NULL) { + if (confFile == NULL) + { out = stdout; - } else { + } + else + { out = fopen(confFile, "w"); - if (out == NULL) { + if (out == NULL) + { reportErr(true, OPEN_WRITE_FILE_ERROR, confFile, strerror(errno)); return false; } } - if (unzipDataFile(data, dest, sectionName) == false) { - if (confFile != NULL) { + if (unzipDataFile(data, dest, sectionName) == false) + { + if (confFile != NULL) + { fclose(out); } return false; } fprintf(out, "%s", (char*)&(dest[0])); - if (confFile != NULL) { + if (confFile != NULL) + { fclose(out); } return true; } -bool SubCommand::checkGuidsFlags(u_int16_t devType, u_int8_t fwType, - bool guidsSpecified, bool macsSpecified, bool uidSpecified, bool ibDev, bool ethDev) +bool SubCommand::checkGuidsFlags(u_int16_t devType, + u_int8_t fwType, + bool guidsSpecified, + bool macsSpecified, + bool uidSpecified, + bool ibDev, + bool ethDev) { (void)ibDev; - if (guidsSpecified || macsSpecified || uidSpecified) { - if (uidSpecified && fwType != FIT_FS3 && fwType != FIT_FS4 && fwType != FIT_FSCTRL) { + if (guidsSpecified || macsSpecified || uidSpecified) + { + if (uidSpecified && fwType != FIT_FS3 && fwType != FIT_FS4 && fwType != FIT_FSCTRL) + { reportErr(true, "-uid flag is applicable only for FS3/FS4 FW Only.\n"); return false; - } else if (fwType != FIT_FS2 && !ethDev && macsSpecified) { + } + else if (fwType != FIT_FS2 && !ethDev && macsSpecified) + { reportErr(true, "-mac(s) flag is not applicable for IB MT%d device.\n", devType); return false; } @@ -1259,16 +1427,21 @@ void SubCommand::printMissingGuidErr(bool ibDev, bool ethDev) { - const char *missingInfo; - const char *missingFlags; + const char* missingInfo; + const char* missingFlags; - if (ibDev && ethDev) { + if (ibDev && ethDev) + { missingInfo = "GUIDs / MACs"; missingFlags = "-guid(s) / -mac(s)"; - } else if (ibDev) { + } + else if (ibDev) + { missingInfo = "GUIDs"; missingFlags = "-guid(s)"; - } else { + } + else + { missingInfo = "MACs"; missingFlags = "-mac(s)"; } @@ -1282,40 +1455,50 @@ // check if we need to extract 2 values or 1 u_int32_t tempNum0 = 0, tempNum1 = 0; string tempNumStr; - if (valStr.find(',') != string::npos) { + if (valStr.find(',') != string::npos) + { std::stringstream ss((valStr.c_str())); // get first value - if (!std::getline(ss, tempNumStr, ',')) { + if (!std::getline(ss, tempNumStr, ',')) + { reportErr(true, FLINT_INVALID_ARG_ERROR, origArg.c_str()); return false; } - if (!str2Num(tempNumStr.c_str(), tempNum0)) { + if (!str2Num(tempNumStr.c_str(), tempNum0)) + { reportErr(true, FLINT_INVALID_ARG_ERROR, origArg.c_str()); return false; } // get second value - if (!std::getline(ss, tempNumStr, ',')) { + if (!std::getline(ss, tempNumStr, ',')) + { reportErr(true, FLINT_INVALID_ARG_ERROR, origArg.c_str()); return false; } - if (!str2Num(tempNumStr.c_str(), tempNum1)) { + if (!str2Num(tempNumStr.c_str(), tempNum1)) + { reportErr(true, FLINT_INVALID_ARG_ERROR, origArg.c_str()); return false; } // make sure no other tokens are present - if (!(!std::getline(ss, tempNumStr, ','))) { + if (!(!std::getline(ss, tempNumStr, ','))) + { reportErr(true, FLINT_INVALID_ARG_ERROR, origArg.c_str()); return false; } - } else { - if (!str2Num(valStr.c_str(), tempNum0)) { + } + else + { + if (!str2Num(valStr.c_str(), tempNum0)) + { reportErr(true, FLINT_INVALID_ARG_ERROR, origArg.c_str()); return false; } tempNum1 = tempNum0; } // perform checks - if (tempNum0 >= 255 || tempNum1 >= 255) { + if (tempNum0 >= 255 || tempNum1 >= 255) + { reportErr(true, "Invalid argument values, values should be taken from the range [0..254]\n"); return false; } @@ -1326,34 +1509,45 @@ bool SubCommand::extractUIDArgs(std::vector& cmdArgs, u_int8_t numOfGuids[2], u_int8_t stepSize[2]) { - //extract num_of_guids and step_size from numGuidsStr, stepStr + // extract num_of_guids and step_size from numGuidsStr, stepStr string tag, valStr; - for (std::vector::iterator it = cmdArgs.begin(); it != cmdArgs.end(); it++) { + for (std::vector::iterator it = cmdArgs.begin(); it != cmdArgs.end(); it++) + { std::stringstream ss((it->c_str())); // get the tag - if (!std::getline(ss, tag, '=')) { + if (!std::getline(ss, tag, '=')) + { reportErr(true, FLINT_INVALID_ARG_ERROR, it->c_str()); return false; } // get the val - if (!std::getline(ss, valStr, '=')) { + if (!std::getline(ss, valStr, '=')) + { reportErr(true, FLINT_INVALID_ARG_ERROR, it->c_str()); return false; } // make sure no other tokens are present - if (!(!std::getline(ss, valStr, '='))) { + if (!(!std::getline(ss, valStr, '='))) + { reportErr(true, FLINT_INVALID_ARG_ERROR, it->c_str()); return false; } - if (tag == "guids_num") { - if (!extractValuesFromString(valStr, numOfGuids, *it)) { + if (tag == "guids_num") + { + if (!extractValuesFromString(valStr, numOfGuids, *it)) + { return false; } - } else if (tag == "step_size") { - if (!extractValuesFromString(valStr, stepSize, *it)) { + } + else if (tag == "step_size") + { + if (!extractValuesFromString(valStr, stepSize, *it)) + { return false; } - } else { + } + else + { reportErr(true, FLINT_INVALID_ARG_ERROR, it->c_str()); return false; } @@ -1363,26 +1557,27 @@ const char* SubCommand::fwImgTypeToStr(u_int8_t fwImgType) { - switch (fwImgType) { - case FIT_FS2: - return "FS2"; - break; + switch (fwImgType) + { + case FIT_FS2: + return "FS2"; + break; - case FIT_FS3: - return "FS3"; - break; + case FIT_FS3: + return "FS3"; + break; - case FIT_FS4: - return "FS4"; - break; + case FIT_FS4: + return "FS4"; + break; - case FIT_FSCTRL: - return "FSCTRL"; - break; + case FIT_FSCTRL: + return "FSCTRL"; + break; - default: - return "Unknown"; - break; + default: + return "Unknown"; + break; } } @@ -1406,30 +1601,31 @@ _cmdType = SC_Extract_4MB_Image; } -Extract4MBImageSubCommand::~Extract4MBImageSubCommand() -{ - -} +Extract4MBImageSubCommand::~Extract4MBImageSubCommand() {} FlintStatus Extract4MBImageSubCommand::executeCommand() { vector img; - if (preFwOps() == FLINT_FAILED) { + if (preFwOps() == FLINT_FAILED) + { return FLINT_FAILED; } - if (_imgOps->FwType() == FIT_FS2) { + if (_imgOps->FwType() == FIT_FS2) + { reportErr(true, "Extracting FW Data is applicable only for FS3/FS4 FW.\n"); return FLINT_FAILED; } - if (!_imgOps->FwExtract4MBImage(img, true)) { + if (!_imgOps->FwExtract4MBImage(img, true)) + { reportErr(true, "Extracting FW Data failed: %s.\n", _imgOps->err()); return FLINT_FAILED; } - if (!writeToFile(_flintParams.cmd_params[0], img)) { + if (!writeToFile(_flintParams.cmd_params[0], img)) + { return FLINT_FAILED; } @@ -1453,22 +1649,22 @@ _cmdType = SC_Add_Hmac; } -AddHmacSubCommand:: ~AddHmacSubCommand() -{ - -} +AddHmacSubCommand::~AddHmacSubCommand() {} FlintStatus AddHmacSubCommand::executeCommand() { - if (preFwOps() == FLINT_FAILED) { + if (preFwOps() == FLINT_FAILED) + { return FLINT_FAILED; } - if (_imgOps->FwType() != FIT_FS4) { + if (_imgOps->FwType() != FIT_FS4) + { reportErr(true, "Signing with HMAC is applicable only for FS4 FW.\n"); return FLINT_FAILED; } - if (!_imgOps->FwSignWithHmac(_flintParams.key.c_str())) { + if (!_imgOps->FwSignWithHmac(_flintParams.key.c_str())) + { reportErr(true, FLINT_HMAC_ERROR, _imgOps->err()); return FLINT_FAILED; } @@ -1477,22 +1673,23 @@ bool AddHmacSubCommand::verifyParams() { - if (!_flintParams.key_specified) { + if (!_flintParams.key_specified) + { reportErr(true, "To sign with HMAC, you must provide a key \n"); return false; } - if (_flintParams.cmd_params.size() > 0) { - reportErr(true, FLINT_CMD_ARGS_ERROR, _name.c_str(), 1, - (int)_flintParams.cmd_params.size()); + if (_flintParams.cmd_params.size() > 0) + { + reportErr(true, FLINT_CMD_ARGS_ERROR, _name.c_str(), 1, (int)_flintParams.cmd_params.size()); return false; } return true; } /*********************** -* Class: SignSubCommand -***********************/ + * Class: SignSubCommand + ***********************/ SignSubCommand::SignSubCommand() { _name = "sign"; @@ -1501,48 +1698,60 @@ _flagLong = "sign"; _flagShort = ""; _paramExp = "None"; - _example = FLINT_NAME " -i fw_image.bin [--private_key file.pem --key_uuid uuid_string] OR [--openssl_engine engine --openssl_key_id identifier --key_uuid uuid_string] sign"; + _example = FLINT_NAME + " -i fw_image.bin [--private_key file.pem --key_uuid uuid_string] OR [--openssl_engine engine --openssl_key_id identifier --key_uuid uuid_string] sign"; _v = Wtv_Img; _maxCmdParamNum = 0; _cmdType = SC_Sign; } -SignSubCommand:: ~SignSubCommand() -{ - -} +SignSubCommand::~SignSubCommand() {} FlintStatus SignSubCommand::executeCommand() { - if (preFwOps() == FLINT_FAILED) { + if (preFwOps() == FLINT_FAILED) + { return FLINT_FAILED; } - if (_imgOps->FwType() != FIT_FS3 && _imgOps->FwType() != FIT_FS4) { + if (_imgOps->FwType() != FIT_FS3 && _imgOps->FwType() != FIT_FS4) + { reportErr(true, IMAGE_SIGN_TYPE_ERROR); return FLINT_FAILED; } - if (_flintParams.openssl_engine_usage_specified) { + if (_flintParams.openssl_engine_usage_specified) + { #if !defined(NO_OPEN_SSL) && !defined(NO_DYNAMIC_ENGINE) MlxSign::OpensslEngineSigner engineSigner(_flintParams.openssl_engine, _flintParams.openssl_key_id); int rc = engineSigner.init(); - if (rc) { - reportErr(true, "Failed to initialze %s engine (rc = 0x%x)\n", _flintParams.openssl_engine.c_str(), rc); + if (rc) + { + reportErr(true, "Failed to initialize %s engine (rc = 0x%x)\n", _flintParams.openssl_engine.c_str(), rc); + return FLINT_FAILED; + } + + // flint sign over openssl only allow for 4K key size + int keySize = engineSigner.getPrivateKeySize(); + if (keySize != KEY_SIZE_512) + { + reportErr(true, "The HSM key has to be 4096 bit!\n"); return FLINT_FAILED; } vector fourMbImage; vector signature; vector sha; - if (!_imgOps->FwCalcSHA(MlxSign::SHA512, sha, fourMbImage)) { + if (!_imgOps->FwCalcSHA(MlxSign::SHA512, sha, fourMbImage)) + { reportErr(true, FLINT_IMAGE_READ_ERROR, _imgOps->err()); return FLINT_FAILED; } rc = engineSigner.sign(fourMbImage, signature); - if (rc) { + if (rc) + { reportErr(true, "Failed to set private key from engine (rc = 0x%x)\n", rc); return FLINT_FAILED; } - if (!_imgOps->InsertSecureFWSignature(signature, _flintParams.privkey_uuid.c_str(), - &verifyCbFunc)) { + if (!_imgOps->InsertSecureFWSignature(signature, _flintParams.privkey_uuid.c_str(), &verifyCbFunc)) + { reportErr(true, FLINT_SIGN_ERROR, _imgOps->err()); return FLINT_FAILED; } @@ -1552,90 +1761,42 @@ return FLINT_FAILED; #endif } - if (_flintParams.hsm_specified) { -#ifdef __WIN__ + if (_flintParams.hsm_specified) + { + // Luna HSM not supported reportErr(true, FLINT_NO_HSM); return FLINT_FAILED; -#else - HSMLunaClient m_HSMLunaClient; - string hsm_password = _flintParams.hsm_password; - if (!m_HSMLunaClient.Init(hsm_password)) { - reportErr(true, HSM_INIT_ERROR); - return FLINT_FAILED; - } - if (_imgOps->FwType() != FIT_FS3 && _imgOps->FwType() != FIT_FS4) { - reportErr(true, IMAGE_SIGN_TYPE_ERROR); - return FLINT_FAILED; - } - vector fourMbImage; - vector sha; - if (!_imgOps->FwCalcSHA(MlxSign::SHA512, sha, fourMbImage)) { - reportErr(true, FLINT_IMAGE_READ_ERROR, _imgOps->err()); - return FLINT_FAILED; - } - string private_key_label = _flintParams.private_key_label; - string public_key_label = _flintParams.public_key_label; - cout << "Get private label '" << private_key_label << "'" << endl; - cout << "Get public label '" << public_key_label << "'" << endl; - vector signature; - unsigned long numOfLabels = 0; - m_HSMLunaClient.CheckExistingLabel(private_key_label, numOfLabels); - if (numOfLabels > 1) { - reportErr(true, HSM_PRIVATE_KEY_DUPLICATE); - m_HSMLunaClient.CleanUp(); - return FLINT_FAILED; - } - numOfLabels = 0; - if (public_key_label.empty() == false) { - m_HSMLunaClient.CheckExistingLabel(public_key_label, numOfLabels); - if (numOfLabels > 1) { - reportErr(true, HSM_PUBLIC_KEY_DUPLICATE); - m_HSMLunaClient.CleanUp(); - return FLINT_FAILED; - } - } - if (m_HSMLunaClient.RSA_CreateSignature(fourMbImage, private_key_label, - public_key_label, signature) != CKR_OK) { - reportErr(true, HSM_SIGNATURE_CREATION_FAILED); - m_HSMLunaClient.CleanUp(); - return FLINT_FAILED; - } - m_HSMLunaClient.CleanUp(); - if (signature.size() != 512) { - reportErr(true, "The HSM key has to be 4096 bit!\n"); - return FLINT_FAILED; - } - if (!_imgOps->InsertSecureFWSignature(signature, _flintParams.privkey_uuid.c_str(), - &verifyCbFunc)) { - reportErr(true, FLINT_SIGN_ERROR, _imgOps->err()); - return FLINT_FAILED; - } - return FLINT_SUCCESS; -#endif } - else { - if (_flintParams.privkey_specified && _flintParams.uuid_specified) { - if (_flintParams.privkey2_specified && _flintParams.uuid2_specified) { + else + { + if (_flintParams.privkey_specified && _flintParams.uuid_specified) + { + if (_flintParams.privkey2_specified && _flintParams.uuid2_specified) + { if (!_imgOps->FwSignWithTwoRSAKeys(_flintParams.privkey_file.c_str(), - _flintParams.privkey_uuid.c_str(), - _flintParams.privkey2_file.c_str(), - _flintParams.privkey2_uuid.c_str(), - &verifyCbFunc)) { + _flintParams.privkey_uuid.c_str(), + _flintParams.privkey2_file.c_str(), + _flintParams.privkey2_uuid.c_str(), + &verifyCbFunc)) + { reportErr(true, FLINT_SIGN_ERROR, _imgOps->err()); return FLINT_FAILED; } } - else { - if (!_imgOps->FwSignWithOneRSAKey(_flintParams.privkey_file.c_str(), - _flintParams.privkey_uuid.c_str(), - &verifyCbFunc)) { + else + { + if (!_imgOps->signForFwUpdate(_flintParams.privkey_file.c_str(), _flintParams.privkey_uuid.c_str(), + &verifyCbFunc)) + { reportErr(true, FLINT_SIGN_ERROR, _imgOps->err()); return FLINT_FAILED; } } } - else { - if (!_imgOps->FwInsertSHA256(&verifyCbFunc)) { + else + { + if (!_imgOps->FwInsertSHA256(&verifyCbFunc)) + { reportErr(true, FLINT_SIGN_ERROR, _imgOps->err()); return FLINT_FAILED; } @@ -1646,57 +1807,78 @@ bool SignSubCommand::verifyParams() { - if (_flintParams.openssl_engine_usage_specified) { - if (_flintParams.privkey_uuid.empty()) { + if (_flintParams.openssl_engine_usage_specified) + { + if (_flintParams.privkey_uuid.empty()) + { reportErr(true, "To Sign the image with OpenSSL you must provide UUID string.\n"); return false; } - if (_flintParams.openssl_engine.empty() || _flintParams.openssl_key_id.empty()) { + if (_flintParams.openssl_engine.empty() || _flintParams.openssl_key_id.empty()) + { reportErr(true, "To Sign the image with OpenSSL you must provide the engine and the key identifier.\n"); return false; } - return true; + if (_flintParams.openssl_key_id.find("type=public", 0) != std::string::npos) + { + reportErr(true, "The Sign command with --openssl_key_id flag does not accept public keys\n"); + return false; + } + if (_flintParams.privkey_specified) + { + reportErr( + true, + "The Sign command does not accept --private_key flag with the following flags: --openssl_engine, --openssl_key_id\n"); + return false; + } } - else if (_flintParams.hsm_specified) { - if (_flintParams.uuid_specified == false) { + else if (_flintParams.hsm_specified) + { + if (_flintParams.uuid_specified == false) + { reportErr(true, HSM_UUID_MISSING); return false; } - if (_flintParams.private_key_label_specified == false) { + if (_flintParams.private_key_label_specified == false) + { reportErr(true, HSM_PRIVATE_KEY_LABEL_MISSING); return false; } - if (_flintParams.hsm_password_specified == false) { + if (_flintParams.hsm_password_specified == false) + { reportErr(true, HSM_PASSWORD_MISSING); return false; } - return true; } - else { - if (_flintParams.privkey_specified ^ _flintParams.uuid_specified) { + else + { + if (_flintParams.privkey_specified ^ _flintParams.uuid_specified) + { reportErr(true, "To Sign the image with RSA you must provide " - "private key and uuid.\n"); + "private key and uuid.\n"); return false; } - if (!_flintParams.privkey_specified && _flintParams.privkey2_specified) { + if (!_flintParams.privkey_specified && _flintParams.privkey2_specified) + { reportErr(true, "Use --private_key if you want to sign with only one key.\n"); return false; } - if (_flintParams.privkey2_specified ^ _flintParams.uuid2_specified) { + if (_flintParams.privkey2_specified ^ _flintParams.uuid2_specified) + { reportErr(true, "To Sign the image with two RSA keys you must provide " - "two private keys and two uuid.\n"); + "two private keys and two uuid.\n"); return false; } - if (_flintParams.cmd_params.size() > 0) { - reportErr(true, FLINT_CMD_ARGS_ERROR, _name.c_str(), 1, - (int)_flintParams.cmd_params.size()); + if (_flintParams.cmd_params.size() > 0) + { + reportErr(true, FLINT_CMD_ARGS_ERROR, _name.c_str(), 1, (int)_flintParams.cmd_params.size()); return false; } - return true; } + return true; } /*********************** @@ -1705,7 +1887,8 @@ BinaryCompareSubCommand::BinaryCompareSubCommand() { _name = "Binary compare"; - _desc = "Binary compare between device firmware and given BIN file. If there is a silent mode, no progress is displayed."; + _desc = + "Binary compare between device firmware and given BIN file. If there is a silent mode, no progress is displayed."; _extendedDesc = _desc; _flagLong = "binary_compare"; _flagShort = "bc"; @@ -1722,10 +1905,11 @@ _unknownProgress = 0; } -BinaryCompareSubCommand:: ~BinaryCompareSubCommand() +BinaryCompareSubCommand::~BinaryCompareSubCommand() { #ifndef NO_MSTARCHIVE - if (_mfa2Pkg != NULL) { + if (_mfa2Pkg != NULL) + { delete _mfa2Pkg; } #endif @@ -1733,14 +1917,18 @@ bool BinaryCompareSubCommand::verifyParams() { - if (_flintParams.num_of_args == 2) { + if (_flintParams.num_of_args == 2) + { return true; } - else if (_flintParams.num_of_args == 3 && _flintParams.silent == true) { + else if (_flintParams.num_of_args == 3 && _flintParams.silent == true) + { return true; } - else { - fprintf(stdout, "The binary comparison command doesn't accept any flags, except device, image and silent mode.\n"); + else + { + fprintf(stdout, + "The binary comparison command doesn't accept any flags, except device, image and silent mode.\n"); return false; } } @@ -1748,50 +1936,60 @@ FlintStatus BinaryCompareSubCommand::compareMFA2() { #ifndef NO_MSTARCHIVE - if (preFwOps() == FLINT_FAILED) { + if (preFwOps() == FLINT_FAILED) + { return FLINT_FAILED; } - mfile *mf = _fwOps->getMfileObj(); + mfile* mf = _fwOps->getMfileObj(); int is_livefish_mode = dm_is_livefish_mode(mf); - if (is_livefish_mode == 1) { + if (is_livefish_mode == 1) + { _flintParams.override_cache_replacement = true; } - if (!_fwOps->FwQuery(&_devInfo, true, false, true, false, (_flintParams.silent == false))) { + if (!_fwOps->FwQuery(&_devInfo, true, false, true, false, (_flintParams.silent == false))) + { reportErr(true, FLINT_FAILED_QUERY_ERROR, "Device", _flintParams.device.c_str(), _fwOps->err()); return FLINT_FAILED; } vector componentBuffer; - map_string_to_component matchingComponentsMap = _mfa2Pkg->getMatchingComponents( - (char*)_devInfo.fw_info.psid, _devInfo.fw_info.fw_ver); + map_string_to_component matchingComponentsMap = + _mfa2Pkg->getMatchingComponents((char*)_devInfo.fw_info.psid, _devInfo.fw_info.fw_ver); u_int32_t matchingSize = matchingComponentsMap.size(); - if (matchingSize == 0) { - printf("\33[2K\r");//clear the current line + if (matchingSize == 0) + { + printf("\33[2K\r"); // clear the current line reportErr(true, "CompareMFA2 : No matching component found for device.\n"); return FLINT_FAILED; } std::vector imgBuffInFile; - if (!_fwOps->FwExtract4MBImage(imgBuffInFile, true, (_flintParams.silent == false), true)) { + if (!_fwOps->FwExtract4MBImage(imgBuffInFile, true, (_flintParams.silent == false), true)) + { reportErr(true, FLINT_IMAGE_READ_ERROR, _fwOps->err()); return FLINT_FAILED; } - for (u_int32_t choice = 0; choice < matchingSize; choice++) { - if (_mfa2Pkg->unzipComponent(matchingComponentsMap, choice, componentBuffer) == false) { + for (u_int32_t choice = 0; choice < matchingSize; choice++) + { + if (_mfa2Pkg->unzipComponent(matchingComponentsMap, choice, componentBuffer) == false) + { reportErr(true, "CompareMFA2 : Error occurred while extracting MFA2\n"); return FLINT_FAILED; } unsigned int i = 0; - for (; i < imgBuffInFile.size(); i++) { - if (componentBuffer[i] != imgBuffInFile[i]) { + for (; i < imgBuffInFile.size(); i++) + { + if (componentBuffer[i] != imgBuffInFile[i]) + { break; } } - if (i == imgBuffInFile.size()) { - printf("\33[2K\r");//clear the current line + if (i == imgBuffInFile.size()) + { + printf("\33[2K\r"); // clear the current line printf("Binary comparison success.\n"); return FLINT_SUCCESS; } } - printf("\33[2K\r");//clear the current line + printf("\33[2K\r"); // clear the current line reportErr(true, "Binary comparison failed.\n"); return FLINT_FAILED; #else @@ -1805,7 +2003,8 @@ #ifndef NO_MSTARCHIVE string mfa2file = _flintParams.image; _mfa2Pkg = MFA2::LoadMFA2Package(mfa2file); - if (_mfa2Pkg != NULL) { + if (_mfa2Pkg != NULL) + { _flintParams.mfa2_specified = true; return compareMFA2(); } @@ -1816,83 +2015,95 @@ vector image_critical; vector image_non_critical; - if (preFwOps() == FLINT_FAILED) { - if (_imgOps) { + if (preFwOps() == FLINT_FAILED) + { + if (_imgOps) + { const char* errMessage = _imgOps->err(); - if (errMessage != NULL && strlen(errMessage) > 0) { + if (errMessage != NULL && strlen(errMessage) > 0) + { reportErr(true, "Error occurred while executing flint initialization : %s\n", errMessage); } } return FLINT_FAILED; } - mfile *mf = _fwOps->getMfileObj(); + mfile* mf = _fwOps->getMfileObj(); int is_livefish_mode = dm_is_livefish_mode(mf); - if (is_livefish_mode == 1) { + if (is_livefish_mode == 1) + { _flintParams.override_cache_replacement = true; } _fwType = _fwOps->FwType(); // query both image and device - if (!_fwOps->FwQuery(&_devInfo, true, false, true, false, (_flintParams.silent == false))) { + if (!_fwOps->FwQuery(&_devInfo, true, false, true, false, (_flintParams.silent == false))) + { reportErr(true, FLINT_FAILED_QUERY_ERROR, "Device", _flintParams.device.c_str(), _fwOps->err()); return FLINT_FAILED; } - if (!_imgOps->FwQuery(&_imgInfo)) { + if (!_imgOps->FwQuery(&_imgInfo)) + { reportErr(true, FLINT_FAILED_QUERY_ERROR, "Image", _flintParams.image.c_str(), _imgOps->err()); return FLINT_FAILED; } - if (strcmp((char*)_imgInfo.fw_info.psid, (char*)_devInfo.fw_info.psid)) { - printf("\33[2K\r");//clear the current line + if (strcmp((char*)_imgInfo.fw_info.psid, (char*)_devInfo.fw_info.psid)) + { + printf("\33[2K\r"); // clear the current line reportErr(true, "Binary comparison failed - PSID mismatch.\n"); return FLINT_FAILED; } FwVersion img_version = FwOperations::createFwVersion(&_imgInfo.fw_info); FwVersion dev_version = FwOperations::createFwVersion(&_devInfo.fw_info); - if (img_version != dev_version) { - printf("\33[2K\r"); //clear the current line + if (img_version != dev_version) + { + printf("\33[2K\r"); // clear the current line reportErr(true, "Binary comparison failed - versions mismatch.\n"); return FLINT_FAILED; } u_int32_t imgSize = 0; - //on first call we get the image size - if (!_fwOps->FwReadData(NULL, &imgSize)) { + // on first call we get the image size + if (!_fwOps->FwReadData(NULL, &imgSize)) + { reportErr(true, FLINT_IMAGE_READ_ERROR, _fwOps->err()); return FLINT_FAILED; } std::vector imgBuffOnDevice(imgSize); - //on second call we fill it - if (!_fwOps->FwReadData((void*)(&imgBuffOnDevice[0]), &imgSize, _flintParams.silent == false)) { + // on second call we fill it + if (!_fwOps->FwReadData((void*)(&imgBuffOnDevice[0]), &imgSize, _flintParams.silent == false)) + { reportErr(true, FLINT_IMAGE_READ_ERROR, _fwOps->err()); return FLINT_FAILED; } std::vector imgBuffInFile; - if (!_imgOps->FwExtract4MBImage(imgBuffInFile, false, (_flintParams.silent == false))) { + if (!_imgOps->FwExtract4MBImage(imgBuffInFile, false, (_flintParams.silent == false))) + { reportErr(true, FLINT_IMAGE_READ_ERROR, _imgOps->err()); return FLINT_FAILED; } _imgOps->PrepItocSectionsForCompare(image_critical, image_non_critical); _fwOps->PrepItocSectionsForCompare(device_critical, device_non_critical); - if (image_critical != device_critical) { + if (image_critical != device_critical) + { reportErr(true, "Binary comparison failed - binary mismatch.\n"); return FLINT_FAILED; } - if (image_non_critical != device_non_critical) { + if (image_non_critical != device_non_critical) + { reportErr(true, "Binary comparison failed - binary mismatch.\n"); return FLINT_FAILED; } - printf("\33[2K\r");//clear the current line + printf("\33[2K\r"); // clear the current line printf("Binary comparison success.\n"); return FLINT_SUCCESS; } - /*********************** -* Class: SignRSASubCommand -***********************/ + * Class: SignRSASubCommand + ***********************/ SignRSASubCommand::SignRSASubCommand() { _name = "rsa_sign"; @@ -1901,267 +2112,172 @@ _flagLong = "rsa_sign"; _flagShort = ""; _paramExp = "None"; - _example = FLINT_NAME " -i fw_image.bin [--private_key file.pem] OR [--private_key_label