summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLiang Qi <liang.qi@qt.io>2019-01-26 08:35:40 +0100
committerLiang Qi <liang.qi@qt.io>2019-01-26 08:35:40 +0100
commit980567b3a32b2e2f00c86f2d627cd82b5230dd0f (patch)
treebc8cc4005b2e07cbc5cad8ba30f8c9fa4f236c3d /src
parente81acde7d0cf5fb44a3fb2cf0bf7aaa2c65f807e (diff)
parent730cbad8824bcfcb7ab60371a6563cfb6dd5658d (diff)
Merge remote-tracking branch 'origin/5.12' into dev
Conflicts: src/android/templates/AndroidManifest.xml tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp Change-Id: I4c9679e3a8ebba118fbf4772301ff8fde60455b9
Diffstat (limited to 'src')
-rw-r--r--src/3rdparty/freebsd/qt_attribution.json2
-rw-r--r--src/3rdparty/freetype/freetype.pro7
-rw-r--r--src/3rdparty/sqlite/qt_attribution.json4
-rw-r--r--src/3rdparty/sqlite/sqlite3.c2664
-rw-r--r--src/3rdparty/sqlite/sqlite3.h142
-rw-r--r--src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java2
-rw-r--r--src/android/templates/AndroidManifest.xml23
-rw-r--r--src/corelib/codecs/qwindowscodec.cpp6
-rw-r--r--src/corelib/doc/qtcore.qdocconf3
-rw-r--r--src/corelib/global/qglobal.cpp2
-rw-r--r--src/corelib/global/qnumeric_p.h2
-rw-r--r--src/corelib/io/qdir.cpp93
-rw-r--r--src/corelib/io/qdir_p.h12
-rw-r--r--src/corelib/io/qfilesystemengine_win.cpp3
-rw-r--r--src/corelib/io/qurl.cpp8
-rw-r--r--src/corelib/kernel/qobject.cpp14
-rw-r--r--src/corelib/kernel/qtestsupport_core.cpp6
-rw-r--r--src/corelib/tools/qsimd.cpp2
-rw-r--r--src/gui/configure.json6
-rw-r--r--src/gui/doc/qtgui.qdocconf5
-rw-r--r--src/gui/image/qbmphandler.cpp2
-rw-r--r--src/gui/image/qimage.cpp2
-rw-r--r--src/gui/kernel/qhighdpiscaling.cpp9
-rw-r--r--src/gui/kernel/qplatformwindow.h2
-rw-r--r--src/gui/kernel/qtestsupport_gui.cpp10
-rw-r--r--src/gui/kernel/qwindow.cpp3
-rw-r--r--src/gui/painting/qdrawhelper_sse4.cpp2
-rw-r--r--src/gui/painting/qpdf.cpp7
-rw-r--r--src/gui/painting/qplatformbackingstore.cpp1
-rw-r--r--src/gui/text/qtextengine.cpp12
-rw-r--r--src/network/kernel/qnetworkinterface_unix.cpp2
-rw-r--r--src/network/ssl/qpassworddigestor.cpp2
-rw-r--r--src/platformheaders/eglfsfunctions/qeglfsfunctions.h9
-rw-r--r--src/platformheaders/eglfsfunctions/qeglfsfunctions.qdoc15
-rw-r--r--src/platformheaders/linuxfbfunctions/linuxfbfunctions.pri1
-rw-r--r--src/platformheaders/linuxfbfunctions/qlinuxfbfunctions.h74
-rw-r--r--src/platformheaders/linuxfbfunctions/qlinuxfbfunctions.qdoc82
-rw-r--r--src/platformheaders/platformheaders.pro1
-rw-r--r--src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp11
-rw-r--r--src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler_p.h3
-rw-r--r--src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp6
-rw-r--r--src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h1
-rw-r--r--src/plugins/bearer/networkmanager/qnetworkmanagerservice.h2
-rw-r--r--src/plugins/platforms/android/qandroidinputcontext.cpp38
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfsintegration.cpp13
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfsintegration_p.h1
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp4
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_viv/qeglfsvivintegration.cpp4
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.cpp4
-rw-r--r--src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp49
-rw-r--r--src/plugins/platforms/linuxfb/qlinuxfbintegration.h8
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowsglcontext.cpp4
-rw-r--r--src/plugins/platforms/windows/qwindowspointerhandler.cpp221
-rw-r--r--src/plugins/platforms/windows/qwindowspointerhandler.h5
-rw-r--r--src/plugins/platforms/windows/qwindowssystemtrayicon.cpp39
-rw-r--r--src/plugins/platforms/windows/qwindowssystemtrayicon.h2
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.cpp54
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.h5
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.cpp5
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_basic.cpp8
-rw-r--r--src/plugins/platforms/xcb/qxcbeventqueue.cpp9
-rw-r--r--src/plugins/platforms/xcb/qxcbeventqueue.h7
-rw-r--r--src/plugins/platforms/xcb/xcb_qpa_lib.pro4
-rw-r--r--src/plugins/styles/mac/qmacstyle_mac.mm62
-rw-r--r--src/printsupport/kernel/qprinterinfo.cpp30
-rw-r--r--src/testlib/doc/qttestlib.qdocconf8
-rw-r--r--src/testlib/doc/snippets/code/src_corelib_kernel_qtestsupport_core.cpp (renamed from src/corelib/doc/snippets/code/src_corelib_kernel_qtestsupport_core.cpp)0
-rw-r--r--src/testlib/qtestcase.cpp4
-rw-r--r--src/testlib/qtestcase.qdoc22
-rw-r--r--src/testlib/qtestevent.qdoc2
-rw-r--r--src/testlib/qxmltestlogger.cpp9
-rw-r--r--src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp2
-rw-r--r--src/tools/qdbusxml2cpp/qdbusxml2cpp.cpp2
-rw-r--r--src/widgets/dialogs/qmessagebox.cpp2
-rw-r--r--src/widgets/doc/qtwidgets.qdocconf3
-rw-r--r--src/widgets/itemviews/qlistview.cpp9
-rw-r--r--src/widgets/kernel/qtestsupport_widgets.cpp17
-rw-r--r--src/widgets/kernel/qtooltip.cpp24
-rw-r--r--src/widgets/kernel/qwidget.cpp22
-rw-r--r--src/widgets/kernel/qwidget_p.h4
-rw-r--r--src/widgets/kernel/qwidgetwindow.cpp7
-rw-r--r--src/widgets/styles/qcommonstyle.cpp36
-rw-r--r--src/widgets/styles/qstylesheetstyle.cpp11
-rw-r--r--src/widgets/widgets/qlineedit.cpp1
86 files changed, 3070 insertions, 960 deletions
diff --git a/src/3rdparty/freebsd/qt_attribution.json b/src/3rdparty/freebsd/qt_attribution.json
index 6a4a9ca1af..644880a90d 100644
--- a/src/3rdparty/freebsd/qt_attribution.json
+++ b/src/3rdparty/freebsd/qt_attribution.json
@@ -7,7 +7,7 @@
"Description": "strtoll() and strtoull() are functions for converting a string to (unsigned) long long integer.",
"Homepage": "https://github.com/freebsd/freebsd/",
- "Upstream": "https://raw.githubusercontent.com/freebsd/freebsd/raw/tree/master/lib/libc/stdlib/$file",
+ "DownloadLocation": "https://github.com/freebsd/freebsd/tree/master/lib/libc/stdlib",
"Version": "upstream has complicated with std locales; do not update",
"Version": "18b29f3fb8abee5d57ed8f4a44f806bec7e0eeff",
"License": "BSD 3-clause \"New\" or \"Revised\" License",
diff --git a/src/3rdparty/freetype/freetype.pro b/src/3rdparty/freetype/freetype.pro
index 47ac9122ae..4034815158 100644
--- a/src/3rdparty/freetype/freetype.pro
+++ b/src/3rdparty/freetype/freetype.pro
@@ -69,8 +69,11 @@ DEFINES += FT2_BUILD_LIBRARY
DEFINES += FT_CONFIG_OPTION_SYSTEM_ZLIB
include(../zlib_dependency.pri)
-DEFINES += FT_CONFIG_OPTION_USE_PNG
include($$OUT_PWD/../../gui/qtgui-config.pri)
-QMAKE_USE_PRIVATE += libpng
+QT_FOR_CONFIG += gui-private
+qtConfig(png) {
+ DEFINES += FT_CONFIG_OPTION_USE_PNG
+ QMAKE_USE_PRIVATE += libpng
+}
DEFINES += TT_CONFIG_OPTION_SUBPIXEL_HINTING
diff --git a/src/3rdparty/sqlite/qt_attribution.json b/src/3rdparty/sqlite/qt_attribution.json
index d00a95f1aa..542fa9efb9 100644
--- a/src/3rdparty/sqlite/qt_attribution.json
+++ b/src/3rdparty/sqlite/qt_attribution.json
@@ -6,8 +6,8 @@
"Description": "SQLite is a small C library that implements a self-contained, embeddable, zero-configuration SQL database engine.",
"Homepage": "https://www.sqlite.org/",
- "Version": "3.24.0",
- "DownloadLocation": "https://www.sqlite.org/2018/sqlite-amalgamation-3240000.zip",
+ "Version": "3.26.0",
+ "DownloadLocation": "https://www.sqlite.org/2018/sqlite-amalgamation-3260000.zip",
"License": "Public Domain",
"Copyright": "The authors disclaim copyright to the source code. However, a license can be obtained if needed."
}
diff --git a/src/3rdparty/sqlite/sqlite3.c b/src/3rdparty/sqlite/sqlite3.c
index 7b525c36bb..d015df2c31 100644
--- a/src/3rdparty/sqlite/sqlite3.c
+++ b/src/3rdparty/sqlite/sqlite3.c
@@ -1,6 +1,6 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
-** version 3.25.2. By combining all the individual C code files into this
+** version 3.26.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
@@ -260,6 +260,9 @@ static const char * const sqlite3azCompileOpt[] = {
#if SQLITE_ENABLE_FTS5
"ENABLE_FTS5",
#endif
+#if SQLITE_ENABLE_GEOPOLY
+ "ENABLE_GEOPOLY",
+#endif
#if SQLITE_ENABLE_HIDDEN_COLUMNS
"ENABLE_HIDDEN_COLUMNS",
#endif
@@ -290,6 +293,9 @@ static const char * const sqlite3azCompileOpt[] = {
#if SQLITE_ENABLE_MULTIPLEX
"ENABLE_MULTIPLEX",
#endif
+#if SQLITE_ENABLE_NORMALIZE
+ "ENABLE_NORMALIZE",
+#endif
#if SQLITE_ENABLE_NULL_TRIM
"ENABLE_NULL_TRIM",
#endif
@@ -1156,9 +1162,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.25.2"
-#define SQLITE_VERSION_NUMBER 3025002
-#define SQLITE_SOURCE_ID "2018-09-25 19:08:10 fb90e7189ae6d62e77ba3a308ca5d683f90bbe633cf681865365b8e92792d1c7"
+#define SQLITE_VERSION "3.26.0"
+#define SQLITE_VERSION_NUMBER 3026000
+#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -3050,6 +3056,7 @@ struct sqlite3_mem_methods {
** is invoked.
**
** <dl>
+** [[SQLITE_DBCONFIG_LOOKASIDE]]
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
** <dd> ^This option takes three additional arguments that determine the
** [lookaside memory allocator] configuration for the [database connection].
@@ -3072,6 +3079,7 @@ struct sqlite3_mem_methods {
** memory is in use leaves the configuration unchanged and returns
** [SQLITE_BUSY].)^</dd>
**
+** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
** <dd> ^This option is used to enable or disable the enforcement of
** [foreign key constraints]. There should be two additional arguments.
@@ -3082,6 +3090,7 @@ struct sqlite3_mem_methods {
** following this call. The second parameter may be a NULL pointer, in
** which case the FK enforcement setting is not reported back. </dd>
**
+** [[SQLITE_DBCONFIG_ENABLE_TRIGGER]]
** <dt>SQLITE_DBCONFIG_ENABLE_TRIGGER</dt>
** <dd> ^This option is used to enable or disable [CREATE TRIGGER | triggers].
** There should be two additional arguments.
@@ -3092,6 +3101,7 @@ struct sqlite3_mem_methods {
** following this call. The second parameter may be a NULL pointer, in
** which case the trigger setting is not reported back. </dd>
**
+** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
** <dd> ^This option is used to enable or disable the two-argument
** version of the [fts3_tokenizer()] function which is part of the
@@ -3105,6 +3115,7 @@ struct sqlite3_mem_methods {
** following this call. The second parameter may be a NULL pointer, in
** which case the new setting is not reported back. </dd>
**
+** [[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION]]
** <dt>SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION</dt>
** <dd> ^This option is used to enable or disable the [sqlite3_load_extension()]
** interface independently of the [load_extension()] SQL function.
@@ -3122,7 +3133,7 @@ struct sqlite3_mem_methods {
** be a NULL pointer, in which case the new setting is not reported back.
** </dd>
**
-** <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
+** [[SQLITE_DBCONFIG_MAINDBNAME]] <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
** <dd> ^This option is used to change the name of the "main" database
** schema. ^The sole argument is a pointer to a constant UTF8 string
** which will become the new schema name in place of "main". ^SQLite
@@ -3131,6 +3142,7 @@ struct sqlite3_mem_methods {
** until after the database connection closes.
** </dd>
**
+** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]]
** <dt>SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE</dt>
** <dd> Usually, when a database in wal mode is closed or detached from a
** database handle, SQLite checks if this will mean that there are now no
@@ -3144,7 +3156,7 @@ struct sqlite3_mem_methods {
** have been disabled - 0 if they are not disabled, 1 if they are.
** </dd>
**
-** <dt>SQLITE_DBCONFIG_ENABLE_QPSG</dt>
+** [[SQLITE_DBCONFIG_ENABLE_QPSG]] <dt>SQLITE_DBCONFIG_ENABLE_QPSG</dt>
** <dd>^(The SQLITE_DBCONFIG_ENABLE_QPSG option activates or deactivates
** the [query planner stability guarantee] (QPSG). When the QPSG is active,
** a single SQL query statement will always use the same algorithm regardless
@@ -3160,7 +3172,7 @@ struct sqlite3_mem_methods {
** following this call.
** </dd>
**
-** <dt>SQLITE_DBCONFIG_TRIGGER_EQP</dt>
+** [[SQLITE_DBCONFIG_TRIGGER_EQP]] <dt>SQLITE_DBCONFIG_TRIGGER_EQP</dt>
** <dd> By default, the output of EXPLAIN QUERY PLAN commands does not
** include output for any operations performed by trigger programs. This
** option is used to set or clear (the default) a flag that governs this
@@ -3172,7 +3184,7 @@ struct sqlite3_mem_methods {
** it is not disabled, 1 if it is.
** </dd>
**
-** <dt>SQLITE_DBCONFIG_RESET_DATABASE</dt>
+** [[SQLITE_DBCONFIG_RESET_DATABASE]] <dt>SQLITE_DBCONFIG_RESET_DATABASE</dt>
** <dd> Set the SQLITE_DBCONFIG_RESET_DATABASE flag and then run
** [VACUUM] in order to reset a database back to an empty database
** with no schema and no content. The following process works even for
@@ -3191,6 +3203,18 @@ struct sqlite3_mem_methods {
** Because resetting a database is destructive and irreversible, the
** process requires the use of this obscure API and multiple steps to help
** ensure that it does not happen by accident.
+**
+** [[SQLITE_DBCONFIG_DEFENSIVE]] <dt>SQLITE_DBCONFIG_DEFENSIVE</dt>
+** <dd>The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the
+** "defensive" flag for a database connection. When the defensive
+** flag is enabled, language features that allow ordinary SQL to
+** deliberately corrupt the database file are disabled. The disabled
+** features include but are not limited to the following:
+** <ul>
+** <li> The [PRAGMA writable_schema=ON] statement.
+** <li> Writes to the [sqlite_dbpage] virtual table.
+** <li> Direct writes to [shadow tables].
+** </ul>
** </dd>
** </dl>
*/
@@ -3204,7 +3228,8 @@ struct sqlite3_mem_methods {
#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_MAX 1009 /* Largest DBCONFIG */
+#define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */
+#define SQLITE_DBCONFIG_MAX 1010 /* Largest DBCONFIG */
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
@@ -4642,9 +4667,19 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** on this hint by avoiding the use of [lookaside memory] so as not to
** deplete the limited store of lookaside memory. Future versions of
** SQLite may act on this hint differently.
+**
+** [[SQLITE_PREPARE_NORMALIZE]] ^(<dt>SQLITE_PREPARE_NORMALIZE</dt>
+** <dd>The SQLITE_PREPARE_NORMALIZE flag indicates that a normalized
+** representation of the SQL statement should be calculated and then
+** associated with the prepared statement, which can be obtained via
+** the [sqlite3_normalized_sql()] interface.)^ 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.
** </dl>
*/
#define SQLITE_PREPARE_PERSISTENT 0x01
+#define SQLITE_PREPARE_NORMALIZE 0x02
/*
** CAPI3REF: Compiling An SQL Statement
@@ -4802,6 +4837,11 @@ SQLITE_API int 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
@@ -4817,14 +4857,16 @@ SQLITE_API int sqlite3_prepare16_v3(
** bound parameter expansions. ^The [SQLITE_OMIT_TRACE] compile-time
** option causes sqlite3_expanded_sql() to always return NULL.
**
-** ^The string returned by sqlite3_sql(P) is managed by SQLite and is
-** automatically freed when the prepared statement is finalized.
+** ^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
@@ -7314,6 +7356,9 @@ struct sqlite3_module {
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*);
};
/*
@@ -8236,6 +8281,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#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
@@ -9648,6 +9694,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...);
** can use to customize and optimize their behavior.
**
** <dl>
+** [[SQLITE_VTAB_CONSTRAINT_SUPPORT]]
** <dt>SQLITE_VTAB_CONSTRAINT_SUPPORT
** <dd>Calls of the form
** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported,
@@ -10417,7 +10464,7 @@ struct sqlite3_rtree_query_info {
sqlite3_int64 iRowid; /* Rowid for current entry */
sqlite3_rtree_dbl rParentScore; /* Score of parent node */
int eParentWithin; /* Visibility of parent node */
- int eWithin; /* OUT: Visiblity */
+ 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 */
@@ -10913,12 +10960,38 @@ SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession);
** 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 <b>experimental</b>
+** 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
+**
+** The following flags may passed via the 4th parameter to
+** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
+**
+** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
+** Invert the changeset while iterating through it. This is equivalent to
+** inverting a changeset using sqlite3changeset_invert() before applying it.
+** It is an error to specify this flag with a patchset.
+*/
+#define SQLITE_CHANGESETSTART_INVERT 0x0002
/*
@@ -11573,7 +11646,7 @@ SQLITE_API int sqlite3changeset_apply_v2(
),
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase, /* OUT: Rebase data */
- int flags /* Combination of SESSION_APPLY_* flags */
+ int flags /* SESSION_CHANGESETAPPLY_* flags */
);
/*
@@ -11591,8 +11664,14 @@ SQLITE_API int sqlite3changeset_apply_v2(
** causes the sessions module to omit this savepoint. In this case, if the
** caller has an open transaction or savepoint when apply_v2() is called,
** it may revert the partially applied changeset by rolling it back.
+**
+** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
+** Invert the changeset before applying it. This is equivalent to inverting
+** a changeset using sqlite3changeset_invert() before applying it. It is
+** an error to specify this flag with a patchset.
*/
#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001
+#define SQLITE_CHANGESETAPPLY_INVERT 0x0002
/*
** CAPI3REF: Constants Passed To The Conflict Handler
@@ -11986,6 +12065,12 @@ SQLITE_API int sqlite3changeset_start_strm(
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),
@@ -12012,6 +12097,45 @@ SQLITE_API int sqlite3rebaser_rebase_strm(
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.
+**
+** <dl>
+** <dt>SQLITE_SESSION_CONFIG_STRMSIZE<dd>
+** 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.
+** </dl>
+**
+** 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().
+*/
+#define SQLITE_SESSION_CONFIG_STRMSIZE 1
/*
** Make sure we can call this stuff from C++.
@@ -15277,9 +15401,6 @@ SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager);
SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager);
SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3*);
-# ifdef SQLITE_DIRECT_OVERFLOW_READ
-SQLITE_PRIVATE int sqlite3PagerUseWal(Pager *pPager, Pgno);
-# endif
# ifdef SQLITE_ENABLE_SNAPSHOT
SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot);
SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot);
@@ -15287,8 +15408,10 @@ SQLITE_PRIVATE int sqlite3PagerSnapshotRecover(Pager *pPager);
SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot);
SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager);
# endif
-#else
-# define sqlite3PagerUseWal(x,y) 0
+#endif
+
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
+SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno);
#endif
#ifdef SQLITE_ENABLE_ZIPVFS
@@ -15533,6 +15656,10 @@ SQLITE_PRIVATE int sqlite3HeaderSizePcache1(void);
/* Number of dirty pages as a percentage of the configured cache size */
SQLITE_PRIVATE int sqlite3PCachePercentDirty(PCache*);
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
+SQLITE_PRIVATE int sqlite3PCacheIsDirty(PCache *pCache);
+#endif
+
#endif /* _PCACHE_H_ */
/************** End of pcache.h **********************************************/
@@ -16038,12 +16165,14 @@ struct LookasideSlot {
** functions use a regular table table from hash.h.)
**
** Hash each FuncDef structure into one of the FuncDefHash.a[] slots.
-** Collisions are on the FuncDef.u.pHash chain.
+** Collisions are on the FuncDef.u.pHash chain. Use the SQLITE_FUNC_HASH()
+** macro to compute a hash on the function name.
*/
#define SQLITE_FUNC_HASH_SZ 23
struct FuncDefHash {
FuncDef *a[SQLITE_FUNC_HASH_SZ]; /* Hash table for functions */
};
+#define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ)
#ifdef SQLITE_USER_AUTHENTICATION
/*
@@ -16104,7 +16233,7 @@ struct sqlite3 {
Db *aDb; /* All backends */
int nDb; /* Number of backends currently in use */
u32 mDbFlags; /* flags recording internal state */
- u32 flags; /* flags settable by pragmas. See below */
+ u64 flags; /* flags settable by pragmas. See below */
i64 lastRowid; /* ROWID of most recent insert (see above) */
i64 szMmap; /* Default mmap_size setting */
u32 nSchemaLock; /* Do not reset the schema when non-zero */
@@ -16270,14 +16399,17 @@ struct sqlite3 {
#define SQLITE_TriggerEQP 0x01000000 /* Show trigger EXPLAIN QUERY PLAN */
#define SQLITE_ResetDatabase 0x02000000 /* Reset the database */
#define SQLITE_LegacyAlter 0x04000000 /* Legacy ALTER TABLE behaviour */
+#define SQLITE_NoSchemaError 0x08000000 /* Do not report schema parse errors*/
+#define SQLITE_Defensive 0x10000000 /* Input SQL is likely hostile */
/* Flags used only if debugging */
+#define HI(X) ((u64)(X)<<32)
#ifdef SQLITE_DEBUG
-#define SQLITE_SqlTrace 0x08000000 /* Debug print SQL as it executes */
-#define SQLITE_VdbeListing 0x10000000 /* Debug listings of VDBE programs */
-#define SQLITE_VdbeTrace 0x20000000 /* True to trace VDBE execution */
-#define SQLITE_VdbeAddopTrace 0x40000000 /* Trace sqlite3VdbeAddOp() calls */
-#define SQLITE_VdbeEQP 0x80000000 /* Debug EXPLAIN QUERY PLAN */
+#define SQLITE_SqlTrace HI(0x0001) /* Debug print SQL as it executes */
+#define SQLITE_VdbeListing HI(0x0002) /* Debug listings of VDBE progs */
+#define SQLITE_VdbeTrace HI(0x0004) /* True to trace VDBE execution */
+#define SQLITE_VdbeAddopTrace HI(0x0008) /* Trace sqlite3VdbeAddOp() calls */
+#define SQLITE_VdbeEQP HI(0x0010) /* Debug EXPLAIN QUERY PLAN */
#endif
/*
@@ -16411,8 +16543,9 @@ struct FuncDestructor {
** single query - might change over time */
#define SQLITE_FUNC_AFFINITY 0x4000 /* Built-in affinity() function */
#define SQLITE_FUNC_OFFSET 0x8000 /* Built-in sqlite_offset() function */
-#define SQLITE_FUNC_WINDOW 0x10000 /* Built-in window-only function */
-#define SQLITE_FUNC_WINDOW_SIZE 0x20000 /* Requires partition size as arg. */
+#define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */
+#define SQLITE_FUNC_WINDOW_SIZE 0x20000 /* Requires partition size as arg. */
+#define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */
/*
** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are
@@ -16488,10 +16621,13 @@ struct FuncDestructor {
#define AGGREGATE2(zName, nArg, arg, nc, xStep, xFinal, extraFlags) \
{nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|extraFlags, \
SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xFinal,0,#zName, {0}}
-
#define WAGGREGATE(zName, nArg, arg, nc, xStep, xFinal, xValue, xInverse, f) \
{nArg, 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, \
+ 0, 0, xFunc, 0, 0, 0, #zName, {0} }
+
/*
** All current savepoints are stored in a linked list starting at
@@ -16676,6 +16812,9 @@ struct VTable {
struct Table {
char *zName; /* Name of the table or view */
Column *aCol; /* Information about each column */
+#ifdef SQLITE_ENABLE_NORMALIZE
+ Hash *pColHash; /* All columns indexed by name */
+#endif
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 */
@@ -16726,6 +16865,7 @@ struct Table {
#define TF_StatsUsed 0x0100 /* Query planner decisions affected by
** Index.aiRowLogEst[] values */
#define TF_HasNotNull 0x0200 /* Contains NOT NULL constraints */
+#define TF_Shadow 0x0400 /* True for a shadow table */
/*
** Test to see whether or not a table is a virtual table. This is
@@ -17013,6 +17153,12 @@ struct IndexSample {
};
/*
+** Possible values to use within the flags argument to sqlite3GetToken().
+*/
+#define SQLITE_TOKEN_QUOTED 0x1 /* Token is a quoted identifier. */
+#define SQLITE_TOKEN_KEYWORD 0x2 /* Token is a keyword. */
+
+/*
** Each token coming out of the lexer is an instance of
** this structure. Tokens are also used as part of an expression.
**
@@ -17193,11 +17339,11 @@ struct Expr {
** TK_COLUMN: the value of p5 for OP_Column
** TK_AGG_FUNCTION: nesting depth */
AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
- Table *pTab; /* Table for TK_COLUMN expressions. Can be NULL
- ** for a column of an index on an expression */
-#ifndef SQLITE_OMIT_WINDOWFUNC
- Window *pWin; /* Window definition for window functions */
-#endif
+ union {
+ Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
+ ** for a column of an index on an expression */
+ Window *pWin; /* TK_FUNCTION: Window definition for the func */
+ } y;
};
/*
@@ -17227,6 +17373,7 @@ struct Expr {
#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */
#define EP_Alias 0x400000 /* Is an alias for a result set column */
#define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */
+#define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */
/*
** The EP_Propagate mask is a set of properties that automatically propagate
@@ -17912,6 +18059,7 @@ struct AuthContext {
*/
#define OPFLAG_NCHANGE 0x01 /* OP_Insert: Set to update db->nChange */
/* Also used in P2 (not P5) of OP_Delete */
+#define OPFLAG_NOCHNG 0x01 /* OP_VColumn nochange for UPDATE */
#define OPFLAG_EPHEM 0x01 /* OP_Column: Ephemeral output is ok */
#define OPFLAG_LASTROWID 0x20 /* Set to update db->lastRowid */
#define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */
@@ -18130,6 +18278,7 @@ struct Sqlite3Config {
int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */
#endif
int bLocaltimeFault; /* True to fail localtime() calls */
+ int bInternalFunctions; /* Internal SQL functions are visible */
int iOnceResetThreshold; /* When to reset OP_Once counters */
u32 szSorterRef; /* Min size in bytes to use sorter-refs */
};
@@ -18383,6 +18532,7 @@ SQLITE_PRIVATE int sqlite3IsIdChar(u8);
*/
SQLITE_PRIVATE int sqlite3StrICmp(const char*,const char*);
SQLITE_PRIVATE int sqlite3Strlen30(const char*);
+#define sqlite3Strlen30NN(C) (strlen(C)&0x3fffffff)
SQLITE_PRIVATE char *sqlite3ColumnType(Column*,char*);
#define sqlite3StrNICmp sqlite3_strnicmp
@@ -18499,6 +18649,7 @@ SQLITE_PRIVATE void *sqlite3TestTextToPtr(const char*);
SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView*, const Expr*, u8);
SQLITE_PRIVATE void sqlite3TreeViewBareExprList(TreeView*, const ExprList*, const char*);
SQLITE_PRIVATE void sqlite3TreeViewExprList(TreeView*, const ExprList*, u8, const char*);
+SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView*, const SrcList*);
SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView*, const Select*, u8);
SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView*, const With*, u8);
#ifndef SQLITE_OMIT_WINDOWFUNC
@@ -18731,11 +18882,15 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr*, int*);
SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*);
SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char);
SQLITE_PRIVATE int sqlite3IsRowid(const char*);
+#ifdef SQLITE_ENABLE_NORMALIZE
+SQLITE_PRIVATE int sqlite3IsRowidN(const char*, int);
+#endif
SQLITE_PRIVATE void sqlite3GenerateRowDelete(
Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int);
SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int);
SQLITE_PRIVATE int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*,Index*,int);
SQLITE_PRIVATE void sqlite3ResolvePartIdxLabel(Parse*,int);
+SQLITE_PRIVATE int sqlite3ExprReferencesUpdatedColumn(Expr*,int*,int);
SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int,
u8,u8,int,int*,int*,Upsert*);
#ifdef SQLITE_ENABLE_NULL_TRIM
@@ -18756,6 +18911,9 @@ 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);
+#ifdef SQLITE_ENABLE_NORMALIZE
+SQLITE_PRIVATE FuncDef *sqlite3FunctionSearchN(int,const char*,int);
+#endif
SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(FuncDef*,int);
SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,u8,u8);
SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void);
@@ -18913,6 +19071,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Toke
SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse*,Expr*,const char*);
SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr*);
SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *, CollSeq *);
+SQLITE_PRIVATE int sqlite3WritableSchema(sqlite3*);
SQLITE_PRIVATE int sqlite3CheckObjectName(Parse *, const char *);
SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *, int);
SQLITE_PRIVATE int sqlite3AddInt64(i64*,i64);
@@ -18959,6 +19118,9 @@ SQLITE_PRIVATE void sqlite3AlterFunctions(void);
SQLITE_PRIVATE void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
SQLITE_PRIVATE void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*);
SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *);
+#ifdef SQLITE_ENABLE_NORMALIZE
+SQLITE_PRIVATE int sqlite3GetTokenNormalized(const unsigned char *, int *, int *);
+#endif
SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...);
SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*, int);
SQLITE_PRIVATE int sqlite3CodeSubselect(Parse*, Expr *, int, int);
@@ -19116,6 +19278,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*);
+#ifdef SQLITE_ENABLE_NORMALIZE
+SQLITE_PRIVATE void sqlite3Normalize(Vdbe*, const char*, int, u8);
+#endif
SQLITE_PRIVATE int sqlite3Reprepare(Vdbe*);
SQLITE_PRIVATE void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*);
SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
@@ -19577,6 +19742,7 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
0, /* xTestCallback */
#endif
0, /* bLocaltimeFault */
+ 0, /* bInternalFunctions */
0x7ffffffe, /* iOnceResetThreshold */
SQLITE_DEFAULT_SORTERREF_SIZE /* szSorterRef */
};
@@ -20068,6 +20234,9 @@ struct Vdbe {
yDbMask lockMask; /* Subset of btreeMask that requires a lock */
u32 aCounter[7]; /* 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 */
+#endif
void *pFree; /* Free this when deleting the vdbe */
VdbeFrame *pFrame; /* Parent frame */
VdbeFrame *pDelFrame; /* List of frame objects to free on VM reset */
@@ -20130,7 +20299,9 @@ int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *);
SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(sqlite3*,VdbeCursor*,UnpackedRecord*,int*);
SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3*, BtCursor*, i64*);
SQLITE_PRIVATE int sqlite3VdbeExec(Vdbe*);
+#ifndef SQLITE_OMIT_EXPLAIN
SQLITE_PRIVATE int sqlite3VdbeList(Vdbe*);
+#endif
SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe*);
SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *, int);
SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem*);
@@ -20169,7 +20340,9 @@ SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
#ifndef SQLITE_OMIT_WINDOWFUNC
SQLITE_PRIVATE int sqlite3VdbeMemAggValue(Mem*, Mem*, FuncDef*);
#endif
+#ifndef SQLITE_OMIT_EXPLAIN
SQLITE_PRIVATE const char *sqlite3OpcodeName(int);
+#endif
SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int n);
SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int);
@@ -28296,6 +28469,42 @@ SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 m
}
}
+/*
+** Generate a human-readable description of a SrcList object.
+*/
+SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){
+ int i;
+ for(i=0; i<pSrc->nSrc; i++){
+ const struct SrcList_item *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);
+ }
+ if( pItem->pTab ){
+ sqlite3_str_appendf(&x, " tabname=%Q", pItem->pTab->zName);
+ }
+ if( pItem->zAlias ){
+ sqlite3_str_appendf(&x, " (AS %s)", pItem->zAlias);
+ }
+ if( pItem->fg.jointype & JT_LEFT ){
+ sqlite3_str_appendf(&x, " LEFT-JOIN");
+ }
+ sqlite3StrAccumFinish(&x);
+ sqlite3TreeViewItem(pView, zLine, i<pSrc->nSrc-1);
+ if( pItem->pSelect ){
+ sqlite3TreeViewSelect(pView, pItem->pSelect, 0);
+ }
+ if( pItem->fg.isTabFunc ){
+ sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:");
+ }
+ sqlite3TreeViewPop(pView);
+ }
+}
/*
** Generate a human-readable description of a Select object.
@@ -28350,39 +28559,9 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m
}
#endif
if( p->pSrc && p->pSrc->nSrc ){
- int i;
pView = sqlite3TreeViewPush(pView, (n--)>0);
sqlite3TreeViewLine(pView, "FROM");
- for(i=0; i<p->pSrc->nSrc; i++){
- struct SrcList_item *pItem = &p->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);
- }
- if( pItem->pTab ){
- sqlite3_str_appendf(&x, " tabname=%Q", pItem->pTab->zName);
- }
- if( pItem->zAlias ){
- sqlite3_str_appendf(&x, " (AS %s)", pItem->zAlias);
- }
- if( pItem->fg.jointype & JT_LEFT ){
- sqlite3_str_appendf(&x, " LEFT-JOIN");
- }
- sqlite3StrAccumFinish(&x);
- sqlite3TreeViewItem(pView, zLine, i<p->pSrc->nSrc-1);
- if( pItem->pSelect ){
- sqlite3TreeViewSelect(pView, pItem->pSelect, 0);
- }
- if( pItem->fg.isTabFunc ){
- sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:");
- }
- sqlite3TreeViewPop(pView);
- }
+ sqlite3TreeViewSrcList(pView, p->pSrc);
sqlite3TreeViewPop(pView);
}
if( p->pWhere ){
@@ -28672,7 +28851,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
}else{
pFarg = pExpr->x.pList;
#ifndef SQLITE_OMIT_WINDOWFUNC
- pWin = pExpr->pWin;
+ pWin = pExpr->y.pWin;
#else
pWin = 0;
#endif
@@ -31500,6 +31679,20 @@ static unsigned int strHash(const char *z){
}
return h;
}
+#ifdef SQLITE_ENABLE_NORMALIZE
+static unsigned int strHashN(const char *z, int n){
+ unsigned int h = 0;
+ int i;
+ for(i=0; i<n; i++){
+ /* Knuth multiplicative hashing. (Sorting & Searching, p. 510).
+ ** 0x9e3779b1 is 2654435761 which is the closest prime number to
+ ** (2**32)*golden_ratio, where golden_ratio = (sqrt(5) - 1)/2. */
+ h += sqlite3UpperToLower[z[i]];
+ h *= 0x9e3779b1;
+ }
+ return h;
+}
+#endif /* SQLITE_ENABLE_NORMALIZE */
/* Link pNew element into the hash table pH. If pEntry!=0 then also
@@ -31611,6 +31804,40 @@ static HashElem *findElementWithHash(
}
return &nullElement;
}
+#ifdef SQLITE_ENABLE_NORMALIZE
+static HashElem *findElementWithHashN(
+ const Hash *pH, /* The pH to be searched */
+ const char *pKey, /* The key we are searching for */
+ int nKey, /* Number of key bytes to use */
+ unsigned int *pHash /* Write the hash value here */
+){
+ HashElem *elem; /* Used to loop thru the element list */
+ int count; /* Number of elements left to test */
+ unsigned int h; /* The computed hash */
+ static HashElem nullElement = { 0, 0, 0, 0 };
+
+ if( pH->ht ){ /*OPTIMIZATION-IF-TRUE*/
+ struct _ht *pEntry;
+ h = strHashN(pKey, nKey) % pH->htsize;
+ pEntry = &pH->ht[h];
+ elem = pEntry->chain;
+ count = pEntry->count;
+ }else{
+ h = 0;
+ elem = pH->first;
+ count = pH->count;
+ }
+ if( pHash ) *pHash = h;
+ while( count-- ){
+ assert( elem!=0 );
+ if( sqlite3StrNICmp(elem->pKey,pKey,nKey)==0 ){
+ return elem;
+ }
+ elem = elem->next;
+ }
+ return &nullElement;
+}
+#endif /* SQLITE_ENABLE_NORMALIZE */
/* Remove a single entry from the hash table given a pointer to that
** element and a hash on the element's key.
@@ -31655,6 +31882,14 @@ SQLITE_PRIVATE void *sqlite3HashFind(const Hash *pH, const char *pKey){
assert( pKey!=0 );
return findElementWithHash(pH, pKey, 0)->data;
}
+#ifdef SQLITE_ENABLE_NORMALIZE
+SQLITE_PRIVATE void *sqlite3HashFindN(const Hash *pH, const char *pKey, int nKey){
+ assert( pH!=0 );
+ assert( pKey!=0 );
+ assert( nKey>=0 );
+ return findElementWithHashN(pH, pKey, nKey, 0)->data;
+}
+#endif /* SQLITE_ENABLE_NORMALIZE */
/* Insert an element into the hash table pH. The key is pKey
** and the data is "data".
@@ -32037,12 +32272,10 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
#define SQLITE_FSFLAGS_IS_MSDOS 0x1
/*
-** If we are to be thread-safe, include the pthreads header and define
-** the SQLITE_UNIX_THREADS macro.
+** If we are to be thread-safe, include the pthreads header.
*/
#if SQLITE_THREADSAFE
/* # include <pthread.h> */
-# define SQLITE_UNIX_THREADS 1
#endif
/*
@@ -33218,8 +33451,7 @@ struct unixFileId {
/*
** An instance of the following structure is allocated for each open
-** inode. Or, on LinuxThreads, there is one of these structures for
-** each inode opened by each thread.
+** inode.
**
** A single inode can have multiple file descriptors, so each unixFile
** structure contains a pointer to an instance of this object and this
@@ -33265,13 +33497,16 @@ struct unixInodeInfo {
/*
** A lists of all unixInodeInfo objects.
+**
+** Must hold unixBigLock in order to read or write this variable.
*/
static unixInodeInfo *inodeList = 0; /* All unixInodeInfo objects */
#ifdef SQLITE_DEBUG
/*
-** True if the inode mutex is held, or not. Used only within assert()
-** to help verify correct mutex usage.
+** True if the inode mutex (on the unixFile.pFileMutex field) is held, or not.
+** This routine is used only within assert() to help verify correct mutex
+** usage.
*/
int unixFileMutexHeld(unixFile *pFile){
assert( pFile->pInode );
@@ -33399,8 +33634,8 @@ static void closePendingFds(unixFile *pFile){
/*
** Release a unixInodeInfo structure previously allocated by findInodeInfo().
**
-** The mutex entered using the unixEnterMutex() function must be held
-** when this function is called.
+** The global mutex must be held when this routine is called, but the mutex
+** on the inode being deleted must NOT be held.
*/
static void releaseInodeInfo(unixFile *pFile){
unixInodeInfo *pInode = pFile->pInode;
@@ -33435,8 +33670,7 @@ static void releaseInodeInfo(unixFile *pFile){
** describes that file descriptor. Create a new one if necessary. The
** return value might be uninitialized if an error occurs.
**
-** The mutex entered using the unixEnterMutex() function must be held
-** when this function is called.
+** The global mutex must held when calling this routine.
**
** Return an appropriate error code.
*/
@@ -33497,6 +33731,7 @@ static int findInodeInfo(
#else
fileId.ino = (u64)statbuf.st_ino;
#endif
+ assert( unixMutexHeld() );
pInode = inodeList;
while( pInode && memcmp(&fileId, &pInode->fileId, sizeof(fileId)) ){
pInode = pInode->pNext;
@@ -33516,6 +33751,7 @@ static int findInodeInfo(
}
}
pInode->nRef = 1;
+ assert( unixMutexHeld() );
pInode->pNext = inodeList;
pInode->pPrev = 0;
if( inodeList ) inodeList->pPrev = pInode;
@@ -36313,18 +36549,18 @@ static int unixGetpagesize(void){
**
** The following fields are read-only after the object is created:
**
-** fid
+** hShm
** zFilename
**
-** Either unixShmNode.mutex must be held or unixShmNode.nRef==0 and
+** Either unixShmNode.pShmMutex must be held or unixShmNode.nRef==0 and
** unixMutexHeld() is true when reading or writing any other field
** in this structure.
*/
struct unixShmNode {
unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */
- sqlite3_mutex *mutex; /* Mutex to access this object */
+ sqlite3_mutex *pShmMutex; /* Mutex to access this object */
char *zFilename; /* Name of the mmapped file */
- int h; /* Open file descriptor */
+ int hShm; /* Open file descriptor */
int szRegion; /* Size of shared-memory regions */
u16 nRegion; /* Size of array apRegion */
u8 isReadonly; /* True if read-only */
@@ -36346,16 +36582,16 @@ struct unixShmNode {
** The following fields are initialized when this object is created and
** are read-only thereafter:
**
-** unixShm.pFile
+** unixShm.pShmNode
** unixShm.id
**
-** All other fields are read/write. The unixShm.pFile->mutex must be held
-** while accessing any read/write fields.
+** All other fields are read/write. The unixShm.pShmNode->pShmMutex must
+** be held while accessing any read/write fields.
*/
struct unixShm {
unixShmNode *pShmNode; /* The underlying unixShmNode object */
unixShm *pNext; /* Next unixShm with the same unixShmNode */
- u8 hasMutex; /* True if holding the unixShmNode mutex */
+ u8 hasMutex; /* True if holding the unixShmNode->pShmMutex */
u8 id; /* Id of this connection within its unixShmNode */
u16 sharedMask; /* Mask of shared locks held */
u16 exclMask; /* Mask of exclusive locks held */
@@ -36385,7 +36621,8 @@ static int unixShmSystemLock(
/* Access to the unixShmNode object is serialized by the caller */
pShmNode = pFile->pInode->pShmNode;
- assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->mutex) );
+ assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) );
+ assert( pShmNode->nRef>0 || unixMutexHeld() );
/* Shared locks never span more than one byte */
assert( n==1 || lockType!=F_RDLCK );
@@ -36393,13 +36630,13 @@ static int unixShmSystemLock(
/* Locks are within range */
assert( n>=1 && n<=SQLITE_SHM_NLOCK );
- if( pShmNode->h>=0 ){
+ if( pShmNode->hShm>=0 ){
/* Initialize the locking parameters */
f.l_type = lockType;
f.l_whence = SEEK_SET;
f.l_start = ofst;
f.l_len = n;
- rc = osSetPosixAdvisoryLock(pShmNode->h, &f, pFile);
+ rc = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile);
rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY;
}
@@ -36471,18 +36708,18 @@ static void unixShmPurge(unixFile *pFd){
int nShmPerMap = unixShmRegionPerMap();
int i;
assert( p->pInode==pFd->pInode );
- sqlite3_mutex_free(p->mutex);
+ sqlite3_mutex_free(p->pShmMutex);
for(i=0; i<p->nRegion; i+=nShmPerMap){
- if( p->h>=0 ){
+ if( p->hShm>=0 ){
osMunmap(p->apRegion[i], p->szRegion);
}else{
sqlite3_free(p->apRegion[i]);
}
}
sqlite3_free(p->apRegion);
- if( p->h>=0 ){
- robust_close(pFd, p->h, __LINE__);
- p->h = -1;
+ if( p->hShm>=0 ){
+ robust_close(pFd, p->hShm, __LINE__);
+ p->hShm = -1;
}
p->pInode->pShmNode = 0;
sqlite3_free(p);
@@ -36524,7 +36761,7 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){
lock.l_start = UNIX_SHM_DMS;
lock.l_len = 1;
lock.l_type = F_WRLCK;
- if( osFcntl(pShmNode->h, F_GETLK, &lock)!=0 ) {
+ if( osFcntl(pShmNode->hShm, F_GETLK, &lock)!=0 ) {
rc = SQLITE_IOERR_LOCK;
}else if( lock.l_type==F_UNLCK ){
if( pShmNode->isReadonly ){
@@ -36532,7 +36769,12 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){
rc = SQLITE_READONLY_CANTINIT;
}else{
rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1);
- if( rc==SQLITE_OK && robust_ftruncate(pShmNode->h, 0) ){
+ /* The first connection to attach must truncate the -shm file. We
+ ** truncate to 3 bytes (an arbitrary small number, less than the
+ ** -shm header size) rather than 0 as a system debugging aid, to
+ ** help detect if a -shm file truncation is legitimate or is the work
+ ** or a rogue process. */
+ if( rc==SQLITE_OK && robust_ftruncate(pShmNode->hShm, 3) ){
rc = unixLogError(SQLITE_IOERR_SHMOPEN,"ftruncate",pShmNode->zFilename);
}
}
@@ -36638,12 +36880,12 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
sqlite3_snprintf(nShmFilename, zShm, "%s-shm", zBasePath);
sqlite3FileSuffix3(pDbFd->zPath, zShm);
#endif
- pShmNode->h = -1;
+ pShmNode->hShm = -1;
pDbFd->pInode->pShmNode = pShmNode;
pShmNode->pInode = pDbFd->pInode;
if( sqlite3GlobalConfig.bCoreMutex ){
- pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
- if( pShmNode->mutex==0 ){
+ pShmNode->pShmMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+ if( pShmNode->pShmMutex==0 ){
rc = SQLITE_NOMEM_BKPT;
goto shm_open_err;
}
@@ -36651,11 +36893,11 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
if( pInode->bProcessLock==0 ){
if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
- pShmNode->h = robust_open(zShm, O_RDWR|O_CREAT, (sStat.st_mode&0777));
+ pShmNode->hShm = robust_open(zShm, O_RDWR|O_CREAT,(sStat.st_mode&0777));
}
- if( pShmNode->h<0 ){
- pShmNode->h = robust_open(zShm, O_RDONLY, (sStat.st_mode&0777));
- if( pShmNode->h<0 ){
+ if( pShmNode->hShm<0 ){
+ pShmNode->hShm = robust_open(zShm, O_RDONLY, (sStat.st_mode&0777));
+ if( pShmNode->hShm<0 ){
rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShm);
goto shm_open_err;
}
@@ -36666,7 +36908,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
** is owned by the same user that owns the original database. Otherwise,
** the original owner will not be able to connect.
*/
- robustFchown(pShmNode->h, sStat.st_uid, sStat.st_gid);
+ robustFchown(pShmNode->hShm, sStat.st_uid, sStat.st_gid);
rc = unixLockSharedMemory(pDbFd, pShmNode);
if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err;
@@ -36686,13 +36928,13 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
** the cover of the unixEnterMutex() mutex and the pointer from the
** new (struct unixShm) object to the pShmNode has been set. All that is
** left to do is to link the new object into the linked list starting
- ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex
- ** mutex.
+ ** at pShmNode->pFirst. This must be done while holding the
+ ** pShmNode->pShmMutex.
*/
- sqlite3_mutex_enter(pShmNode->mutex);
+ sqlite3_mutex_enter(pShmNode->pShmMutex);
p->pNext = pShmNode->pFirst;
pShmNode->pFirst = p;
- sqlite3_mutex_leave(pShmNode->mutex);
+ sqlite3_mutex_leave(pShmNode->pShmMutex);
return rc;
/* Jump here on any error */
@@ -36744,7 +36986,7 @@ static int unixShmMap(
p = pDbFd->pShm;
pShmNode = p->pShmNode;
- sqlite3_mutex_enter(pShmNode->mutex);
+ sqlite3_mutex_enter(pShmNode->pShmMutex);
if( pShmNode->isUnlocked ){
rc = unixLockSharedMemory(pDbFd, pShmNode);
if( rc!=SQLITE_OK ) goto shmpage_out;
@@ -36752,8 +36994,8 @@ static int unixShmMap(
}
assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 );
assert( pShmNode->pInode==pDbFd->pInode );
- assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 );
- assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 );
+ assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 );
+ assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 );
/* Minimum number of regions required to be mapped. */
nReqRegion = ((iRegion+nShmPerMap) / nShmPerMap) * nShmPerMap;
@@ -36765,12 +37007,12 @@ static int unixShmMap(
pShmNode->szRegion = szRegion;
- if( pShmNode->h>=0 ){
+ if( pShmNode->hShm>=0 ){
/* The requested region is not mapped into this processes address space.
** Check to see if it has been allocated (i.e. if the wal-index file is
** large enough to contain the requested region).
*/
- if( osFstat(pShmNode->h, &sStat) ){
+ if( osFstat(pShmNode->hShm, &sStat) ){
rc = SQLITE_IOERR_SHMSIZE;
goto shmpage_out;
}
@@ -36798,7 +37040,7 @@ static int unixShmMap(
assert( (nByte % pgsz)==0 );
for(iPg=(sStat.st_size/pgsz); iPg<(nByte/pgsz); iPg++){
int x = 0;
- if( seekAndWriteFd(pShmNode->h, iPg*pgsz + pgsz-1, "", 1, &x)!=1 ){
+ if( seekAndWriteFd(pShmNode->hShm, iPg*pgsz + pgsz-1,"",1,&x)!=1 ){
const char *zFile = pShmNode->zFilename;
rc = unixLogError(SQLITE_IOERR_SHMSIZE, "write", zFile);
goto shmpage_out;
@@ -36821,22 +37063,22 @@ static int unixShmMap(
int nMap = szRegion*nShmPerMap;
int i;
void *pMem;
- if( pShmNode->h>=0 ){
+ if( pShmNode->hShm>=0 ){
pMem = osMmap(0, nMap,
pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE,
- MAP_SHARED, pShmNode->h, szRegion*(i64)pShmNode->nRegion
+ MAP_SHARED, pShmNode->hShm, szRegion*(i64)pShmNode->nRegion
);
if( pMem==MAP_FAILED ){
rc = unixLogError(SQLITE_IOERR_SHMMAP, "mmap", pShmNode->zFilename);
goto shmpage_out;
}
}else{
- pMem = sqlite3_malloc64(szRegion);
+ pMem = sqlite3_malloc64(nMap);
if( pMem==0 ){
rc = SQLITE_NOMEM_BKPT;
goto shmpage_out;
}
- memset(pMem, 0, szRegion);
+ memset(pMem, 0, nMap);
}
for(i=0; i<nShmPerMap; i++){
@@ -36853,7 +37095,7 @@ shmpage_out:
*pp = 0;
}
if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY;
- sqlite3_mutex_leave(pShmNode->mutex);
+ sqlite3_mutex_leave(pShmNode->pShmMutex);
return rc;
}
@@ -36887,12 +37129,12 @@ static int unixShmLock(
|| flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
|| flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );
- assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 );
- assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 );
+ assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 );
+ assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 );
mask = (1<<(ofst+n)) - (1<<ofst);
assert( n>1 || mask==(1<<ofst) );
- sqlite3_mutex_enter(pShmNode->mutex);
+ sqlite3_mutex_enter(pShmNode->pShmMutex);
if( flags & SQLITE_SHM_UNLOCK ){
u16 allMask = 0; /* Mask of locks held by siblings */
@@ -36965,7 +37207,7 @@ static int unixShmLock(
}
}
}
- sqlite3_mutex_leave(pShmNode->mutex);
+ sqlite3_mutex_leave(pShmNode->pShmMutex);
OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n",
p->id, osGetpid(0), p->sharedMask, p->exclMask));
return rc;
@@ -37015,14 +37257,14 @@ static int unixShmUnmap(
/* Remove connection p from the set of connections associated
** with pShmNode */
- sqlite3_mutex_enter(pShmNode->mutex);
+ sqlite3_mutex_enter(pShmNode->pShmMutex);
for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){}
*pp = p->pNext;
/* Free the connection p */
sqlite3_free(p);
pDbFd->pShm = 0;
- sqlite3_mutex_leave(pShmNode->mutex);
+ sqlite3_mutex_leave(pShmNode->pShmMutex);
/* If pShmNode->nRef has reached 0, then close the underlying
** shared-memory file, too */
@@ -37031,7 +37273,7 @@ static int unixShmUnmap(
assert( pShmNode->nRef>0 );
pShmNode->nRef--;
if( pShmNode->nRef==0 ){
- if( deleteFlag && pShmNode->h>=0 ){
+ if( deleteFlag && pShmNode->hShm>=0 ){
osUnlink(pShmNode->zFilename);
}
unixShmPurge(pDbFd);
@@ -40449,8 +40691,7 @@ struct winFile {
int nFetchOut; /* Number of outstanding xFetch references */
HANDLE hMap; /* Handle for accessing memory mapping */
void *pMapRegion; /* Area memory mapped */
- sqlite3_int64 mmapSize; /* Usable size of mapped region */
- sqlite3_int64 mmapSizeActual; /* Actual size of mapped region */
+ sqlite3_int64 mmapSize; /* Size of mapped region */
sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */
#endif
};
@@ -43071,6 +43312,26 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
DWORD lastErrno;
#if SQLITE_MAX_MMAP_SIZE>0
sqlite3_int64 oldMmapSize;
+ if( pFile->nFetchOut>0 ){
+ /* File truncation is a no-op if there are outstanding memory mapped
+ ** pages. This is because truncating the file means temporarily unmapping
+ ** the file, and that might delete memory out from under existing cursors.
+ **
+ ** This can result in incremental vacuum not truncating the file,
+ ** if there is an active read cursor when the incremental vacuum occurs.
+ ** No real harm comes of this - the database file is not corrupted,
+ ** though some folks might complain that the file is bigger than it
+ ** needs to be.
+ **
+ ** The only feasible work-around is to defer the truncation until after
+ ** all references to memory-mapped content are closed. That is doable,
+ ** but involves adding a few branches in the common write code path which
+ ** could slow down normal operations slightly. Hence, we have decided for
+ ** now to simply make trancations a no-op if there are pending reads. We
+ ** can maybe revisit this decision in the future.
+ */
+ return SQLITE_OK;
+ }
#endif
assert( pFile );
@@ -44499,9 +44760,9 @@ shmpage_out:
static int winUnmapfile(winFile *pFile){
assert( pFile!=0 );
OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, pMapRegion=%p, "
- "mmapSize=%lld, mmapSizeActual=%lld, mmapSizeMax=%lld\n",
+ "mmapSize=%lld, mmapSizeMax=%lld\n",
osGetCurrentProcessId(), pFile, pFile->hMap, pFile->pMapRegion,
- pFile->mmapSize, pFile->mmapSizeActual, pFile->mmapSizeMax));
+ pFile->mmapSize, pFile->mmapSizeMax));
if( pFile->pMapRegion ){
if( !osUnmapViewOfFile(pFile->pMapRegion) ){
pFile->lastErrno = osGetLastError();
@@ -44513,7 +44774,6 @@ static int winUnmapfile(winFile *pFile){
}
pFile->pMapRegion = 0;
pFile->mmapSize = 0;
- pFile->mmapSizeActual = 0;
}
if( pFile->hMap!=NULL ){
if( !osCloseHandle(pFile->hMap) ){
@@ -44624,7 +44884,6 @@ static int winMapfile(winFile *pFd, sqlite3_int64 nByte){
}
pFd->pMapRegion = pNew;
pFd->mmapSize = nMap;
- pFd->mmapSizeActual = nMap;
}
OSTRACE(("MAP-FILE pid=%lu, pFile=%p, rc=SQLITE_OK\n",
@@ -45426,7 +45685,6 @@ static int winOpen(
pFile->hMap = NULL;
pFile->pMapRegion = 0;
pFile->mmapSize = 0;
- pFile->mmapSizeActual = 0;
pFile->mmapSizeMax = sqlite3GlobalConfig.szMmap;
#endif
@@ -47323,7 +47581,7 @@ bitvec_end:
** The PCache.pSynced variable is used to optimize searching for a dirty
** page to eject from the cache mid-transaction. It is better to eject
** a page that does not require a journal sync than one that does.
-** Therefore, pSynced is maintained to that it *almost* always points
+** Therefore, pSynced is maintained so that it *almost* always points
** to either the oldest page in the pDirty/pDirtyTail list that has a
** clear PGHDR_NEED_SYNC flag or to a page that is older than this one
** (so that the right page to eject can be found by following pDirtyPrev
@@ -48147,6 +48405,15 @@ SQLITE_PRIVATE int sqlite3PCachePercentDirty(PCache *pCache){
return nCache ? (int)(((i64)nDirty * 100) / nCache) : 0;
}
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
+/*
+** Return true if there are one or more dirty pages in the cache. Else false.
+*/
+SQLITE_PRIVATE int sqlite3PCacheIsDirty(PCache *pCache){
+ return (pCache->pDirty!=0);
+}
+#endif
+
#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG)
/*
** For all dirty pages currently in the cache, invoke the specified
@@ -48270,7 +48537,8 @@ struct PgHdr1 {
};
/*
-** A page is pinned if it is no on the LRU list
+** A page is pinned if it is not on the LRU list. To be "pinned" means
+** that the page is in active use and must not be deallocated.
*/
#define PAGE_IS_PINNED(p) ((p)->pLruNext==0)
#define PAGE_IS_UNPINNED(p) ((p)->pLruNext!=0)
@@ -50910,19 +51178,30 @@ static const unsigned char aJournalMagic[] = {
*/
#define isOpen(pFd) ((pFd)->pMethods!=0)
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
/*
-** Return true if this pager uses a write-ahead log to read page pgno.
-** Return false if the pager reads pgno directly from the database.
+** Return true if page pgno can be read directly from the database file
+** by the b-tree layer. This is the case if:
+**
+** * the database file is open,
+** * there are no dirty pages in the cache, and
+** * the desired page is not currently in the wal file.
*/
-#if !defined(SQLITE_OMIT_WAL) && defined(SQLITE_DIRECT_OVERFLOW_READ)
-SQLITE_PRIVATE int sqlite3PagerUseWal(Pager *pPager, Pgno pgno){
- u32 iRead = 0;
- int rc;
- if( pPager->pWal==0 ) return 0;
- rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
- return rc || iRead;
+SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){
+ if( pPager->fd->pMethods==0 ) return 0;
+ if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0;
+#ifndef SQLITE_OMIT_WAL
+ if( pPager->pWal ){
+ u32 iRead = 0;
+ int rc;
+ rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
+ return (rc==SQLITE_OK && iRead==0);
+ }
+#endif
+ return 1;
}
#endif
+
#ifndef SQLITE_OMIT_WAL
# define pagerUseWal(x) ((x)->pWal!=0)
#else
@@ -57106,7 +57385,11 @@ SQLITE_PRIVATE void sqlite3PagerSetCodec(
void (*xCodecFree)(void*),
void *pCodec
){
- if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec);
+ if( pPager->xCodecFree ){
+ pPager->xCodecFree(pPager->pCodec);
+ }else{
+ pager_reset(pPager);
+ }
pPager->xCodec = pPager->memDb ? 0 : xCodec;
pPager->xCodecSizeChng = xCodecSizeChng;
pPager->xCodecFree = xCodecFree;
@@ -65766,7 +66049,7 @@ static int lockBtree(BtShared *pBt){
pageSize-usableSize);
return rc;
}
- if( (pBt->db->flags & SQLITE_WriteSchema)==0 && nPage>nPageFile ){
+ if( sqlite3WritableSchema(pBt->db)==0 && nPage>nPageFile ){
rc = SQLITE_CORRUPT_BKPT;
goto page1_init_failed;
}
@@ -66240,6 +66523,7 @@ static int relocatePage(
eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE );
assert( sqlite3_mutex_held(pBt->mutex) );
assert( pDbPage->pBt==pBt );
+ if( iDbPage<3 ) return SQLITE_CORRUPT_BKPT;
/* Move page iDbPage from its current location to page number iFreePage */
TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n",
@@ -67411,9 +67695,6 @@ static int accessPayload(
/* Need to read this page properly. It contains some of the
** range of data that is being read (eOp==0) or written (eOp!=0).
*/
-#ifdef SQLITE_DIRECT_OVERFLOW_READ
- sqlite3_file *fd; /* File from which to do direct overflow read */
-#endif
int a = amt;
if( a + offset > ovflSize ){
a = ovflSize - offset;
@@ -67424,7 +67705,7 @@ static int accessPayload(
**
** 1) this is a read operation, and
** 2) data is required from the start of this overflow page, and
- ** 3) there is no open write-transaction, and
+ ** 3) there are no dirty pages in the page-cache
** 4) the database is file-backed, and
** 5) the page is not in the WAL file
** 6) at least 4 bytes have already been read into the output buffer
@@ -67435,11 +67716,10 @@ static int accessPayload(
*/
if( eOp==0 /* (1) */
&& offset==0 /* (2) */
- && pBt->inTransaction==TRANS_READ /* (3) */
- && (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (4) */
- && 0==sqlite3PagerUseWal(pBt->pPager, nextPage) /* (5) */
+ && sqlite3PagerDirectReadOk(pBt->pPager, nextPage) /* (3,4,5) */
&& &pBuf[-4]>=pBufStart /* (6) */
){
+ sqlite3_file *fd = sqlite3PagerFile(pBt->pPager);
u8 aSave[4];
u8 *aWrite = &pBuf[-4];
assert( aWrite>=pBufStart ); /* due to (6) */
@@ -74035,7 +74315,8 @@ SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){
assert( fg & MEM_Real );
sqlite3_snprintf(nByte, pMem->z, "%!.15g", pMem->u.r);
}
- pMem->n = sqlite3Strlen30(pMem->z);
+ assert( pMem->z!=0 );
+ pMem->n = sqlite3Strlen30NN(pMem->z);
pMem->enc = SQLITE_UTF8;
pMem->flags |= MEM_Str|MEM_Term;
if( bForce ) pMem->flags &= ~(MEM_Int|MEM_Real);
@@ -75610,6 +75891,13 @@ SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, u8 prepFlag
}
assert( p->zSql==0 );
p->zSql = sqlite3DbStrNDup(p->db, z, n);
+#ifdef SQLITE_ENABLE_NORMALIZE
+ assert( p->zNormSql==0 );
+ if( p->zSql && (prepFlags & SQLITE_PREPARE_NORMALIZE)!=0 ){
+ sqlite3Normalize(p, p->zSql, n, prepFlags);
+ assert( p->zNormSql!=0 || p->db->mallocFailed );
+ }
+#endif
}
/*
@@ -75631,6 +75919,11 @@ SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){
zTmp = pA->zSql;
pA->zSql = pB->zSql;
pB->zSql = zTmp;
+#ifdef SQLITE_ENABLE_NORMALIZE
+ zTmp = pA->zNormSql;
+ pA->zNormSql = pB->zNormSql;
+ pB->zNormSql = zTmp;
+#endif
pB->expmask = pA->expmask;
pB->prepFlags = pA->prepFlags;
memcpy(pB->aCounter, pA->aCounter, sizeof(pB->aCounter));
@@ -78702,6 +78995,9 @@ SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
vdbeFreeOpArray(db, p->aOp, p->nOp);
sqlite3DbFree(db, p->aColName);
sqlite3DbFree(db, p->zSql);
+#ifdef SQLITE_ENABLE_NORMALIZE
+ sqlite3DbFree(db, p->zNormSql);
+#endif
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
{
int i;
@@ -80103,7 +80399,9 @@ SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){
(void)getVarint32((u8*)m.z, szHdr);
testcase( szHdr==3 );
testcase( szHdr==m.n );
- if( unlikely(szHdr<3 || (int)szHdr>m.n) ){
+ testcase( szHdr>0x7fffffff );
+ assert( m.n>=0 );
+ if( unlikely(szHdr<3 || szHdr>(unsigned)m.n) ){
goto idx_rowid_corruption;
}
@@ -82114,6 +82412,16 @@ SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt){
#endif
}
+#ifdef SQLITE_ENABLE_NORMALIZE
+/*
+** Return the normalized SQL associated with a prepared statement.
+*/
+SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt){
+ Vdbe *p = (Vdbe *)pStmt;
+ return p ? p->zNormSql : 0;
+}
+#endif /* SQLITE_ENABLE_NORMALIZE */
+
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
/*
** Allocate and populate an UnpackedRecord structure based on the serialized
@@ -85553,17 +85861,25 @@ case OP_MakeRecord: {
if( nVarint<sqlite3VarintLen(nHdr) ) nHdr++;
}
nByte = nHdr+nData;
- if( nByte+nZero>db->aLimit[SQLITE_LIMIT_LENGTH] ){
- goto too_big;
- }
/* Make sure the output register has a buffer large enough to store
** the new record. The output register (pOp->p3) is not allowed to
** be one of the input registers (because the following call to
** sqlite3VdbeMemClearAndResize() could clobber the value before it is used).
*/
- if( sqlite3VdbeMemClearAndResize(pOut, (int)nByte) ){
- goto no_mem;
+ if( nByte+nZero<=pOut->szMalloc ){
+ /* The output register is already large enough to hold the record.
+ ** No error checks or buffer enlargement is required */
+ pOut->z = pOut->zMalloc;
+ }else{
+ /* Need to make sure that the output is not too big and then enlarge
+ ** the output register to hold the full result */
+ if( nByte+nZero>db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ goto too_big;
+ }
+ if( sqlite3VdbeMemClearAndResize(pOut, (int)nByte) ){
+ goto no_mem;
+ }
}
zNewRecord = (u8 *)pOut->z;
@@ -88419,7 +88735,7 @@ case OP_ParseSchema: {
{
zMaster = MASTER_NAME;
initData.db = db;
- initData.iDb = pOp->p1;
+ initData.iDb = iDb;
initData.pzErrMsg = &p->zErrMsg;
initData.mInitFlags = 0;
zSql = sqlite3MPrintf(db,
@@ -89616,10 +89932,11 @@ case OP_VFilter: { /* jump */
**
** If the VColumn opcode is being used to fetch the value of
** an unchanging column during an UPDATE operation, then the P5
-** value is 1. Otherwise, P5 is 0. The P5 value is returned
-** by sqlite3_vtab_nochange() routine and can be used
-** by virtual table implementations to return special "no-change"
-** marks which can be more efficient, depending on the virtual table.
+** value is OPFLAG_NOCHNG. This will cause the sqlite3_vtab_nochange()
+** function to return true inside the xColumn method of the virtual
+** table implementation. The P5 column might also contain other
+** bits (OPFLAG_LENGTHARG or OPFLAG_TYPEOFARG) but those bits are
+** unused by OP_VColumn.
*/
case OP_VColumn: {
sqlite3_vtab *pVtab;
@@ -89641,7 +89958,8 @@ case OP_VColumn: {
assert( pModule->xColumn );
memset(&sContext, 0, sizeof(sContext));
sContext.pOut = pDest;
- if( pOp->p5 ){
+ testcase( (pOp->p5 & OPFLAG_NOCHNG)==0 && pOp->p5!=0 );
+ if( pOp->p5 & OPFLAG_NOCHNG ){
sqlite3VdbeMemSetNull(pDest);
pDest->flags = MEM_Null|MEM_Zero;
pDest->u.nZero = 0;
@@ -94000,8 +94318,8 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){
if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
}
#ifndef SQLITE_OMIT_WINDOWFUNC
- if( !ExprHasProperty(pExpr, EP_Reduced) && pExpr->pWin ){
- Window *pWin = pExpr->pWin;
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ Window *pWin = pExpr->y.pWin;
if( sqlite3WalkExprList(pWalker, pWin->pPartition) ) return WRC_Abort;
if( sqlite3WalkExprList(pWalker, pWin->pOrderBy) ) return WRC_Abort;
if( sqlite3WalkExpr(pWalker, pWin->pFilter) ) return WRC_Abort;
@@ -94274,7 +94592,7 @@ SQLITE_PRIVATE int sqlite3MatchSpanName(
** (even if X is implied).
** pExpr->iTable Set to the cursor number for the table obtained
** from pSrcList.
-** pExpr->pTab Points to the Table structure of X.Y (even if
+** pExpr->y.pTab Points to the Table structure of X.Y (even if
** X and/or Y are implied.)
** pExpr->iColumn Set to the column number within the table.
** pExpr->op Set to TK_COLUMN.
@@ -94318,7 +94636,6 @@ static int lookupName(
/* Initialize the node to no-match */
pExpr->iTable = -1;
- pExpr->pTab = 0;
ExprSetVVAProperty(pExpr, EP_NoReduce);
/* Translate the schema name in zDb into a pointer to the corresponding
@@ -94380,7 +94697,7 @@ static int lookupName(
continue;
}
if( IN_RENAME_OBJECT && pItem->zAlias ){
- sqlite3RenameTokenRemap(pParse, 0, (void*)&pExpr->pTab);
+ sqlite3RenameTokenRemap(pParse, 0, (void*)&pExpr->y.pTab);
}
}
if( 0==(cntTab++) ){
@@ -94406,13 +94723,13 @@ static int lookupName(
}
if( pMatch ){
pExpr->iTable = pMatch->iCursor;
- pExpr->pTab = pMatch->pTab;
+ pExpr->y.pTab = pMatch->pTab;
/* RIGHT JOIN not (yet) supported */
assert( (pMatch->fg.jointype & JT_RIGHT)==0 );
if( (pMatch->fg.jointype & JT_LEFT)!=0 ){
ExprSetProperty(pExpr, EP_CanBeNull);
}
- pSchema = pExpr->pTab->pSchema;
+ pSchema = pExpr->y.pTab->pSchema;
}
} /* if( pSrcList ) */
@@ -94469,7 +94786,7 @@ static int lookupName(
testcase( iCol==(-1) );
if( IN_RENAME_OBJECT ){
pExpr->iColumn = iCol;
- pExpr->pTab = pTab;
+ pExpr->y.pTab = pTab;
eNewExprOp = TK_COLUMN;
}else{
pExpr->iTable = pNC->uNC.pUpsert->regData + iCol;
@@ -94491,7 +94808,7 @@ static int lookupName(
testcase( iCol==32 );
pParse->newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
}
- pExpr->pTab = pTab;
+ pExpr->y.pTab = pTab;
pExpr->iColumn = (i16)iCol;
eNewExprOp = TK_TRIGGER;
#endif /* SQLITE_OMIT_TRIGGER */
@@ -94591,7 +94908,7 @@ static int lookupName(
assert( pExpr->op==TK_ID );
if( ExprHasProperty(pExpr,EP_DblQuoted) ){
pExpr->op = TK_STRING;
- pExpr->pTab = 0;
+ pExpr->y.pTab = 0;
return WRC_Prune;
}
if( sqlite3ExprIdToTrueFalse(pExpr) ){
@@ -94669,9 +94986,9 @@ SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSr
Expr *p = sqlite3ExprAlloc(db, TK_COLUMN, 0, 0);
if( p ){
struct SrcList_item *pItem = &pSrc->a[iSrc];
- p->pTab = pItem->pTab;
+ p->y.pTab = pItem->pTab;
p->iTable = pItem->iCursor;
- if( p->pTab->iPKey==iCol ){
+ if( p->y.pTab->iPKey==iCol ){
p->iColumn = -1;
}else{
p->iColumn = (ynVar)iCol;
@@ -94761,7 +95078,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
pItem = pSrcList->a;
assert( HasRowid(pItem->pTab) && pItem->pTab->pSelect==0 );
pExpr->op = TK_COLUMN;
- pExpr->pTab = pItem->pTab;
+ pExpr->y.pTab = pItem->pTab;
pExpr->iTable = pItem->iCursor;
pExpr->iColumn = -1;
pExpr->affinity = SQLITE_AFF_INTEGER;
@@ -94805,9 +95122,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
zColumn = pRight->u.zToken;
if( IN_RENAME_OBJECT ){
sqlite3RenameTokenRemap(pParse, (void*)pExpr, (void*)pRight);
- }
- if( IN_RENAME_OBJECT ){
- sqlite3RenameTokenRemap(pParse, (void*)&pExpr->pTab, (void*)pLeft);
+ sqlite3RenameTokenRemap(pParse, (void*)&pExpr->y.pTab, (void*)pLeft);
}
}
return lookupName(pParse, zDb, zTable, zColumn, pNC, pExpr);
@@ -94889,6 +95204,15 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
notValid(pParse, pNC, "non-deterministic functions",
NC_IdxExpr|NC_PartIdx);
}
+ if( (pDef->funcFlags & SQLITE_FUNC_INTERNAL)!=0
+ && pParse->nested==0
+ && sqlite3Config.bInternalFunctions==0
+ ){
+ /* Internal-use-only functions are disallowed unless the
+ ** SQL is being compiled using sqlite3NestedParse() */
+ no_such_func = 1;
+ pDef = 0;
+ }
}
if( 0==IN_RENAME_OBJECT ){
@@ -94897,18 +95221,18 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|| (pDef->xValue==0 && pDef->xInverse==0)
|| (pDef->xValue && pDef->xInverse && pDef->xSFunc && pDef->xFinalize)
);
- if( pDef && pDef->xValue==0 && pExpr->pWin ){
+ if( pDef && pDef->xValue==0 && ExprHasProperty(pExpr, EP_WinFunc) ){
sqlite3ErrorMsg(pParse,
"%.*s() may not be used as a window function", nId, zId
);
pNC->nErr++;
}else if(
(is_agg && (pNC->ncFlags & NC_AllowAgg)==0)
- || (is_agg && (pDef->funcFlags & SQLITE_FUNC_WINDOW) && !pExpr->pWin)
- || (is_agg && pExpr->pWin && (pNC->ncFlags & NC_AllowWin)==0)
+ || (is_agg && (pDef->funcFlags&SQLITE_FUNC_WINDOW) && !pExpr->y.pWin)
+ || (is_agg && pExpr->y.pWin && (pNC->ncFlags & NC_AllowWin)==0)
){
const char *zType;
- if( (pDef->funcFlags & SQLITE_FUNC_WINDOW) || pExpr->pWin ){
+ if( (pDef->funcFlags & SQLITE_FUNC_WINDOW) || pExpr->y.pWin ){
zType = "window";
}else{
zType = "aggregate";
@@ -94938,7 +95262,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
}
if( is_agg ){
#ifndef SQLITE_OMIT_WINDOWFUNC
- pNC->ncFlags &= ~(pExpr->pWin ? NC_AllowWin : NC_AllowAgg);
+ pNC->ncFlags &= ~(pExpr->y.pWin ? NC_AllowWin : NC_AllowAgg);
#else
pNC->ncFlags &= ~NC_AllowAgg;
#endif
@@ -94947,17 +95271,17 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
sqlite3WalkExprList(pWalker, pList);
if( is_agg ){
#ifndef SQLITE_OMIT_WINDOWFUNC
- if( pExpr->pWin ){
+ if( pExpr->y.pWin ){
Select *pSel = pNC->pWinSelect;
- sqlite3WalkExprList(pWalker, pExpr->pWin->pPartition);
- sqlite3WalkExprList(pWalker, pExpr->pWin->pOrderBy);
- sqlite3WalkExpr(pWalker, pExpr->pWin->pFilter);
- sqlite3WindowUpdate(pParse, pSel->pWinDefn, pExpr->pWin, pDef);
+ sqlite3WalkExprList(pWalker, pExpr->y.pWin->pPartition);
+ sqlite3WalkExprList(pWalker, pExpr->y.pWin->pOrderBy);
+ sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter);
+ sqlite3WindowUpdate(pParse, pSel->pWinDefn, pExpr->y.pWin, pDef);
if( 0==pSel->pWin
- || 0==sqlite3WindowCompare(pParse, pSel->pWin, pExpr->pWin)
+ || 0==sqlite3WindowCompare(pParse, pSel->pWin, pExpr->y.pWin)
){
- pExpr->pWin->pNextWin = pSel->pWin;
- pSel->pWin = pExpr->pWin;
+ pExpr->y.pWin->pNextWin = pSel->pWin;
+ pSel->pWin = pExpr->y.pWin;
}
pNC->ncFlags |= NC_AllowWin;
}else
@@ -95380,13 +95704,13 @@ static int resolveOrderGroupBy(
for(j=0; j<pSelect->pEList->nExpr; j++){
if( sqlite3ExprCompare(0, pE, pSelect->pEList->a[j].pExpr, -1)==0 ){
#ifndef SQLITE_OMIT_WINDOWFUNC
- if( pE->pWin ){
+ if( ExprHasProperty(pE, EP_WinFunc) ){
/* Since this window function is being changed into a reference
** to the same window function the result set, remove the instance
** of this window function from the Select.pWin list. */
Window **pp;
for(pp=&pSelect->pWin; *pp; pp=&(*pp)->pNextWin){
- if( *pp==pE->pWin ){
+ if( *pp==pE->y.pWin ){
*pp = (*pp)->pNextWin;
}
}
@@ -95849,8 +96173,8 @@ SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr){
return sqlite3AffinityType(pExpr->u.zToken, 0);
}
#endif
- if( (op==TK_AGG_COLUMN || op==TK_COLUMN) && pExpr->pTab ){
- return sqlite3TableColumnAffinity(pExpr->pTab, pExpr->iColumn);
+ 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 );
@@ -95934,13 +96258,13 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
if( p->flags & EP_Generic ) break;
if( (op==TK_AGG_COLUMN || op==TK_COLUMN
|| op==TK_REGISTER || op==TK_TRIGGER)
- && p->pTab!=0
+ && p->y.pTab!=0
){
- /* op==TK_REGISTER && p->pTab!=0 happens when pExpr was originally
+ /* 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->pTab->aCol[j].zColl;
+ const char *zColl = p->y.pTab->aCol[j].zColl;
pColl = sqlite3FindCollSeq(db, ENC(db), zColl, 0);
}
break;
@@ -96843,6 +97167,10 @@ 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) );
#ifdef SQLITE_DEBUG
if( ExprHasProperty(p, EP_Leaf) && !ExprHasProperty(p, EP_TokenOnly) ){
assert( p->pLeft==0 );
@@ -96861,8 +97189,9 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
}else{
sqlite3ExprListDelete(db, p->x.pList);
}
- if( !ExprHasProperty(p, EP_Reduced) ){
- sqlite3WindowDelete(db, p->pWin);
+ if( ExprHasProperty(p, EP_WinFunc) ){
+ assert( p->op==TK_FUNCTION );
+ sqlite3WindowDelete(db, p->y.pWin);
}
}
if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken);
@@ -96926,7 +97255,7 @@ static int dupedExprStructSize(Expr *p, int flags){
assert( (0xfff & (EP_Reduced|EP_TokenOnly))==0 );
if( 0==flags || p->op==TK_SELECT_COLUMN
#ifndef SQLITE_OMIT_WINDOWFUNC
- || p->pWin
+ || ExprHasProperty(p, EP_WinFunc)
#endif
){
nSize = EXPR_FULLSIZE;
@@ -96953,7 +97282,7 @@ static int dupedExprStructSize(Expr *p, int flags){
static int dupedExprNodeSize(Expr *p, int flags){
int nByte = dupedExprStructSize(p, flags) & 0xfff;
if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
- nByte += sqlite3Strlen30(p->u.zToken)+1;
+ nByte += sqlite3Strlen30NN(p->u.zToken)+1;
}
return ROUND8(nByte);
}
@@ -97056,22 +97385,24 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){
}
/* Fill in pNew->pLeft and pNew->pRight. */
- zAlloc += dupedExprNodeSize(p, dupFlags);
- if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly) ){
+ if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly|EP_WinFunc) ){
+ zAlloc += dupedExprNodeSize(p, dupFlags);
if( !ExprHasProperty(pNew, EP_TokenOnly|EP_Leaf) ){
pNew->pLeft = p->pLeft ?
exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc) : 0;
pNew->pRight = p->pRight ?
exprDup(db, p->pRight, EXPRDUP_REDUCE, &zAlloc) : 0;
}
- }else{
#ifndef SQLITE_OMIT_WINDOWFUNC
- if( ExprHasProperty(p, EP_Reduced|EP_TokenOnly) ){
- pNew->pWin = 0;
- }else{
- pNew->pWin = sqlite3WindowDup(db, pNew, p->pWin);
+ if( ExprHasProperty(p, EP_WinFunc) ){
+ pNew->y.pWin = sqlite3WindowDup(db, pNew, p->y.pWin);
+ assert( ExprHasProperty(pNew, EP_WinFunc) );
}
#endif /* SQLITE_OMIT_WINDOWFUNC */
+ if( pzBuffer ){
+ *pzBuffer = zAlloc;
+ }
+ }else{
if( !ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){
if( pNew->op==TK_SELECT_COLUMN ){
pNew->pLeft = p->pLeft;
@@ -97083,9 +97414,6 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){
pNew->pRight = sqlite3ExprDup(db, p->pRight, 0);
}
}
- if( pzBuffer ){
- *pzBuffer = zAlloc;
- }
}
return pNew;
}
@@ -97880,8 +98208,8 @@ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){
return 0;
case TK_COLUMN:
return ExprHasProperty(p, EP_CanBeNull) ||
- p->pTab==0 || /* Reference to column of index on expression */
- (p->iColumn>=0 && p->pTab->aCol[p->iColumn].notNull==0);
+ p->y.pTab==0 || /* Reference to column of index on expression */
+ (p->iColumn>=0 && p->y.pTab->aCol[p->iColumn].notNull==0);
default:
return 1;
}
@@ -97936,6 +98264,14 @@ SQLITE_PRIVATE int sqlite3IsRowid(const char *z){
if( sqlite3StrICmp(z, "OID")==0 ) return 1;
return 0;
}
+#ifdef SQLITE_ENABLE_NORMALIZE
+SQLITE_PRIVATE int sqlite3IsRowidN(const char *z, int n){
+ if( sqlite3StrNICmp(z, "_ROWID_", n)==0 ) return 1;
+ if( sqlite3StrNICmp(z, "ROWID", n)==0 ) return 1;
+ if( sqlite3StrNICmp(z, "OID", n)==0 ) return 1;
+ return 0;
+}
+#endif
/*
** pX is the RHS of an IN operator. If pX is a SELECT statement
@@ -99169,7 +99505,7 @@ expr_code_doover:
** constant.
*/
int iReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft,target);
- int aff = sqlite3TableColumnAffinity(pExpr->pTab, pExpr->iColumn);
+ int aff = sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn);
if( aff!=SQLITE_AFF_BLOB ){
static const char zAff[] = "B\000C\000D\000E";
assert( SQLITE_AFF_BLOB=='A' );
@@ -99193,7 +99529,7 @@ expr_code_doover:
iTab = pParse->iSelfTab - 1;
}
}
- return sqlite3ExprCodeGetColumn(pParse, pExpr->pTab,
+ return sqlite3ExprCodeGetColumn(pParse, pExpr->y.pTab,
pExpr->iColumn, iTab, target,
pExpr->op2);
}
@@ -99407,8 +99743,8 @@ expr_code_doover:
CollSeq *pColl = 0; /* A collating sequence */
#ifndef SQLITE_OMIT_WINDOWFUNC
- if( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) && pExpr->pWin ){
- return pExpr->pWin->regResult;
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ return pExpr->y.pWin->regResult;
}
#endif
@@ -99651,7 +99987,7 @@ expr_code_doover:
** p1==1 -> old.a p1==4 -> new.a
** p1==2 -> old.b p1==5 -> new.b
*/
- Table *pTab = pExpr->pTab;
+ Table *pTab = pExpr->y.pTab;
int p1 = pExpr->iTable * (pTab->nCol+1) + 1 + pExpr->iColumn;
assert( pExpr->iTable==0 || pExpr->iTable==1 );
@@ -99662,7 +99998,7 @@ expr_code_doover:
sqlite3VdbeAddOp2(v, OP_Param, p1, target);
VdbeComment((v, "r[%d]=%s.%s", target,
(pExpr->iTable ? "new" : "old"),
- (pExpr->iColumn<0 ? "rowid" : pExpr->pTab->aCol[pExpr->iColumn].zName)
+ (pExpr->iColumn<0 ? "rowid" : pExpr->y.pTab->aCol[pExpr->iColumn].zName)
));
#ifndef SQLITE_OMIT_FLOATING_POINT
@@ -100513,6 +100849,20 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTa
if( pA->op!=TK_COLUMN && pA->op!=TK_AGG_COLUMN && pA->u.zToken ){
if( pA->op==TK_FUNCTION ){
if( sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2;
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ /* Justification for the assert():
+ ** window functions have p->op==TK_FUNCTION but aggregate functions
+ ** have p->op==TK_AGG_FUNCTION. So any comparison between an aggregate
+ ** function and a window function should have failed before reaching
+ ** this point. And, it is not possible to have a window function and
+ ** a scalar function with the same name and number of arguments. So
+ ** if we reach this point, either A and B both window functions or
+ ** neither are a window functions. */
+ assert( ExprHasProperty(pA,EP_WinFunc)==ExprHasProperty(pB,EP_WinFunc) );
+ if( ExprHasProperty(pA,EP_WinFunc) ){
+ if( sqlite3WindowCompare(pParse,pA->y.pWin,pB->y.pWin)!=0 ) return 2;
+ }
+#endif
}else if( pA->op==TK_COLLATE ){
if( sqlite3_stricmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2;
}else if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){
@@ -100532,21 +100882,6 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTa
if( pA->iTable!=pB->iTable
&& (pA->iTable!=iTab || NEVER(pB->iTable>=0)) ) return 2;
}
-#ifndef SQLITE_OMIT_WINDOWFUNC
- /* Justification for the assert():
- ** window functions have p->op==TK_FUNCTION but aggregate functions
- ** have p->op==TK_AGG_FUNCTION. So any comparison between an aggregate
- ** function and a window function should have failed before reaching
- ** this point. And, it is not possible to have a window function and
- ** a scalar function with the same name and number of arguments. So
- ** if we reach this point, either A and B both window functions or
- ** neither are a window functions. */
- assert( (pA->pWin==0)==(pB->pWin==0) );
-
- if( pA->pWin!=0 ){
- if( sqlite3WindowCompare(pParse,pA->pWin,pB->pWin)!=0 ) return 2;
- }
-#endif
}
return 0;
}
@@ -100687,8 +101022,8 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
testcase( pExpr->op==TK_LE );
testcase( pExpr->op==TK_GT );
testcase( pExpr->op==TK_GE );
- if( (pExpr->pLeft->op==TK_COLUMN && IsVirtual(pExpr->pLeft->pTab))
- || (pExpr->pRight->op==TK_COLUMN && IsVirtual(pExpr->pRight->pTab))
+ if( (pExpr->pLeft->op==TK_COLUMN && IsVirtual(pExpr->pLeft->y.pTab))
+ || (pExpr->pRight->op==TK_COLUMN && IsVirtual(pExpr->pRight->y.pTab))
){
return WRC_Prune;
}
@@ -100919,7 +101254,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
&& (k = addAggInfoColumn(pParse->db, pAggInfo))>=0
){
pCol = &pAggInfo->aCol[k];
- pCol->pTab = pExpr->pTab;
+ pCol->pTab = pExpr->y.pTab;
pCol->iTable = pExpr->iTable;
pCol->iColumn = pExpr->iColumn;
pCol->iMem = ++pParse->nMem;
@@ -101802,10 +102137,16 @@ static void renameTokenCheckAll(Parse *pParse, void *pPtr){
#endif
/*
-** Add a new RenameToken object mapping parse tree element pPtr into
-** token *pToken to the Parse object currently under construction.
+** Remember that the parser tree element pPtr was created using
+** the token pToken.
+**
+** In other words, construct a new RenameToken object and add it
+** to the list of RenameToken objects currently being built up
+** in pParse->pRename.
**
-** Return a copy of pPtr.
+** The pPtr argument is returned so that this routine can be used
+** with tail recursion in tokenExpr() routine, for a small performance
+** improvement.
*/
SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){
RenameToken *pNew;
@@ -101938,7 +102279,7 @@ static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){
renameTokenFind(pWalker->pParse, p, (void*)pExpr);
}else if( pExpr->op==TK_COLUMN
&& pExpr->iColumn==p->iCol
- && p->pTab==pExpr->pTab
+ && p->pTab==pExpr->y.pTab
){
renameTokenFind(pWalker->pParse, p, (void*)pExpr);
}
@@ -102196,9 +102537,14 @@ static int renameResolveTrigger(Parse *pParse, const char *zDb){
db->aDb[sqlite3SchemaToIndex(db, pNew->pTabSchema)].zDbSName
);
pParse->eTriggerOp = pNew->op;
+ /* ALWAYS() because if the table of the trigger does not exist, the
+ ** error would have been hit before this point */
+ if( ALWAYS(pParse->pTriggerTab) ){
+ rc = sqlite3ViewGetColumnNames(pParse, pParse->pTriggerTab);
+ }
/* Resolve symbols in WHEN clause */
- if( pNew->pWhen ){
+ if( rc==SQLITE_OK && pNew->pWhen ){
rc = sqlite3ResolveExprNames(&sNC, pNew->pWhen);
}
@@ -102312,15 +102658,8 @@ static void renameParseCleanup(Parse *pParse){
** into zNew. The name should be quoted if bQuote is true.
**
** This function is used internally by the ALTER TABLE RENAME COLUMN command.
-** Though accessible to application code, it is not intended for use by
-** applications. The existance of this function, and the way it works,
-** is subject to change without notice.
-**
-** If any of the parameters are out-of-bounds, then simply return NULL.
-** An out-of-bounds parameter can only occur when the application calls
-** this function directly. The parameters will always be well-formed when
-** this routine is invoked by the bytecode for a legitimate ALTER TABLE
-** statement.
+** It is only accessible to SQL created using sqlite3NestedParse(). It is
+** not reachable from ordinary SQL passed into sqlite3_prepare().
*/
static void renameColumnFunc(
sqlite3_context *context,
@@ -102476,8 +102815,8 @@ renameColumnFunc_done:
*/
static int renameTableExprCb(Walker *pWalker, Expr *pExpr){
RenameCtx *p = pWalker->u.pRename;
- if( pExpr->op==TK_COLUMN && p->pTab==pExpr->pTab ){
- renameTokenFind(pWalker->pParse, p, (void*)&pExpr->pTab);
+ if( pExpr->op==TK_COLUMN && p->pTab==pExpr->y.pTab ){
+ renameTokenFind(pWalker->pParse, p, (void*)&pExpr->y.pTab);
}
return WRC_Continue;
}
@@ -102574,7 +102913,7 @@ static void renameTableFunc(
}else{
/* Modify any FK definitions to point to the new table. */
#ifndef SQLITE_OMIT_FOREIGN_KEY
- if( db->flags & SQLITE_ForeignKeys ){
+ if( isLegacy==0 || (db->flags & SQLITE_ForeignKeys) ){
FKey *pFKey;
for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){
if( sqlite3_stricmp(pFKey->zTo, zOld)==0 ){
@@ -102728,9 +103067,9 @@ static void renameTableTest(
*/
SQLITE_PRIVATE void sqlite3AlterFunctions(void){
static FuncDef aAlterTableFuncs[] = {
- FUNCTION(sqlite_rename_column, 9, 0, 0, renameColumnFunc),
- FUNCTION(sqlite_rename_table, 7, 0, 0, renameTableFunc),
- FUNCTION(sqlite_rename_test, 5, 0, 0, renameTableTest),
+ INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc),
+ INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc),
+ INTERNAL_FUNCTION(sqlite_rename_test, 5, renameTableTest),
};
sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
}
@@ -104779,7 +105118,7 @@ static void attachFunc(
if( pNew->pBt ) sqlite3BtreeClose(pNew->pBt);
pNew->pBt = 0;
pNew->pSchema = 0;
- rc = sqlite3BtreeOpen(pVfs, "x", db, &pNew->pBt, 0, SQLITE_OPEN_MAIN_DB);
+ rc = sqlite3BtreeOpen(pVfs, "x\0", db, &pNew->pBt, 0, SQLITE_OPEN_MAIN_DB);
}else{
/* This is a real ATTACH
**
@@ -105459,6 +105798,7 @@ SQLITE_PRIVATE void sqlite3AuthRead(
int iCol; /* Index of column in table */
assert( pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER );
+ assert( !IN_RENAME_OBJECT || db->xAuth==0 );
if( db->xAuth==0 ) return;
iDb = sqlite3SchemaToIndex(pParse->db, pSchema);
if( iDb<0 ){
@@ -105515,6 +105855,7 @@ SQLITE_PRIVATE int sqlite3AuthCheck(
/* Don't do any authorization checks if the database is initialising
** 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;
}
@@ -105938,17 +106279,15 @@ SQLITE_PRIVATE Table *sqlite3LocateTable(
if( p==0 ){
const char *zMsg = flags & LOCATE_VIEW ? "no such view" : "no such table";
#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( sqlite3FindDbName(db, zDbase)<1 ){
- /* 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. */
- 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) ){
- return pMod->pEpoTab;
- }
+ /* 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. */
+ 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) ){
+ return pMod->pEpoTab;
}
#endif
if( (flags & LOCATE_NOERR)==0 ){
@@ -106128,17 +106467,22 @@ SQLITE_PRIVATE void sqlite3ResetOneSchema(sqlite3 *db, int iDb){
SQLITE_PRIVATE void sqlite3ResetAllSchemasOfConnection(sqlite3 *db){
int i;
sqlite3BtreeEnterAll(db);
- assert( db->nSchemaLock==0 );
for(i=0; i<db->nDb; i++){
Db *pDb = &db->aDb[i];
if( pDb->pSchema ){
- sqlite3SchemaClear(pDb->pSchema);
+ if( db->nSchemaLock==0 ){
+ sqlite3SchemaClear(pDb->pSchema);
+ }else{
+ DbSetProperty(db, i, DB_ResetWanted);
+ }
}
}
db->mDbFlags &= ~(DBFLAG_SchemaChange|DBFLAG_SchemaKnownOk);
sqlite3VtabUnlockList(db);
sqlite3BtreeLeaveAll(db);
- sqlite3CollapseDatabaseArray(db);
+ if( db->nSchemaLock==0 ){
+ sqlite3CollapseDatabaseArray(db);
+ }
}
/*
@@ -106215,6 +106559,12 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){
/* Delete the Table structure itself.
*/
+#ifdef SQLITE_ENABLE_NORMALIZE
+ if( pTable->pColHash ){
+ sqlite3HashClear(pTable->pColHash);
+ sqlite3_free(pTable->pColHash);
+ }
+#endif
sqlite3DeleteColumnNames(db, pTable);
sqlite3DbFree(db, pTable->zName);
sqlite3DbFree(db, pTable->zColAff);
@@ -106374,6 +106724,20 @@ SQLITE_PRIVATE int sqlite3TwoPartName(
}
/*
+** True if PRAGMA writable_schema is ON
+*/
+SQLITE_PRIVATE int sqlite3WritableSchema(sqlite3 *db){
+ testcase( (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))==0 );
+ testcase( (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))==
+ SQLITE_WriteSchema );
+ testcase( (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))==
+ SQLITE_Defensive );
+ testcase( (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))==
+ (SQLITE_WriteSchema|SQLITE_Defensive) );
+ return (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))==SQLITE_WriteSchema;
+}
+
+/*
** This routine is used to check if the UTF-8 string zName is a legal
** unqualified name for a new schema object (table, index, view or
** trigger). All names are legal except those that begin with the string
@@ -106382,7 +106746,7 @@ SQLITE_PRIVATE int sqlite3TwoPartName(
*/
SQLITE_PRIVATE int sqlite3CheckObjectName(Parse *pParse, const char *zName){
if( !pParse->db->init.busy && pParse->nested==0
- && (pParse->db->flags & SQLITE_WriteSchema)==0
+ && sqlite3WritableSchema(pParse->db)==0
&& 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){
sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s", zName);
return SQLITE_ERROR;
@@ -107458,6 +107822,36 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
recomputeColumnsNotIndexed(pPk);
}
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+/*
+** Return true if zName is a shadow table name in the current database
+** connection.
+**
+** zName is temporarily modified while this routine is running, but is
+** restored to its original value prior to this routine returning.
+*/
+static int isShadowTableName(sqlite3 *db, char *zName){
+ char *zTail; /* Pointer to the last "_" in zName */
+ Table *pTab; /* Table that zName is a shadow of */
+ Module *pMod; /* Module for the virtual table */
+
+ zTail = strrchr(zName, '_');
+ if( zTail==0 ) return 0;
+ *zTail = 0;
+ pTab = sqlite3FindTable(db, zName, 0);
+ *zTail = '_';
+ if( pTab==0 ) return 0;
+ if( !IsVirtual(pTab) ) return 0;
+ pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->azModuleArg[0]);
+ if( pMod==0 ) return 0;
+ if( pMod->pModule->iVersion<3 ) return 0;
+ if( pMod->pModule->xShadowName==0 ) return 0;
+ return pMod->pModule->xShadowName(zTail+1);
+}
+#else
+# define isShadowTableName(x,y) 0
+#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
+
/*
** This routine is called to report the final ")" that terminates
** a CREATE TABLE statement.
@@ -107497,6 +107891,10 @@ SQLITE_PRIVATE void sqlite3EndTable(
p = pParse->pNewTable;
if( p==0 ) return;
+ if( pSelect==0 && isShadowTableName(db, p->zName) ){
+ p->tabFlags |= TF_Shadow;
+ }
+
/* If the db->init.busy is 1 it means we are reading the SQL off the
** "sqlite_master" or "sqlite_temp_master" table on the disk.
** So do not write to the disk again. Extract the root page number
@@ -108004,7 +108402,7 @@ SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3 *db, int iDb, int iFrom, int iT
static void destroyRootPage(Parse *pParse, int iTable, int iDb){
Vdbe *v = sqlite3GetVdbe(pParse);
int r1 = sqlite3GetTempReg(pParse);
- assert( iTable>1 );
+ if( iTable<2 ) sqlite3ErrorMsg(pParse, "corrupt schema");
sqlite3VdbeAddOp3(v, OP_Destroy, iTable, r1, iDb);
sqlite3MayAbort(pParse);
#ifndef SQLITE_OMIT_AUTOVACUUM
@@ -110459,6 +110857,21 @@ static FuncDef *functionSearch(
}
return 0;
}
+#ifdef SQLITE_ENABLE_NORMALIZE
+SQLITE_PRIVATE FuncDef *sqlite3FunctionSearchN(
+ int h, /* Hash of the name */
+ const char *zFunc, /* Name of function */
+ int nFunc /* Length of the name */
+){
+ FuncDef *p;
+ for(p=sqlite3BuiltinFunctions.a[h]; p; p=p->u.pHash){
+ if( sqlite3StrNICmp(p->zName, zFunc, nFunc)==0 ){
+ return p;
+ }
+ }
+ return 0;
+}
+#endif /* SQLITE_ENABLE_NORMALIZE */
/*
** Insert a new FuncDef into a FuncDefHash hash table.
@@ -110472,7 +110885,7 @@ SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(
FuncDef *pOther;
const char *zName = aDef[i].zName;
int nName = sqlite3Strlen30(zName);
- int h = (zName[0] + nName) % SQLITE_FUNC_HASH_SZ;
+ int h = SQLITE_FUNC_HASH(zName[0], nName);
assert( zName[0]>='a' && zName[0]<='z' );
pOther = functionSearch(h, zName);
if( pOther ){
@@ -110551,7 +110964,7 @@ SQLITE_PRIVATE FuncDef *sqlite3FindFunction(
*/
if( !createFlag && (pBest==0 || (db->mDbFlags & DBFLAG_PreferBuiltin)!=0) ){
bestScore = 0;
- h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % SQLITE_FUNC_HASH_SZ;
+ h = SQLITE_FUNC_HASH(sqlite3UpperToLower[(u8)zName[0]], nName);
p = functionSearch(h, zName);
while( p ){
int score = matchQuality(p, nArg, enc);
@@ -110699,32 +111112,49 @@ SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
return pTab;
}
+/* Return true if table pTab is read-only.
+**
+** A table is read-only if any of the following are true:
+**
+** 1) It is a virtual table and no implementation of the xUpdate method
+** has been provided
+**
+** 2) It is a system table (i.e. sqlite_master), this call is not
+** part of a nested parse and writable_schema pragma has not
+** been specified
+**
+** 3) The table is a shadow table, the database connection is in
+** defensive mode, and the current sqlite3_prepare()
+** is for a top-level SQL statement.
+*/
+static int tabIsReadOnly(Parse *pParse, Table *pTab){
+ sqlite3 *db;
+ if( IsVirtual(pTab) ){
+ return sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0;
+ }
+ if( (pTab->tabFlags & (TF_Readonly|TF_Shadow))==0 ) return 0;
+ db = pParse->db;
+ if( (pTab->tabFlags & TF_Readonly)!=0 ){
+ return sqlite3WritableSchema(db)==0 && pParse->nested==0;
+ }
+ assert( pTab->tabFlags & TF_Shadow );
+ return (db->flags & SQLITE_Defensive)!=0
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ && db->pVtabCtx==0
+#endif
+ && db->nVdbeExec==0;
+}
+
/*
** Check to make sure the given table is writable. If it is not
** writable, generate an error message and return 1. If it is
** writable return 0;
*/
SQLITE_PRIVATE int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){
- /* A table is not writable under the following circumstances:
- **
- ** 1) It is a virtual table and no implementation of the xUpdate method
- ** has been provided, or
- ** 2) It is a system table (i.e. sqlite_master), this call is not
- ** part of a nested parse and writable_schema pragma has not
- ** been specified.
- **
- ** In either case leave an error message in pParse and return non-zero.
- */
- if( ( IsVirtual(pTab)
- && sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 )
- || ( (pTab->tabFlags & TF_Readonly)!=0
- && (pParse->db->flags & SQLITE_WriteSchema)==0
- && pParse->nested==0 )
- ){
+ if( tabIsReadOnly(pParse, pTab) ){
sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName);
return 1;
}
-
#ifndef SQLITE_OMIT_VIEW
if( !viewOk && pTab->pSelect ){
sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName);
@@ -114128,7 +114558,7 @@ static Expr *exprTableColumn(
){
Expr *pExpr = sqlite3Expr(db, TK_COLUMN, 0);
if( pExpr ){
- pExpr->pTab = pTab;
+ pExpr->y.pTab = pTab;
pExpr->iTable = iCursor;
pExpr->iColumn = iCol;
}
@@ -115204,7 +115634,8 @@ SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
}while( i>=0 && zColAff[i]==SQLITE_AFF_BLOB );
pTab->zColAff = zColAff;
}
- i = sqlite3Strlen30(zColAff);
+ assert( zColAff!=0 );
+ i = sqlite3Strlen30NN(zColAff);
if( i ){
if( iReg ){
sqlite3VdbeAddOp4(v, OP_Affinity, iReg, i, 0, zColAff, i);
@@ -116184,14 +116615,15 @@ insert_cleanup:
#endif
/*
-** Meanings of bits in of pWalker->eCode for checkConstraintUnchanged()
+** Meanings of bits in of pWalker->eCode for
+** sqlite3ExprReferencesUpdatedColumn()
*/
#define CKCNSTRNT_COLUMN 0x01 /* CHECK constraint uses a changing column */
#define CKCNSTRNT_ROWID 0x02 /* CHECK constraint references the ROWID */
-/* This is the Walker callback from checkConstraintUnchanged(). Set
-** bit 0x01 of pWalker->eCode if
-** pWalker->eCode to 0 if this expression node references any of the
+/* This is the Walker callback from sqlite3ExprReferencesUpdatedColumn().
+* Set bit 0x01 of pWalker->eCode if pWalker->eCode to 0 and if this
+** expression node references any of the
** columns that are being modifed by an UPDATE statement.
*/
static int checkConstraintExprNode(Walker *pWalker, Expr *pExpr){
@@ -116213,12 +116645,21 @@ static int checkConstraintExprNode(Walker *pWalker, Expr *pExpr){
** only columns that are modified by the UPDATE are those for which
** aiChng[i]>=0, and also the ROWID is modified if chngRowid is true.
**
-** Return true if CHECK constraint pExpr does not use any of the
+** Return true if CHECK constraint pExpr uses any of the
** changing columns (or the rowid if it is changing). In other words,
-** return true if this CHECK constraint can be skipped when validating
+** return true if this CHECK constraint must be validated for
** the new row in the UPDATE statement.
+**
+** 2018-09-15: pExpr might also be an expression for an index-on-expressions.
+** The operation of this routine is the same - return true if an only if
+** the expression uses one or more of columns identified by the second and
+** third arguments.
*/
-static int checkConstraintUnchanged(Expr *pExpr, int *aiChng, int chngRowid){
+SQLITE_PRIVATE int sqlite3ExprReferencesUpdatedColumn(
+ Expr *pExpr, /* The expression to be checked */
+ int *aiChng, /* aiChng[x]>=0 if column x changed by the UPDATE */
+ int chngRowid /* True if UPDATE changes the rowid */
+){
Walker w;
memset(&w, 0, sizeof(w));
w.eCode = 0;
@@ -116233,7 +116674,7 @@ static int checkConstraintUnchanged(Expr *pExpr, int *aiChng, int chngRowid){
testcase( w.eCode==CKCNSTRNT_COLUMN );
testcase( w.eCode==CKCNSTRNT_ROWID );
testcase( w.eCode==(CKCNSTRNT_ROWID|CKCNSTRNT_COLUMN) );
- return !w.eCode;
+ return w.eCode!=0;
}
/*
@@ -116439,7 +116880,13 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
for(i=0; i<pCheck->nExpr; i++){
int allOk;
Expr *pExpr = pCheck->a[i].pExpr;
- if( aiChng && checkConstraintUnchanged(pExpr, aiChng, pkChng) ) continue;
+ if( aiChng
+ && !sqlite3ExprReferencesUpdatedColumn(pExpr, aiChng, pkChng)
+ ){
+ /* The check constraints do not reference any of the columns being
+ ** updated so there is no point it verifying the check constraint */
+ continue;
+ }
allOk = sqlite3VdbeMakeLabel(v);
sqlite3VdbeVerifyAbortable(v, onError);
sqlite3ExprIfTrue(pParse, pExpr, allOk, SQLITE_JUMPIFNULL);
@@ -117940,12 +118387,15 @@ struct sqlite3_api_routines {
int (*str_errcode)(sqlite3_str*);
int (*str_length)(sqlite3_str*);
char *(*str_value)(sqlite3_str*);
+ /* Version 3.25.0 and later */
int (*create_window_function)(sqlite3*,const char*,int,int,void*,
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*),
void (*xValue)(sqlite3_context*),
void (*xInv)(sqlite3_context*,int,sqlite3_value**),
void(*xDestroy)(void*));
+ /* Version 3.26.0 and later */
+ const char *(*normalized_sql)(sqlite3_stmt*);
};
/*
@@ -118233,6 +118683,8 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_str_value sqlite3_api->str_value
/* Version 3.25.0 and later */
#define sqlite3_create_window_function sqlite3_api->create_window_function
+/* Version 3.26.0 and later */
+#define sqlite3_normalized_sql sqlite3_api->normalized_sql
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
@@ -118321,6 +118773,7 @@ typedef int (*sqlite3_loadext_entry)(
# define sqlite3_declare_vtab 0
# define sqlite3_vtab_config 0
# define sqlite3_vtab_on_conflict 0
+# define sqlite3_vtab_collation 0
#endif
#ifdef SQLITE_OMIT_SHARED_CACHE
@@ -118688,7 +119141,13 @@ static const sqlite3_api_routines sqlite3Apis = {
sqlite3_str_length,
sqlite3_str_value,
/* Version 3.25.0 and later */
- sqlite3_create_window_function
+ sqlite3_create_window_function,
+ /* Version 3.26.0 and later */
+#ifdef SQLITE_ENABLE_NORMALIZE
+ sqlite3_normalized_sql
+#else
+ 0
+#endif
};
/*
@@ -119138,10 +119597,9 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){
#define PragTyp_ACTIVATE_EXTENSIONS 40
#define PragTyp_HEXKEY 41
#define PragTyp_KEY 42
-#define PragTyp_REKEY 43
-#define PragTyp_LOCK_STATUS 44
-#define PragTyp_PARSER_TRACE 45
-#define PragTyp_STATS 46
+#define PragTyp_LOCK_STATUS 43
+#define PragTyp_PARSER_TRACE 44
+#define PragTyp_STATS 45
/* Property flags associated with various pragma. */
#define PragFlg_NeedSchema 0x01 /* Force schema load before running */
@@ -119158,58 +119616,57 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){
** result column is different from the name of the pragma
*/
static const char *const pragCName[] = {
- /* 0 */ "cache_size", /* Used by: default_cache_size */
- /* 1 */ "cid", /* Used by: table_info */
- /* 2 */ "name",
- /* 3 */ "type",
- /* 4 */ "notnull",
- /* 5 */ "dflt_value",
- /* 6 */ "pk",
- /* 7 */ "tbl", /* Used by: stats */
- /* 8 */ "idx",
- /* 9 */ "wdth",
- /* 10 */ "hght",
- /* 11 */ "flgs",
- /* 12 */ "seqno", /* Used by: index_info */
- /* 13 */ "cid",
- /* 14 */ "name",
+ /* 0 */ "id", /* Used by: foreign_key_list */
+ /* 1 */ "seq",
+ /* 2 */ "table",
+ /* 3 */ "from",
+ /* 4 */ "to",
+ /* 5 */ "on_update",
+ /* 6 */ "on_delete",
+ /* 7 */ "match",
+ /* 8 */ "cid", /* Used by: table_xinfo */
+ /* 9 */ "name",
+ /* 10 */ "type",
+ /* 11 */ "notnull",
+ /* 12 */ "dflt_value",
+ /* 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 */ "seq", /* Used by: index_list */
- /* 22 */ "name",
- /* 23 */ "unique",
- /* 24 */ "origin",
- /* 25 */ "partial",
- /* 26 */ "seq", /* Used by: database_list */
+ /* 21 */ "tbl", /* Used by: stats */
+ /* 22 */ "idx",
+ /* 23 */ "wdth",
+ /* 24 */ "hght",
+ /* 25 */ "flgs",
+ /* 26 */ "seq", /* Used by: index_list */
/* 27 */ "name",
- /* 28 */ "file",
- /* 29 */ "name", /* Used by: function_list */
- /* 30 */ "builtin",
- /* 31 */ "name", /* Used by: module_list pragma_list */
- /* 32 */ "seq", /* Used by: collation_list */
- /* 33 */ "name",
- /* 34 */ "id", /* Used by: foreign_key_list */
- /* 35 */ "seq",
- /* 36 */ "table",
- /* 37 */ "from",
- /* 38 */ "to",
- /* 39 */ "on_update",
- /* 40 */ "on_delete",
- /* 41 */ "match",
- /* 42 */ "table", /* Used by: foreign_key_check */
- /* 43 */ "rowid",
- /* 44 */ "parent",
- /* 45 */ "fkid",
- /* 46 */ "busy", /* Used by: wal_checkpoint */
- /* 47 */ "log",
- /* 48 */ "checkpointed",
- /* 49 */ "timeout", /* Used by: busy_timeout */
- /* 50 */ "database", /* Used by: lock_status */
- /* 51 */ "status",
+ /* 28 */ "unique",
+ /* 29 */ "origin",
+ /* 30 */ "partial",
+ /* 31 */ "table", /* Used by: foreign_key_check */
+ /* 32 */ "rowid",
+ /* 33 */ "parent",
+ /* 34 */ "fkid",
+ /* index_info reuses 15 */
+ /* 35 */ "seq", /* Used by: database_list */
+ /* 36 */ "name",
+ /* 37 */ "file",
+ /* 38 */ "busy", /* Used by: wal_checkpoint */
+ /* 39 */ "log",
+ /* 40 */ "checkpointed",
+ /* 41 */ "name", /* Used by: function_list */
+ /* 42 */ "builtin",
+ /* collation_list reuses 26 */
+ /* 43 */ "database", /* Used by: lock_status */
+ /* 44 */ "status",
+ /* 45 */ "cache_size", /* Used by: default_cache_size */
+ /* module_list pragma_list reuses 9 */
+ /* 46 */ "timeout", /* Used by: busy_timeout */
};
/* Definitions of all built-in pragmas */
@@ -119219,7 +119676,7 @@ typedef struct PragmaName {
u8 mPragFlg; /* Zero or more PragFlg_XXX values */
u8 iPragCName; /* Start of column names in pragCName[] */
u8 nPragCName; /* Num of col names. 0 means use pragma name */
- u32 iArg; /* Extra argument */
+ u64 iArg; /* Extra argument */
} PragmaName;
static const PragmaName aPragmaName[] = {
#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
@@ -119255,7 +119712,7 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "busy_timeout",
/* ePragTyp: */ PragTyp_BUSY_TIMEOUT,
/* ePragFlg: */ PragFlg_Result0,
- /* ColNames: */ 49, 1,
+ /* ColNames: */ 46, 1,
/* iArg: */ 0 },
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
{/* zName: */ "cache_size",
@@ -119292,7 +119749,7 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "collation_list",
/* ePragTyp: */ PragTyp_COLLATION_LIST,
/* ePragFlg: */ PragFlg_Result0,
- /* ColNames: */ 32, 2,
+ /* ColNames: */ 26, 2,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS)
@@ -119327,14 +119784,14 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "database_list",
/* ePragTyp: */ PragTyp_DATABASE_LIST,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0,
- /* ColNames: */ 26, 3,
+ /* ColNames: */ 35, 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: */ 0, 1,
+ /* ColNames: */ 45, 1,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
@@ -119364,14 +119821,14 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "foreign_key_check",
/* ePragTyp: */ PragTyp_FOREIGN_KEY_CHECK,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0,
- /* ColNames: */ 42, 4,
+ /* ColNames: */ 31, 4,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_FOREIGN_KEY)
{/* zName: */ "foreign_key_list",
/* ePragTyp: */ PragTyp_FOREIGN_KEY_LIST,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
- /* ColNames: */ 34, 8,
+ /* ColNames: */ 0, 8,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
@@ -119407,7 +119864,7 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "function_list",
/* ePragTyp: */ PragTyp_FUNCTION_LIST,
/* ePragFlg: */ PragFlg_Result0,
- /* ColNames: */ 29, 2,
+ /* ColNames: */ 41, 2,
/* iArg: */ 0 },
#endif
#endif
@@ -119416,12 +119873,12 @@ static const PragmaName aPragmaName[] = {
/* ePragTyp: */ PragTyp_HEXKEY,
/* ePragFlg: */ 0,
/* ColNames: */ 0, 0,
- /* iArg: */ 0 },
+ /* iArg: */ 2 },
{/* zName: */ "hexrekey",
/* ePragTyp: */ PragTyp_HEXKEY,
/* ePragFlg: */ 0,
/* ColNames: */ 0, 0,
- /* iArg: */ 0 },
+ /* iArg: */ 3 },
#endif
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
#if !defined(SQLITE_OMIT_CHECK)
@@ -119443,12 +119900,12 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "index_info",
/* ePragTyp: */ PragTyp_INDEX_INFO,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
- /* ColNames: */ 12, 3,
+ /* ColNames: */ 15, 3,
/* iArg: */ 0 },
{/* zName: */ "index_list",
/* ePragTyp: */ PragTyp_INDEX_LIST,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
- /* ColNames: */ 21, 5,
+ /* ColNames: */ 26, 5,
/* iArg: */ 0 },
{/* zName: */ "index_xinfo",
/* ePragTyp: */ PragTyp_INDEX_INFO,
@@ -119505,7 +119962,7 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "lock_status",
/* ePragTyp: */ PragTyp_LOCK_STATUS,
/* ePragFlg: */ PragFlg_Result0,
- /* ColNames: */ 50, 2,
+ /* ColNames: */ 43, 2,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
@@ -119531,7 +119988,7 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "module_list",
/* ePragTyp: */ PragTyp_MODULE_LIST,
/* ePragFlg: */ PragFlg_Result0,
- /* ColNames: */ 31, 1,
+ /* ColNames: */ 9, 1,
/* iArg: */ 0 },
#endif
#endif
@@ -119564,7 +120021,7 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "pragma_list",
/* ePragTyp: */ PragTyp_PRAGMA_LIST,
/* ePragFlg: */ PragFlg_Result0,
- /* ColNames: */ 31, 1,
+ /* ColNames: */ 9, 1,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
@@ -119595,10 +120052,10 @@ static const PragmaName aPragmaName[] = {
#endif
#if defined(SQLITE_HAS_CODEC)
{/* zName: */ "rekey",
- /* ePragTyp: */ PragTyp_REKEY,
+ /* ePragTyp: */ PragTyp_KEY,
/* ePragFlg: */ 0,
/* ColNames: */ 0, 0,
- /* iArg: */ 0 },
+ /* iArg: */ 1 },
#endif
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
{/* zName: */ "reverse_unordered_selects",
@@ -119651,7 +120108,7 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "stats",
/* ePragTyp: */ PragTyp_STATS,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq,
- /* ColNames: */ 7, 5,
+ /* ColNames: */ 21, 5,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
@@ -119665,8 +120122,13 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "table_info",
/* ePragTyp: */ PragTyp_TABLE_INFO,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
- /* ColNames: */ 1, 6,
+ /* ColNames: */ 8, 6,
/* iArg: */ 0 },
+ {/* zName: */ "table_xinfo",
+ /* ePragTyp: */ PragTyp_TABLE_INFO,
+ /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
+ /* ColNames: */ 8, 7,
+ /* iArg: */ 1 },
#endif
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
{/* zName: */ "temp_store",
@@ -119680,6 +120142,18 @@ static const PragmaName aPragmaName[] = {
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
#endif
+#if defined(SQLITE_HAS_CODEC)
+ {/* zName: */ "textkey",
+ /* ePragTyp: */ PragTyp_KEY,
+ /* ePragFlg: */ 0,
+ /* ColNames: */ 0, 0,
+ /* iArg: */ 4 },
+ {/* zName: */ "textrekey",
+ /* ePragTyp: */ PragTyp_KEY,
+ /* ePragFlg: */ 0,
+ /* ColNames: */ 0, 0,
+ /* iArg: */ 5 },
+#endif
{/* zName: */ "threads",
/* ePragTyp: */ PragTyp_THREADS,
/* ePragFlg: */ PragFlg_Result0,
@@ -119730,7 +120204,7 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "wal_checkpoint",
/* ePragTyp: */ PragTyp_WAL_CHECKPOINT,
/* ePragFlg: */ PragFlg_NeedSchema,
- /* ColNames: */ 46, 3,
+ /* ColNames: */ 38, 3,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
@@ -119738,10 +120212,10 @@ static const PragmaName aPragmaName[] = {
/* ePragTyp: */ PragTyp_FLAG,
/* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
/* ColNames: */ 0, 0,
- /* iArg: */ SQLITE_WriteSchema },
+ /* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError },
#endif
};
-/* Number of pragmas: 61 on by default, 78 total. */
+/* Number of pragmas: 62 on by default, 81 total. */
/************** End of pragma.h **********************************************/
/************** Continuing where we left off in pragma.c *********************/
@@ -120753,7 +121227,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
setPragmaResultColumnNames(v, pPragma);
returnSingleInt(v, (db->flags & pPragma->iArg)!=0 );
}else{
- int mask = pPragma->iArg; /* Mask of bits to set or clear. */
+ u64 mask = pPragma->iArg; /* Mask of bits to set or clear. */
if( db->autoCommit==0 ){
/* Foreign key support may not be enabled or disabled while not
** in auto-commit mode. */
@@ -120802,15 +121276,17 @@ SQLITE_PRIVATE void sqlite3Pragma(
Table *pTab;
pTab = sqlite3LocateTable(pParse, LOCATE_NOERR, zRight, zDb);
if( pTab ){
+ int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
int i, k;
int nHidden = 0;
Column *pCol;
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
- pParse->nMem = 6;
- sqlite3CodeVerifySchema(pParse, iDb);
+ pParse->nMem = 7;
+ sqlite3CodeVerifySchema(pParse, iTabDb);
sqlite3ViewGetColumnNames(pParse, pTab);
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
- if( IsHiddenColumn(pCol) ){
+ int isHidden = IsHiddenColumn(pCol);
+ if( isHidden && pPragma->iArg==0 ){
nHidden++;
continue;
}
@@ -120822,13 +121298,14 @@ SQLITE_PRIVATE void sqlite3Pragma(
for(k=1; k<=pTab->nCol && pPk->aiColumn[k-1]!=i; k++){}
}
assert( pCol->pDflt==0 || pCol->pDflt->op==TK_SPAN );
- sqlite3VdbeMultiLoad(v, 1, "issisi",
+ sqlite3VdbeMultiLoad(v, 1, pPragma->iArg ? "issisii" : "issisi",
i-nHidden,
pCol->zName,
sqlite3ColumnType(pCol,""),
pCol->notNull ? 1 : 0,
pCol->pDflt ? pCol->pDflt->u.zToken : 0,
- k);
+ k,
+ isHidden);
}
}
}
@@ -120866,6 +121343,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
Table *pTab;
pIdx = sqlite3FindIndex(db, zRight, zDb);
if( pIdx ){
+ int iIdxDb = sqlite3SchemaToIndex(db, pIdx->pSchema);
int i;
int mx;
if( pPragma->iArg ){
@@ -120878,7 +121356,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
pParse->nMem = 3;
}
pTab = pIdx->pTable;
- sqlite3CodeVerifySchema(pParse, iDb);
+ sqlite3CodeVerifySchema(pParse, iIdxDb);
assert( pParse->nMem<=pPragma->nPragCName );
for(i=0; i<mx; i++){
i16 cnum = pIdx->aiColumn[i];
@@ -120902,8 +121380,9 @@ SQLITE_PRIVATE void sqlite3Pragma(
int i;
pTab = sqlite3FindTable(db, zRight, zDb);
if( pTab ){
+ int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
pParse->nMem = 5;
- sqlite3CodeVerifySchema(pParse, iDb);
+ sqlite3CodeVerifySchema(pParse, iTabDb);
for(pIdx=pTab->pIndex, i=0; pIdx; pIdx=pIdx->pNext, i++){
const char *azOrigin[] = { "c", "u", "pk" };
sqlite3VdbeMultiLoad(v, 1, "isisi",
@@ -120950,6 +121429,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
pParse->nMem = 2;
for(i=0; i<SQLITE_FUNC_HASH_SZ; i++){
for(p=sqlite3BuiltinFunctions.a[i]; p; p=p->u.pHash ){
+ if( p->funcFlags & SQLITE_FUNC_INTERNAL ) continue;
sqlite3VdbeMultiLoad(v, 1, "si", p->zName, 1);
}
}
@@ -120991,9 +121471,10 @@ SQLITE_PRIVATE void sqlite3Pragma(
if( pTab ){
pFK = pTab->pFKey;
if( pFK ){
+ int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
int i = 0;
pParse->nMem = 8;
- sqlite3CodeVerifySchema(pParse, iDb);
+ sqlite3CodeVerifySchema(pParse, iTabDb);
while(pFK){
int j;
for(j=0; j<pFK->nCol; j++){
@@ -121038,9 +121519,9 @@ SQLITE_PRIVATE void sqlite3Pragma(
pParse->nMem += 4;
regKey = ++pParse->nMem;
regRow = ++pParse->nMem;
- sqlite3CodeVerifySchema(pParse, iDb);
k = sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash);
while( k ){
+ int iTabDb;
if( zRight ){
pTab = sqlite3LocateTable(pParse, 0, zRight, zDb);
k = 0;
@@ -121049,21 +121530,23 @@ SQLITE_PRIVATE void sqlite3Pragma(
k = sqliteHashNext(k);
}
if( pTab==0 || pTab->pFKey==0 ) continue;
- sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
+ iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+ sqlite3CodeVerifySchema(pParse, iTabDb);
+ sqlite3TableLock(pParse, iTabDb, pTab->tnum, 0, pTab->zName);
if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow;
- sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead);
+ sqlite3OpenTable(pParse, 0, iTabDb, pTab, OP_OpenRead);
sqlite3VdbeLoadString(v, regResult, pTab->zName);
for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){
pParent = sqlite3FindTable(db, pFK->zTo, zDb);
if( pParent==0 ) continue;
pIdx = 0;
- sqlite3TableLock(pParse, iDb, pParent->tnum, 0, pParent->zName);
+ sqlite3TableLock(pParse, iTabDb, pParent->tnum, 0, pParent->zName);
x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, 0);
if( x==0 ){
if( pIdx==0 ){
- sqlite3OpenTable(pParse, i, iDb, pParent, OP_OpenRead);
+ sqlite3OpenTable(pParse, i, iTabDb, pParent, OP_OpenRead);
}else{
- sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iDb);
+ sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iTabDb);
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
}
}else{
@@ -121832,12 +122315,24 @@ SQLITE_PRIVATE void sqlite3Pragma(
#endif
#ifdef SQLITE_HAS_CODEC
+ /* Pragma iArg
+ ** ---------- ------
+ ** key 0
+ ** rekey 1
+ ** hexkey 2
+ ** hexrekey 3
+ ** textkey 4
+ ** textrekey 5
+ */
case PragTyp_KEY: {
- if( zRight ) sqlite3_key_v2(db, zDb, zRight, sqlite3Strlen30(zRight));
- break;
- }
- case PragTyp_REKEY: {
- if( zRight ) sqlite3_rekey_v2(db, zDb, zRight, sqlite3Strlen30(zRight));
+ if( zRight ){
+ int n = pPragma->iArg<4 ? sqlite3Strlen30(zRight) : -1;
+ if( (pPragma->iArg & 1)==0 ){
+ sqlite3_key_v2(db, zDb, zRight, n);
+ }else{
+ sqlite3_rekey_v2(db, zDb, zRight, n);
+ }
+ }
break;
}
case PragTyp_HEXKEY: {
@@ -121849,7 +122344,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
iByte = (iByte<<4) + sqlite3HexToInt(zRight[i]);
if( (i&1)!=0 ) zKey[i/2] = iByte;
}
- if( (zLeft[3] & 0xf)==0xb ){
+ if( (pPragma->iArg & 1)==0 ){
sqlite3_key_v2(db, zDb, zKey, i/2);
}else{
sqlite3_rekey_v2(db, zDb, zKey, i/2);
@@ -122179,7 +122674,8 @@ static const sqlite3_module pragmaVtabModule = {
0, /* xRename - rename the table */
0, /* xSavepoint */
0, /* xRelease */
- 0 /* xRollbackTo */
+ 0, /* xRollbackTo */
+ 0 /* xShadowName */
};
/*
@@ -122532,8 +123028,8 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl
rc = SQLITE_NOMEM_BKPT;
sqlite3ResetAllSchemasOfConnection(db);
}
- if( rc==SQLITE_OK || (db->flags&SQLITE_WriteSchema)){
- /* Black magic: If the SQLITE_WriteSchema flag is set, then consider
+ 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
@@ -122914,6 +123410,294 @@ static int sqlite3LockAndPrepare(
return rc;
}
+#ifdef SQLITE_ENABLE_NORMALIZE
+/*
+** Checks if the specified token is a table, column, or function name,
+** based on the databases associated with the statement being prepared.
+** If the function fails, zero is returned and pRc is filled with the
+** error code.
+*/
+static int shouldTreatAsIdentifier(
+ sqlite3 *db, /* Database handle. */
+ const char *zToken, /* Pointer to start of token to be checked */
+ int nToken, /* Length of token to be checked */
+ int *pRc /* Pointer to error code upon failure */
+){
+ int bFound = 0; /* Non-zero if token is an identifier name. */
+ int i, j; /* Database and column loop indexes. */
+ Schema *pSchema; /* Schema for current database. */
+ Hash *pHash; /* Hash table of tables for current database. */
+ HashElem *e; /* Hash element for hash table iteration. */
+ Table *pTab; /* Database table for columns being checked. */
+
+ if( sqlite3IsRowidN(zToken, nToken) ){
+ return 1;
+ }
+ if( nToken>0 ){
+ int hash = SQLITE_FUNC_HASH(sqlite3UpperToLower[(u8)zToken[0]], nToken);
+ if( sqlite3FunctionSearchN(hash, zToken, nToken) ) return 1;
+ }
+ assert( db!=0 );
+ sqlite3_mutex_enter(db->mutex);
+ sqlite3BtreeEnterAll(db);
+ for(i=0; i<db->nDb; i++){
+ pHash = &db->aFunc;
+ if( sqlite3HashFindN(pHash, zToken, nToken) ){
+ bFound = 1;
+ break;
+ }
+ pSchema = db->aDb[i].pSchema;
+ if( pSchema==0 ) continue;
+ pHash = &pSchema->tblHash;
+ if( sqlite3HashFindN(pHash, zToken, nToken) ){
+ bFound = 1;
+ break;
+ }
+ for(e=sqliteHashFirst(pHash); e; e=sqliteHashNext(e)){
+ pTab = sqliteHashData(e);
+ if( pTab==0 ) continue;
+ pHash = pTab->pColHash;
+ if( pHash==0 ){
+ pTab->pColHash = pHash = sqlite3_malloc(sizeof(Hash));
+ if( pHash ){
+ sqlite3HashInit(pHash);
+ for(j=0; j<pTab->nCol; j++){
+ Column *pCol = &pTab->aCol[j];
+ sqlite3HashInsert(pHash, pCol->zName, pCol);
+ }
+ }else{
+ *pRc = SQLITE_NOMEM_BKPT;
+ bFound = 0;
+ goto done;
+ }
+ }
+ if( pHash && sqlite3HashFindN(pHash, zToken, nToken) ){
+ bFound = 1;
+ goto done;
+ }
+ }
+ }
+done:
+ sqlite3BtreeLeaveAll(db);
+ sqlite3_mutex_leave(db->mutex);
+ return bFound;
+}
+
+/*
+** Attempt to estimate the final output buffer size needed for the fully
+** normalized version of the specified SQL string. This should take into
+** account any potential expansion that could occur (e.g. via IN clauses
+** being expanded, etc). This size returned is the total number of bytes
+** including the NUL terminator.
+*/
+static int estimateNormalizedSize(
+ const char *zSql, /* The original SQL string */
+ int nSql, /* Length of original SQL string */
+ u8 prepFlags /* The flags passed to sqlite3_prepare_v3() */
+){
+ int nOut = nSql + 4;
+ const char *z = zSql;
+ while( nOut<nSql*5 ){
+ while( z[0]!=0 && z[0]!='I' && z[0]!='i' ){ z++; }
+ if( z[0]==0 ) break;
+ z++;
+ if( z[0]!='N' && z[0]!='n' ) break;
+ z++;
+ while( sqlite3Isspace(z[0]) ){ z++; }
+ if( z[0]!='(' ) break;
+ z++;
+ nOut += 5; /* ?,?,? */
+ }
+ return nOut;
+}
+
+/*
+** Copy the current token into the output buffer while dealing with quoted
+** identifiers. By default, all letters will be converted into lowercase.
+** If the bUpper flag is set, uppercase will be used. The piOut argument
+** will be used to update the target index into the output string.
+*/
+static void copyNormalizedToken(
+ const char *zSql, /* The original SQL string */
+ int iIn, /* Current index into the original SQL string */
+ int nToken, /* Number of bytes in the current token */
+ int tokenFlags, /* Flags returned by the tokenizer */
+ char *zOut, /* The output string */
+ int *piOut /* Pointer to target index into the output string */
+){
+ int bQuoted = tokenFlags & SQLITE_TOKEN_QUOTED;
+ int bKeyword = tokenFlags & SQLITE_TOKEN_KEYWORD;
+ int j = *piOut, k = 0;
+ for(; k<nToken; k++){
+ if( bQuoted ){
+ if( k==0 && iIn>0 ){
+ zOut[j++] = '"';
+ continue;
+ }else if( k==nToken-1 ){
+ zOut[j++] = '"';
+ continue;
+ }
+ }
+ if( bKeyword ){
+ zOut[j++] = sqlite3Toupper(zSql[iIn+k]);
+ }else{
+ zOut[j++] = sqlite3Tolower(zSql[iIn+k]);
+ }
+ }
+ *piOut = j;
+}
+
+/*
+** Perform normalization of the SQL contained in the prepared statement and
+** store the result in the zNormSql field. The schema for the associated
+** databases are consulted while performing the normalization in order to
+** determine if a token appears to be an identifier. All identifiers are
+** left intact in the normalized SQL and all literals are replaced with a
+** single '?'.
+*/
+SQLITE_PRIVATE void sqlite3Normalize(
+ Vdbe *pVdbe, /* VM being reprepared */
+ const char *zSql, /* The original SQL string */
+ int nSql, /* Size of the input string in bytes */
+ u8 prepFlags /* The flags passed to sqlite3_prepare_v3() */
+){
+ sqlite3 *db; /* Database handle. */
+ char *z; /* The output string */
+ int nZ; /* Size of the output string in bytes */
+ int i; /* Next character to read from zSql[] */
+ int j; /* Next character to fill in on z[] */
+ int tokenType = 0; /* Type of the next token */
+ int prevTokenType = 0; /* Type of the previous token, except spaces */
+ int n; /* Size of the next token */
+ int nParen = 0; /* Nesting level of parenthesis */
+ Hash inHash; /* Table of parenthesis levels to output index. */
+
+ db = sqlite3VdbeDb(pVdbe);
+ assert( db!=0 );
+ assert( pVdbe->zNormSql==0 );
+ if( zSql==0 ) return;
+ nZ = estimateNormalizedSize(zSql, nSql, prepFlags);
+ z = sqlite3DbMallocRawNN(db, nZ);
+ if( z==0 ) return;
+ sqlite3HashInit(&inHash);
+ for(i=j=0; i<nSql && zSql[i]; i+=n){
+ int flags = 0;
+ if( tokenType!=TK_SPACE ) prevTokenType = tokenType;
+ n = sqlite3GetTokenNormalized((unsigned char*)zSql+i, &tokenType, &flags);
+ switch( tokenType ){
+ case TK_SPACE: {
+ break;
+ }
+ case TK_ILLEGAL: {
+ sqlite3DbFree(db, z);
+ sqlite3HashClear(&inHash);
+ return;
+ }
+ case TK_STRING:
+ case TK_INTEGER:
+ case TK_FLOAT:
+ case TK_VARIABLE:
+ case TK_BLOB: {
+ z[j++] = '?';
+ break;
+ }
+ case TK_LP:
+ case TK_RP: {
+ if( tokenType==TK_LP ){
+ nParen++;
+ if( prevTokenType==TK_IN ){
+ assert( nParen<nSql );
+ sqlite3HashInsert(&inHash, zSql+nParen, SQLITE_INT_TO_PTR(j));
+ }
+ }else{
+ int jj;
+ assert( nParen<nSql );
+ jj = SQLITE_PTR_TO_INT(sqlite3HashFind(&inHash, zSql+nParen));
+ if( jj>0 ){
+ sqlite3HashInsert(&inHash, zSql+nParen, 0);
+ assert( jj+6<nZ );
+ memcpy(z+jj+1, "?,?,?", 5);
+ j = jj+6;
+ assert( nZ-1-j>=0 );
+ assert( nZ-1-j<nZ );
+ memset(z+j, 0, nZ-1-j);
+ }
+ nParen--;
+ }
+ assert( nParen>=0 );
+ /* Fall through */
+ }
+ case TK_MINUS:
+ case TK_SEMI:
+ case TK_PLUS:
+ case TK_STAR:
+ case TK_SLASH:
+ case TK_REM:
+ case TK_EQ:
+ case TK_LE:
+ case TK_NE:
+ case TK_LSHIFT:
+ case TK_LT:
+ case TK_RSHIFT:
+ case TK_GT:
+ case TK_GE:
+ case TK_BITOR:
+ case TK_CONCAT:
+ case TK_COMMA:
+ case TK_BITAND:
+ case TK_BITNOT:
+ case TK_DOT:
+ case TK_IN:
+ case TK_IS:
+ case TK_NOT:
+ case TK_NULL:
+ case TK_ID: {
+ if( tokenType==TK_NULL ){
+ if( prevTokenType==TK_IS || prevTokenType==TK_NOT ){
+ /* NULL is a keyword in this case, not a literal value */
+ }else{
+ /* Here the NULL is a literal value */
+ z[j++] = '?';
+ break;
+ }
+ }
+ if( j>0 && sqlite3IsIdChar(z[j-1]) && sqlite3IsIdChar(zSql[i]) ){
+ z[j++] = ' ';
+ }
+ if( tokenType==TK_ID ){
+ int i2 = i, n2 = n, rc = SQLITE_OK;
+ if( nParen>0 ){
+ assert( nParen<nSql );
+ sqlite3HashInsert(&inHash, zSql+nParen, 0);
+ }
+ if( flags&SQLITE_TOKEN_QUOTED ){ i2++; n2-=2; }
+ if( shouldTreatAsIdentifier(db, zSql+i2, n2, &rc)==0 ){
+ if( rc!=SQLITE_OK ){
+ sqlite3DbFree(db, z);
+ sqlite3HashClear(&inHash);
+ return;
+ }
+ if( sqlite3_keyword_check(zSql+i2, n2)==0 ){
+ z[j++] = '?';
+ break;
+ }
+ }
+ }
+ copyNormalizedToken(zSql, i, n, flags, z, &j);
+ break;
+ }
+ }
+ }
+ assert( j<nZ && "one" );
+ while( j>0 && z[j-1]==' ' ){ j--; }
+ if( j>0 && z[j-1]!=';' ){ z[j++] = ';'; }
+ z[j] = 0;
+ assert( j<nZ && "two" );
+ pVdbe->zNormSql = z;
+ sqlite3HashClear(&inHash);
+}
+#endif /* SQLITE_ENABLE_NORMALIZE */
+
/*
** Rerun the compilation of a statement after a schema change.
**
@@ -123926,7 +124710,7 @@ static void selectExprDefer(
struct ExprList_item *pItem = &pEList->a[i];
if( pItem->u.x.iOrderByCol==0 ){
Expr *pExpr = pItem->pExpr;
- Table *pTab = pExpr->pTab;
+ Table *pTab = pExpr->y.pTab;
if( pExpr->op==TK_COLUMN && pExpr->iColumn>=0 && pTab && !IsVirtual(pTab)
&& (pTab->aCol[pExpr->iColumn].colFlags & COLFLAG_SORTERREF)
){
@@ -123949,12 +124733,12 @@ static void selectExprDefer(
Expr *pNew = sqlite3PExpr(pParse, TK_COLUMN, 0, 0);
if( pNew ){
pNew->iTable = pExpr->iTable;
- pNew->pTab = pExpr->pTab;
+ pNew->y.pTab = pExpr->y.pTab;
pNew->iColumn = pPk ? pPk->aiColumn[k] : -1;
pExtra = sqlite3ExprListAppend(pParse, pExtra, pNew);
}
}
- pSort->aDefer[nDefer].pTab = pExpr->pTab;
+ pSort->aDefer[nDefer].pTab = pExpr->y.pTab;
pSort->aDefer[nDefer].iCsr = pExpr->iTable;
pSort->aDefer[nDefer].nKey = nKey;
nDefer++;
@@ -124803,7 +125587,7 @@ static const char *columnTypeImpl(
break;
}
- assert( pTab && pExpr->pTab==pTab );
+ assert( pTab && 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
@@ -124988,7 +125772,7 @@ static void generateColumnNames(
assert( p!=0 );
assert( p->op!=TK_AGG_COLUMN ); /* Agg processing has not run yet */
- assert( p->op!=TK_COLUMN || p->pTab!=0 ); /* Covering idx not yet coded */
+ assert( p->op!=TK_COLUMN || p->y.pTab!=0 ); /* Covering idx not yet coded */
if( pEList->a[i].zName ){
/* An AS clause always takes first priority */
char *zName = pEList->a[i].zName;
@@ -124996,7 +125780,7 @@ static void generateColumnNames(
}else if( srcName && p->op==TK_COLUMN ){
char *zCol;
int iCol = p->iColumn;
- pTab = p->pTab;
+ pTab = p->y.pTab;
assert( pTab!=0 );
if( iCol<0 ) iCol = pTab->iPKey;
assert( iCol==-1 || (iCol>=0 && iCol<pTab->nCol) );
@@ -125087,7 +125871,7 @@ SQLITE_PRIVATE int sqlite3ColumnsFromExprList(
if( pColExpr->op==TK_COLUMN ){
/* For columns use the column name name */
int iCol = pColExpr->iColumn;
- Table *pTab = pColExpr->pTab;
+ Table *pTab = pColExpr->y.pTab;
assert( pTab!=0 );
if( iCol<0 ) iCol = pTab->iPKey;
zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid";
@@ -125441,6 +126225,13 @@ static void generateWithRecursiveQuery(
Expr *pLimit; /* Saved LIMIT and OFFSET */
int regLimit, regOffset; /* Registers used by LIMIT and OFFSET */
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( p->pWin ){
+ sqlite3ErrorMsg(pParse, "cannot use window functions in recursive queries");
+ return;
+ }
+#endif
+
/* Obtain authorization to do a recursive query */
if( sqlite3AuthCheck(pParse, SQLITE_RECURSIVE, 0, 0, 0) ) return;
@@ -127190,7 +127981,7 @@ static int flattenSubquery(
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
/*
-** A structure to keep track of all of the column values that fixed to
+** A structure to keep track of all of the column values that are fixed to
** a known value due to WHERE clause constraints of the form COLUMN=VALUE.
*/
typedef struct WhereConst WhereConst;
@@ -127202,13 +127993,28 @@ struct WhereConst {
};
/*
-** Add a new entry to the pConst object
+** Add a new entry to the pConst object. Except, do not add duplicate
+** pColumn entires.
*/
static void constInsert(
- WhereConst *pConst,
- Expr *pColumn,
- Expr *pValue
+ WhereConst *pConst, /* The WhereConst into which we are inserting */
+ Expr *pColumn, /* The COLUMN part of the constraint */
+ Expr *pValue /* The VALUE part of the constraint */
){
+ int i;
+ assert( pColumn->op==TK_COLUMN );
+
+ /* 2018-10-25 ticket [cf5ed20f]
+ ** Make sure the same pColumn is not inserted more than once */
+ for(i=0; i<pConst->nConst; i++){
+ const Expr *pExpr = pConst->apExpr[i*2];
+ assert( pExpr->op==TK_COLUMN );
+ if( pExpr->iTable==pColumn->iTable
+ && pExpr->iColumn==pColumn->iColumn
+ ){
+ return; /* Already present. Return without doing anything. */
+ }
+ }
pConst->nConst++;
pConst->apExpr = sqlite3DbReallocOrFree(pConst->pParse->db, pConst->apExpr,
@@ -131191,6 +131997,57 @@ SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){
}
/*
+** Check to see if column iCol of index pIdx references any of the
+** columns defined by aXRef and chngRowid. Return true if it does
+** and false if not. This is an optimization. False-positives are a
+** performance degradation, but false-negatives can result in a corrupt
+** index and incorrect answers.
+**
+** aXRef[j] will be non-negative if column j of the original table is
+** being updated. chngRowid will be true if the rowid of the table is
+** being updated.
+*/
+static int indexColumnIsBeingUpdated(
+ Index *pIdx, /* The index to check */
+ int iCol, /* Which column of the index to check */
+ int *aXRef, /* aXRef[j]>=0 if column j is being updated */
+ int chngRowid /* true if the rowid is being updated */
+){
+ i16 iIdxCol = pIdx->aiColumn[iCol];
+ assert( iIdxCol!=XN_ROWID ); /* Cannot index rowid */
+ if( iIdxCol>=0 ){
+ return aXRef[iIdxCol]>=0;
+ }
+ assert( iIdxCol==XN_EXPR );
+ assert( pIdx->aColExpr!=0 );
+ assert( pIdx->aColExpr->a[iCol].pExpr!=0 );
+ return sqlite3ExprReferencesUpdatedColumn(pIdx->aColExpr->a[iCol].pExpr,
+ aXRef,chngRowid);
+}
+
+/*
+** Check to see if index pIdx is a partial index whose conditional
+** expression might change values due to an UPDATE. Return true if
+** the index is subject to change and false if the index is guaranteed
+** to be unchanged. This is an optimization. False-positives are a
+** performance degradation, but false-negatives can result in a corrupt
+** index and incorrect answers.
+**
+** aXRef[j] will be non-negative if column j of the original table is
+** being updated. chngRowid will be true if the rowid of the table is
+** being updated.
+*/
+static int indexWhereClauseMightChange(
+ Index *pIdx, /* The index to check */
+ int *aXRef, /* aXRef[j]>=0 if column j is being updated */
+ int chngRowid /* true if the rowid is being updated */
+){
+ if( pIdx->pPartIdxWhere==0 ) return 0;
+ return sqlite3ExprReferencesUpdatedColumn(pIdx->pPartIdxWhere,
+ aXRef, chngRowid);
+}
+
+/*
** Process an UPDATE statement.
**
** UPDATE OR IGNORE table_wxyz SET a=b, c=d WHERE e<5 AND f NOT NULL;
@@ -131413,19 +132270,18 @@ SQLITE_PRIVATE void sqlite3Update(
/* There is one entry in the aRegIdx[] array for each index on the table
** being updated. Fill in aRegIdx[] with a register number that will hold
** the key for accessing each index.
- **
- ** FIXME: Be smarter about omitting indexes that use expressions.
*/
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
int reg;
- if( chngKey || hasFK>1 || pIdx->pPartIdxWhere || pIdx==pPk ){
+ if( chngKey || hasFK>1 || pIdx==pPk
+ || indexWhereClauseMightChange(pIdx,aXRef,chngRowid)
+ ){
reg = ++pParse->nMem;
pParse->nMem += pIdx->nColumn;
}else{
reg = 0;
for(i=0; i<pIdx->nKeyCol; i++){
- i16 iIdxCol = pIdx->aiColumn[i];
- if( iIdxCol<0 || aXRef[iIdxCol]>=0 ){
+ if( indexColumnIsBeingUpdated(pIdx, i, aXRef, chngRowid) ){
reg = ++pParse->nMem;
pParse->nMem += pIdx->nColumn;
if( (onError==OE_Replace)
@@ -131974,7 +132830,7 @@ static void updateVirtualTable(
sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i);
}else{
sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i);
- sqlite3VdbeChangeP5(v, 1); /* Enable sqlite3_vtab_nochange() */
+ sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG);/* Enable sqlite3_vtab_nochange() */
}
}
if( HasRowid(pTab) ){
@@ -132475,7 +133331,8 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
saved_mTrace = db->mTrace;
db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks;
db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum;
- db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder | SQLITE_CountRows);
+ db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder
+ | SQLITE_Defensive | SQLITE_CountRows);
db->mTrace = 0;
zDbMain = db->aDb[iDb].zDbSName;
@@ -133017,7 +133874,6 @@ SQLITE_PRIVATE void sqlite3VtabBeginParse(
Token *pModuleName, /* Name of the module for the virtual table */
int ifNotExists /* No error if the table already exists */
){
- int iDb; /* The database the table is being created in */
Table *pTable; /* The new virtual table */
sqlite3 *db; /* Database connection */
@@ -133027,8 +133883,6 @@ SQLITE_PRIVATE void sqlite3VtabBeginParse(
assert( 0==pTable->pIndex );
db = pParse->db;
- iDb = sqlite3SchemaToIndex(db, pTable->pSchema);
- assert( iDb>=0 );
assert( pTable->nModuleArg==0 );
addModuleArgument(db, pTable, sqlite3NameFromToken(db, pModuleName));
@@ -133048,6 +133902,8 @@ SQLITE_PRIVATE void sqlite3VtabBeginParse(
** The second call, to obtain permission to create the table, is made now.
*/
if( pTable->azModuleArg ){
+ 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);
}
@@ -133742,7 +134598,7 @@ SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(
/* Check to see the left operand is a column in a virtual table */
if( NEVER(pExpr==0) ) return pDef;
if( pExpr->op!=TK_COLUMN ) return pDef;
- pTab = pExpr->pTab;
+ pTab = pExpr->y.pTab;
if( pTab==0 ) return pDef;
if( !IsVirtual(pTab) ) return pDef;
pVtab = sqlite3GetVTable(db, pTab)->pVtab;
@@ -134362,12 +135218,33 @@ struct WhereLoopBuilder {
int nRecValid; /* Number of valid fields currently in pRec */
#endif
unsigned int bldFlags; /* SQLITE_BLDF_* flags */
+ unsigned int iPlanLimit; /* Search limiter */
};
/* Allowed values for WhereLoopBuider.bldFlags */
#define SQLITE_BLDF_INDEXED 0x0001 /* An index is used */
#define SQLITE_BLDF_UNIQUE 0x0002 /* All keys of a UNIQUE index used */
+/* The WhereLoopBuilder.iPlanLimit is used to limit the number of
+** index+constraint combinations the query planner will consider for a
+** particular query. If this parameter is unlimited, then certain
+** pathological queries can spend excess time in the sqlite3WhereBegin()
+** routine. The limit is high enough that is should not impact real-world
+** queries.
+**
+** SQLITE_QUERY_PLANNER_LIMIT is the baseline limit. The limit is
+** increased by SQLITE_QUERY_PLANNER_LIMIT_INCR before each term of the FROM
+** clause is processed, so that every table in a join is guaranteed to be
+** able to propose a some index+constraint combinations even if the initial
+** baseline limit was exhausted by prior tables of the join.
+*/
+#ifndef SQLITE_QUERY_PLANNER_LIMIT
+# define SQLITE_QUERY_PLANNER_LIMIT 20000
+#endif
+#ifndef SQLITE_QUERY_PLANNER_LIMIT_INCR
+# define SQLITE_QUERY_PLANNER_LIMIT_INCR 1000
+#endif
+
/*
** The WHERE clause processing routine has two halves. The
** first part does the start of the WHERE loop and the second
@@ -134929,7 +135806,7 @@ static Expr *removeUnindexableInClauseTerms(
for(i=iEq; i<pLoop->nLTerm; i++){
if( pLoop->aLTerm[i]->pExpr==pX ){
int iField = pLoop->aLTerm[i]->iField - 1;
- assert( pOrigRhs->a[iField].pExpr!=0 );
+ if( pOrigRhs->a[iField].pExpr==0 ) continue; /* Duplicate PK column */
pRhs = sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr);
pOrigRhs->a[iField].pExpr = 0;
assert( pOrigLhs->a[iField].pExpr!=0 );
@@ -135621,7 +136498,7 @@ static int whereIndexExprTransNode(Walker *p, Expr *pExpr){
pExpr->op = TK_COLUMN;
pExpr->iTable = pX->iIdxCur;
pExpr->iColumn = pX->iIdxCol;
- pExpr->pTab = 0;
+ pExpr->y.pTab = 0;
return WRC_Prune;
}else{
return WRC_Continue;
@@ -137021,7 +137898,7 @@ static int isLikeOrGlob(
){
if( pLeft->op!=TK_COLUMN
|| sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT
- || IsVirtual(pLeft->pTab) /* Value might be numeric */
+ || IsVirtual(pLeft->y.pTab) /* Value might be numeric */
){
sqlite3ExprDelete(db, pPrefix);
sqlite3ValueFree(pVal);
@@ -137122,7 +137999,7 @@ static int isAuxiliaryVtabOperator(
** MATCH(expression,vtab_column)
*/
pCol = pList->a[1].pExpr;
- if( pCol->op==TK_COLUMN && IsVirtual(pCol->pTab) ){
+ if( pCol->op==TK_COLUMN && IsVirtual(pCol->y.pTab) ){
for(i=0; i<ArraySize(aOp); i++){
if( sqlite3StrICmp(pExpr->u.zToken, aOp[i].zOp)==0 ){
*peOp2 = aOp[i].eOp2;
@@ -137144,12 +138021,12 @@ static int isAuxiliaryVtabOperator(
** with function names in an arbitrary case.
*/
pCol = pList->a[0].pExpr;
- if( pCol->op==TK_COLUMN && IsVirtual(pCol->pTab) ){
+ if( pCol->op==TK_COLUMN && IsVirtual(pCol->y.pTab) ){
sqlite3_vtab *pVtab;
sqlite3_module *pMod;
void (*xNotUsed)(sqlite3_context*,int,sqlite3_value**);
void *pNotUsed;
- pVtab = sqlite3GetVTable(db, pCol->pTab)->pVtab;
+ pVtab = sqlite3GetVTable(db, pCol->y.pTab)->pVtab;
assert( pVtab!=0 );
assert( pVtab->pModule!=0 );
pMod = (sqlite3_module *)pVtab->pModule;
@@ -137167,10 +138044,10 @@ static int isAuxiliaryVtabOperator(
int res = 0;
Expr *pLeft = pExpr->pLeft;
Expr *pRight = pExpr->pRight;
- if( pLeft->op==TK_COLUMN && IsVirtual(pLeft->pTab) ){
+ if( pLeft->op==TK_COLUMN && IsVirtual(pLeft->y.pTab) ){
res++;
}
- if( pRight && pRight->op==TK_COLUMN && IsVirtual(pRight->pTab) ){
+ if( pRight && pRight->op==TK_COLUMN && IsVirtual(pRight->y.pTab) ){
res++;
SWAP(Expr*, pLeft, pRight);
}
@@ -138122,6 +138999,7 @@ static void exprAnalyze(
if( pExpr->op==TK_NOTNULL
&& pExpr->pLeft->op==TK_COLUMN
&& pExpr->pLeft->iColumn>=0
+ && !ExprHasProperty(pExpr, EP_FromJoin)
&& OptimizationEnabled(db, SQLITE_Stat34)
){
Expr *pNewExpr;
@@ -138313,6 +139191,7 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(
pArgs = pItem->u1.pFuncArg;
if( pArgs==0 ) return;
for(j=k=0; j<pArgs->nExpr; j++){
+ Expr *pRhs;
while( k<pTab->nCol && (pTab->aCol[k].colFlags & COLFLAG_HIDDEN)==0 ){k++;}
if( k>=pTab->nCol ){
sqlite3ErrorMsg(pParse, "too many arguments on %s() - max %d",
@@ -138323,9 +139202,10 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(
if( pColRef==0 ) return;
pColRef->iTable = pItem->iCursor;
pColRef->iColumn = k++;
- pColRef->pTab = pTab;
- pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef,
- sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0));
+ pColRef->y.pTab = pTab;
+ pRhs = sqlite3PExpr(pParse, TK_UPLUS,
+ sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0);
+ pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, pRhs);
whereClauseInsert(pWC, pTerm, TERM_DYNAMIC);
}
}
@@ -139188,7 +140068,6 @@ static void constructAutomaticIndex(
translateColumnToCopy(pParse, addrTop, pLevel->iTabCur,
pTabItem->regResult, 1);
sqlite3VdbeGoto(v, addrTop);
- pTabItem->fg.viaCoroutine = 0;
}else{
sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v);
}
@@ -139366,9 +140245,11 @@ static sqlite3_index_info *allocateIndexInfo(
** method of the virtual table with the sqlite3_index_info object that
** comes in as the 3rd argument to this function.
**
-** If an error occurs, pParse is populated with an error message and a
-** non-zero value is returned. Otherwise, 0 is returned and the output
-** part of the sqlite3_index_info structure is left populated.
+** If an error occurs, pParse is populated with an error message and an
+** appropriate error code is returned. A return of SQLITE_CONSTRAINT from
+** xBestIndex is not considered an error. SQLITE_CONSTRAINT indicates that
+** the current configuration of "unusable" flags in sqlite3_index_info can
+** not result in a valid plan.
**
** Whether or not an error is returned, it is the responsibility of the
** caller to eventually free p->idxStr if p->needToFreeIdxStr indicates
@@ -139382,7 +140263,7 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
rc = pVtab->pModule->xBestIndex(pVtab, p);
TRACE_IDX_OUTPUTS(p);
- if( rc!=SQLITE_OK ){
+ if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){
if( rc==SQLITE_NOMEM ){
sqlite3OomFault(pParse->db);
}else if( !pVtab->zErrMsg ){
@@ -139393,19 +140274,7 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
}
sqlite3_free(pVtab->zErrMsg);
pVtab->zErrMsg = 0;
-
-#if 0
- /* This error is now caught by the caller.
- ** Search for "xBestIndex malfunction" below */
- for(i=0; i<p->nConstraint; i++){
- if( !p->aConstraint[i].usable && p->aConstraintUsage[i].argvIndex>0 ){
- sqlite3ErrorMsg(pParse,
- "table %s: xBestIndex returned an invalid plan", pTab->zName);
- }
- }
-#endif
-
- return pParse->nErr;
+ return rc;
}
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
@@ -140460,6 +141329,14 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
sqlite3 *db = pWInfo->pParse->db;
int rc;
+ /* Stop the search once we hit the query planner search limit */
+ if( pBuilder->iPlanLimit==0 ){
+ WHERETRACE(0xffffffff,("=== query planner search limit reached ===\n"));
+ if( pBuilder->pOrSet ) pBuilder->pOrSet->n = 0;
+ return SQLITE_DONE;
+ }
+ pBuilder->iPlanLimit--;
+
/* If pBuilder->pOrSet is defined, then only keep track of the costs
** and prereqs.
*/
@@ -141470,7 +142347,17 @@ static int whereLoopAddVirtualOne(
/* Invoke the virtual table xBestIndex() method */
rc = vtabBestIndex(pParse, pSrc->pTab, pIdxInfo);
- if( rc ) return rc;
+ if( rc ){
+ if( rc==SQLITE_CONSTRAINT ){
+ /* If the xBestIndex method returns SQLITE_CONSTRAINT, that means
+ ** that the particular combination of parameters provided is unusable.
+ ** Make no entries in the loop table.
+ */
+ WHERETRACE(0xffff, (" ^^^^--- non-viable plan rejected!\n"));
+ return SQLITE_OK;
+ }
+ return rc;
+ }
mxTerm = -1;
assert( pNew->nLSlot>=nConstraint );
@@ -141866,9 +142753,11 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
/* Loop over the tables in the join, from left to right */
pNew = pBuilder->pNew;
whereLoopInit(pNew);
+ pBuilder->iPlanLimit = SQLITE_QUERY_PLANNER_LIMIT;
for(iTab=0, pItem=pTabList->a; pItem<pEnd; iTab++, pItem++){
Bitmask mUnusable = 0;
pNew->iTab = iTab;
+ pBuilder->iPlanLimit += SQLITE_QUERY_PLANNER_LIMIT_INCR;
pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor);
if( ((pItem->fg.jointype|priorJointype) & (JT_LEFT|JT_CROSS))!=0 ){
/* This condition is true when pItem is the FROM clause term on the
@@ -141894,7 +142783,15 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
rc = whereLoopAddOr(pBuilder, mPrereq, mUnusable);
}
mPrior |= pNew->maskSelf;
- if( rc || db->mallocFailed ) break;
+ if( rc || db->mallocFailed ){
+ if( rc==SQLITE_DONE ){
+ /* We hit the query planner search limit set by iPlanLimit */
+ sqlite3_log(SQLITE_WARNING, "abbreviated query algorithm search");
+ rc = SQLITE_OK;
+ }else{
+ break;
+ }
+ }
}
whereLoopClear(db, pNew);
@@ -144276,12 +145173,12 @@ static int selectWindowRewriteExprCb(Walker *pWalker, Expr *pExpr){
switch( pExpr->op ){
case TK_FUNCTION:
- if( pExpr->pWin==0 ){
+ if( !ExprHasProperty(pExpr, EP_WinFunc) ){
break;
}else{
Window *pWin;
for(pWin=p->pWin; pWin; pWin=pWin->pNextWin){
- if( pExpr->pWin==pWin ){
+ if( pExpr->y.pWin==pWin ){
assert( pWin->pOwner==pExpr );
return WRC_Prune;
}
@@ -144398,7 +145295,7 @@ static ExprList *exprListAppendList(
*/
SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
int rc = SQLITE_OK;
- if( p->pWin ){
+ if( p->pWin && p->pPrior==0 ){
Vdbe *v = sqlite3GetVdbe(pParse);
sqlite3 *db = pParse->db;
Select *pSub = 0; /* The subquery */
@@ -144611,11 +145508,13 @@ windowAllocErr:
*/
SQLITE_PRIVATE void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
if( p ){
+ assert( p->op==TK_FUNCTION );
/* This routine is only called for the parser. If pWin was not
** allocated due to an OOM, then the parser would fail before ever
** invoking this routine */
if( ALWAYS(pWin) ){
- p->pWin = pWin;
+ p->y.pWin = pWin;
+ ExprSetProperty(p, EP_WinFunc);
pWin->pOwner = p;
if( p->flags & EP_Distinct ){
sqlite3ErrorMsg(pParse,
@@ -145778,7 +146677,7 @@ static void windowCodeDefaultStep(
*/
SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p){
Window *pNew = 0;
- if( p ){
+ if( ALWAYS(p) ){
pNew = sqlite3DbMallocZero(db, sizeof(Window));
if( pNew ){
pNew->zName = sqlite3DbStrDup(db, p->zName);
@@ -145930,6 +146829,7 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep(
** input grammar file:
*/
/* #include <stdio.h> */
+/* #include <assert.h> */
/************ Begin %include sections from the grammar ************************/
/* #include "sqliteInt.h" */
@@ -146031,13 +146931,10 @@ static void disableLookaside(Parse *pParse){
p->pLeft = p->pRight = 0;
p->x.pList = 0;
p->pAggInfo = 0;
- p->pTab = 0;
+ p->y.pTab = 0;
p->op2 = 0;
p->iTable = 0;
p->iColumn = 0;
-#ifndef SQLITE_OMIT_WINDOWFUNC
- p->pWin = 0;
-#endif
p->u.zToken = (char*)&p[1];
memcpy(p->u.zToken, t.z, t.n);
p->u.zToken[t.n] = 0;
@@ -150229,10 +151126,9 @@ SQLITE_PRIVATE void sqlite3Parser(
yymajor = YYNOCODE;
}else{
while( yypParser->yytos >= yypParser->yystack
- && yymx != YYERRORSYMBOL
&& (yyact = yy_find_reduce_action(
yypParser->yytos->stateno,
- YYERRORSYMBOL)) >= YY_MIN_REDUCE
+ YYERRORSYMBOL)) > YY_MAX_SHIFTREDUCE
){
yy_pop_parser_stack(yypParser);
}
@@ -151199,6 +152095,73 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
return i;
}
+#ifdef SQLITE_ENABLE_NORMALIZE
+/*
+** Return the length (in bytes) of the token that begins at z[0].
+** Store the token type in *tokenType before returning. If flags has
+** SQLITE_TOKEN_NORMALIZE flag enabled, use the identifier token type
+** for keywords. Add SQLITE_TOKEN_QUOTED to flags if the token was
+** actually a quoted identifier. Add SQLITE_TOKEN_KEYWORD to flags
+** if the token was recognized as a keyword; this is useful when the
+** SQLITE_TOKEN_NORMALIZE flag is used, because it enables the caller
+** to differentiate between a keyword being treated as an identifier
+** (for normalization purposes) and an actual identifier.
+*/
+SQLITE_PRIVATE int sqlite3GetTokenNormalized(
+ const unsigned char *z,
+ int *tokenType,
+ int *flags
+){
+ int n;
+ unsigned char iClass = aiClass[*z];
+ if( iClass==CC_KYWD ){
+ int i;
+ for(i=1; aiClass[z[i]]<=CC_KYWD; i++){}
+ if( IdChar(z[i]) ){
+ /* This token started out using characters that can appear in keywords,
+ ** but z[i] is a character not allowed within keywords, so this must
+ ** be an identifier instead */
+ i++;
+ while( IdChar(z[i]) ){ i++; }
+ *tokenType = TK_ID;
+ return i;
+ }
+ *tokenType = TK_ID;
+ n = keywordCode((char*)z, i, tokenType);
+ /* If the token is no longer considered to be an identifier, then it is a
+ ** keyword of some kind. Make the token back into an identifier and then
+ ** set the SQLITE_TOKEN_KEYWORD flag. Several non-identifier tokens are
+ ** used verbatim, including IN, IS, NOT, and NULL. */
+ switch( *tokenType ){
+ case TK_ID: {
+ /* do nothing, handled by caller */
+ break;
+ }
+ case TK_IN:
+ case TK_IS:
+ case TK_NOT:
+ case TK_NULL: {
+ *flags |= SQLITE_TOKEN_KEYWORD;
+ break;
+ }
+ default: {
+ *tokenType = TK_ID;
+ *flags |= SQLITE_TOKEN_KEYWORD;
+ break;
+ }
+ }
+ }else{
+ n = sqlite3GetToken(z, tokenType);
+ /* If the token is considered to be an identifier and the character class
+ ** of the first character is a quote, set the SQLITE_TOKEN_QUOTED flag. */
+ if( *tokenType==TK_ID && (iClass==CC_QUOTE || iClass==CC_QUOTE2) ){
+ *flags |= SQLITE_TOKEN_QUOTED;
+ }
+ }
+ return n;
+}
+#endif /* SQLITE_ENABLE_NORMALIZE */
+
/*
** Run the parser on the given SQL string. The parser structure is
** passed in. An SQLITE_ status code is returned. If an error occurs
@@ -152596,6 +153559,7 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
{ SQLITE_DBCONFIG_ENABLE_QPSG, SQLITE_EnableQPSG },
{ SQLITE_DBCONFIG_TRIGGER_EQP, SQLITE_TriggerEQP },
{ SQLITE_DBCONFIG_RESET_DATABASE, SQLITE_ResetDatabase },
+ { SQLITE_DBCONFIG_DEFENSIVE, SQLITE_Defensive },
};
unsigned int i;
rc = SQLITE_ERROR; /* IMP: R-42790-23372 */
@@ -154823,6 +155787,9 @@ static int openDatabase(
#if defined(SQLITE_ENABLE_QPSG)
| SQLITE_EnableQPSG
#endif
+#if defined(SQLITE_DEFAULT_DEFENSIVE)
+ | SQLITE_Defensive
+#endif
;
sqlite3HashInit(&db->aCollSeq);
#ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -155710,15 +156677,26 @@ SQLITE_API int sqlite3_test_control(int op, ...){
/* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, int onoff);
**
- ** If parameter onoff is non-zero, configure the wrappers so that all
- ** subsequent calls to localtime() and variants fail. If onoff is zero,
- ** undo this setting.
+ ** If parameter onoff is non-zero, subsequent calls to localtime()
+ ** and its variants fail. If onoff is zero, undo this setting.
*/
case SQLITE_TESTCTRL_LOCALTIME_FAULT: {
sqlite3GlobalConfig.bLocaltimeFault = va_arg(ap, int);
break;
}
+ /* sqlite3_test_control(SQLITE_TESTCTRL_INTERNAL_FUNCS, int onoff);
+ **
+ ** If parameter onoff is non-zero, internal-use-only SQL functions
+ ** are visible to ordinary SQL. This is useful for testing but is
+ ** unsafe because invalid parameters to those internal-use-only functions
+ ** can result in crashes or segfaults.
+ */
+ case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS: {
+ sqlite3GlobalConfig.bInternalFunctions = va_arg(ap, int);
+ break;
+ }
+
/* sqlite3_test_control(SQLITE_TESTCTRL_NEVER_CORRUPT, int);
**
** Set or clear a flag that indicates that the database file is always well-
@@ -159161,7 +160139,7 @@ static int fts3ScanInteriorNode(
const char *zCsr = zNode; /* Cursor to iterate through node */
const char *zEnd = &zCsr[nNode];/* End of interior node buffer */
char *zBuffer = 0; /* Buffer to load terms into */
- int nAlloc = 0; /* Size of allocated buffer */
+ 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 */
@@ -159199,14 +160177,14 @@ static int fts3ScanInteriorNode(
zCsr += fts3GetVarint32(zCsr, &nSuffix);
assert( nPrefix>=0 && nSuffix>=0 );
- if( &zCsr[nSuffix]>zEnd ){
+ if( nPrefix>zCsr-zNode || nSuffix>zEnd-zCsr ){
rc = FTS_CORRUPT_VTAB;
goto finish_scan;
}
- if( nPrefix+nSuffix>nAlloc ){
+ if( (i64)nPrefix+nSuffix>nAlloc ){
char *zNew;
- nAlloc = (nPrefix+nSuffix) * 2;
- zNew = (char *)sqlite3_realloc(zBuffer, nAlloc);
+ nAlloc = ((i64)nPrefix+nSuffix) * 2;
+ zNew = (char *)sqlite3_realloc64(zBuffer, nAlloc);
if( !zNew ){
rc = SQLITE_NOMEM;
goto finish_scan;
@@ -161186,8 +162164,23 @@ static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
return SQLITE_OK;
}
+/*
+** Return true if zName is the extension on one of the shadow tables used
+** by this module.
+*/
+static int fts3ShadowName(const char *zName){
+ static const char *azName[] = {
+ "content", "docsize", "segdir", "segments", "stat",
+ };
+ unsigned int i;
+ for(i=0; i<sizeof(azName)/sizeof(azName[0]); i++){
+ if( sqlite3_stricmp(zName, azName[i])==0 ) return 1;
+ }
+ return 0;
+}
+
static const sqlite3_module fts3Module = {
- /* iVersion */ 2,
+ /* iVersion */ 3,
/* xCreate */ fts3CreateMethod,
/* xConnect */ fts3ConnectMethod,
/* xBestIndex */ fts3BestIndexMethod,
@@ -161210,6 +162203,7 @@ static const sqlite3_module fts3Module = {
/* xSavepoint */ fts3SavepointMethod,
/* xRelease */ fts3ReleaseMethod,
/* xRollbackTo */ fts3RollbackToMethod,
+ /* xShadowName */ fts3ShadowName,
};
/*
@@ -161490,6 +162484,7 @@ static int fts3EvalPhraseLoad(
return rc;
}
+#ifndef SQLITE_DISABLE_FTS4_DEFERRED
/*
** This function is called on each phrase after the position lists for
** any deferred tokens have been loaded into memory. It updates the phrases
@@ -161593,6 +162588,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
return SQLITE_OK;
}
+#endif /* SQLITE_DISABLE_FTS4_DEFERRED */
/*
** Maximum number of tokens a phrase may have to be considered for the
@@ -163841,7 +164837,8 @@ SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db){
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
- 0 /* xRollbackTo */
+ 0, /* xRollbackTo */
+ 0 /* xShadowName */
};
int rc; /* Return code */
@@ -167400,7 +168397,8 @@ SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash){
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
- 0 /* xRollbackTo */
+ 0, /* xRollbackTo */
+ 0 /* xShadowName */
};
int rc; /* Return code */
@@ -168788,15 +169786,19 @@ static int fts3SegReaderNext(
** safe (no risk of overread) even if the node data is corrupted. */
pNext += fts3GetVarint32(pNext, &nPrefix);
pNext += fts3GetVarint32(pNext, &nSuffix);
- if( nPrefix<0 || nSuffix<=0
- || &pNext[nSuffix]>&pReader->aNode[pReader->nNode]
+ if( nSuffix<=0
+ || (&pReader->aNode[pReader->nNode] - pNext)<nSuffix
+ || nPrefix>pReader->nTermAlloc
){
return FTS_CORRUPT_VTAB;
}
- if( nPrefix+nSuffix>pReader->nTermAlloc ){
- int nNew = (nPrefix+nSuffix)*2;
- char *zNew = sqlite3_realloc(pReader->zTerm, nNew);
+ /* Both nPrefix and nSuffix were read by fts3GetVarint32() and so are
+ ** between 0 and 0x7FFFFFFF. But the sum of the two may cause integer
+ ** overflow - hence the (i64) casts. */
+ if( (i64)nPrefix+nSuffix>(i64)pReader->nTermAlloc ){
+ i64 nNew = ((i64)nPrefix+nSuffix)*2;
+ char *zNew = sqlite3_realloc64(pReader->zTerm, nNew);
if( !zNew ){
return SQLITE_NOMEM;
}
@@ -168818,7 +169820,7 @@ static int fts3SegReaderNext(
** b-tree node. And that the final byte of the doclist is 0x00. If either
** of these statements is untrue, then the data structure is corrupt.
*/
- if( &pReader->aDoclist[pReader->nDoclist]>&pReader->aNode[pReader->nNode]
+ if( (&pReader->aNode[pReader->nNode] - pReader->aDoclist)<pReader->nDoclist
|| (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1])
){
return FTS_CORRUPT_VTAB;
@@ -171144,6 +172146,9 @@ static int nodeReaderNext(NodeReader *p){
}
p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nSuffix);
+ if( nPrefix>p->iOff || nSuffix>p->nNode-p->iOff ){
+ return SQLITE_CORRUPT_VTAB;
+ }
blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc);
if( rc==SQLITE_OK ){
memcpy(&p->term.a[nPrefix], &p->aNode[p->iOff], nSuffix);
@@ -171151,6 +172156,9 @@ static int nodeReaderNext(NodeReader *p){
p->iOff += nSuffix;
if( p->iChild==0 ){
p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &p->nDoclist);
+ if( (p->nNode-p->iOff)<p->nDoclist ){
+ return SQLITE_CORRUPT_VTAB;
+ }
p->aDoclist = &p->aNode[p->iOff];
p->iOff += p->nDoclist;
}
@@ -171158,7 +172166,6 @@ static int nodeReaderNext(NodeReader *p){
}
assert( p->iOff<=p->nNode );
-
return rc;
}
@@ -177568,6 +178575,9 @@ static int jsonEachConnect(
#define JEACH_PARENT 5
#define JEACH_FULLKEY 6
#define JEACH_PATH 7
+/* The xBestIndex method assumes that the JSON and ROOT columns are
+** the last two columns in the table. Should this ever changes, be
+** sure to update the xBestIndex method. */
#define JEACH_JSON 8
#define JEACH_ROOT 9
@@ -177825,35 +178835,54 @@ static int jsonEachBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
- int i;
- int jsonIdx = -1;
- int rootIdx = -1;
+ int i; /* Loop counter or computed array index */
+ int aIdx[2]; /* Index of constraints for JSON and ROOT */
+ int unusableMask = 0; /* Mask of unusable JSON and ROOT constraints */
+ int idxMask = 0; /* Mask of usable == constraints JSON and ROOT */
const struct sqlite3_index_constraint *pConstraint;
+ /* This implementation assumes that JSON and ROOT are the last two
+ ** columns in the table */
+ assert( JEACH_ROOT == JEACH_JSON+1 );
UNUSED_PARAM(tab);
+ aIdx[0] = aIdx[1] = -1;
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
- if( pConstraint->usable==0 ) continue;
- if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
- switch( pConstraint->iColumn ){
- case JEACH_JSON: jsonIdx = i; break;
- case JEACH_ROOT: rootIdx = i; break;
- default: /* no-op */ break;
- }
- }
- if( jsonIdx<0 ){
+ int iCol;
+ int iMask;
+ if( pConstraint->iColumn < JEACH_JSON ) continue;
+ iCol = pConstraint->iColumn - JEACH_JSON;
+ assert( iCol==0 || iCol==1 );
+ iMask = 1 << iCol;
+ if( pConstraint->usable==0 ){
+ unusableMask |= iMask;
+ }else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
+ aIdx[iCol] = i;
+ idxMask |= iMask;
+ }
+ }
+ if( (unusableMask & ~idxMask)!=0 ){
+ /* If there are any unusable constraints on JSON or ROOT, then reject
+ ** this entire plan */
+ return SQLITE_CONSTRAINT;
+ }
+ if( aIdx[0]<0 ){
+ /* No JSON input. Leave estimatedCost at the huge value that it was
+ ** initialized to to discourage the query planner from selecting this
+ ** plan. */
pIdxInfo->idxNum = 0;
- pIdxInfo->estimatedCost = 1e99;
}else{
pIdxInfo->estimatedCost = 1.0;
- pIdxInfo->aConstraintUsage[jsonIdx].argvIndex = 1;
- pIdxInfo->aConstraintUsage[jsonIdx].omit = 1;
- if( rootIdx<0 ){
- pIdxInfo->idxNum = 1;
+ i = aIdx[0];
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
+ pIdxInfo->aConstraintUsage[i].omit = 1;
+ if( aIdx[1]<0 ){
+ pIdxInfo->idxNum = 1; /* Only JSON supplied. Plan 1 */
}else{
- pIdxInfo->aConstraintUsage[rootIdx].argvIndex = 2;
- pIdxInfo->aConstraintUsage[rootIdx].omit = 1;
- pIdxInfo->idxNum = 3;
+ i = aIdx[1];
+ pIdxInfo->aConstraintUsage[i].argvIndex = 2;
+ pIdxInfo->aConstraintUsage[i].omit = 1;
+ pIdxInfo->idxNum = 3; /* Both JSON and ROOT are supplied. Plan 3 */
}
}
return SQLITE_OK;
@@ -177962,7 +178991,8 @@ static sqlite3_module jsonEachModule = {
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
- 0 /* xRollbackTo */
+ 0, /* xRollbackTo */
+ 0 /* xShadowName */
};
/* The methods of the json_tree virtual table. */
@@ -177989,7 +179019,8 @@ static sqlite3_module jsonTreeModule = {
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
- 0 /* xRollbackTo */
+ 0, /* xRollbackTo */
+ 0 /* xShadowName */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -181419,8 +182450,24 @@ static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){
return rc;
}
+
+/*
+** Return true if zName is the extension on one of the shadow tables used
+** by this module.
+*/
+static int rtreeShadowName(const char *zName){
+ static const char *azName[] = {
+ "node", "parent", "rowid"
+ };
+ unsigned int i;
+ for(i=0; i<sizeof(azName)/sizeof(azName[0]); i++){
+ if( sqlite3_stricmp(zName, azName[i])==0 ) return 1;
+ }
+ return 0;
+}
+
static sqlite3_module rtreeModule = {
- 2, /* iVersion */
+ 3, /* iVersion */
rtreeCreate, /* xCreate - create a table */
rtreeConnect, /* xConnect - connect to an existing table */
rtreeBestIndex, /* xBestIndex - Determine search strategy */
@@ -181443,6 +182490,7 @@ static sqlite3_module rtreeModule = {
rtreeSavepoint, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
+ rtreeShadowName /* xShadowName */
};
static int rtreeSqlInit(
@@ -182433,14 +183481,24 @@ typedef float GeoCoord;
**
** encoding (1 byte) 0=big-endian, 1=little-endian
** nvertex (3 bytes) Number of vertexes as a big-endian integer
+**
+** Enough space is allocated for 4 coordinates, to work around over-zealous
+** warnings coming from some compiler (notably, clang). In reality, the size
+** of each GeoPoly memory allocate is adjusted as necessary so that the
+** GeoPoly.a[] array at the end is the appropriate size.
*/
typedef struct GeoPoly GeoPoly;
struct GeoPoly {
int nVertex; /* Number of vertexes */
unsigned char hdr[4]; /* Header for on-disk representation */
- GeoCoord a[2]; /* 2*nVertex values. X (longitude) first, then Y */
+ GeoCoord a[8]; /* 2*nVertex values. X (longitude) first, then Y */
};
+/* The size of a memory allocation needed for a GeoPoly object sufficient
+** to hold N coordinate pairs.
+*/
+#define GEOPOLY_SZ(N) (sizeof(GeoPoly) + sizeof(GeoCoord)*2*((N)-4))
+
/*
** State of a parse of a GeoJSON input.
*/
@@ -182465,7 +183523,7 @@ static void geopolySwab32(unsigned char *a){
/* Skip whitespace. Return the next non-whitespace character. */
static char geopolySkipSpace(GeoParse *p){
- while( p->z[0] && safe_isspace(p->z[0]) ) p->z++;
+ while( safe_isspace(p->z[0]) ) p->z++;
return p->z[0];
}
@@ -182485,7 +183543,7 @@ static int geopolyParseNumber(GeoParse *p, GeoCoord *pVal){
if( c=='0' && z[j+1]>='0' && z[j+1]<='9' ) return 0;
for(;; j++){
c = z[j];
- if( c>='0' && c<='9' ) continue;
+ if( safe_isdigit(c) ) continue;
if( c=='.' ){
if( z[j-1]=='-' ) return 0;
if( seenDP ) return 0;
@@ -182507,7 +183565,17 @@ static int geopolyParseNumber(GeoParse *p, GeoCoord *pVal){
break;
}
if( z[j-1]<'0' ) return 0;
- if( pVal ) *pVal = (GeoCoord)atof((const char*)p->z);
+ if( pVal ){
+#ifdef SQLITE_AMALGAMATION
+ /* The sqlite3AtoF() routine is much much faster than atof(), if it
+ ** is available */
+ double r;
+ (void)sqlite3AtoF((const char*)p->z, &r, j, SQLITE_UTF8);
+ *pVal = r;
+#else
+ *pVal = (GeoCoord)atof((const char*)p->z);
+#endif
+ }
p->z += j;
return 1;
}
@@ -182565,12 +183633,10 @@ static GeoPoly *geopolyParseJson(const unsigned char *z, int *pRc){
&& s.a[1]==s.a[s.nVertex*2-1]
&& (s.z++, geopolySkipSpace(&s)==0)
){
- int nByte;
GeoPoly *pOut;
int x = 1;
s.nVertex--; /* Remove the redundant vertex at the end */
- nByte = sizeof(GeoPoly) * s.nVertex*2*sizeof(GeoCoord);
- pOut = sqlite3_malloc64( nByte );
+ pOut = sqlite3_malloc64( GEOPOLY_SZ(s.nVertex) );
x = 1;
if( pOut==0 ) goto parse_json_err;
pOut->nVertex = s.nVertex;
@@ -182774,6 +183840,27 @@ static void geopolyXformFunc(
}
/*
+** Compute the area enclosed by the polygon.
+**
+** This routine can also be used to detect polygons that rotate in
+** the wrong direction. Polygons are suppose to be counter-clockwise (CCW).
+** This routine returns a negative value for clockwise (CW) polygons.
+*/
+static double geopolyArea(GeoPoly *p){
+ double rArea = 0.0;
+ int ii;
+ for(ii=0; ii<p->nVertex-1; ii++){
+ rArea += (p->a[ii*2] - p->a[ii*2+2]) /* (x0 - x1) */
+ * (p->a[ii*2+1] + p->a[ii*2+3]) /* (y0 + y1) */
+ * 0.5;
+ }
+ rArea += (p->a[ii*2] - p->a[0]) /* (xN - x0) */
+ * (p->a[ii*2+1] + p->a[1]) /* (yN + y0) */
+ * 0.5;
+ return rArea;
+}
+
+/*
** Implementation of the geopoly_area(X) function.
**
** If the input is a well-formed Geopoly BLOB then return the area
@@ -182788,22 +183875,107 @@ static void geopolyAreaFunc(
){
GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
if( p ){
- double rArea = 0.0;
- int ii;
- for(ii=0; ii<p->nVertex-1; ii++){
- rArea += (p->a[ii*2] - p->a[ii*2+2]) /* (x0 - x1) */
- * (p->a[ii*2+1] + p->a[ii*2+3]) /* (y0 + y1) */
- * 0.5;
- }
- rArea += (p->a[ii*2] - p->a[0]) /* (xN - x0) */
- * (p->a[ii*2+1] + p->a[1]) /* (yN + y0) */
- * 0.5;
- sqlite3_result_double(context, rArea);
+ sqlite3_result_double(context, geopolyArea(p));
sqlite3_free(p);
}
}
/*
+** Implementation of the geopoly_ccw(X) function.
+**
+** If the rotation of polygon X is clockwise (incorrect) instead of
+** counter-clockwise (the correct winding order according to RFC7946)
+** then reverse the order of the vertexes in polygon X.
+**
+** In other words, this routine returns a CCW polygon regardless of the
+** winding order of its input.
+**
+** Use this routine to sanitize historical inputs that that sometimes
+** contain polygons that wind in the wrong direction.
+*/
+static void geopolyCcwFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
+ if( p ){
+ if( geopolyArea(p)<0.0 ){
+ int ii, jj;
+ for(ii=2, jj=p->nVertex*2 - 2; ii<jj; ii+=2, jj-=2){
+ GeoCoord t = p->a[ii];
+ p->a[ii] = p->a[jj];
+ p->a[jj] = t;
+ t = p->a[ii+1];
+ p->a[ii+1] = p->a[jj+1];
+ p->a[jj+1] = t;
+ }
+ }
+ sqlite3_result_blob(context, p->hdr,
+ 4+8*p->nVertex, SQLITE_TRANSIENT);
+ sqlite3_free(p);
+ }
+}
+
+#define GEOPOLY_PI 3.1415926535897932385
+
+/* Fast approximation for sine(X) for X between -0.5*pi and 2*pi
+*/
+static double geopolySine(double r){
+ assert( r>=-0.5*GEOPOLY_PI && r<=2.0*GEOPOLY_PI );
+ if( r>=1.5*GEOPOLY_PI ){
+ r -= 2.0*GEOPOLY_PI;
+ }
+ if( r>=0.5*GEOPOLY_PI ){
+ return -geopolySine(r-GEOPOLY_PI);
+ }else{
+ double r2 = r*r;
+ double r3 = r2*r;
+ double r5 = r3*r2;
+ return 0.9996949*r - 0.1656700*r3 + 0.0075134*r5;
+ }
+}
+
+/*
+** Function: geopoly_regular(X,Y,R,N)
+**
+** Construct a simple, convex, regular polygon centered at X, Y
+** with circumradius R and with N sides.
+*/
+static void geopolyRegularFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ double x = sqlite3_value_double(argv[0]);
+ double y = sqlite3_value_double(argv[1]);
+ double r = sqlite3_value_double(argv[2]);
+ int n = sqlite3_value_int(argv[3]);
+ int i;
+ GeoPoly *p;
+
+ if( n<3 || r<=0.0 ) return;
+ if( n>1000 ) n = 1000;
+ p = sqlite3_malloc64( sizeof(*p) + (n-1)*2*sizeof(GeoCoord) );
+ if( p==0 ){
+ sqlite3_result_error_nomem(context);
+ return;
+ }
+ i = 1;
+ p->hdr[0] = *(unsigned char*)&i;
+ p->hdr[1] = 0;
+ p->hdr[2] = (n>>8)&0xff;
+ p->hdr[3] = n&0xff;
+ for(i=0; i<n; i++){
+ double rAngle = 2.0*GEOPOLY_PI*i/n;
+ p->a[i*2] = x - r*geopolySine(rAngle-0.5*GEOPOLY_PI);
+ p->a[i*2+1] = y + r*geopolySine(rAngle);
+ }
+ sqlite3_result_blob(context, p->hdr, 4+8*n, SQLITE_TRANSIENT);
+ sqlite3_free(p);
+}
+
+/*
** If pPoly is a polygon, compute its bounding box. Then:
**
** (1) if aCoord!=0 store the bounding box in aCoord, returning NULL
@@ -182847,7 +184019,7 @@ static GeoPoly *geopolyBBox(
if( pRc ) *pRc = SQLITE_OK;
if( aCoord==0 ){
geopolyBboxFill:
- pOut = sqlite3_realloc(p, sizeof(GeoPoly)+sizeof(GeoCoord)*6);
+ pOut = sqlite3_realloc(p, GEOPOLY_SZ(4));
if( pOut==0 ){
sqlite3_free(p);
if( context ) sqlite3_result_error_nomem(context);
@@ -183875,7 +185047,16 @@ static int geopolyUpdate(
if( sqlite3_value_nochange(aData[2]) ){
sqlite3_bind_null(pUp, 2);
}else{
- sqlite3_bind_value(pUp, 2, aData[2]);
+ GeoPoly *p = 0;
+ if( sqlite3_value_type(aData[2])==SQLITE_TEXT
+ && (p = geopolyFuncParam(0, aData[2], &rc))!=0
+ && rc==SQLITE_OK
+ ){
+ sqlite3_bind_blob(pUp, 2, p->hdr, 4+8*p->nVertex, SQLITE_TRANSIENT);
+ }else{
+ sqlite3_bind_value(pUp, 2, aData[2]);
+ }
+ sqlite3_free(p);
nChange = 1;
}
for(jj=1; jj<pRtree->nAux; jj++){
@@ -183919,7 +185100,7 @@ static int geopolyFindFunction(
static sqlite3_module geopolyModule = {
- 2, /* iVersion */
+ 3, /* iVersion */
geopolyCreate, /* xCreate - create a table */
geopolyConnect, /* xConnect - connect to an existing table */
geopolyBestIndex, /* xBestIndex - Determine search strategy */
@@ -183942,25 +185123,29 @@ static sqlite3_module geopolyModule = {
rtreeSavepoint, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
+ rtreeShadowName /* xShadowName */
};
static int sqlite3_geopoly_init(sqlite3 *db){
int rc = SQLITE_OK;
static const struct {
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
- int nArg;
+ signed char nArg;
+ unsigned char bPure;
const char *zName;
} aFunc[] = {
- { geopolyAreaFunc, 1, "geopoly_area" },
- { geopolyBlobFunc, 1, "geopoly_blob" },
- { geopolyJsonFunc, 1, "geopoly_json" },
- { geopolySvgFunc, -1, "geopoly_svg" },
- { geopolyWithinFunc, 2, "geopoly_within" },
- { geopolyContainsPointFunc, 3, "geopoly_contains_point" },
- { geopolyOverlapFunc, 2, "geopoly_overlap" },
- { geopolyDebugFunc, 1, "geopoly_debug" },
- { geopolyBBoxFunc, 1, "geopoly_bbox" },
- { geopolyXformFunc, 7, "geopoly_xform" },
+ { geopolyAreaFunc, 1, 1, "geopoly_area" },
+ { geopolyBlobFunc, 1, 1, "geopoly_blob" },
+ { geopolyJsonFunc, 1, 1, "geopoly_json" },
+ { geopolySvgFunc, -1, 1, "geopoly_svg" },
+ { geopolyWithinFunc, 2, 1, "geopoly_within" },
+ { geopolyContainsPointFunc, 3, 1, "geopoly_contains_point" },
+ { geopolyOverlapFunc, 2, 1, "geopoly_overlap" },
+ { geopolyDebugFunc, 1, 0, "geopoly_debug" },
+ { geopolyBBoxFunc, 1, 1, "geopoly_bbox" },
+ { geopolyXformFunc, 7, 1, "geopoly_xform" },
+ { geopolyRegularFunc, 4, 1, "geopoly_regular" },
+ { geopolyCcwFunc, 1, 1, "geopoly_ccw" },
};
static const struct {
void (*xStep)(sqlite3_context*,int,sqlite3_value**);
@@ -183971,8 +185156,9 @@ static int sqlite3_geopoly_init(sqlite3 *db){
};
int i;
for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
+ int enc = aFunc[i].bPure ? SQLITE_UTF8|SQLITE_DETERMINISTIC : SQLITE_UTF8;
rc = sqlite3_create_function(db, aFunc[i].zName, aFunc[i].nArg,
- SQLITE_UTF8, 0,
+ enc, 0,
aFunc[i].xFunc, 0, 0);
}
for(i=0; i<sizeof(aAgg)/sizeof(aAgg[0]) && rc==SQLITE_OK; i++){
@@ -185999,7 +187185,8 @@ struct rbu_vfs {
sqlite3_vfs *pRealVfs; /* Underlying VFS */
sqlite3_mutex *mutex; /* Mutex to protect pMain */
sqlite3rbu *pRbu; /* Owner RBU object */
- rbu_file *pMain; /* Linked list of main db files */
+ rbu_file *pMain; /* List of main db files */
+ rbu_file *pMainRbu; /* List of main db files with pRbu!=0 */
};
/*
@@ -186028,6 +187215,7 @@ struct rbu_file {
const char *zWal; /* Wal filename for this main db file */
rbu_file *pWalFd; /* Wal file descriptor for this main db */
rbu_file *pMainNext; /* Next MAIN_DB file */
+ rbu_file *pMainRbuNext; /* Next MAIN_DB file with pRbu!=0 */
};
/*
@@ -189625,6 +190813,69 @@ static int rbuUpdateTempSize(rbu_file *pFd, sqlite3_int64 nNew){
}
/*
+** Add an item to the main-db lists, if it is not already present.
+**
+** There are two main-db lists. One for all file descriptors, and one
+** for all file descriptors with rbu_file.pDb!=0. If the argument has
+** rbu_file.pDb!=0, then it is assumed to already be present on the
+** main list and is only added to the pDb!=0 list.
+*/
+static void rbuMainlistAdd(rbu_file *p){
+ rbu_vfs *pRbuVfs = p->pRbuVfs;
+ rbu_file *pIter;
+ assert( (p->openFlags & SQLITE_OPEN_MAIN_DB) );
+ sqlite3_mutex_enter(pRbuVfs->mutex);
+ if( p->pRbu==0 ){
+ for(pIter=pRbuVfs->pMain; pIter; pIter=pIter->pMainNext);
+ p->pMainNext = pRbuVfs->pMain;
+ pRbuVfs->pMain = p;
+ }else{
+ for(pIter=pRbuVfs->pMainRbu; pIter && pIter!=p; pIter=pIter->pMainRbuNext){}
+ if( pIter==0 ){
+ p->pMainRbuNext = pRbuVfs->pMainRbu;
+ pRbuVfs->pMainRbu = p;
+ }
+ }
+ sqlite3_mutex_leave(pRbuVfs->mutex);
+}
+
+/*
+** Remove an item from the main-db lists.
+*/
+static void rbuMainlistRemove(rbu_file *p){
+ rbu_file **pp;
+ sqlite3_mutex_enter(p->pRbuVfs->mutex);
+ for(pp=&p->pRbuVfs->pMain; *pp && *pp!=p; pp=&((*pp)->pMainNext)){}
+ if( *pp ) *pp = p->pMainNext;
+ p->pMainNext = 0;
+ for(pp=&p->pRbuVfs->pMainRbu; *pp && *pp!=p; pp=&((*pp)->pMainRbuNext)){}
+ if( *pp ) *pp = p->pMainRbuNext;
+ p->pMainRbuNext = 0;
+ sqlite3_mutex_leave(p->pRbuVfs->mutex);
+}
+
+/*
+** Given that zWal points to a buffer containing a wal file name passed to
+** either the xOpen() or xAccess() VFS method, search the main-db list for
+** a file-handle opened by the same database connection on the corresponding
+** database file.
+**
+** If parameter bRbu is true, only search for file-descriptors with
+** rbu_file.pDb!=0.
+*/
+static rbu_file *rbuFindMaindb(rbu_vfs *pRbuVfs, const char *zWal, int bRbu){
+ rbu_file *pDb;
+ sqlite3_mutex_enter(pRbuVfs->mutex);
+ if( bRbu ){
+ for(pDb=pRbuVfs->pMainRbu; pDb && pDb->zWal!=zWal; pDb=pDb->pMainRbuNext){}
+ }else{
+ for(pDb=pRbuVfs->pMain; pDb && pDb->zWal!=zWal; pDb=pDb->pMainNext){}
+ }
+ sqlite3_mutex_leave(pRbuVfs->mutex);
+ return pDb;
+}
+
+/*
** Close an rbu file.
*/
static int rbuVfsClose(sqlite3_file *pFile){
@@ -189641,17 +190892,14 @@ static int rbuVfsClose(sqlite3_file *pFile){
sqlite3_free(p->zDel);
if( p->openFlags & SQLITE_OPEN_MAIN_DB ){
- rbu_file **pp;
- sqlite3_mutex_enter(p->pRbuVfs->mutex);
- for(pp=&p->pRbuVfs->pMain; *pp!=p; pp=&((*pp)->pMainNext));
- *pp = p->pMainNext;
- sqlite3_mutex_leave(p->pRbuVfs->mutex);
+ rbuMainlistRemove(p);
rbuUnlockShm(p);
p->pReal->pMethods->xShmUnmap(p->pReal, 0);
}
else if( (p->openFlags & SQLITE_OPEN_DELETEONCLOSE) && p->pRbu ){
rbuUpdateTempSize(p, 0);
}
+ assert( p->pMainNext==0 && p->pRbuVfs->pMain!=p );
/* Close the underlying file handle */
rc = p->pReal->pMethods->xClose(p->pReal);
@@ -189910,6 +191158,9 @@ static int rbuVfsFileControl(sqlite3_file *pFile, int op, void *pArg){
}else if( rc==SQLITE_NOTFOUND ){
pRbu->pTargetFd = p;
p->pRbu = pRbu;
+ if( p->openFlags & SQLITE_OPEN_MAIN_DB ){
+ rbuMainlistAdd(p);
+ }
if( p->pWalFd ) p->pWalFd->pRbu = pRbu;
rc = SQLITE_OK;
}
@@ -190071,20 +191322,6 @@ static int rbuVfsShmUnmap(sqlite3_file *pFile, int delFlag){
return rc;
}
-/*
-** Given that zWal points to a buffer containing a wal file name passed to
-** either the xOpen() or xAccess() VFS method, return a pointer to the
-** file-handle opened by the same database connection on the corresponding
-** database file.
-*/
-static rbu_file *rbuFindMaindb(rbu_vfs *pRbuVfs, const char *zWal){
- rbu_file *pDb;
- sqlite3_mutex_enter(pRbuVfs->mutex);
- for(pDb=pRbuVfs->pMain; pDb && pDb->zWal!=zWal; pDb=pDb->pMainNext){}
- sqlite3_mutex_leave(pRbuVfs->mutex);
- return pDb;
-}
-
/*
** A main database named zName has just been opened. The following
** function returns a pointer to a buffer owned by SQLite that contains
@@ -190163,7 +191400,7 @@ static int rbuVfsOpen(
pFd->zWal = rbuMainToWal(zName, flags);
}
else if( flags & SQLITE_OPEN_WAL ){
- rbu_file *pDb = rbuFindMaindb(pRbuVfs, zName);
+ rbu_file *pDb = rbuFindMaindb(pRbuVfs, zName, 0);
if( pDb ){
if( pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){
/* This call is to open a *-wal file. Intead, open the *-oal. This
@@ -190215,10 +191452,7 @@ static int rbuVfsOpen(
** mutex protected linked list of all such files. */
pFile->pMethods = &rbuvfs_io_methods;
if( flags & SQLITE_OPEN_MAIN_DB ){
- sqlite3_mutex_enter(pRbuVfs->mutex);
- pFd->pMainNext = pRbuVfs->pMain;
- pRbuVfs->pMain = pFd;
- sqlite3_mutex_leave(pRbuVfs->mutex);
+ rbuMainlistAdd(pFd);
}
}else{
sqlite3_free(pFd->zDel);
@@ -190266,7 +191500,7 @@ static int rbuVfsAccess(
** file opened instead.
*/
if( rc==SQLITE_OK && flags==SQLITE_ACCESS_EXISTS ){
- rbu_file *pDb = rbuFindMaindb(pRbuVfs, zPath);
+ rbu_file *pDb = rbuFindMaindb(pRbuVfs, zPath, 1);
if( pDb && pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){
if( *pResOut ){
rc = SQLITE_CANTOPEN;
@@ -190679,17 +191913,15 @@ static int statDisconnect(sqlite3_vtab *pVtab){
static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int i;
- pIdxInfo->estimatedCost = 1.0e6; /* Initial cost estimate */
-
/* Look for a valid schema=? constraint. If found, change the idxNum to
** 1 and request the value of that constraint be sent to xFilter. And
** lower the cost estimate to encourage the constrained version to be
** used.
*/
for(i=0; i<pIdxInfo->nConstraint; i++){
- if( pIdxInfo->aConstraint[i].usable==0 ) continue;
- if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
if( pIdxInfo->aConstraint[i].iColumn!=10 ) continue;
+ if( pIdxInfo->aConstraint[i].usable==0 ) return SQLITE_CONSTRAINT;
+ if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
pIdxInfo->idxNum = 1;
pIdxInfo->estimatedCost = 1.0;
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
@@ -190739,7 +191971,7 @@ static int statOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
return SQLITE_OK;
}
-static void statClearPage(StatPage *p){
+static void statClearCells(StatPage *p){
int i;
if( p->aCell ){
for(i=0; i<p->nCell; i++){
@@ -190747,6 +191979,12 @@ static void statClearPage(StatPage *p){
}
sqlite3_free(p->aCell);
}
+ p->nCell = 0;
+ p->aCell = 0;
+}
+
+static void statClearPage(StatPage *p){
+ statClearCells(p);
sqlite3PagerUnref(p->pPg);
sqlite3_free(p->zPath);
memset(p, 0, sizeof(StatPage));
@@ -190809,22 +192047,33 @@ static int statDecodePage(Btree *pBt, StatPage *p){
u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0];
p->flags = aHdr[0];
+ if( p->flags==0x0A || p->flags==0x0D ){
+ isLeaf = 1;
+ nHdr = 8;
+ }else if( p->flags==0x05 || p->flags==0x02 ){
+ isLeaf = 0;
+ nHdr = 12;
+ }else{
+ goto statPageIsCorrupt;
+ }
+ if( p->iPgno==1 ) nHdr += 100;
p->nCell = get2byte(&aHdr[3]);
p->nMxPayload = 0;
-
- isLeaf = (p->flags==0x0A || p->flags==0x0D);
- nHdr = 12 - isLeaf*4 + (p->iPgno==1)*100;
+ szPage = sqlite3BtreeGetPageSize(pBt);
nUnused = get2byte(&aHdr[5]) - nHdr - 2*p->nCell;
nUnused += (int)aHdr[7];
iOff = get2byte(&aHdr[1]);
while( iOff ){
+ int iNext;
+ if( iOff>=szPage ) goto statPageIsCorrupt;
nUnused += get2byte(&aData[iOff+2]);
- iOff = get2byte(&aData[iOff]);
+ iNext = get2byte(&aData[iOff]);
+ if( iNext<iOff+4 && iNext>0 ) goto statPageIsCorrupt;
+ iOff = iNext;
}
p->nUnused = nUnused;
p->iRightChildPg = isLeaf ? 0 : sqlite3Get4byte(&aHdr[8]);
- szPage = sqlite3BtreeGetPageSize(pBt);
if( p->nCell ){
int i; /* Used to iterate through cells */
@@ -190841,6 +192090,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){
StatCell *pCell = &p->aCell[i];
iOff = get2byte(&aData[nHdr+i*2]);
+ if( iOff<nHdr || iOff>=szPage ) goto statPageIsCorrupt;
if( !isLeaf ){
pCell->iChildPg = sqlite3Get4byte(&aData[iOff]);
iOff += 4;
@@ -190857,13 +192107,14 @@ static int statDecodePage(Btree *pBt, StatPage *p){
}
if( nPayload>(u32)p->nMxPayload ) p->nMxPayload = nPayload;
getLocalPayload(nUsable, p->flags, nPayload, &nLocal);
+ if( nLocal<0 ) goto statPageIsCorrupt;
pCell->nLocal = nLocal;
- assert( nLocal>=0 );
assert( nPayload>=(u32)nLocal );
assert( nLocal<=(nUsable-35) );
if( nPayload>(u32)nLocal ){
int j;
int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4);
+ if( iOff+nLocal>nUsable ) goto statPageIsCorrupt;
pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4);
pCell->nOvfl = nOvfl;
pCell->aOvfl = sqlite3_malloc64(sizeof(u32)*nOvfl);
@@ -190887,6 +192138,11 @@ static int statDecodePage(Btree *pBt, StatPage *p){
}
return SQLITE_OK;
+
+statPageIsCorrupt:
+ p->flags = 0;
+ statClearCells(p);
+ return SQLITE_OK;
}
/*
@@ -191182,6 +192438,7 @@ SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
+ 0 /* xShadowName */
};
return sqlite3_create_module(db, "dbstat", &dbstat_module, 0);
}
@@ -191312,9 +192569,8 @@ static int dbpageBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
if( p->iColumn!=DBPAGE_COLUMN_SCHEMA ) continue;
if( p->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
if( !p->usable ){
- /* No solution. Use the default SQLITE_BIG_DBL cost */
- pIdxInfo->estimatedRows = 0x7fffffff;
- return SQLITE_OK;
+ /* No solution. */
+ return SQLITE_CONSTRAINT;
}
iPlan = 2;
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
@@ -191506,6 +192762,10 @@ static int dbpageUpdate(
Pager *pPager;
int szPage;
+ if( pTab->db->flags & SQLITE_Defensive ){
+ zErr = "read-only";
+ goto update_fail;
+ }
if( argc==1 ){
zErr = "cannot delete";
goto update_fail;
@@ -191596,6 +192856,7 @@ SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
+ 0 /* xShadowName */
};
return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0);
}
@@ -191632,6 +192893,8 @@ typedef struct SessionInput SessionInput;
# endif
#endif
+static int sessions_strm_chunk_size = SESSIONS_STRM_CHUNK_SIZE;
+
typedef struct SessionHook SessionHook;
struct SessionHook {
void *pCtx;
@@ -191694,6 +192957,7 @@ struct sqlite3_changeset_iter {
SessionInput in; /* Input buffer or stream */
SessionBuffer tblhdr; /* Buffer to hold apValue/zTab/abPK/ */
int bPatchset; /* True if this is a patchset */
+ int bInvert; /* True to invert changeset */
int rc; /* Iterator error code */
sqlite3_stmt *pConflict; /* Points to conflicting row, if any */
char *zTab; /* Current table */
@@ -191850,6 +193114,42 @@ struct SessionTable {
** The records associated with INSERT changes are in the same format as for
** changesets. It is not possible for a record associated with an INSERT
** change to contain a field set to "undefined".
+**
+** REBASE BLOB FORMAT:
+**
+** A rebase blob may be output by sqlite3changeset_apply_v2() and its
+** streaming equivalent for use with the sqlite3_rebaser APIs to rebase
+** existing changesets. A rebase blob contains one entry for each conflict
+** resolved using either the OMIT or REPLACE strategies within the apply_v2()
+** call.
+**
+** The format used for a rebase blob is very similar to that used for
+** changesets. All entries related to a single table are grouped together.
+**
+** Each group of entries begins with a table header in changeset format:
+**
+** 1 byte: Constant 0x54 (capital 'T')
+** Varint: Number of columns in the table.
+** nCol bytes: 0x01 for PK columns, 0x00 otherwise.
+** N bytes: Unqualified table name (encoded using UTF-8). Nul-terminated.
+**
+** Followed by one or more entries associated with the table.
+**
+** 1 byte: Either SQLITE_INSERT (0x12), DELETE (0x09).
+** 1 byte: Flag. 0x01 for REPLACE, 0x00 for OMIT.
+** record: (in the record format defined above).
+**
+** In a rebase blob, the first field is set to SQLITE_INSERT if the change
+** that caused the conflict was an INSERT or UPDATE, or to SQLITE_DELETE if
+** it was a DELETE. The second field is set to 0x01 if the conflict
+** resolution strategy was REPLACE, or 0x00 if it was OMIT.
+**
+** If the change that caused the conflict was a DELETE, then the single
+** record is a copy of the old.* record from the original changeset. If it
+** was an INSERT, then the single record is a copy of the new.* record. If
+** the conflicting change was an UPDATE, then the single record is a copy
+** of the new.* record with the PK fields filled in based on the original
+** old.* record.
*/
/*
@@ -193400,12 +194700,12 @@ SQLITE_API int sqlite3session_attach(
static int sessionBufferGrow(SessionBuffer *p, int nByte, int *pRc){
if( *pRc==SQLITE_OK && p->nAlloc-p->nBuf<nByte ){
u8 *aNew;
- int nNew = p->nAlloc ? p->nAlloc : 128;
+ i64 nNew = p->nAlloc ? p->nAlloc : 128;
do {
nNew = nNew*2;
- }while( nNew<(p->nBuf+nByte) );
+ }while( (nNew-p->nBuf)<nByte );
- aNew = (u8 *)sqlite3_realloc(p->aBuf, nNew);
+ aNew = (u8 *)sqlite3_realloc64(p->aBuf, nNew);
if( 0==aNew ){
*pRc = SQLITE_NOMEM;
}else{
@@ -194003,12 +195303,12 @@ static int sessionGenerateChangeset(
rc = sqlite3_reset(pSel);
}
- /* If the buffer is now larger than SESSIONS_STRM_CHUNK_SIZE, pass
+ /* If the buffer is now larger than sessions_strm_chunk_size, pass
** its contents to the xOutput() callback. */
if( xOutput
&& rc==SQLITE_OK
&& buf.nBuf>nNoop
- && buf.nBuf>SESSIONS_STRM_CHUNK_SIZE
+ && buf.nBuf>sessions_strm_chunk_size
){
rc = xOutput(pOut, (void*)buf.aBuf, buf.nBuf);
nNoop = -1;
@@ -194147,7 +195447,8 @@ static int sessionChangesetStart(
int (*xInput)(void *pIn, void *pData, int *pnData),
void *pIn,
int nChangeset, /* Size of buffer pChangeset in bytes */
- void *pChangeset /* Pointer to buffer containing changeset */
+ void *pChangeset, /* Pointer to buffer containing changeset */
+ int bInvert /* True to invert changeset */
){
sqlite3_changeset_iter *pRet; /* Iterator to return */
int nByte; /* Number of bytes to allocate for iterator */
@@ -194167,6 +195468,7 @@ static int sessionChangesetStart(
pRet->in.xInput = xInput;
pRet->in.pIn = pIn;
pRet->in.bEof = (xInput ? 0 : 1);
+ pRet->bInvert = bInvert;
/* Populate the output variable and return success. */
*pp = pRet;
@@ -194181,7 +195483,16 @@ SQLITE_API int sqlite3changeset_start(
int nChangeset, /* Size of buffer pChangeset in bytes */
void *pChangeset /* Pointer to buffer containing changeset */
){
- return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset);
+ return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, 0);
+}
+SQLITE_API int sqlite3changeset_start_v2(
+ sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */
+ int nChangeset, /* Size of buffer pChangeset in bytes */
+ void *pChangeset, /* Pointer to buffer containing changeset */
+ int flags
+){
+ int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT);
+ return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, bInvert);
}
/*
@@ -194192,7 +195503,16 @@ SQLITE_API int sqlite3changeset_start_strm(
int (*xInput)(void *pIn, void *pData, int *pnData),
void *pIn
){
- return sessionChangesetStart(pp, xInput, pIn, 0, 0);
+ return sessionChangesetStart(pp, xInput, pIn, 0, 0, 0);
+}
+SQLITE_API int sqlite3changeset_start_v2_strm(
+ sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn,
+ int flags
+){
+ int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT);
+ return sessionChangesetStart(pp, xInput, pIn, 0, 0, bInvert);
}
/*
@@ -194200,7 +195520,7 @@ SQLITE_API int sqlite3changeset_start_strm(
** object and the buffer is full, discard some data to free up space.
*/
static void sessionDiscardData(SessionInput *pIn){
- if( pIn->xInput && pIn->iNext>=SESSIONS_STRM_CHUNK_SIZE ){
+ if( pIn->xInput && pIn->iNext>=sessions_strm_chunk_size ){
int nMove = pIn->buf.nBuf - pIn->iNext;
assert( nMove>=0 );
if( nMove>0 ){
@@ -194223,7 +195543,7 @@ static int sessionInputBuffer(SessionInput *pIn, int nByte){
int rc = SQLITE_OK;
if( pIn->xInput ){
while( !pIn->bEof && (pIn->iNext+nByte)>=pIn->nData && rc==SQLITE_OK ){
- int nNew = SESSIONS_STRM_CHUNK_SIZE;
+ int nNew = sessions_strm_chunk_size;
if( pIn->bNoDiscard==0 ) sessionDiscardData(pIn);
if( SQLITE_OK==sessionBufferGrow(&pIn->buf, nNew, &rc) ){
@@ -194571,10 +195891,10 @@ static int sessionChangesetNext(
op = p->in.aData[p->in.iNext++];
}
- if( p->zTab==0 ){
+ if( p->zTab==0 || (p->bPatchset && p->bInvert) ){
/* The first record in the changeset is not a table header. Must be a
** corrupt changeset. */
- assert( p->in.iNext==1 );
+ assert( p->in.iNext==1 || p->zTab );
return (p->rc = SQLITE_CORRUPT_BKPT);
}
@@ -194599,33 +195919,39 @@ static int sessionChangesetNext(
*paRec = &p->in.aData[p->in.iNext];
p->in.iNext += *pnRec;
}else{
+ sqlite3_value **apOld = (p->bInvert ? &p->apValue[p->nCol] : p->apValue);
+ sqlite3_value **apNew = (p->bInvert ? p->apValue : &p->apValue[p->nCol]);
/* If this is an UPDATE or DELETE, read the old.* record. */
if( p->op!=SQLITE_INSERT && (p->bPatchset==0 || p->op==SQLITE_DELETE) ){
u8 *abPK = p->bPatchset ? p->abPK : 0;
- p->rc = sessionReadRecord(&p->in, p->nCol, abPK, p->apValue);
+ p->rc = sessionReadRecord(&p->in, p->nCol, abPK, apOld);
if( p->rc!=SQLITE_OK ) return p->rc;
}
/* If this is an INSERT or UPDATE, read the new.* record. */
if( p->op!=SQLITE_DELETE ){
- p->rc = sessionReadRecord(&p->in, p->nCol, 0, &p->apValue[p->nCol]);
+ p->rc = sessionReadRecord(&p->in, p->nCol, 0, apNew);
if( p->rc!=SQLITE_OK ) return p->rc;
}
- if( p->bPatchset && p->op==SQLITE_UPDATE ){
+ if( (p->bPatchset || p->bInvert) && p->op==SQLITE_UPDATE ){
/* If this is an UPDATE that is part of a patchset, then all PK and
** modified fields are present in the new.* record. The old.* record
** is currently completely empty. This block shifts the PK fields from
** new.* to old.*, to accommodate the code that reads these arrays. */
for(i=0; i<p->nCol; i++){
- assert( p->apValue[i]==0 );
+ assert( p->bPatchset==0 || p->apValue[i]==0 );
if( p->abPK[i] ){
+ assert( p->apValue[i]==0 );
p->apValue[i] = p->apValue[i+p->nCol];
if( p->apValue[i]==0 ) return (p->rc = SQLITE_CORRUPT_BKPT);
p->apValue[i+p->nCol] = 0;
}
}
+ }else if( p->bInvert ){
+ if( p->op==SQLITE_INSERT ) p->op = SQLITE_DELETE;
+ else if( p->op==SQLITE_DELETE ) p->op = SQLITE_INSERT;
}
}
@@ -194942,7 +196268,7 @@ static int sessionChangesetInvert(
}
assert( rc==SQLITE_OK );
- if( xOutput && sOut.nBuf>=SESSIONS_STRM_CHUNK_SIZE ){
+ if( xOutput && sOut.nBuf>=sessions_strm_chunk_size ){
rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
sOut.nBuf = 0;
if( rc!=SQLITE_OK ) goto finished_invert;
@@ -195021,7 +196347,8 @@ struct SessionApplyCtx {
int bDeferConstraints; /* True to defer constraints */
SessionBuffer constraints; /* Deferred constraints are stored here */
SessionBuffer rebase; /* Rebase information (if any) here */
- int bRebaseStarted; /* If table header is already in rebase */
+ u8 bRebaseStarted; /* If table header is already in rebase */
+ u8 bRebase; /* True to collect rebase information */
};
/*
@@ -195418,35 +196745,36 @@ static int sessionRebaseAdd(
sqlite3_changeset_iter *pIter /* Iterator pointing at current change */
){
int rc = SQLITE_OK;
- int i;
- int eOp = pIter->op;
- if( p->bRebaseStarted==0 ){
- /* Append a table-header to the rebase buffer */
- const char *zTab = pIter->zTab;
- sessionAppendByte(&p->rebase, 'T', &rc);
- sessionAppendVarint(&p->rebase, p->nCol, &rc);
- sessionAppendBlob(&p->rebase, p->abPK, p->nCol, &rc);
- sessionAppendBlob(&p->rebase, (u8*)zTab, (int)strlen(zTab)+1, &rc);
- p->bRebaseStarted = 1;
- }
-
- assert( eType==SQLITE_CHANGESET_REPLACE||eType==SQLITE_CHANGESET_OMIT );
- assert( eOp==SQLITE_DELETE || eOp==SQLITE_INSERT || eOp==SQLITE_UPDATE );
-
- sessionAppendByte(&p->rebase,
- (eOp==SQLITE_DELETE ? SQLITE_DELETE : SQLITE_INSERT), &rc
- );
- sessionAppendByte(&p->rebase, (eType==SQLITE_CHANGESET_REPLACE), &rc);
- for(i=0; i<p->nCol; i++){
- sqlite3_value *pVal = 0;
- if( eOp==SQLITE_DELETE || (eOp==SQLITE_UPDATE && p->abPK[i]) ){
- sqlite3changeset_old(pIter, i, &pVal);
- }else{
- sqlite3changeset_new(pIter, i, &pVal);
+ if( p->bRebase ){
+ int i;
+ int eOp = pIter->op;
+ if( p->bRebaseStarted==0 ){
+ /* Append a table-header to the rebase buffer */
+ const char *zTab = pIter->zTab;
+ sessionAppendByte(&p->rebase, 'T', &rc);
+ sessionAppendVarint(&p->rebase, p->nCol, &rc);
+ sessionAppendBlob(&p->rebase, p->abPK, p->nCol, &rc);
+ sessionAppendBlob(&p->rebase, (u8*)zTab, (int)strlen(zTab)+1, &rc);
+ p->bRebaseStarted = 1;
}
- sessionAppendValue(&p->rebase, pVal, &rc);
- }
+ assert( eType==SQLITE_CHANGESET_REPLACE||eType==SQLITE_CHANGESET_OMIT );
+ assert( eOp==SQLITE_DELETE || eOp==SQLITE_INSERT || eOp==SQLITE_UPDATE );
+
+ sessionAppendByte(&p->rebase,
+ (eOp==SQLITE_DELETE ? SQLITE_DELETE : SQLITE_INSERT), &rc
+ );
+ sessionAppendByte(&p->rebase, (eType==SQLITE_CHANGESET_REPLACE), &rc);
+ for(i=0; i<p->nCol; i++){
+ sqlite3_value *pVal = 0;
+ if( eOp==SQLITE_DELETE || (eOp==SQLITE_UPDATE && p->abPK[i]) ){
+ sqlite3changeset_old(pIter, i, &pVal);
+ }else{
+ sqlite3changeset_new(pIter, i, &pVal);
+ }
+ sessionAppendValue(&p->rebase, pVal, &rc);
+ }
+ }
return rc;
}
@@ -195789,7 +197117,7 @@ static int sessionRetryConstraints(
SessionBuffer cons = pApply->constraints;
memset(&pApply->constraints, 0, sizeof(SessionBuffer));
- rc = sessionChangesetStart(&pIter2, 0, 0, cons.nBuf, cons.aBuf);
+ rc = sessionChangesetStart(&pIter2, 0, 0, cons.nBuf, cons.aBuf, 0);
if( rc==SQLITE_OK ){
int nByte = 2*pApply->nCol*sizeof(sqlite3_value*);
int rc2;
@@ -195855,6 +197183,7 @@ static int sessionChangesetApply(
pIter->in.bNoDiscard = 1;
memset(&sApply, 0, sizeof(sApply));
+ sApply.bRebase = (ppRebase && pnRebase);
sqlite3_mutex_enter(sqlite3_db_mutex(db));
if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){
rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
@@ -196005,7 +197334,8 @@ static int sessionChangesetApply(
}
}
- if( rc==SQLITE_OK && bPatchset==0 && ppRebase && pnRebase ){
+ assert( sApply.bRebase || sApply.rebase.nBuf==0 );
+ if( rc==SQLITE_OK && bPatchset==0 && sApply.bRebase ){
*ppRebase = (void*)sApply.rebase.aBuf;
*pnRebase = sApply.rebase.nBuf;
sApply.rebase.aBuf = 0;
@@ -196043,7 +197373,8 @@ SQLITE_API int sqlite3changeset_apply_v2(
int flags
){
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
- int rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
+ int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
+ int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset,bInverse);
if( rc==SQLITE_OK ){
rc = sessionChangesetApply(
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
@@ -196100,7 +197431,8 @@ SQLITE_API int sqlite3changeset_apply_v2_strm(
int flags
){
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
- int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn);
+ int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
+ int rc = sessionChangesetStart(&pIter, xInput, pIn, 0, 0, bInverse);
if( rc==SQLITE_OK ){
rc = sessionChangesetApply(
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
@@ -196473,13 +197805,12 @@ static int sessionChangegroupOutput(
sessionAppendByte(&buf, p->op, &rc);
sessionAppendByte(&buf, p->bIndirect, &rc);
sessionAppendBlob(&buf, p->aRecord, p->nRecord, &rc);
+ if( rc==SQLITE_OK && xOutput && buf.nBuf>=sessions_strm_chunk_size ){
+ rc = xOutput(pOut, buf.aBuf, buf.nBuf);
+ buf.nBuf = 0;
+ }
}
}
-
- if( rc==SQLITE_OK && xOutput && buf.nBuf>=SESSIONS_STRM_CHUNK_SIZE ){
- rc = xOutput(pOut, buf.aBuf, buf.nBuf);
- buf.nBuf = 0;
- }
}
if( rc==SQLITE_OK ){
@@ -196870,7 +198201,7 @@ static int sessionRebase(
sessionAppendByte(&sOut, pIter->bIndirect, &rc);
sessionAppendBlob(&sOut, aRec, nRec, &rc);
}
- if( rc==SQLITE_OK && xOutput && sOut.nBuf>SESSIONS_STRM_CHUNK_SIZE ){
+ if( rc==SQLITE_OK && xOutput && sOut.nBuf>sessions_strm_chunk_size ){
rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
sOut.nBuf = 0;
}
@@ -196981,6 +198312,27 @@ SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p){
}
}
+/*
+** Global configuration
+*/
+SQLITE_API int sqlite3session_config(int op, void *pArg){
+ int rc = SQLITE_OK;
+ switch( op ){
+ case SQLITE_SESSION_CONFIG_STRMSIZE: {
+ int *pInt = (int*)pArg;
+ if( *pInt>0 ){
+ sessions_strm_chunk_size = *pInt;
+ }
+ *pInt = sessions_strm_chunk_size;
+ break;
+ }
+ default:
+ rc = SQLITE_MISUSE;
+ break;
+ }
+ return rc;
+}
+
#endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */
/************** End of sqlite3session.c **************************************/
@@ -198415,6 +199767,7 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*);
** input grammar file:
*/
/* #include <stdio.h> */
+/* #include <assert.h> */
/************ Begin %include sections from the grammar ************************/
/* #include "fts5Int.h" */
@@ -199742,10 +201095,9 @@ static void sqlite3Fts5Parser(
fts5yymajor = fts5YYNOCODE;
}else{
while( fts5yypParser->fts5yytos >= fts5yypParser->fts5yystack
- && fts5yymx != fts5YYERRORSYMBOL
&& (fts5yyact = fts5yy_find_reduce_action(
fts5yypParser->fts5yytos->stateno,
- fts5YYERRORSYMBOL)) >= fts5YY_MIN_REDUCE
+ fts5YYERRORSYMBOL)) > fts5YY_MAX_SHIFTREDUCE
){
fts5yy_pop_parser_stack(fts5yypParser);
}
@@ -210695,7 +212047,7 @@ static int sqlite3Fts5IndexQuery(
fts5CloseReader(p);
}
- *ppIter = &pRet->base;
+ *ppIter = (Fts5IndexIter*)pRet;
sqlite3Fts5BufferFree(&buf);
}
return fts5IndexReturn(p);
@@ -214444,12 +215796,27 @@ static void fts5SourceIdFunc(
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
- sqlite3_result_text(pCtx, "fts5: 2018-09-25 19:08:10 fb90e7189ae6d62e77ba3a308ca5d683f90bbe633cf681865365b8e92792d1c7", -1, SQLITE_TRANSIENT);
+ sqlite3_result_text(pCtx, "fts5: 2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9", -1, SQLITE_TRANSIENT);
+}
+
+/*
+** Return true if zName is the extension on one of the shadow tables used
+** by this module.
+*/
+static int fts5ShadowName(const char *zName){
+ static const char *azName[] = {
+ "config", "content", "data", "docsize", "idx"
+ };
+ unsigned int i;
+ for(i=0; i<sizeof(azName)/sizeof(azName[0]); i++){
+ if( sqlite3_stricmp(zName, azName[i])==0 ) return 1;
+ }
+ return 0;
}
static int fts5Init(sqlite3 *db){
static const sqlite3_module fts5Mod = {
- /* iVersion */ 2,
+ /* iVersion */ 3,
/* xCreate */ fts5CreateMethod,
/* xConnect */ fts5ConnectMethod,
/* xBestIndex */ fts5BestIndexMethod,
@@ -214472,6 +215839,7 @@ static int fts5Init(sqlite3 *db){
/* xSavepoint */ fts5SavepointMethod,
/* xRelease */ fts5ReleaseMethod,
/* xRollbackTo */ fts5RollbackToMethod,
+ /* xShadowName */ fts5ShadowName
};
int rc;
@@ -218516,6 +219884,8 @@ static int fts5VocabInstanceNext(Fts5VocabCursor *pCsr){
i64 *pp = &pCsr->iInstPos;
int *po = &pCsr->iInstOff;
+ assert( sqlite3Fts5IterEof(pIter)==0 );
+ assert( pCsr->bEof==0 );
while( eDetail==FTS5_DETAIL_NONE
|| sqlite3Fts5PoslistNext64(pIter->pData, pIter->nData, po, pp)
){
@@ -218525,7 +219895,7 @@ static int fts5VocabInstanceNext(Fts5VocabCursor *pCsr){
rc = sqlite3Fts5IterNextScan(pCsr->pIter);
if( rc==SQLITE_OK ){
rc = fts5VocabInstanceNewTerm(pCsr);
- if( eDetail==FTS5_DETAIL_NONE ) break;
+ if( pCsr->bEof || eDetail==FTS5_DETAIL_NONE ) break;
}
if( rc ){
pCsr->bEof = 1;
@@ -218840,6 +220210,7 @@ static int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
+ /* xShadowName */ 0
};
void *p = (void*)pGlobal;
@@ -218847,8 +220218,6 @@ static int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){
}
-
-
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) */
@@ -219122,6 +220491,7 @@ static sqlite3_module stmtModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
+ 0, /* xShadowName */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -219154,9 +220524,9 @@ SQLITE_API int sqlite3_stmt_init(
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
/************** End of stmt.c ************************************************/
-#if __LINE__!=219157
+#if __LINE__!=220527
#undef SQLITE_SOURCE_ID
-#define SQLITE_SOURCE_ID "2018-09-25 19:08:10 fb90e7189ae6d62e77ba3a308ca5d683f90bbe633cf681865365b8e92792alt2"
+#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238alt2"
#endif
/* Return the source-id for this library */
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
diff --git a/src/3rdparty/sqlite/sqlite3.h b/src/3rdparty/sqlite/sqlite3.h
index 4612ecda7f..f36ae57a64 100644
--- a/src/3rdparty/sqlite/sqlite3.h
+++ b/src/3rdparty/sqlite/sqlite3.h
@@ -123,9 +123,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.25.2"
-#define SQLITE_VERSION_NUMBER 3025002
-#define SQLITE_SOURCE_ID "2018-09-25 19:08:10 fb90e7189ae6d62e77ba3a308ca5d683f90bbe633cf681865365b8e92792d1c7"
+#define SQLITE_VERSION "3.26.0"
+#define SQLITE_VERSION_NUMBER 3026000
+#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -2017,6 +2017,7 @@ struct sqlite3_mem_methods {
** is invoked.
**
** <dl>
+** [[SQLITE_DBCONFIG_LOOKASIDE]]
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
** <dd> ^This option takes three additional arguments that determine the
** [lookaside memory allocator] configuration for the [database connection].
@@ -2039,6 +2040,7 @@ struct sqlite3_mem_methods {
** memory is in use leaves the configuration unchanged and returns
** [SQLITE_BUSY].)^</dd>
**
+** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
** <dd> ^This option is used to enable or disable the enforcement of
** [foreign key constraints]. There should be two additional arguments.
@@ -2049,6 +2051,7 @@ struct sqlite3_mem_methods {
** following this call. The second parameter may be a NULL pointer, in
** which case the FK enforcement setting is not reported back. </dd>
**
+** [[SQLITE_DBCONFIG_ENABLE_TRIGGER]]
** <dt>SQLITE_DBCONFIG_ENABLE_TRIGGER</dt>
** <dd> ^This option is used to enable or disable [CREATE TRIGGER | triggers].
** There should be two additional arguments.
@@ -2059,6 +2062,7 @@ struct sqlite3_mem_methods {
** following this call. The second parameter may be a NULL pointer, in
** which case the trigger setting is not reported back. </dd>
**
+** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
** <dd> ^This option is used to enable or disable the two-argument
** version of the [fts3_tokenizer()] function which is part of the
@@ -2072,6 +2076,7 @@ struct sqlite3_mem_methods {
** following this call. The second parameter may be a NULL pointer, in
** which case the new setting is not reported back. </dd>
**
+** [[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION]]
** <dt>SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION</dt>
** <dd> ^This option is used to enable or disable the [sqlite3_load_extension()]
** interface independently of the [load_extension()] SQL function.
@@ -2089,7 +2094,7 @@ struct sqlite3_mem_methods {
** be a NULL pointer, in which case the new setting is not reported back.
** </dd>
**
-** <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
+** [[SQLITE_DBCONFIG_MAINDBNAME]] <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
** <dd> ^This option is used to change the name of the "main" database
** schema. ^The sole argument is a pointer to a constant UTF8 string
** which will become the new schema name in place of "main". ^SQLite
@@ -2098,6 +2103,7 @@ struct sqlite3_mem_methods {
** until after the database connection closes.
** </dd>
**
+** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]]
** <dt>SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE</dt>
** <dd> Usually, when a database in wal mode is closed or detached from a
** database handle, SQLite checks if this will mean that there are now no
@@ -2111,7 +2117,7 @@ struct sqlite3_mem_methods {
** have been disabled - 0 if they are not disabled, 1 if they are.
** </dd>
**
-** <dt>SQLITE_DBCONFIG_ENABLE_QPSG</dt>
+** [[SQLITE_DBCONFIG_ENABLE_QPSG]] <dt>SQLITE_DBCONFIG_ENABLE_QPSG</dt>
** <dd>^(The SQLITE_DBCONFIG_ENABLE_QPSG option activates or deactivates
** the [query planner stability guarantee] (QPSG). When the QPSG is active,
** a single SQL query statement will always use the same algorithm regardless
@@ -2127,7 +2133,7 @@ struct sqlite3_mem_methods {
** following this call.
** </dd>
**
-** <dt>SQLITE_DBCONFIG_TRIGGER_EQP</dt>
+** [[SQLITE_DBCONFIG_TRIGGER_EQP]] <dt>SQLITE_DBCONFIG_TRIGGER_EQP</dt>
** <dd> By default, the output of EXPLAIN QUERY PLAN commands does not
** include output for any operations performed by trigger programs. This
** option is used to set or clear (the default) a flag that governs this
@@ -2139,7 +2145,7 @@ struct sqlite3_mem_methods {
** it is not disabled, 1 if it is.
** </dd>
**
-** <dt>SQLITE_DBCONFIG_RESET_DATABASE</dt>
+** [[SQLITE_DBCONFIG_RESET_DATABASE]] <dt>SQLITE_DBCONFIG_RESET_DATABASE</dt>
** <dd> Set the SQLITE_DBCONFIG_RESET_DATABASE flag and then run
** [VACUUM] in order to reset a database back to an empty database
** with no schema and no content. The following process works even for
@@ -2158,6 +2164,18 @@ struct sqlite3_mem_methods {
** Because resetting a database is destructive and irreversible, the
** process requires the use of this obscure API and multiple steps to help
** ensure that it does not happen by accident.
+**
+** [[SQLITE_DBCONFIG_DEFENSIVE]] <dt>SQLITE_DBCONFIG_DEFENSIVE</dt>
+** <dd>The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the
+** "defensive" flag for a database connection. When the defensive
+** flag is enabled, language features that allow ordinary SQL to
+** deliberately corrupt the database file are disabled. The disabled
+** features include but are not limited to the following:
+** <ul>
+** <li> The [PRAGMA writable_schema=ON] statement.
+** <li> Writes to the [sqlite_dbpage] virtual table.
+** <li> Direct writes to [shadow tables].
+** </ul>
** </dd>
** </dl>
*/
@@ -2171,7 +2189,8 @@ struct sqlite3_mem_methods {
#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_MAX 1009 /* Largest DBCONFIG */
+#define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */
+#define SQLITE_DBCONFIG_MAX 1010 /* Largest DBCONFIG */
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
@@ -3609,9 +3628,19 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** on this hint by avoiding the use of [lookaside memory] so as not to
** deplete the limited store of lookaside memory. Future versions of
** SQLite may act on this hint differently.
+**
+** [[SQLITE_PREPARE_NORMALIZE]] ^(<dt>SQLITE_PREPARE_NORMALIZE</dt>
+** <dd>The SQLITE_PREPARE_NORMALIZE flag indicates that a normalized
+** representation of the SQL statement should be calculated and then
+** associated with the prepared statement, which can be obtained via
+** the [sqlite3_normalized_sql()] interface.)^ 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.
** </dl>
*/
#define SQLITE_PREPARE_PERSISTENT 0x01
+#define SQLITE_PREPARE_NORMALIZE 0x02
/*
** CAPI3REF: Compiling An SQL Statement
@@ -3769,6 +3798,11 @@ SQLITE_API int 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
@@ -3784,14 +3818,16 @@ SQLITE_API int sqlite3_prepare16_v3(
** bound parameter expansions. ^The [SQLITE_OMIT_TRACE] compile-time
** option causes sqlite3_expanded_sql() to always return NULL.
**
-** ^The string returned by sqlite3_sql(P) is managed by SQLite and is
-** automatically freed when the prepared statement is finalized.
+** ^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
@@ -6281,6 +6317,9 @@ struct sqlite3_module {
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*);
};
/*
@@ -7203,6 +7242,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#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
@@ -8615,6 +8655,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...);
** can use to customize and optimize their behavior.
**
** <dl>
+** [[SQLITE_VTAB_CONSTRAINT_SUPPORT]]
** <dt>SQLITE_VTAB_CONSTRAINT_SUPPORT
** <dd>Calls of the form
** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported,
@@ -9384,7 +9425,7 @@ struct sqlite3_rtree_query_info {
sqlite3_int64 iRowid; /* Rowid for current entry */
sqlite3_rtree_dbl rParentScore; /* Score of parent node */
int eParentWithin; /* Visibility of parent node */
- int eWithin; /* OUT: Visiblity */
+ 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 */
@@ -9880,12 +9921,38 @@ SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession);
** 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 <b>experimental</b>
+** 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
+**
+** The following flags may passed via the 4th parameter to
+** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
+**
+** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
+** Invert the changeset while iterating through it. This is equivalent to
+** inverting a changeset using sqlite3changeset_invert() before applying it.
+** It is an error to specify this flag with a patchset.
+*/
+#define SQLITE_CHANGESETSTART_INVERT 0x0002
/*
@@ -10540,7 +10607,7 @@ SQLITE_API int sqlite3changeset_apply_v2(
),
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase, /* OUT: Rebase data */
- int flags /* Combination of SESSION_APPLY_* flags */
+ int flags /* SESSION_CHANGESETAPPLY_* flags */
);
/*
@@ -10558,8 +10625,14 @@ SQLITE_API int sqlite3changeset_apply_v2(
** causes the sessions module to omit this savepoint. In this case, if the
** caller has an open transaction or savepoint when apply_v2() is called,
** it may revert the partially applied changeset by rolling it back.
+**
+** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
+** Invert the changeset before applying it. This is equivalent to inverting
+** a changeset using sqlite3changeset_invert() before applying it. It is
+** an error to specify this flag with a patchset.
*/
#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001
+#define SQLITE_CHANGESETAPPLY_INVERT 0x0002
/*
** CAPI3REF: Constants Passed To The Conflict Handler
@@ -10953,6 +11026,12 @@ SQLITE_API int sqlite3changeset_start_strm(
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),
@@ -10979,6 +11058,45 @@ SQLITE_API int sqlite3rebaser_rebase_strm(
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.
+**
+** <dl>
+** <dt>SQLITE_SESSION_CONFIG_STRMSIZE<dd>
+** 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.
+** </dl>
+**
+** 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().
+*/
+#define SQLITE_SESSION_CONFIG_STRMSIZE 1
/*
** Make sure we can call this stuff from C++.
diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java
index 7826527918..ae9ccc09db 100644
--- a/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java
+++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java
@@ -612,7 +612,7 @@ public abstract class QtLoader {
}
if (m_qtLibs != null) {
- String libPrefix = apkDeployFromSystem ? libsDir + "lib" : localPrefix + "lib/lib";
+ String libPrefix = libsDir + "lib";
for (int i = 0; i < m_qtLibs.length; i++)
libraryList.add(libPrefix + m_qtLibs[i] + ".so");
}
diff --git a/src/android/templates/AndroidManifest.xml b/src/android/templates/AndroidManifest.xml
index 67a4a429f6..992ceb3aa2 100644
--- a/src/android/templates/AndroidManifest.xml
+++ b/src/android/templates/AndroidManifest.xml
@@ -1,5 +1,17 @@
<?xml version='1.0' encoding='utf-8'?>
<manifest package="org.qtproject.example" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
+ <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
+
+ <!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
+ Remove the comment if you do not require these default permissions. -->
+ <!-- %%INSERT_PERMISSIONS -->
+
+ <!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
+ Remove the comment if you do not require these default features. -->
+ <!-- %%INSERT_FEATURES -->
+
+ <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
+
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
android:name="org.qtproject.qt5.android.bindings.QtActivity"
@@ -70,15 +82,4 @@
</application>
- <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
- <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
-
- <!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
- Remove the comment if you do not require these default permissions. -->
- <!-- %%INSERT_PERMISSIONS -->
-
- <!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
- Remove the comment if you do not require these default features. -->
- <!-- %%INSERT_FEATURES -->
-
</manifest>
diff --git a/src/corelib/codecs/qwindowscodec.cpp b/src/corelib/codecs/qwindowscodec.cpp
index 6b703f7517..710935a65a 100644
--- a/src/corelib/codecs/qwindowscodec.cpp
+++ b/src/corelib/codecs/qwindowscodec.cpp
@@ -83,8 +83,12 @@ QString QWindowsLocalCodec::convertToUnicode(const char *chars, int length, Conv
len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
prev, 2, wc.data(), wc.length());
if (len) {
- prepend = true;
sp.append(QChar(wc[0]));
+ if (mblen == 1) {
+ state->remainingChars = 0;
+ return sp;
+ }
+ prepend = true;
mb++;
mblen--;
wc[0] = 0;
diff --git a/src/corelib/doc/qtcore.qdocconf b/src/corelib/doc/qtcore.qdocconf
index 5a42e21845..85dcde4607 100644
--- a/src/corelib/doc/qtcore.qdocconf
+++ b/src/corelib/doc/qtcore.qdocconf
@@ -45,6 +45,9 @@ excludedirs += snippets
excludefiles += ../../../examples/widgets/tools/customcompleter/doc/src/customcompleter.qdoc \
../../../examples/widgets/tools/codecs/doc/src/codecs.qdoc
+# Included in qttestlib.qdocconf instead
+excludefiles += ../kernel/qtestsupport_core.cpp
+
manifestmeta.highlighted.names = "QtCore/JSON Save Game Example" \
"QtCore/Local Fortune*"
diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp
index 055455c431..3f0b78ec55 100644
--- a/src/corelib/global/qglobal.cpp
+++ b/src/corelib/global/qglobal.cpp
@@ -2050,6 +2050,8 @@ static const char *osVer_helper(QOperatingSystemVersion version = QOperatingSyst
return "Sierra";
case 13:
return "High Sierra";
+ case 14:
+ return "Mojave";
}
}
// unknown, future version
diff --git a/src/corelib/global/qnumeric_p.h b/src/corelib/global/qnumeric_p.h
index c762da80d3..4a225b2599 100644
--- a/src/corelib/global/qnumeric_p.h
+++ b/src/corelib/global/qnumeric_p.h
@@ -348,7 +348,7 @@ template <> inline bool mul_overflow(qint64 v1, qint64 v2, qint64 *r)
// as signed for the low bits and use a signed right shift to verify that
// 'high' is nothing but sign bits that match the sign of 'low'.
- qint64 high = __mulh(v1, v2);
+ qint64 high = Q_SMULH(v1, v2);
*r = qint64(quint64(v1) * quint64(v2));
return (*r >> 63) != high;
}
diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp
index c5a1dab50b..9c4ae5a1c0 100644
--- a/src/corelib/io/qdir.cpp
+++ b/src/corelib/io/qdir.cpp
@@ -748,6 +748,21 @@ static int drivePrefixLength(const QString &path)
}
#endif // Q_OS_WIN
+static bool treatAsAbsolute(const QString &path)
+{
+ // ### Qt 6: be consistent about absolute paths
+
+ // QFileInfo will use the right FS-engine for virtual file-systems
+ // (e.g. resource paths). Unfortunately, for real file-systems, it relies
+ // on QFileSystemEntry's isRelative(), which is flawed on MS-Win, ignoring
+ // its (correct) isAbsolute(). So only use that isAbsolute() unless there's
+ // a colon in the path.
+ // FIXME: relies on virtual file-systems having colons in their prefixes.
+ // The case of an MS-absolute C:/... path happens to work either way.
+ return (path.contains(QLatin1Char(':')) && QFileInfo(path).isAbsolute())
+ || QFileSystemEntry(path).isAbsolute();
+}
+
/*!
Returns the path name of a file in the directory. Does \e not
check if the file actually exists in the directory; but see
@@ -759,13 +774,10 @@ static int drivePrefixLength(const QString &path)
*/
QString QDir::filePath(const QString &fileName) const
{
- const QDirPrivate* d = d_ptr.constData();
- // Mistrust our own isAbsolutePath() for real files; Q_OS_WIN needs a drive.
- if (fileName.startsWith(QLatin1Char(':')) // i.e. resource path
- ? isAbsolutePath(fileName) : QFileSystemEntry(fileName).isAbsolute()) {
+ if (treatAsAbsolute(fileName))
return fileName;
- }
+ const QDirPrivate* d = d_ptr.constData();
QString ret = d->dirEntry.filePath();
if (fileName.isEmpty())
return ret;
@@ -793,13 +805,10 @@ QString QDir::filePath(const QString &fileName) const
*/
QString QDir::absoluteFilePath(const QString &fileName) const
{
- const QDirPrivate* d = d_ptr.constData();
- // Mistrust our own isAbsolutePath() for real files; Q_OS_WIN needs a drive.
- if (fileName.startsWith(QLatin1Char(':')) // i.e. resource path
- ? isAbsolutePath(fileName) : QFileSystemEntry(fileName).isAbsolute()) {
+ if (treatAsAbsolute(fileName))
return fileName;
- }
+ const QDirPrivate* d = d_ptr.constData();
d->resolveAbsoluteEntry();
const QString absoluteDirPath = d->absoluteDirEntry.filePath();
if (fileName.isEmpty())
@@ -2164,9 +2173,10 @@ bool QDir::match(const QString &filter, const QString &fileName)
This method is shared with QUrl, so it doesn't deal with QDir::separator(),
nor does it remove the trailing slash, if any.
*/
-Q_AUTOTEST_EXPORT QString qt_normalizePathSegments(const QString &name, bool allowUncPaths,
- bool *ok = nullptr)
+QString qt_normalizePathSegments(const QString &name, QDirPrivate::PathNormalizations flags, bool *ok)
{
+ const bool allowUncPaths = QDirPrivate::AllowUncPaths & flags;
+ const bool isRemote = QDirPrivate::RemotePath & flags;
const int len = name.length();
if (ok)
@@ -2188,14 +2198,30 @@ Q_AUTOTEST_EXPORT QString qt_normalizePathSegments(const QString &name, bool all
i -= prefixLength;
// replicate trailing slash (i > 0 checks for emptiness of input string p)
- if (i > 0 && p[i] == '/') {
+ // except for remote paths because there can be /../ or /./ ending
+ if (i > 0 && p[i] == '/' && !isRemote) {
out[--used] = '/';
--i;
}
+ auto isDot = [](const ushort *p, int i) {
+ return i > 1 && p[i - 1] == '.' && p[i - 2] == '/';
+ };
+ auto isDotDot = [](const ushort *p, int i) {
+ return i > 2 && p[i - 1] == '.' && p[i - 2] == '.' && p[i - 3] == '/';
+ };
+
while (i >= 0) {
- // remove trailing slashes
+ // copy trailing slashes for remote urls
if (p[i] == '/') {
+ if (isRemote && !up) {
+ if (isDot(p, i)) {
+ i -= 2;
+ continue;
+ }
+ out[--used] = p[i];
+ }
+
--i;
continue;
}
@@ -2207,10 +2233,17 @@ Q_AUTOTEST_EXPORT QString qt_normalizePathSegments(const QString &name, bool all
}
// detect up dir
- if (i >= 1 && p[i] == '.' && p[i-1] == '.'
- && (i == 1 || (i >= 2 && p[i-2] == '/'))) {
+ if (i >= 1 && p[i] == '.' && p[i-1] == '.' && (i < 2 || p[i - 2] == '/')) {
++up;
- i -= 2;
+ i -= i >= 2 ? 3 : 2;
+
+ if (isRemote) {
+ // moving up should consider empty path segments too (/path//../ -> /path/)
+ while (i > 0 && up && p[i] == '/') {
+ --up;
+ --i;
+ }
+ }
continue;
}
@@ -2220,7 +2253,27 @@ Q_AUTOTEST_EXPORT QString qt_normalizePathSegments(const QString &name, bool all
// skip or copy
while (i >= 0) {
- if (p[i] == '/') { // do not copy slashes
+ if (p[i] == '/') {
+ // copy all slashes as is for remote urls if they are not part of /./ or /../
+ if (isRemote && !up) {
+ while (i > 0 && p[i] == '/' && !isDotDot(p, i)) {
+
+ if (isDot(p, i)) {
+ i -= 2;
+ continue;
+ }
+
+ out[--used] = p[i];
+ --i;
+ }
+
+ // in case of /./, jump over
+ if (isDot(p, i))
+ i -= 2;
+
+ break;
+ }
+
--i;
break;
}
@@ -2241,7 +2294,7 @@ Q_AUTOTEST_EXPORT QString qt_normalizePathSegments(const QString &name, bool all
*ok = prefixLength == 0 || up == 0;
// add remaining '..'
- while (up) {
+ while (up && !isRemote) {
if (used != len && out[used] != '/') // is not empty and there isn't already a '/'
out[--used] = '/';
out[--used] = '.';
@@ -2287,7 +2340,7 @@ static QString qt_cleanPath(const QString &path, bool *ok)
if (dir_separator != QLatin1Char('/'))
name.replace(dir_separator, QLatin1Char('/'));
- QString ret = qt_normalizePathSegments(name, OSSupportsUncPaths, ok);
+ QString ret = qt_normalizePathSegments(name, OSSupportsUncPaths ? QDirPrivate::AllowUncPaths : QDirPrivate::DefaultNormalization, ok);
// Strip away last slash except for root directories
if (ret.length() > 1 && ret.endsWith(QLatin1Char('/'))) {
diff --git a/src/corelib/io/qdir_p.h b/src/corelib/io/qdir_p.h
index 85d915223c..0f3ab7f899 100644
--- a/src/corelib/io/qdir_p.h
+++ b/src/corelib/io/qdir_p.h
@@ -59,6 +59,14 @@ QT_BEGIN_NAMESPACE
class QDirPrivate : public QSharedData
{
public:
+ enum PathNormalization {
+ DefaultNormalization = 0x00,
+ AllowUncPaths = 0x01,
+ RemotePath = 0x02
+ };
+ Q_DECLARE_FLAGS(PathNormalizations, PathNormalization)
+ Q_FLAGS(PathNormalizations)
+
explicit QDirPrivate(const QString &path, const QStringList &nameFilters_ = QStringList(),
QDir::SortFlags sort_ = QDir::SortFlags(QDir::Name | QDir::IgnoreCase),
QDir::Filters filters_ = QDir::AllEntries);
@@ -97,6 +105,10 @@ public:
mutable QFileSystemMetaData metaData;
};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QDirPrivate::PathNormalizations)
+
+Q_AUTOTEST_EXPORT QString qt_normalizePathSegments(const QString &name, QDirPrivate::PathNormalizations flags, bool *ok = nullptr);
+
QT_END_NAMESPACE
#endif
diff --git a/src/corelib/io/qfilesystemengine_win.cpp b/src/corelib/io/qfilesystemengine_win.cpp
index a796fd005a..3f4b46573b 100644
--- a/src/corelib/io/qfilesystemengine_win.cpp
+++ b/src/corelib/io/qfilesystemengine_win.cpp
@@ -1038,8 +1038,7 @@ bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemM
if (what & QFileSystemMetaData::Permissions)
fillPermissions(fname, data, what);
- if ((what & QFileSystemMetaData::LinkType)
- && data.missingFlags(QFileSystemMetaData::LinkType)) {
+ if (what & QFileSystemMetaData::LinkType) {
data.knownFlagsMask |= QFileSystemMetaData::LinkType;
if (data.fileAttribute_ & FILE_ATTRIBUTE_REPARSE_POINT) {
WIN32_FIND_DATA findData;
diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp
index b324df53b2..6d82981fd6 100644
--- a/src/corelib/io/qurl.cpp
+++ b/src/corelib/io/qurl.cpp
@@ -253,7 +253,8 @@
and contains no query or fragment, a local file path is returned.
\value StripTrailingSlash The trailing slash is removed from the path, if one is present.
\value NormalizePathSegments Modifies the path to remove redundant directory separators,
- and to resolve "."s and ".."s (as far as possible).
+ and to resolve "."s and ".."s (as far as possible). For non-local paths, adjacent
+ slashes are preserved.
Note that the case folding rules in \l{RFC 3491}{Nameprep}, which QUrl
conforms to, require host names to always be converted to lower case,
@@ -419,10 +420,9 @@
#endif
#include "private/qipaddress_p.h"
#include "qurlquery.h"
+#include "private/qdir_p.h"
QT_BEGIN_NAMESPACE
-extern QString qt_normalizePathSegments(const QString &name, bool allowUncPaths,
- bool *ok = nullptr); // qdir.cpp
inline static bool isHex(char c)
{
@@ -930,7 +930,7 @@ inline void QUrlPrivate::appendPath(QString &appendTo, QUrl::FormattingOptions o
{
QString thePath = path;
if (options & QUrl::NormalizePathSegments) {
- thePath = qt_normalizePathSegments(path, false);
+ thePath = qt_normalizePathSegments(path, isLocalFile() ? QDirPrivate::DefaultNormalization : QDirPrivate::RemotePath);
}
QStringRef thePathRef(&thePath);
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp
index 600a801fe0..73b8c33efe 100644
--- a/src/corelib/kernel/qobject.cpp
+++ b/src/corelib/kernel/qobject.cpp
@@ -4342,6 +4342,12 @@ QDebug operator<<(QDebug dbg, const QObject *o)
in a QVariant, you can convert them to strings. Likewise, passing them to
QDebug will print out their names.
+ Mind that the enum values are stored as signed \c int in the meta object system.
+ Registering enumerations with values outside the range of values valid for \c int
+ will lead to overflows and potentially undefined behavior when accessing them through
+ the meta object system. QML, for example, does access registered enumerations through
+ the meta object system.
+
\sa {Qt's Property System}
*/
@@ -4393,6 +4399,12 @@ QDebug operator<<(QDebug dbg, const QObject *o)
used in a QVariant, you can convert them to strings. Likewise, passing them
to QDebug will print out their names.
+ Mind that the enum values are stored as signed \c int in the meta object system.
+ Registering enumerations with values outside the range of values valid for \c int
+ will lead to overflows and potentially undefined behavior when accessing them through
+ the meta object system. QML, for example, does access registered enumerations through
+ the meta object system.
+
\sa {Qt's Property System}
*/
@@ -4450,7 +4462,7 @@ QDebug operator<<(QDebug dbg, const QObject *o)
macro, it must appear in the private section of a class definition.
Q_GADGETs can have Q_ENUM, Q_PROPERTY and Q_INVOKABLE, but they cannot have
- signals or slots
+ signals or slots.
Q_GADGET makes a class member, \c{staticMetaObject}, available.
\c{staticMetaObject} is of type QMetaObject and provides access to the
diff --git a/src/corelib/kernel/qtestsupport_core.cpp b/src/corelib/kernel/qtestsupport_core.cpp
index e00ad75fef..c54b933f94 100644
--- a/src/corelib/kernel/qtestsupport_core.cpp
+++ b/src/corelib/kernel/qtestsupport_core.cpp
@@ -59,8 +59,7 @@ Q_CORE_EXPORT void QTestPrivate::qSleep(int ms)
#endif
}
-/*! \fn template <typename Functor> bool qWaitFor(Functor predicate, int timeout)
- \relates QTest
+/*! \fn template <typename Functor> bool QTest::qWaitFor(Functor predicate, int timeout)
Waits for \a timeout milliseconds or until the \a predicate returns true.
@@ -77,8 +76,7 @@ Q_CORE_EXPORT void QTestPrivate::qSleep(int ms)
*/
-/*! \fn void qWait(int ms)
- \relates QTest
+/*! \fn void QTest::qWait(int ms)
Waits for \a ms milliseconds. While waiting, events will be processed and
your test will stay responsive to user interface events or network communication.
diff --git a/src/corelib/tools/qsimd.cpp b/src/corelib/tools/qsimd.cpp
index 4a44cf02e0..1508681021 100644
--- a/src/corelib/tools/qsimd.cpp
+++ b/src/corelib/tools/qsimd.cpp
@@ -293,7 +293,7 @@ static void cpuidFeatures07_00(uint &ebx, uint &ecx, uint &edx)
#endif
}
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !(defined(Q_CC_GNU) || defined(Q_CC_GHS))
// fallback overload in case this intrinsic does not exist: unsigned __int64 _xgetbv(unsigned int);
inline quint64 _xgetbv(__int64) { return 0; }
#endif
diff --git a/src/gui/configure.json b/src/gui/configure.json
index 39c0232c0b..70d0817791 100644
--- a/src/gui/configure.json
+++ b/src/gui/configure.json
@@ -239,7 +239,8 @@
"sources": [
{ "type": "pkgConfig", "args": "freetype2" },
{ "type": "freetype", "libs": "-lfreetype", "condition": "!config.wasm" },
- { "libs": "-s USE_FREETYPE=1", "condition": "config.wasm" }
+ { "libs": "-s USE_FREETYPE=1", "condition": "config.wasm" },
+ { "libs": "-lfreetype" }
],
"use": [
{ "lib": "zlib", "condition": "features.system-zlib" }
@@ -1436,7 +1437,7 @@
},
"eglfs_x11": {
"label": "EGLFS X11",
- "condition": "features.eglfs && features.xcb && features.xcb-xlib && features.egl_x11",
+ "condition": "features.eglfs && features.xcb-xlib && features.egl_x11",
"output": [ "privateFeature" ]
},
"gif": {
@@ -1569,7 +1570,6 @@
},
"xcb-xlib": {
"label": "XCB Xlib",
- "emitIf": "features.xcb",
"condition": "features.xlib && libs.xcb_xlib",
"output": [ "privateFeature" ]
},
diff --git a/src/gui/doc/qtgui.qdocconf b/src/gui/doc/qtgui.qdocconf
index e546c817a7..b8b8a00cd6 100644
--- a/src/gui/doc/qtgui.qdocconf
+++ b/src/gui/doc/qtgui.qdocconf
@@ -54,7 +54,10 @@ exampledirs += ../../../examples/gui \
imagedirs += images \
../../../examples/gui/doc/images \
- ../../../doc/src/images \
+ ../../../doc/src/images
+
+# Included in qttestlib.qdocconf instead
+excludefiles += ../kernel/qtestsupport_gui.cpp
manifestmeta.highlighted.names = "QtGui/Analog Clock Window Example"
diff --git a/src/gui/image/qbmphandler.cpp b/src/gui/image/qbmphandler.cpp
index 5dff4ab0ac..7257853c3e 100644
--- a/src/gui/image/qbmphandler.cpp
+++ b/src/gui/image/qbmphandler.cpp
@@ -188,7 +188,7 @@ static bool read_dib_infoheader(QDataStream &s, BMP_INFOHDR &bi)
if (!(comp == BMP_RGB || (nbits == 4 && comp == BMP_RLE4) ||
(nbits == 8 && comp == BMP_RLE8) || ((nbits == 16 || nbits == 32) && comp == BMP_BITFIELDS)))
return false; // weird compression type
- if (bi.biWidth < 0 || quint64(bi.biWidth) * qAbs(bi.biHeight) > 16384 * 16384)
+ if (bi.biWidth <= 0 || !bi.biHeight || quint64(bi.biWidth) * qAbs(bi.biHeight) > 16384 * 16384)
return false;
return true;
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp
index c02a396b00..16de045cf0 100644
--- a/src/gui/image/qimage.cpp
+++ b/src/gui/image/qimage.cpp
@@ -3570,7 +3570,7 @@ void QImage::rgbSwapped_inplace()
The loader attempts to read the image using the specified \a format, e.g.,
PNG or JPG. If \a format is not specified (which is the default), it is
auto-detected based on the file's suffix and header. For details, see
- {QImageReader::setAutoDetectImageFormat()}{QImageReader}.
+ QImageReader::setAutoDetectImageFormat().
The file name can either refer to an actual file on disk or to one
of the application's embedded resources. See the
diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp
index a455aa639d..22e46e0851 100644
--- a/src/gui/kernel/qhighdpiscaling.cpp
+++ b/src/gui/kernel/qhighdpiscaling.cpp
@@ -313,9 +313,12 @@ void QHighDpiScaling::updateHighDpiScaling()
}
m_active = m_globalScalingActive || m_screenFactorSet || m_pixelDensityScalingActive;
- QPlatformScreen *primaryScreen = QGuiApplication::primaryScreen()->handle();
- qreal sf = screenSubfactor(primaryScreen);
- QDpi primaryDpi = primaryScreen->logicalDpi();
+ QScreen *primaryScreen = QGuiApplication::primaryScreen();
+ if (!primaryScreen)
+ return;
+ QPlatformScreen *platformScreen = primaryScreen->handle();
+ qreal sf = screenSubfactor(platformScreen);
+ QDpi primaryDpi = platformScreen->logicalDpi();
m_logicalDpi = QDpi(primaryDpi.first / sf, primaryDpi.second / sf);
}
diff --git a/src/gui/kernel/qplatformwindow.h b/src/gui/kernel/qplatformwindow.h
index 995ccad70e..4d48cc2f13 100644
--- a/src/gui/kernel/qplatformwindow.h
+++ b/src/gui/kernel/qplatformwindow.h
@@ -110,7 +110,7 @@ public:
virtual bool isActive() const;
virtual bool isAncestorOf(const QPlatformWindow *child) const;
virtual bool isEmbedded() const;
- virtual bool isForeignWindow() const { return window()->type() == Qt::ForeignWindow; };
+ virtual bool isForeignWindow() const { return false; };
virtual QPoint mapToGlobal(const QPoint &pos) const;
virtual QPoint mapFromGlobal(const QPoint &pos) const;
diff --git a/src/gui/kernel/qtestsupport_gui.cpp b/src/gui/kernel/qtestsupport_gui.cpp
index 56e0eb52b3..8a6f662274 100644
--- a/src/gui/kernel/qtestsupport_gui.cpp
+++ b/src/gui/kernel/qtestsupport_gui.cpp
@@ -44,23 +44,21 @@
QT_BEGIN_NAMESPACE
-/*! \fn bool qWaitForWindowActive(QWindow *window, int timeout)
- \relates QTest
+/*!
\since 5.0
Waits for \a timeout milliseconds or until the \a window is active.
Returns \c true if \c window is active within \a timeout milliseconds, otherwise returns \c false.
- \sa QTest::qWaitForWindowExposed(), QWindow::isActive()
+ \sa qWaitForWindowExposed(), QWindow::isActive()
*/
Q_GUI_EXPORT bool QTest::qWaitForWindowActive(QWindow *window, int timeout)
{
return QTest::qWaitFor([&]() { return window->isActive(); }, timeout);
}
-/*! \fn bool qWaitForWindowExposed(QWindow *window, int timeout)
- \relates QTest
+/*!
\since 5.0
Waits for \a timeout milliseconds or until the \a window is exposed.
@@ -73,7 +71,7 @@ Q_GUI_EXPORT bool QTest::qWaitForWindowActive(QWindow *window, int timeout)
area is completely covered by other windows, or if the window is otherwise not visible. This function
will then time out when waiting for such a window.
- \sa QTest::qWaitForWindowActive(), QWindow::isExposed()
+ \sa qWaitForWindowActive(), QWindow::isExposed()
*/
Q_GUI_EXPORT bool QTest::qWaitForWindowExposed(QWindow *window, int timeout)
{
diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp
index c85f6a118d..0b833cfbc7 100644
--- a/src/gui/kernel/qwindow.cpp
+++ b/src/gui/kernel/qwindow.cpp
@@ -919,8 +919,7 @@ void QWindow::setFlag(Qt::WindowType flag, bool on)
*/
Qt::WindowType QWindow::type() const
{
- Q_D(const QWindow);
- return static_cast<Qt::WindowType>(int(d->windowFlags & Qt::WindowType_Mask));
+ return static_cast<Qt::WindowType>(int(flags() & Qt::WindowType_Mask));
}
/*!
diff --git a/src/gui/painting/qdrawhelper_sse4.cpp b/src/gui/painting/qdrawhelper_sse4.cpp
index e4b93e8511..1da3b75ade 100644
--- a/src/gui/painting/qdrawhelper_sse4.cpp
+++ b/src/gui/painting/qdrawhelper_sse4.cpp
@@ -225,7 +225,7 @@ template<bool RGBA>
static inline void convertARGBFromRGBA64PM_sse4(uint *buffer, const QRgba64 *src, int count)
{
int i = 0;
- const __m128i alphaMask = _mm_set1_epi64x(Q_UINT64_C(0xffff) << 48);
+ const __m128i alphaMask = _mm_set1_epi64x(qint64(Q_UINT64_C(0xffff) << 48));
const __m128i alphaMask32 = _mm_set1_epi32(0xff000000);
const __m128i rgbaMask = _mm_setr_epi8(2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15);
const __m128i zero = _mm_setzero_si128();
diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp
index 83c7ba1add..b23cc903b1 100644
--- a/src/gui/painting/qpdf.cpp
+++ b/src/gui/painting/qpdf.cpp
@@ -1954,13 +1954,14 @@ void QPdfEnginePrivate::writePage()
"/Contents %d 0 R\n"
"/Resources %d 0 R\n"
"/Annots %d 0 R\n"
- "/MediaBox [0 0 %f %f]\n",
+ "/MediaBox [0 0 %s %s]\n",
pageRoot, pageStream, resources, annots,
// make sure we use the pagesize from when we started the page, since the user may have changed it
- currentPage->pageSize.width() / userUnit, currentPage->pageSize.height() / userUnit);
+ QByteArray::number(currentPage->pageSize.width() / userUnit, 'f').constData(),
+ QByteArray::number(currentPage->pageSize.height() / userUnit, 'f').constData());
if (pdfVersion >= QPdfEngine::Version_1_6)
- xprintf("/UserUnit %f\n", userUnit);
+ xprintf("/UserUnit %s\n", QByteArray::number(userUnit, 'f').constData());
xprintf(">>\n"
"endobj\n");
diff --git a/src/gui/painting/qplatformbackingstore.cpp b/src/gui/painting/qplatformbackingstore.cpp
index 3081a4b1b6..afb4613ba5 100644
--- a/src/gui/painting/qplatformbackingstore.cpp
+++ b/src/gui/painting/qplatformbackingstore.cpp
@@ -411,6 +411,7 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion &regi
} else if (!region.isEmpty()){
funcs->glBindTexture(GL_TEXTURE_2D, d_ptr->textureId);
QPlatformGraphicsBufferHelper::lockAndBindToTexture(graphicsBuffer, &d_ptr->needsSwizzle, &d_ptr->premultiplied);
+ graphicsBuffer->unlock();
}
if (graphicsBuffer->origin() == QPlatformGraphicsBuffer::OriginBottomLeft)
diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp
index bdb5592e9e..506df0664d 100644
--- a/src/gui/text/qtextengine.cpp
+++ b/src/gui/text/qtextengine.cpp
@@ -1972,14 +1972,18 @@ void QTextEngine::shape(int item) const
}
// fix log clusters to point to the previous glyph, as the object doesn't have a glyph of it's own.
// This is required so that all entries in the array get initialized and are ordered correctly.
- ushort *lc = logClusters(&li);
- *lc = item ? lc[-1] : 0;
+ if (layoutData->logClustersPtr) {
+ ushort *lc = logClusters(&li);
+ *lc = (lc != layoutData->logClustersPtr) ? lc[-1] : 0;
+ }
} else if (li.analysis.flags == QScriptAnalysis::Tab) {
// set up at least the ascent/descent/leading of the script item for the tab
fontEngine(li, &li.ascent, &li.descent, &li.leading);
// see the comment above
- ushort *lc = logClusters(&li);
- *lc = item ? lc[-1] : 0;
+ if (layoutData->logClustersPtr) {
+ ushort *lc = logClusters(&li);
+ *lc = (lc != layoutData->logClustersPtr) ? lc[-1] : 0;
+ }
} else {
shapeText(item);
}
diff --git a/src/network/kernel/qnetworkinterface_unix.cpp b/src/network/kernel/qnetworkinterface_unix.cpp
index 8f290e5107..c28c5ea9e6 100644
--- a/src/network/kernel/qnetworkinterface_unix.cpp
+++ b/src/network/kernel/qnetworkinterface_unix.cpp
@@ -464,8 +464,10 @@ static QNetworkInterface::InterfaceType probeIfType(int socket, int iftype, stru
case IFM_ETHER:
return QNetworkInterface::Ethernet;
+#ifdef IFM_FDDI
case IFM_FDDI:
return QNetworkInterface::Fddi;
+#endif
case IFM_IEEE80211:
return QNetworkInterface::Ieee80211;
diff --git a/src/network/ssl/qpassworddigestor.cpp b/src/network/ssl/qpassworddigestor.cpp
index 127d94e849..706fa1de05 100644
--- a/src/network/ssl/qpassworddigestor.cpp
+++ b/src/network/ssl/qpassworddigestor.cpp
@@ -75,7 +75,7 @@ namespace QPasswordDigestor {
\a salt must always be 8 bytes long!
\note This function is provided for use with legacy applications and all
- new applications are recommended to use \l {pbkdf2} {PBKDF2}.
+ new applications are recommended to use \l {deriveKeyPbkdf2} {PBKDF2}.
\sa deriveKeyPbkdf2, QCryptographicHash, QCryptographicHash::hashLength
*/
diff --git a/src/platformheaders/eglfsfunctions/qeglfsfunctions.h b/src/platformheaders/eglfsfunctions/qeglfsfunctions.h
index 7165c3cff4..869fe7ce3f 100644
--- a/src/platformheaders/eglfsfunctions/qeglfsfunctions.h
+++ b/src/platformheaders/eglfsfunctions/qeglfsfunctions.h
@@ -49,7 +49,9 @@ class QEglFSFunctions
{
public:
typedef void (*LoadKeymapType)(const QString &filename);
+ typedef void (*SwitchLangType)();
static QByteArray loadKeymapTypeIdentifier() { return QByteArrayLiteral("EglFSLoadKeymap"); }
+ static QByteArray switchLangTypeIdentifier() { return QByteArrayLiteral("EglFSSwitchLang"); }
static void loadKeymap(const QString &filename)
{
@@ -58,6 +60,13 @@ public:
func(filename);
}
+ static void switchLang()
+ {
+ SwitchLangType func = reinterpret_cast<SwitchLangType>(QGuiApplication::platformFunction(switchLangTypeIdentifier()));
+ if (func)
+ func();
+ }
+
typedef int (*Vsp2AddLayerType)(const QScreen *screen, int dmabufFd, const QSize &size, const QPoint &position, uint drmPixelFormat, uint bytesPerLine);
static QByteArray vsp2AddLayerTypeIdentifier() { return QByteArrayLiteral("EglFSVsp2AddLayer"); }
diff --git a/src/platformheaders/eglfsfunctions/qeglfsfunctions.qdoc b/src/platformheaders/eglfsfunctions/qeglfsfunctions.qdoc
index 0bee9190cf..a5f37f8377 100644
--- a/src/platformheaders/eglfsfunctions/qeglfsfunctions.qdoc
+++ b/src/platformheaders/eglfsfunctions/qeglfsfunctions.qdoc
@@ -67,6 +67,21 @@
*/
/*!
+ \fn void QEglFSFunctions::switchLang()
+
+ Switches between English and other language when the keymap is loaded.
+ Usually the keymap contains two languages: English and national. When
+ you load the keymap, English is selected by default. This function allows
+ to switch between these languages.
+
+ \note This is functional only when the evdev keyboard support code is
+ compiled in to the platform plugin. When using external generic plugins via
+ the \c{-plugin} argument, or when the environment variable
+ \c{QT_QPA_EGLFS_DISABLE_INPUT} is set or when building Qt without evdev
+ support, this function will have no effect.
+*/
+
+/*!
\fn int QEglFSFunctions::vsp2AddLayer(const QScreen *screen, int dmabufFd, const QSize &size, const QPoint &position, uint drmPixelFormat, uint bytesPerLine)
\internal
\preliminary
diff --git a/src/platformheaders/linuxfbfunctions/linuxfbfunctions.pri b/src/platformheaders/linuxfbfunctions/linuxfbfunctions.pri
new file mode 100644
index 0000000000..8a092c3049
--- /dev/null
+++ b/src/platformheaders/linuxfbfunctions/linuxfbfunctions.pri
@@ -0,0 +1 @@
+HEADERS += $$PWD/qlinuxfbfunctions.h
diff --git a/src/platformheaders/linuxfbfunctions/qlinuxfbfunctions.h b/src/platformheaders/linuxfbfunctions/qlinuxfbfunctions.h
new file mode 100644
index 0000000000..94c4c24270
--- /dev/null
+++ b/src/platformheaders/linuxfbfunctions/qlinuxfbfunctions.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLINUXFBFUNCTIONS_H
+#define QLINUXFBFUNCTIONS_H
+
+#include <QtCore/QByteArray>
+#include <QtGui/QGuiApplication>
+
+QT_BEGIN_NAMESPACE
+
+class QLinuxFbFunctions
+{
+public:
+ typedef void (*LoadKeymapType)(const QString &filename);
+ typedef void (*SwitchLangType)();
+ static QByteArray loadKeymapTypeIdentifier() { return QByteArrayLiteral("LinuxFbLoadKeymap"); }
+ static QByteArray switchLangTypeIdentifier() { return QByteArrayLiteral("LinuxFbSwitchLang"); }
+
+ static void loadKeymap(const QString &filename)
+ {
+ LoadKeymapType func = reinterpret_cast<LoadKeymapType>(QGuiApplication::platformFunction(loadKeymapTypeIdentifier()));
+ if (func)
+ func(filename);
+ }
+
+ static void switchLang()
+ {
+ SwitchLangType func = reinterpret_cast<SwitchLangType>(QGuiApplication::platformFunction(switchLangTypeIdentifier()));
+ if (func)
+ func();
+ }
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QLINUXFBFUNCTIONS_H
diff --git a/src/platformheaders/linuxfbfunctions/qlinuxfbfunctions.qdoc b/src/platformheaders/linuxfbfunctions/qlinuxfbfunctions.qdoc
new file mode 100644
index 0000000000..939aa76946
--- /dev/null
+++ b/src/platformheaders/linuxfbfunctions/qlinuxfbfunctions.qdoc
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QLinuxFbFunctions
+ \inmodule QtPlatformHeaders
+
+ \brief The QLinuxFbFunctions class is an inline class containing
+ platform-specific functionality for the linuxfb platform plugin that is
+ typically used on systems running Embedded Linux or Android.
+
+ \note There is no binary compatibility guarantee for this class,
+ meaning that an application using it is only guaranteed to work with the Qt
+ version it was developed against.
+*/
+
+/*!
+ \typedef QLinuxFbFunctions::LoadKeymapType
+
+ Function type for loadKeymap.
+*/
+
+/*!
+ \fn QByteArray QLinuxFbFunctions::loadKeymapTypeIdentifier()
+
+ \return the identifier that can be passed to
+ QGuiApplication::platformFunction() to query the entry point for the
+ loadKeymap function implementation.
+*/
+
+/*!
+ \fn void QLinuxFbFunctions::loadKeymap(const QString &filename)
+
+ Loads and switches to the keymap from \a filename. When \a filename is
+ empty, the default keymap, which is either the built-on one or the keymap
+ given in the plugin specification, is restored.
+
+ \note This is functional only when the evdev keyboard support code is
+ compiled in to the platform plugin. When using external generic plugins via
+ the \c{-plugin} argument, or when the environment variable
+ \c{QT_QPA_FB_DISABLE_INPUT} is set or when building Qt without evdev
+ support, this function will have no effect.
+*/
+
+/*!
+ \fn void QLinuxFbFunctions::switchLang()
+
+ Switches between English and other language when the keymap is loaded.
+ Usually the keymap contains two languages: English and national. When
+ you load the keymap, English is selected by default. This function allows
+ to switch between these languages.
+
+ \note This is functional only when the evdev keyboard support code is
+ compiled in to the platform plugin. When using external generic plugins via
+ the \c{-plugin} argument, or when the environment variable
+ \c{QT_QPA_FB_DISABLE_INPUT} is set or when building Qt without evdev
+ support, this function will have no effect.
+*/
diff --git a/src/platformheaders/platformheaders.pro b/src/platformheaders/platformheaders.pro
index caaa67a486..392acc54f0 100644
--- a/src/platformheaders/platformheaders.pro
+++ b/src/platformheaders/platformheaders.pro
@@ -10,6 +10,7 @@ include(windowsfunctions/windowsfunctions.pri)
include(helper/helper.pri)
include(cocoafunctions/cocoafunctions.pri)
include(waylandfunctions/waylandfunctions.pri)
+include(linuxfbfunctions/linuxfbfunctions.pri)
QMAKE_DOCS = $$PWD/doc/qtplatformheaders.qdocconf
diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp
index b21d5d9ef5..ad134a825f 100644
--- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp
+++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp
@@ -76,7 +76,7 @@ void QFdContainer::reset() Q_DECL_NOTHROW
QEvdevKeyboardHandler::QEvdevKeyboardHandler(const QString &device, QFdContainer &fd, bool disableZap, bool enableCompose, const QString &keymapFile)
: m_device(device), m_fd(fd.release()), m_notify(nullptr),
m_modifiers(0), m_composing(0), m_dead_unicode(0xffff),
- m_no_zap(disableZap), m_do_compose(enableCompose),
+ m_langLock(0), m_no_zap(disableZap), m_do_compose(enableCompose),
m_keymap(0), m_keymap_size(0), m_keycompose(0), m_keycompose_size(0)
{
qCDebug(qLcEvdevKey) << "Create keyboard handler with for device" << device;
@@ -253,6 +253,8 @@ QEvdevKeyboardHandler::KeycodeAction QEvdevKeyboardHandler::processKeycode(quint
quint8 testmods = m_modifiers;
if (m_locks[0] /*CapsLock*/ && (m->flags & QEvdevKeyboardMap::IsLetter))
testmods ^= QEvdevKeyboardMap::ModShift;
+ if (m_langLock)
+ testmods ^= QEvdevKeyboardMap::ModAltGr;
if (m->modifiers == testmods)
map_withmod = m;
}
@@ -509,6 +511,8 @@ void QEvdevKeyboardHandler::unloadKeymap()
m_locks[2] = 1;
qCDebug(qLcEvdevKey, "numlock=%d , capslock=%d, scrolllock=%d", m_locks[1], m_locks[0], m_locks[2]);
}
+
+ m_langLock = 0;
}
bool QEvdevKeyboardHandler::loadKeymap(const QString &file)
@@ -570,4 +574,9 @@ bool QEvdevKeyboardHandler::loadKeymap(const QString &file)
return true;
}
+void QEvdevKeyboardHandler::switchLang()
+{
+ m_langLock ^= 1;
+}
+
QT_END_NAMESPACE
diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler_p.h b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler_p.h
index c5821ae3ac..21e6d055a0 100644
--- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler_p.h
+++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler_p.h
@@ -192,6 +192,8 @@ public:
void readKeycode();
KeycodeAction processKeycode(quint16 keycode, bool pressed, bool autorepeat);
+ void switchLang();
+
private:
void processKeyEvent(int nativecode, int unicode, int qtcode,
Qt::KeyboardModifiers modifiers, bool isPress, bool autoRepeat);
@@ -206,6 +208,7 @@ private:
quint8 m_locks[3];
int m_composing;
quint16 m_dead_unicode;
+ quint8 m_langLock;
bool m_no_zap;
bool m_do_compose;
diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp
index 85e6a80879..e1659bc0d9 100644
--- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp
+++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp
@@ -153,4 +153,10 @@ void QEvdevKeyboardManager::loadKeymap(const QString &file)
}
}
+void QEvdevKeyboardManager::switchLang()
+{
+ foreach (QEvdevKeyboardHandler *handler, m_keyboards)
+ handler->switchLang();
+}
+
QT_END_NAMESPACE
diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h
index 27ea7e468e..326e438a7c 100644
--- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h
+++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h
@@ -68,6 +68,7 @@ public:
~QEvdevKeyboardManager();
void loadKeymap(const QString &file);
+ void switchLang();
void addKeyboard(const QString &deviceNode = QString());
void removeKeyboard(const QString &deviceNode);
diff --git a/src/plugins/bearer/networkmanager/qnetworkmanagerservice.h b/src/plugins/bearer/networkmanager/qnetworkmanagerservice.h
index 65e618d15f..c879083faf 100644
--- a/src/plugins/bearer/networkmanager/qnetworkmanagerservice.h
+++ b/src/plugins/bearer/networkmanager/qnetworkmanagerservice.h
@@ -243,7 +243,7 @@ public:
// bool setConnections();
Q_SIGNALS:
- void propertiesChanged(QMap <QString,QVariant>);
+ void propertiesChanged(QMap<QString,QVariant>);
void propertiesReady();
private Q_SLOTS:
diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp
index 7b3546f9bb..cdc52e1cb4 100644
--- a/src/plugins/platforms/android/qandroidinputcontext.cpp
+++ b/src/plugins/platforms/android/qandroidinputcontext.cpp
@@ -99,13 +99,12 @@ static jfieldID m_selectionStartFieldID = 0;
static jfieldID m_startOffsetFieldID = 0;
static jfieldID m_textFieldID = 0;
-Q_DECLARE_METATYPE(std::function<void()>)
-
static void runOnQtThread(const std::function<void()> &func)
{
- const bool block = QGuiApplication::applicationState() >= Qt::ApplicationInactive;
- QMetaObject::invokeMethod(m_androidInputContext, "safeCall",
- block ? Qt::BlockingQueuedConnection : Qt::QueuedConnection, Q_ARG(std::function<void()>, func));
+ AndroidDeadlockProtector protector;
+ if (!protector.acquire())
+ return;
+ QMetaObject::invokeMethod(m_androidInputContext, "safeCall", Qt::BlockingQueuedConnection, Q_ARG(std::function<void()>, func));
}
static jboolean beginBatchEdit(JNIEnv */*env*/, jobject /*thiz*/)
@@ -116,7 +115,7 @@ static jboolean beginBatchEdit(JNIEnv */*env*/, jobject /*thiz*/)
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
qDebug("@@@ BEGINBATCH");
#endif
- jboolean res;
+ jboolean res = JNI_FALSE;
runOnQtThread([&res]{res = m_androidInputContext->beginBatchEdit();});
return res;
}
@@ -130,7 +129,7 @@ static jboolean endBatchEdit(JNIEnv */*env*/, jobject /*thiz*/)
qDebug("@@@ ENDBATCH");
#endif
- jboolean res;
+ jboolean res = JNI_FALSE;
runOnQtThread([&res]{res = m_androidInputContext->endBatchEdit();});
return res;
}
@@ -149,7 +148,7 @@ static jboolean commitText(JNIEnv *env, jobject /*thiz*/, jstring text, jint new
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
qDebug() << "@@@ COMMIT" << str << newCursorPosition;
#endif
- jboolean res;
+ jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->commitText(str, newCursorPosition);});
return res;
}
@@ -162,7 +161,7 @@ static jboolean deleteSurroundingText(JNIEnv */*env*/, jobject /*thiz*/, jint le
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
qDebug() << "@@@ DELETE" << leftLength << rightLength;
#endif
- jboolean res;
+ jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->deleteSurroundingText(leftLength, rightLength);});
return res;
}
@@ -175,7 +174,7 @@ static jboolean finishComposingText(JNIEnv */*env*/, jobject /*thiz*/)
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
qDebug("@@@ FINISH");
#endif
- jboolean res;
+ jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->finishComposingText();});
return res;
}
@@ -185,7 +184,7 @@ static jint getCursorCapsMode(JNIEnv */*env*/, jobject /*thiz*/, jint reqModes)
if (!m_androidInputContext)
return 0;
- jboolean res;
+ jint res = 0;
runOnQtThread([&]{res = m_androidInputContext->getCursorCapsMode(reqModes);});
return res;
}
@@ -270,7 +269,7 @@ static jboolean setComposingText(JNIEnv *env, jobject /*thiz*/, jstring text, ji
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
qDebug() << "@@@ SET" << str << newCursorPosition;
#endif
- jboolean res;
+ jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->setComposingText(str, newCursorPosition);});
return res;
}
@@ -283,7 +282,7 @@ static jboolean setComposingRegion(JNIEnv */*env*/, jobject /*thiz*/, jint start
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
qDebug() << "@@@ SETR" << start << end;
#endif
- jboolean res;
+ jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->setComposingRegion(start, end);});
return res;
}
@@ -297,7 +296,7 @@ static jboolean setSelection(JNIEnv */*env*/, jobject /*thiz*/, jint start, jint
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
qDebug() << "@@@ SETSEL" << start << end;
#endif
- jboolean res;
+ jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->setSelection(start, end);});
return res;
@@ -311,7 +310,7 @@ static jboolean selectAll(JNIEnv */*env*/, jobject /*thiz*/)
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
qDebug("@@@ SELALL");
#endif
- jboolean res;
+ jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->selectAll();});
return res;
}
@@ -324,7 +323,7 @@ static jboolean cut(JNIEnv */*env*/, jobject /*thiz*/)
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
qDebug("@@@");
#endif
- jboolean res;
+ jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->cut();});
return res;
}
@@ -337,7 +336,7 @@ static jboolean copy(JNIEnv */*env*/, jobject /*thiz*/)
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
qDebug("@@@");
#endif
- jboolean res;
+ jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->copy();});
return res;
}
@@ -350,7 +349,7 @@ static jboolean copyURL(JNIEnv */*env*/, jobject /*thiz*/)
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
qDebug("@@@");
#endif
- jboolean res;
+ jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->copyURL();});
return res;
}
@@ -363,7 +362,7 @@ static jboolean paste(JNIEnv */*env*/, jobject /*thiz*/)
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
qDebug("@@@ PASTE");
#endif
- jboolean res;
+ jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->paste();});
return res;
}
@@ -516,7 +515,6 @@ QAndroidInputContext::QAndroidInputContext()
m_handleMode = Hidden;
updateSelectionHandles();
});
- qRegisterMetaType<std::function<void()>>();
}
QAndroidInputContext::~QAndroidInputContext()
diff --git a/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp b/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp
index 72420199e3..8ccb0ef2cd 100644
--- a/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp
+++ b/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp
@@ -429,6 +429,8 @@ QFunctionPointer QEglFSIntegration::platformFunction(const QByteArray &function)
#if QT_CONFIG(evdev)
if (function == QEglFSFunctions::loadKeymapTypeIdentifier())
return QFunctionPointer(loadKeymapStatic);
+ else if (function == QEglFSFunctions::switchLangTypeIdentifier())
+ return QFunctionPointer(switchLangStatic);
#endif
return qt_egl_device_integration()->platformFunction(function);
@@ -447,6 +449,17 @@ void QEglFSIntegration::loadKeymapStatic(const QString &filename)
#endif
}
+void QEglFSIntegration::switchLangStatic()
+{
+#if QT_CONFIG(evdev)
+ QEglFSIntegration *self = static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration());
+ if (self->m_kbdMgr)
+ self->m_kbdMgr->switchLang();
+ else
+ qWarning("QEglFSIntegration: Cannot switch language, no keyboard handler found");
+#endif
+}
+
void QEglFSIntegration::createInputHandlers()
{
#if QT_CONFIG(libinput)
diff --git a/src/plugins/platforms/eglfs/api/qeglfsintegration_p.h b/src/plugins/platforms/eglfs/api/qeglfsintegration_p.h
index c288876678..4b4585d33c 100644
--- a/src/plugins/platforms/eglfs/api/qeglfsintegration_p.h
+++ b/src/plugins/platforms/eglfs/api/qeglfsintegration_p.h
@@ -110,6 +110,7 @@ private:
EGLNativeDisplayType nativeDisplay() const;
void createInputHandlers();
static void loadKeymapStatic(const QString &filename);
+ static void switchLangStatic();
EGLDisplay m_display;
QPlatformInputContext *m_inputContext;
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp
index 402338197d..1e4f4e72c8 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp
@@ -118,6 +118,8 @@ QPlatformCursor *QEglFSKmsGbmIntegration::createCursor(QPlatformScreen *screen)
qCDebug(qLcEglfsKmsDebug, "Using plain OpenGL mouse cursor");
return new QEglFSCursor(screen);
}
+#else
+ Q_UNUSED(screen);
#endif
return nullptr;
}
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp
index a67457a6a5..ab39af6b80 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp
@@ -289,7 +289,9 @@ QPlatformCursor *QEglFSKmsEglDeviceIntegration::createCursor(QPlatformScreen *sc
{
#if QT_CONFIG(opengl)
if (screenConfig()->separateScreens())
- return new QEglFSCursor(screen);
+ return new QEglFSCursor(screen);
+#else
+ Q_UNUSED(screen);
#endif
return nullptr;
}
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv/qeglfsvivintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv/qeglfsvivintegration.cpp
index 763a4a462b..2fc076ad0c 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv/qeglfsvivintegration.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv/qeglfsvivintegration.cpp
@@ -65,7 +65,7 @@ void QEglFSVivIntegration::platformInit()
VivanteInit();
mNativeDisplay = fbGetDisplay();
#else
- mNativeDisplay = fbGetDisplayByIndex(framebufferIndex());
+ mNativeDisplay = static_cast<EGLNativeDisplayType>(fbGetDisplayByIndex(framebufferIndex()));
#endif
fbGetDisplayGeometry(mNativeDisplay, &width, &height);
@@ -88,7 +88,7 @@ EGLNativeWindowType QEglFSVivIntegration::createNativeWindow(QPlatformWindow *wi
Q_UNUSED(window)
Q_UNUSED(format)
- EGLNativeWindowType eglWindow = fbCreateWindow(mNativeDisplay, 0, 0, size.width(), size.height());
+ EGLNativeWindowType eglWindow = static_cast<EGLNativeWindowType>(fbCreateWindow(mNativeDisplay, 0, 0, size.width(), size.height()));
return eglWindow;
}
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.cpp
index 61e2f17766..3bdae239cd 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.cpp
@@ -60,7 +60,7 @@ void QEglFSVivWaylandIntegration::platformInit()
}
mWaylandDisplay = wl_display_create();
- mNativeDisplay = fbGetDisplay(mWaylandDisplay);
+ mNativeDisplay = static_cast<EGLNativeDisplayType>(fbGetDisplay(mWaylandDisplay));
fbGetDisplayGeometry(mNativeDisplay, &width, &height);
mScreenSize.setHeight(height);
mScreenSize.setWidth(width);
@@ -81,7 +81,7 @@ EGLNativeWindowType QEglFSVivWaylandIntegration::createNativeWindow(QPlatformWin
Q_UNUSED(window)
Q_UNUSED(format)
- EGLNativeWindowType eglWindow = fbCreateWindow(mNativeDisplay, 0, 0, size.width(), size.height());
+ EGLNativeWindowType eglWindow = static_cast<EGLNativeWindowType>(fbCreateWindow(mNativeDisplay, 0, 0, size.width(), size.height()));
return eglWindow;
}
diff --git a/src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp b/src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp
index f835dbf6d4..9e38900bcd 100644
--- a/src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp
+++ b/src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp
@@ -69,12 +69,15 @@
#include <QtInputSupport/private/qtslib_p.h>
#endif
+#include <QtPlatformHeaders/qlinuxfbfunctions.h>
+
QT_BEGIN_NAMESPACE
QLinuxFbIntegration::QLinuxFbIntegration(const QStringList &paramList)
: m_primaryScreen(nullptr),
m_fontDb(new QGenericUnixFontDatabase),
- m_services(new QGenericUnixServices)
+ m_services(new QGenericUnixServices),
+ m_kbdMgr(0)
{
#if QT_CONFIG(kms)
if (qEnvironmentVariableIntValue("QT_QPA_FB_DRM") != 0)
@@ -98,8 +101,6 @@ void QLinuxFbIntegration::initialize()
m_inputContext = QPlatformInputContextFactory::create();
- m_nativeInterface.reset(new QPlatformNativeInterface);
-
m_vtHandler.reset(new QFbVtHandler);
if (!qEnvironmentVariableIntValue("QT_QPA_FB_DISABLE_INPUT"))
@@ -163,7 +164,7 @@ void QLinuxFbIntegration::createInputHandlers()
#endif
#if QT_CONFIG(evdev)
- new QEvdevKeyboardManager(QLatin1String("EvdevKeyboard"), QString(), this);
+ m_kbdMgr = new QEvdevKeyboardManager(QLatin1String("EvdevKeyboard"), QString(), this);
new QEvdevMouseManager(QLatin1String("EvdevMouse"), QString(), this);
#if QT_CONFIG(tslib)
if (!useTslib)
@@ -174,7 +175,45 @@ void QLinuxFbIntegration::createInputHandlers()
QPlatformNativeInterface *QLinuxFbIntegration::nativeInterface() const
{
- return m_nativeInterface.data();
+ return const_cast<QLinuxFbIntegration *>(this);
+}
+
+QFunctionPointer QLinuxFbIntegration::platformFunction(const QByteArray &function) const
+{
+#if QT_CONFIG(evdev)
+ if (function == QLinuxFbFunctions::loadKeymapTypeIdentifier())
+ return QFunctionPointer(loadKeymapStatic);
+ else if (function == QLinuxFbFunctions::switchLangTypeIdentifier())
+ return QFunctionPointer(switchLangStatic);
+#else
+ Q_UNUSED(function)
+#endif
+
+ return 0;
+}
+
+void QLinuxFbIntegration::loadKeymapStatic(const QString &filename)
+{
+#if QT_CONFIG(evdev)
+ QLinuxFbIntegration *self = static_cast<QLinuxFbIntegration *>(QGuiApplicationPrivate::platformIntegration());
+ if (self->m_kbdMgr)
+ self->m_kbdMgr->loadKeymap(filename);
+ else
+ qWarning("QLinuxFbIntegration: Cannot load keymap, no keyboard handler found");
+#else
+ Q_UNUSED(filename);
+#endif
+}
+
+void QLinuxFbIntegration::switchLangStatic()
+{
+#if QT_CONFIG(evdev)
+ QLinuxFbIntegration *self = static_cast<QLinuxFbIntegration *>(QGuiApplicationPrivate::platformIntegration());
+ if (self->m_kbdMgr)
+ self->m_kbdMgr->switchLang();
+ else
+ qWarning("QLinuxFbIntegration: Cannot switch language, no keyboard handler found");
+#endif
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/linuxfb/qlinuxfbintegration.h b/src/plugins/platforms/linuxfb/qlinuxfbintegration.h
index 22578bf980..7a871b3812 100644
--- a/src/plugins/platforms/linuxfb/qlinuxfbintegration.h
+++ b/src/plugins/platforms/linuxfb/qlinuxfbintegration.h
@@ -48,6 +48,7 @@ QT_BEGIN_NAMESPACE
class QAbstractEventDispatcher;
class QFbScreen;
class QFbVtHandler;
+class QEvdevKeyboardManager;
class QLinuxFbIntegration : public QPlatformIntegration, public QPlatformNativeInterface
{
@@ -71,15 +72,20 @@ public:
QList<QPlatformScreen *> screens() const;
+ QFunctionPointer platformFunction(const QByteArray &function) const Q_DECL_OVERRIDE;
+
private:
void createInputHandlers();
+ static void loadKeymapStatic(const QString &filename);
+ static void switchLangStatic();
QFbScreen *m_primaryScreen;
QPlatformInputContext *m_inputContext;
QScopedPointer<QPlatformFontDatabase> m_fontDb;
QScopedPointer<QPlatformServices> m_services;
QScopedPointer<QFbVtHandler> m_vtHandler;
- QScopedPointer<QPlatformNativeInterface> m_nativeInterface;
+
+ QEvdevKeyboardManager *m_kbdMgr;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index 1f80ac5872..fee093f675 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -1115,8 +1115,10 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
case QtWindows::DisplayChangedEvent:
if (QWindowsTheme *t = QWindowsTheme::instance())
t->displayChanged();
+ QWindowsWindow::displayChanged();
return d->m_screenManager.handleDisplayChange(wParam, lParam);
case QtWindows::SettingChangedEvent:
+ QWindowsWindow::settingsChanged();
return d->m_screenManager.handleScreenChanges();
default:
break;
diff --git a/src/plugins/platforms/windows/qwindowsglcontext.cpp b/src/plugins/platforms/windows/qwindowsglcontext.cpp
index 851a6c961e..21cb794196 100644
--- a/src/plugins/platforms/windows/qwindowsglcontext.cpp
+++ b/src/plugins/platforms/windows/qwindowsglcontext.cpp
@@ -1350,10 +1350,10 @@ bool QWindowsGLContext::makeCurrent(QPlatformSurface *surface)
// Set the swap interval
if (m_staticContext->wglSwapInternalExt) {
const int interval = surface->format().swapInterval();
- if (interval >= 0 && m_swapInterval != interval) {
+ if (m_swapInterval != interval)
m_swapInterval = interval;
+ if (interval >= 0)
m_staticContext->wglSwapInternalExt(interval);
- }
}
return success;
diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.cpp b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
index 7935d0c36f..e428a8cf63 100644
--- a/src/plugins/platforms/windows/qwindowspointerhandler.cpp
+++ b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
@@ -54,7 +54,6 @@
# include "qwindowsdrag.h"
#endif
-#include <qpa/qwindowsysteminterface.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/qscreen.h>
#include <QtGui/qtouchdevice.h>
@@ -201,6 +200,8 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q
return false;
}
+ m_lastPointerType = pointerType;
+
// Handle non-client pen/touch as generic mouse events for compatibility with QDockWindow.
if ((pointerType == QT_PT_TOUCH || pointerType == QT_PT_PEN) && (et & QtWindows::NonClientEventFlag)) {
POINTER_INFO pointerInfo;
@@ -427,6 +428,15 @@ static bool isValidWheelReceiver(QWindow *candidate)
return false;
}
+static bool isMenuWindow(QWindow *window)
+{
+ if (window)
+ if (QObject *fo = window->focusObject())
+ if (fo->inherits("QMenu"))
+ return true;
+ return false;
+}
+
static QTouchDevice *createTouchDevice()
{
const int digitizers = GetSystemMetrics(SM_DIGITIZER);
@@ -455,6 +465,103 @@ QTouchDevice *QWindowsPointerHandler::ensureTouchDevice()
return m_touchDevice;
}
+void QWindowsPointerHandler::handleCaptureRelease(QWindow *window,
+ QWindow *currentWindowUnderPointer,
+ HWND hwnd,
+ QEvent::Type eventType,
+ Qt::MouseButtons mouseButtons)
+{
+ QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window->handle());
+
+ // Qt expects the platform plugin to capture the mouse on any button press until release.
+ if (!platformWindow->hasMouseCapture() && eventType == QEvent::MouseButtonPress) {
+
+ platformWindow->setMouseGrabEnabled(true);
+ platformWindow->setFlag(QWindowsWindow::AutoMouseCapture);
+ qCDebug(lcQpaEvents) << "Automatic mouse capture " << window;
+
+ // Implement "Click to focus" for native child windows (unless it is a native widget window).
+ if (!window->isTopLevel() && !window->inherits("QWidgetWindow") && QGuiApplication::focusWindow() != window)
+ window->requestActivate();
+
+ } else if (platformWindow->hasMouseCapture()
+ && platformWindow->testFlag(QWindowsWindow::AutoMouseCapture)
+ && eventType == QEvent::MouseButtonRelease
+ && !mouseButtons) {
+
+ platformWindow->setMouseGrabEnabled(false);
+ qCDebug(lcQpaEvents) << "Releasing automatic mouse capture " << window;
+ }
+
+ // Enter new window: track to generate leave event.
+ // If there is an active capture, only track if the current window is capturing,
+ // so we don't get extra leave when cursor leaves the application.
+ if (window != m_currentWindow &&
+ (!platformWindow->hasMouseCapture() || currentWindowUnderPointer == window)) {
+ trackLeave(hwnd);
+ m_currentWindow = window;
+ }
+}
+
+void QWindowsPointerHandler::handleEnterLeave(QWindow *window,
+ QWindow *currentWindowUnderPointer,
+ QPoint globalPos)
+{
+ QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window->handle());
+ const bool hasCapture = platformWindow->hasMouseCapture();
+
+ // No enter or leave events are sent as long as there is an autocapturing window.
+ if (!hasCapture || !platformWindow->testFlag(QWindowsWindow::AutoMouseCapture)) {
+
+ // Leave is needed if:
+ // 1) There is no capture and we move from a window to another window.
+ // Note: Leaving the application entirely is handled in translateMouseEvent(WM_MOUSELEAVE).
+ // 2) There is capture and we move out of the capturing window.
+ // 3) There is a new capture and we were over another window.
+ if ((m_windowUnderPointer && m_windowUnderPointer != currentWindowUnderPointer
+ && (!hasCapture || window == m_windowUnderPointer))
+ || (hasCapture && m_previousCaptureWindow != window && m_windowUnderPointer
+ && m_windowUnderPointer != window)) {
+
+ qCDebug(lcQpaEvents) << "Leaving window " << m_windowUnderPointer;
+ QWindowSystemInterface::handleLeaveEvent(m_windowUnderPointer);
+
+ if (hasCapture && currentWindowUnderPointer != window) {
+ // Clear tracking if capturing and current window is not the capturing window
+ // to avoid leave when mouse actually leaves the application.
+ m_currentWindow = nullptr;
+ // We are not officially in any window, but we need to set some cursor to clear
+ // whatever cursor the left window had, so apply the cursor of the capture window.
+ platformWindow->applyCursor();
+ }
+ }
+
+ // Enter is needed if:
+ // 1) There is no capture and we move to a new window.
+ // 2) There is capture and we move into the capturing window.
+ // 3) The capture just ended and we are over non-capturing window.
+ if ((currentWindowUnderPointer && m_windowUnderPointer != currentWindowUnderPointer
+ && (!hasCapture || currentWindowUnderPointer == window))
+ || (m_previousCaptureWindow && !hasCapture && currentWindowUnderPointer
+ && currentWindowUnderPointer != m_previousCaptureWindow)) {
+
+ QPoint wumLocalPos;
+ if (QWindowsWindow *wumPlatformWindow = QWindowsWindow::windowsWindowOf(currentWindowUnderPointer)) {
+ wumLocalPos = wumPlatformWindow->mapFromGlobal(globalPos);
+ wumPlatformWindow->applyCursor();
+ }
+ qCDebug(lcQpaEvents) << "Entering window " << currentWindowUnderPointer;
+ QWindowSystemInterface::handleEnterEvent(currentWindowUnderPointer, wumLocalPos, globalPos);
+ }
+
+ // We need to track m_windowUnderPointer separately from m_currentWindow, as Windows
+ // mouse tracking will not trigger WM_MOUSELEAVE for leaving window when mouse capture is set.
+ m_windowUnderPointer = currentWindowUnderPointer;
+ }
+
+ m_previousCaptureWindow = hasCapture ? window : nullptr;
+}
+
bool QWindowsPointerHandler::translateMouseTouchPadEvent(QWindow *window, HWND hwnd,
QtWindows::WindowsEventType et,
MSG msg, PVOID vPointerInfo)
@@ -464,9 +571,7 @@ bool QWindowsPointerHandler::translateMouseTouchPadEvent(QWindow *window, HWND h
const QPoint localPos = QWindowsGeometryHint::mapFromGlobal(hwnd, globalPos);
const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
const Qt::MouseButtons mouseButtons = mouseButtonsFromPointerFlags(pointerInfo->pointerFlags);
-
QWindow *currentWindowUnderPointer = getWindowUnderPointer(window, globalPos);
- QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window->handle());
switch (msg.message) {
case WM_NCPOINTERDOWN:
@@ -485,38 +590,17 @@ bool QWindowsPointerHandler::translateMouseTouchPadEvent(QWindow *window, HWND h
keyModifiers, Qt::MouseEventNotSynthesized);
return false; // To allow window dragging, etc.
} else {
- if (eventType == QEvent::MouseButtonPress) {
- // Implement "Click to focus" for native child windows (unless it is a native widget window).
- if (!window->isTopLevel() && !window->inherits("QWidgetWindow") && QGuiApplication::focusWindow() != window)
- window->requestActivate();
- }
- if (currentWindowUnderPointer != m_windowUnderPointer) {
- if (m_windowUnderPointer && m_windowUnderPointer == m_currentWindow) {
- QWindowSystemInterface::handleLeaveEvent(m_windowUnderPointer);
- m_currentWindow = nullptr;
- }
-
- if (currentWindowUnderPointer) {
- if (currentWindowUnderPointer != m_currentWindow) {
- QWindowSystemInterface::handleEnterEvent(currentWindowUnderPointer, localPos, globalPos);
- m_currentWindow = currentWindowUnderPointer;
- if (QWindowsWindow *wumPlatformWindow = QWindowsWindow::windowsWindowOf(currentWindowUnderPointer))
- wumPlatformWindow->applyCursor();
- trackLeave(hwnd);
- }
- } else {
- platformWindow->applyCursor();
- }
- m_windowUnderPointer = currentWindowUnderPointer;
- }
+
+ handleCaptureRelease(window, currentWindowUnderPointer, hwnd, eventType, mouseButtons);
+ handleEnterLeave(window, currentWindowUnderPointer, globalPos);
QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos, mouseButtons, button, eventType,
keyModifiers, Qt::MouseEventNotSynthesized);
// The initial down click over the QSizeGrip area, which posts a resize WM_SYSCOMMAND
// has go to through DefWindowProc() for resizing to work, so we return false here,
- // unless the mouse is captured, as it would mess with menu processing.
- return msg.message != WM_POINTERDOWN || GetCapture();
+ // unless the click was on a menu, as it would mess with menu processing.
+ return msg.message != WM_POINTERDOWN || isMenuWindow(window);
}
}
case WM_POINTERHWHEEL:
@@ -641,6 +725,7 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
// Only the primary pointer will generate mouse messages.
enqueueTouchEvent(window, touchPoints, QWindowsKeyMapper::queryKeyboardModifiers());
} else {
+ flushTouchEvents(m_touchDevice);
QWindowSystemInterface::handleTouchEvent(window, m_touchDevice, touchPoints,
QWindowsKeyMapper::queryKeyboardModifiers());
}
@@ -755,6 +840,16 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin
#endif
}
+static inline bool isMouseEventSynthesizedFromPenOrTouch()
+{
+ // For details, see
+ // https://docs.microsoft.com/en-us/windows/desktop/tablet/system-events-and-mouse-messages
+ const LONG_PTR SIGNATURE_MASK = 0xFFFFFF00;
+ const LONG_PTR MI_WP_SIGNATURE = 0xFF515700;
+
+ return ((::GetMessageExtraInfo() & SIGNATURE_MASK) == MI_WP_SIGNATURE);
+}
+
// Process old-style mouse messages here.
bool QWindowsPointerHandler::translateMouseEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, LRESULT *result)
{
@@ -763,8 +858,6 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window, HWND hwnd, QtW
flushTabletEvents();
*result = 0;
- if (et != QtWindows::MouseWheelEvent && msg.message != WM_MOUSELEAVE && msg.message != WM_MOUSEMOVE)
- return false;
const QPoint eventPos(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
QPoint localPos;
@@ -778,7 +871,26 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window, HWND hwnd, QtW
}
const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
- QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window->handle());
+ const Qt::MouseButtons mouseButtons = mouseButtonsFromKeyState(msg.wParam);
+
+ // Handle "press and hold for right-clicking".
+ // We have to synthesize it here as it only comes from Windows as a fake RMB.
+ // MS docs say we could use bit 7 from extraInfo to distinguish pen from touch,
+ // but on the Surface it is set for both. So we use the last pointer type.
+ if (isMouseEventSynthesizedFromPenOrTouch()) {
+ if ((msg.message == WM_RBUTTONDOWN || msg.message == WM_RBUTTONUP)
+ && (((m_lastPointerType == QT_PT_PEN)
+ && QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTabletEvents))
+ || ((m_lastPointerType == QT_PT_TOUCH)
+ && QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents)))) {
+ QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos, mouseButtons, Qt::RightButton,
+ (msg.message == WM_RBUTTONDOWN) ? QEvent::MouseButtonPress
+ : QEvent::MouseButtonRelease,
+ keyModifiers, Qt::MouseEventSynthesizedBySystem);
+ }
+ // Messages synthesized from touch/pen are only used for flushing queues and press&hold.
+ return false;
+ }
if (et == QtWindows::MouseWheelEvent) {
@@ -799,50 +911,21 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window, HWND hwnd, QtW
}
if (msg.message == WM_MOUSELEAVE) {
+
if (window == m_currentWindow) {
- QWindowSystemInterface::handleLeaveEvent(window);
+ QWindow *leaveTarget = m_windowUnderPointer ? m_windowUnderPointer : m_currentWindow;
+ qCDebug(lcQpaEvents) << "Leaving window " << leaveTarget;
+ QWindowSystemInterface::handleLeaveEvent(leaveTarget);
m_windowUnderPointer = nullptr;
m_currentWindow = nullptr;
- platformWindow->applyCursor();
}
- return false;
- }
-
- // Windows sends a mouse move with no buttons pressed to signal "Enter"
- // when a window is shown over the cursor. Discard the event and only use
- // it for generating QEvent::Enter to be consistent with other platforms -
- // X11 and macOS.
- static QPoint lastMouseMovePos;
- const bool discardEvent = msg.wParam == 0 && (m_windowUnderPointer.isNull() || globalPos == lastMouseMovePos);
- lastMouseMovePos = globalPos;
-
- QWindow *currentWindowUnderPointer = getWindowUnderPointer(window, globalPos);
- if (currentWindowUnderPointer != m_windowUnderPointer) {
- if (m_windowUnderPointer && m_windowUnderPointer == m_currentWindow) {
- QWindowSystemInterface::handleLeaveEvent(m_windowUnderPointer);
- m_currentWindow = nullptr;
- }
+ } else if (msg.message == WM_MOUSEMOVE) {
- if (currentWindowUnderPointer) {
- if (currentWindowUnderPointer != m_currentWindow) {
- QWindowSystemInterface::handleEnterEvent(currentWindowUnderPointer, localPos, globalPos);
- m_currentWindow = currentWindowUnderPointer;
- if (QWindowsWindow *wumPlatformWindow = QWindowsWindow::windowsWindowOf(currentWindowUnderPointer))
- wumPlatformWindow->applyCursor();
- trackLeave(hwnd);
- }
- } else {
- platformWindow->applyCursor();
- }
- m_windowUnderPointer = currentWindowUnderPointer;
+ QWindow *currentWindowUnderPointer = getWindowUnderPointer(window, globalPos);
+ handleCaptureRelease(window, currentWindowUnderPointer, hwnd, QEvent::MouseMove, mouseButtons);
+ handleEnterLeave(window, currentWindowUnderPointer, globalPos);
}
-
- const Qt::MouseButtons mouseButtons = mouseButtonsFromKeyState(msg.wParam);
-
- if (!discardEvent)
- QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos, mouseButtons, Qt::NoButton, QEvent::MouseMove,
- keyModifiers, Qt::MouseEventNotSynthesized);
return false;
}
diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.h b/src/plugins/platforms/windows/qwindowspointerhandler.h
index 3861ebf919..ec3179e821 100644
--- a/src/plugins/platforms/windows/qwindowspointerhandler.h
+++ b/src/plugins/platforms/windows/qwindowspointerhandler.h
@@ -46,6 +46,7 @@
#include <QtCore/qpointer.h>
#include <QtCore/qscopedpointer.h>
#include <QtCore/qhash.h>
+#include <qpa/qwindowsysteminterface.h>
QT_BEGIN_NAMESPACE
@@ -68,12 +69,16 @@ private:
bool translateMouseTouchPadEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, PVOID vPointerInfo);
bool translateTouchEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, PVOID vTouchInfo, unsigned int count);
bool translatePenEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, PVOID vPenInfo);
+ void handleCaptureRelease(QWindow *window, QWindow *currentWindowUnderPointer, HWND hwnd, QEvent::Type eventType, Qt::MouseButtons mouseButtons);
+ void handleEnterLeave(QWindow *window, QWindow *currentWindowUnderPointer, QPoint globalPos);
QTouchDevice *m_touchDevice = nullptr;
QHash<int, QPointF> m_lastTouchPositions;
QPointer<QWindow> m_windowUnderPointer;
QPointer<QWindow> m_currentWindow;
+ QWindow *m_previousCaptureWindow = nullptr;
bool m_needsEnterOnPointerUpdate = false;
+ DWORD m_lastPointerType = 0;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp
index 3c27f2914d..b738007f0e 100644
--- a/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp
+++ b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp
@@ -103,6 +103,13 @@ static void setIconContents(NOTIFYICONDATA &tnd, const QString &tip, HICON hIcon
qStringToLimitedWCharArray(tip, tnd.szTip, sizeof(tnd.szTip) / sizeof(wchar_t));
}
+static void setIconVisibility(NOTIFYICONDATA &tnd, bool v)
+{
+ tnd.uFlags |= NIF_STATE;
+ tnd.dwStateMask = NIS_HIDDEN;
+ tnd.dwState = v ? 0 : NIS_HIDDEN;
+}
+
// Match the HWND of the dummy window to the instances
struct QWindowsHwndSystemTrayIconEntry
{
@@ -176,12 +183,6 @@ static inline HWND createTrayIconMessageWindow()
QWindowsSystemTrayIcon::QWindowsSystemTrayIcon()
{
- // For restoring the tray icon after explorer crashes
- if (!MYWM_TASKBARCREATED)
- MYWM_TASKBARCREATED = RegisterWindowMessage(L"TaskbarCreated");
- // Allow the WM_TASKBARCREATED message through the UIPI filter
- ChangeWindowMessageFilterEx(m_hwnd, MYWM_TASKBARCREATED, MSGFLT_ALLOW, 0);
- qCDebug(lcQpaTrayIcon) << __FUNCTION__ << this << "MYWM_TASKBARCREATED=" << MYWM_TASKBARCREATED;
}
QWindowsSystemTrayIcon::~QWindowsSystemTrayIcon()
@@ -193,12 +194,15 @@ QWindowsSystemTrayIcon::~QWindowsSystemTrayIcon()
void QWindowsSystemTrayIcon::init()
{
qCDebug(lcQpaTrayIcon) << __FUNCTION__ << this;
- ensureInstalled();
+ m_visible = true;
+ if (!setIconVisible(m_visible))
+ ensureInstalled();
}
void QWindowsSystemTrayIcon::cleanup()
{
qCDebug(lcQpaTrayIcon) << __FUNCTION__ << this;
+ m_visible = false;
ensureCleanup();
}
@@ -310,6 +314,13 @@ bool QWindowsSystemTrayIcon::ensureInstalled()
m_hwnd = createTrayIconMessageWindow();
if (Q_UNLIKELY(m_hwnd == nullptr))
return false;
+ // For restoring the tray icon after explorer crashes
+ if (!MYWM_TASKBARCREATED)
+ MYWM_TASKBARCREATED = RegisterWindowMessage(L"TaskbarCreated");
+ // Allow the WM_TASKBARCREATED message through the UIPI filter
+ ChangeWindowMessageFilterEx(m_hwnd, MYWM_TASKBARCREATED, MSGFLT_ALLOW, nullptr);
+ qCDebug(lcQpaTrayIcon) << __FUNCTION__ << this << "MYWM_TASKBARCREATED=" << MYWM_TASKBARCREATED;
+
QWindowsHwndSystemTrayIconEntry entry{m_hwnd, this};
hwndTrayIconEntries()->append(entry);
sendTrayMessage(NIM_ADD);
@@ -333,6 +344,18 @@ void QWindowsSystemTrayIcon::ensureCleanup()
m_toolTip.clear();
}
+bool QWindowsSystemTrayIcon::setIconVisible(bool visible)
+{
+ if (!isInstalled())
+ return false;
+ NOTIFYICONDATA tnd;
+ initNotifyIconData(tnd);
+ tnd.uID = q_uNOTIFYICONID;
+ tnd.hWnd = m_hwnd;
+ setIconVisibility(tnd, visible);
+ return Shell_NotifyIcon(NIM_MODIFY, &tnd) == TRUE;
+}
+
bool QWindowsSystemTrayIcon::sendTrayMessage(DWORD msg)
{
NOTIFYICONDATA tnd;
@@ -340,6 +363,8 @@ bool QWindowsSystemTrayIcon::sendTrayMessage(DWORD msg)
tnd.uID = q_uNOTIFYICONID;
tnd.hWnd = m_hwnd;
tnd.uFlags = NIF_SHOWTIP;
+ if (msg != NIM_DELETE && !m_visible)
+ setIconVisibility(tnd, m_visible);
if (msg == NIM_ADD || msg == NIM_MODIFY)
setIconContents(tnd, m_toolTip, m_hIcon);
if (!Shell_NotifyIcon(msg, &tnd))
diff --git a/src/plugins/platforms/windows/qwindowssystemtrayicon.h b/src/plugins/platforms/windows/qwindowssystemtrayicon.h
index a8adb9641f..44e1bcc761 100644
--- a/src/plugins/platforms/windows/qwindowssystemtrayicon.h
+++ b/src/plugins/platforms/windows/qwindowssystemtrayicon.h
@@ -84,6 +84,7 @@ private:
bool ensureInstalled();
void ensureCleanup();
bool sendTrayMessage(DWORD msg);
+ bool setIconVisible(bool visible);
HICON createIcon(const QIcon &icon);
QIcon m_icon;
@@ -92,6 +93,7 @@ private:
HICON m_hIcon = nullptr;
mutable QPointer<QWindowsPopupMenu> m_menu;
bool m_ignoreNextMouseRelease = false;
+ bool m_visible = false;
};
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
index 6201da7b72..8a6466c9cf 100644
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
@@ -482,26 +482,22 @@ struct WindowCreationData
typedef QWindowsWindowData WindowData;
enum Flags { ForceChild = 0x1, ForceTopLevel = 0x2 };
- WindowCreationData() : parentHandle(0), type(Qt::Widget), style(0), exStyle(0),
- topLevel(false), popup(false), dialog(false),
- tool(false), embedded(false), hasAlpha(false) {}
-
void fromWindow(const QWindow *w, const Qt::WindowFlags flags, unsigned creationFlags = 0);
inline WindowData create(const QWindow *w, const WindowData &data, QString title) const;
inline void applyWindowFlags(HWND hwnd) const;
void initialize(const QWindow *w, HWND h, bool frameChange, qreal opacityLevel) const;
Qt::WindowFlags flags;
- HWND parentHandle;
- Qt::WindowType type;
- unsigned style;
- unsigned exStyle;
- bool topLevel;
- bool popup;
- bool dialog;
- bool tool;
- bool embedded;
- bool hasAlpha;
+ HWND parentHandle = nullptr;
+ Qt::WindowType type = Qt::Widget;
+ unsigned style = 0;
+ unsigned exStyle = 0;
+ bool topLevel = false;
+ bool popup = false;
+ bool dialog = false;
+ bool tool = false;
+ bool embedded = false;
+ bool hasAlpha = false;
};
QDebug operator<<(QDebug debug, const WindowCreationData &d)
@@ -555,12 +551,6 @@ static QScreen *screenForName(const QWindow *w, const QString &name)
return winScreen;
}
-static QScreen *forcedScreenForGLWindow(const QWindow *w)
-{
- const QString forceToScreen = GpuDescription::detect().gpuSuitableScreen;
- return forceToScreen.isEmpty() ? nullptr : screenForName(w, forceToScreen);
-}
-
static QPoint calcPosition(const QWindow *w, const QWindowCreationContextPtr &context, const QMargins &invMargins)
{
const QPoint orgPos(context->frameX - invMargins.left(), context->frameY - invMargins.top());
@@ -569,7 +559,7 @@ static QPoint calcPosition(const QWindow *w, const QWindowCreationContextPtr &co
return orgPos;
// Workaround for QTBUG-50371
- const QScreen *screenForGL = forcedScreenForGLWindow(w);
+ const QScreen *screenForGL = QWindowsWindow::forcedScreenForGLWindow(w);
if (!screenForGL)
return orgPos;
@@ -1364,6 +1354,28 @@ void QWindowsWindow::setDropSiteEnabled(bool dropEnabled)
#endif // QT_CONFIG(clipboard) && QT_CONFIG(draganddrop)
}
+bool QWindowsWindow::m_screenForGLInitialized = false;
+
+void QWindowsWindow::displayChanged()
+{
+ m_screenForGLInitialized = false;
+}
+
+void QWindowsWindow::settingsChanged()
+{
+ m_screenForGLInitialized = false;
+}
+
+QScreen *QWindowsWindow::forcedScreenForGLWindow(const QWindow *w)
+{
+ static QString forceToScreen;
+ if (!m_screenForGLInitialized) {
+ forceToScreen = GpuDescription::detect().gpuSuitableScreen;
+ m_screenForGLInitialized = true;
+ }
+ return forceToScreen.isEmpty() ? nullptr : screenForName(w, forceToScreen);
+}
+
// Returns topmost QWindowsWindow ancestor even if there are embedded windows in the chain.
// Returns this window if it is the topmost ancestor.
QWindow *QWindowsWindow::topLevelOf(QWindow *w)
diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h
index 4d136151fe..b07bd15d2a 100644
--- a/src/plugins/platforms/windows/qwindowswindow.h
+++ b/src/plugins/platforms/windows/qwindowswindow.h
@@ -297,6 +297,9 @@ public:
void handleHidden();
void handleCompositionSettingsChanged();
+ static void displayChanged();
+ static void settingsChanged();
+ static QScreen *forcedScreenForGLWindow(const QWindow *w);
static QWindowsWindow *windowsWindowOf(const QWindow *w);
static QWindow *topLevelOf(QWindow *w);
static inline void *userDataOf(HWND hwnd);
@@ -378,6 +381,8 @@ private:
HICON m_iconBig = 0;
void *m_surface = nullptr;
+ static bool m_screenForGLInitialized;
+
#if QT_CONFIG(vulkan)
// note: intentionally not using void * in order to avoid breaking x86
VkSurfaceKHR m_vkSurface = 0;
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp
index bc92f82d5f..ac8b029916 100644
--- a/src/plugins/platforms/xcb/qxcbclipboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp
@@ -810,8 +810,9 @@ xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t window, i
connection()->flush();
- // sleep 50 ms, so we don't use up CPU cycles all the time.
- QThread::msleep(50);
+ const auto elapsed = timer.elapsed();
+ if (elapsed < clipboard_timeout)
+ queue->waitForNewEvents(clipboard_timeout - elapsed);
} while (timer.elapsed() < clipboard_timeout);
return nullptr;
diff --git a/src/plugins/platforms/xcb/qxcbconnection_basic.cpp b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp
index c69912c361..af72285135 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_basic.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp
@@ -287,7 +287,7 @@ void QXcbBasicConnection::initializeShm()
logging->setEnabled(QtMsgType::QtWarningMsg, true);
}
-void QXcbBasicConnection::initializeXRandr()
+void QXcbBasicConnection::initializeXRender()
{
const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_render_id);
if (!reply || !reply->present) {
@@ -303,7 +303,7 @@ void QXcbBasicConnection::initializeXRandr()
return;
}
- m_hasXRandr = true;
+ m_hasXRender = true;
m_xrenderVersion.first = xrenderQuery->major_version;
m_xrenderVersion.second = xrenderQuery->minor_version;
}
@@ -337,7 +337,7 @@ void QXcbBasicConnection::initializeXFixes()
m_xfixesFirstEvent = reply->first_event;
}
-void QXcbBasicConnection::initializeXRender()
+void QXcbBasicConnection::initializeXRandr()
{
const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_randr_id);
if (!reply || !reply->present)
@@ -352,7 +352,7 @@ void QXcbBasicConnection::initializeXRender()
return;
}
- m_hasXRender = true;
+ m_hasXRandr = true;
m_xrandrFirstEvent = reply->first_event;
}
diff --git a/src/plugins/platforms/xcb/qxcbeventqueue.cpp b/src/plugins/platforms/xcb/qxcbeventqueue.cpp
index 527bca26a8..82a36c0727 100644
--- a/src/plugins/platforms/xcb/qxcbeventqueue.cpp
+++ b/src/plugins/platforms/xcb/qxcbeventqueue.cpp
@@ -229,6 +229,8 @@ void QXcbEventQueue::run()
enqueueEvent(event);
while (!m_closeConnectionDetected && (event = xcb_poll_for_queued_event(connection)))
enqueueEvent(event);
+
+ m_newEventsCondition.wakeOne();
wakeUpDispatcher();
}
@@ -346,6 +348,13 @@ bool QXcbEventQueue::peekEventQueue(PeekerCallback peeker, void *peekerData,
return result;
}
+void QXcbEventQueue::waitForNewEvents(unsigned long time)
+{
+ m_newEventsMutex.lock();
+ m_newEventsCondition.wait(&m_newEventsMutex, time);
+ m_newEventsMutex.unlock();
+}
+
void QXcbEventQueue::sendCloseConnectionEvent() const
{
// A hack to close XCB connection. Apparently XCB does not have any APIs for this?
diff --git a/src/plugins/platforms/xcb/qxcbeventqueue.h b/src/plugins/platforms/xcb/qxcbeventqueue.h
index 235f2824be..11d0b8e963 100644
--- a/src/plugins/platforms/xcb/qxcbeventqueue.h
+++ b/src/plugins/platforms/xcb/qxcbeventqueue.h
@@ -43,6 +43,8 @@
#include <QtCore/QHash>
#include <QtCore/QEventLoop>
#include <QtCore/QVector>
+#include <QtCore/QMutex>
+#include <QtCore/QWaitCondition>
#include <xcb/xcb.h>
@@ -104,6 +106,8 @@ public:
bool peekEventQueue(PeekerCallback peeker, void *peekerData = nullptr,
PeekOptions option = PeekDefault, qint32 peekerId = -1);
+ void waitForNewEvents(unsigned long time = ULONG_MAX);
+
private:
QXcbEventNode *qXcbEventNodeFactory(xcb_generic_event_t *event);
void dequeueNode();
@@ -131,6 +135,9 @@ private:
// debug stats
quint64 m_nodesOnHeap = 0;
+
+ QMutex m_newEventsMutex;
+ QWaitCondition m_newEventsCondition;
};
template<typename Peeker>
diff --git a/src/plugins/platforms/xcb/xcb_qpa_lib.pro b/src/plugins/platforms/xcb/xcb_qpa_lib.pro
index 647058167b..db3d6629b3 100644
--- a/src/plugins/platforms/xcb/xcb_qpa_lib.pro
+++ b/src/plugins/platforms/xcb/xcb_qpa_lib.pro
@@ -112,4 +112,8 @@ qtConfig(xkb) {
qtConfig(dlopen): QMAKE_USE += libdl
+# qxcbkeyboard.cpp's KeyTbl has more than 256 levels of expansion and older
+# Clang uses that as a limit (it's 1024 in current versions).
+clang:!intel_icc: QMAKE_CXXFLAGS += -ftemplate-depth=1024
+
load(qt_module)
diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm
index 04fa280f39..a52871d302 100644
--- a/src/plugins/styles/mac/qmacstyle_mac.mm
+++ b/src/plugins/styles/mac/qmacstyle_mac.mm
@@ -3217,6 +3217,29 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai
static_cast<NSTextFieldCell *>(tf.cell).bezelStyle = isRounded ? NSTextFieldRoundedBezel : NSTextFieldSquareBezel;
tf.frame = opt->rect.toCGRect();
d->drawNSViewInRect(tf, opt->rect, p, ^(CGContextRef, const CGRect &rect) {
+ if (!qt_mac_applicationIsInDarkMode()) {
+ // In 'Dark' mode controls are transparent, so we do not
+ // over-paint the (potentially custom) color in the background.
+ // In 'Light' mode we have to care about the correct
+ // background color. See the comments below for PE_PanelLineEdit.
+ CGContextRef cgContext = NSGraphicsContext.currentContext.CGContext;
+ // See QMacCGContext, here we expect bitmap context created with
+ // color space 'kCGColorSpaceSRGB', if it's something else - we
+ // give up.
+ if (cgContext ? bool(CGBitmapContextGetColorSpace(cgContext)) : false) {
+ tf.drawsBackground = YES;
+ const QColor bgColor = frame->palette.brush(QPalette::Base).color();
+ tf.backgroundColor = [NSColor colorWithSRGBRed:bgColor.redF()
+ green:bgColor.greenF()
+ blue:bgColor.blueF()
+ alpha:bgColor.alphaF()];
+ if (bgColor.alpha() != 255) {
+ // No way we can have it bezeled and transparent ...
+ tf.bordered = YES;
+ }
+ }
+ }
+
[tf.cell drawWithFrame:rect inView:tf];
});
} else {
@@ -3225,21 +3248,36 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai
}
break;
case PE_PanelLineEdit:
- QCommonStyle::drawPrimitive(pe, opt, p, w);
- // Draw the focus frame for widgets other than QLineEdit (e.g. for line edits in Webkit).
- // Focus frame is drawn outside the rectangle passed in the option-rect.
- if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
-#if QT_CONFIG(lineedit)
- if ((opt->state & State_HasFocus) && !qobject_cast<const QLineEdit*>(w)) {
- int vmargin = pixelMetric(QStyle::PM_FocusFrameVMargin);
- int hmargin = pixelMetric(QStyle::PM_FocusFrameHMargin);
- QStyleOptionFrame focusFrame = *panel;
- focusFrame.rect = panel->rect.adjusted(-hmargin, -vmargin, hmargin, vmargin);
- drawControl(CE_FocusFrame, &focusFrame, p, w);
+ {
+ const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(opt);
+ if (qt_mac_applicationIsInDarkMode() || (panel && panel->lineWidth <= 0)) {
+ // QCommonStyle::drawPrimitive(PE_PanelLineEdit) fill the background with
+ // a proper color, defined in opt->palette and then, if lineWidth > 0, it
+ // calls QMacStyle::drawPrimitive(PE_FrameLineEdit). We use NSTextFieldCell
+ // to handle PE_FrameLineEdit, which will use system-default background.
+ // In 'Dark' mode it's transparent and thus it's not over-painted.
+ QCommonStyle::drawPrimitive(pe, opt, p, w);
+ } else {
+ // In 'Light' mode, if panel->lineWidth > 0, we have to use the correct
+ // background color when drawing PE_FrameLineEdit, so let's call it
+ // directly and set the proper color there.
+ drawPrimitive(PE_FrameLineEdit, opt, p, w);
}
+
+ // Draw the focus frame for widgets other than QLineEdit (e.g. for line edits in Webkit).
+ // Focus frame is drawn outside the rectangle passed in the option-rect.
+ if (panel) {
+#if QT_CONFIG(lineedit)
+ if ((opt->state & State_HasFocus) && !qobject_cast<const QLineEdit*>(w)) {
+ int vmargin = pixelMetric(QStyle::PM_FocusFrameVMargin);
+ int hmargin = pixelMetric(QStyle::PM_FocusFrameHMargin);
+ QStyleOptionFrame focusFrame = *panel;
+ focusFrame.rect = panel->rect.adjusted(-hmargin, -vmargin, hmargin, vmargin);
+ drawControl(CE_FocusFrame, &focusFrame, p, w);
+ }
#endif
+ }
}
-
break;
case PE_PanelScrollAreaCorner: {
const QBrush brush(opt->palette.brush(QPalette::Base));
diff --git a/src/printsupport/kernel/qprinterinfo.cpp b/src/printsupport/kernel/qprinterinfo.cpp
index 2eec95d254..167b2361ef 100644
--- a/src/printsupport/kernel/qprinterinfo.cpp
+++ b/src/printsupport/kernel/qprinterinfo.cpp
@@ -3,9 +3,9 @@
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
-** This file is part of the documentation of the Qt Toolkit.
+** This file is part of the QtGui module of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:FDL$
+** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
@@ -14,13 +14,25 @@
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
** $QT_END_LICENSE$
**
****************************************************************************/
diff --git a/src/testlib/doc/qttestlib.qdocconf b/src/testlib/doc/qttestlib.qdocconf
index 6d5d3442a3..5fdf6d9415 100644
--- a/src/testlib/doc/qttestlib.qdocconf
+++ b/src/testlib/doc/qttestlib.qdocconf
@@ -31,8 +31,16 @@ depends += qtcore qtdoc qtwidgets qtgui qmake qtqmltest
headerdirs += ..
+headers += ../../corelib/kernel/qtestsupport_core.h \
+ ../../gui/kernel/qtestsupport_gui.h \
+ ../../widgets/kernel/qtestsupport_widgets.h
+
sourcedirs += ..
+sources += ../../corelib/kernel/qtestsupport_core.cpp \
+ ../../gui/kernel/qtestsupport_gui.cpp \
+ ../../widgets/kernel/qtestsupport_widgets.cpp
+
exampledirs += ../../../examples/qtestlib \
../ \
snippets
diff --git a/src/corelib/doc/snippets/code/src_corelib_kernel_qtestsupport_core.cpp b/src/testlib/doc/snippets/code/src_corelib_kernel_qtestsupport_core.cpp
index e793cb1f55..e793cb1f55 100644
--- a/src/corelib/doc/snippets/code/src_corelib_kernel_qtestsupport_core.cpp
+++ b/src/testlib/doc/snippets/code/src_corelib_kernel_qtestsupport_core.cpp
diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp
index 15e123bdb7..4263f9f049 100644
--- a/src/testlib/qtestcase.cpp
+++ b/src/testlib/qtestcase.cpp
@@ -2426,7 +2426,7 @@ bool QTest::currentTestFailed()
Sleeps for \a ms milliseconds, blocking execution of the
test. qSleep() will not do any event processing and leave your test
unresponsive. Network communication might time out while
- sleeping. Use \l qWait() to do non-blocking sleeping.
+ sleeping. Use \l {QTest::qWait()} to do non-blocking sleeping.
\a ms must be greater than 0.
@@ -2437,7 +2437,7 @@ bool QTest::currentTestFailed()
Example:
\snippet code/src_qtestlib_qtestcase.cpp 23
- \sa qWait()
+ \sa {QTest::qWait()}
*/
void QTest::qSleep(int ms)
{
diff --git a/src/testlib/qtestcase.qdoc b/src/testlib/qtestcase.qdoc
index 31903ea29c..2af016304d 100644
--- a/src/testlib/qtestcase.qdoc
+++ b/src/testlib/qtestcase.qdoc
@@ -995,6 +995,22 @@
*/
/*!
+ \fn char *QTest::toString(const QCborError &c)
+ \overload
+ \since 5.12
+
+ Returns a textual representation of the given CBOR error \a c.
+*/
+
+/*!
+ \fn template <class... Types> char *QTest::toString(const std::tuple<Types...> &tuple)
+ \overload
+ \since 5.12
+
+ Returns a textual representation of the given \a tuple.
+*/
+
+/*!
\fn char *QTest::toString(const QTime &time)
\overload
@@ -1113,6 +1129,12 @@
*/
/*!
+ \fn template <typename Tuple, int... I> char *QTest::toString(const Tuple &tuple, QtPrivate::IndexesList<I...> )
+ \internal
+ \since 5.12
+*/
+
+/*!
\fn QTouchDevice *QTest::createTouchDevice(QTouchDevice::DeviceType devType = QTouchDevice::TouchScreen)
\since 5.8
diff --git a/src/testlib/qtestevent.qdoc b/src/testlib/qtestevent.qdoc
index f0d3bff162..af84f2ac2b 100644
--- a/src/testlib/qtestevent.qdoc
+++ b/src/testlib/qtestevent.qdoc
@@ -133,7 +133,7 @@
Adds a \a msecs milliseconds delay.
- \sa QTest::qWait()
+ \sa {QTest::qWait()}
*/
/*! \fn void QTestEventList::simulate(QWidget *w)
diff --git a/src/testlib/qxmltestlogger.cpp b/src/testlib/qxmltestlogger.cpp
index 72cb53bca7..7153c016c5 100644
--- a/src/testlib/qxmltestlogger.cpp
+++ b/src/testlib/qxmltestlogger.cpp
@@ -136,8 +136,9 @@ void QXmlTestLogger::startLogging()
void QXmlTestLogger::stopLogging()
{
QTestCharBuffer buf;
- QTest::qt_asprintf(&buf,
- "<Duration msecs=\"%f\"/>\n", QTestLog::msecsTotalTime());
+
+ QTest::qt_asprintf(&buf, "<Duration msecs=\"%s\"/>\n",
+ QString::number(QTestLog::msecsTotalTime()).toUtf8().constData());
outputString(buf.constData());
if (xmlmode == QXmlTestLogger::Complete) {
outputString("</TestCase>\n");
@@ -159,9 +160,9 @@ void QXmlTestLogger::leaveTestFunction()
{
QTestCharBuffer buf;
QTest::qt_asprintf(&buf,
- " <Duration msecs=\"%f\"/>\n"
+ " <Duration msecs=\"%s\"/>\n"
"</TestFunction>\n",
- QTestLog::msecsFunctionTime());
+ QString::number(QTestLog::msecsFunctionTime()).toUtf8().constData());
outputString(buf.constData());
}
diff --git a/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp b/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp
index 738905a9a0..7c6f0bdeef 100644
--- a/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp
+++ b/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp
@@ -62,7 +62,7 @@ static const char docTypeHeader[] =
#define PROGRAMNAME "qdbuscpp2xml"
#define PROGRAMVERSION "0.2"
-#define PROGRAMCOPYRIGHT "Copyright (C) 2018 The Qt Company Ltd."
+#define PROGRAMCOPYRIGHT "Copyright (C) 2019 The Qt Company Ltd."
static QString outputFile;
static int flags;
diff --git a/src/tools/qdbusxml2cpp/qdbusxml2cpp.cpp b/src/tools/qdbusxml2cpp/qdbusxml2cpp.cpp
index 360e3c8bc3..ea410cd257 100644
--- a/src/tools/qdbusxml2cpp/qdbusxml2cpp.cpp
+++ b/src/tools/qdbusxml2cpp/qdbusxml2cpp.cpp
@@ -46,7 +46,7 @@
#define PROGRAMNAME "qdbusxml2cpp"
#define PROGRAMVERSION "0.8"
-#define PROGRAMCOPYRIGHT "Copyright (C) 2018 The Qt Company Ltd."
+#define PROGRAMCOPYRIGHT "Copyright (C) 2019 The Qt Company Ltd."
#define ANNOTATION_NO_WAIT "org.freedesktop.DBus.Method.NoReply"
diff --git a/src/widgets/dialogs/qmessagebox.cpp b/src/widgets/dialogs/qmessagebox.cpp
index ce918b8a74..ac1952a642 100644
--- a/src/widgets/dialogs/qmessagebox.cpp
+++ b/src/widgets/dialogs/qmessagebox.cpp
@@ -1890,7 +1890,7 @@ void QMessageBox::aboutQt(QWidget *parent, const QString &title)
"<p>Qt and the Qt logo are trademarks of The Qt Company Ltd.</p>"
"<p>Qt is The Qt Company Ltd product developed as an open source "
"project. See <a href=\"http://%3/\">%3</a> for more information.</p>"
- ).arg(QStringLiteral("2018"),
+ ).arg(QStringLiteral("2019"),
QStringLiteral("qt.io/licensing"),
QStringLiteral("qt.io"));
QMessageBox *msgBox = new QMessageBox(parent);
diff --git a/src/widgets/doc/qtwidgets.qdocconf b/src/widgets/doc/qtwidgets.qdocconf
index 4700ee29bf..5d7262fca1 100644
--- a/src/widgets/doc/qtwidgets.qdocconf
+++ b/src/widgets/doc/qtwidgets.qdocconf
@@ -40,6 +40,9 @@ exampledirs += ../../../examples/widgets \
excludedirs += snippets
+# Included in qttestlib.qdocconf instead
+excludefiles += ../kernel/qtestsupport_widgets.cpp
+
imagedirs += images \
../../../doc/src/images \
../../../examples/widgets/doc/images \
diff --git a/src/widgets/itemviews/qlistview.cpp b/src/widgets/itemviews/qlistview.cpp
index fdac332367..143b243091 100644
--- a/src/widgets/itemviews/qlistview.cpp
+++ b/src/widgets/itemviews/qlistview.cpp
@@ -982,18 +982,9 @@ void QListView::paintEvent(QPaintEvent *e)
? qMax(viewport()->size().width(), d->contentsSize().width()) - 2 * d->spacing()
: qMax(viewport()->size().height(), d->contentsSize().height()) - 2 * d->spacing();
- const int rowCount = d->commonListView->rowCount();
QVector<QModelIndex>::const_iterator end = toBeRendered.constEnd();
for (QVector<QModelIndex>::const_iterator it = toBeRendered.constBegin(); it != end; ++it) {
Q_ASSERT((*it).isValid());
- if (rowCount == 1)
- option.viewItemPosition = QStyleOptionViewItem::OnlyOne;
- else if ((*it).row() == 0)
- option.viewItemPosition = QStyleOptionViewItem::Beginning;
- else if ((*it).row() == rowCount - 1)
- option.viewItemPosition = QStyleOptionViewItem::End;
- else
- option.viewItemPosition = QStyleOptionViewItem::Middle;
option.rect = visualRect(*it);
if (flow() == TopToBottom)
diff --git a/src/widgets/kernel/qtestsupport_widgets.cpp b/src/widgets/kernel/qtestsupport_widgets.cpp
index b227e6ff5d..0056bebdc6 100644
--- a/src/widgets/kernel/qtestsupport_widgets.cpp
+++ b/src/widgets/kernel/qtestsupport_widgets.cpp
@@ -46,15 +46,14 @@
QT_BEGIN_NAMESPACE
-/*! \fn bool qWaitForWindowActive(QWidget *widget, int timeout)
- \relates QTest
+/*!
\since 5.0
Waits for \a timeout milliseconds or until the \a widget's window is active.
Returns \c true if \c widget's window is active within \a timeout milliseconds, otherwise returns \c false.
- \sa QTest::qWaitForWindowExposed(), QWidget::isActiveWindow()
+ \sa qWaitForWindowExposed(), QWidget::isActiveWindow()
*/
Q_WIDGETS_EXPORT Q_REQUIRED_RESULT bool QTest::qWaitForWindowActive(QWidget *widget, int timeout)
{
@@ -63,8 +62,7 @@ Q_WIDGETS_EXPORT Q_REQUIRED_RESULT bool QTest::qWaitForWindowActive(QWidget *wid
return false;
}
-/*! \fn bool qWaitForWindowExposed(QWidget *widget, int timeout)
- \relates QTest
+/*!
\since 5.0
Waits for \a timeout milliseconds or until the \a widget's window is exposed.
@@ -80,7 +78,7 @@ Q_WIDGETS_EXPORT Q_REQUIRED_RESULT bool QTest::qWaitForWindowActive(QWidget *wid
A specific configuration where this happens is when using QGLWidget as a viewport widget on macOS:
The viewport widget gets the expose event, not the parent widget.
- \sa QTest::qWaitForWindowActive()
+ \sa qWaitForWindowActive()
*/
Q_WIDGETS_EXPORT Q_REQUIRED_RESULT bool QTest::qWaitForWindowExposed(QWidget *widget, int timeout)
{
@@ -89,11 +87,12 @@ Q_WIDGETS_EXPORT Q_REQUIRED_RESULT bool QTest::qWaitForWindowExposed(QWidget *wi
return false;
}
-/*! \fn bool qWaitForWindowShown(QWidget *widget, int timeout)
- \relates QTest
+/*! \fn bool QTest::qWaitForWindowShown(QWidget *widget, int timeout)
\since 5.0
\deprecated
+ Use qWaitForWindowExposed() instead.
+
Waits for \a timeout milliseconds or until the \a widget's window is exposed.
Returns \c true if \c widget's window is exposed within \a timeout milliseconds, otherwise returns \c false.
@@ -107,7 +106,7 @@ Q_WIDGETS_EXPORT Q_REQUIRED_RESULT bool QTest::qWaitForWindowExposed(QWidget *wi
QTest::qWaitForWindowShown(&widget);
\endcode
- \sa QTest::qWaitForWindowActive(), QTest::qWaitForWindowExposed()
+ \sa qWaitForWindowActive(), qWaitForWindowExposed()
*/
QT_END_NAMESPACE
diff --git a/src/widgets/kernel/qtooltip.cpp b/src/widgets/kernel/qtooltip.cpp
index 9d8b0062f5..cf0f3f153b 100644
--- a/src/widgets/kernel/qtooltip.cpp
+++ b/src/widgets/kernel/qtooltip.cpp
@@ -216,7 +216,6 @@ void QTipLabel::reuseTip(const QString &text, int msecDisplayTime, const QPoint
}
#endif
- setWordWrap(true);
setText(text);
updateSize(pos);
restartExpireTimer(msecDisplayTime);
@@ -235,20 +234,17 @@ void QTipLabel::updateSize(const QPoint &pos)
// Make it look good with the default ToolTip font on Mac, which has a small descent.
if (fm.descent() == 2 && fm.ascent() >= 11)
++extra.rheight();
+ setWordWrap(Qt::mightBeRichText(text()));
QSize sh = sizeHint();
- if (wordWrap()) {
- // ### When the above WinRT code is fixed, windowhandle should be used to find the screen.
- QScreen *screen = QGuiApplication::screenAt(pos);
- if (!screen)
- screen = QGuiApplication::primaryScreen();
- if (screen) {
- const qreal screenWidth = screen->geometry().width();
- if (sh.width() > screenWidth) {
- // Try to use widely accepted 75chars max length or 80% of the screen width else.
- // See https://en.wikipedia.org/wiki/Line_length
- sh.setWidth(qMin(fm.averageCharWidth() * 75, static_cast<int>(screenWidth * .8)));
- sh.setHeight(heightForWidth(sh.width()));
- }
+ // ### When the above WinRT code is fixed, windowhandle should be used to find the screen.
+ QScreen *screen = QGuiApplication::screenAt(pos);
+ if (!screen)
+ screen = QGuiApplication::primaryScreen();
+ if (screen) {
+ const qreal screenWidth = screen->geometry().width();
+ if (!wordWrap() && sh.width() > screenWidth) {
+ setWordWrap(true);
+ sh = sizeHint();
}
}
resize(sh + extra);
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp
index 6616cb6de3..c0430bec23 100644
--- a/src/widgets/kernel/qwidget.cpp
+++ b/src/widgets/kernel/qwidget.cpp
@@ -247,6 +247,7 @@ QWidgetPrivate::QWidgetPrivate(int version)
#ifndef QT_NO_TOOLTIP
, toolTipDuration(-1)
#endif
+ , directFontResolveMask(0)
, inheritedFontResolveMask(0)
, inheritedPaletteResolveMask(0)
, leftmargin(0)
@@ -4519,7 +4520,7 @@ void QWidget::setForegroundRole(QPalette::ColorRole role)
the "color", "background-color", "selection-color",
"selection-background-color" and "alternate-background-color".
- \sa QApplication::palette(), QWidget::font(), \l {Qt Style Sheets}
+ \sa QApplication::palette(), QWidget::font(), {Qt Style Sheets}
*/
const QPalette &QWidget::palette() const
{
@@ -4757,6 +4758,18 @@ QFont QWidgetPrivate::naturalWidgetFont(uint inheritedMask) const
/*!
\internal
+ Returns a font suitable for inheritance, where only locally set attributes are considered resolved.
+*/
+QFont QWidgetPrivate::localFont() const
+{
+ QFont localfont = data.fnt;
+ localfont.resolve(directFontResolveMask);
+ return localfont;
+}
+
+/*!
+ \internal
+
Determine which font is implicitly imposed on this widget by its ancestors
and QApplication::font, resolve this against its own font (attributes from
the implicit font are copied over). Then propagate this font to this
@@ -4765,7 +4778,7 @@ QFont QWidgetPrivate::naturalWidgetFont(uint inheritedMask) const
void QWidgetPrivate::resolveFont()
{
QFont naturalFont = naturalWidgetFont(inheritedFontResolveMask);
- QFont resolvedFont = data.fnt.resolve(naturalFont);
+ QFont resolvedFont = localFont().resolve(naturalFont);
setFont_helper(resolvedFont);
}
@@ -4804,6 +4817,11 @@ void QWidgetPrivate::updateFont(const QFont &font)
inheritedFontResolveMask = 0;
}
uint newMask = data.fnt.resolve() | inheritedFontResolveMask;
+ // Set the font as also having resolved inherited traits, so the result of reading QWidget::font()
+ // isn't all weak information, but save the original mask to be able to let new changes on the
+ // parent widget font propagate correctly.
+ directFontResolveMask = data.fnt.resolve();
+ data.fnt.resolve(newMask);
for (int i = 0; i < children.size(); ++i) {
QWidget *w = qobject_cast<QWidget*>(children.at(i));
diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h
index 7c757a43f2..ab5dc6bfba 100644
--- a/src/widgets/kernel/qwidget_p.h
+++ b/src/widgets/kernel/qwidget_p.h
@@ -383,10 +383,11 @@ public:
void updateFont(const QFont &);
inline void setFont_helper(const QFont &font) {
- if (data.fnt.resolve() == font.resolve() && data.fnt == font)
+ if (directFontResolveMask == font.resolve() && data.fnt == font)
return;
updateFont(font);
}
+ QFont localFont() const;
void resolveFont();
QFont naturalWidgetFont(uint inheritedMask) const;
@@ -730,6 +731,7 @@ public:
#endif
// Other variables.
+ uint directFontResolveMask;
uint inheritedFontResolveMask;
uint inheritedPaletteResolveMask;
short leftmargin;
diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp
index 279c6c0282..991a05fa02 100644
--- a/src/widgets/kernel/qwidgetwindow.cpp
+++ b/src/widgets/kernel/qwidgetwindow.cpp
@@ -899,10 +899,10 @@ void QWidgetWindow::handleDragMoveEvent(QDragMoveEvent *event)
const QPoint mapped = widget->mapFromGlobal(m_widget->mapToGlobal(event->pos()));
QDragMoveEvent translated(mapped, event->possibleActions(), event->mimeData(),
event->mouseButtons(), event->keyboardModifiers());
- translated.setDropAction(event->dropAction());
- translated.setAccepted(event->isAccepted());
if (widget == m_dragTarget) { // Target widget unchanged: Send DragMove
+ translated.setDropAction(event->dropAction());
+ translated.setAccepted(event->isAccepted());
QGuiApplication::forwardEvent(m_dragTarget, &translated, event);
} else {
if (m_dragTarget) { // Send DragLeave to previous
@@ -912,6 +912,9 @@ void QWidgetWindow::handleDragMoveEvent(QDragMoveEvent *event)
}
// Send DragEnter to new widget.
handleDragEnterEvent(static_cast<QDragEnterEvent*>(event), widget);
+ // Handling 'DragEnter' should suffice for the application.
+ translated.setDropAction(event->dropAction());
+ translated.setAccepted(event->isAccepted());
// The drag enter event is always immediately followed by a drag move event,
// see QDragEnterEvent documentation.
QGuiApplication::forwardEvent(m_dragTarget, &translated, event);
diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp
index 49543061cc..2bfc1acc26 100644
--- a/src/widgets/styles/qcommonstyle.cpp
+++ b/src/widgets/styles/qcommonstyle.cpp
@@ -946,17 +946,20 @@ void QCommonStylePrivate::viewItemDrawText(QPainter *p, const QStyleOptionViewIt
const QRectF boundingRect = textLayout.boundingRect();
const QRect layoutRect = QStyle::alignedRect(option->direction, option->displayAlignment,
boundingRect.size().toSize(), textRect);
- const QPointF position = layoutRect.topLeft();
- const int lineCount = textLayout.lineCount();
+ QPointF paintPosition = QPointF(textRect.x(), layoutRect.top());
+ QString newText;
qreal height = 0;
+ const int lineCount = textLayout.lineCount();
for (int i = 0; i < lineCount; ++i) {
const QTextLine line = textLayout.lineAt(i);
height += line.height();
// above visible rect
- if (height + layoutRect.top() <= textRect.top())
+ if (height + layoutRect.top() <= textRect.top()) {
+ paintPosition.ry() += line.height();
continue;
+ }
const int start = line.textStart();
const int length = line.textLength();
@@ -971,26 +974,33 @@ void QCommonStylePrivate::viewItemDrawText(QPainter *p, const QStyleOptionViewIt
elideLastVisibleLine = true;
}
+ QString text = textLayout.text().mid(start, length);
if (drawElided || elideLastVisibleLine) {
- QString text = textLayout.text().mid(start, length);
- if (elideLastVisibleLine)
+ if (elideLastVisibleLine) {
+ if (text.endsWith(QChar::LineSeparator))
+ text.chop(1);
text += QChar(0x2026);
+ }
const QStackTextEngine engine(text, option->font);
- const QString elidedText = engine.elidedText(option->textElideMode, textRect.width());
- const QPointF pos(position.x() + line.x(),
- position.y() + line.y() + line.ascent());
- p->save();
- p->setFont(option->font);
- p->drawText(pos, elidedText);
- p->restore();
+ newText += engine.elidedText(option->textElideMode, textRect.width());
+ // sometimes drawElided is true but no eliding is done so the text ends
+ // with QChar::LineSeparator - don't add another one. This happened with
+ // arabic text in the testcase for QTBUG-72805
+ if (i < lineCount - 1 &&
+ !newText.endsWith(QChar::LineSeparator))
+ newText += QChar::LineSeparator;
} else {
- line.draw(p, position);
+ newText += text;
}
// below visible text, can stop
if (height + layoutRect.top() >= textRect.bottom())
break;
}
+
+ textLayout.setText(newText);
+ viewItemTextLayout(textLayout, textRect.width());
+ textLayout.draw(p, paintPosition);
}
/*! \internal
diff --git a/src/widgets/styles/qstylesheetstyle.cpp b/src/widgets/styles/qstylesheetstyle.cpp
index e261055c3c..e73d019408 100644
--- a/src/widgets/styles/qstylesheetstyle.cpp
+++ b/src/widgets/styles/qstylesheetstyle.cpp
@@ -2645,7 +2645,7 @@ void QStyleSheetStyle::setPalette(QWidget *w)
QRenderRule rule = renderRule(w, PseudoElement_None, map[i].state | extendedPseudoClass(w));
if (i == 0) {
if (!w->property("_q_styleSheetWidgetFont").isValid()) {
- saveWidgetFont(w, w->font());
+ saveWidgetFont(w, w->d_func()->localFont());
}
updateStyleSheetFont(w);
if (ew != w)
@@ -6033,7 +6033,7 @@ void QStyleSheetStyle::updateStyleSheetFont(QWidget* w) const
unsetStyleSheetFont(w);
if (rule.font.resolve()) {
- QFont wf = w->font();
+ QFont wf = w->d_func()->localFont();
styleSheetCaches->customFontWidgets.insert(w, {wf, rule.font.resolve()});
QFont font = rule.font.resolve(wf);
@@ -6041,7 +6041,9 @@ void QStyleSheetStyle::updateStyleSheetFont(QWidget* w) const
w->setFont(font);
}
} else {
- QFont font = rule.font.resolve(w->font());
+ QFont wf = w->d_func()->localFont();
+ QFont font = rule.font.resolve(wf);
+ font.resolve(wf.resolve() | rule.font.resolve());
if ((!w->isWindow() || w->testAttribute(Qt::WA_WindowPropagation))
&& isNaturalChild(w) && qobject_cast<QWidget *>(w->parent())) {
@@ -6049,10 +6051,11 @@ void QStyleSheetStyle::updateStyleSheetFont(QWidget* w) const
font = font.resolve(static_cast<QWidget *>(w->parent())->font());
}
- if (w->data->fnt == font)
+ if (wf.resolve() == font.resolve() && wf == font)
return;
w->data->fnt = font;
+ w->d_func()->directFontResolveMask = font.resolve();
QEvent e(QEvent::FontChange);
QApplication::sendEvent(w, &e);
diff --git a/src/widgets/widgets/qlineedit.cpp b/src/widgets/widgets/qlineedit.cpp
index 2ae2e16c89..4301a3a2e7 100644
--- a/src/widgets/widgets/qlineedit.cpp
+++ b/src/widgets/widgets/qlineedit.cpp
@@ -1681,6 +1681,7 @@ void QLineEdit::mouseDoubleClickEvent(QMouseEvent* e)
/*!
\fn void QLineEdit::inputRejected()
+ \since 5.12
This signal is emitted when the user presses a key that is not
considered to be acceptable input. For example, if a key press