summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config.tests/unix/qqnx_imf/qqnx_imf.cpp48
-rw-r--r--config.tests/unix/qqnx_imf/qqnx_imf.pro3
-rwxr-xr-xconfigure20
-rw-r--r--dist/changes-5.3.036
-rw-r--r--examples/network/dnslookup/dnslookup.cpp13
-rw-r--r--examples/network/network.pro2
-rw-r--r--examples/widgets/widgets/spinboxes/window.cpp25
-rw-r--r--examples/widgets/widgets/spinboxes/window.h3
-rw-r--r--mkspecs/common/winrt_winphone/qmake.conf18
-rw-r--r--mkspecs/common/winrt_winphone/qplatformdefs.h141
-rw-r--r--mkspecs/features/configure.prf6
-rw-r--r--mkspecs/features/winrt/console.prf5
-rw-r--r--mkspecs/qnx-armv7le-qcc/qplatformdefs.h7
-rw-r--r--mkspecs/qnx-x86-qcc/qplatformdefs.h7
-rw-r--r--mkspecs/unsupported/linux-libc++-clang/qmake.conf20
-rw-r--r--mkspecs/unsupported/linux-libc++-clang/qplatformdefs.h100
-rw-r--r--mkspecs/winphone-arm-msvc2012/qmake.conf4
-rw-r--r--mkspecs/winphone-arm-msvc2012/qplatformdefs.h2
-rw-r--r--mkspecs/winphone-x86-msvc2012/qmake.conf4
-rw-r--r--mkspecs/winphone-x86-msvc2012/qplatformdefs.h2
-rw-r--r--mkspecs/winrt-arm-msvc2012/qmake.conf4
-rw-r--r--mkspecs/winrt-arm-msvc2012/qplatformdefs.h2
-rw-r--r--mkspecs/winrt-arm-msvc2013/qmake.conf20
-rw-r--r--mkspecs/winrt-arm-msvc2013/qplatformdefs.h42
-rw-r--r--mkspecs/winrt-x64-msvc2012/qmake.conf4
-rw-r--r--mkspecs/winrt-x64-msvc2012/qplatformdefs.h2
-rw-r--r--mkspecs/winrt-x64-msvc2013/qmake.conf20
-rw-r--r--mkspecs/winrt-x64-msvc2013/qplatformdefs.h42
-rw-r--r--mkspecs/winrt-x86-msvc2012/qmake.conf4
-rw-r--r--mkspecs/winrt-x86-msvc2012/qplatformdefs.h2
-rw-r--r--mkspecs/winrt-x86-msvc2013/qmake.conf20
-rw-r--r--mkspecs/winrt-x86-msvc2013/qplatformdefs.h42
-rw-r--r--qmake/generators/win32/msvc_nmake.cpp70
-rw-r--r--src/3rdparty/angle/include/EGL/eglplatform.h10
-rw-r--r--src/3rdparty/angle/src/compiler/osinclude.h35
-rw-r--r--src/3rdparty/angle/src/compiler/ossource_posix.cpp8
-rw-r--r--src/3rdparty/angle/src/compiler/ossource_win.cpp8
-rw-r--r--src/3rdparty/angle/src/compiler/ossource_winrt.cpp75
-rw-r--r--src/3rdparty/angle/src/libEGL/Display.cpp8
-rw-r--r--src/3rdparty/angle/src/libEGL/Display.h4
-rw-r--r--src/3rdparty/angle/src/libEGL/Surface.cpp35
-rw-r--r--src/3rdparty/angle/src/libEGL/Surface.h7
-rw-r--r--src/3rdparty/angle/src/libEGL/libEGL.cpp4
-rw-r--r--src/3rdparty/angle/src/libEGL/main.cpp40
-rw-r--r--src/3rdparty/angle/src/libGLESv2/Buffer.cpp8
-rw-r--r--src/3rdparty/angle/src/libGLESv2/Buffer.h4
-rw-r--r--src/3rdparty/angle/src/libGLESv2/libGLESv2.cpp4
-rw-r--r--src/3rdparty/angle/src/libGLESv2/main.cpp39
-rw-r--r--src/3rdparty/angle/src/libGLESv2/precompiled.h15
-rw-r--r--src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage.h2
-rw-r--r--src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage11.cpp9
-rw-r--r--src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage11.h2
-rw-r--r--src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage9.cpp2
-rw-r--r--src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage9.h2
-rw-r--r--src/3rdparty/angle/src/libGLESv2/renderer/Image11.cpp7
-rw-r--r--src/3rdparty/angle/src/libGLESv2/renderer/IndexBuffer11.cpp4
-rw-r--r--src/3rdparty/angle/src/libGLESv2/renderer/RenderStateCache.cpp3
-rw-r--r--src/3rdparty/angle/src/libGLESv2/renderer/Renderer.cpp23
-rw-r--r--src/3rdparty/angle/src/libGLESv2/renderer/Renderer.h27
-rw-r--r--src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.cpp298
-rw-r--r--src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.h4
-rw-r--r--src/3rdparty/angle/src/libGLESv2/renderer/SwapChain.h4
-rw-r--r--src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.cpp34
-rw-r--r--src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.h2
-rw-r--r--src/3rdparty/angle/src/libGLESv2/renderer/TextureStorage11.cpp8
-rw-r--r--src/3rdparty/angle/src/libGLESv2/renderer/renderer11_utils.cpp4
-rw-r--r--src/3rdparty/angle/src/libGLESv2/renderer/renderer11_utils.h2
-rw-r--r--src/3rdparty/angle/src/libGLESv2/renderer/shaders/Clear11.hlsl4
-rw-r--r--src/3rdparty/angle/src/libGLESv2/utilities.cpp53
-rw-r--r--src/3rdparty/libjpeg.pri2
-rw-r--r--src/3rdparty/libpng/pngpriv.h2
-rw-r--r--src/3rdparty/sqlite/shell.c547
-rw-r--r--src/3rdparty/sqlite/sqlite3.c15512
-rw-r--r--src/3rdparty/sqlite/sqlite3.h152
-rw-r--r--src/angle/patches/0012-ANGLE-Support-WinRT.patch1131
-rw-r--r--src/angle/patches/0013-ANGLE-Enable-D3D11-for-feature-level-9-cards.patch990
-rw-r--r--src/angle/src/common/common.pri2
-rw-r--r--src/angle/src/compiler/translator_common.pro7
-rw-r--r--src/angle/src/config.pri5
-rw-r--r--src/angle/src/libGLESv2/libGLESv2.pro8
-rw-r--r--src/corelib/global/qcompilerdetection.h11
-rw-r--r--src/corelib/global/qglobal.cpp15
-rw-r--r--src/corelib/global/qglobal.h12
-rw-r--r--src/corelib/global/qlibraryinfo.cpp33
-rw-r--r--src/corelib/global/qlibraryinfo.h4
-rw-r--r--src/corelib/global/qnamespace.h6
-rw-r--r--src/corelib/global/qnamespace.qdoc22
-rw-r--r--src/corelib/global/qnumeric.cpp136
-rw-r--r--src/corelib/global/qnumeric.h3
-rw-r--r--src/corelib/io/io.pri6
-rw-r--r--src/corelib/io/qdatastream.cpp3
-rw-r--r--src/corelib/io/qdatastream.h5
-rw-r--r--src/corelib/io/qdir.cpp2
-rw-r--r--src/corelib/io/qfilesystemengine_win.cpp192
-rw-r--r--src/corelib/io/qfilesystementry.cpp6
-rw-r--r--src/corelib/io/qfilesystemiterator_unix.cpp4
-rw-r--r--src/corelib/io/qfilesystemiterator_win.cpp14
-rw-r--r--src/corelib/io/qfilesystemmetadata_p.h6
-rw-r--r--src/corelib/io/qfsfileengine_win.cpp73
-rw-r--r--src/corelib/io/qlockfile_win.cpp14
-rw-r--r--src/corelib/io/qsettings.cpp67
-rw-r--r--src/corelib/io/qstandardpaths.cpp202
-rw-r--r--src/corelib/io/qstandardpaths_winrt.cpp127
-rw-r--r--src/corelib/io/qtemporaryfile.cpp15
-rw-r--r--src/corelib/itemmodels/qitemselectionmodel.cpp3
-rw-r--r--src/corelib/kernel/kernel.pri18
-rw-r--r--src/corelib/kernel/qcoreapplication.cpp10
-rw-r--r--src/corelib/kernel/qeventdispatcher_glib_p.h20
-rw-r--r--src/corelib/kernel/qeventdispatcher_unix_p.h32
-rw-r--r--src/corelib/kernel/qeventdispatcher_winrt.cpp394
-rw-r--r--src/corelib/kernel/qeventdispatcher_winrt_p.h168
-rw-r--r--src/corelib/kernel/qfunctions_p.h2
-rw-r--r--src/corelib/kernel/qfunctions_winrt.cpp105
-rw-r--r--src/corelib/kernel/qfunctions_winrt.h122
-rw-r--r--src/corelib/kernel/qobject.cpp73
-rw-r--r--src/corelib/kernel/qobject.h46
-rw-r--r--src/corelib/kernel/qvariant.cpp2
-rw-r--r--src/corelib/kernel/qvariant_p.h2
-rw-r--r--src/corelib/kernel/qwineventnotifier.cpp4
-rw-r--r--src/corelib/plugin/qlibrary_win.cpp6
-rw-r--r--src/corelib/thread/qmutex_win.cpp9
-rw-r--r--src/corelib/thread/qthread.cpp2
-rw-r--r--src/corelib/thread/qthread_p.h9
-rw-r--r--src/corelib/thread/qthread_win.cpp154
-rw-r--r--src/corelib/thread/qthreadpool.cpp28
-rw-r--r--src/corelib/thread/qthreadpool_p.h3
-rw-r--r--src/corelib/thread/qwaitcondition_win.cpp11
-rw-r--r--src/corelib/tools/qalgorithms.h86
-rw-r--r--src/corelib/tools/qcollator_win.cpp24
-rw-r--r--src/corelib/tools/qdatetime.cpp3
-rw-r--r--src/corelib/tools/qlocale_win.cpp206
-rw-r--r--src/corelib/tools/qstring.cpp13
-rw-r--r--src/corelib/tools/qstring.h3
-rw-r--r--src/corelib/tools/qtimezone.cpp6
-rw-r--r--src/corelib/tools/tools.pri7
-rw-r--r--src/gui/kernel/qevent.cpp18
-rw-r--r--src/gui/kernel/qevent.h2
-rw-r--r--src/gui/kernel/qguiapplication.cpp36
-rw-r--r--src/gui/kernel/qguiapplication_p.h4
-rw-r--r--src/gui/kernel/qguivariant.cpp2
-rw-r--r--src/gui/kernel/qstylehints.cpp106
-rw-r--r--src/gui/kernel/qstylehints.h7
-rw-r--r--src/gui/kernel/qsurfaceformat.cpp124
-rw-r--r--src/gui/kernel/qsurfaceformat.h12
-rw-r--r--src/gui/kernel/qwindowsysteminterface.cpp20
-rw-r--r--src/gui/kernel/qwindowsysteminterface.h16
-rw-r--r--src/gui/kernel/qwindowsysteminterface_p.h11
-rw-r--r--src/gui/math3d/qvector2d.h82
-rw-r--r--src/gui/math3d/qvector3d.h86
-rw-r--r--src/gui/math3d/qvector4d.h90
-rw-r--r--src/gui/painting/qbrush.cpp81
-rw-r--r--src/gui/painting/qdrawhelper.cpp44
-rw-r--r--src/gui/painting/qpen.cpp4
-rw-r--r--src/gui/text/qfontengine_ft.cpp4
-rw-r--r--src/gui/text/qtextcursor.cpp1
-rw-r--r--src/gui/text/qtextdocument.cpp80
-rw-r--r--src/gui/text/qtextdocument.h8
-rw-r--r--src/gui/text/qtextdocument_p.h1
-rw-r--r--src/network/kernel/qdnslookup.cpp19
-rw-r--r--src/network/kernel/qdnslookup.h6
-rw-r--r--src/network/kernel/qdnslookup_p.h1
-rw-r--r--src/network/socket/qsocks5socketengine.cpp7
-rw-r--r--src/platformsupport/eglconvenience/qeglconvenience.cpp1
-rw-r--r--src/platformsupport/eglconvenience/qeglplatformcontext.cpp24
-rw-r--r--src/platformsupport/eglconvenience/qeglplatformcontext_p.h3
-rw-r--r--src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp67
-rw-r--r--src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp1
-rw-r--r--src/plugins/platforms/eglfs/qeglfscontext.cpp20
-rw-r--r--src/plugins/platforms/eglfs/qeglfscontext.h3
-rw-r--r--src/plugins/platforms/eglfs/qeglfswindow.cpp2
-rw-r--r--src/plugins/platforms/minimal/qminimalintegration.cpp17
-rw-r--r--src/plugins/platforms/offscreen/qoffscreenintegration.cpp8
-rw-r--r--src/plugins/platforms/platforms.pro3
-rw-r--r--src/plugins/platforms/qnx/qnx.pro3
-rw-r--r--src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.cpp30
-rw-r--r--src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.h7
-rw-r--r--src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp1469
-rw-r--r--src/plugins/platforms/qnx/qqnxinputcontext_imf.h62
-rw-r--r--src/plugins/platforms/qnx/qqnxinputcontext_noimf.cpp9
-rw-r--r--src/plugins/platforms/qnx/qqnxintegration.cpp27
-rw-r--r--src/plugins/platforms/qnx/qqnxscreeneventfilter.h58
-rw-r--r--src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp31
-rw-r--r--src/plugins/platforms/qnx/qqnxscreeneventhandler.h7
-rw-r--r--src/plugins/platforms/qnx/qqnxvirtualkeyboardbps.cpp6
-rw-r--r--src/plugins/platforms/windows/qwindowsglcontext.cpp26
-rw-r--r--src/plugins/platforms/windows/qwindowsglcontext.h6
-rw-r--r--src/plugins/platforms/windows/qwindowsintegration.cpp8
-rw-r--r--src/plugins/platforms/windows/qwindowsintegration.h2
-rw-r--r--src/plugins/platforms/windows/qwindowsmousehandler.cpp13
-rw-r--r--src/plugins/platforms/winrt/blit.hlsl14
-rw-r--r--src/plugins/platforms/winrt/main.cpp74
-rw-r--r--src/plugins/platforms/winrt/qwinrtbackingstore.cpp393
-rw-r--r--src/plugins/platforms/winrt/qwinrtbackingstore.h77
-rw-r--r--src/plugins/platforms/winrt/qwinrtcursor.cpp153
-rw-r--r--src/plugins/platforms/winrt/qwinrtcursor.h77
-rw-r--r--src/plugins/platforms/winrt/qwinrteglcontext.cpp63
-rw-r--r--src/plugins/platforms/winrt/qwinrteglcontext.h63
-rw-r--r--src/plugins/platforms/winrt/qwinrteventdispatcher.cpp79
-rw-r--r--src/plugins/platforms/winrt/qwinrteventdispatcher.h80
-rw-r--r--src/plugins/platforms/winrt/qwinrtfontdatabase.cpp64
-rw-r--r--src/plugins/platforms/winrt/qwinrtfontdatabase.h57
-rw-r--r--src/plugins/platforms/winrt/qwinrtinputcontext.cpp300
-rw-r--r--src/plugins/platforms/winrt/qwinrtinputcontext.h121
-rw-r--r--src/plugins/platforms/winrt/qwinrtintegration.cpp194
-rw-r--r--src/plugins/platforms/winrt/qwinrtintegration.h86
-rw-r--r--src/plugins/platforms/winrt/qwinrtscreen.cpp975
-rw-r--r--src/plugins/platforms/winrt/qwinrtscreen.h168
-rw-r--r--src/plugins/platforms/winrt/qwinrtservices.cpp138
-rw-r--r--src/plugins/platforms/winrt/qwinrtservices.h80
-rw-r--r--src/plugins/platforms/winrt/qwinrtwindow.cpp119
-rw-r--r--src/plugins/platforms/winrt/qwinrtwindow.h72
-rw-r--r--src/plugins/platforms/winrt/winrt.json3
-rw-r--r--src/plugins/platforms/winrt/winrt.pro55
-rw-r--r--src/plugins/platforms/xcb/qglxintegration.cpp38
-rw-r--r--src/plugins/platforms/xcb/qglxintegration.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp35
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbsessionmanager.cpp1
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.cpp2
-rw-r--r--src/printsupport/widgets/qprintpreviewwidget.cpp7
-rw-r--r--src/sql/kernel/qsqlquery.cpp17
-rw-r--r--src/src.pro8
-rw-r--r--src/testlib/qtestcase.cpp48
-rw-r--r--src/testlib/qtestcase.h47
-rw-r--r--src/testlib/qtestlog.cpp59
-rw-r--r--src/testlib/qtestlog_p.h2
-rw-r--r--src/tools/uic/cpp/cppwriteincludes.cpp3
-rw-r--r--src/tools/uic/main.cpp6
-rw-r--r--src/tools/uic/option.h1
-rw-r--r--src/widgets/dialogs/qcolordialog.cpp4
-rw-r--r--src/widgets/dialogs/qfiledialog.cpp5
-rw-r--r--src/widgets/dialogs/qfilesystemmodel.cpp2
-rw-r--r--src/widgets/dialogs/qfontdialog.cpp6
-rw-r--r--src/widgets/dialogs/qinputdialog.cpp9
-rw-r--r--src/widgets/dialogs/qmessagebox.cpp4
-rw-r--r--src/widgets/itemviews/qfileiconprovider.cpp8
-rw-r--r--src/widgets/itemviews/qtreewidget.cpp5
-rw-r--r--src/widgets/kernel/qapplication.cpp12
-rw-r--r--src/widgets/kernel/qapplication_qpa.cpp4
-rw-r--r--src/widgets/kernel/qwidget.cpp2
-rw-r--r--src/widgets/kernel/qwidgetbackingstore.cpp6
-rw-r--r--src/widgets/kernel/win.pri2
-rw-r--r--src/widgets/styles/qwindowsstyle.cpp20
-rw-r--r--src/widgets/util/util.pri2
-rw-r--r--src/widgets/widgets/qabstractspinbox.cpp33
-rw-r--r--src/widgets/widgets/qabstractspinbox.h4
-rw-r--r--src/widgets/widgets/qabstractspinbox_p.h1
-rw-r--r--src/widgets/widgets/qcombobox.cpp12
-rw-r--r--src/widgets/widgets/qdatetimeedit.cpp10
-rw-r--r--src/widgets/widgets/qdockarealayout.cpp4
-rw-r--r--src/widgets/widgets/qfontcombobox.cpp7
-rw-r--r--src/widgets/widgets/qlcdnumber.h1
-rw-r--r--src/widgets/widgets/qmdiarea.cpp3
-rw-r--r--src/widgets/widgets/qmenu.cpp4
-rw-r--r--src/widgets/widgets/qplaintextedit.cpp22
-rw-r--r--src/widgets/widgets/qspinbox.cpp16
-rw-r--r--src/widgets/widgets/qsplashscreen.cpp4
-rw-r--r--src/widgets/widgets/qtabwidget.cpp3
-rw-r--r--src/winmain/qtmain_winrt.cpp18
-rw-r--r--tests/auto/corelib/codecs/qtextcodec/echo/main.cpp3
-rw-r--r--tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp93
-rw-r--r--tests/auto/corelib/io/io.pro7
-rw-r--r--tests/auto/corelib/io/qfile/test/test.pro7
-rw-r--r--tests/auto/corelib/io/qfile/tst_qfile.cpp24
-rw-r--r--tests/auto/corelib/io/qfileinfo/qfileinfo.pro2
-rw-r--r--tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp16
-rw-r--r--tests/auto/corelib/io/qfilesystemwatcher/tst_qfilesystemwatcher.cpp6
-rw-r--r--tests/auto/corelib/io/qsettings/tst_qsettings.cpp8
-rw-r--r--tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp6
-rw-r--r--tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp4
-rw-r--r--tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp4
-rw-r--r--tests/auto/corelib/kernel/kernel.pro2
-rw-r--r--tests/auto/corelib/kernel/qeventloop/qeventloop.pro2
-rw-r--r--tests/auto/corelib/kernel/qmetatype/qmetatype.pro4
-rw-r--r--tests/auto/corelib/kernel/qobject/tst_qobject.cpp49
-rw-r--r--tests/auto/corelib/thread/qthread/tst_qthread.cpp36
-rw-r--r--tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp25
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp2
-rw-r--r--tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp4
-rw-r--r--tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp5
-rw-r--r--tests/auto/corelib/tools/qlocale/tst_qlocale.cpp6
-rw-r--r--tests/auto/corelib/tools/qstring/tst_qstring.cpp2
-rw-r--r--tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp2
-rw-r--r--tests/auto/gui/image/qpixmap/qpixmap.pro2
-rw-r--r--tests/auto/gui/image/qpixmap/tst_qpixmap.cpp6
-rw-r--r--tests/auto/gui/kernel/qguivariant/test/black.pngbin0 -> 697 bytes
-rw-r--r--tests/auto/gui/kernel/qguivariant/test/black2.pngbin0 -> 697 bytes
-rw-r--r--tests/auto/gui/kernel/qguivariant/test/test.pro1
-rw-r--r--tests/auto/gui/kernel/qguivariant/test/tst_qguivariant.cpp29
-rw-r--r--tests/auto/gui/kernel/qguivariant/test/tst_qguivariant.qrc6
-rw-r--r--tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp50
-rw-r--r--tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp3
-rw-r--r--tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp2
-rw-r--r--tests/auto/printsupport/dialogs/dialogs.pro3
-rw-r--r--tests/auto/printsupport/dialogs/qabstractprintdialog/.gitignore (renamed from tests/auto/widgets/dialogs/qabstractprintdialog/.gitignore)0
-rw-r--r--tests/auto/printsupport/dialogs/qabstractprintdialog/qabstractprintdialog.pro (renamed from tests/auto/widgets/dialogs/qabstractprintdialog/qabstractprintdialog.pro)0
-rw-r--r--tests/auto/printsupport/dialogs/qabstractprintdialog/tst_qabstractprintdialog.cpp (renamed from tests/auto/widgets/dialogs/qabstractprintdialog/tst_qabstractprintdialog.cpp)0
-rw-r--r--tests/auto/printsupport/printsupport.pro1
-rw-r--r--tests/auto/sql/kernel/qsqldatabase/tst_databases.h7
-rw-r--r--tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp3
-rw-r--r--tests/auto/testlib/selftests/crashes/tst_crashes.cpp2
-rw-r--r--tests/auto/testlib/selftests/expected_verifyexceptionthrown.lightxml49
-rw-r--r--tests/auto/testlib/selftests/expected_verifyexceptionthrown.txt21
-rw-r--r--tests/auto/testlib/selftests/expected_verifyexceptionthrown.xml52
-rw-r--r--tests/auto/testlib/selftests/expected_verifyexceptionthrown.xunitxml31
-rw-r--r--tests/auto/testlib/selftests/expected_warnings.lightxml14
-rw-r--r--tests/auto/testlib/selftests/expected_warnings.txt6
-rw-r--r--tests/auto/testlib/selftests/expected_warnings.xml14
-rw-r--r--tests/auto/testlib/selftests/expected_warnings.xunitxml11
-rw-r--r--tests/auto/testlib/selftests/selftests.pri1
-rw-r--r--tests/auto/testlib/selftests/selftests.qrc4
-rw-r--r--tests/auto/testlib/selftests/tst_selftests.cpp4
-rw-r--r--tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp162
-rw-r--r--tests/auto/testlib/selftests/verifyexceptionthrown/verifyexceptionthrown.pro8
-rw-r--r--tests/auto/testlib/selftests/warnings/tst_warnings.cpp17
-rw-r--r--tests/auto/tools/uic/baseline/translation/Dialog_without_Buttons_tr.h50
-rw-r--r--tests/auto/tools/uic/tst_uic.cpp52
-rw-r--r--tests/auto/widgets/dialogs/dialogs.pro3
-rw-r--r--tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp7
-rw-r--r--tests/auto/widgets/graphicsview/qgraphicsitem/qgraphicsitem.pro2
-rw-r--r--tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp2
-rw-r--r--tests/auto/widgets/graphicsview/qgraphicsscene/qgraphicsscene.pro2
-rw-r--r--tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp2
-rw-r--r--tests/auto/widgets/itemviews/qitemdelegate/qitemdelegate.pro2
-rw-r--r--tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp2
-rw-r--r--tests/auto/widgets/itemviews/qlistview/qlistview.pro2
-rw-r--r--tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp4
-rw-r--r--tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp4
-rw-r--r--tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp24
-rw-r--r--tests/auto/widgets/kernel/qwidget/qwidget.pro2
-rw-r--r--tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp24
-rw-r--r--tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp2
-rw-r--r--tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp46
-rw-r--r--tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp49
-rw-r--r--tests/auto/widgets/widgets/qtabwidget/qtabwidget.pro2
-rw-r--r--tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp2
-rw-r--r--tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp15
-rw-r--r--tests/baselineserver/shared/baselineprotocol.cpp6
-rw-r--r--tests/benchmarks/corelib/io/qdir/10000/bench_qdir_10000.cpp5
-rw-r--r--tests/benchmarks/corelib/io/qdiriterator/main.cpp5
-rw-r--r--tests/benchmarks/corelib/io/qdiriterator/qfilesystemiterator.cpp5
-rw-r--r--tests/benchmarks/corelib/io/qfile/main.cpp26
-rw-r--r--tests/benchmarks/corelib/io/qfileinfo/main.cpp4
-rw-r--r--tests/benchmarks/corelib/thread/qmutex/tst_qmutex.cpp4
-rw-r--r--tests/benchmarks/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp174
-rw-r--r--tests/benchmarks/corelib/thread/thread.pro1
-rw-r--r--tests/shared/filesystem.h2
-rw-r--r--tools/configure/configureapp.cpp36
348 files changed, 22216 insertions, 8426 deletions
diff --git a/config.tests/unix/qqnx_imf/qqnx_imf.cpp b/config.tests/unix/qqnx_imf/qqnx_imf.cpp
new file mode 100644
index 0000000000..b88593bf33
--- /dev/null
+++ b/config.tests/unix/qqnx_imf/qqnx_imf.cpp
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the config.tests 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "imf/imf_client.h"
+
+int main(int, char **)
+{
+ imf_client_init();
+ return 0;
+}
diff --git a/config.tests/unix/qqnx_imf/qqnx_imf.pro b/config.tests/unix/qqnx_imf/qqnx_imf.pro
new file mode 100644
index 0000000000..c51adb65ad
--- /dev/null
+++ b/config.tests/unix/qqnx_imf/qqnx_imf.pro
@@ -0,0 +1,3 @@
+SOURCES = qqnx_imf.cpp
+CONFIG -= qt
+LIBS += -linput_client
diff --git a/configure b/configure
index 120150ca87..c3297a691a 100755
--- a/configure
+++ b/configure
@@ -630,6 +630,7 @@ CFG_PKGCONFIG=auto
CFG_STACK_PROTECTOR_STRONG=auto
CFG_SLOG2=auto
CFG_PPS=auto
+CFG_QNX_IMF=auto
CFG_SYSTEM_PROXIES=no
# Target architecture
@@ -1770,6 +1771,13 @@ while [ "$#" -gt 0 ]; do
UNKNOWN_OPT=yes
fi
;;
+ imf)
+ if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then
+ CFG_QNX_IMF="$VAL"
+ else
+ UNKNOWN_OPT=yes
+ fi
+ ;;
pps)
if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then
CFG_PPS="$VAL"
@@ -3731,6 +3739,8 @@ if [ "$CFG_COMPILE_EXAMPLES" = "yes" ]; then
fi
#-------------------------------------------------------------------------------
+ -no-imf ............ Do not compile with imf support.
+ -imf ............... Compile with imf support.
# generate qconfig.cpp
#-------------------------------------------------------------------------------
[ -d "$outpath/src/corelib/global" ] || mkdir -p "$outpath/src/corelib/global"
@@ -4356,7 +4366,14 @@ if [ "$XPLATFORM_QNX" = "yes" ]; then
CFG_SLOG2=no
fi
fi
-
+ if [ "$CFG_QNX_IMF" != "no" ]; then
+ if compileTest unix/qqnx_imf "qqnx_imf"; then
+ CFG_QNX_IMF=yes
+ QMAKE_CONFIG="$QMAKE_CONFIG qqnx_imf"
+ else
+ CFG_QNX_IMF=no
+ fi
+ fi
if [ "$CFG_PPS" != "no" ]; then
if compileTest unix/pps "pps"; then
CFG_PPS=yes
@@ -6707,6 +6724,7 @@ fi
report_support " Session management ....." "$CFG_SM"
if [ "$XPLATFORM_QNX" = "yes" ]; then
report_support " SLOG2 .................." "$CFG_SLOG2"
+ report_support " IMF ...................." "$CFG_QNX_IMF"
report_support " PPS ...................." "$CFG_PPS"
fi
report_support " SQL drivers:"
diff --git a/dist/changes-5.3.0 b/dist/changes-5.3.0
new file mode 100644
index 0000000000..35fc9cabb0
--- /dev/null
+++ b/dist/changes-5.3.0
@@ -0,0 +1,36 @@
+Qt 5.3 introduces many new features and improvements as well as bugfixes
+over the 5.2.x series. For more details, refer to the online documentation
+included in this distribution. The documentation is also available online:
+
+ http://qt-project.org/doc/qt-5.3
+
+The Qt version 5.3 series is binary compatible with the 5.2.x series.
+Applications compiled for 5.2 will continue to run with 5.3.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+ http://bugreports.qt-project.org/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* Library *
+****************************************************************************
+
+QtWidgets
+---------
+
+QtCore
+------
+
+ - Added QSignalBlocker, a RAII-style wrapper around
+ QObject::blockSignals().
+
+QtGui
+-----
+
+ - Added setSwapInterval() to QSurfaceFormat. Platforms that support
+ setting the swap interval are now defaulting to the value of 1,
+ meaning vsync is enabled.
diff --git a/examples/network/dnslookup/dnslookup.cpp b/examples/network/dnslookup/dnslookup.cpp
index 77e8abc927..202a5f9580 100644
--- a/examples/network/dnslookup/dnslookup.cpp
+++ b/examples/network/dnslookup/dnslookup.cpp
@@ -50,7 +50,7 @@
static void usage() {
printf("Qt DNS example - performs DNS lookups\n"
- "Usage: dnslookup [-t <type>] name\n\n");
+ "Usage: dnslookup [-t <type>] [-s nameserver] name\n\n");
}
DnsManager::DnsManager()
@@ -93,6 +93,17 @@ void DnsManager::execute()
return;
}
}
+ if (args.size() > 1 && args.first() == "-s") {
+ args.takeFirst();
+ const QString ns = args.takeFirst();
+ QHostAddress nameserver(ns);
+ if (nameserver.isNull() || nameserver.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol) {
+ printf("Bad nameserver address: %s\n", qPrintable(ns));
+ QCoreApplication::instance()->quit();
+ return;
+ }
+ dns->setNameserver(nameserver);
+ }
if (args.isEmpty()) {
usage();
QCoreApplication::instance()->quit();
diff --git a/examples/network/network.pro b/examples/network/network.pro
index be4ccdbddf..8ed72315e2 100644
--- a/examples/network/network.pro
+++ b/examples/network/network.pro
@@ -23,7 +23,7 @@ qtHaveModule(widgets) {
multicastsender
# no QProcess
- !vxworks:!qnx:SUBDIRS += network-chat
+ !vxworks:!qnx:!winrt:SUBDIRS += network-chat
contains(QT_CONFIG, openssl):SUBDIRS += securesocketclient
contains(QT_CONFIG, openssl-linked):SUBDIRS += securesocketclient
diff --git a/examples/widgets/widgets/spinboxes/window.cpp b/examples/widgets/widgets/spinboxes/window.cpp
index acce642ec6..7c2f6e45bc 100644
--- a/examples/widgets/widgets/spinboxes/window.cpp
+++ b/examples/widgets/widgets/spinboxes/window.cpp
@@ -94,6 +94,16 @@ void Window::createSpinBoxes()
priceSpinBox->setValue(99);
//! [4] //! [5]
+ groupSeparatorSpinBox = new QSpinBox;
+ groupSeparatorSpinBox->setRange(-99999999, 99999999);
+ groupSeparatorSpinBox->setValue(1000);
+ groupSeparatorSpinBox->setGroupSeparatorShown(true);
+ QCheckBox *groupSeparatorChkBox = new QCheckBox;
+ groupSeparatorChkBox->setText(tr("Show group separator"));
+ groupSeparatorChkBox->setChecked(true);
+ connect(groupSeparatorChkBox, &QCheckBox::toggled, groupSeparatorSpinBox,
+ &QSpinBox::setGroupSeparatorShown);
+
QLabel *hexLabel = new QLabel(tr("Enter a value between "
"%1 and %2:").arg('-' + QString::number(31, 16)).arg(QString::number(31, 16)));
QSpinBox *hexSpinBox = new QSpinBox;
@@ -111,6 +121,8 @@ void Window::createSpinBoxes()
spinBoxLayout->addWidget(priceSpinBox);
spinBoxLayout->addWidget(hexLabel);
spinBoxLayout->addWidget(hexSpinBox);
+ spinBoxLayout->addWidget(groupSeparatorChkBox);
+ spinBoxLayout->addWidget(groupSeparatorSpinBox);
spinBoxesGroup->setLayout(spinBoxLayout);
}
//! [5]
@@ -237,6 +249,17 @@ void Window::createDoubleSpinBoxes()
//! [17]
this, SLOT(changePrecision(int)));
+ groupSeparatorSpinBox_d = new QDoubleSpinBox;
+ groupSeparatorSpinBox_d->setRange(-99999999, 99999999);
+ groupSeparatorSpinBox_d->setDecimals(2);
+ groupSeparatorSpinBox_d->setValue(1000.00);
+ groupSeparatorSpinBox_d->setGroupSeparatorShown(true);
+ QCheckBox *groupSeparatorChkBox = new QCheckBox;
+ groupSeparatorChkBox->setText(tr("Show group separator"));
+ groupSeparatorChkBox->setChecked(true);
+ connect(groupSeparatorChkBox, &QCheckBox::toggled, groupSeparatorSpinBox_d,
+ &QDoubleSpinBox::setGroupSeparatorShown);
+
//! [18]
QVBoxLayout *spinBoxLayout = new QVBoxLayout;
spinBoxLayout->addWidget(precisionLabel);
@@ -247,6 +270,8 @@ void Window::createDoubleSpinBoxes()
spinBoxLayout->addWidget(scaleSpinBox);
spinBoxLayout->addWidget(priceLabel);
spinBoxLayout->addWidget(priceSpinBox);
+ spinBoxLayout->addWidget(groupSeparatorChkBox);
+ spinBoxLayout->addWidget(groupSeparatorSpinBox_d);
doubleSpinBoxesGroup->setLayout(spinBoxLayout);
}
//! [18]
diff --git a/examples/widgets/widgets/spinboxes/window.h b/examples/widgets/widgets/spinboxes/window.h
index ef7af04f59..32622c2c24 100644
--- a/examples/widgets/widgets/spinboxes/window.h
+++ b/examples/widgets/widgets/spinboxes/window.h
@@ -45,6 +45,7 @@
QT_BEGIN_NAMESPACE
class QDateTimeEdit;
+class QSpinBox;
class QDoubleSpinBox;
class QGroupBox;
class QLabel;
@@ -75,6 +76,8 @@ private:
QGroupBox *editsGroup;
QGroupBox *doubleSpinBoxesGroup;
QLabel *meetingLabel;
+ QSpinBox *groupSeparatorSpinBox;
+ QDoubleSpinBox *groupSeparatorSpinBox_d;
};
//! [0]
diff --git a/mkspecs/common/winrt_winphone/qmake.conf b/mkspecs/common/winrt_winphone/qmake.conf
index a1bf19d3aa..6bcbf2ff66 100644
--- a/mkspecs/common/winrt_winphone/qmake.conf
+++ b/mkspecs/common/winrt_winphone/qmake.conf
@@ -1,16 +1,15 @@
#
# qmake configuration for winrt and windows phone 8
#
-# Written for Microsoft Visual C++ 2012
+# Written for Microsoft Visual C++
#
MAKEFILE_GENERATOR = MSBUILD
QMAKE_COMPILER = msvc
QMAKE_PLATFORM = winrt win32
CONFIG += incremental flat precompile_header autogen_precompile_source debug_and_release debug_and_release_target no_generated_target_info autogen_wmappmanifest rtti
-DEFINES += UNICODE WINRT QT_LARGEFILE_SUPPORT Q_BYTE_ORDER=Q_LITTLE_ENDIAN \
+DEFINES += UNICODE WIN32 QT_LARGEFILE_SUPPORT Q_BYTE_ORDER=Q_LITTLE_ENDIAN \
QT_NO_PRINTER QT_NO_PRINTDIALOG # TODO: Remove when printing is re-enabled
-QMAKE_COMPILER_DEFINES += _MSC_VER=1700 WINRT
DEPLOYMENT_PLUGIN += qwinrt
@@ -19,7 +18,6 @@ QMAKE_LEX = flex
QMAKE_LEXFLAGS =
QMAKE_YACC = byacc
QMAKE_YACCFLAGS = -d
-#QMAKE_CFLAGS = -nologo -Zm200 -Zc:wchar_t-
QMAKE_CFLAGS = -nologo -Zm200
QMAKE_CFLAGS_WARN_ON = -W3
QMAKE_CFLAGS_WARN_OFF = -W0
@@ -68,16 +66,18 @@ QMAKE_LFLAGS = /NOLOGO /DYNAMICBASE /NXCOMPAT /NODEFAULTLIB:ole32.lib
QMAKE_LFLAGS_RELEASE = /INCREMENTAL:NO
QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO = /DEBUG /OPT:REF
QMAKE_LFLAGS_DEBUG = /DEBUG
-QMAKE_LFLAGS_CONSOLE = /SUBSYSTEM:CONSOLE
QMAKE_LFLAGS_WINDOWS = /SUBSYSTEM:WINDOWS
-QMAKE_LFLAGS_EXE = /WINMD /MANIFEST:NO
-QMAKE_LFLAGS_DLL = /WINMD /MANIFEST:NO /DLL /WINMDFILE:$(DESTDIR_TARGET).winmd
+QMAKE_LFLAGS_EXE = /MANIFEST:NO
+QMAKE_LFLAGS_DLL = /MANIFEST:NO /DLL
QMAKE_LFLAGS_LTCG = /LTCG
QMAKE_EXTENSION_STATICLIB = lib
+QMAKE_LIBS += runtimeobject.lib
QMAKE_LIBS_CORE =
-QMAKE_LIBS_GUI = d3d11.lib
+QMAKE_LIBS_GUI =
QMAKE_LIBS_NETWORK =
+QMAKE_LIBS_OPENGL_ES2 = libEGL.lib libGLESv2.lib
+QMAKE_LIBS_OPENGL_ES2_DEBUG = libEGLd.lib libGLESv2d.lib
QMAKE_LIBS_QT_ENTRY = -lqtmain /ENTRY:wmainCRTStartup
@@ -89,5 +89,5 @@ include(../shell-win32.conf)
VCPROJ_EXTENSION = .vcxproj
VCSOLUTION_EXTENSION = .sln
-VCPROJ_KEYWORD = Qt4VSv1.0
+VCPROJ_KEYWORD = Qt4VSv1.0
load(qt_config)
diff --git a/mkspecs/common/winrt_winphone/qplatformdefs.h b/mkspecs/common/winrt_winphone/qplatformdefs.h
new file mode 100644
index 0000000000..96f20569d2
--- /dev/null
+++ b/mkspecs/common/winrt_winphone/qplatformdefs.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the qmake spec 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPLATFORMDEFS_H
+#define QPLATFORMDEFS_H
+
+#ifdef UNICODE
+#ifndef _UNICODE
+#define _UNICODE
+#endif
+#endif
+
+// Get Qt defines/settings
+
+#include "qglobal.h"
+#include "qfunctions_winrt.h"
+
+#define _POSIX_
+#include <limits.h>
+#undef _POSIX_
+
+#include <tchar.h>
+#include <io.h>
+#include <direct.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+
+#ifdef QT_LARGEFILE_SUPPORT
+#define QT_STATBUF struct _stati64 // non-ANSI defs
+#define QT_STATBUF4TSTAT struct _stati64 // non-ANSI defs
+#define QT_STAT ::_stati64
+#define QT_FSTAT ::_fstati64
+#else
+#define QT_STATBUF struct _stat // non-ANSI defs
+#define QT_STATBUF4TSTAT struct _stat // non-ANSI defs
+#define QT_STAT ::_stat
+#define QT_FSTAT ::_fstat
+#endif
+#define QT_STAT_REG _S_IFREG
+#define QT_STAT_DIR _S_IFDIR
+#define QT_STAT_MASK _S_IFMT
+#if defined(_S_IFLNK)
+# define QT_STAT_LNK _S_IFLNK
+#endif
+#define QT_FILENO _fileno
+#define QT_OPEN ::_open
+#define QT_CLOSE ::_close
+#ifdef QT_LARGEFILE_SUPPORT
+#define QT_LSEEK ::_lseeki64
+#define QT_TSTAT ::_tstati64
+#else
+#define QT_LSEEK ::_lseek
+#define QT_TSTAT ::_tstat
+#endif
+#define QT_READ ::_read
+#define QT_WRITE ::_write
+#define QT_ACCESS ::_access
+#define QT_GETCWD ::_getcwd
+#define QT_CHDIR ::_chdir
+#define QT_MKDIR ::_mkdir
+#define QT_RMDIR ::_rmdir
+#define QT_OPEN_LARGEFILE 0
+#define QT_OPEN_RDONLY _O_RDONLY
+#define QT_OPEN_WRONLY _O_WRONLY
+#define QT_OPEN_RDWR _O_RDWR
+#define QT_OPEN_CREAT _O_CREAT
+#define QT_OPEN_TRUNC _O_TRUNC
+#define QT_OPEN_APPEND _O_APPEND
+#if defined(O_TEXT)
+# define QT_OPEN_TEXT _O_TEXT
+# define QT_OPEN_BINARY _O_BINARY
+#endif
+
+#include "../common/c89/qplatformdefs.h"
+
+#ifdef QT_LARGEFILE_SUPPORT
+#undef QT_FSEEK
+#undef QT_FTELL
+#undef QT_OFF_T
+
+#define QT_FSEEK ::_fseeki64
+#define QT_FTELL ::_ftelli64
+#define QT_OFF_T __int64
+#endif
+
+#define QT_SIGNAL_ARGS int
+
+#define QT_VSNPRINTF(buffer, count, format, arg) \
+ vsnprintf_s(buffer, count, count-1, format, arg)
+
+#define QT_SNPRINTF ::_snprintf
+
+# define F_OK 0
+# define X_OK 1
+# define W_OK 2
+# define R_OK 4
+
+typedef int mode_t;
+
+#endif // QPLATFORMDEFS_H
diff --git a/mkspecs/features/configure.prf b/mkspecs/features/configure.prf
index 39144e7216..fe41c541a2 100644
--- a/mkspecs/features/configure.prf
+++ b/mkspecs/features/configure.prf
@@ -53,6 +53,12 @@ defineTest(qtCompileTest) {
# Disable qmake features which are typically counterproductive for tests
qmake_configs = "\"CONFIG -= qt debug_and_release app_bundle lib_bundle\""
+ # On WinRT we need to change the entry point as we cannot create windows
+ # applications
+ winrt {
+ qmake_configs += " \"QMAKE_LFLAGS+=/ENTRY:main\""
+ }
+
# Clean up after previous run
exists($$test_out_dir/Makefile):qtRunLoggedCommand("$$test_cmd_base $$QMAKE_MAKE distclean")
diff --git a/mkspecs/features/winrt/console.prf b/mkspecs/features/winrt/console.prf
new file mode 100644
index 0000000000..c4afe5b96e
--- /dev/null
+++ b/mkspecs/features/winrt/console.prf
@@ -0,0 +1,5 @@
+# This is an empty prf file to overwrite the win32 version.
+# On Windows RT all applications need to be windows applications
+# and also link to winmain. Inside winmain we create the launch
+# arguments and also initialize the UI.
+
diff --git a/mkspecs/qnx-armv7le-qcc/qplatformdefs.h b/mkspecs/qnx-armv7le-qcc/qplatformdefs.h
index 2fac2ae3d2..b47aecde0d 100644
--- a/mkspecs/qnx-armv7le-qcc/qplatformdefs.h
+++ b/mkspecs/qnx-armv7le-qcc/qplatformdefs.h
@@ -83,8 +83,15 @@
#include <arpa/inet.h>
#define QT_USE_XOPEN_LFS_EXTENSIONS
+#if !defined(__EXT_QNX__READDIR64_R)
#define QT_NO_READDIR64
+#endif
#include "../common/posix/qplatformdefs.h"
+#if defined(__EXT_QNX__READDIR64_R)
+#define QT_EXT_QNX_READDIR_R ::_readdir64_r
+#elif defined(__EXT_QNX__READDIR_R)
+#define QT_EXT_QNX_READDIR_R ::_readdir_r
+#endif
#define QT_SNPRINTF ::snprintf
#define QT_VSNPRINTF ::vsnprintf
diff --git a/mkspecs/qnx-x86-qcc/qplatformdefs.h b/mkspecs/qnx-x86-qcc/qplatformdefs.h
index 2fac2ae3d2..b47aecde0d 100644
--- a/mkspecs/qnx-x86-qcc/qplatformdefs.h
+++ b/mkspecs/qnx-x86-qcc/qplatformdefs.h
@@ -83,8 +83,15 @@
#include <arpa/inet.h>
#define QT_USE_XOPEN_LFS_EXTENSIONS
+#if !defined(__EXT_QNX__READDIR64_R)
#define QT_NO_READDIR64
+#endif
#include "../common/posix/qplatformdefs.h"
+#if defined(__EXT_QNX__READDIR64_R)
+#define QT_EXT_QNX_READDIR_R ::_readdir64_r
+#elif defined(__EXT_QNX__READDIR_R)
+#define QT_EXT_QNX_READDIR_R ::_readdir_r
+#endif
#define QT_SNPRINTF ::snprintf
#define QT_VSNPRINTF ::vsnprintf
diff --git a/mkspecs/unsupported/linux-libc++-clang/qmake.conf b/mkspecs/unsupported/linux-libc++-clang/qmake.conf
new file mode 100644
index 0000000000..bf0abb2a54
--- /dev/null
+++ b/mkspecs/unsupported/linux-libc++-clang/qmake.conf
@@ -0,0 +1,20 @@
+#
+# qmake configuration for linux-clang
+#
+
+MAKEFILE_GENERATOR = UNIX
+CONFIG += incremental
+
+QMAKE_INCREMENTAL_STYLE = sublib
+
+include(../../common/linux.conf)
+include(../../common/gcc-base-unix.conf)
+include(../../common/clang.conf)
+
+QMAKE_CFLAGS_RELEASE = -Os
+QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE
+
+QMAKE_CXXFLAGS_CXX11 += -std=c++11 -stdlib=libc++
+QMAKE_LFLAGS_CXX11 += -stdlib=libc++ -lc++abi
+
+load(qt_config)
diff --git a/mkspecs/unsupported/linux-libc++-clang/qplatformdefs.h b/mkspecs/unsupported/linux-libc++-clang/qplatformdefs.h
new file mode 100644
index 0000000000..c1066ee9a2
--- /dev/null
+++ b/mkspecs/unsupported/linux-libc++-clang/qplatformdefs.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the qmake spec 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPLATFORMDEFS_H
+#define QPLATFORMDEFS_H
+
+// Get Qt defines/settings
+
+#include "qglobal.h"
+
+// Set any POSIX/XOPEN defines at the top of this file to turn on specific APIs
+
+// 1) need to reset default environment if _BSD_SOURCE is defined
+// 2) need to specify POSIX thread interfaces explicitly in glibc 2.0
+// 3) it seems older glibc need this to include the X/Open stuff
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#include <unistd.h>
+
+
+// We are hot - unistd.h should have turned on the specific APIs we requested
+
+#include <features.h>
+#include <pthread.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/ipc.h>
+#include <sys/time.h>
+#include <sys/shm.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#ifndef QT_NO_IPV6IFNAME
+#include <net/if.h>
+#endif
+
+#define QT_USE_XOPEN_LFS_EXTENSIONS
+#include "../../common/posix/qplatformdefs.h"
+
+#undef QT_SOCKLEN_T
+
+#if defined(__GLIBC__) && (__GLIBC__ >= 2)
+#define QT_SOCKLEN_T socklen_t
+#else
+#define QT_SOCKLEN_T int
+#endif
+
+#if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500)
+#define QT_SNPRINTF ::snprintf
+#define QT_VSNPRINTF ::vsnprintf
+#endif
+
+#endif // QPLATFORMDEFS_H
diff --git a/mkspecs/winphone-arm-msvc2012/qmake.conf b/mkspecs/winphone-arm-msvc2012/qmake.conf
index 1e31ac51d0..d5d26d2a96 100644
--- a/mkspecs/winphone-arm-msvc2012/qmake.conf
+++ b/mkspecs/winphone-arm-msvc2012/qmake.conf
@@ -5,6 +5,7 @@
#
include(../common/winrt_winphone/qmake.conf)
+QMAKE_COMPILER_DEFINES += _MSC_VER=1700
QMAKE_PLATFORM = winphone $$QMAKE_PLATFORM
DEFINES += WINAPI_FAMILY=WINAPI_FAMILY_PHONE_APP ARM __ARM__ __arm__ QT_NO_CURSOR
@@ -13,3 +14,6 @@ QMAKE_LFLAGS += /MACHINE:ARM
QMAKE_LIBS += WindowsPhoneCore.lib PhoneAppModelHost.lib ws2_32.lib
VCPROJ_ARCH = ARM
+MSVC_VER = 11.0
+WINSDK_VER = 8.0
+WINTARGET_VER = WP80
diff --git a/mkspecs/winphone-arm-msvc2012/qplatformdefs.h b/mkspecs/winphone-arm-msvc2012/qplatformdefs.h
index e03bce8e6c..781107b2dc 100644
--- a/mkspecs/winphone-arm-msvc2012/qplatformdefs.h
+++ b/mkspecs/winphone-arm-msvc2012/qplatformdefs.h
@@ -39,4 +39,4 @@
**
****************************************************************************/
-#include "../win32-msvc2005/qplatformdefs.h"
+#include "../common/winrt_winphone/qplatformdefs.h"
diff --git a/mkspecs/winphone-x86-msvc2012/qmake.conf b/mkspecs/winphone-x86-msvc2012/qmake.conf
index 8836de58cf..59e6fb45db 100644
--- a/mkspecs/winphone-x86-msvc2012/qmake.conf
+++ b/mkspecs/winphone-x86-msvc2012/qmake.conf
@@ -5,6 +5,7 @@
#
include(../common/winrt_winphone/qmake.conf)
+QMAKE_COMPILER_DEFINES += _MSC_VER=1700
QMAKE_PLATFORM = winphone $$QMAKE_PLATFORM
DEFINES += WINAPI_FAMILY=WINAPI_FAMILY_PHONE_APP X86 __X86__ __x86__ QT_NO_CURSOR
@@ -13,3 +14,6 @@ QMAKE_LFLAGS += /MACHINE:X86
QMAKE_LIBS += WindowsPhoneCore.lib PhoneAppModelHost.lib ws2_32.lib
VCPROJ_ARCH = x86
+MSVC_VER = 11.0
+WINSDK_VER = 8.0
+WINTARGET_VER = WP80
diff --git a/mkspecs/winphone-x86-msvc2012/qplatformdefs.h b/mkspecs/winphone-x86-msvc2012/qplatformdefs.h
index e03bce8e6c..781107b2dc 100644
--- a/mkspecs/winphone-x86-msvc2012/qplatformdefs.h
+++ b/mkspecs/winphone-x86-msvc2012/qplatformdefs.h
@@ -39,4 +39,4 @@
**
****************************************************************************/
-#include "../win32-msvc2005/qplatformdefs.h"
+#include "../common/winrt_winphone/qplatformdefs.h"
diff --git a/mkspecs/winrt-arm-msvc2012/qmake.conf b/mkspecs/winrt-arm-msvc2012/qmake.conf
index cafdbf4a93..951dc2db35 100644
--- a/mkspecs/winrt-arm-msvc2012/qmake.conf
+++ b/mkspecs/winrt-arm-msvc2012/qmake.conf
@@ -5,6 +5,7 @@
#
include(../common/winrt_winphone/qmake.conf)
+QMAKE_COMPILER_DEFINES += _MSC_VER=1700
DEFINES += WINAPI_FAMILY=WINAPI_FAMILY_APP ARM __ARM__ __arm__
QMAKE_LFLAGS += /MACHINE:ARM
@@ -12,3 +13,6 @@ QMAKE_LFLAGS += /MACHINE:ARM
QMAKE_LIBS += windowscodecs.lib kernel32.lib ole32.lib
VCPROJ_ARCH = ARM
+MSVC_VER = 11.0
+WINSDK_VER = 8.0
+WINTARGET_VER = win8
diff --git a/mkspecs/winrt-arm-msvc2012/qplatformdefs.h b/mkspecs/winrt-arm-msvc2012/qplatformdefs.h
index e03bce8e6c..781107b2dc 100644
--- a/mkspecs/winrt-arm-msvc2012/qplatformdefs.h
+++ b/mkspecs/winrt-arm-msvc2012/qplatformdefs.h
@@ -39,4 +39,4 @@
**
****************************************************************************/
-#include "../win32-msvc2005/qplatformdefs.h"
+#include "../common/winrt_winphone/qplatformdefs.h"
diff --git a/mkspecs/winrt-arm-msvc2013/qmake.conf b/mkspecs/winrt-arm-msvc2013/qmake.conf
new file mode 100644
index 0000000000..5527a7a41c
--- /dev/null
+++ b/mkspecs/winrt-arm-msvc2013/qmake.conf
@@ -0,0 +1,20 @@
+#
+# qmake configuration for winrt-arm-msvc2013
+#
+# Written for Microsoft Visual C++ 2013
+#
+
+include(../common/winrt_winphone/qmake.conf)
+QMAKE_COMPILER_DEFINES += _MSC_VER=1800
+DEFINES += WINAPI_FAMILY=WINAPI_FAMILY_APP ARM __ARM__ __arm__
+
+QMAKE_CFLAGS += -FS
+QMAKE_CXXFLAGS += -FS
+QMAKE_LFLAGS += /MACHINE:ARM
+
+QMAKE_LIBS += windowscodecs.lib kernel32.lib ole32.lib
+
+VCPROJ_ARCH = ARM
+MSVC_VER = 12.0
+WINSDK_VER = 8.1
+WINTARGET_VER = winv6.3
diff --git a/mkspecs/winrt-arm-msvc2013/qplatformdefs.h b/mkspecs/winrt-arm-msvc2013/qplatformdefs.h
new file mode 100644
index 0000000000..781107b2dc
--- /dev/null
+++ b/mkspecs/winrt-arm-msvc2013/qplatformdefs.h
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the qmake spec 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "../common/winrt_winphone/qplatformdefs.h"
diff --git a/mkspecs/winrt-x64-msvc2012/qmake.conf b/mkspecs/winrt-x64-msvc2012/qmake.conf
index 784d0ccb7e..96806499bc 100644
--- a/mkspecs/winrt-x64-msvc2012/qmake.conf
+++ b/mkspecs/winrt-x64-msvc2012/qmake.conf
@@ -5,6 +5,7 @@
#
include(../common/winrt_winphone/qmake.conf)
+QMAKE_COMPILER_DEFINES += _MSC_VER=1700
DEFINES += WINAPI_FAMILY=WINAPI_FAMILY_APP X64 __X64__ __x64__
QMAKE_LFLAGS += /MACHINE:X64
@@ -12,3 +13,6 @@ QMAKE_LFLAGS += /MACHINE:X64
QMAKE_LIBS += windowscodecs.lib kernel32.lib ole32.lib
VCPROJ_ARCH = x64
+MSVC_VER = 11.0
+WINSDK_VER = 8.0
+WINTARGET_VER = win8
diff --git a/mkspecs/winrt-x64-msvc2012/qplatformdefs.h b/mkspecs/winrt-x64-msvc2012/qplatformdefs.h
index e03bce8e6c..781107b2dc 100644
--- a/mkspecs/winrt-x64-msvc2012/qplatformdefs.h
+++ b/mkspecs/winrt-x64-msvc2012/qplatformdefs.h
@@ -39,4 +39,4 @@
**
****************************************************************************/
-#include "../win32-msvc2005/qplatformdefs.h"
+#include "../common/winrt_winphone/qplatformdefs.h"
diff --git a/mkspecs/winrt-x64-msvc2013/qmake.conf b/mkspecs/winrt-x64-msvc2013/qmake.conf
new file mode 100644
index 0000000000..e7d2432cc4
--- /dev/null
+++ b/mkspecs/winrt-x64-msvc2013/qmake.conf
@@ -0,0 +1,20 @@
+#
+# qmake configuration for winrt-x64-msvc2013
+#
+# Written for Microsoft Visual C++ 2013
+#
+
+include(../common/winrt_winphone/qmake.conf)
+QMAKE_COMPILER_DEFINES += _MSC_VER=1800
+DEFINES += WINAPI_FAMILY=WINAPI_FAMILY_APP X64 __X64__ __x64__
+
+QMAKE_CFLAGS += -FS
+QMAKE_CXXFLAGS += -FS
+QMAKE_LFLAGS += /MACHINE:X64
+
+QMAKE_LIBS += windowscodecs.lib kernel32.lib ole32.lib
+
+VCPROJ_ARCH = x64
+MSVC_VER = 12.0
+WINSDK_VER = 8.1
+WINTARGET_VER = winv6.3
diff --git a/mkspecs/winrt-x64-msvc2013/qplatformdefs.h b/mkspecs/winrt-x64-msvc2013/qplatformdefs.h
new file mode 100644
index 0000000000..781107b2dc
--- /dev/null
+++ b/mkspecs/winrt-x64-msvc2013/qplatformdefs.h
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the qmake spec 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "../common/winrt_winphone/qplatformdefs.h"
diff --git a/mkspecs/winrt-x86-msvc2012/qmake.conf b/mkspecs/winrt-x86-msvc2012/qmake.conf
index 559c9b5d05..b0d9017ebb 100644
--- a/mkspecs/winrt-x86-msvc2012/qmake.conf
+++ b/mkspecs/winrt-x86-msvc2012/qmake.conf
@@ -5,6 +5,7 @@
#
include(../common/winrt_winphone/qmake.conf)
+QMAKE_COMPILER_DEFINES += _MSC_VER=1700
DEFINES += WINAPI_FAMILY=WINAPI_FAMILY_APP X86 __X86__ __x86__
QMAKE_LFLAGS += /MACHINE:X86
@@ -12,3 +13,6 @@ QMAKE_LFLAGS += /MACHINE:X86
QMAKE_LIBS += windowscodecs.lib kernel32.lib ole32.lib
VCPROJ_ARCH = x86
+MSVC_VER = 11.0
+WINSDK_VER = 8.0
+WINTARGET_VER = win8
diff --git a/mkspecs/winrt-x86-msvc2012/qplatformdefs.h b/mkspecs/winrt-x86-msvc2012/qplatformdefs.h
index e03bce8e6c..781107b2dc 100644
--- a/mkspecs/winrt-x86-msvc2012/qplatformdefs.h
+++ b/mkspecs/winrt-x86-msvc2012/qplatformdefs.h
@@ -39,4 +39,4 @@
**
****************************************************************************/
-#include "../win32-msvc2005/qplatformdefs.h"
+#include "../common/winrt_winphone/qplatformdefs.h"
diff --git a/mkspecs/winrt-x86-msvc2013/qmake.conf b/mkspecs/winrt-x86-msvc2013/qmake.conf
new file mode 100644
index 0000000000..40fd339b5b
--- /dev/null
+++ b/mkspecs/winrt-x86-msvc2013/qmake.conf
@@ -0,0 +1,20 @@
+#
+# qmake configuration for winrt-x86-msvc2013
+#
+# Written for Microsoft Visual C++ 2013
+#
+
+include(../common/winrt_winphone/qmake.conf)
+QMAKE_COMPILER_DEFINES += _MSC_VER=1800 _WIN32
+DEFINES += WINAPI_FAMILY=WINAPI_FAMILY_APP X86 __X86__ __x86__
+
+QMAKE_CFLAGS += -FS
+QMAKE_CXXFLAGS += -FS
+QMAKE_LFLAGS += /MACHINE:X86
+
+QMAKE_LIBS += windowscodecs.lib kernel32.lib ole32.lib
+
+VCPROJ_ARCH = x86
+MSVC_VER = 12.0
+WINSDK_VER = 8.1
+WINTARGET_VER = winv6.3
diff --git a/mkspecs/winrt-x86-msvc2013/qplatformdefs.h b/mkspecs/winrt-x86-msvc2013/qplatformdefs.h
new file mode 100644
index 0000000000..781107b2dc
--- /dev/null
+++ b/mkspecs/winrt-x86-msvc2013/qplatformdefs.h
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the qmake spec 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "../common/winrt_winphone/qplatformdefs.h"
diff --git a/qmake/generators/win32/msvc_nmake.cpp b/qmake/generators/win32/msvc_nmake.cpp
index 723f2a03e9..6d148e41a2 100644
--- a/qmake/generators/win32/msvc_nmake.cpp
+++ b/qmake/generators/win32/msvc_nmake.cpp
@@ -124,28 +124,61 @@ NmakeMakefileGenerator::writeMakefile(QTextStream &t)
compiler = QStringLiteral("x86_amd64");
compilerArch = QStringLiteral("amd64");
}
+
+ const QString msvcVer = project->first("MSVC_VER").toQString();
+ if (msvcVer.isEmpty()) {
+ fprintf(stderr, "Mkspec does not specify MSVC_VER. Cannot continue.\n");
+ return false;
+ }
+ const QString winsdkVer = project->first("WINSDK_VER").toQString();
+ if (winsdkVer.isEmpty()) {
+ fprintf(stderr, "Mkspec does not specify WINSDK_VER. Cannot continue.\n");
+ return false;
+ }
+ const QString targetVer = project->first("WINTARGET_VER").toQString();
+ if (targetVer.isEmpty()) {
+ fprintf(stderr, "Mkspec does not specify WINTARGET_VER. Cannot continue.\n");
+ return false;
+ }
+
+ const bool isPhone = project->isActiveConfig(QStringLiteral("winphone"));
#ifdef Q_OS_WIN
-#ifdef Q_OS_WIN64
- const QString regKey = QStringLiteral("Software\\Wow6432Node\\Microsoft\\VisualStudio\\11.0\\Setup\\VC\\ProductDir");
-#else
- const QString regKey = QStringLiteral("Software\\Microsoft\\VisualStudio\\11.0\\Setup\\VC\\ProductDir");
+ QString regKeyPrefix;
+#if !defined(Q_OS_WIN64) && _WIN32_WINNT >= 0x0501
+ BOOL isWow64;
+ IsWow64Process(GetCurrentProcess(), &isWow64);
+ if (!isWow64)
+ regKeyPrefix = QStringLiteral("Software\\");
+ else
#endif
+ regKeyPrefix = QStringLiteral("Software\\Wow6432Node\\");
+
+ QString regKey = regKeyPrefix + QStringLiteral("Microsoft\\VisualStudio\\") + msvcVer + ("\\Setup\\VC\\ProductDir");
const QString vcInstallDir = qt_readRegistryKey(HKEY_LOCAL_MACHINE, regKey);
if (vcInstallDir.isEmpty()) {
- fprintf(stderr, "Failed to find the Visual Studio 2012 installation directory.\n"
- "Is it installed?.\n");
+ fprintf(stderr, "Failed to find the Visual Studio installation directory.\n");
+ return false;
+ }
+
+ regKey = regKeyPrefix
+ + (isPhone ? QStringLiteral("Microsoft\\Microsoft SDKs\\WindowsPhone\\v")
+ : QStringLiteral("Microsoft\\Microsoft SDKs\\Windows\\v"))
+ + winsdkVer + QStringLiteral("\\InstallationFolder");
+ const QString kitDir = qt_readRegistryKey(HKEY_LOCAL_MACHINE, regKey);
+ if (kitDir.isEmpty()) {
+ fprintf(stderr, "Failed to find the Windows Kit installation directory.\n");
return false;
}
#else
const QString vcInstallDir = "/fake/vc_install_dir";
+ const QString kitDir = "/fake/sdk_install_dir";
#endif // Q_OS_WIN
QStringList incDirs;
QStringList libDirs;
QStringList binDirs;
- const bool isPhone = project->isActiveConfig(QStringLiteral("winphone"));
if (isPhone) {
- QString sdkDir = vcInstallDir + QStringLiteral("/WPSDK/WP80");
+ QString sdkDir = vcInstallDir + QStringLiteral("/WPSDK/") + targetVer;
if (!QDir(sdkDir).exists()) {
fprintf(stderr, "Failed to find the Windows Phone SDK in %s.\n"
"Check that it is properly installed.\n",
@@ -155,14 +188,6 @@ NmakeMakefileGenerator::writeMakefile(QTextStream &t)
incDirs << sdkDir + QStringLiteral("/include");
libDirs << sdkDir + QStringLiteral("/lib/") + compilerArch;
binDirs << sdkDir + QStringLiteral("/bin/") + compiler;
-
- QString kitDir = vcInstallDir + QStringLiteral("/../../Windows Phone Kits/8.0");
- if (!QDir(kitDir).exists()) {
- fprintf(stderr, "Failed to find the Windows Phone Kit in %s.\n"
- "Check that it is properly installed.\n",
- qPrintable(QDir::toNativeSeparators(kitDir)));
- return false;
- }
libDirs << kitDir + QStringLiteral("/lib/") + arch;
incDirs << kitDir + QStringLiteral("/include")
<< kitDir + QStringLiteral("/include/abi")
@@ -170,18 +195,11 @@ NmakeMakefileGenerator::writeMakefile(QTextStream &t)
<< kitDir + QStringLiteral("/include/minwin");
} else {
incDirs << vcInstallDir + QStringLiteral("/include");
- libDirs << vcInstallDir + QStringLiteral("/lib/") + compilerArch;
+ libDirs << vcInstallDir + QStringLiteral("/lib/store/") + compilerArch
+ << vcInstallDir + QStringLiteral("/lib/") + compilerArch;
binDirs << vcInstallDir + QStringLiteral("/bin/") + compiler
<< vcInstallDir + QStringLiteral("/../Common7/IDE");
-
- QString kitDir = vcInstallDir + QStringLiteral("/../../Windows Kits/8.0");
- if (!QDir(kitDir).exists()) {
- fprintf(stderr, "Failed to find the Windows Kit in %s.\n"
- "Check that it is properly installed.\n",
- qPrintable(QDir::toNativeSeparators(kitDir)));
- return false;
- }
- libDirs << kitDir + QStringLiteral("/Lib/win8/um/") + arch;
+ libDirs << kitDir + QStringLiteral("/Lib/") + targetVer + ("/um/") + arch;
incDirs << kitDir + QStringLiteral("/include/um")
<< kitDir + QStringLiteral("/include/shared")
<< kitDir + QStringLiteral("/include/winrt");
diff --git a/src/3rdparty/angle/include/EGL/eglplatform.h b/src/3rdparty/angle/include/EGL/eglplatform.h
index 34283f2e90..eb15ae569d 100644
--- a/src/3rdparty/angle/include/EGL/eglplatform.h
+++ b/src/3rdparty/angle/include/EGL/eglplatform.h
@@ -67,7 +67,15 @@
* implementations.
*/
-#if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */
+#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) /* Windows Runtime */
+
+struct IUnknown;
+
+typedef int EGLNativeDisplayType;
+typedef void *EGLNativePixmapType;
+typedef IUnknown *EGLNativeWindowType;
+
+#elif defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
diff --git a/src/3rdparty/angle/src/compiler/osinclude.h b/src/3rdparty/angle/src/compiler/osinclude.h
index d8bb1a797c..60177d5fe5 100644
--- a/src/3rdparty/angle/src/compiler/osinclude.h
+++ b/src/3rdparty/angle/src/compiler/osinclude.h
@@ -13,27 +13,26 @@
//
#if defined(_WIN32) || defined(_WIN64)
+#define STRICT
+#define VC_EXTRALEAN 1
+#include <windows.h>
+#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
+#define ANGLE_OS_WINRT
+#else
#define ANGLE_OS_WIN
+#endif
#elif defined(__APPLE__) || defined(__linux__) || \
defined(__FreeBSD__) || defined(__OpenBSD__) || \
defined(__sun) || defined(ANDROID) || \
defined(__GLIBC__) || defined(__GNU__) || \
defined(__QNX__)
#define ANGLE_OS_POSIX
-#else
-#error Unsupported platform.
-#endif
-
-#if defined(ANGLE_OS_WIN)
-#define STRICT
-#define VC_EXTRALEAN 1
-#include <windows.h>
-#elif defined(ANGLE_OS_POSIX)
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>
-#endif // ANGLE_OS_WIN
-
+#else
+#error Unsupported platform.
+#endif
#include "compiler/debug.h"
@@ -43,23 +42,17 @@
#if defined(ANGLE_OS_WIN)
typedef DWORD OS_TLSIndex;
#define OS_INVALID_TLS_INDEX (TLS_OUT_OF_INDEXES)
+#elif defined(ANGLE_OS_WINRT)
+typedef size_t OS_TLSIndex;
+#define OS_INVALID_TLS_INDEX ((DWORD)0xFFFFFF)
#elif defined(ANGLE_OS_POSIX)
typedef pthread_key_t OS_TLSIndex;
#define OS_INVALID_TLS_INDEX (static_cast<OS_TLSIndex>(-1))
#endif // ANGLE_OS_WIN
OS_TLSIndex OS_AllocTLSIndex();
+void *OS_GetTLSValue(OS_TLSIndex nIndex);
bool OS_SetTLSValue(OS_TLSIndex nIndex, void *lpvValue);
bool OS_FreeTLSIndex(OS_TLSIndex nIndex);
-inline void* OS_GetTLSValue(OS_TLSIndex nIndex)
-{
- ASSERT(nIndex != OS_INVALID_TLS_INDEX);
-#if defined(ANGLE_OS_WIN)
- return TlsGetValue(nIndex);
-#elif defined(ANGLE_OS_POSIX)
- return pthread_getspecific(nIndex);
-#endif // ANGLE_OS_WIN
-}
-
#endif // __OSINCLUDE_H
diff --git a/src/3rdparty/angle/src/compiler/ossource_posix.cpp b/src/3rdparty/angle/src/compiler/ossource_posix.cpp
index 1e1e699aeb..35510c1af5 100644
--- a/src/3rdparty/angle/src/compiler/ossource_posix.cpp
+++ b/src/3rdparty/angle/src/compiler/ossource_posix.cpp
@@ -33,6 +33,14 @@ OS_TLSIndex OS_AllocTLSIndex()
}
+void *OS_GetTLSValue(OS_TLSIndex nIndex)
+{
+ ASSERT(nIndex != OS_INVALID_TLS_INDEX);
+
+ return pthread_getspecific(nIndex);
+}
+
+
bool OS_SetTLSValue(OS_TLSIndex nIndex, void *lpvValue)
{
if (nIndex == OS_INVALID_TLS_INDEX) {
diff --git a/src/3rdparty/angle/src/compiler/ossource_win.cpp b/src/3rdparty/angle/src/compiler/ossource_win.cpp
index 89922fef3f..708a1ad311 100644
--- a/src/3rdparty/angle/src/compiler/ossource_win.cpp
+++ b/src/3rdparty/angle/src/compiler/ossource_win.cpp
@@ -29,6 +29,14 @@ OS_TLSIndex OS_AllocTLSIndex()
}
+void *OS_GetTLSValue(OS_TLSIndex nIndex)
+{
+ ASSERT(nIndex != OS_INVALID_TLS_INDEX);
+
+ return TlsGetValue(nIndex);
+}
+
+
bool OS_SetTLSValue(OS_TLSIndex nIndex, void *lpvValue)
{
if (nIndex == OS_INVALID_TLS_INDEX) {
diff --git a/src/3rdparty/angle/src/compiler/ossource_winrt.cpp b/src/3rdparty/angle/src/compiler/ossource_winrt.cpp
new file mode 100644
index 0000000000..84443abc02
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/ossource_winrt.cpp
@@ -0,0 +1,75 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/osinclude.h"
+//
+// This file contains contains Windows Runtime specific functions
+//
+
+#if !defined(ANGLE_OS_WINRT)
+#error Trying to build a WinRT specific file in a non-WinRT build.
+#endif
+
+#include <vector>
+
+
+//
+// Thread Local Storage Operations
+//
+__declspec(thread) std::vector<void *> *tls = nullptr;
+__declspec(thread) std::vector<OS_TLSIndex> *freeIndices = nullptr;
+
+OS_TLSIndex OS_AllocTLSIndex()
+{
+ if (!tls)
+ tls = new std::vector<void*>;
+
+ if (freeIndices && !freeIndices->empty()) {
+ OS_TLSIndex index = freeIndices->back();
+ freeIndices->pop_back();
+ return index;
+ } else {
+ tls->push_back(nullptr);
+ return tls->size() - 1;
+ }
+}
+
+
+void *OS_GetTLSValue(OS_TLSIndex nIndex)
+{
+ ASSERT(nIndex != OS_INVALID_TLS_INDEX);
+ ASSERT(tls);
+
+ return tls->at(nIndex);
+}
+
+
+bool OS_SetTLSValue(OS_TLSIndex nIndex, void *lpvValue)
+{
+ if (!tls || nIndex >= tls->size() || nIndex == OS_INVALID_TLS_INDEX) {
+ ASSERT(0 && "OS_SetTLSValue(): Invalid TLS Index");
+ return false;
+ }
+
+ tls->at(nIndex) = lpvValue;
+ return true;
+}
+
+
+bool OS_FreeTLSIndex(OS_TLSIndex nIndex)
+{
+ if (!tls || nIndex >= tls->size() || nIndex == OS_INVALID_TLS_INDEX) {
+ ASSERT(0 && "OS_SetTLSValue(): Invalid TLS Index");
+ return false;
+ }
+
+ if (!freeIndices)
+ freeIndices = new std::vector<OS_TLSIndex>;
+
+ freeIndices->push_back(nIndex);
+
+ return true;
+}
diff --git a/src/3rdparty/angle/src/libEGL/Display.cpp b/src/3rdparty/angle/src/libEGL/Display.cpp
index a382c3b1eb..14973aff30 100644
--- a/src/3rdparty/angle/src/libEGL/Display.cpp
+++ b/src/3rdparty/angle/src/libEGL/Display.cpp
@@ -186,7 +186,7 @@ bool Display::getConfigAttrib(EGLConfig config, EGLint attribute, EGLint *value)
-EGLSurface Display::createWindowSurface(HWND window, EGLConfig config, const EGLint *attribList)
+EGLSurface Display::createWindowSurface(EGLNativeWindowType window, EGLConfig config, const EGLint *attribList)
{
const Config *configuration = mConfigSet.get(config);
EGLint postSubBufferSupported = EGL_FALSE;
@@ -456,7 +456,7 @@ bool Display::isValidSurface(egl::Surface *surface)
return mSurfaceSet.find(surface) != mSurfaceSet.end();
}
-bool Display::hasExistingWindowSurface(HWND window)
+bool Display::hasExistingWindowSurface(EGLNativeWindowType window)
{
for (SurfaceSet::iterator surface = mSurfaceSet.begin(); surface != mSurfaceSet.end(); surface++)
{
@@ -471,7 +471,6 @@ bool Display::hasExistingWindowSurface(HWND window)
void Display::initExtensionString()
{
- HMODULE swiftShader = GetModuleHandle(TEXT("swiftshader_d3d9.dll"));
bool shareHandleSupported = mRenderer->getShareHandleSupport();
mExtensionString = "";
@@ -487,10 +486,13 @@ void Display::initExtensionString()
mExtensionString += "EGL_ANGLE_query_surface_pointer ";
+#if !defined(ANGLE_OS_WINRT)
+ HMODULE swiftShader = GetModuleHandle(TEXT("swiftshader_d3d9.dll"));
if (swiftShader)
{
mExtensionString += "EGL_ANGLE_software_display ";
}
+#endif
if (shareHandleSupported)
{
diff --git a/src/3rdparty/angle/src/libEGL/Display.h b/src/3rdparty/angle/src/libEGL/Display.h
index 58c3940331..5d55410440 100644
--- a/src/3rdparty/angle/src/libEGL/Display.h
+++ b/src/3rdparty/angle/src/libEGL/Display.h
@@ -40,7 +40,7 @@ class Display
bool getConfigs(EGLConfig *configs, const EGLint *attribList, EGLint configSize, EGLint *numConfig);
bool getConfigAttrib(EGLConfig config, EGLint attribute, EGLint *value);
- EGLSurface createWindowSurface(HWND window, EGLConfig config, const EGLint *attribList);
+ EGLSurface createWindowSurface(EGLNativeWindowType window, EGLConfig config, const EGLint *attribList);
EGLSurface createOffscreenSurface(EGLConfig config, HANDLE shareHandle, const EGLint *attribList);
EGLContext createContext(EGLConfig configHandle, const gl::Context *shareContext, bool notifyResets, bool robustAccess);
@@ -51,7 +51,7 @@ class Display
bool isValidConfig(EGLConfig config);
bool isValidContext(gl::Context *context);
bool isValidSurface(egl::Surface *surface);
- bool hasExistingWindowSurface(HWND window);
+ bool hasExistingWindowSurface(EGLNativeWindowType window);
rx::Renderer *getRenderer() { return mRenderer; };
diff --git a/src/3rdparty/angle/src/libEGL/Surface.cpp b/src/3rdparty/angle/src/libEGL/Surface.cpp
index 83fbbf5b07..ee8d4800ae 100644
--- a/src/3rdparty/angle/src/libEGL/Surface.cpp
+++ b/src/3rdparty/angle/src/libEGL/Surface.cpp
@@ -22,10 +22,15 @@
#include <algorithm>
+#if defined(ANGLE_OS_WINRT)
+#include <windows.foundation.h>
+#include <windows.ui.core.h>
+#endif
+
namespace egl
{
-Surface::Surface(Display *display, const Config *config, HWND window, EGLint postSubBufferSupported)
+Surface::Surface(Display *display, const Config *config, EGLNativeWindowType window, EGLint postSubBufferSupported)
: mDisplay(display), mConfig(config), mWindow(window), mPostSubBufferSupported(postSubBufferSupported)
{
mRenderer = mDisplay->getRenderer();
@@ -98,6 +103,7 @@ bool Surface::resetSwapChain()
if (mWindow)
{
+#if !defined(ANGLE_OS_WINRT)
RECT windowRect;
if (!GetClientRect(getWindowHandle(), &windowRect))
{
@@ -109,6 +115,14 @@ bool Surface::resetSwapChain()
width = windowRect.right - windowRect.left;
height = windowRect.bottom - windowRect.top;
+#else
+ ABI::Windows::Foundation::Rect windowRect;
+ ABI::Windows::UI::Core::ICoreWindow *window;
+ ASSERT(SUCCEEDED(mWindow->QueryInterface(IID_PPV_ARGS(&window))));
+ window->get_Bounds(&windowRect);
+ width = windowRect.Width;
+ height = windowRect.Height;
+#endif
}
else
{
@@ -228,7 +242,7 @@ bool Surface::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
return true;
}
-HWND Surface::getWindowHandle()
+EGLNativeWindowType Surface::getWindowHandle()
{
return mWindow;
}
@@ -237,6 +251,7 @@ HWND Surface::getWindowHandle()
#define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
#define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
+#if !defined(ANGLE_OS_WINRT)
static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
if (message == WM_SIZE)
@@ -250,9 +265,13 @@ static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam
WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc));
return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam);
}
+#endif
void Surface::subclassWindow()
{
+#if defined(ANGLE_OS_WINRT)
+ mWindowSubclassed = false;
+#else
if (!mWindow)
{
return;
@@ -276,10 +295,12 @@ void Surface::subclassWindow()
SetProp(mWindow, kSurfaceProperty, reinterpret_cast<HANDLE>(this));
SetProp(mWindow, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc));
mWindowSubclassed = true;
+#endif
}
void Surface::unsubclassWindow()
{
+#if !defined(ANGLE_OS_WINRT)
if(!mWindowSubclassed)
{
return;
@@ -302,10 +323,12 @@ void Surface::unsubclassWindow()
RemoveProp(mWindow, kSurfaceProperty);
RemoveProp(mWindow, kParentWndProc);
mWindowSubclassed = false;
+#endif
}
bool Surface::checkForOutOfDateSwapChain()
{
+#if !defined(ANGLE_OS_WINRT)
RECT client;
if (!GetClientRect(getWindowHandle(), &client))
{
@@ -316,6 +339,14 @@ bool Surface::checkForOutOfDateSwapChain()
// Grow the buffer now, if the window has grown. We need to grow now to avoid losing information.
int clientWidth = client.right - client.left;
int clientHeight = client.bottom - client.top;
+#else
+ ABI::Windows::Foundation::Rect windowRect;
+ ABI::Windows::UI::Core::ICoreWindow *window;
+ ASSERT(SUCCEEDED(mWindow->QueryInterface(IID_PPV_ARGS(&window))));
+ window->get_Bounds(&windowRect);
+ int clientWidth = windowRect.Width;
+ int clientHeight = windowRect.Height;
+#endif
bool sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
if (mSwapIntervalDirty)
diff --git a/src/3rdparty/angle/src/libEGL/Surface.h b/src/3rdparty/angle/src/libEGL/Surface.h
index 938b800cdd..ae9a380858 100644
--- a/src/3rdparty/angle/src/libEGL/Surface.h
+++ b/src/3rdparty/angle/src/libEGL/Surface.h
@@ -15,6 +15,7 @@
#include <EGL/egl.h>
#include "common/angleutils.h"
+#include "windows.h"
namespace gl
{
@@ -34,7 +35,7 @@ class Config;
class Surface
{
public:
- Surface(Display *display, const egl::Config *config, HWND window, EGLint postSubBufferSupported);
+ Surface(Display *display, const egl::Config *config, EGLNativeWindowType window, EGLint postSubBufferSupported);
Surface(Display *display, const egl::Config *config, HANDLE shareHandle, EGLint width, EGLint height, EGLenum textureFormat, EGLenum textureTarget);
~Surface();
@@ -43,7 +44,7 @@ class Surface
void release();
bool resetSwapChain();
- HWND getWindowHandle();
+ EGLNativeWindowType getWindowHandle();
bool swap();
bool postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height);
@@ -79,7 +80,7 @@ private:
bool resetSwapChain(int backbufferWidth, int backbufferHeight);
bool swapRect(EGLint x, EGLint y, EGLint width, EGLint height);
- const HWND mWindow; // Window that the surface is created for.
+ const EGLNativeWindowType mWindow; // Window that the surface is created for.
bool mWindowSubclassed; // Indicates whether we successfully subclassed mWindow for WM_RESIZE hooking
const egl::Config *mConfig; // EGL config surface was created with
EGLint mHeight; // Height of surface
diff --git a/src/3rdparty/angle/src/libEGL/libEGL.cpp b/src/3rdparty/angle/src/libEGL/libEGL.cpp
index 6e10c3926d..5bcb5d5959 100644
--- a/src/3rdparty/angle/src/libEGL/libEGL.cpp
+++ b/src/3rdparty/angle/src/libEGL/libEGL.cpp
@@ -308,14 +308,16 @@ EGLSurface __stdcall eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EG
return EGL_NO_SURFACE;
}
+#if !defined(ANGLE_OS_WINRT)
HWND window = (HWND)win;
if (!IsWindow(window))
{
return egl::error(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
}
+#endif
- return display->createWindowSurface(window, config, attrib_list);
+ return display->createWindowSurface(win, config, attrib_list);
}
catch(std::bad_alloc&)
{
diff --git a/src/3rdparty/angle/src/libEGL/main.cpp b/src/3rdparty/angle/src/libEGL/main.cpp
index 7dea5fc74b..964b4b21fd 100644
--- a/src/3rdparty/angle/src/libEGL/main.cpp
+++ b/src/3rdparty/angle/src/libEGL/main.cpp
@@ -1,3 +1,4 @@
+#include "../libGLESv2/precompiled.h"
//
// Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
@@ -12,7 +13,13 @@
#ifndef QT_OPENGL_ES_2_ANGLE_STATIC
+#if !defined(ANGLE_OS_WINRT)
static DWORD currentTLS = TLS_OUT_OF_INDEXES;
+#else
+static __declspec(thread) void *currentTLS = 0;
+#endif
+
+namespace egl { Current *getCurrent(); }
extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
{
@@ -35,22 +42,25 @@ extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved
}
#endif
+#if !defined(ANGLE_OS_WINRT)
currentTLS = TlsAlloc();
if (currentTLS == TLS_OUT_OF_INDEXES)
{
return FALSE;
}
+#endif
}
// Fall throught to initialize index
case DLL_THREAD_ATTACH:
{
- egl::Current *current = (egl::Current*)LocalAlloc(LPTR, sizeof(egl::Current));
+ egl::Current *current = egl::getCurrent();
if (current)
{
+#if !defined(ANGLE_OS_WINRT)
TlsSetValue(currentTLS, current);
-
+#endif
current->error = EGL_SUCCESS;
current->API = EGL_OPENGL_ES_API;
current->display = EGL_NO_DISPLAY;
@@ -61,24 +71,35 @@ extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved
break;
case DLL_THREAD_DETACH:
{
- void *current = TlsGetValue(currentTLS);
+ egl::Current *current = egl::getCurrent();
if (current)
{
+#if !defined(ANGLE_OS_WINRT)
LocalFree((HLOCAL)current);
+#else
+ HeapFree(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, current);
+ currentTLS = 0;
+#endif
}
}
break;
case DLL_PROCESS_DETACH:
{
- void *current = TlsGetValue(currentTLS);
+ egl::Current *current = egl::getCurrent();
if (current)
{
+#if !defined(ANGLE_OS_WINRT)
LocalFree((HLOCAL)current);
}
TlsFree(currentTLS);
+#else
+ HeapFree(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, current);
+ currentTLS = 0;
+ }
+#endif
}
break;
default:
@@ -95,7 +116,16 @@ namespace egl
Current *getCurrent()
{
#ifndef QT_OPENGL_ES_2_ANGLE_STATIC
- return (Current*)TlsGetValue(currentTLS);
+#if !defined(ANGLE_OS_WINRT)
+ Current *current = (Current*)TlsGetValue(currentTLS);
+ if (!current)
+ current = (Current*)LocalAlloc(LPTR, sizeof(Current));
+ return current;
+#else
+ if (!currentTLS)
+ currentTLS = HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS|HEAP_ZERO_MEMORY, sizeof(Current));
+ return (Current*)currentTLS;
+#endif
#else
// No precautions for thread safety taken as ANGLE is used single-threaded in Qt.
static Current curr = { EGL_SUCCESS, EGL_OPENGL_ES_API, EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE };
diff --git a/src/3rdparty/angle/src/libGLESv2/Buffer.cpp b/src/3rdparty/angle/src/libGLESv2/Buffer.cpp
index c007d5d9e9..40baa95760 100644
--- a/src/3rdparty/angle/src/libGLESv2/Buffer.cpp
+++ b/src/3rdparty/angle/src/libGLESv2/Buffer.cpp
@@ -37,11 +37,11 @@ Buffer::~Buffer()
delete mStaticIndexBuffer;
}
-void Buffer::bufferData(const void *data, GLsizeiptr size, GLenum usage)
+void Buffer::bufferData(const void *data, GLsizeiptr size, GLenum usage, GLenum target)
{
mBufferStorage->clear();
mIndexRangeCache.clear();
- mBufferStorage->setData(data, size, 0);
+ mBufferStorage->setData(data, size, 0, target);
mUsage = usage;
@@ -54,9 +54,9 @@ void Buffer::bufferData(const void *data, GLsizeiptr size, GLenum usage)
}
}
-void Buffer::bufferSubData(const void *data, GLsizeiptr size, GLintptr offset)
+void Buffer::bufferSubData(const void *data, GLsizeiptr size, GLintptr offset, GLenum target)
{
- mBufferStorage->setData(data, size, offset);
+ mBufferStorage->setData(data, size, offset, target);
mIndexRangeCache.invalidateRange(offset, size);
if ((mStaticVertexBuffer && mStaticVertexBuffer->getBufferSize() != 0) || (mStaticIndexBuffer && mStaticIndexBuffer->getBufferSize() != 0))
diff --git a/src/3rdparty/angle/src/libGLESv2/Buffer.h b/src/3rdparty/angle/src/libGLESv2/Buffer.h
index 4048f4b906..9b86b9791f 100644
--- a/src/3rdparty/angle/src/libGLESv2/Buffer.h
+++ b/src/3rdparty/angle/src/libGLESv2/Buffer.h
@@ -33,8 +33,8 @@ class Buffer : public RefCountObject
virtual ~Buffer();
- void bufferData(const void *data, GLsizeiptr size, GLenum usage);
- void bufferSubData(const void *data, GLsizeiptr size, GLintptr offset);
+ void bufferData(const void *data, GLsizeiptr size, GLenum usage, GLenum target);
+ void bufferSubData(const void *data, GLsizeiptr size, GLintptr offset, GLenum target);
GLenum usage() const;
diff --git a/src/3rdparty/angle/src/libGLESv2/libGLESv2.cpp b/src/3rdparty/angle/src/libGLESv2/libGLESv2.cpp
index 320bbccc27..91719f8e6d 100644
--- a/src/3rdparty/angle/src/libGLESv2/libGLESv2.cpp
+++ b/src/3rdparty/angle/src/libGLESv2/libGLESv2.cpp
@@ -758,7 +758,7 @@ void __stdcall glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data,
return gl::error(GL_INVALID_OPERATION);
}
- buffer->bufferData(data, size, usage);
+ buffer->bufferData(data, size, usage, target);
}
}
catch(std::bad_alloc&)
@@ -812,7 +812,7 @@ void __stdcall glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size,
return gl::error(GL_INVALID_VALUE);
}
- buffer->bufferSubData(data, size, offset);
+ buffer->bufferSubData(data, size, offset, target);
}
}
catch(std::bad_alloc&)
diff --git a/src/3rdparty/angle/src/libGLESv2/main.cpp b/src/3rdparty/angle/src/libGLESv2/main.cpp
index 730a6ac022..defdf35f77 100644
--- a/src/3rdparty/angle/src/libGLESv2/main.cpp
+++ b/src/3rdparty/angle/src/libGLESv2/main.cpp
@@ -13,7 +13,13 @@
#ifndef QT_OPENGL_ES_2_ANGLE_STATIC
+#if !defined(ANGLE_OS_WINRT)
static DWORD currentTLS = TLS_OUT_OF_INDEXES;
+#else
+static __declspec(thread) void *currentTLS = 0;
+#endif
+
+namespace gl { Current *getCurrent(); }
extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
{
@@ -21,22 +27,25 @@ extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved
{
case DLL_PROCESS_ATTACH:
{
+#if !defined(ANGLE_OS_WINRT)
currentTLS = TlsAlloc();
if (currentTLS == TLS_OUT_OF_INDEXES)
{
return FALSE;
}
+#endif
}
// Fall throught to initialize index
case DLL_THREAD_ATTACH:
{
- gl::Current *current = (gl::Current*)LocalAlloc(LPTR, sizeof(gl::Current));
+ gl::Current *current = gl::getCurrent();
if (current)
{
+#if !defined(ANGLE_OS_WINRT)
TlsSetValue(currentTLS, current);
-
+#endif
current->context = NULL;
current->display = NULL;
}
@@ -44,24 +53,35 @@ extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved
break;
case DLL_THREAD_DETACH:
{
- void *current = TlsGetValue(currentTLS);
+ gl::Current *current = gl::getCurrent();
if (current)
{
+#if !defined(ANGLE_OS_WINRT)
LocalFree((HLOCAL)current);
+#else
+ HeapFree(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, current);
+ currentTLS = 0;
+#endif
}
}
break;
case DLL_PROCESS_DETACH:
{
- void *current = TlsGetValue(currentTLS);
+ gl::Current *current = gl::getCurrent();
if (current)
{
+#if !defined(ANGLE_OS_WINRT)
LocalFree((HLOCAL)current);
}
TlsFree(currentTLS);
+#else
+ HeapFree(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, current);
+ currentTLS = 0;
+ }
+#endif
}
break;
default:
@@ -78,7 +98,16 @@ namespace gl
Current *getCurrent()
{
#ifndef QT_OPENGL_ES_2_ANGLE_STATIC
- return (Current*)TlsGetValue(currentTLS);
+#if !defined(ANGLE_OS_WINRT)
+ Current *current = (Current*)TlsGetValue(currentTLS);
+ if (!current)
+ current = (Current*)LocalAlloc(LPTR, sizeof(Current));
+ return current;
+#else
+ if (!currentTLS)
+ currentTLS = HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS|HEAP_ZERO_MEMORY, sizeof(Current));
+ return (Current*)currentTLS;
+#endif
#else
// No precautions for thread safety taken as ANGLE is used single-threaded in Qt.
static gl::Current curr = { 0, 0 };
diff --git a/src/3rdparty/angle/src/libGLESv2/precompiled.h b/src/3rdparty/angle/src/libGLESv2/precompiled.h
index 50dec6b084..823d27bb60 100644
--- a/src/3rdparty/angle/src/libGLESv2/precompiled.h
+++ b/src/3rdparty/angle/src/libGLESv2/precompiled.h
@@ -32,13 +32,28 @@
#include <unordered_map>
#include <vector>
+#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
+#define ANGLE_OS_WINRT
+#if WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP
+#define ANGLE_OS_WINPHONE
+#endif
+#endif
+
#ifndef ANGLE_ENABLE_D3D11
#include <d3d9.h>
#else
+#if !defined(ANGLE_OS_WINRT)
#include <D3D11.h>
+#else
+#include <d3d11_1.h>
+#define Sleep(x) WaitForSingleObjectEx(GetCurrentThread(), x, FALSE)
+#define GetVersion() WINVER
+#endif
#include <dxgi.h>
#endif
+#ifndef ANGLE_OS_WINPHONE
#include <D3Dcompiler.h>
+#endif
#ifdef _MSC_VER
#include <hash_map>
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage.h b/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage.h
index ace1a11bae..14a8c2765b 100644
--- a/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage.h
+++ b/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage.h
@@ -22,7 +22,7 @@ class BufferStorage
// The data returned is only guaranteed valid until next non-const method.
virtual void *getData() = 0;
- virtual void setData(const void* data, unsigned int size, unsigned int offset) = 0;
+ virtual void setData(const void* data, unsigned int size, unsigned int offset, unsigned int target) = 0;
virtual void clear() = 0;
virtual unsigned int getSize() const = 0;
virtual bool supportsDirectBinding() const = 0;
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage11.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage11.cpp
index 3647d8a898..2f694db061 100644
--- a/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage11.cpp
+++ b/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage11.cpp
@@ -131,7 +131,7 @@ void *BufferStorage11::getData()
return mResolvedData;
}
-void BufferStorage11::setData(const void* data, unsigned int size, unsigned int offset)
+void BufferStorage11::setData(const void* data, unsigned int size, unsigned int offset, unsigned int target)
{
ID3D11Device *device = mRenderer->getDevice();
ID3D11DeviceContext *context = mRenderer->getDeviceContext();
@@ -201,7 +201,10 @@ void BufferStorage11::setData(const void* data, unsigned int size, unsigned int
D3D11_BUFFER_DESC bufferDesc;
bufferDesc.ByteWidth = requiredBufferSize;
bufferDesc.Usage = D3D11_USAGE_DEFAULT;
- bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_INDEX_BUFFER;
+ if (mRenderer->getFeatureLevel() > D3D_FEATURE_LEVEL_9_3)
+ bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_INDEX_BUFFER;
+ else
+ bufferDesc.BindFlags = target == GL_ARRAY_BUFFER ? D3D11_BIND_VERTEX_BUFFER : D3D11_BIND_INDEX_BUFFER;
bufferDesc.CPUAccessFlags = 0;
bufferDesc.MiscFlags = 0;
bufferDesc.StructureByteStride = 0;
@@ -324,7 +327,7 @@ unsigned int BufferStorage11::getSize() const
bool BufferStorage11::supportsDirectBinding() const
{
- return true;
+ return mRenderer->getFeatureLevel() >= D3D_FEATURE_LEVEL_10_0;
}
void BufferStorage11::markBufferUsage()
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage11.h b/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage11.h
index b62348b0c9..c9489627c3 100644
--- a/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage11.h
+++ b/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage11.h
@@ -24,7 +24,7 @@ class BufferStorage11 : public BufferStorage
static BufferStorage11 *makeBufferStorage11(BufferStorage *bufferStorage);
virtual void *getData();
- virtual void setData(const void* data, unsigned int size, unsigned int offset);
+ virtual void setData(const void* data, unsigned int size, unsigned int offset, unsigned int target);
virtual void clear();
virtual unsigned int getSize() const;
virtual bool supportsDirectBinding() const;
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage9.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage9.cpp
index e69e7a8921..57fd29bf80 100644
--- a/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage9.cpp
+++ b/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage9.cpp
@@ -36,7 +36,7 @@ void *BufferStorage9::getData()
return mMemory;
}
-void BufferStorage9::setData(const void* data, unsigned int size, unsigned int offset)
+void BufferStorage9::setData(const void* data, unsigned int size, unsigned int offset, unsigned int)
{
if (!mMemory || offset + size > mAllocatedSize)
{
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage9.h b/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage9.h
index 3e803969bc..82ae577e23 100644
--- a/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage9.h
+++ b/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage9.h
@@ -23,7 +23,7 @@ class BufferStorage9 : public BufferStorage
static BufferStorage9 *makeBufferStorage9(BufferStorage *bufferStorage);
virtual void *getData();
- virtual void setData(const void* data, unsigned int size, unsigned int offset);
+ virtual void setData(const void* data, unsigned int size, unsigned int offset, unsigned int target = 0);
virtual void clear();
virtual unsigned int getSize() const;
virtual bool supportsDirectBinding() const;
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/Image11.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/Image11.cpp
index 09c8922d07..81e9e9ecb2 100644
--- a/src/3rdparty/angle/src/libGLESv2/renderer/Image11.cpp
+++ b/src/3rdparty/angle/src/libGLESv2/renderer/Image11.cpp
@@ -136,7 +136,7 @@ bool Image11::redefine(Renderer *renderer, GLint internalformat, GLsizei width,
mHeight = height;
mInternalFormat = internalformat;
// compute the d3d format that will be used
- mDXGIFormat = gl_d3d11::ConvertTextureFormat(internalformat);
+ mDXGIFormat = gl_d3d11::ConvertTextureFormat(internalformat, mRenderer->getFeatureLevel());
mActualFormat = d3d11_gl::ConvertTextureInternalFormat(mDXGIFormat);
if (mStagingTexture)
@@ -185,7 +185,10 @@ void Image11::loadData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei heig
switch (mInternalFormat)
{
case GL_ALPHA8_EXT:
- loadAlphaDataToNative(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData);
+ if (mRenderer->getFeatureLevel() >= D3D_FEATURE_LEVEL_10_0)
+ loadAlphaDataToNative(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData);
+ else
+ loadAlphaDataToBGRA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData);
break;
case GL_LUMINANCE8_EXT:
loadLuminanceDataToNativeOrBGRA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData, false);
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/IndexBuffer11.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/IndexBuffer11.cpp
index 66604c4558..36a62adc1c 100644
--- a/src/3rdparty/angle/src/libGLESv2/renderer/IndexBuffer11.cpp
+++ b/src/3rdparty/angle/src/libGLESv2/renderer/IndexBuffer11.cpp
@@ -170,7 +170,7 @@ DXGI_FORMAT IndexBuffer11::getIndexFormat() const
{
case GL_UNSIGNED_BYTE: return DXGI_FORMAT_R16_UINT;
case GL_UNSIGNED_SHORT: return DXGI_FORMAT_R16_UINT;
- case GL_UNSIGNED_INT: return DXGI_FORMAT_R32_UINT;
+ case GL_UNSIGNED_INT: return mRenderer->get32BitIndexSupport() ? DXGI_FORMAT_R32_UINT : DXGI_FORMAT_R16_UINT;
default: UNREACHABLE(); return DXGI_FORMAT_UNKNOWN;
}
}
@@ -180,4 +180,4 @@ ID3D11Buffer *IndexBuffer11::getBuffer() const
return mBuffer;
}
-} \ No newline at end of file
+}
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/RenderStateCache.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/RenderStateCache.cpp
index b3111af72b..fd388dfe08 100644
--- a/src/3rdparty/angle/src/libGLESv2/renderer/RenderStateCache.cpp
+++ b/src/3rdparty/angle/src/libGLESv2/renderer/RenderStateCache.cpp
@@ -387,7 +387,8 @@ ID3D11SamplerState *RenderStateCache::getSamplerState(const gl::SamplerState &sa
samplerDesc.BorderColor[2] = 0.0f;
samplerDesc.BorderColor[3] = 0.0f;
samplerDesc.MinLOD = gl_d3d11::ConvertMinLOD(samplerState.minFilter, samplerState.lodOffset);
- samplerDesc.MaxLOD = gl_d3d11::ConvertMaxLOD(samplerState.minFilter, samplerState.lodOffset);
+ samplerDesc.MaxLOD = mDevice->GetFeatureLevel() >= D3D_FEATURE_LEVEL_10_0
+ ? gl_d3d11::ConvertMaxLOD(samplerState.minFilter, samplerState.lodOffset) : FLT_MAX;
ID3D11SamplerState *dx11SamplerState = NULL;
HRESULT result = mDevice->CreateSamplerState(&samplerDesc, &dx11SamplerState);
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/Renderer.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/Renderer.cpp
index 21ad223467..7ba183d250 100644
--- a/src/3rdparty/angle/src/libGLESv2/renderer/Renderer.cpp
+++ b/src/3rdparty/angle/src/libGLESv2/renderer/Renderer.cpp
@@ -28,13 +28,18 @@
#define D3DERR_OUTOFVIDEOMEMORY MAKE_HRESULT(1, 0x876, 380)
#endif
-#ifdef __MINGW32__
-
#ifndef D3DCOMPILER_DLL
+#ifndef ANGLE_OS_WINPHONE
+#define D3DCOMPILER_DLL L"d3dcompiler_43.dll" // Lowest common denominator
+#else
+#define D3DCOMPILER_DLL L"qtd3dcompiler.dll" // Placeholder DLL for phone
+#endif // ANGLE_OS_WINPHONE
+#endif // D3DCOMPILER_DLL
-//Add define + typedefs for older MinGW-w64 headers (pre 5783)
+#if defined(__MINGW32__) || defined(ANGLE_OS_WINPHONE)
-#define D3DCOMPILER_DLL L"d3dcompiler_43.dll"
+//Add define + typedefs for older MinGW-w64 headers (pre 5783)
+//Also define these on Windows Phone, which doesn't have a shader compiler
HRESULT WINAPI D3DCompile(const void *data, SIZE_T data_size, const char *filename,
const D3D_SHADER_MACRO *defines, ID3DInclude *include, const char *entrypoint,
@@ -43,9 +48,7 @@ typedef HRESULT (WINAPI *pD3DCompile)(const void *data, SIZE_T data_size, const
const D3D_SHADER_MACRO *defines, ID3DInclude *include, const char *entrypoint,
const char *target, UINT sflags, UINT eflags, ID3DBlob **shader, ID3DBlob **error_messages);
-#endif // D3DCOMPILER_DLL
-
-#endif // __MINGW32__
+#endif // __MINGW32__ || ANGLE_OS_WINPHONE
namespace rx
{
@@ -81,7 +84,11 @@ bool Renderer::initializeCompiler()
}
#else
// Load the version of the D3DCompiler DLL associated with the Direct3D version ANGLE was built with.
+#if !defined(ANGLE_OS_WINRT)
mD3dCompilerModule = LoadLibrary(D3DCOMPILER_DLL);
+#else
+ mD3dCompilerModule = LoadPackagedLibrary(D3DCOMPILER_DLL, NULL);
+#endif
#endif // ANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES
if (!mD3dCompilerModule)
@@ -225,4 +232,4 @@ void glDestroyRenderer(rx::Renderer *renderer)
delete renderer;
}
-} \ No newline at end of file
+}
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/Renderer.h b/src/3rdparty/angle/src/libGLESv2/renderer/Renderer.h
index 04e877ba9e..ac67c27e71 100644
--- a/src/3rdparty/angle/src/libGLESv2/renderer/Renderer.h
+++ b/src/3rdparty/angle/src/libGLESv2/renderer/Renderer.h
@@ -1,3 +1,4 @@
+#include "../precompiled.h"
//
// Copyright (c) 2012-2013 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
@@ -13,6 +14,30 @@
#include "libGLESv2/Uniform.h"
#include "libGLESv2/angletypes.h"
+#ifndef D3DCOMPILE_OPTIMIZATION_LEVEL0
+#define D3DCOMPILE_OPTIMIZATION_LEVEL0 (1 << 14)
+#endif
+#ifndef D3DCOMPILE_OPTIMIZATION_LEVEL1
+#define D3DCOMPILE_OPTIMIZATION_LEVEL1 0
+#endif
+#ifndef D3DCOMPILE_OPTIMIZATION_LEVEL2
+#define D3DCOMPILE_OPTIMIZATION_LEVEL2 ((1 << 14) | (1 << 15))
+#endif
+#ifndef D3DCOMPILE_OPTIMIZATION_LEVEL3
+#define D3DCOMPILE_OPTIMIZATION_LEVEL3 (1 << 15)
+#endif
+#ifndef D3DCOMPILE_DEBUG
+#define D3DCOMPILE_DEBUG (1 << 0)
+#endif
+#ifndef D3DCOMPILE_SKIP_OPTIMIZATION
+#define D3DCOMPILE_SKIP_OPTIMIZATION (1 << 2)
+#endif
+#ifndef D3DCOMPILE_AVOID_FLOW_CONTROL
+#define D3DCOMPILE_AVOID_FLOW_CONTROL (1 << 9)
+#endif
+#ifndef D3DCOMPILE_PREFER_FLOW_CONTROL
+#define D3DCOMPILE_PREFER_FLOW_CONTROL (1 << 10)
+#endif
#if !defined(ANGLE_COMPILE_OPTIMIZATION_LEVEL)
#define ANGLE_COMPILE_OPTIMIZATION_LEVEL D3DCOMPILE_OPTIMIZATION_LEVEL3
#endif
@@ -107,7 +132,7 @@ class Renderer
virtual void sync(bool block) = 0;
- virtual SwapChain *createSwapChain(HWND window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat) = 0;
+ virtual SwapChain *createSwapChain(EGLNativeWindowType window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat) = 0;
virtual void setSamplerState(gl::SamplerType type, int index, const gl::SamplerState &sampler) = 0;
virtual void setTexture(gl::SamplerType type, int index, gl::Texture *texture) = 0;
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.cpp
index a43101807a..f83e9e91ce 100644
--- a/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.cpp
+++ b/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.cpp
@@ -137,6 +137,7 @@ EGLint Renderer11::initialize()
return EGL_NOT_INITIALIZED;
}
+#if !defined(ANGLE_OS_WINRT)
mDxgiModule = LoadLibrary(TEXT("dxgi.dll"));
mD3d11Module = LoadLibrary(TEXT("d3d11.dll"));
@@ -155,12 +156,17 @@ EGLint Renderer11::initialize()
ERR("Could not retrieve D3D11CreateDevice address - aborting!\n");
return EGL_NOT_INITIALIZED;
}
+#endif
D3D_FEATURE_LEVEL featureLevels[] =
{
+ D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
+ D3D_FEATURE_LEVEL_9_3,
+ D3D_FEATURE_LEVEL_9_2,
+ D3D_FEATURE_LEVEL_9_1,
};
HRESULT result = S_OK;
@@ -203,8 +209,12 @@ EGLint Renderer11::initialize()
}
}
+#if !defined(ANGLE_OS_WINRT)
IDXGIDevice *dxgiDevice = NULL;
- result = mDevice->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice);
+#else
+ IDXGIDevice1 *dxgiDevice = NULL;
+#endif
+ result = mDevice->QueryInterface(IID_PPV_ARGS(&dxgiDevice));
if (FAILED(result))
{
@@ -524,7 +534,7 @@ void Renderer11::sync(bool block)
}
}
-SwapChain *Renderer11::createSwapChain(HWND window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat)
+SwapChain *Renderer11::createSwapChain(EGLNativeWindowType window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat)
{
return new rx::SwapChain11(this, window, shareHandle, backBufferFormat, depthBufferFormat);
}
@@ -1108,6 +1118,43 @@ void Renderer11::drawElements(GLenum mode, GLsizei count, GLenum type, const GLv
}
}
+template <typename T>
+static void drawLineLoopIndexed(T *data, GLenum type, const GLvoid *indices, GLsizei count)
+{
+ switch (type)
+ {
+ case GL_NONE: // Non-indexed draw
+ for (int i = 0; i < count; i++)
+ {
+ data[i] = i;
+ }
+ data[count] = 0;
+ break;
+ case GL_UNSIGNED_BYTE:
+ for (int i = 0; i < count; i++)
+ {
+ data[i] = static_cast<const GLubyte*>(indices)[i];
+ }
+ data[count] = static_cast<const GLubyte*>(indices)[0];
+ break;
+ case GL_UNSIGNED_SHORT:
+ for (int i = 0; i < count; i++)
+ {
+ data[i] = static_cast<const GLushort*>(indices)[i];
+ }
+ data[count] = static_cast<const GLushort*>(indices)[0];
+ break;
+ case GL_UNSIGNED_INT:
+ for (int i = 0; i < count; i++)
+ {
+ data[i] = static_cast<const GLuint*>(indices)[i];
+ }
+ data[count] = static_cast<const GLuint*>(indices)[0];
+ break;
+ default: UNREACHABLE();
+ }
+}
+
void Renderer11::drawLineLoop(GLsizei count, GLenum type, const GLvoid *indices, int minIndex, gl::Buffer *elementArrayBuffer)
{
// Get the raw indices for an indexed draw
@@ -1156,59 +1203,71 @@ void Renderer11::drawLineLoop(GLsizei count, GLenum type, const GLvoid *indices,
return gl::error(GL_OUT_OF_MEMORY);
}
- unsigned int *data = reinterpret_cast<unsigned int*>(mappedMemory);
+ if (get32BitIndexSupport())
+ drawLineLoopIndexed(reinterpret_cast<unsigned int*>(mappedMemory), type, indices, count);
+ else
+ drawLineLoopIndexed(reinterpret_cast<unsigned short*>(mappedMemory), type, indices, count);
+
unsigned int indexBufferOffset = offset;
+ if (!mLineLoopIB->unmapBuffer())
+ {
+ ERR("Could not unmap index buffer for GL_LINE_LOOP.");
+ return gl::error(GL_OUT_OF_MEMORY);
+ }
+
+ if (mAppliedIBSerial != mLineLoopIB->getSerial() || mAppliedIBOffset != indexBufferOffset)
+ {
+ IndexBuffer11 *indexBuffer = IndexBuffer11::makeIndexBuffer11(mLineLoopIB->getIndexBuffer());
+
+ mDeviceContext->IASetIndexBuffer(indexBuffer->getBuffer(), indexBuffer->getIndexFormat(), indexBufferOffset);
+ mAppliedIBSerial = mLineLoopIB->getSerial();
+ mAppliedStorageIBSerial = 0;
+ mAppliedIBOffset = indexBufferOffset;
+ }
+
+ mDeviceContext->DrawIndexed(count + 1, 0, -minIndex);
+}
+
+template <typename T>
+static void drawTriangleFanIndexed(T *data, GLenum type, const GLvoid *indices, unsigned int numTris)
+{
switch (type)
{
case GL_NONE: // Non-indexed draw
- for (int i = 0; i < count; i++)
+ for (unsigned int i = 0; i < numTris; i++)
{
- data[i] = i;
+ data[i*3 + 0] = 0;
+ data[i*3 + 1] = i + 1;
+ data[i*3 + 2] = i + 2;
}
- data[count] = 0;
break;
case GL_UNSIGNED_BYTE:
- for (int i = 0; i < count; i++)
+ for (unsigned int i = 0; i < numTris; i++)
{
- data[i] = static_cast<const GLubyte*>(indices)[i];
+ data[i*3 + 0] = static_cast<const GLubyte*>(indices)[0];
+ data[i*3 + 1] = static_cast<const GLubyte*>(indices)[i + 1];
+ data[i*3 + 2] = static_cast<const GLubyte*>(indices)[i + 2];
}
- data[count] = static_cast<const GLubyte*>(indices)[0];
break;
case GL_UNSIGNED_SHORT:
- for (int i = 0; i < count; i++)
+ for (unsigned int i = 0; i < numTris; i++)
{
- data[i] = static_cast<const GLushort*>(indices)[i];
+ data[i*3 + 0] = static_cast<const GLushort*>(indices)[0];
+ data[i*3 + 1] = static_cast<const GLushort*>(indices)[i + 1];
+ data[i*3 + 2] = static_cast<const GLushort*>(indices)[i + 2];
}
- data[count] = static_cast<const GLushort*>(indices)[0];
break;
case GL_UNSIGNED_INT:
- for (int i = 0; i < count; i++)
+ for (unsigned int i = 0; i < numTris; i++)
{
- data[i] = static_cast<const GLuint*>(indices)[i];
+ data[i*3 + 0] = static_cast<const GLuint*>(indices)[0];
+ data[i*3 + 1] = static_cast<const GLuint*>(indices)[i + 1];
+ data[i*3 + 2] = static_cast<const GLuint*>(indices)[i + 2];
}
- data[count] = static_cast<const GLuint*>(indices)[0];
break;
default: UNREACHABLE();
}
-
- if (!mLineLoopIB->unmapBuffer())
- {
- ERR("Could not unmap index buffer for GL_LINE_LOOP.");
- return gl::error(GL_OUT_OF_MEMORY);
- }
-
- if (mAppliedIBSerial != mLineLoopIB->getSerial() || mAppliedIBOffset != indexBufferOffset)
- {
- IndexBuffer11 *indexBuffer = IndexBuffer11::makeIndexBuffer11(mLineLoopIB->getIndexBuffer());
-
- mDeviceContext->IASetIndexBuffer(indexBuffer->getBuffer(), indexBuffer->getIndexFormat(), indexBufferOffset);
- mAppliedIBSerial = mLineLoopIB->getSerial();
- mAppliedStorageIBSerial = 0;
- mAppliedIBOffset = indexBufferOffset;
- }
-
- mDeviceContext->DrawIndexed(count + 1, 0, -minIndex);
}
void Renderer11::drawTriangleFan(GLsizei count, GLenum type, const GLvoid *indices, int minIndex, gl::Buffer *elementArrayBuffer, int instances)
@@ -1261,45 +1320,12 @@ void Renderer11::drawTriangleFan(GLsizei count, GLenum type, const GLvoid *indic
return gl::error(GL_OUT_OF_MEMORY);
}
- unsigned int *data = reinterpret_cast<unsigned int*>(mappedMemory);
- unsigned int indexBufferOffset = offset;
+ if (get32BitIndexSupport())
+ drawTriangleFanIndexed(reinterpret_cast<unsigned int*>(mappedMemory), type, indices, numTris);
+ else
+ drawTriangleFanIndexed(reinterpret_cast<unsigned short*>(mappedMemory), type, indices, numTris);
- switch (type)
- {
- case GL_NONE: // Non-indexed draw
- for (unsigned int i = 0; i < numTris; i++)
- {
- data[i*3 + 0] = 0;
- data[i*3 + 1] = i + 1;
- data[i*3 + 2] = i + 2;
- }
- break;
- case GL_UNSIGNED_BYTE:
- for (unsigned int i = 0; i < numTris; i++)
- {
- data[i*3 + 0] = static_cast<const GLubyte*>(indices)[0];
- data[i*3 + 1] = static_cast<const GLubyte*>(indices)[i + 1];
- data[i*3 + 2] = static_cast<const GLubyte*>(indices)[i + 2];
- }
- break;
- case GL_UNSIGNED_SHORT:
- for (unsigned int i = 0; i < numTris; i++)
- {
- data[i*3 + 0] = static_cast<const GLushort*>(indices)[0];
- data[i*3 + 1] = static_cast<const GLushort*>(indices)[i + 1];
- data[i*3 + 2] = static_cast<const GLushort*>(indices)[i + 2];
- }
- break;
- case GL_UNSIGNED_INT:
- for (unsigned int i = 0; i < numTris; i++)
- {
- data[i*3 + 0] = static_cast<const GLuint*>(indices)[0];
- data[i*3 + 1] = static_cast<const GLuint*>(indices)[i + 1];
- data[i*3 + 2] = static_cast<const GLuint*>(indices)[i + 2];
- }
- break;
- default: UNREACHABLE();
- }
+ unsigned int indexBufferOffset = offset;
if (!mTriangleFanIB->unmapBuffer())
{
@@ -1509,7 +1535,7 @@ void Renderer11::applyUniforms(gl::ProgramBinary *programBinary, gl::UniformArra
}
// needed for the point sprite geometry shader
- if (mCurrentGeometryConstantBuffer != mDriverConstantBufferPS)
+ if (mFeatureLevel >= D3D_FEATURE_LEVEL_10_0 && mCurrentGeometryConstantBuffer != mDriverConstantBufferPS)
{
mDeviceContext->GSSetConstantBuffers(0, 1, &mDriverConstantBufferPS);
mCurrentGeometryConstantBuffer = mDriverConstantBufferPS;
@@ -1923,9 +1949,13 @@ bool Renderer11::testDeviceResettable()
D3D_FEATURE_LEVEL featureLevels[] =
{
+ D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
+ D3D_FEATURE_LEVEL_9_3,
+ D3D_FEATURE_LEVEL_9_2,
+ D3D_FEATURE_LEVEL_9_1,
};
ID3D11Device* dummyDevice;
@@ -2104,11 +2134,17 @@ float Renderer11::getTextureMaxAnisotropy() const
{
switch (mFeatureLevel)
{
+ case D3D_FEATURE_LEVEL_11_1:
case D3D_FEATURE_LEVEL_11_0:
return D3D11_MAX_MAXANISOTROPY;
case D3D_FEATURE_LEVEL_10_1:
case D3D_FEATURE_LEVEL_10_0:
return D3D10_MAX_MAXANISOTROPY;
+ case D3D_FEATURE_LEVEL_9_3:
+ case D3D_FEATURE_LEVEL_9_2:
+ return 16;
+ case D3D_FEATURE_LEVEL_9_1:
+ return D3D_FL9_1_DEFAULT_MAX_ANISOTROPY;
default: UNREACHABLE();
return 0;
}
@@ -2123,11 +2159,17 @@ Range Renderer11::getViewportBounds() const
{
switch (mFeatureLevel)
{
+ case D3D_FEATURE_LEVEL_11_1:
case D3D_FEATURE_LEVEL_11_0:
return Range(D3D11_VIEWPORT_BOUNDS_MIN, D3D11_VIEWPORT_BOUNDS_MAX);
case D3D_FEATURE_LEVEL_10_1:
case D3D_FEATURE_LEVEL_10_0:
return Range(D3D10_VIEWPORT_BOUNDS_MIN, D3D10_VIEWPORT_BOUNDS_MAX);
+ case D3D_FEATURE_LEVEL_9_3:
+ return Range(D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION * -2, D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION * 2);
+ case D3D_FEATURE_LEVEL_9_2:
+ case D3D_FEATURE_LEVEL_9_1:
+ return Range(D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION * -2, D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION * 2);
default: UNREACHABLE();
return Range(0, 0);
}
@@ -2138,10 +2180,15 @@ unsigned int Renderer11::getMaxVertexTextureImageUnits() const
META_ASSERT(MAX_TEXTURE_IMAGE_UNITS_VTF_SM4 <= gl::IMPLEMENTATION_MAX_VERTEX_TEXTURE_IMAGE_UNITS);
switch (mFeatureLevel)
{
+ case D3D_FEATURE_LEVEL_11_1:
case D3D_FEATURE_LEVEL_11_0:
case D3D_FEATURE_LEVEL_10_1:
case D3D_FEATURE_LEVEL_10_0:
return MAX_TEXTURE_IMAGE_UNITS_VTF_SM4;
+ case D3D_FEATURE_LEVEL_9_3:
+ case D3D_FEATURE_LEVEL_9_2:
+ case D3D_FEATURE_LEVEL_9_1:
+ return 0;
default: UNREACHABLE();
return 0;
}
@@ -2165,15 +2212,41 @@ unsigned int Renderer11::getReservedFragmentUniformVectors() const
unsigned int Renderer11::getMaxVertexUniformVectors() const
{
META_ASSERT(MAX_VERTEX_UNIFORM_VECTORS_D3D11 <= D3D10_REQ_CONSTANT_BUFFER_ELEMENT_COUNT);
- ASSERT(mFeatureLevel >= D3D_FEATURE_LEVEL_10_0);
- return MAX_VERTEX_UNIFORM_VECTORS_D3D11;
+ switch (mFeatureLevel)
+ {
+ case D3D_FEATURE_LEVEL_11_1:
+ case D3D_FEATURE_LEVEL_11_0:
+ case D3D_FEATURE_LEVEL_10_1:
+ case D3D_FEATURE_LEVEL_10_0:
+ return MAX_VERTEX_UNIFORM_VECTORS_D3D11;
+ case D3D_FEATURE_LEVEL_9_3:
+ case D3D_FEATURE_LEVEL_9_2:
+ case D3D_FEATURE_LEVEL_9_1:
+ return MAX_VERTEX_UNIFORM_VECTORS_D3D9;
+ default:
+ UNIMPLEMENTED();
+ return 0;
+ }
}
unsigned int Renderer11::getMaxFragmentUniformVectors() const
{
META_ASSERT(MAX_FRAGMENT_UNIFORM_VECTORS_D3D11 <= D3D10_REQ_CONSTANT_BUFFER_ELEMENT_COUNT);
- ASSERT(mFeatureLevel >= D3D_FEATURE_LEVEL_10_0);
- return MAX_FRAGMENT_UNIFORM_VECTORS_D3D11;
+ switch (mFeatureLevel)
+ {
+ case D3D_FEATURE_LEVEL_11_1:
+ case D3D_FEATURE_LEVEL_11_0:
+ case D3D_FEATURE_LEVEL_10_1:
+ case D3D_FEATURE_LEVEL_10_0:
+ return MAX_FRAGMENT_UNIFORM_VECTORS_D3D11;
+ case D3D_FEATURE_LEVEL_9_3:
+ return 221;
+ case D3D_FEATURE_LEVEL_9_2:
+ case D3D_FEATURE_LEVEL_9_1:
+ return 29;
+ default: UNREACHABLE();
+ return 0;
+ }
}
unsigned int Renderer11::getMaxVaryingVectors() const
@@ -2181,11 +2254,17 @@ unsigned int Renderer11::getMaxVaryingVectors() const
META_ASSERT(gl::IMPLEMENTATION_MAX_VARYING_VECTORS == D3D11_VS_OUTPUT_REGISTER_COUNT);
switch (mFeatureLevel)
{
+ case D3D_FEATURE_LEVEL_11_1:
case D3D_FEATURE_LEVEL_11_0:
return D3D11_VS_OUTPUT_REGISTER_COUNT;
case D3D_FEATURE_LEVEL_10_1:
+ return D3D10_1_VS_OUTPUT_REGISTER_COUNT;
case D3D_FEATURE_LEVEL_10_0:
return D3D10_VS_OUTPUT_REGISTER_COUNT;
+ case D3D_FEATURE_LEVEL_9_3:
+ case D3D_FEATURE_LEVEL_9_2:
+ case D3D_FEATURE_LEVEL_9_1:
+ return 8;
default: UNREACHABLE();
return 0;
}
@@ -2195,10 +2274,15 @@ bool Renderer11::getNonPower2TextureSupport() const
{
switch (mFeatureLevel)
{
+ case D3D_FEATURE_LEVEL_11_1:
case D3D_FEATURE_LEVEL_11_0:
case D3D_FEATURE_LEVEL_10_1:
case D3D_FEATURE_LEVEL_10_0:
return true;
+ case D3D_FEATURE_LEVEL_9_3:
+ case D3D_FEATURE_LEVEL_9_2:
+ case D3D_FEATURE_LEVEL_9_1:
+ return false;
default: UNREACHABLE();
return false;
}
@@ -2208,10 +2292,15 @@ bool Renderer11::getOcclusionQuerySupport() const
{
switch (mFeatureLevel)
{
+ case D3D_FEATURE_LEVEL_11_1:
case D3D_FEATURE_LEVEL_11_0:
case D3D_FEATURE_LEVEL_10_1:
case D3D_FEATURE_LEVEL_10_0:
+ case D3D_FEATURE_LEVEL_9_3:
+ case D3D_FEATURE_LEVEL_9_2:
return true;
+ case D3D_FEATURE_LEVEL_9_1:
+ return false;
default: UNREACHABLE();
return false;
}
@@ -2221,10 +2310,15 @@ bool Renderer11::getInstancingSupport() const
{
switch (mFeatureLevel)
{
+ case D3D_FEATURE_LEVEL_11_1:
case D3D_FEATURE_LEVEL_11_0:
case D3D_FEATURE_LEVEL_10_1:
case D3D_FEATURE_LEVEL_10_0:
+ case D3D_FEATURE_LEVEL_9_3:
return true;
+ case D3D_FEATURE_LEVEL_9_2:
+ case D3D_FEATURE_LEVEL_9_1:
+ return false;
default: UNREACHABLE();
return false;
}
@@ -2242,10 +2336,15 @@ bool Renderer11::getDerivativeInstructionSupport() const
{
switch (mFeatureLevel)
{
+ case D3D_FEATURE_LEVEL_11_1:
case D3D_FEATURE_LEVEL_11_0:
case D3D_FEATURE_LEVEL_10_1:
case D3D_FEATURE_LEVEL_10_0:
+ case D3D_FEATURE_LEVEL_9_3:
return true;
+ case D3D_FEATURE_LEVEL_9_2:
+ case D3D_FEATURE_LEVEL_9_1:
+ return false;
default: UNREACHABLE();
return false;
}
@@ -2261,9 +2360,13 @@ int Renderer11::getMajorShaderModel() const
{
switch (mFeatureLevel)
{
+ case D3D_FEATURE_LEVEL_11_1:
case D3D_FEATURE_LEVEL_11_0: return D3D11_SHADER_MAJOR_VERSION; // 5
case D3D_FEATURE_LEVEL_10_1: return D3D10_1_SHADER_MAJOR_VERSION; // 4
case D3D_FEATURE_LEVEL_10_0: return D3D10_SHADER_MAJOR_VERSION; // 4
+ case D3D_FEATURE_LEVEL_9_3:
+ case D3D_FEATURE_LEVEL_9_2:
+ case D3D_FEATURE_LEVEL_9_1: return 4; // SM4 level 9, but treat as 4
default: UNREACHABLE(); return 0;
}
}
@@ -2272,9 +2375,13 @@ int Renderer11::getMinorShaderModel() const
{
switch (mFeatureLevel)
{
+ case D3D_FEATURE_LEVEL_11_1:
case D3D_FEATURE_LEVEL_11_0: return D3D11_SHADER_MINOR_VERSION; // 0
case D3D_FEATURE_LEVEL_10_1: return D3D10_1_SHADER_MINOR_VERSION; // 1
case D3D_FEATURE_LEVEL_10_0: return D3D10_SHADER_MINOR_VERSION; // 0
+ case D3D_FEATURE_LEVEL_9_3:
+ case D3D_FEATURE_LEVEL_9_2:
+ case D3D_FEATURE_LEVEL_9_1: return 0;
default: UNREACHABLE(); return 0;
}
}
@@ -2295,11 +2402,17 @@ int Renderer11::getMaxViewportDimension() const
switch (mFeatureLevel)
{
- case D3D_FEATURE_LEVEL_11_0:
+ case D3D_FEATURE_LEVEL_11_1:
+ case D3D_FEATURE_LEVEL_11_0:
return D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 16384
case D3D_FEATURE_LEVEL_10_1:
case D3D_FEATURE_LEVEL_10_0:
return D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 8192
+ case D3D_FEATURE_LEVEL_9_3:
+ return D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 4096
+ case D3D_FEATURE_LEVEL_9_2:
+ case D3D_FEATURE_LEVEL_9_1:
+ return D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 2048
default: UNREACHABLE();
return 0;
}
@@ -2309,9 +2422,13 @@ int Renderer11::getMaxTextureWidth() const
{
switch (mFeatureLevel)
{
+ case D3D_FEATURE_LEVEL_11_1:
case D3D_FEATURE_LEVEL_11_0: return D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 16384
case D3D_FEATURE_LEVEL_10_1:
case D3D_FEATURE_LEVEL_10_0: return D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 8192
+ case D3D_FEATURE_LEVEL_9_3: return D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 4096
+ case D3D_FEATURE_LEVEL_9_2:
+ case D3D_FEATURE_LEVEL_9_1: return D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 2048
default: UNREACHABLE(); return 0;
}
}
@@ -2320,9 +2437,13 @@ int Renderer11::getMaxTextureHeight() const
{
switch (mFeatureLevel)
{
+ case D3D_FEATURE_LEVEL_11_1:
case D3D_FEATURE_LEVEL_11_0: return D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 16384
case D3D_FEATURE_LEVEL_10_1:
case D3D_FEATURE_LEVEL_10_0: return D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 8192
+ case D3D_FEATURE_LEVEL_9_3: return D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 4096
+ case D3D_FEATURE_LEVEL_9_2:
+ case D3D_FEATURE_LEVEL_9_1: return D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 2048
default: UNREACHABLE(); return 0;
}
}
@@ -2331,9 +2452,13 @@ bool Renderer11::get32BitIndexSupport() const
{
switch (mFeatureLevel)
{
- case D3D_FEATURE_LEVEL_11_0:
+ case D3D_FEATURE_LEVEL_11_1:
+ case D3D_FEATURE_LEVEL_11_0:
case D3D_FEATURE_LEVEL_10_1:
case D3D_FEATURE_LEVEL_10_0: return D3D10_REQ_DRAWINDEXED_INDEX_COUNT_2_TO_EXP >= 32; // true
+ case D3D_FEATURE_LEVEL_9_3:
+ case D3D_FEATURE_LEVEL_9_2:
+ case D3D_FEATURE_LEVEL_9_1: return false;
default: UNREACHABLE(); return false;
}
}
@@ -2380,14 +2505,22 @@ unsigned int Renderer11::getMaxRenderTargets() const
{
META_ASSERT(D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT <= gl::IMPLEMENTATION_MAX_DRAW_BUFFERS);
META_ASSERT(D3D10_SIMULTANEOUS_RENDER_TARGET_COUNT <= gl::IMPLEMENTATION_MAX_DRAW_BUFFERS);
+ META_ASSERT(D3D_FL9_3_SIMULTANEOUS_RENDER_TARGET_COUNT <= gl::IMPLEMENTATION_MAX_DRAW_BUFFERS);
+ META_ASSERT(D3D_FL9_1_SIMULTANEOUS_RENDER_TARGET_COUNT <= gl::IMPLEMENTATION_MAX_DRAW_BUFFERS);
switch (mFeatureLevel)
{
+ case D3D_FEATURE_LEVEL_11_1:
case D3D_FEATURE_LEVEL_11_0:
return D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; // 8
case D3D_FEATURE_LEVEL_10_1:
case D3D_FEATURE_LEVEL_10_0:
return D3D10_SIMULTANEOUS_RENDER_TARGET_COUNT; // 8
+ case D3D_FEATURE_LEVEL_9_3:
+ return D3D_FL9_3_SIMULTANEOUS_RENDER_TARGET_COUNT; // 4
+ case D3D_FEATURE_LEVEL_9_2:
+ case D3D_FEATURE_LEVEL_9_1:
+ return D3D_FL9_1_SIMULTANEOUS_RENDER_TARGET_COUNT; // 1
default:
UNREACHABLE();
return 1;
@@ -2815,7 +2948,7 @@ ShaderExecutable *Renderer11::loadExecutable(const void *function, size_t length
ShaderExecutable *Renderer11::compileToExecutable(gl::InfoLog &infoLog, const char *shaderHLSL, rx::ShaderType type)
{
- const char *profile = NULL;
+ std::string profile;
switch (type)
{
@@ -2833,7 +2966,12 @@ ShaderExecutable *Renderer11::compileToExecutable(gl::InfoLog &infoLog, const ch
return NULL;
}
- ID3DBlob *binary = (ID3DBlob*)compileToBinary(infoLog, shaderHLSL, profile, D3DCOMPILE_OPTIMIZATION_LEVEL0, false);
+ if (mFeatureLevel == D3D_FEATURE_LEVEL_9_3)
+ profile += "_level_9_3";
+ else if (mFeatureLevel == D3D_FEATURE_LEVEL_9_2 || mFeatureLevel == D3D_FEATURE_LEVEL_9_1)
+ profile += "_level_9_1";
+
+ ID3DBlob *binary = (ID3DBlob*)compileToBinary(infoLog, shaderHLSL, profile.c_str(), D3DCOMPILE_OPTIMIZATION_LEVEL0, false);
if (!binary)
return NULL;
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.h b/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.h
index f024855f97..433945da7a 100644
--- a/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.h
+++ b/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.h
@@ -32,6 +32,7 @@ class StreamingIndexBufferInterface;
enum
{
+ MAX_VERTEX_UNIFORM_VECTORS_D3D9 = 254,
MAX_VERTEX_UNIFORM_VECTORS_D3D11 = 1024,
MAX_FRAGMENT_UNIFORM_VECTORS_D3D11 = 1024
};
@@ -52,7 +53,7 @@ class Renderer11 : public Renderer
virtual void sync(bool block);
- virtual SwapChain *createSwapChain(HWND window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat);
+ virtual SwapChain *createSwapChain(EGLNativeWindowType window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat);
virtual void setSamplerState(gl::SamplerType type, int index, const gl::SamplerState &sampler);
virtual void setTexture(gl::SamplerType type, int index, gl::Texture *texture);
@@ -177,6 +178,7 @@ class Renderer11 : public Renderer
ID3D11Device *getDevice() { return mDevice; }
ID3D11DeviceContext *getDeviceContext() { return mDeviceContext; };
IDXGIFactory *getDxgiFactory() { return mDxgiFactory; };
+ D3D_FEATURE_LEVEL getFeatureLevel() const { return mFeatureLevel; }
bool getRenderTargetResource(gl::Renderbuffer *colorbuffer, unsigned int *subresourceIndex, ID3D11Texture2D **resource);
void unapplyRenderTargets();
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain.h b/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain.h
index 14c0515fc8..a6870ebedc 100644
--- a/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain.h
+++ b/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain.h
@@ -18,7 +18,7 @@ namespace rx
class SwapChain
{
public:
- SwapChain(HWND window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat)
+ SwapChain(EGLNativeWindowType window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat)
: mWindow(window), mShareHandle(shareHandle), mBackBufferFormat(backBufferFormat), mDepthBufferFormat(depthBufferFormat)
{
}
@@ -33,7 +33,7 @@ class SwapChain
virtual HANDLE getShareHandle() {return mShareHandle;};
protected:
- const HWND mWindow; // Window that the surface is created for.
+ const EGLNativeWindowType mWindow; // Window that the surface is created for.
const GLenum mBackBufferFormat;
const GLenum mDepthBufferFormat;
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.cpp
index 0da58cbe2e..9770772194 100644
--- a/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.cpp
+++ b/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.cpp
@@ -17,7 +17,7 @@
namespace rx
{
-SwapChain11::SwapChain11(Renderer11 *renderer, HWND window, HANDLE shareHandle,
+SwapChain11::SwapChain11(Renderer11 *renderer, EGLNativeWindowType window, HANDLE shareHandle,
GLenum backBufferFormat, GLenum depthBufferFormat)
: mRenderer(renderer), SwapChain(window, shareHandle, backBufferFormat, depthBufferFormat)
{
@@ -468,6 +468,7 @@ EGLint SwapChain11::reset(int backbufferWidth, int backbufferHeight, EGLint swap
if (mWindow)
{
+#if !defined(ANGLE_OS_WINRT)
// We cannot create a swap chain for an HWND that is owned by a different process
DWORD currentProcessId = GetCurrentProcessId();
DWORD wndProcessId;
@@ -491,14 +492,39 @@ EGLint SwapChain11::reset(int backbufferWidth, int backbufferHeight, EGLint swap
swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
swapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
+ swapChainDesc.Windowed = TRUE;
+ swapChainDesc.OutputWindow = mWindow;
+#else
+ IDXGIFactory2 *factory;
+ HRESULT result = mRenderer->getDxgiFactory()->QueryInterface(IID_PPV_ARGS(&factory));
+ ASSERT(SUCCEEDED(result));
+
+ DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
+ swapChainDesc.Format = gl_d3d11::ConvertRenderbufferFormat(mBackBufferFormat);
+ swapChainDesc.Width = backbufferWidth;
+ swapChainDesc.Height = backbufferHeight;
+ swapChainDesc.Stereo = FALSE;
+#if !defined(ANGLE_OS_WINPHONE)
+ swapChainDesc.BufferCount = 2;
+ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
+#else
+ swapChainDesc.BufferCount = 1;
+ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
+#endif
+#endif
+
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.Flags = 0;
- swapChainDesc.OutputWindow = mWindow;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
- swapChainDesc.Windowed = TRUE;
- HRESULT result = factory->CreateSwapChain(device, &swapChainDesc, &mSwapChain);
+#if !defined(ANGLE_OS_WINRT)
+ result = factory->CreateSwapChain(device, &swapChainDesc, &mSwapChain);
+#else
+ IDXGISwapChain1 *swapChain;
+ result = factory->CreateSwapChainForCoreWindow(device, mWindow, &swapChainDesc, NULL, &swapChain);
+ mSwapChain = swapChain;
+#endif
if (FAILED(result))
{
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.h b/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.h
index 800104602e..2a030c839d 100644
--- a/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.h
+++ b/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.h
@@ -19,7 +19,7 @@ class Renderer11;
class SwapChain11 : public SwapChain
{
public:
- SwapChain11(Renderer11 *renderer, HWND window, HANDLE shareHandle,
+ SwapChain11(Renderer11 *renderer, EGLNativeWindowType window, HANDLE shareHandle,
GLenum backBufferFormat, GLenum depthBufferFormat);
virtual ~SwapChain11();
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/TextureStorage11.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/TextureStorage11.cpp
index 408b48ebab..32a407a988 100644
--- a/src/3rdparty/angle/src/libGLESv2/renderer/TextureStorage11.cpp
+++ b/src/3rdparty/angle/src/libGLESv2/renderer/TextureStorage11.cpp
@@ -222,14 +222,14 @@ TextureStorage11_2D::TextureStorage11_2D(Renderer *renderer, SwapChain11 *swapch
}
TextureStorage11_2D::TextureStorage11_2D(Renderer *renderer, int levels, GLenum internalformat, GLenum usage, bool forceRenderable, GLsizei width, GLsizei height)
- : TextureStorage11(renderer, GetTextureBindFlags(gl_d3d11::ConvertTextureFormat(internalformat), usage, forceRenderable))
+ : TextureStorage11(renderer, GetTextureBindFlags(gl_d3d11::ConvertTextureFormat(internalformat, Renderer11::makeRenderer11(renderer)->getFeatureLevel()), usage, forceRenderable))
{
for (unsigned int i = 0; i < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
{
mRenderTarget[i] = NULL;
}
- DXGI_FORMAT convertedFormat = gl_d3d11::ConvertTextureFormat(internalformat);
+ DXGI_FORMAT convertedFormat = gl_d3d11::ConvertTextureFormat(internalformat, Renderer11::makeRenderer11(renderer)->getFeatureLevel());
if (d3d11::IsDepthStencilFormat(convertedFormat))
{
mTextureFormat = d3d11::GetDepthTextureFormat(convertedFormat);
@@ -440,7 +440,7 @@ void TextureStorage11_2D::generateMipmap(int level)
}
TextureStorage11_Cube::TextureStorage11_Cube(Renderer *renderer, int levels, GLenum internalformat, GLenum usage, bool forceRenderable, int size)
- : TextureStorage11(renderer, GetTextureBindFlags(gl_d3d11::ConvertTextureFormat(internalformat), usage, forceRenderable))
+ : TextureStorage11(renderer, GetTextureBindFlags(gl_d3d11::ConvertTextureFormat(internalformat, Renderer11::makeRenderer11(renderer)->getFeatureLevel()), usage, forceRenderable))
{
for (unsigned int i = 0; i < 6; i++)
{
@@ -450,7 +450,7 @@ TextureStorage11_Cube::TextureStorage11_Cube(Renderer *renderer, int levels, GLe
}
}
- DXGI_FORMAT convertedFormat = gl_d3d11::ConvertTextureFormat(internalformat);
+ DXGI_FORMAT convertedFormat = gl_d3d11::ConvertTextureFormat(internalformat, Renderer11::makeRenderer11(renderer)->getFeatureLevel());
if (d3d11::IsDepthStencilFormat(convertedFormat))
{
mTextureFormat = d3d11::GetDepthTextureFormat(convertedFormat);
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/renderer11_utils.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/renderer11_utils.cpp
index 13800da258..0624a61160 100644
--- a/src/3rdparty/angle/src/libGLESv2/renderer/renderer11_utils.cpp
+++ b/src/3rdparty/angle/src/libGLESv2/renderer/renderer11_utils.cpp
@@ -329,7 +329,7 @@ DXGI_FORMAT ConvertRenderbufferFormat(GLenum format)
return DXGI_FORMAT_R8G8B8A8_UNORM;
}
-DXGI_FORMAT ConvertTextureFormat(GLenum internalformat)
+DXGI_FORMAT ConvertTextureFormat(GLenum internalformat, D3D_FEATURE_LEVEL featureLevel)
{
switch (internalformat)
{
@@ -342,7 +342,7 @@ DXGI_FORMAT ConvertTextureFormat(GLenum internalformat)
case GL_LUMINANCE8_ALPHA8_EXT:
return DXGI_FORMAT_R8G8B8A8_UNORM;
case GL_ALPHA8_EXT:
- return DXGI_FORMAT_A8_UNORM;
+ return featureLevel >= D3D_FEATURE_LEVEL_10_0 ? DXGI_FORMAT_A8_UNORM : DXGI_FORMAT_B8G8R8A8_UNORM;
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
return DXGI_FORMAT_BC1_UNORM;
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/renderer11_utils.h b/src/3rdparty/angle/src/libGLESv2/renderer/renderer11_utils.h
index 1bc48c1a13..70ad4fea2b 100644
--- a/src/3rdparty/angle/src/libGLESv2/renderer/renderer11_utils.h
+++ b/src/3rdparty/angle/src/libGLESv2/renderer/renderer11_utils.h
@@ -32,7 +32,7 @@ FLOAT ConvertMinLOD(GLenum minFilter, unsigned int lodOffset);
FLOAT ConvertMaxLOD(GLenum minFilter, unsigned int lodOffset);
DXGI_FORMAT ConvertRenderbufferFormat(GLenum format);
-DXGI_FORMAT ConvertTextureFormat(GLenum format);
+DXGI_FORMAT ConvertTextureFormat(GLenum format, D3D_FEATURE_LEVEL featureLevel);
}
namespace d3d11_gl
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/shaders/Clear11.hlsl b/src/3rdparty/angle/src/libGLESv2/renderer/shaders/Clear11.hlsl
index 042ac699b6..cb132dc99c 100644
--- a/src/3rdparty/angle/src/libGLESv2/renderer/shaders/Clear11.hlsl
+++ b/src/3rdparty/angle/src/libGLESv2/renderer/shaders/Clear11.hlsl
@@ -12,10 +12,12 @@ struct PS_OutputMultiple
float4 color1 : SV_TARGET1;
float4 color2 : SV_TARGET2;
float4 color3 : SV_TARGET3;
+#ifdef SM4
float4 color4 : SV_TARGET4;
float4 color5 : SV_TARGET5;
float4 color6 : SV_TARGET6;
float4 color7 : SV_TARGET7;
+#endif
};
PS_OutputMultiple PS_ClearMultiple(in float4 inPosition : SV_POSITION, in float4 inColor : COLOR)
@@ -25,10 +27,12 @@ PS_OutputMultiple PS_ClearMultiple(in float4 inPosition : SV_POSITION, in float4
outColor.color1 = inColor;
outColor.color2 = inColor;
outColor.color3 = inColor;
+#ifdef SM4
outColor.color4 = inColor;
outColor.color5 = inColor;
outColor.color6 = inColor;
outColor.color7 = inColor;
+#endif
return outColor;
}
diff --git a/src/3rdparty/angle/src/libGLESv2/utilities.cpp b/src/3rdparty/angle/src/libGLESv2/utilities.cpp
index 32df49e672..8fd193b164 100644
--- a/src/3rdparty/angle/src/libGLESv2/utilities.cpp
+++ b/src/3rdparty/angle/src/libGLESv2/utilities.cpp
@@ -10,6 +10,14 @@
#include "libGLESv2/utilities.h"
#include "libGLESv2/mathutil.h"
+#if defined(ANGLE_OS_WINRT)
+#include <locale>
+#include <codecvt>
+#include <wrl.h>
+#include <windows.storage.h>
+using namespace ABI::Windows::Storage;
+#endif
+
namespace gl
{
@@ -737,7 +745,50 @@ bool IsTriangleMode(GLenum drawMode)
std::string getTempPath()
{
+#if defined(ANGLE_OS_WINRT)
+
+ static std::string path;
+
+ while (path.empty()) {
+ IApplicationDataStatics *applicationDataFactory;
+ HRESULT result = RoGetActivationFactory(Microsoft::WRL::Wrappers::HStringReference(RuntimeClass_Windows_Storage_ApplicationData).Get(),
+ IID_PPV_ARGS(&applicationDataFactory));
+ if (FAILED(result))
+ break;
+
+ IApplicationData *applicationData;
+ result = applicationDataFactory->get_Current(&applicationData);
+ if (FAILED(result))
+ break;
+
+ IStorageFolder *storageFolder;
+ result = applicationData->get_LocalFolder(&storageFolder);
+ if (FAILED(result))
+ break;
+
+ IStorageItem *localFolder;
+ result = storageFolder->QueryInterface(IID_PPV_ARGS(&localFolder));
+ if (FAILED(result))
+ break;
+
+ HSTRING localFolderPath;
+ result = localFolder->get_Path(&localFolderPath);
+ if (FAILED(result))
+ break;
+
+ std::wstring_convert< std::codecvt_utf8<wchar_t> > converter;
+ path = converter.to_bytes(WindowsGetStringRawBuffer(localFolderPath, NULL));
+ if (path.empty())
+ {
+ UNREACHABLE();
+ break;
+ }
+ }
+
+#else
+
char path[MAX_PATH];
+
DWORD pathLen = GetTempPathA(sizeof(path) / sizeof(path[0]), path);
if (pathLen == 0)
{
@@ -751,6 +802,8 @@ std::string getTempPath()
UNREACHABLE();
return std::string();
}
+
+#endif
return path;
}
diff --git a/src/3rdparty/libjpeg.pri b/src/3rdparty/libjpeg.pri
index e5826eae3c..82c6ed536d 100644
--- a/src/3rdparty/libjpeg.pri
+++ b/src/3rdparty/libjpeg.pri
@@ -4,6 +4,8 @@ wince*: {
contains(CE_ARCH,x86):CONFIG += exceptions_off
}
+winrt: DEFINES += NO_GETENV
+
#Disable warnings in 3rdparty code due to unused arguments
contains(QMAKE_CC, gcc): {
QMAKE_CFLAGS_WARN_ON += -Wno-unused-parameter -Wno-main
diff --git a/src/3rdparty/libpng/pngpriv.h b/src/3rdparty/libpng/pngpriv.h
index 592d4ee0cb..f01e56f612 100644
--- a/src/3rdparty/libpng/pngpriv.h
+++ b/src/3rdparty/libpng/pngpriv.h
@@ -362,7 +362,7 @@ typedef PNG_CONST png_uint_16p FAR * png_const_uint_16pp;
#if defined(WIN32) || defined(_Windows) || defined(_WINDOWS) || \
defined(_WIN32) || defined(__WIN32__)
# include <windows.h> /* defines _WINDOWS_ macro */
-# if defined(WINAPI_FAMILY) && ((WINAPI_FAMILY & WINAPI_FAMILY_DESKTOP_APP) == WINAPI_PARTITION_APP)
+# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
# define _WINRT_ /* Define a macro for Windows Runtime builds */
# endif
#endif
diff --git a/src/3rdparty/sqlite/shell.c b/src/3rdparty/sqlite/shell.c
index 1be2871fed..41ea56492e 100644
--- a/src/3rdparty/sqlite/shell.c
+++ b/src/3rdparty/sqlite/shell.c
@@ -53,7 +53,6 @@
# include <readline/history.h>
#endif
#if !defined(HAVE_EDITLINE) && (!defined(HAVE_READLINE) || HAVE_READLINE!=1)
-# define readline(p) local_getline(p,stdin,0)
# define add_history(X)
# define read_history(X)
# define write_history(X)
@@ -65,13 +64,18 @@
#define isatty(h) _isatty(h)
#define access(f,m) _access((f),(m))
#undef popen
-#define popen(a,b) _popen((a),(b))
+#define popen _popen
#undef pclose
-#define pclose(x) _pclose(x)
+#define pclose _pclose
#else
/* Make sure isatty() has a prototype.
*/
extern int isatty(int);
+
+/* popen and pclose are not C89 functions and so are sometimes omitted from
+** the <stdio.h> header */
+extern FILE *popen(const char*,const char*);
+extern int pclose(FILE*);
#endif
#if defined(_WIN32_WCE)
@@ -332,23 +336,13 @@ static void shellstaticFunc(
** to the text. NULL is returned at end of file, or if malloc()
** fails.
**
-** The interface is like "readline" but no command-line editing
-** is done.
+** If zLine is not NULL then it is a malloced buffer returned from
+** a previous call to this routine that may be reused.
*/
-static char *local_getline(char *zPrompt, FILE *in, int csvFlag){
- char *zLine;
- int nLine;
- int n;
- int inQuote = 0;
+static char *local_getline(char *zLine, FILE *in){
+ int nLine = zLine==0 ? 0 : 100;
+ int n = 0;
- if( zPrompt && *zPrompt ){
- printf("%s",zPrompt);
- fflush(stdout);
- }
- nLine = 100;
- zLine = malloc( nLine );
- if( zLine==0 ) return 0;
- n = 0;
while( 1 ){
if( n+100>nLine ){
nLine = nLine*2 + 100;
@@ -363,42 +357,48 @@ static char *local_getline(char *zPrompt, FILE *in, int csvFlag){
zLine[n] = 0;
break;
}
- while( zLine[n] ){
- if( zLine[n]=='"' ) inQuote = !inQuote;
- n++;
- }
- if( n>0 && zLine[n-1]=='\n' && (!inQuote || !csvFlag) ){
+ while( zLine[n] ) n++;
+ if( n>0 && zLine[n-1]=='\n' ){
n--;
if( n>0 && zLine[n-1]=='\r' ) n--;
zLine[n] = 0;
break;
}
}
- zLine = realloc( zLine, n+1 );
return zLine;
}
/*
** Retrieve a single line of input text.
**
-** zPrior is a string of prior text retrieved. If not the empty
-** string, then issue a continuation prompt.
+** If in==0 then read from standard input and prompt before each line.
+** If isContinuation is true, then a continuation prompt is appropriate.
+** If isContinuation is zero, then the main prompt should be used.
+**
+** If zPrior is not NULL then it is a buffer from a prior call to this
+** routine that can be reused.
+**
+** The result is stored in space obtained from malloc() and must either
+** be freed by the caller or else passed back into this routine via the
+** zPrior argument for reuse.
*/
-static char *one_input_line(const char *zPrior, FILE *in){
+static char *one_input_line(FILE *in, char *zPrior, int isContinuation){
char *zPrompt;
char *zResult;
if( in!=0 ){
- return local_getline(0, in, 0);
- }
- if( zPrior && zPrior[0] ){
- zPrompt = continuePrompt;
+ zResult = local_getline(zPrior, in);
}else{
- zPrompt = mainPrompt;
- }
- zResult = readline(zPrompt);
+ zPrompt = isContinuation ? continuePrompt : mainPrompt;
#if defined(HAVE_READLINE) && HAVE_READLINE==1
- if( zResult && *zResult ) add_history(zResult);
+ free(zPrior);
+ zResult = readline(zPrompt);
+ if( zResult && *zResult ) add_history(zResult);
+#else
+ printf("%s", zPrompt);
+ fflush(stdout);
+ zResult = local_getline(zPrior, stdin);
#endif
+ }
return zResult;
}
@@ -554,7 +554,7 @@ static void output_c_string(FILE *out, const char *z){
}else if( c=='\r' ){
fputc('\\', out);
fputc('r', out);
- }else if( !isprint(c) ){
+ }else if( !isprint(c&0xff) ){
fprintf(out, "\\%03o", c&0xff);
}else{
fputc(c, out);
@@ -974,7 +974,7 @@ static int run_table_dump_query(
rc = sqlite3_prepare(p->db, zSelect, -1, &pSelect, 0);
if( rc!=SQLITE_OK || !pSelect ){
fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db));
- p->nErr++;
+ if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++;
return rc;
}
rc = sqlite3_step(pSelect);
@@ -1001,7 +1001,7 @@ static int run_table_dump_query(
rc = sqlite3_finalize(pSelect);
if( rc!=SQLITE_OK ){
fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db));
- p->nErr++;
+ if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++;
}
return rc;
}
@@ -1109,6 +1109,8 @@ static int display_stats(
fprintf(pArg->out, "Sort Operations: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX, bReset);
fprintf(pArg->out, "Autoindex Inserts: %d\n", iCur);
+ iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset);
+ fprintf(pArg->out, "Virtual Machine Steps: %d\n", iCur);
}
return 0;
@@ -1192,7 +1194,7 @@ static int shell_exec(
char **azCols = (char **)pData; /* Names of result columns */
char **azVals = &azCols[nCol]; /* Results */
int *aiTypes = (int *)&azVals[nCol]; /* Result types */
- int i;
+ int i, x;
assert(sizeof(int) <= sizeof(char *));
/* save off ptrs to column names */
for(i=0; i<nCol; i++){
@@ -1201,8 +1203,12 @@ static int shell_exec(
do{
/* extract the data and data types */
for(i=0; i<nCol; i++){
- azVals[i] = (char *)sqlite3_column_text(pStmt, i);
- aiTypes[i] = sqlite3_column_type(pStmt, i);
+ aiTypes[i] = x = sqlite3_column_type(pStmt, i);
+ if( x==SQLITE_BLOB && pArg && pArg->mode==MODE_Insert ){
+ azVals[i] = "";
+ }else{
+ azVals[i] = (char*)sqlite3_column_text(pStmt, i);
+ }
if( !azVals[i] && (aiTypes[i]!=SQLITE_NULL) ){
rc = SQLITE_NOMEM;
break; /* from for */
@@ -1278,7 +1284,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
if( strcmp(zTable, "sqlite_sequence")==0 ){
zPrepStmt = "DELETE FROM sqlite_sequence;\n";
- }else if( strcmp(zTable, "sqlite_stat1")==0 ){
+ }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 ){
fprintf(p->out, "ANALYZE sqlite_master;\n");
}else if( strncmp(zTable, "sqlite_", 7)==0 ){
return 0;
@@ -1490,6 +1496,7 @@ static void open_db(struct callback_data *p){
** \t -> tab
** \n -> newline
** \r -> carriage return
+** \" -> "
** \NNN -> ascii character NNN in octal
** \\ -> backslash
*/
@@ -1505,6 +1512,8 @@ static void resolve_backslashes(char *z){
c = '\t';
}else if( c=='r' ){
c = '\r';
+ }else if( c=='\\' ){
+ c = '\\';
}else if( c>='0' && c<='7' ){
c -= '0';
if( z[i+1]>='0' && z[i+1]<='7' ){
@@ -1523,21 +1532,14 @@ static void resolve_backslashes(char *z){
}
/*
-** Interpret zArg as a boolean value. Return either 0 or 1.
-*/
-static int booleanValue(char *zArg){
- int i;
- for(i=0; zArg[i]>='0' && zArg[i]<='9'; i++){}
- if( i>0 && zArg[i]==0 ) return atoi(zArg);
- if( sqlite3_stricmp(zArg, "on")==0 || sqlite3_stricmp(zArg,"yes")==0 ){
- return 1;
- }
- if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){
- return 0;
- }
- fprintf(stderr, "ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n",
- zArg);
- return 0;
+** Return the value of a hexadecimal digit. Return -1 if the input
+** is not a hex digit.
+*/
+static int hexDigitValue(char c){
+ if( c>='0' && c<='9' ) return c - '0';
+ if( c>='a' && c<='f' ) return c - 'a' + 10;
+ if( c>='A' && c<='F' ) return c - 'A' + 10;
+ return -1;
}
/*
@@ -1564,11 +1566,20 @@ static sqlite3_int64 integerValue(const char *zArg){
}else if( zArg[0]=='+' ){
zArg++;
}
- while( isdigit(zArg[0]) ){
- v = v*10 + zArg[0] - '0';
- zArg++;
+ if( zArg[0]=='0' && zArg[1]=='x' ){
+ int x;
+ zArg += 2;
+ while( (x = hexDigitValue(zArg[0]))>=0 ){
+ v = (v<<4) + x;
+ zArg++;
+ }
+ }else{
+ while( IsDigit(zArg[0]) ){
+ v = v*10 + zArg[0] - '0';
+ zArg++;
+ }
}
- for(i=0; i<sizeof(aMult)/sizeof(aMult[0]); i++){
+ for(i=0; i<ArraySize(aMult); i++){
if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){
v *= aMult[i].iMult;
break;
@@ -1578,6 +1589,29 @@ static sqlite3_int64 integerValue(const char *zArg){
}
/*
+** Interpret zArg as either an integer or a boolean value. Return 1 or 0
+** for TRUE and FALSE. Return the integer value if appropriate.
+*/
+static int booleanValue(char *zArg){
+ int i;
+ if( zArg[0]=='0' && zArg[1]=='x' ){
+ for(i=2; hexDigitValue(zArg[i])>=0; i++){}
+ }else{
+ for(i=0; zArg[i]>='0' && zArg[i]<='9'; i++){}
+ }
+ if( i>0 && zArg[i]==0 ) return (int)(integerValue(zArg) & 0xffffffff);
+ if( sqlite3_stricmp(zArg, "on")==0 || sqlite3_stricmp(zArg,"yes")==0 ){
+ return 1;
+ }
+ if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){
+ return 0;
+ }
+ fprintf(stderr, "ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n",
+ zArg);
+ return 0;
+}
+
+/*
** Close an output file, assuming it is not stderr or stdout
*/
static void output_file_close(FILE *f){
@@ -1624,6 +1658,105 @@ static void test_breakpoint(void){
}
/*
+** An object used to read a CSV file
+*/
+typedef struct CSVReader CSVReader;
+struct CSVReader {
+ const char *zFile; /* Name of the input file */
+ FILE *in; /* Read the CSV text from this input stream */
+ char *z; /* Accumulated text for a field */
+ int n; /* Number of bytes in z */
+ int nAlloc; /* Space allocated for z[] */
+ int nLine; /* Current line number */
+ int cTerm; /* Character that terminated the most recent field */
+ int cSeparator; /* The separator character. (Usually ",") */
+};
+
+/* Append a single byte to z[] */
+static void csv_append_char(CSVReader *p, int c){
+ if( p->n+1>=p->nAlloc ){
+ p->nAlloc += p->nAlloc + 100;
+ p->z = sqlite3_realloc(p->z, p->nAlloc);
+ if( p->z==0 ){
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ }
+ p->z[p->n++] = (char)c;
+}
+
+/* Read a single field of CSV text. Compatible with rfc4180 and extended
+** with the option of having a separator other than ",".
+**
+** + Input comes from p->in.
+** + Store results in p->z of length p->n. Space to hold p->z comes
+** from sqlite3_malloc().
+** + Use p->cSep as the separator. The default is ",".
+** + Keep track of the line number in p->nLine.
+** + Store the character that terminates the field in p->cTerm. Store
+** EOF on end-of-file.
+** + Report syntax errors on stderr
+*/
+static char *csv_read_one_field(CSVReader *p){
+ int c, pc;
+ int cSep = p->cSeparator;
+ p->n = 0;
+ c = fgetc(p->in);
+ if( c==EOF || seenInterrupt ){
+ p->cTerm = EOF;
+ return 0;
+ }
+ if( c=='"' ){
+ int startLine = p->nLine;
+ int cQuote = c;
+ pc = 0;
+ while( 1 ){
+ c = fgetc(p->in);
+ if( c=='\n' ) p->nLine++;
+ if( c==cQuote ){
+ if( pc==cQuote ){
+ pc = 0;
+ continue;
+ }
+ }
+ if( (c==cSep && pc==cQuote)
+ || (c=='\n' && pc==cQuote)
+ || (c=='\n' && pc=='\r' && p->n>=2 && p->z[p->n-2]==cQuote)
+ || (c==EOF && pc==cQuote)
+ ){
+ do{ p->n--; }while( p->z[p->n]!=cQuote );
+ p->cTerm = c;
+ break;
+ }
+ if( pc==cQuote && c!='\r' ){
+ fprintf(stderr, "%s:%d: unescaped %c character\n",
+ p->zFile, p->nLine, cQuote);
+ }
+ if( c==EOF ){
+ fprintf(stderr, "%s:%d: unterminated %c-quoted field\n",
+ p->zFile, startLine, cQuote);
+ p->cTerm = EOF;
+ break;
+ }
+ csv_append_char(p, c);
+ pc = c;
+ }
+ }else{
+ while( c!=EOF && c!=cSep && c!='\n' ){
+ csv_append_char(p, c);
+ c = fgetc(p->in);
+ }
+ if( c=='\n' ){
+ p->nLine++;
+ if( p->n>1 && p->z[p->n-1]=='\r' ) p->n--;
+ }
+ p->cTerm = c;
+ }
+ if( p->z ) p->z[p->n] = 0;
+ return p->z;
+}
+
+/*
** If an input line begins with "." then invoke this routine to
** process that line.
**
@@ -1644,7 +1777,10 @@ static int do_meta_command(char *zLine, struct callback_data *p){
if( zLine[i]=='\'' || zLine[i]=='"' ){
int delim = zLine[i++];
azArg[nArg++] = &zLine[i];
- while( zLine[i] && zLine[i]!=delim ){ i++; }
+ while( zLine[i] && zLine[i]!=delim ){
+ if( zLine[i]=='\\' && delim=='"' && zLine[i+1]!=0 ) i++;
+ i++;
+ }
if( zLine[i]==delim ){
zLine[i++] = 0;
}
@@ -1665,7 +1801,6 @@ static int do_meta_command(char *zLine, struct callback_data *p){
if( c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0 ){
const char *zDestFile = 0;
const char *zDb = 0;
- const char *zKey = 0;
sqlite3 *pDest;
sqlite3_backup *pBackup;
int j;
@@ -1673,9 +1808,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
const char *z = azArg[j];
if( z[0]=='-' ){
while( z[0]=='-' ) z++;
- if( strcmp(z,"key")==0 && j<nArg-1 ){
- zKey = azArg[++j];
- }else
+ /* No options to process at this time */
{
fprintf(stderr, "unknown option: %s\n", azArg[j]);
return 1;
@@ -1701,11 +1834,6 @@ static int do_meta_command(char *zLine, struct callback_data *p){
sqlite3_close(pDest);
return 1;
}
-#ifdef SQLITE_HAS_CODEC
- sqlite3_key(pDest, zKey, (int)strlen(zKey));
-#else
- (void)zKey;
-#endif
open_db(p);
pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb);
if( pBackup==0 ){
@@ -1808,7 +1936,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}else
if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){
- if( nArg>1 && (rc = atoi(azArg[1]))!=0 ) exit(rc);
+ if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) exit(rc);
rc = 2;
}else
@@ -1861,48 +1989,98 @@ static int do_meta_command(char *zLine, struct callback_data *p){
if( c=='i' && strncmp(azArg[0], "import", n)==0 && nArg==3 ){
char *zTable = azArg[2]; /* Insert data into this table */
- char *zFile = azArg[1]; /* The file from which to extract data */
+ char *zFile = azArg[1]; /* Name of file to extra content from */
sqlite3_stmt *pStmt = NULL; /* A statement */
int nCol; /* Number of columns in the table */
int nByte; /* Number of bytes in an SQL string */
int i, j; /* Loop counters */
+ int needCommit; /* True to COMMIT or ROLLBACK at end */
int nSep; /* Number of bytes in p->separator[] */
char *zSql; /* An SQL statement */
- char *zLine; /* A single line of input from the file */
- char **azCol; /* zLine[] broken up into columns */
- char *zCommit; /* How to commit changes */
- FILE *in; /* The input file */
- int lineno = 0; /* Line number of input file */
+ CSVReader sCsv; /* Reader context */
+ int (*xCloser)(FILE*); /* Procedure to close th3 connection */
+ seenInterrupt = 0;
+ memset(&sCsv, 0, sizeof(sCsv));
open_db(p);
nSep = strlen30(p->separator);
if( nSep==0 ){
fprintf(stderr, "Error: non-null separator required for import\n");
return 1;
}
+ if( nSep>1 ){
+ fprintf(stderr, "Error: multi-character separators not allowed"
+ " for import\n");
+ return 1;
+ }
+ sCsv.zFile = zFile;
+ sCsv.nLine = 1;
+ if( sCsv.zFile[0]=='|' ){
+ sCsv.in = popen(sCsv.zFile+1, "r");
+ sCsv.zFile = "<pipe>";
+ xCloser = pclose;
+ }else{
+ sCsv.in = fopen(sCsv.zFile, "rb");
+ xCloser = fclose;
+ }
+ if( sCsv.in==0 ){
+ fprintf(stderr, "Error: cannot open \"%s\"\n", zFile);
+ return 1;
+ }
+ sCsv.cSeparator = p->separator[0];
zSql = sqlite3_mprintf("SELECT * FROM %s", zTable);
if( zSql==0 ){
fprintf(stderr, "Error: out of memory\n");
+ xCloser(sCsv.in);
return 1;
}
nByte = strlen30(zSql);
rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0);
+ if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(db))==0 ){
+ char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable);
+ char cSep = '(';
+ while( csv_read_one_field(&sCsv) ){
+ zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCsv.z);
+ cSep = ',';
+ if( sCsv.cTerm!=sCsv.cSeparator ) break;
+ }
+ if( cSep=='(' ){
+ sqlite3_free(zCreate);
+ sqlite3_free(sCsv.z);
+ xCloser(sCsv.in);
+ fprintf(stderr,"%s: empty file\n", sCsv.zFile);
+ return 1;
+ }
+ zCreate = sqlite3_mprintf("%z\n)", zCreate);
+ rc = sqlite3_exec(p->db, zCreate, 0, 0, 0);
+ sqlite3_free(zCreate);
+ if( rc ){
+ fprintf(stderr, "CREATE TABLE %s(...) failed: %s\n", zTable,
+ sqlite3_errmsg(db));
+ sqlite3_free(sCsv.z);
+ xCloser(sCsv.in);
+ return 1;
+ }
+ rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0);
+ }
sqlite3_free(zSql);
if( rc ){
if (pStmt) sqlite3_finalize(pStmt);
fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db));
+ xCloser(sCsv.in);
return 1;
}
nCol = sqlite3_column_count(pStmt);
sqlite3_finalize(pStmt);
pStmt = 0;
if( nCol==0 ) return 0; /* no columns, no error */
- zSql = malloc( nByte + 20 + nCol*2 );
+ zSql = sqlite3_malloc( nByte*2 + 20 + nCol*2 );
if( zSql==0 ){
fprintf(stderr, "Error: out of memory\n");
+ xCloser(sCsv.in);
return 1;
}
- sqlite3_snprintf(nByte+20, zSql, "INSERT INTO %s VALUES(?", zTable);
+ sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable);
j = strlen30(zSql);
for(i=1; i<nCol; i++){
zSql[j++] = ',';
@@ -1911,79 +2089,52 @@ static int do_meta_command(char *zLine, struct callback_data *p){
zSql[j++] = ')';
zSql[j] = 0;
rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0);
- free(zSql);
+ sqlite3_free(zSql);
if( rc ){
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db));
if (pStmt) sqlite3_finalize(pStmt);
+ xCloser(sCsv.in);
return 1;
}
- in = fopen(zFile, "rb");
- if( in==0 ){
- fprintf(stderr, "Error: cannot open \"%s\"\n", zFile);
- sqlite3_finalize(pStmt);
- return 1;
- }
- azCol = malloc( sizeof(azCol[0])*(nCol+1) );
- if( azCol==0 ){
- fprintf(stderr, "Error: out of memory\n");
- fclose(in);
- sqlite3_finalize(pStmt);
- return 1;
- }
- sqlite3_exec(p->db, "BEGIN", 0, 0, 0);
- zCommit = "COMMIT";
- while( (zLine = local_getline(0, in, 1))!=0 ){
- char *z, c;
- int inQuote = 0;
- lineno++;
- azCol[0] = zLine;
- for(i=0, z=zLine; (c = *z)!=0; z++){
- if( c=='"' ) inQuote = !inQuote;
- if( c=='\n' ) lineno++;
- if( !inQuote && c==p->separator[0] && strncmp(z,p->separator,nSep)==0 ){
- *z = 0;
+ needCommit = sqlite3_get_autocommit(db);
+ if( needCommit ) sqlite3_exec(db, "BEGIN", 0, 0, 0);
+ do{
+ int startLine = sCsv.nLine;
+ for(i=0; i<nCol; i++){
+ char *z = csv_read_one_field(&sCsv);
+ if( z==0 && i==0 ) break;
+ sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT);
+ if( i<nCol-1 && sCsv.cTerm!=sCsv.cSeparator ){
+ fprintf(stderr, "%s:%d: expected %d columns but found %d - "
+ "filling the rest with NULL\n",
+ sCsv.zFile, startLine, nCol, i+1);
i++;
- if( i<nCol ){
- azCol[i] = &z[nSep];
- z += nSep-1;
- }
+ while( i<nCol ){ sqlite3_bind_null(pStmt, i); i++; }
}
- } /* end for */
- *z = 0;
- if( i+1!=nCol ){
- fprintf(stderr,
- "Error: %s line %d: expected %d columns of data but found %d\n",
- zFile, lineno, nCol, i+1);
- zCommit = "ROLLBACK";
- free(zLine);
- rc = 1;
- break; /* from while */
}
- for(i=0; i<nCol; i++){
- if( azCol[i][0]=='"' ){
- int k;
- for(z=azCol[i], j=1, k=0; z[j]; j++){
- if( z[j]=='"' ){ j++; if( z[j]==0 ) break; }
- z[k++] = z[j];
- }
- z[k] = 0;
+ if( sCsv.cTerm==sCsv.cSeparator ){
+ do{
+ csv_read_one_field(&sCsv);
+ i++;
+ }while( sCsv.cTerm==sCsv.cSeparator );
+ fprintf(stderr, "%s:%d: expected %d columns but found %d - "
+ "extras ignored\n",
+ sCsv.zFile, startLine, nCol, i);
+ }
+ if( i>=nCol ){
+ sqlite3_step(pStmt);
+ rc = sqlite3_reset(pStmt);
+ if( rc!=SQLITE_OK ){
+ fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCsv.zFile, startLine,
+ sqlite3_errmsg(db));
}
- sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC);
- }
- sqlite3_step(pStmt);
- rc = sqlite3_reset(pStmt);
- free(zLine);
- if( rc!=SQLITE_OK ){
- fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db));
- zCommit = "ROLLBACK";
- rc = 1;
- break; /* from while */
}
- } /* end while */
- free(azCol);
- fclose(in);
+ }while( sCsv.cTerm!=EOF );
+
+ xCloser(sCsv.in);
+ sqlite3_free(sCsv.z);
sqlite3_finalize(pStmt);
- sqlite3_exec(p->db, zCommit, 0, 0, 0);
+ if( needCommit ) sqlite3_exec(db, "COMMIT", 0, 0, 0);
}else
if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg<3 ){
@@ -2305,6 +2456,29 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}
}else
+#ifdef SQLITE_DEBUG
+ /* Undocumented commands for internal testing. Subject to change
+ ** without notice. */
+ if( c=='s' && n>=10 && strncmp(azArg[0], "selftest-", 9)==0 ){
+ if( strncmp(azArg[0]+9, "boolean", n-9)==0 ){
+ int i, v;
+ for(i=1; i<nArg; i++){
+ v = booleanValue(azArg[i]);
+ fprintf(p->out, "%s: %d 0x%x\n", azArg[i], v, v);
+ }
+ }
+ if( strncmp(azArg[0]+9, "integer", n-9)==0 ){
+ int i; sqlite3_int64 v;
+ for(i=1; i<nArg; i++){
+ char zBuf[200];
+ v = integerValue(azArg[i]);
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%s: %lld 0x%llx\n", azArg[i], v, v);
+ fprintf(p->out, "%s", zBuf);
+ }
+ }
+ }else
+#endif
+
if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){
sqlite3_snprintf(sizeof(p->separator), p->separator,
"%.*s", (int)sizeof(p->separator)-1, azArg[1]);
@@ -2458,7 +2632,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}
}
}
- if( testctrl<0 ) testctrl = atoi(azArg[1]);
+ if( testctrl<0 ) testctrl = (int)integerValue(azArg[1]);
if( (testctrl<SQLITE_TESTCTRL_FIRST) || (testctrl>SQLITE_TESTCTRL_LAST) ){
fprintf(stderr,"Error: invalid testctrl option: %s\n", azArg[1]);
}else{
@@ -2492,7 +2666,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
/* sqlite3_test_control(int, uint) */
case SQLITE_TESTCTRL_PENDING_BYTE:
if( nArg==3 ){
- unsigned int opt = (unsigned int)integerValue(azArg[2]);
+ unsigned int opt = (unsigned int)integerValue(azArg[2]);
rc = sqlite3_test_control(testctrl, opt);
fprintf(p->out, "%d (0x%08x)\n", rc, rc);
} else {
@@ -2505,7 +2679,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
case SQLITE_TESTCTRL_ASSERT:
case SQLITE_TESTCTRL_ALWAYS:
if( nArg==3 ){
- int opt = atoi(azArg[2]);
+ int opt = booleanValue(azArg[2]);
rc = sqlite3_test_control(testctrl, opt);
fprintf(p->out, "%d (0x%08x)\n", rc, rc);
} else {
@@ -2542,7 +2716,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 && nArg==2 ){
open_db(p);
- sqlite3_busy_timeout(p->db, atoi(azArg[1]));
+ sqlite3_busy_timeout(p->db, (int)integerValue(azArg[1]));
}else
if( HAS_TIMER && c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0
@@ -2592,7 +2766,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
int j;
assert( nArg<=ArraySize(azArg) );
for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){
- p->colWidth[j-1] = atoi(azArg[j]);
+ p->colWidth[j-1] = (int)integerValue(azArg[j]);
}
}else
@@ -2609,7 +2783,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
** Return TRUE if a semicolon occurs anywhere in the first N characters
** of string z[].
*/
-static int _contains_semicolon(const char *z, int N){
+static int line_contains_semicolon(const char *z, int N){
int i;
for(i=0; i<N; i++){ if( z[i]==';' ) return 1; }
return 0;
@@ -2644,7 +2818,7 @@ static int _all_whitespace(const char *z){
** than a semi-colon. The SQL Server style "go" command is understood
** as is the Oracle "/".
*/
-static int _is_command_terminator(const char *zLine){
+static int line_is_command_terminator(const char *zLine){
while( IsSpace(zLine[0]) ){ zLine++; };
if( zLine[0]=='/' && _all_whitespace(&zLine[1]) ){
return 1; /* Oracle */
@@ -2660,7 +2834,7 @@ static int _is_command_terminator(const char *zLine){
** Return true if zSql is a complete SQL statement. Return false if it
** ends in the middle of a string literal or C-style comment.
*/
-static int _is_complete(char *zSql, int nSql){
+static int line_is_complete(char *zSql, int nSql){
int rc;
if( zSql==0 ) return 1;
zSql[nSql] = ';';
@@ -2680,20 +2854,21 @@ static int _is_complete(char *zSql, int nSql){
** Return the number of errors.
*/
static int process_input(struct callback_data *p, FILE *in){
- char *zLine = 0;
- char *zSql = 0;
- int nSql = 0;
- int nSqlPrior = 0;
- char *zErrMsg;
- int rc;
- int errCnt = 0;
- int lineno = 0;
- int startline = 0;
+ char *zLine = 0; /* A single input line */
+ char *zSql = 0; /* Accumulated SQL text */
+ int nLine; /* Length of current line */
+ int nSql = 0; /* Bytes of zSql[] used */
+ int nAlloc = 0; /* Allocated zSql[] space */
+ int nSqlPrior = 0; /* Bytes of zSql[] used by prior line */
+ char *zErrMsg; /* Error message returned */
+ int rc; /* Error code */
+ int errCnt = 0; /* Number of errors seen */
+ int lineno = 0; /* Current line number */
+ int startline = 0; /* Line number for start of current input */
while( errCnt==0 || !bail_on_error || (in==0 && stdin_is_interactive) ){
fflush(p->out);
- free(zLine);
- zLine = one_input_line(zSql, in);
+ zLine = one_input_line(in, zLine, nSql>0);
if( zLine==0 ){
/* End of input */
if( stdin_is_interactive ) printf("\n");
@@ -2704,7 +2879,7 @@ static int process_input(struct callback_data *p, FILE *in){
seenInterrupt = 0;
}
lineno++;
- if( (zSql==0 || zSql[0]==0) && _all_whitespace(zLine) ) continue;
+ if( nSql==0 && _all_whitespace(zLine) ) continue;
if( zLine && zLine[0]=='.' && nSql==0 ){
if( p->echoOn ) printf("%s\n", zLine);
rc = do_meta_command(zLine, p);
@@ -2715,35 +2890,32 @@ static int process_input(struct callback_data *p, FILE *in){
}
continue;
}
- if( _is_command_terminator(zLine) && _is_complete(zSql, nSql) ){
+ if( line_is_command_terminator(zLine) && line_is_complete(zSql, nSql) ){
memcpy(zLine,";",2);
}
+ nLine = strlen30(zLine);
+ if( nSql+nLine+2>=nAlloc ){
+ nAlloc = nSql+nLine+100;
+ zSql = realloc(zSql, nAlloc);
+ if( zSql==0 ){
+ fprintf(stderr, "Error: out of memory\n");
+ exit(1);
+ }
+ }
nSqlPrior = nSql;
- if( zSql==0 ){
+ if( nSql==0 ){
int i;
for(i=0; zLine[i] && IsSpace(zLine[i]); i++){}
- if( zLine[i]!=0 ){
- nSql = strlen30(zLine);
- zSql = malloc( nSql+3 );
- if( zSql==0 ){
- fprintf(stderr, "Error: out of memory\n");
- exit(1);
- }
- memcpy(zSql, zLine, nSql+1);
- startline = lineno;
- }
+ assert( nAlloc>0 && zSql!=0 );
+ memcpy(zSql, zLine+i, nLine+1-i);
+ startline = lineno;
+ nSql = nLine-i;
}else{
- int len = strlen30(zLine);
- zSql = realloc( zSql, nSql + len + 4 );
- if( zSql==0 ){
- fprintf(stderr,"Error: out of memory\n");
- exit(1);
- }
zSql[nSql++] = '\n';
- memcpy(&zSql[nSql], zLine, len+1);
- nSql += len;
+ memcpy(zSql+nSql, zLine, nLine+1);
+ nSql += nLine;
}
- if( zSql && _contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior)
+ if( nSql && line_contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior)
&& sqlite3_complete(zSql) ){
p->cnt = 0;
open_db(p);
@@ -2767,16 +2939,12 @@ static int process_input(struct callback_data *p, FILE *in){
}
errCnt++;
}
- free(zSql);
- zSql = 0;
nSql = 0;
- }else if( zSql && _all_whitespace(zSql) ){
- free(zSql);
- zSql = 0;
+ }else if( nSql && _all_whitespace(zSql) ){
nSql = 0;
}
}
- if( zSql ){
+ if( nSql ){
if( !_all_whitespace(zSql) ){
fprintf(stderr, "Error: incomplete SQL: %s\n", zSql);
}
@@ -3024,7 +3192,6 @@ int main(int argc, char **argv){
stdin_is_interactive = 0;
}else if( strcmp(z,"-heap")==0 ){
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
- int j, c;
const char *zSize;
sqlite3_int64 szHeap;
diff --git a/src/3rdparty/sqlite/sqlite3.c b/src/3rdparty/sqlite/sqlite3.c
index 03fa649610..f2696ec0d4 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.7.17. By combining all the individual C code files into this
+** version 3.8.1. 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
@@ -25,549 +25,6 @@
#ifndef SQLITE_API
# define SQLITE_API
#endif
-/************** Begin file sqliteInt.h ***************************************/
-/*
-** 2001 September 15
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-*************************************************************************
-** Internal interface definitions for SQLite.
-**
-*/
-#ifndef _SQLITEINT_H_
-#define _SQLITEINT_H_
-
-/*
-** These #defines should enable >2GB file support on POSIX if the
-** underlying operating system supports it. If the OS lacks
-** large file support, or if the OS is windows, these should be no-ops.
-**
-** Ticket #2739: The _LARGEFILE_SOURCE macro must appear before any
-** system #includes. Hence, this block of code must be the very first
-** code in all source files.
-**
-** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch
-** on the compiler command line. This is necessary if you are compiling
-** on a recent machine (ex: Red Hat 7.2) but you want your code to work
-** on an older machine (ex: Red Hat 6.0). If you compile on Red Hat 7.2
-** without this option, LFS is enable. But LFS does not exist in the kernel
-** in Red Hat 6.0, so the code won't work. Hence, for maximum binary
-** portability you should omit LFS.
-**
-** Similar is true for Mac OS X. LFS is only supported on Mac OS X 9 and later.
-*/
-#ifndef SQLITE_DISABLE_LFS
-# define _LARGE_FILE 1
-# ifndef _FILE_OFFSET_BITS
-# define _FILE_OFFSET_BITS 64
-# endif
-# define _LARGEFILE_SOURCE 1
-#endif
-
-/*
-** Include the configuration header output by 'configure' if we're using the
-** autoconf-based build
-*/
-#ifdef _HAVE_SQLITE_CONFIG_H
-#include "config.h"
-#endif
-
-/************** Include sqliteLimit.h in the middle of sqliteInt.h ***********/
-/************** Begin file sqliteLimit.h *************************************/
-/*
-** 2007 May 7
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-*************************************************************************
-**
-** This file defines various limits of what SQLite can process.
-*/
-
-/*
-** The maximum length of a TEXT or BLOB in bytes. This also
-** limits the size of a row in a table or index.
-**
-** The hard limit is the ability of a 32-bit signed integer
-** to count the size: 2^31-1 or 2147483647.
-*/
-#ifndef SQLITE_MAX_LENGTH
-# define SQLITE_MAX_LENGTH 1000000000
-#endif
-
-/*
-** This is the maximum number of
-**
-** * Columns in a table
-** * Columns in an index
-** * Columns in a view
-** * Terms in the SET clause of an UPDATE statement
-** * Terms in the result set of a SELECT statement
-** * Terms in the GROUP BY or ORDER BY clauses of a SELECT statement.
-** * Terms in the VALUES clause of an INSERT statement
-**
-** The hard upper limit here is 32676. Most database people will
-** tell you that in a well-normalized database, you usually should
-** not have more than a dozen or so columns in any table. And if
-** that is the case, there is no point in having more than a few
-** dozen values in any of the other situations described above.
-*/
-#ifndef SQLITE_MAX_COLUMN
-# define SQLITE_MAX_COLUMN 2000
-#endif
-
-/*
-** The maximum length of a single SQL statement in bytes.
-**
-** It used to be the case that setting this value to zero would
-** turn the limit off. That is no longer true. It is not possible
-** to turn this limit off.
-*/
-#ifndef SQLITE_MAX_SQL_LENGTH
-# define SQLITE_MAX_SQL_LENGTH 1000000000
-#endif
-
-/*
-** The maximum depth of an expression tree. This is limited to
-** some extent by SQLITE_MAX_SQL_LENGTH. But sometime you might
-** want to place more severe limits on the complexity of an
-** expression.
-**
-** A value of 0 used to mean that the limit was not enforced.
-** But that is no longer true. The limit is now strictly enforced
-** at all times.
-*/
-#ifndef SQLITE_MAX_EXPR_DEPTH
-# define SQLITE_MAX_EXPR_DEPTH 1000
-#endif
-
-/*
-** The maximum number of terms in a compound SELECT statement.
-** The code generator for compound SELECT statements does one
-** level of recursion for each term. A stack overflow can result
-** if the number of terms is too large. In practice, most SQL
-** never has more than 3 or 4 terms. Use a value of 0 to disable
-** any limit on the number of terms in a compount SELECT.
-*/
-#ifndef SQLITE_MAX_COMPOUND_SELECT
-# define SQLITE_MAX_COMPOUND_SELECT 500
-#endif
-
-/*
-** The maximum number of opcodes in a VDBE program.
-** Not currently enforced.
-*/
-#ifndef SQLITE_MAX_VDBE_OP
-# define SQLITE_MAX_VDBE_OP 25000
-#endif
-
-/*
-** The maximum number of arguments to an SQL function.
-*/
-#ifndef SQLITE_MAX_FUNCTION_ARG
-# define SQLITE_MAX_FUNCTION_ARG 127
-#endif
-
-/*
-** The maximum number of in-memory pages to use for the main database
-** table and for temporary tables. The SQLITE_DEFAULT_CACHE_SIZE
-*/
-#ifndef SQLITE_DEFAULT_CACHE_SIZE
-# define SQLITE_DEFAULT_CACHE_SIZE 2000
-#endif
-#ifndef SQLITE_DEFAULT_TEMP_CACHE_SIZE
-# define SQLITE_DEFAULT_TEMP_CACHE_SIZE 500
-#endif
-
-/*
-** The default number of frames to accumulate in the log file before
-** checkpointing the database in WAL mode.
-*/
-#ifndef SQLITE_DEFAULT_WAL_AUTOCHECKPOINT
-# define SQLITE_DEFAULT_WAL_AUTOCHECKPOINT 1000
-#endif
-
-/*
-** The maximum number of attached databases. This must be between 0
-** and 62. The upper bound on 62 is because a 64-bit integer bitmap
-** is used internally to track attached databases.
-*/
-#ifndef SQLITE_MAX_ATTACHED
-# define SQLITE_MAX_ATTACHED 10
-#endif
-
-
-/*
-** The maximum value of a ?nnn wildcard that the parser will accept.
-*/
-#ifndef SQLITE_MAX_VARIABLE_NUMBER
-# define SQLITE_MAX_VARIABLE_NUMBER 999
-#endif
-
-/* Maximum page size. The upper bound on this value is 65536. This a limit
-** imposed by the use of 16-bit offsets within each page.
-**
-** Earlier versions of SQLite allowed the user to change this value at
-** compile time. This is no longer permitted, on the grounds that it creates
-** a library that is technically incompatible with an SQLite library
-** compiled with a different limit. If a process operating on a database
-** with a page-size of 65536 bytes crashes, then an instance of SQLite
-** compiled with the default page-size limit will not be able to rollback
-** the aborted transaction. This could lead to database corruption.
-*/
-#ifdef SQLITE_MAX_PAGE_SIZE
-# undef SQLITE_MAX_PAGE_SIZE
-#endif
-#define SQLITE_MAX_PAGE_SIZE 65536
-
-
-/*
-** The default size of a database page.
-*/
-#ifndef SQLITE_DEFAULT_PAGE_SIZE
-# define SQLITE_DEFAULT_PAGE_SIZE 1024
-#endif
-#if SQLITE_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE
-# undef SQLITE_DEFAULT_PAGE_SIZE
-# define SQLITE_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE
-#endif
-
-/*
-** Ordinarily, if no value is explicitly provided, SQLite creates databases
-** with page size SQLITE_DEFAULT_PAGE_SIZE. However, based on certain
-** device characteristics (sector-size and atomic write() support),
-** SQLite may choose a larger value. This constant is the maximum value
-** SQLite will choose on its own.
-*/
-#ifndef SQLITE_MAX_DEFAULT_PAGE_SIZE
-# define SQLITE_MAX_DEFAULT_PAGE_SIZE 8192
-#endif
-#if SQLITE_MAX_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE
-# undef SQLITE_MAX_DEFAULT_PAGE_SIZE
-# define SQLITE_MAX_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE
-#endif
-
-
-/*
-** Maximum number of pages in one database file.
-**
-** This is really just the default value for the max_page_count pragma.
-** This value can be lowered (or raised) at run-time using that the
-** max_page_count macro.
-*/
-#ifndef SQLITE_MAX_PAGE_COUNT
-# define SQLITE_MAX_PAGE_COUNT 1073741823
-#endif
-
-/*
-** Maximum length (in bytes) of the pattern in a LIKE or GLOB
-** operator.
-*/
-#ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH
-# define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000
-#endif
-
-/*
-** Maximum depth of recursion for triggers.
-**
-** A value of 1 means that a trigger program will not be able to itself
-** fire any triggers. A value of 0 means that no trigger programs at all
-** may be executed.
-*/
-#ifndef SQLITE_MAX_TRIGGER_DEPTH
-# define SQLITE_MAX_TRIGGER_DEPTH 1000
-#endif
-
-/************** End of sqliteLimit.h *****************************************/
-/************** Continuing where we left off in sqliteInt.h ******************/
-
-/* Disable nuisance warnings on Borland compilers */
-#if defined(__BORLANDC__)
-#pragma warn -rch /* unreachable code */
-#pragma warn -ccc /* Condition is always true or false */
-#pragma warn -aus /* Assigned value is never used */
-#pragma warn -csu /* Comparing signed and unsigned */
-#pragma warn -spa /* Suspicious pointer arithmetic */
-#endif
-
-/* Needed for various definitions... */
-#ifndef _GNU_SOURCE
-# define _GNU_SOURCE
-#endif
-
-#if defined(__OpenBSD__) && !defined(_BSD_SOURCE)
-# define _BSD_SOURCE
-#endif
-
-/*
-** Include standard header files as necessary
-*/
-#ifdef HAVE_STDINT_H
-#include <stdint.h>
-#endif
-#ifdef HAVE_INTTYPES_H
-#include <inttypes.h>
-#endif
-
-/*
-** The following macros are used to cast pointers to integers and
-** integers to pointers. The way you do this varies from one compiler
-** to the next, so we have developed the following set of #if statements
-** to generate appropriate macros for a wide range of compilers.
-**
-** The correct "ANSI" way to do this is to use the intptr_t type.
-** Unfortunately, that typedef is not available on all compilers, or
-** if it is available, it requires an #include of specific headers
-** that vary from one machine to the next.
-**
-** Ticket #3860: The llvm-gcc-4.2 compiler from Apple chokes on
-** the ((void*)&((char*)0)[X]) construct. But MSVC chokes on ((void*)(X)).
-** So we have to define the macros in different ways depending on the
-** compiler.
-*/
-#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */
-# define SQLITE_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X))
-# define SQLITE_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X))
-#elif !defined(__GNUC__) /* Works for compilers other than LLVM */
-# define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X])
-# define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0))
-#elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */
-# define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X))
-# define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X))
-#else /* Generates a warning - but it always works */
-# define SQLITE_INT_TO_PTR(X) ((void*)(X))
-# define SQLITE_PTR_TO_INT(X) ((int)(X))
-#endif
-
-/*
-** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2.
-** 0 means mutexes are permanently disable and the library is never
-** threadsafe. 1 means the library is serialized which is the highest
-** level of threadsafety. 2 means the libary is multithreaded - multiple
-** threads can use SQLite as long as no two threads try to use the same
-** database connection at the same time.
-**
-** Older versions of SQLite used an optional THREADSAFE macro.
-** We support that for legacy.
-*/
-#if !defined(SQLITE_THREADSAFE)
-# if defined(THREADSAFE)
-# define SQLITE_THREADSAFE THREADSAFE
-# else
-# define SQLITE_THREADSAFE 1 /* IMP: R-07272-22309 */
-# endif
-#endif
-
-/*
-** Powersafe overwrite is on by default. But can be turned off using
-** the -DSQLITE_POWERSAFE_OVERWRITE=0 command-line option.
-*/
-#ifndef SQLITE_POWERSAFE_OVERWRITE
-# define SQLITE_POWERSAFE_OVERWRITE 1
-#endif
-
-/*
-** The SQLITE_DEFAULT_MEMSTATUS macro must be defined as either 0 or 1.
-** It determines whether or not the features related to
-** SQLITE_CONFIG_MEMSTATUS are available by default or not. This value can
-** be overridden at runtime using the sqlite3_config() API.
-*/
-#if !defined(SQLITE_DEFAULT_MEMSTATUS)
-# define SQLITE_DEFAULT_MEMSTATUS 1
-#endif
-
-/*
-** Exactly one of the following macros must be defined in order to
-** specify which memory allocation subsystem to use.
-**
-** SQLITE_SYSTEM_MALLOC // Use normal system malloc()
-** SQLITE_WIN32_MALLOC // Use Win32 native heap API
-** SQLITE_ZERO_MALLOC // Use a stub allocator that always fails
-** SQLITE_MEMDEBUG // Debugging version of system malloc()
-**
-** On Windows, if the SQLITE_WIN32_MALLOC_VALIDATE macro is defined and the
-** assert() macro is enabled, each call into the Win32 native heap subsystem
-** will cause HeapValidate to be called. If heap validation should fail, an
-** assertion will be triggered.
-**
-** (Historical note: There used to be several other options, but we've
-** pared it down to just these three.)
-**
-** If none of the above are defined, then set SQLITE_SYSTEM_MALLOC as
-** the default.
-*/
-#if defined(SQLITE_SYSTEM_MALLOC) \
- + defined(SQLITE_WIN32_MALLOC) \
- + defined(SQLITE_ZERO_MALLOC) \
- + defined(SQLITE_MEMDEBUG)>1
-# error "Two or more of the following compile-time configuration options\
- are defined but at most one is allowed:\
- SQLITE_SYSTEM_MALLOC, SQLITE_WIN32_MALLOC, SQLITE_MEMDEBUG,\
- SQLITE_ZERO_MALLOC"
-#endif
-#if defined(SQLITE_SYSTEM_MALLOC) \
- + defined(SQLITE_WIN32_MALLOC) \
- + defined(SQLITE_ZERO_MALLOC) \
- + defined(SQLITE_MEMDEBUG)==0
-# define SQLITE_SYSTEM_MALLOC 1
-#endif
-
-/*
-** If SQLITE_MALLOC_SOFT_LIMIT is not zero, then try to keep the
-** sizes of memory allocations below this value where possible.
-*/
-#if !defined(SQLITE_MALLOC_SOFT_LIMIT)
-# define SQLITE_MALLOC_SOFT_LIMIT 1024
-#endif
-
-/*
-** We need to define _XOPEN_SOURCE as follows in order to enable
-** recursive mutexes on most Unix systems. But Mac OS X is different.
-** The _XOPEN_SOURCE define causes problems for Mac OS X we are told,
-** so it is omitted there. See ticket #2673.
-**
-** Later we learn that _XOPEN_SOURCE is poorly or incorrectly
-** implemented on some systems. So we avoid defining it at all
-** if it is already defined or if it is unneeded because we are
-** not doing a threadsafe build. Ticket #2681.
-**
-** See also ticket #2741.
-*/
-#if !defined(_XOPEN_SOURCE) && !defined(__DARWIN__) \
- && !defined(__APPLE__) && SQLITE_THREADSAFE
-# define _XOPEN_SOURCE 500 /* Needed to enable pthread recursive mutexes */
-#endif
-
-/*
-** The TCL headers are only needed when compiling the TCL bindings.
-*/
-#if defined(SQLITE_TCL) || defined(TCLSH)
-# include <tcl.h>
-#endif
-
-/*
-** NDEBUG and SQLITE_DEBUG are opposites. It should always be true that
-** defined(NDEBUG)==!defined(SQLITE_DEBUG). If this is not currently true,
-** make it true by defining or undefining NDEBUG.
-**
-** Setting NDEBUG makes the code smaller and run faster by disabling the
-** number assert() statements in the code. So we want the default action
-** to be for NDEBUG to be set and NDEBUG to be undefined only if SQLITE_DEBUG
-** is set. Thus NDEBUG becomes an opt-in rather than an opt-out
-** feature.
-*/
-#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
-# define NDEBUG 1
-#endif
-#if defined(NDEBUG) && defined(SQLITE_DEBUG)
-# undef NDEBUG
-#endif
-
-/*
-** The testcase() macro is used to aid in coverage testing. When
-** doing coverage testing, the condition inside the argument to
-** testcase() must be evaluated both true and false in order to
-** get full branch coverage. The testcase() macro is inserted
-** to help ensure adequate test coverage in places where simple
-** condition/decision coverage is inadequate. For example, testcase()
-** can be used to make sure boundary values are tested. For
-** bitmask tests, testcase() can be used to make sure each bit
-** is significant and used at least once. On switch statements
-** where multiple cases go to the same block of code, testcase()
-** can insure that all cases are evaluated.
-**
-*/
-#ifdef SQLITE_COVERAGE_TEST
-SQLITE_PRIVATE void sqlite3Coverage(int);
-# define testcase(X) if( X ){ sqlite3Coverage(__LINE__); }
-#else
-# define testcase(X)
-#endif
-
-/*
-** The TESTONLY macro is used to enclose variable declarations or
-** other bits of code that are needed to support the arguments
-** within testcase() and assert() macros.
-*/
-#if !defined(NDEBUG) || defined(SQLITE_COVERAGE_TEST)
-# define TESTONLY(X) X
-#else
-# define TESTONLY(X)
-#endif
-
-/*
-** Sometimes we need a small amount of code such as a variable initialization
-** to setup for a later assert() statement. We do not want this code to
-** appear when assert() is disabled. The following macro is therefore
-** used to contain that setup code. The "VVA" acronym stands for
-** "Verification, Validation, and Accreditation". In other words, the
-** code within VVA_ONLY() will only run during verification processes.
-*/
-#ifndef NDEBUG
-# define VVA_ONLY(X) X
-#else
-# define VVA_ONLY(X)
-#endif
-
-/*
-** The ALWAYS and NEVER macros surround boolean expressions which
-** are intended to always be true or false, respectively. Such
-** expressions could be omitted from the code completely. But they
-** are included in a few cases in order to enhance the resilience
-** of SQLite to unexpected behavior - to make the code "self-healing"
-** or "ductile" rather than being "brittle" and crashing at the first
-** hint of unplanned behavior.
-**
-** In other words, ALWAYS and NEVER are added for defensive code.
-**
-** When doing coverage testing ALWAYS and NEVER are hard-coded to
-** be true and false so that the unreachable code then specify will
-** not be counted as untested code.
-*/
-#if defined(SQLITE_COVERAGE_TEST)
-# define ALWAYS(X) (1)
-# define NEVER(X) (0)
-#elif !defined(NDEBUG)
-# define ALWAYS(X) ((X)?1:(assert(0),0))
-# define NEVER(X) ((X)?(assert(0),1):0)
-#else
-# define ALWAYS(X) (X)
-# define NEVER(X) (X)
-#endif
-
-/*
-** Return true (non-zero) if the input is a integer that is too large
-** to fit in 32-bits. This macro is used inside of various testcase()
-** macros to verify that we have tested SQLite for large-file support.
-*/
-#define IS_BIG_INT(X) (((X)&~(i64)0xffffffff)!=0)
-
-/*
-** The macro unlikely() is a hint that surrounds a boolean
-** expression that is usually false. Macro likely() surrounds
-** a boolean expression that is usually true. GCC is able to
-** use these hints to generate better code, sometimes.
-*/
-#if defined(__GNUC__) && 0
-# define likely(X) __builtin_expect((X),1)
-# define unlikely(X) __builtin_expect((X),0)
-#else
-# define likely(X) !!(X)
-# define unlikely(X) !!(X)
-#endif
-
-/************** Include sqlite3.h in the middle of sqliteInt.h ***************/
/************** Begin file sqlite3.h *****************************************/
/*
** 2001 September 15
@@ -678,9 +135,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.7.17"
-#define SQLITE_VERSION_NUMBER 3007017
-#define SQLITE_SOURCE_ID "2013-05-20 00:56:22 118a3b35693b134d56ebd780123b7fd6f1497668"
+#define SQLITE_VERSION "3.8.1"
+#define SQLITE_VERSION_NUMBER 3008001
+#define SQLITE_SOURCE_ID "2013-10-17 12:57:35 c78be6d786c19073b3a6730dfe3fb1be54f5657a"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -1049,11 +506,15 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8))
#define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23<<8))
#define SQLITE_IOERR_MMAP (SQLITE_IOERR | (24<<8))
+#define SQLITE_IOERR_GETTEMPPATH (SQLITE_IOERR | (25<<8))
+#define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8))
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
+#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8))
#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
+#define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8))
#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
@@ -1070,6 +531,7 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8))
#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
+#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
/*
** CAPI3REF: Flags For File Open Operations
@@ -2183,27 +1645,27 @@ struct sqlite3_mem_methods {
** function must be threadsafe. </dd>
**
** [[SQLITE_CONFIG_URI]] <dt>SQLITE_CONFIG_URI
-** <dd> This option takes a single argument of type int. If non-zero, then
+** <dd>^(This option takes a single argument of type int. If non-zero, then
** URI handling is globally enabled. If the parameter is zero, then URI handling
-** is globally disabled. If URI handling is globally enabled, all filenames
+** is globally disabled.)^ ^If URI handling is globally enabled, all filenames
** passed to [sqlite3_open()], [sqlite3_open_v2()], [sqlite3_open16()] or
** specified as part of [ATTACH] commands are interpreted as URIs, regardless
** of whether or not the [SQLITE_OPEN_URI] flag is set when the database
-** connection is opened. If it is globally disabled, filenames are
+** connection is opened. ^If it is globally disabled, filenames are
** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the
-** database connection is opened. By default, URI handling is globally
+** database connection is opened. ^(By default, URI handling is globally
** disabled. The default value may be changed by compiling with the
-** [SQLITE_USE_URI] symbol defined.
+** [SQLITE_USE_URI] symbol defined.)^
**
** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]] <dt>SQLITE_CONFIG_COVERING_INDEX_SCAN
-** <dd> This option takes a single integer argument which is interpreted as
+** <dd>^This option takes a single integer argument which is interpreted as
** a boolean in order to enable or disable the use of covering indices for
-** full table scans in the query optimizer. The default setting is determined
+** full table scans in the query optimizer. ^The default setting is determined
** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on"
** if that compile-time option is omitted.
** The ability to disable the use of covering indices for full table scans
** is because some incorrectly coded legacy applications might malfunction
-** malfunction when the optimization is enabled. Providing the ability to
+** when the optimization is enabled. Providing the ability to
** disable the optimization allows the older, buggy application code to work
** without change even with newer versions of SQLite.
**
@@ -2232,16 +1694,16 @@ struct sqlite3_mem_methods {
**
** [[SQLITE_CONFIG_MMAP_SIZE]]
** <dt>SQLITE_CONFIG_MMAP_SIZE
-** <dd>SQLITE_CONFIG_MMAP_SIZE takes two 64-bit integer (sqlite3_int64) values
+** <dd>^SQLITE_CONFIG_MMAP_SIZE takes two 64-bit integer (sqlite3_int64) values
** that are the default mmap size limit (the default setting for
** [PRAGMA mmap_size]) and the maximum allowed mmap size limit.
-** The default setting can be overridden by each database connection using
+** ^The default setting can be overridden by each database connection using
** either the [PRAGMA mmap_size] command, or by using the
-** [SQLITE_FCNTL_MMAP_SIZE] file control. The maximum allowed mmap size
+** [SQLITE_FCNTL_MMAP_SIZE] file control. ^(The maximum allowed mmap size
** cannot be changed at run-time. Nor may the maximum allowed mmap size
** exceed the compile-time maximum mmap size set by the
-** [SQLITE_MAX_MMAP_SIZE] compile-time option.
-** If either argument to this option is negative, then that argument is
+** [SQLITE_MAX_MMAP_SIZE] compile-time option.)^
+** ^If either argument to this option is negative, then that argument is
** changed to its compile-time default.
** </dl>
*/
@@ -3128,9 +2590,10 @@ SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*,
** interface is to keep a GUI updated during a large query.
**
** ^The parameter P is passed through as the only parameter to the
-** callback function X. ^The parameter N is the number of
+** callback function X. ^The parameter N is the approximate number of
** [virtual machine instructions] that are evaluated between successive
-** invocations of the callback X.
+** invocations of the callback X. ^If N is less than one then the progress
+** handler is disabled.
**
** ^Only a single progress handler may be defined at one time per
** [database connection]; setting a new progress handler cancels the
@@ -4750,41 +4213,49 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
/*
** CAPI3REF: Function Auxiliary Data
**
-** The following two functions may be used by scalar SQL functions to
+** These functions may be used by (non-aggregate) SQL functions to
** associate metadata with argument values. If the same value is passed to
** multiple invocations of the same SQL function during query execution, under
-** some circumstances the associated metadata may be preserved. This may
-** be used, for example, to add a regular-expression matching scalar
-** function. The compiled version of the regular expression is stored as
-** metadata associated with the SQL value passed as the regular expression
-** pattern. The compiled regular expression can be reused on multiple
-** invocations of the same function so that the original pattern string
-** does not need to be recompiled on each invocation.
+** some circumstances the associated metadata may be preserved. An example
+** of where this might be useful is in a regular-expression matching
+** function. The compiled version of the regular expression can be stored as
+** metadata associated with the pattern string.
+** Then as long as the pattern string remains the same,
+** the compiled regular expression can be reused on multiple
+** invocations of the same function.
**
** ^The sqlite3_get_auxdata() interface returns a pointer to the metadata
** associated by the sqlite3_set_auxdata() function with the Nth argument
-** value to the application-defined function. ^If no metadata has been ever
-** been set for the Nth argument of the function, or if the corresponding
-** function parameter has changed since the meta-data was set,
-** then sqlite3_get_auxdata() returns a NULL pointer.
-**
-** ^The sqlite3_set_auxdata() interface saves the metadata
-** pointed to by its 3rd parameter as the metadata for the N-th
-** argument of the application-defined function. Subsequent
-** calls to sqlite3_get_auxdata() might return this data, if it has
-** not been destroyed.
-** ^If it is not NULL, SQLite will invoke the destructor
-** function given by the 4th parameter to sqlite3_set_auxdata() on
-** the metadata when the corresponding function parameter changes
-** or when the SQL statement completes, whichever comes first.
-**
-** SQLite is free to call the destructor and drop metadata on any
-** parameter of any function at any time. ^The only guarantee is that
-** the destructor will be called before the metadata is dropped.
+** value to the application-defined function. ^If there is no metadata
+** associated with the function argument, this sqlite3_get_auxdata() interface
+** returns a NULL pointer.
+**
+** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th
+** argument of the application-defined function. ^Subsequent
+** calls to sqlite3_get_auxdata(C,N) return P from the most recent
+** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or
+** NULL if the metadata has been discarded.
+** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL,
+** SQLite will invoke the destructor function X with parameter P exactly
+** once, when the metadata is discarded.
+** SQLite is free to discard the metadata at any time, including: <ul>
+** <li> when the corresponding function parameter changes, or
+** <li> when [sqlite3_reset()] or [sqlite3_finalize()] is called for the
+** SQL statement, or
+** <li> when sqlite3_set_auxdata() is invoked again on the same parameter, or
+** <li> during the original sqlite3_set_auxdata() call when a memory
+** allocation error occurs. </ul>)^
+**
+** Note the last bullet in particular. The destructor X in
+** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the
+** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata()
+** should be called near the end of the function implementation and the
+** function implementation should not make any use of P after
+** sqlite3_set_auxdata() has been called.
**
** ^(In practice, metadata is preserved between function calls for
-** expressions that are constant at compile time. This includes literal
-** values and [parameters].)^
+** function parameters that are compile-time constants, including literal
+** values and [parameters] and expressions composed from the same.)^
**
** These routines must be called from the same thread in which
** the SQL function is running.
@@ -5089,6 +4560,11 @@ SQLITE_API int sqlite3_key(
sqlite3 *db, /* Database to be rekeyed */
const void *pKey, int nKey /* The key */
);
+SQLITE_API int sqlite3_key_v2(
+ sqlite3 *db, /* Database to be rekeyed */
+ const char *zDbName, /* Name of the database */
+ const void *pKey, int nKey /* The key */
+);
/*
** Change the key on an open database. If the current database is not
@@ -5102,6 +4578,11 @@ SQLITE_API int sqlite3_rekey(
sqlite3 *db, /* Database to be rekeyed */
const void *pKey, int nKey /* The new key */
);
+SQLITE_API int sqlite3_rekey_v2(
+ sqlite3 *db, /* Database to be rekeyed */
+ const char *zDbName, /* Name of the database */
+ const void *pKey, int nKey /* The new key */
+);
/*
** Specify the activation key for a SEE database. Unless
@@ -5687,11 +5168,24 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
** on the list of automatic extensions is a harmless no-op. ^No entry point
** will be called more than once for each database connection that is opened.
**
-** See also: [sqlite3_reset_auto_extension()].
+** See also: [sqlite3_reset_auto_extension()]
+** and [sqlite3_cancel_auto_extension()]
*/
SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void));
/*
+** CAPI3REF: Cancel Automatic Extension Loading
+**
+** ^The [sqlite3_cancel_auto_extension(X)] interface unregisters the
+** initialization routine X that was registered using a prior call to
+** [sqlite3_auto_extension(X)]. ^The [sqlite3_cancel_auto_extension(X)]
+** routine returns 1 if initialization routine X was successfully
+** unregistered and it returns 0 if X was not on the list of initialization
+** routines.
+*/
+SQLITE_API int sqlite3_cancel_auto_extension(void (*xEntryPoint)(void));
+
+/*
** CAPI3REF: Reset Automatic Extension Loading
**
** ^This interface disables all automatic extensions previously
@@ -6803,6 +6297,12 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The
** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0.
** </dd>
+**
+** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt>
+** <dd>This parameter returns zero for the current value if and only if
+** all foreign key constraints (deferred or immediate) have been
+** resolved.)^ ^The highwater mark is always 0.
+** </dd>
** </dl>
*/
#define SQLITE_DBSTATUS_LOOKASIDE_USED 0
@@ -6815,7 +6315,8 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
#define SQLITE_DBSTATUS_CACHE_HIT 7
#define SQLITE_DBSTATUS_CACHE_MISS 8
#define SQLITE_DBSTATUS_CACHE_WRITE 9
-#define SQLITE_DBSTATUS_MAX 9 /* Largest defined DBSTATUS */
+#define SQLITE_DBSTATUS_DEFERRED_FKS 10
+#define SQLITE_DBSTATUS_MAX 10 /* Largest defined DBSTATUS */
/*
@@ -6869,11 +6370,21 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
** A non-zero value in this counter may indicate an opportunity to
** improvement performance by adding permanent indices that do not
** need to be reinitialized each time the statement is run.</dd>
+**
+** [[SQLITE_STMTSTATUS_VM_STEP]] <dt>SQLITE_STMTSTATUS_VM_STEP</dt>
+** <dd>^This is the number of virtual machine operations executed
+** by the prepared statement if that number is less than or equal
+** to 2147483647. The number of virtual machine operations can be
+** used as a proxy for the total work done by the prepared statement.
+** If the number of virtual machine operations exceeds 2147483647
+** then the value returned by this statement status code is undefined.
+** </dd>
** </dl>
*/
#define SQLITE_STMTSTATUS_FULLSCAN_STEP 1
#define SQLITE_STMTSTATUS_SORT 2
#define SQLITE_STMTSTATUS_AUTOINDEX 3
+#define SQLITE_STMTSTATUS_VM_STEP 4
/*
** CAPI3REF: Custom Page Cache Object
@@ -7752,7 +7263,7 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *);
#if 0
} /* End of the 'extern "C"' block */
#endif
-#endif
+#endif /* _SQLITE3_H_ */
/*
** 2010 August 30
@@ -7816,7 +7327,526 @@ struct sqlite3_rtree_geometry {
/************** End of sqlite3.h *********************************************/
+/************** Begin file sqliteInt.h ***************************************/
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** Internal interface definitions for SQLite.
+**
+*/
+#ifndef _SQLITEINT_H_
+#define _SQLITEINT_H_
+
+/*
+** These #defines should enable >2GB file support on POSIX if the
+** underlying operating system supports it. If the OS lacks
+** large file support, or if the OS is windows, these should be no-ops.
+**
+** Ticket #2739: The _LARGEFILE_SOURCE macro must appear before any
+** system #includes. Hence, this block of code must be the very first
+** code in all source files.
+**
+** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch
+** on the compiler command line. This is necessary if you are compiling
+** on a recent machine (ex: Red Hat 7.2) but you want your code to work
+** on an older machine (ex: Red Hat 6.0). If you compile on Red Hat 7.2
+** without this option, LFS is enable. But LFS does not exist in the kernel
+** in Red Hat 6.0, so the code won't work. Hence, for maximum binary
+** portability you should omit LFS.
+**
+** Similar is true for Mac OS X. LFS is only supported on Mac OS X 9 and later.
+*/
+#ifndef SQLITE_DISABLE_LFS
+# define _LARGE_FILE 1
+# ifndef _FILE_OFFSET_BITS
+# define _FILE_OFFSET_BITS 64
+# endif
+# define _LARGEFILE_SOURCE 1
+#endif
+
+/*
+** Include the configuration header output by 'configure' if we're using the
+** autoconf-based build
+*/
+#ifdef _HAVE_SQLITE_CONFIG_H
+#include "config.h"
+#endif
+
+/************** Include sqliteLimit.h in the middle of sqliteInt.h ***********/
+/************** Begin file sqliteLimit.h *************************************/
+/*
+** 2007 May 7
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+**
+** This file defines various limits of what SQLite can process.
+*/
+
+/*
+** The maximum length of a TEXT or BLOB in bytes. This also
+** limits the size of a row in a table or index.
+**
+** The hard limit is the ability of a 32-bit signed integer
+** to count the size: 2^31-1 or 2147483647.
+*/
+#ifndef SQLITE_MAX_LENGTH
+# define SQLITE_MAX_LENGTH 1000000000
+#endif
+
+/*
+** This is the maximum number of
+**
+** * Columns in a table
+** * Columns in an index
+** * Columns in a view
+** * Terms in the SET clause of an UPDATE statement
+** * Terms in the result set of a SELECT statement
+** * Terms in the GROUP BY or ORDER BY clauses of a SELECT statement.
+** * Terms in the VALUES clause of an INSERT statement
+**
+** The hard upper limit here is 32676. Most database people will
+** tell you that in a well-normalized database, you usually should
+** not have more than a dozen or so columns in any table. And if
+** that is the case, there is no point in having more than a few
+** dozen values in any of the other situations described above.
+*/
+#ifndef SQLITE_MAX_COLUMN
+# define SQLITE_MAX_COLUMN 2000
+#endif
+
+/*
+** The maximum length of a single SQL statement in bytes.
+**
+** It used to be the case that setting this value to zero would
+** turn the limit off. That is no longer true. It is not possible
+** to turn this limit off.
+*/
+#ifndef SQLITE_MAX_SQL_LENGTH
+# define SQLITE_MAX_SQL_LENGTH 1000000000
+#endif
+
+/*
+** The maximum depth of an expression tree. This is limited to
+** some extent by SQLITE_MAX_SQL_LENGTH. But sometime you might
+** want to place more severe limits on the complexity of an
+** expression.
+**
+** A value of 0 used to mean that the limit was not enforced.
+** But that is no longer true. The limit is now strictly enforced
+** at all times.
+*/
+#ifndef SQLITE_MAX_EXPR_DEPTH
+# define SQLITE_MAX_EXPR_DEPTH 1000
+#endif
+
+/*
+** The maximum number of terms in a compound SELECT statement.
+** The code generator for compound SELECT statements does one
+** level of recursion for each term. A stack overflow can result
+** if the number of terms is too large. In practice, most SQL
+** never has more than 3 or 4 terms. Use a value of 0 to disable
+** any limit on the number of terms in a compount SELECT.
+*/
+#ifndef SQLITE_MAX_COMPOUND_SELECT
+# define SQLITE_MAX_COMPOUND_SELECT 500
+#endif
+
+/*
+** The maximum number of opcodes in a VDBE program.
+** Not currently enforced.
+*/
+#ifndef SQLITE_MAX_VDBE_OP
+# define SQLITE_MAX_VDBE_OP 25000
+#endif
+
+/*
+** The maximum number of arguments to an SQL function.
+*/
+#ifndef SQLITE_MAX_FUNCTION_ARG
+# define SQLITE_MAX_FUNCTION_ARG 127
+#endif
+
+/*
+** The maximum number of in-memory pages to use for the main database
+** table and for temporary tables. The SQLITE_DEFAULT_CACHE_SIZE
+*/
+#ifndef SQLITE_DEFAULT_CACHE_SIZE
+# define SQLITE_DEFAULT_CACHE_SIZE 2000
+#endif
+#ifndef SQLITE_DEFAULT_TEMP_CACHE_SIZE
+# define SQLITE_DEFAULT_TEMP_CACHE_SIZE 500
+#endif
+
+/*
+** The default number of frames to accumulate in the log file before
+** checkpointing the database in WAL mode.
+*/
+#ifndef SQLITE_DEFAULT_WAL_AUTOCHECKPOINT
+# define SQLITE_DEFAULT_WAL_AUTOCHECKPOINT 1000
+#endif
+
+/*
+** The maximum number of attached databases. This must be between 0
+** and 62. The upper bound on 62 is because a 64-bit integer bitmap
+** is used internally to track attached databases.
+*/
+#ifndef SQLITE_MAX_ATTACHED
+# define SQLITE_MAX_ATTACHED 10
+#endif
+
+
+/*
+** The maximum value of a ?nnn wildcard that the parser will accept.
+*/
+#ifndef SQLITE_MAX_VARIABLE_NUMBER
+# define SQLITE_MAX_VARIABLE_NUMBER 999
+#endif
+
+/* Maximum page size. The upper bound on this value is 65536. This a limit
+** imposed by the use of 16-bit offsets within each page.
+**
+** Earlier versions of SQLite allowed the user to change this value at
+** compile time. This is no longer permitted, on the grounds that it creates
+** a library that is technically incompatible with an SQLite library
+** compiled with a different limit. If a process operating on a database
+** with a page-size of 65536 bytes crashes, then an instance of SQLite
+** compiled with the default page-size limit will not be able to rollback
+** the aborted transaction. This could lead to database corruption.
+*/
+#ifdef SQLITE_MAX_PAGE_SIZE
+# undef SQLITE_MAX_PAGE_SIZE
+#endif
+#define SQLITE_MAX_PAGE_SIZE 65536
+
+
+/*
+** The default size of a database page.
+*/
+#ifndef SQLITE_DEFAULT_PAGE_SIZE
+# define SQLITE_DEFAULT_PAGE_SIZE 1024
+#endif
+#if SQLITE_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE
+# undef SQLITE_DEFAULT_PAGE_SIZE
+# define SQLITE_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE
+#endif
+
+/*
+** Ordinarily, if no value is explicitly provided, SQLite creates databases
+** with page size SQLITE_DEFAULT_PAGE_SIZE. However, based on certain
+** device characteristics (sector-size and atomic write() support),
+** SQLite may choose a larger value. This constant is the maximum value
+** SQLite will choose on its own.
+*/
+#ifndef SQLITE_MAX_DEFAULT_PAGE_SIZE
+# define SQLITE_MAX_DEFAULT_PAGE_SIZE 8192
+#endif
+#if SQLITE_MAX_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE
+# undef SQLITE_MAX_DEFAULT_PAGE_SIZE
+# define SQLITE_MAX_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE
+#endif
+
+
+/*
+** Maximum number of pages in one database file.
+**
+** This is really just the default value for the max_page_count pragma.
+** This value can be lowered (or raised) at run-time using that the
+** max_page_count macro.
+*/
+#ifndef SQLITE_MAX_PAGE_COUNT
+# define SQLITE_MAX_PAGE_COUNT 1073741823
+#endif
+
+/*
+** Maximum length (in bytes) of the pattern in a LIKE or GLOB
+** operator.
+*/
+#ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH
+# define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000
+#endif
+
+/*
+** Maximum depth of recursion for triggers.
+**
+** A value of 1 means that a trigger program will not be able to itself
+** fire any triggers. A value of 0 means that no trigger programs at all
+** may be executed.
+*/
+#ifndef SQLITE_MAX_TRIGGER_DEPTH
+# define SQLITE_MAX_TRIGGER_DEPTH 1000
+#endif
+
+/************** End of sqliteLimit.h *****************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
+
+/* Disable nuisance warnings on Borland compilers */
+#if defined(__BORLANDC__)
+#pragma warn -rch /* unreachable code */
+#pragma warn -ccc /* Condition is always true or false */
+#pragma warn -aus /* Assigned value is never used */
+#pragma warn -csu /* Comparing signed and unsigned */
+#pragma warn -spa /* Suspicious pointer arithmetic */
+#endif
+
+/* Needed for various definitions... */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#if defined(__OpenBSD__) && !defined(_BSD_SOURCE)
+# define _BSD_SOURCE
+#endif
+
+/*
+** Include standard header files as necessary
+*/
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+/*
+** The following macros are used to cast pointers to integers and
+** integers to pointers. The way you do this varies from one compiler
+** to the next, so we have developed the following set of #if statements
+** to generate appropriate macros for a wide range of compilers.
+**
+** The correct "ANSI" way to do this is to use the intptr_t type.
+** Unfortunately, that typedef is not available on all compilers, or
+** if it is available, it requires an #include of specific headers
+** that vary from one machine to the next.
+**
+** Ticket #3860: The llvm-gcc-4.2 compiler from Apple chokes on
+** the ((void*)&((char*)0)[X]) construct. But MSVC chokes on ((void*)(X)).
+** So we have to define the macros in different ways depending on the
+** compiler.
+*/
+#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */
+# define SQLITE_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X))
+# define SQLITE_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X))
+#elif !defined(__GNUC__) /* Works for compilers other than LLVM */
+# define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X])
+# define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0))
+#elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */
+# define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X))
+# define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X))
+#else /* Generates a warning - but it always works */
+# define SQLITE_INT_TO_PTR(X) ((void*)(X))
+# define SQLITE_PTR_TO_INT(X) ((int)(X))
+#endif
+
+/*
+** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2.
+** 0 means mutexes are permanently disable and the library is never
+** threadsafe. 1 means the library is serialized which is the highest
+** level of threadsafety. 2 means the library is multithreaded - multiple
+** threads can use SQLite as long as no two threads try to use the same
+** database connection at the same time.
+**
+** Older versions of SQLite used an optional THREADSAFE macro.
+** We support that for legacy.
+*/
+#if !defined(SQLITE_THREADSAFE)
+# if defined(THREADSAFE)
+# define SQLITE_THREADSAFE THREADSAFE
+# else
+# define SQLITE_THREADSAFE 1 /* IMP: R-07272-22309 */
+# endif
+#endif
+
+/*
+** Powersafe overwrite is on by default. But can be turned off using
+** the -DSQLITE_POWERSAFE_OVERWRITE=0 command-line option.
+*/
+#ifndef SQLITE_POWERSAFE_OVERWRITE
+# define SQLITE_POWERSAFE_OVERWRITE 1
+#endif
+
+/*
+** The SQLITE_DEFAULT_MEMSTATUS macro must be defined as either 0 or 1.
+** It determines whether or not the features related to
+** SQLITE_CONFIG_MEMSTATUS are available by default or not. This value can
+** be overridden at runtime using the sqlite3_config() API.
+*/
+#if !defined(SQLITE_DEFAULT_MEMSTATUS)
+# define SQLITE_DEFAULT_MEMSTATUS 1
+#endif
+
+/*
+** Exactly one of the following macros must be defined in order to
+** specify which memory allocation subsystem to use.
+**
+** SQLITE_SYSTEM_MALLOC // Use normal system malloc()
+** SQLITE_WIN32_MALLOC // Use Win32 native heap API
+** SQLITE_ZERO_MALLOC // Use a stub allocator that always fails
+** SQLITE_MEMDEBUG // Debugging version of system malloc()
+**
+** On Windows, if the SQLITE_WIN32_MALLOC_VALIDATE macro is defined and the
+** assert() macro is enabled, each call into the Win32 native heap subsystem
+** will cause HeapValidate to be called. If heap validation should fail, an
+** assertion will be triggered.
+**
+** If none of the above are defined, then set SQLITE_SYSTEM_MALLOC as
+** the default.
+*/
+#if defined(SQLITE_SYSTEM_MALLOC) \
+ + defined(SQLITE_WIN32_MALLOC) \
+ + defined(SQLITE_ZERO_MALLOC) \
+ + defined(SQLITE_MEMDEBUG)>1
+# error "Two or more of the following compile-time configuration options\
+ are defined but at most one is allowed:\
+ SQLITE_SYSTEM_MALLOC, SQLITE_WIN32_MALLOC, SQLITE_MEMDEBUG,\
+ SQLITE_ZERO_MALLOC"
+#endif
+#if defined(SQLITE_SYSTEM_MALLOC) \
+ + defined(SQLITE_WIN32_MALLOC) \
+ + defined(SQLITE_ZERO_MALLOC) \
+ + defined(SQLITE_MEMDEBUG)==0
+# define SQLITE_SYSTEM_MALLOC 1
+#endif
+
+/*
+** If SQLITE_MALLOC_SOFT_LIMIT is not zero, then try to keep the
+** sizes of memory allocations below this value where possible.
+*/
+#if !defined(SQLITE_MALLOC_SOFT_LIMIT)
+# define SQLITE_MALLOC_SOFT_LIMIT 1024
+#endif
+
+/*
+** We need to define _XOPEN_SOURCE as follows in order to enable
+** recursive mutexes on most Unix systems and fchmod() on OpenBSD.
+** But _XOPEN_SOURCE define causes problems for Mac OS X, so omit
+** it.
+*/
+#if !defined(_XOPEN_SOURCE) && !defined(__DARWIN__) && !defined(__APPLE__)
+# define _XOPEN_SOURCE 600
+#endif
+
+/*
+** NDEBUG and SQLITE_DEBUG are opposites. It should always be true that
+** defined(NDEBUG)==!defined(SQLITE_DEBUG). If this is not currently true,
+** make it true by defining or undefining NDEBUG.
+**
+** Setting NDEBUG makes the code smaller and faster by disabling the
+** assert() statements in the code. So we want the default action
+** to be for NDEBUG to be set and NDEBUG to be undefined only if SQLITE_DEBUG
+** is set. Thus NDEBUG becomes an opt-in rather than an opt-out
+** feature.
+*/
+#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
+# define NDEBUG 1
+#endif
+#if defined(NDEBUG) && defined(SQLITE_DEBUG)
+# undef NDEBUG
+#endif
+
+/*
+** The testcase() macro is used to aid in coverage testing. When
+** doing coverage testing, the condition inside the argument to
+** testcase() must be evaluated both true and false in order to
+** get full branch coverage. The testcase() macro is inserted
+** to help ensure adequate test coverage in places where simple
+** condition/decision coverage is inadequate. For example, testcase()
+** can be used to make sure boundary values are tested. For
+** bitmask tests, testcase() can be used to make sure each bit
+** is significant and used at least once. On switch statements
+** where multiple cases go to the same block of code, testcase()
+** can insure that all cases are evaluated.
+**
+*/
+#ifdef SQLITE_COVERAGE_TEST
+SQLITE_PRIVATE void sqlite3Coverage(int);
+# define testcase(X) if( X ){ sqlite3Coverage(__LINE__); }
+#else
+# define testcase(X)
+#endif
+
+/*
+** The TESTONLY macro is used to enclose variable declarations or
+** other bits of code that are needed to support the arguments
+** within testcase() and assert() macros.
+*/
+#if !defined(NDEBUG) || defined(SQLITE_COVERAGE_TEST)
+# define TESTONLY(X) X
+#else
+# define TESTONLY(X)
+#endif
+
+/*
+** Sometimes we need a small amount of code such as a variable initialization
+** to setup for a later assert() statement. We do not want this code to
+** appear when assert() is disabled. The following macro is therefore
+** used to contain that setup code. The "VVA" acronym stands for
+** "Verification, Validation, and Accreditation". In other words, the
+** code within VVA_ONLY() will only run during verification processes.
+*/
+#ifndef NDEBUG
+# define VVA_ONLY(X) X
+#else
+# define VVA_ONLY(X)
+#endif
+
+/*
+** The ALWAYS and NEVER macros surround boolean expressions which
+** are intended to always be true or false, respectively. Such
+** expressions could be omitted from the code completely. But they
+** are included in a few cases in order to enhance the resilience
+** of SQLite to unexpected behavior - to make the code "self-healing"
+** or "ductile" rather than being "brittle" and crashing at the first
+** hint of unplanned behavior.
+**
+** In other words, ALWAYS and NEVER are added for defensive code.
+**
+** When doing coverage testing ALWAYS and NEVER are hard-coded to
+** be true and false so that the unreachable code they specify will
+** not be counted as untested code.
+*/
+#if defined(SQLITE_COVERAGE_TEST)
+# define ALWAYS(X) (1)
+# define NEVER(X) (0)
+#elif !defined(NDEBUG)
+# define ALWAYS(X) ((X)?1:(assert(0),0))
+# define NEVER(X) ((X)?(assert(0),1):0)
+#else
+# define ALWAYS(X) (X)
+# define NEVER(X) (X)
+#endif
+
+/*
+** Return true (non-zero) if the input is a integer that is too large
+** to fit in 32-bits. This macro is used inside of various testcase()
+** macros to verify that we have tested SQLite for large-file support.
+*/
+#define IS_BIG_INT(X) (((X)&~(i64)0xffffffff)!=0)
+
+/*
+** The macro unlikely() is a hint that surrounds a boolean
+** expression that is usually false. Macro likely() surrounds
+** a boolean expression that is usually true. These hints could,
+** in theory, be used by the compiler to generate better code, but
+** currently they are just comments for human readers.
+*/
+#define likely(X) (X)
+#define unlikely(X) (X)
+
/************** Include hash.h in the middle of sqliteInt.h ******************/
/************** Begin file hash.h ********************************************/
/*
@@ -8154,6 +8184,12 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
#endif
/*
+** Macros to compute minimum and maximum of two numbers.
+*/
+#define MIN(A,B) ((A)<(B)?(A):(B))
+#define MAX(A,B) ((A)>(B)?(A):(B))
+
+/*
** Check to see if this machine uses EBCDIC. (Yes, believe it or
** not, there are still machines out there that use EBCDIC.)
*/
@@ -8237,6 +8273,31 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */
#endif
/*
+** Estimated quantities used for query planning are stored as 16-bit
+** logarithms. For quantity X, the value stored is 10*log2(X). This
+** gives a possible range of values of approximately 1.0e986 to 1e-986.
+** But the allowed values are "grainy". Not every value is representable.
+** For example, quantities 16 and 17 are both represented by a LogEst
+** of 40. However, since LogEst quantatites are suppose to be estimates,
+** not exact values, this imprecision is not a problem.
+**
+** "LogEst" is short for "Logarithimic Estimate".
+**
+** Examples:
+** 1 -> 0 20 -> 43 10000 -> 132
+** 2 -> 10 25 -> 46 25000 -> 146
+** 3 -> 16 100 -> 66 1000000 -> 199
+** 4 -> 20 1000 -> 99 1048576 -> 200
+** 10 -> 33 1024 -> 100 4294967296 -> 320
+**
+** The LogEst can be negative to indicate fractional values.
+** Examples:
+**
+** 0.5 -> -10 0.1 -> -33 0.0625 -> -40
+*/
+typedef INT16_TYPE LogEst;
+
+/*
** Macros to determine whether the machine is big or little endian,
** evaluated at runtime.
*/
@@ -8335,6 +8396,20 @@ SQLITE_PRIVATE const int sqlite3one;
#endif
/*
+** Only one of SQLITE_ENABLE_STAT3 or SQLITE_ENABLE_STAT4 can be defined.
+** Priority is given to SQLITE_ENABLE_STAT4. If either are defined, also
+** define SQLITE_ENABLE_STAT3_OR_STAT4
+*/
+#ifdef SQLITE_ENABLE_STAT4
+# undef SQLITE_ENABLE_STAT3
+# define SQLITE_ENABLE_STAT3_OR_STAT4 1
+#elif SQLITE_ENABLE_STAT3
+# define SQLITE_ENABLE_STAT3_OR_STAT4 1
+#elif SQLITE_ENABLE_STAT3_OR_STAT4
+# undef SQLITE_ENABLE_STAT3_OR_STAT4
+#endif
+
+/*
** An instance of the following structure is used to store the busy-handler
** callback for a given sqlite handle.
**
@@ -8478,9 +8553,7 @@ typedef struct UnpackedRecord UnpackedRecord;
typedef struct VTable VTable;
typedef struct VtabCtx VtabCtx;
typedef struct Walker Walker;
-typedef struct WherePlan WherePlan;
typedef struct WhereInfo WhereInfo;
-typedef struct WhereLevel WhereLevel;
/*
** Defer sourcing vdbe.h and btree.h until after the "u8" and
@@ -8555,7 +8628,7 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
SQLITE_PRIVATE int sqlite3BtreeClose(Btree*);
SQLITE_PRIVATE int sqlite3BtreeSetCacheSize(Btree*,int);
SQLITE_PRIVATE int sqlite3BtreeSetMmapLimit(Btree*,sqlite3_int64);
-SQLITE_PRIVATE int sqlite3BtreeSetSafetyLevel(Btree*,int,int,int);
+SQLITE_PRIVATE int sqlite3BtreeSetPagerFlags(Btree*,unsigned);
SQLITE_PRIVATE int sqlite3BtreeSyncDisabled(Btree*);
SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix);
SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*);
@@ -8779,7 +8852,6 @@ typedef struct Vdbe Vdbe;
** The names of the following types declared in vdbeInt.h are required
** for the VdbeOp definition.
*/
-typedef struct VdbeFunc VdbeFunc;
typedef struct Mem Mem;
typedef struct SubProgram SubProgram;
@@ -8803,7 +8875,6 @@ struct VdbeOp {
i64 *pI64; /* Used when p4type is P4_INT64 */
double *pReal; /* Used when p4type is P4_REAL */
FuncDef *pFunc; /* Used when p4type is P4_FUNCDEF */
- VdbeFunc *pVdbeFunc; /* Used when p4type is P4_VDBEFUNC */
CollSeq *pColl; /* Used when p4type is P4_COLLSEQ */
Mem *pMem; /* Used when p4type is P4_MEM */
VTable *pVtab; /* Used when p4type is P4_VTAB */
@@ -8857,7 +8928,6 @@ typedef struct VdbeOpList VdbeOpList;
#define P4_COLLSEQ (-4) /* P4 is a pointer to a CollSeq structure */
#define P4_FUNCDEF (-5) /* P4 is a pointer to a FuncDef structure */
#define P4_KEYINFO (-6) /* P4 is a pointer to a KeyInfo structure */
-#define P4_VDBEFUNC (-7) /* P4 is a pointer to a VdbeFunc structure */
#define P4_MEM (-8) /* P4 is a pointer to a Mem* structure */
#define P4_TRANSIENT 0 /* P4 is a pointer to a transient string */
#define P4_VTAB (-10) /* P4 is a pointer to an sqlite3_vtab structure */
@@ -8914,151 +8984,151 @@ typedef struct VdbeOpList VdbeOpList;
/************** Begin file opcodes.h *****************************************/
/* Automatically generated. Do not edit */
/* See the mkopcodeh.awk script for details */
-#define OP_Goto 1
-#define OP_Gosub 2
-#define OP_Return 3
-#define OP_Yield 4
-#define OP_HaltIfNull 5
-#define OP_Halt 6
-#define OP_Integer 7
-#define OP_Int64 8
-#define OP_Real 130 /* same as TK_FLOAT */
-#define OP_String8 94 /* same as TK_STRING */
-#define OP_String 9
-#define OP_Null 10
-#define OP_Blob 11
-#define OP_Variable 12
-#define OP_Move 13
-#define OP_Copy 14
-#define OP_SCopy 15
-#define OP_ResultRow 16
-#define OP_Concat 91 /* same as TK_CONCAT */
+#define OP_Function 1
+#define OP_Savepoint 2
+#define OP_AutoCommit 3
+#define OP_Transaction 4
+#define OP_SorterNext 5
+#define OP_Prev 6
+#define OP_Next 7
+#define OP_AggStep 8
+#define OP_Checkpoint 9
+#define OP_JournalMode 10
+#define OP_Vacuum 11
+#define OP_VFilter 12
+#define OP_VUpdate 13
+#define OP_Goto 14
+#define OP_Gosub 15
+#define OP_Return 16
+#define OP_Yield 17
+#define OP_HaltIfNull 18
+#define OP_Not 19 /* same as TK_NOT */
+#define OP_Halt 20
+#define OP_Integer 21
+#define OP_Int64 22
+#define OP_String 23
+#define OP_Null 24
+#define OP_Blob 25
+#define OP_Variable 26
+#define OP_Move 27
+#define OP_Copy 28
+#define OP_SCopy 29
+#define OP_ResultRow 30
+#define OP_CollSeq 31
+#define OP_AddImm 32
+#define OP_MustBeInt 33
+#define OP_RealAffinity 34
+#define OP_Permutation 35
+#define OP_Compare 36
+#define OP_Jump 37
+#define OP_Once 38
+#define OP_If 39
+#define OP_IfNot 40
+#define OP_Column 41
+#define OP_Affinity 42
+#define OP_MakeRecord 43
+#define OP_Count 44
+#define OP_ReadCookie 45
+#define OP_SetCookie 46
+#define OP_VerifyCookie 47
+#define OP_OpenRead 48
+#define OP_OpenWrite 49
+#define OP_OpenAutoindex 50
+#define OP_OpenEphemeral 51
+#define OP_SorterOpen 52
+#define OP_OpenPseudo 53
+#define OP_Close 54
+#define OP_SeekLt 55
+#define OP_SeekLe 56
+#define OP_SeekGe 57
+#define OP_SeekGt 58
+#define OP_Seek 59
+#define OP_NotFound 60
+#define OP_Found 61
+#define OP_IsUnique 62
+#define OP_NotExists 63
+#define OP_Sequence 64
+#define OP_NewRowid 65
+#define OP_Insert 66
+#define OP_InsertInt 67
+#define OP_Or 68 /* same as TK_OR */
+#define OP_And 69 /* same as TK_AND */
+#define OP_Delete 70
+#define OP_ResetCount 71
+#define OP_SorterCompare 72
+#define OP_IsNull 73 /* same as TK_ISNULL */
+#define OP_NotNull 74 /* same as TK_NOTNULL */
+#define OP_Ne 75 /* same as TK_NE */
+#define OP_Eq 76 /* same as TK_EQ */
+#define OP_Gt 77 /* same as TK_GT */
+#define OP_Le 78 /* same as TK_LE */
+#define OP_Lt 79 /* same as TK_LT */
+#define OP_Ge 80 /* same as TK_GE */
+#define OP_SorterData 81
+#define OP_BitAnd 82 /* same as TK_BITAND */
+#define OP_BitOr 83 /* same as TK_BITOR */
+#define OP_ShiftLeft 84 /* same as TK_LSHIFT */
+#define OP_ShiftRight 85 /* same as TK_RSHIFT */
#define OP_Add 86 /* same as TK_PLUS */
#define OP_Subtract 87 /* same as TK_MINUS */
#define OP_Multiply 88 /* same as TK_STAR */
#define OP_Divide 89 /* same as TK_SLASH */
#define OP_Remainder 90 /* same as TK_REM */
-#define OP_CollSeq 17
-#define OP_Function 18
-#define OP_BitAnd 82 /* same as TK_BITAND */
-#define OP_BitOr 83 /* same as TK_BITOR */
-#define OP_ShiftLeft 84 /* same as TK_LSHIFT */
-#define OP_ShiftRight 85 /* same as TK_RSHIFT */
-#define OP_AddImm 20
-#define OP_MustBeInt 21
-#define OP_RealAffinity 22
+#define OP_Concat 91 /* same as TK_CONCAT */
+#define OP_RowKey 92
+#define OP_BitNot 93 /* same as TK_BITNOT */
+#define OP_String8 94 /* same as TK_STRING */
+#define OP_RowData 95
+#define OP_Rowid 96
+#define OP_NullRow 97
+#define OP_Last 98
+#define OP_SorterSort 99
+#define OP_Sort 100
+#define OP_Rewind 101
+#define OP_SorterInsert 102
+#define OP_IdxInsert 103
+#define OP_IdxDelete 104
+#define OP_IdxRowid 105
+#define OP_IdxLT 106
+#define OP_IdxGE 107
+#define OP_Destroy 108
+#define OP_Clear 109
+#define OP_CreateIndex 110
+#define OP_CreateTable 111
+#define OP_ParseSchema 112
+#define OP_LoadAnalysis 113
+#define OP_DropTable 114
+#define OP_DropIndex 115
+#define OP_DropTrigger 116
+#define OP_IntegrityCk 117
+#define OP_RowSetAdd 118
+#define OP_RowSetRead 119
+#define OP_RowSetTest 120
+#define OP_Program 121
+#define OP_Param 122
+#define OP_FkCounter 123
+#define OP_FkIfZero 124
+#define OP_MemMax 125
+#define OP_IfPos 126
+#define OP_IfNeg 127
+#define OP_IfZero 128
+#define OP_AggFinal 129
+#define OP_Real 130 /* same as TK_FLOAT */
+#define OP_IncrVacuum 131
+#define OP_Expire 132
+#define OP_TableLock 133
+#define OP_VBegin 134
+#define OP_VCreate 135
+#define OP_VDestroy 136
+#define OP_VOpen 137
+#define OP_VColumn 138
+#define OP_VNext 139
+#define OP_VRename 140
#define OP_ToText 141 /* same as TK_TO_TEXT */
#define OP_ToBlob 142 /* same as TK_TO_BLOB */
#define OP_ToNumeric 143 /* same as TK_TO_NUMERIC*/
#define OP_ToInt 144 /* same as TK_TO_INT */
#define OP_ToReal 145 /* same as TK_TO_REAL */
-#define OP_Eq 76 /* same as TK_EQ */
-#define OP_Ne 75 /* same as TK_NE */
-#define OP_Lt 79 /* same as TK_LT */
-#define OP_Le 78 /* same as TK_LE */
-#define OP_Gt 77 /* same as TK_GT */
-#define OP_Ge 80 /* same as TK_GE */
-#define OP_Permutation 23
-#define OP_Compare 24
-#define OP_Jump 25
-#define OP_And 69 /* same as TK_AND */
-#define OP_Or 68 /* same as TK_OR */
-#define OP_Not 19 /* same as TK_NOT */
-#define OP_BitNot 93 /* same as TK_BITNOT */
-#define OP_Once 26
-#define OP_If 27
-#define OP_IfNot 28
-#define OP_IsNull 73 /* same as TK_ISNULL */
-#define OP_NotNull 74 /* same as TK_NOTNULL */
-#define OP_Column 29
-#define OP_Affinity 30
-#define OP_MakeRecord 31
-#define OP_Count 32
-#define OP_Savepoint 33
-#define OP_AutoCommit 34
-#define OP_Transaction 35
-#define OP_ReadCookie 36
-#define OP_SetCookie 37
-#define OP_VerifyCookie 38
-#define OP_OpenRead 39
-#define OP_OpenWrite 40
-#define OP_OpenAutoindex 41
-#define OP_OpenEphemeral 42
-#define OP_SorterOpen 43
-#define OP_OpenPseudo 44
-#define OP_Close 45
-#define OP_SeekLt 46
-#define OP_SeekLe 47
-#define OP_SeekGe 48
-#define OP_SeekGt 49
-#define OP_Seek 50
-#define OP_NotFound 51
-#define OP_Found 52
-#define OP_IsUnique 53
-#define OP_NotExists 54
-#define OP_Sequence 55
-#define OP_NewRowid 56
-#define OP_Insert 57
-#define OP_InsertInt 58
-#define OP_Delete 59
-#define OP_ResetCount 60
-#define OP_SorterCompare 61
-#define OP_SorterData 62
-#define OP_RowKey 63
-#define OP_RowData 64
-#define OP_Rowid 65
-#define OP_NullRow 66
-#define OP_Last 67
-#define OP_SorterSort 70
-#define OP_Sort 71
-#define OP_Rewind 72
-#define OP_SorterNext 81
-#define OP_Prev 92
-#define OP_Next 95
-#define OP_SorterInsert 96
-#define OP_IdxInsert 97
-#define OP_IdxDelete 98
-#define OP_IdxRowid 99
-#define OP_IdxLT 100
-#define OP_IdxGE 101
-#define OP_Destroy 102
-#define OP_Clear 103
-#define OP_CreateIndex 104
-#define OP_CreateTable 105
-#define OP_ParseSchema 106
-#define OP_LoadAnalysis 107
-#define OP_DropTable 108
-#define OP_DropIndex 109
-#define OP_DropTrigger 110
-#define OP_IntegrityCk 111
-#define OP_RowSetAdd 112
-#define OP_RowSetRead 113
-#define OP_RowSetTest 114
-#define OP_Program 115
-#define OP_Param 116
-#define OP_FkCounter 117
-#define OP_FkIfZero 118
-#define OP_MemMax 119
-#define OP_IfPos 120
-#define OP_IfNeg 121
-#define OP_IfZero 122
-#define OP_AggStep 123
-#define OP_AggFinal 124
-#define OP_Checkpoint 125
-#define OP_JournalMode 126
-#define OP_Vacuum 127
-#define OP_IncrVacuum 128
-#define OP_Expire 129
-#define OP_TableLock 131
-#define OP_VBegin 132
-#define OP_VCreate 133
-#define OP_VDestroy 134
-#define OP_VOpen 135
-#define OP_VFilter 136
-#define OP_VColumn 137
-#define OP_VNext 138
-#define OP_VRename 139
-#define OP_VUpdate 140
#define OP_Pagecount 146
#define OP_MaxPgcnt 147
#define OP_Trace 148
@@ -9078,24 +9148,24 @@ typedef struct VdbeOpList VdbeOpList;
#define OPFLG_OUT2 0x0020 /* out2: P2 is an output */
#define OPFLG_OUT3 0x0040 /* out3: P3 is an output */
#define OPFLG_INITIALIZER {\
-/* 0 */ 0x00, 0x01, 0x01, 0x04, 0x04, 0x10, 0x00, 0x02,\
-/* 8 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x24,\
-/* 16 */ 0x00, 0x00, 0x00, 0x24, 0x04, 0x05, 0x04, 0x00,\
-/* 24 */ 0x00, 0x01, 0x01, 0x05, 0x05, 0x00, 0x00, 0x00,\
-/* 32 */ 0x02, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00,\
-/* 40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11,\
-/* 48 */ 0x11, 0x11, 0x08, 0x11, 0x11, 0x11, 0x11, 0x02,\
-/* 56 */ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 64 */ 0x00, 0x02, 0x00, 0x01, 0x4c, 0x4c, 0x01, 0x01,\
-/* 72 */ 0x01, 0x05, 0x05, 0x15, 0x15, 0x15, 0x15, 0x15,\
-/* 80 */ 0x15, 0x01, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,\
-/* 88 */ 0x4c, 0x4c, 0x4c, 0x4c, 0x01, 0x24, 0x02, 0x01,\
-/* 96 */ 0x08, 0x08, 0x00, 0x02, 0x01, 0x01, 0x02, 0x00,\
-/* 104 */ 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 112 */ 0x0c, 0x45, 0x15, 0x01, 0x02, 0x00, 0x01, 0x08,\
-/* 120 */ 0x05, 0x05, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00,\
-/* 128 */ 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 136 */ 0x01, 0x00, 0x01, 0x00, 0x00, 0x04, 0x04, 0x04,\
+/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,\
+/* 8 */ 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x01,\
+/* 16 */ 0x04, 0x04, 0x10, 0x24, 0x00, 0x02, 0x02, 0x02,\
+/* 24 */ 0x02, 0x02, 0x02, 0x00, 0x00, 0x24, 0x00, 0x00,\
+/* 32 */ 0x04, 0x05, 0x04, 0x00, 0x00, 0x01, 0x01, 0x05,\
+/* 40 */ 0x05, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,\
+/* 48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,\
+/* 56 */ 0x11, 0x11, 0x11, 0x08, 0x11, 0x11, 0x11, 0x11,\
+/* 64 */ 0x02, 0x02, 0x00, 0x00, 0x4c, 0x4c, 0x00, 0x00,\
+/* 72 */ 0x00, 0x05, 0x05, 0x15, 0x15, 0x15, 0x15, 0x15,\
+/* 80 */ 0x15, 0x00, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,\
+/* 88 */ 0x4c, 0x4c, 0x4c, 0x4c, 0x00, 0x24, 0x02, 0x00,\
+/* 96 */ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x08, 0x08,\
+/* 104 */ 0x00, 0x02, 0x01, 0x01, 0x02, 0x00, 0x02, 0x02,\
+/* 112 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x45,\
+/* 120 */ 0x15, 0x01, 0x02, 0x00, 0x01, 0x08, 0x05, 0x05,\
+/* 128 */ 0x05, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00,\
+/* 136 */ 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x04, 0x04,\
/* 144 */ 0x04, 0x04, 0x02, 0x02, 0x00, 0x00, 0x00,}
/************** End of opcodes.h *********************************************/
@@ -9145,7 +9215,7 @@ SQLITE_PRIVATE sqlite3 *sqlite3VdbeDb(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, int);
SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe*,Vdbe*);
SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*);
-SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetValue(Vdbe*, int, u8);
+SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe*, int, u8);
SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe*, int);
#ifndef SQLITE_OMIT_TRACE
SQLITE_PRIVATE char *sqlite3VdbeExpandSql(Vdbe*, const char*);
@@ -9259,8 +9329,20 @@ typedef struct PgHdr DbPage;
/*
** Flags that make up the mask passed to sqlite3PagerAcquire().
*/
-#define PAGER_ACQUIRE_NOCONTENT 0x01 /* Do not load data from disk */
-#define PAGER_ACQUIRE_READONLY 0x02 /* Read-only page is acceptable */
+#define PAGER_GET_NOCONTENT 0x01 /* Do not load data from disk */
+#define PAGER_GET_READONLY 0x02 /* Read-only page is acceptable */
+
+/*
+** Flags for sqlite3PagerSetFlags()
+*/
+#define PAGER_SYNCHRONOUS_OFF 0x01 /* PRAGMA synchronous=OFF */
+#define PAGER_SYNCHRONOUS_NORMAL 0x02 /* PRAGMA synchronous=NORMAL */
+#define PAGER_SYNCHRONOUS_FULL 0x03 /* PRAGMA synchronous=FULL */
+#define PAGER_SYNCHRONOUS_MASK 0x03 /* Mask for three values above */
+#define PAGER_FULLFSYNC 0x04 /* PRAGMA fullfsync=ON */
+#define PAGER_CKPT_FULLFSYNC 0x08 /* PRAGMA checkpoint_fullfsync=ON */
+#define PAGER_CACHESPILL 0x10 /* PRAGMA cache_spill=ON */
+#define PAGER_FLAGS_MASK 0x1c /* All above except SYNCHRONOUS */
/*
** The remainder of this file contains the declarations of the functions
@@ -9288,7 +9370,7 @@ SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager*, int);
SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int);
SQLITE_PRIVATE void sqlite3PagerSetMmapLimit(Pager *, sqlite3_int64);
SQLITE_PRIVATE void sqlite3PagerShrink(Pager*);
-SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager*,int,int,int);
+SQLITE_PRIVATE void sqlite3PagerSetFlags(Pager*,unsigned);
SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int);
SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *, int);
SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager*);
@@ -9917,7 +9999,6 @@ SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *);
struct Db {
char *zName; /* Name of this database */
Btree *pBt; /* The B*Tree structure for this database file */
- u8 inTrans; /* 0: not writable. 1: Transaction. 2: Checkpoint */
u8 safety_level; /* How aggressive at syncing data to disk */
Schema *pSchema; /* Pointer to database schema (possibly shared) */
};
@@ -10063,9 +10144,10 @@ struct sqlite3 {
u8 busy; /* TRUE if currently initializing */
u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */
} init;
- int activeVdbeCnt; /* Number of VDBEs currently executing */
- int writeVdbeCnt; /* Number of active VDBEs that are writing */
- int vdbeExecCnt; /* Number of nested calls to VdbeExec() */
+ int nVdbeActive; /* Number of VDBEs currently running */
+ int nVdbeRead; /* Number of active VDBEs that read or write */
+ int nVdbeWrite; /* Number of active VDBEs that read and write */
+ int nVdbeExec; /* Number of nested calls to VdbeExec() */
int nExtension; /* Number of loaded extensions */
void **aExtension; /* Array of shared library handles */
void (*xTrace)(void*,const char*); /* Trace function */
@@ -10086,8 +10168,6 @@ struct sqlite3 {
void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*);
void *pCollNeededArg;
sqlite3_value *pErr; /* Most recent error message */
- char *zErrMsg; /* Most recent error message (UTF-8 encoded) */
- char *zErrMsg16; /* Most recent error message (UTF-16 encoded) */
union {
volatile int isInterrupted; /* True if sqlite3_interrupt has been called */
double notUsed1; /* Spacer */
@@ -10101,7 +10181,7 @@ struct sqlite3 {
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
int (*xProgress)(void *); /* The progress callback */
void *pProgressArg; /* Argument to the progress callback */
- int nProgressOps; /* Number of opcodes for progress callback */
+ unsigned nProgressOps; /* Number of opcodes for progress callback */
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
int nVTrans; /* Allocated size of aVTrans */
@@ -10119,6 +10199,7 @@ struct sqlite3 {
int nSavepoint; /* Number of non-transaction savepoints */
int nStatement; /* Number of nested statement-transactions */
i64 nDeferredCons; /* Net deferred constraints this transaction. */
+ i64 nDeferredImmCons; /* Net deferred immediate constraints */
int *pnBytesFreed; /* If not NULL, increment this in DbFree() */
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
@@ -10150,30 +10231,34 @@ struct sqlite3 {
*/
#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */
#define SQLITE_InternChanges 0x00000002 /* Uncommitted Hash table changes */
-#define SQLITE_FullColNames 0x00000004 /* Show full column names on SELECT */
-#define SQLITE_ShortColNames 0x00000008 /* Show short columns names */
-#define SQLITE_CountRows 0x00000010 /* Count rows changed by INSERT, */
+#define SQLITE_FullFSync 0x00000004 /* Use full fsync on the backend */
+#define SQLITE_CkptFullFSync 0x00000008 /* Use full fsync for checkpoint */
+#define SQLITE_CacheSpill 0x00000010 /* OK to spill pager cache */
+#define SQLITE_FullColNames 0x00000020 /* Show full column names on SELECT */
+#define SQLITE_ShortColNames 0x00000040 /* Show short columns names */
+#define SQLITE_CountRows 0x00000080 /* Count rows changed by INSERT, */
/* DELETE, or UPDATE and return */
/* the count using a callback. */
-#define SQLITE_NullCallback 0x00000020 /* Invoke the callback once if the */
+#define SQLITE_NullCallback 0x00000100 /* Invoke the callback once if the */
/* result set is empty */
-#define SQLITE_SqlTrace 0x00000040 /* Debug print SQL as it executes */
-#define SQLITE_VdbeListing 0x00000080 /* Debug listings of VDBE programs */
-#define SQLITE_WriteSchema 0x00000100 /* OK to update SQLITE_MASTER */
-#define SQLITE_VdbeAddopTrace 0x00000200 /* Trace sqlite3VdbeAddOp() calls */
-#define SQLITE_IgnoreChecks 0x00000400 /* Do not enforce check constraints */
-#define SQLITE_ReadUncommitted 0x0000800 /* For shared-cache mode */
-#define SQLITE_LegacyFileFmt 0x00001000 /* Create new databases in format 1 */
-#define SQLITE_FullFSync 0x00002000 /* Use full fsync on the backend */
-#define SQLITE_CkptFullFSync 0x00004000 /* Use full fsync for checkpoint */
-#define SQLITE_RecoveryMode 0x00008000 /* Ignore schema errors */
-#define SQLITE_ReverseOrder 0x00010000 /* Reverse unordered SELECTs */
-#define SQLITE_RecTriggers 0x00020000 /* Enable recursive triggers */
-#define SQLITE_ForeignKeys 0x00040000 /* Enforce foreign key constraints */
-#define SQLITE_AutoIndex 0x00080000 /* Enable automatic indexes */
-#define SQLITE_PreferBuiltin 0x00100000 /* Preference to built-in funcs */
-#define SQLITE_LoadExtension 0x00200000 /* Enable load_extension */
-#define SQLITE_EnableTrigger 0x00400000 /* True to enable triggers */
+#define SQLITE_SqlTrace 0x00000200 /* Debug print SQL as it executes */
+#define SQLITE_VdbeListing 0x00000400 /* Debug listings of VDBE programs */
+#define SQLITE_WriteSchema 0x00000800 /* OK to update SQLITE_MASTER */
+#define SQLITE_VdbeAddopTrace 0x00001000 /* Trace sqlite3VdbeAddOp() calls */
+#define SQLITE_IgnoreChecks 0x00002000 /* Do not enforce check constraints */
+#define SQLITE_ReadUncommitted 0x0004000 /* For shared-cache mode */
+#define SQLITE_LegacyFileFmt 0x00008000 /* Create new databases in format 1 */
+#define SQLITE_RecoveryMode 0x00010000 /* Ignore schema errors */
+#define SQLITE_ReverseOrder 0x00020000 /* Reverse unordered SELECTs */
+#define SQLITE_RecTriggers 0x00040000 /* Enable recursive triggers */
+#define SQLITE_ForeignKeys 0x00080000 /* Enforce foreign key constraints */
+#define SQLITE_AutoIndex 0x00100000 /* Enable automatic indexes */
+#define SQLITE_PreferBuiltin 0x00200000 /* Preference to built-in funcs */
+#define SQLITE_LoadExtension 0x00400000 /* Enable load_extension */
+#define SQLITE_EnableTrigger 0x00800000 /* True to enable triggers */
+#define SQLITE_DeferFKs 0x01000000 /* Defer all FK constraints */
+#define SQLITE_QueryOnly 0x02000000 /* Disable database changes */
+
/*
** Bits of the sqlite3.dbOptFlags field that are used by the
@@ -10190,6 +10275,9 @@ struct sqlite3 {
#define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */
#define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */
#define SQLITE_Transitive 0x0200 /* Transitive constraints */
+#define SQLITE_OmitNoopJoin 0x0400 /* Omit unused tables in joins */
+#define SQLITE_Stat3 0x0800 /* Use the SQLITE_STAT3 table */
+#define SQLITE_AdjustOutEst 0x1000 /* Adjust output estimates using WHERE */
#define SQLITE_AllOpts 0xffff /* All optimizations */
/*
@@ -10223,8 +10311,7 @@ struct sqlite3 {
*/
struct FuncDef {
i16 nArg; /* Number of arguments. -1 means unlimited */
- u8 iPrefEnc; /* Preferred text encoding (SQLITE_UTF8, 16LE, 16BE) */
- u8 flags; /* Some combination of SQLITE_FUNC_* */
+ u16 funcFlags; /* Some combination of SQLITE_FUNC_* */
void *pUserData; /* User data parameter */
FuncDef *pNext; /* Next function with same name */
void (*xFunc)(sqlite3_context*,int,sqlite3_value**); /* Regular function */
@@ -10260,14 +10347,16 @@ struct FuncDestructor {
** values must correspond to OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG. There
** are assert() statements in the code to verify this.
*/
-#define SQLITE_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */
-#define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */
-#define SQLITE_FUNC_EPHEM 0x04 /* Ephemeral. Delete with VDBE */
-#define SQLITE_FUNC_NEEDCOLL 0x08 /* sqlite3GetFuncCollSeq() might be called */
-#define SQLITE_FUNC_COUNT 0x10 /* Built-in count(*) aggregate */
-#define SQLITE_FUNC_COALESCE 0x20 /* Built-in coalesce() or ifnull() function */
-#define SQLITE_FUNC_LENGTH 0x40 /* Built-in length() function */
-#define SQLITE_FUNC_TYPEOF 0x80 /* Built-in typeof() function */
+#define SQLITE_FUNC_ENCMASK 0x003 /* SQLITE_UTF8, SQLITE_UTF16BE or UTF16LE */
+#define SQLITE_FUNC_LIKE 0x004 /* Candidate for the LIKE optimization */
+#define SQLITE_FUNC_CASE 0x008 /* Case-sensitive LIKE-type function */
+#define SQLITE_FUNC_EPHEM 0x010 /* Ephemeral. Delete with VDBE */
+#define SQLITE_FUNC_NEEDCOLL 0x020 /* sqlite3GetFuncCollSeq() might be called */
+#define SQLITE_FUNC_LENGTH 0x040 /* Built-in length() function */
+#define SQLITE_FUNC_TYPEOF 0x080 /* Built-in typeof() function */
+#define SQLITE_FUNC_COUNT 0x100 /* Built-in count(*) aggregate */
+#define SQLITE_FUNC_COALESCE 0x200 /* Built-in coalesce() or ifnull() */
+#define SQLITE_FUNC_UNLIKELY 0x400 /* Built-in unlikely() function */
/*
** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are
@@ -10295,18 +10384,18 @@ struct FuncDestructor {
** parameter.
*/
#define FUNCTION(zName, nArg, iArg, bNC, xFunc) \
- {nArg, SQLITE_UTF8, (bNC*SQLITE_FUNC_NEEDCOLL), \
+ {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
#define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \
- {nArg, SQLITE_UTF8, (bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags, \
+ {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags, \
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \
- {nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \
+ {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
pArg, 0, xFunc, 0, 0, #zName, 0, 0}
#define LIKEFUNC(zName, nArg, arg, flags) \
- {nArg, SQLITE_UTF8, flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0}
+ {nArg, SQLITE_UTF8|flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0}
#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \
- {nArg, SQLITE_UTF8, nc*SQLITE_FUNC_NEEDCOLL, \
+ {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL), \
SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0,0}
/*
@@ -10318,6 +10407,7 @@ struct FuncDestructor {
struct Savepoint {
char *zName; /* Savepoint name (nul-terminated) */
i64 nDeferredCons; /* Number of deferred fk violations */
+ i64 nDeferredImmCons; /* Number of deferred imm fk. */
Savepoint *pNext; /* Parent savepoint (if any) */
};
@@ -10354,7 +10444,8 @@ struct Column {
char *zColl; /* Collating sequence. If NULL, use the default */
u8 notNull; /* An OE_ code for handling a NOT NULL constraint */
char affinity; /* One of the SQLITE_AFF_... values */
- u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */
+ u8 szEst; /* Estimated size of this column. INT==1 */
+ u8 colFlags; /* Boolean properties. See COLFLAG_ defines below */
};
/* Allowed values for Column.colFlags:
@@ -10518,6 +10609,7 @@ struct Table {
i16 iPKey; /* If not negative, use aCol[iPKey] as the primary key */
i16 nCol; /* Number of columns in this table */
u16 nRef; /* Number of pointers to this Table */
+ LogEst szTabRow; /* Estimated size of each table row in bytes */
u8 tabFlags; /* Mask of TF_* values */
u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */
#ifndef SQLITE_OMIT_ALTERTABLE
@@ -10629,19 +10721,23 @@ struct FKey {
#define OE_SetDflt 8 /* Set the foreign key value to its default */
#define OE_Cascade 9 /* Cascade the changes */
-#define OE_Default 99 /* Do whatever the default action is */
+#define OE_Default 10 /* Do whatever the default action is */
/*
** An instance of the following structure is passed as the first
** argument to sqlite3VdbeKeyCompare and is used to control the
** comparison of the two index keys.
+**
+** Note that aSortOrder[] and aColl[] have nField+1 slots. There
+** are nField slots for the columns of an index then one extra slot
+** for the rowid at the end.
*/
struct KeyInfo {
sqlite3 *db; /* The database connection */
u8 enc; /* Text encoding - one of the SQLITE_UTF* values */
- u16 nField; /* Number of entries in aColl[] */
- u8 *aSortOrder; /* Sort order for each column. May be NULL */
+ u16 nField; /* Maximum index for aColl[] and aSortOrder[] */
+ u8 *aSortOrder; /* Sort order for each column. */
CollSeq *aColl[1]; /* Collating sequence for each term of the key */
};
@@ -10710,14 +10806,18 @@ struct Index {
Schema *pSchema; /* Schema containing this index */
u8 *aSortOrder; /* for each column: True==DESC, False==ASC */
char **azColl; /* Array of collation sequence names for index */
+ Expr *pPartIdxWhere; /* WHERE clause for partial indices */
int tnum; /* DB Page containing root of this index */
+ LogEst szIdxRow; /* Estimated average row size in bytes */
u16 nColumn; /* Number of columns in table used by this index */
u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
unsigned autoIndex:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */
unsigned bUnordered:1; /* Use this index for == or IN queries only */
-#ifdef SQLITE_ENABLE_STAT3
+ unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
int nSample; /* Number of elements in aSample[] */
- tRowcnt avgEq; /* Average nEq value for key values not in aSample */
+ int nSampleCol; /* Size of IndexSample.anEq[] and so on */
+ tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */
IndexSample *aSample; /* Samples of the left-most key */
#endif
};
@@ -10728,16 +10828,11 @@ struct Index {
** analyze.c source file for additional information.
*/
struct IndexSample {
- union {
- char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */
- double r; /* Value if eType is SQLITE_FLOAT */
- i64 i; /* Value if eType is SQLITE_INTEGER */
- } u;
- u8 eType; /* SQLITE_NULL, SQLITE_INTEGER ... etc. */
- int nByte; /* Size in byte of text or blob. */
- tRowcnt nEq; /* Est. number of rows where the key equals this sample */
- tRowcnt nLt; /* Est. number of rows where key is less than this sample */
- tRowcnt nDLt; /* Est. number of distinct keys less than this sample */
+ void *p; /* Pointer to sampled record */
+ int n; /* Size of record in bytes */
+ tRowcnt *anEq; /* Est. number of rows where the key equals this sample */
+ tRowcnt *anLt; /* Est. number of rows where key is less than this sample */
+ tRowcnt *anDLt; /* Est. number of distinct keys less than this sample */
};
/*
@@ -10878,7 +10973,7 @@ typedef int ynVar;
struct Expr {
u8 op; /* Operation performed by this node */
char affinity; /* The affinity of the column or 0 if not a column */
- u16 flags; /* Various flags. EP_* See below */
+ u32 flags; /* Various flags. EP_* See below */
union {
char *zToken; /* Token value. Zero terminated and dequoted */
int iValue; /* Non-negative integer value if EP_IntValue */
@@ -10892,8 +10987,8 @@ struct Expr {
Expr *pLeft; /* Left subnode */
Expr *pRight; /* Right subnode */
union {
- ExprList *pList; /* Function arguments or in "<expr> IN (<expr-list)" */
- Select *pSelect; /* Used for sub-selects and "<expr> IN (<select>)" */
+ ExprList *pList; /* op = IN, EXISTS, SELECT, CASE, FUNCTION, BETWEEN */
+ Select *pSelect; /* EP_xIsSelect and op = IN, EXISTS, SELECT */
} x;
/* If the EP_Reduced flag is set in the Expr.flags mask, then no
@@ -10906,12 +11001,12 @@ struct Expr {
#endif
int iTable; /* TK_COLUMN: cursor number of table holding column
** TK_REGISTER: register number
- ** TK_TRIGGER: 1 -> new, 0 -> old */
+ ** TK_TRIGGER: 1 -> new, 0 -> old
+ ** EP_Unlikely: 1000 times likelihood */
ynVar iColumn; /* TK_COLUMN: column index. -1 for rowid.
** TK_VARIABLE: variable number (always >= 1). */
i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */
i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */
- u8 flags2; /* Second set of flags. EP2_... */
u8 op2; /* TK_REGISTER: original value of Expr.op
** TK_COLUMN: the value of p5 for OP_Column
** TK_AGG_FUNCTION: nesting depth */
@@ -10922,51 +11017,46 @@ struct Expr {
/*
** The following are the meanings of bits in the Expr.flags field.
*/
-#define EP_FromJoin 0x0001 /* Originated in ON or USING clause of a join */
-#define EP_Agg 0x0002 /* Contains one or more aggregate functions */
-#define EP_Resolved 0x0004 /* IDs have been resolved to COLUMNs */
-#define EP_Error 0x0008 /* Expression contains one or more errors */
-#define EP_Distinct 0x0010 /* Aggregate function with DISTINCT keyword */
-#define EP_VarSelect 0x0020 /* pSelect is correlated, not constant */
-#define EP_DblQuoted 0x0040 /* token.z was originally in "..." */
-#define EP_InfixFunc 0x0080 /* True for an infix function: LIKE, GLOB, etc */
-#define EP_Collate 0x0100 /* Tree contains a TK_COLLATE opeartor */
-#define EP_FixedDest 0x0200 /* Result needed in a specific register */
-#define EP_IntValue 0x0400 /* Integer value contained in u.iValue */
-#define EP_xIsSelect 0x0800 /* x.pSelect is valid (otherwise x.pList is) */
-#define EP_Hint 0x1000 /* Not used */
-#define EP_Reduced 0x2000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */
-#define EP_TokenOnly 0x4000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
-#define EP_Static 0x8000 /* Held in memory not obtained from malloc() */
+#define EP_FromJoin 0x000001 /* Originated in ON or USING clause of a join */
+#define EP_Agg 0x000002 /* Contains one or more aggregate functions */
+#define EP_Resolved 0x000004 /* IDs have been resolved to COLUMNs */
+#define EP_Error 0x000008 /* Expression contains one or more errors */
+#define EP_Distinct 0x000010 /* Aggregate function with DISTINCT keyword */
+#define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */
+#define EP_DblQuoted 0x000040 /* token.z was originally in "..." */
+#define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */
+#define EP_Collate 0x000100 /* Tree contains a TK_COLLATE opeartor */
+#define EP_FixedDest 0x000200 /* Result needed in a specific register */
+#define EP_IntValue 0x000400 /* Integer value contained in u.iValue */
+#define EP_xIsSelect 0x000800 /* x.pSelect is valid (otherwise x.pList is) */
+#define EP_Skip 0x001000 /* COLLATE, AS, or UNLIKELY */
+#define EP_Reduced 0x002000 /* Expr struct EXPR_REDUCEDSIZE bytes only */
+#define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */
+#define EP_Static 0x008000 /* Held in memory not obtained from malloc() */
+#define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */
+#define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */
+#define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */
/*
-** The following are the meanings of bits in the Expr.flags2 field.
+** These macros can be used to test, set, or clear bits in the
+** Expr.flags field.
*/
-#define EP2_MallocedToken 0x0001 /* Need to sqlite3DbFree() Expr.zToken */
-#define EP2_Irreducible 0x0002 /* Cannot EXPRDUP_REDUCE this Expr */
+#define ExprHasProperty(E,P) (((E)->flags&(P))!=0)
+#define ExprHasAllProperty(E,P) (((E)->flags&(P))==(P))
+#define ExprSetProperty(E,P) (E)->flags|=(P)
+#define ExprClearProperty(E,P) (E)->flags&=~(P)
-/*
-** The pseudo-routine sqlite3ExprSetIrreducible sets the EP2_Irreducible
-** flag on an expression structure. This flag is used for VV&A only. The
-** routine is implemented as a macro that only works when in debugging mode,
-** so as not to burden production code.
+/* The ExprSetVVAProperty() macro is used for Verification, Validation,
+** and Accreditation only. It works like ExprSetProperty() during VVA
+** processes but is a no-op for delivery.
*/
#ifdef SQLITE_DEBUG
-# define ExprSetIrreducible(X) (X)->flags2 |= EP2_Irreducible
+# define ExprSetVVAProperty(E,P) (E)->flags|=(P)
#else
-# define ExprSetIrreducible(X)
+# define ExprSetVVAProperty(E,P)
#endif
/*
-** These macros can be used to test, set, or clear bits in the
-** Expr.flags field.
-*/
-#define ExprHasProperty(E,P) (((E)->flags&(P))==(P))
-#define ExprHasAnyProperty(E,P) (((E)->flags&(P))!=0)
-#define ExprSetProperty(E,P) (E)->flags|=(P)
-#define ExprClearProperty(E,P) (E)->flags&=~(P)
-
-/*
** Macros to determine the number of bytes required by a normal Expr
** struct, an Expr struct with the EP_Reduced flag set in Expr.flags
** and an Expr struct with the EP_TokenOnly flag set.
@@ -11061,6 +11151,11 @@ typedef u64 Bitmask;
#define BMS ((int)(sizeof(Bitmask)*8))
/*
+** A bit in a Bitmask
+*/
+#define MASKBIT(n) (((Bitmask)1)<<(n))
+
+/*
** The following structure describes the FROM clause of a SELECT statement.
** Each table or subquery in the FROM clause is a separate element of
** the SrcList.a[] array.
@@ -11080,8 +11175,8 @@ typedef u64 Bitmask;
** contains more than 63 columns and the 64-th or later column is used.
*/
struct SrcList {
- i16 nSrc; /* Number of tables or subqueries in the FROM clause */
- i16 nAlloc; /* Number of entries allocated in a[] below */
+ u8 nSrc; /* Number of tables or subqueries in the FROM clause */
+ u8 nAlloc; /* Number of entries allocated in a[] below */
struct SrcList_item {
Schema *pSchema; /* Schema to which this item is fixed */
char *zDatabase; /* Name of database holding this table */
@@ -11120,79 +11215,6 @@ struct SrcList {
/*
-** A WherePlan object holds information that describes a lookup
-** strategy.
-**
-** This object is intended to be opaque outside of the where.c module.
-** It is included here only so that that compiler will know how big it
-** is. None of the fields in this object should be used outside of
-** the where.c module.
-**
-** Within the union, pIdx is only used when wsFlags&WHERE_INDEXED is true.
-** pTerm is only used when wsFlags&WHERE_MULTI_OR is true. And pVtabIdx
-** is only used when wsFlags&WHERE_VIRTUALTABLE is true. It is never the
-** case that more than one of these conditions is true.
-*/
-struct WherePlan {
- u32 wsFlags; /* WHERE_* flags that describe the strategy */
- u16 nEq; /* Number of == constraints */
- u16 nOBSat; /* Number of ORDER BY terms satisfied */
- double nRow; /* Estimated number of rows (for EQP) */
- union {
- Index *pIdx; /* Index when WHERE_INDEXED is true */
- struct WhereTerm *pTerm; /* WHERE clause term for OR-search */
- sqlite3_index_info *pVtabIdx; /* Virtual table index to use */
- } u;
-};
-
-/*
-** For each nested loop in a WHERE clause implementation, the WhereInfo
-** structure contains a single instance of this structure. This structure
-** is intended to be private to the where.c module and should not be
-** access or modified by other modules.
-**
-** The pIdxInfo field is used to help pick the best index on a
-** virtual table. The pIdxInfo pointer contains indexing
-** information for the i-th table in the FROM clause before reordering.
-** All the pIdxInfo pointers are freed by whereInfoFree() in where.c.
-** All other information in the i-th WhereLevel object for the i-th table
-** after FROM clause ordering.
-*/
-struct WhereLevel {
- WherePlan plan; /* query plan for this element of the FROM clause */
- int iLeftJoin; /* Memory cell used to implement LEFT OUTER JOIN */
- int iTabCur; /* The VDBE cursor used to access the table */
- int iIdxCur; /* The VDBE cursor used to access pIdx */
- int addrBrk; /* Jump here to break out of the loop */
- int addrNxt; /* Jump here to start the next IN combination */
- int addrCont; /* Jump here to continue with the next loop cycle */
- int addrFirst; /* First instruction of interior of the loop */
- u8 iFrom; /* Which entry in the FROM clause */
- u8 op, p5; /* Opcode and P5 of the opcode that ends the loop */
- int p1, p2; /* Operands of the opcode used to ends the loop */
- union { /* Information that depends on plan.wsFlags */
- struct {
- int nIn; /* Number of entries in aInLoop[] */
- struct InLoop {
- int iCur; /* The VDBE cursor used by this IN operator */
- int addrInTop; /* Top of the IN loop */
- u8 eEndLoopOp; /* IN Loop terminator. OP_Next or OP_Prev */
- } *aInLoop; /* Information about each nested IN operator */
- } in; /* Used when plan.wsFlags&WHERE_IN_ABLE */
- Index *pCovidx; /* Possible covering index for WHERE_MULTI_OR */
- } u;
- double rOptCost; /* "Optimal" cost for this level */
-
- /* The following field is really not part of the current level. But
- ** we need a place to cache virtual table index information for each
- ** virtual table in the FROM clause and the WhereLevel structure is
- ** a convenient place since there is one WhereLevel for each FROM clause
- ** element.
- */
- sqlite3_index_info *pIdxInfo; /* Index info for n-th source table */
-};
-
-/*
** Flags appropriate for the wctrlFlags parameter of sqlite3WhereBegin()
** and the WhereInfo.wctrlFlags member.
*/
@@ -11205,33 +11227,12 @@ struct WhereLevel {
#define WHERE_FORCE_TABLE 0x0020 /* Do not use an index-only search */
#define WHERE_ONETABLE_ONLY 0x0040 /* Only code the 1st table in pTabList */
#define WHERE_AND_ONLY 0x0080 /* Don't use indices for OR terms */
+#define WHERE_GROUPBY 0x0100 /* pOrderBy is really a GROUP BY */
+#define WHERE_DISTINCTBY 0x0200 /* pOrderby is really a DISTINCT clause */
+#define WHERE_WANT_DISTINCT 0x0400 /* All output needs to be distinct */
-/*
-** The WHERE clause processing routine has two halves. The
-** first part does the start of the WHERE loop and the second
-** half does the tail of the WHERE loop. An instance of
-** this structure is returned by the first half and passed
-** into the second half to give some continuity.
+/* Allowed return values from sqlite3WhereIsDistinct()
*/
-struct WhereInfo {
- Parse *pParse; /* Parsing and code generating context */
- SrcList *pTabList; /* List of tables in the join */
- u16 nOBSat; /* Number of ORDER BY terms satisfied by indices */
- u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */
- u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE/DELETE */
- u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */
- u8 eDistinct; /* One of the WHERE_DISTINCT_* values below */
- int iTop; /* The very beginning of the WHERE loop */
- int iContinue; /* Jump here to continue with next record */
- int iBreak; /* Jump here to break out of the loop */
- int nLevel; /* Number of nested loop */
- struct WhereClause *pWC; /* Decomposition of the WHERE clause */
- double savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */
- double nRowOut; /* Estimated number of output rows */
- WhereLevel a[1]; /* Information about each nest loop in WHERE */
-};
-
-/* Allowed values for WhereInfo.eDistinct and DistinctCtx.eTnctType */
#define WHERE_DISTINCT_NOOP 0 /* DISTINCT keyword not used */
#define WHERE_DISTINCT_UNIQUE 1 /* No duplicates */
#define WHERE_DISTINCT_ORDERED 2 /* All duplicates are adjacent */
@@ -11261,7 +11262,7 @@ struct WhereInfo {
struct NameContext {
Parse *pParse; /* The parser */
SrcList *pSrcList; /* One or more tables used to resolve names */
- ExprList *pEList; /* Optional list of named expressions */
+ ExprList *pEList; /* Optional list of result-set columns */
AggInfo *pAggInfo; /* Information about aggregates at this level */
NameContext *pNext; /* Next outer name context. NULL for outermost */
int nRef; /* Number of names resolved by this context */
@@ -11276,8 +11277,7 @@ struct NameContext {
#define NC_HasAgg 0x02 /* One or more aggregate functions seen */
#define NC_IsCheck 0x04 /* True if resolving names in a CHECK constraint */
#define NC_InAggFunc 0x08 /* True if analyzing arguments to an agg func */
-#define NC_AsMaybe 0x10 /* Resolve to AS terms of the result set only
- ** if no other resolution is available */
+#define NC_PartIdx 0x10 /* True if resolving a partial index WHERE */
/*
** An instance of the following structure contains all information
@@ -11305,7 +11305,7 @@ struct Select {
u16 selFlags; /* Various SF_* values */
int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
int addrOpenEphm[3]; /* OP_OpenEphem opcodes related to this select */
- double nSelectRow; /* Estimated number of result rows */
+ u64 nSelectRow; /* Estimated number of result rows */
SrcList *pSrc; /* The FROM clause */
Expr *pWhere; /* The WHERE clause */
ExprList *pGroupBy; /* The GROUP BY clause */
@@ -11332,6 +11332,7 @@ struct Select {
#define SF_Values 0x0080 /* Synthesized from VALUES clause */
#define SF_Materialize 0x0100 /* Force materialization of views */
#define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */
+#define SF_MaybeConvert 0x0400 /* Need convertCompoundSelectToSubquery() */
/*
@@ -11453,6 +11454,7 @@ struct Parse {
u8 iColCache; /* Next entry in aColCache[] to replace */
u8 isMultiWrite; /* True if statement may modify/insert multiple rows */
u8 mayAbort; /* True if statement may throw an ABORT exception */
+ u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */
int aTempReg[8]; /* Holding area for temporary registers */
int nRangeReg; /* Size of the temporary register block */
int iRangeReg; /* First register in temporary register block */
@@ -11462,6 +11464,7 @@ struct Parse {
int nSet; /* Number of sets used so far */
int nOnce; /* Number of OP_Once instructions so far */
int ckBase; /* Base register of data during check constraints */
+ int iPartIdxTab; /* Table corresponding to a partial index */
int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */
int iCacheCnt; /* Counter used to generate aColCache[].lru values */
struct yColCache {
@@ -11489,7 +11492,7 @@ struct Parse {
/* Information used while coding trigger programs. */
Parse *pToplevel; /* Parse structure for main program (or NULL) */
Table *pTriggerTab; /* Table triggers are being coded for */
- double nQueryLoop; /* Estimated number of iterations of a query */
+ u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */
u32 oldmask; /* Mask of old.* columns referenced */
u32 newmask; /* Mask of new.* columns referenced */
u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
@@ -11661,6 +11664,7 @@ typedef struct DbFixer DbFixer;
struct DbFixer {
Parse *pParse; /* The parsing context. Error messages written here */
Schema *pSchema; /* Fix items to this schema */
+ int bVarOnly; /* Check for variable references only */
const char *zDb; /* Make sure all objects are contained in this database */
const char *zType; /* Type of the container - used for error messages */
const Token *pName; /* Name of the container - used for error messages */
@@ -11677,10 +11681,11 @@ struct StrAccum {
int nChar; /* Length of the string so far */
int nAlloc; /* Amount of space allocated in zText */
int mxAlloc; /* Maximum allowed string length */
- u8 mallocFailed; /* Becomes true if any memory allocation fails */
u8 useMalloc; /* 0: none, 1: sqlite3DbMalloc, 2: sqlite3_malloc */
- u8 tooBig; /* Becomes true if string size exceeds limits */
+ u8 accError; /* STRACCUM_NOMEM or STRACCUM_TOOBIG */
};
+#define STRACCUM_NOMEM 1
+#define STRACCUM_TOOBIG 2
/*
** A pointer to this structure is used to communicate information
@@ -12043,7 +12048,7 @@ SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse*, SrcList*);
SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3*, IdList*);
SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3*, SrcList*);
SQLITE_PRIVATE Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
- Token*, int, int);
+ Expr*, int, int);
SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int);
SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
@@ -12059,6 +12064,12 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
SQLITE_PRIVATE void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int);
SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo*);
+SQLITE_PRIVATE u64 sqlite3WhereOutputRowCount(WhereInfo*);
+SQLITE_PRIVATE int sqlite3WhereIsDistinct(WhereInfo*);
+SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo*);
+SQLITE_PRIVATE int sqlite3WhereContinueLabel(WhereInfo*);
+SQLITE_PRIVATE int sqlite3WhereBreakLabel(WhereInfo*);
+SQLITE_PRIVATE int sqlite3WhereOkOnePass(WhereInfo*);
SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8);
SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int);
SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse*, int, int, int);
@@ -12085,8 +12096,9 @@ SQLITE_PRIVATE void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*);
SQLITE_PRIVATE void sqlite3Vacuum(Parse*);
SQLITE_PRIVATE int sqlite3RunVacuum(char**, sqlite3*);
SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3*, Token*);
-SQLITE_PRIVATE int sqlite3ExprCompare(Expr*, Expr*);
-SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList*, ExprList*);
+SQLITE_PRIVATE int sqlite3ExprCompare(Expr*, Expr*, int);
+SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList*, ExprList*, int);
+SQLITE_PRIVATE int sqlite3ExprImpliesExpr(Expr*, Expr*, int);
SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*);
SQLITE_PRIVATE void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*);
SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr*, SrcList*);
@@ -12113,7 +12125,7 @@ SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char);
SQLITE_PRIVATE int sqlite3IsRowid(const char*);
SQLITE_PRIVATE void sqlite3GenerateRowDelete(Parse*, Table*, int, int, int, Trigger *, int);
SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int*);
-SQLITE_PRIVATE int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int);
+SQLITE_PRIVATE int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*);
SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int,
int*,int,int,int,int,int*);
SQLITE_PRIVATE void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int);
@@ -12191,7 +12203,7 @@ SQLITE_PRIVATE int sqlite3AuthReadCol(Parse*, const char *, const char *, int)
#endif
SQLITE_PRIVATE void sqlite3Attach(Parse*, Expr*, Expr*, Expr*);
SQLITE_PRIVATE void sqlite3Detach(Parse*, Expr*);
-SQLITE_PRIVATE int sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*);
+SQLITE_PRIVATE void sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*);
SQLITE_PRIVATE int sqlite3FixSrcList(DbFixer*, SrcList*);
SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*);
SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*);
@@ -12203,6 +12215,12 @@ SQLITE_PRIVATE int sqlite3Atoi(const char*);
SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar);
SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte);
SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**);
+SQLITE_PRIVATE LogEst sqlite3LogEst(u64);
+SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst,LogEst);
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double);
+#endif
+SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst);
/*
** Routines to read and write variable-length integers. These used to
@@ -12288,9 +12306,6 @@ SQLITE_PRIVATE void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8,
SQLITE_PRIVATE void sqlite3ValueFree(sqlite3_value*);
SQLITE_PRIVATE sqlite3_value *sqlite3ValueNew(sqlite3 *);
SQLITE_PRIVATE char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8);
-#ifdef SQLITE_ENABLE_STAT3
-SQLITE_PRIVATE char *sqlite3Utf8to16(sqlite3 *, u8, char *, int, int *);
-#endif
SQLITE_PRIVATE int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **);
SQLITE_PRIVATE void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8);
#ifndef SQLITE_AMALGAMATION
@@ -12316,12 +12331,13 @@ SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*);
SQLITE_PRIVATE int sqlite3MatchSpanName(const char*, const char*, const char*, const char*);
SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*);
SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*);
+SQLITE_PRIVATE void sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*);
SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int);
SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *);
SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *, SrcList *);
SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*);
-SQLITE_PRIVATE char sqlite3AffinityType(const char*);
+SQLITE_PRIVATE char sqlite3AffinityType(const char*, u8*);
SQLITE_PRIVATE void sqlite3Analyze(Parse*, Token*, Token*);
SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*);
SQLITE_PRIVATE int sqlite3FindDb(sqlite3*, Token*);
@@ -12335,6 +12351,7 @@ SQLITE_PRIVATE void sqlite3MinimumFileFormat(Parse*, int, int);
SQLITE_PRIVATE void sqlite3SchemaClear(void *);
SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *, Btree *);
SQLITE_PRIVATE int sqlite3SchemaToIndex(sqlite3 *db, Schema *);
+SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int);
SQLITE_PRIVATE KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *);
SQLITE_PRIVATE int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *,
void (*)(sqlite3_context*,int,sqlite3_value **),
@@ -12355,6 +12372,12 @@ SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int);
SQLITE_PRIVATE void sqlite3BackupRestart(sqlite3_backup *);
SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *);
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+SQLITE_PRIVATE void sqlite3AnalyzeFunctions(void);
+SQLITE_PRIVATE int sqlite3Stat4ProbeSetValue(Parse*,Index*,UnpackedRecord**,Expr*,u8,int,int*);
+SQLITE_PRIVATE void sqlite3Stat4ProbeFree(UnpackedRecord*);
+#endif
+
/*
** The interface to the LEMON-generated parser
*/
@@ -12396,13 +12419,14 @@ SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char*);
#else
SQLITE_PRIVATE void sqlite3VtabClear(sqlite3 *db, Table*);
SQLITE_PRIVATE void sqlite3VtabDisconnect(sqlite3 *db, Table *p);
-SQLITE_PRIVATE int sqlite3VtabSync(sqlite3 *db, char **);
+SQLITE_PRIVATE int sqlite3VtabSync(sqlite3 *db, Vdbe*);
SQLITE_PRIVATE int sqlite3VtabRollback(sqlite3 *db);
SQLITE_PRIVATE int sqlite3VtabCommit(sqlite3 *db);
SQLITE_PRIVATE void sqlite3VtabLock(VTable *);
SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *);
SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3*);
SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *, int, int);
+SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe*, sqlite3_vtab*);
SQLITE_PRIVATE VTable *sqlite3GetVTable(sqlite3*, Table*);
# define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0)
#endif
@@ -12417,6 +12441,7 @@ SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3*, int, const char *);
SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *, VTable *);
SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*);
SQLITE_PRIVATE void sqlite3InvalidFunction(sqlite3_context*,int,sqlite3_value**);
+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 int sqlite3Reprepare(Vdbe*);
@@ -12437,18 +12462,18 @@ SQLITE_PRIVATE int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int);
** provided (enforcement of FK constraints requires the triggers sub-system).
*/
#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
-SQLITE_PRIVATE void sqlite3FkCheck(Parse*, Table*, int, int);
+SQLITE_PRIVATE void sqlite3FkCheck(Parse*, Table*, int, int, int*, int);
SQLITE_PRIVATE void sqlite3FkDropTable(Parse*, SrcList *, Table*);
-SQLITE_PRIVATE void sqlite3FkActions(Parse*, Table*, ExprList*, int);
+SQLITE_PRIVATE void sqlite3FkActions(Parse*, Table*, ExprList*, int, int*, int);
SQLITE_PRIVATE int sqlite3FkRequired(Parse*, Table*, int*, int);
SQLITE_PRIVATE u32 sqlite3FkOldmask(Parse*, Table*);
SQLITE_PRIVATE FKey *sqlite3FkReferences(Table *);
#else
- #define sqlite3FkActions(a,b,c,d)
- #define sqlite3FkCheck(a,b,c,d)
+ #define sqlite3FkActions(a,b,c,d,e,f)
+ #define sqlite3FkCheck(a,b,c,d,e,f)
#define sqlite3FkDropTable(a,b,c)
- #define sqlite3FkOldmask(a,b) 0
- #define sqlite3FkRequired(a,b,c,d) 0
+ #define sqlite3FkOldmask(a,b) 0
+ #define sqlite3FkRequired(a,b,c,d) 0
#endif
#ifndef SQLITE_OMIT_FOREIGN_KEY
SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *, Table*);
@@ -12937,7 +12962,9 @@ static const char * const azCompileOpt[] = {
#ifdef SQLITE_ENABLE_RTREE
"ENABLE_RTREE",
#endif
-#ifdef SQLITE_ENABLE_STAT3
+#if defined(SQLITE_ENABLE_STAT4)
+ "ENABLE_STAT4",
+#elif defined(SQLITE_ENABLE_STAT3)
"ENABLE_STAT3",
#endif
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
@@ -13287,6 +13314,9 @@ typedef struct VdbeSorter VdbeSorter;
/* Opaque type used by the explainer */
typedef struct Explain Explain;
+/* Elements of the linked list at Vdbe.pAuxData */
+typedef struct AuxData AuxData;
+
/*
** A cursor is a pointer into a single BTree within a database file.
** The cursor can seek to a BTree entry with a particular key, or
@@ -13473,23 +13503,19 @@ struct Mem {
#define memIsValid(M) ((M)->flags & MEM_Invalid)==0
#endif
-
-/* A VdbeFunc is just a FuncDef (defined in sqliteInt.h) that contains
-** additional information about auxiliary information bound to arguments
-** of the function. This is used to implement the sqlite3_get_auxdata()
-** and sqlite3_set_auxdata() APIs. The "auxdata" is some auxiliary data
-** that can be associated with a constant argument to a function. This
-** allows functions such as "regexp" to compile their constant regular
-** expression argument once and reused the compiled code for multiple
-** invocations.
+/*
+** Each auxilliary data pointer stored by a user defined function
+** implementation calling sqlite3_set_auxdata() is stored in an instance
+** of this structure. All such structures associated with a single VM
+** are stored in a linked list headed at Vdbe.pAuxData. All are destroyed
+** when the VM is halted (if not before).
*/
-struct VdbeFunc {
- FuncDef *pFunc; /* The definition of the function */
- int nAux; /* Number of entries allocated for apAux[] */
- struct AuxData {
- void *pAux; /* Aux data for the i-th argument */
- void (*xDelete)(void *); /* Destructor for the aux data */
- } apAux[1]; /* One slot for each function argument */
+struct AuxData {
+ int iOp; /* Instruction number of OP_Function opcode */
+ int iArg; /* Index of function argument. */
+ void *pAux; /* Aux data pointer */
+ void (*xDelete)(void *); /* Destructor for the aux data */
+ AuxData *pNext; /* Next element in list */
};
/*
@@ -13507,12 +13533,14 @@ struct VdbeFunc {
*/
struct sqlite3_context {
FuncDef *pFunc; /* Pointer to function information. MUST BE FIRST */
- VdbeFunc *pVdbeFunc; /* Auxilary data, if created. */
Mem s; /* The return value is stored here */
Mem *pMem; /* Memory cell used to store aggregate context */
CollSeq *pColl; /* Collating sequence */
+ Vdbe *pVdbe; /* The VM that owns this context */
+ int iOp; /* Instruction number of OP_Function */
int isError; /* Error code returned by the function. */
- int skipFlag; /* Skip skip accumulator loading if true */
+ u8 skipFlag; /* Skip skip accumulator loading if true */
+ u8 fErrorOrAux; /* isError!=0 or pVdbe->pAuxData modified */
};
/*
@@ -13580,19 +13608,22 @@ struct Vdbe {
bft expired:1; /* True if the VM needs to be recompiled */
bft runOnlyOnce:1; /* Automatically expire on reset */
bft usesStmtJournal:1; /* True if uses a statement journal */
- bft readOnly:1; /* True for read-only statements */
+ bft readOnly:1; /* True for statements that do not write */
+ bft bIsReader:1; /* True for statements that read */
bft isPrepareV2:1; /* True if prepared with prepare_v2() */
bft doingRerun:1; /* True if rerunning after an auto-reprepare */
int nChange; /* Number of db changes made since last reset */
yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */
yDbMask lockMask; /* Subset of btreeMask that requires a lock */
int iStatement; /* Statement number (or 0 if has not opened stmt) */
- int aCounter[3]; /* Counters used by sqlite3_stmt_status() */
+ u32 aCounter[5]; /* Counters used by sqlite3_stmt_status() */
#ifndef SQLITE_OMIT_TRACE
i64 startTime; /* Time when query started - used for profiling */
#endif
+ i64 iCurrentTime; /* Value of julianday('now') for this statement */
i64 nFkConstraint; /* Number of imm. FK constraints this VM */
i64 nStmtDefCons; /* Number of def. constraints when stmt started */
+ i64 nStmtDefImmCons; /* Number of def. imm constraints when stmt started */
char *zSql; /* Text of the SQL statement that generated this */
void *pFree; /* Free this when deleting the vdbe */
#ifdef SQLITE_DEBUG
@@ -13609,6 +13640,7 @@ struct Vdbe {
SubProgram *pProgram; /* Linked list of all sub-programs used by VM */
int nOnceFlag; /* Size of array aOnceFlag[] */
u8 *aOnceFlag; /* Flags for OP_Once */
+ AuxData *pAuxData; /* Linked list of auxdata allocations */
};
/*
@@ -13632,7 +13664,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32);
SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem*, int);
SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(unsigned char*, int, Mem*, int);
SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*);
-SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(VdbeFunc*, int);
+SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(Vdbe*, int, int);
int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *);
SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(VdbeCursor*,UnpackedRecord*,int*);
@@ -13953,6 +13985,16 @@ SQLITE_API int sqlite3_db_status(
break;
}
+ /* Set *pCurrent to non-zero if there are unresolved deferred foreign
+ ** key constraints. Set *pCurrent to zero if all foreign key constraints
+ ** have been satisfied. The *pHighwater is always set to zero.
+ */
+ case SQLITE_DBSTATUS_DEFERRED_FKS: {
+ *pHighwater = 0;
+ *pCurrent = db->nDeferredImmCons>0 || db->nDeferredCons>0;
+ break;
+ }
+
default: {
rc = SQLITE_ERROR;
}
@@ -14258,8 +14300,8 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){
** Return the number of errors.
*/
static int setDateTimeToCurrent(sqlite3_context *context, DateTime *p){
- sqlite3 *db = sqlite3_context_db_handle(context);
- if( sqlite3OsCurrentTimeInt64(db->pVfs, &p->iJD)==SQLITE_OK ){
+ p->iJD = sqlite3StmtCurrentTime(context);
+ if( p->iJD>0 ){
p->validJD = 1;
return 0;
}else{
@@ -14394,6 +14436,10 @@ struct tm *__cdecl localtime(const time_t *t);
**
** If the sqlite3GlobalConfig.bLocaltimeFault variable is true then this
** routine will always fail.
+**
+** EVIDENCE-OF: R-62172-00036 In this implementation, the standard C
+** library function localtime_r() is used to assist in the calculation of
+** local time.
*/
static int osLocaltime(time_t *t, struct tm *pTm){
int rc;
@@ -14450,6 +14496,11 @@ static sqlite3_int64 localtimeOffset(
x = *p;
computeYMD_HMS(&x);
if( x.Y<1971 || x.Y>=2038 ){
+ /* EVIDENCE-OF: R-55269-29598 The localtime_r() C function normally only
+ ** works for years between 1970 and 2037. For dates outside this range,
+ ** SQLite attempts to map the year into an equivalent year within this
+ ** range, do the calculation, then map the year back.
+ */
x.Y = 2000;
x.M = 1;
x.D = 1;
@@ -15046,8 +15097,8 @@ static void currentTimeFunc(
UNUSED_PARAMETER(argc);
UNUSED_PARAMETER(argv);
- db = sqlite3_context_db_handle(context);
- if( sqlite3OsCurrentTimeInt64(db->pVfs, &iT) ) return;
+ iT = sqlite3StmtCurrentTime(context);
+ if( iT<=0 ) return;
t = iT/1000 - 10000*(sqlite3_int64)21086676;
#ifdef HAVE_GMTIME_R
pTm = gmtime_r(&t, &sNow);
@@ -16086,7 +16137,7 @@ static int sqlite3MemSize(void *p){
return 0;
}
pHdr = sqlite3MemsysGetHeader(p);
- return pHdr->iSize;
+ return (int)pHdr->iSize;
}
/*
@@ -16128,7 +16179,7 @@ static void randomFill(char *pBuf, int nByte){
x = SQLITE_PTR_TO_INT(pBuf);
y = nByte | 1;
while( nByte >= 4 ){
- x = (x>>1) ^ (-(x&1) & 0xd0000001);
+ x = (x>>1) ^ (-(int)(x&1) & 0xd0000001);
y = y*1103515245 + 12345;
r = x ^ y;
*(int*)pBuf = r;
@@ -16136,7 +16187,7 @@ static void randomFill(char *pBuf, int nByte){
nByte -= 4;
}
while( nByte-- > 0 ){
- x = (x>>1) ^ (-(x&1) & 0xd0000001);
+ x = (x>>1) ^ (-(int)(x&1) & 0xd0000001);
y = y*1103515245 + 12345;
r = x ^ y;
*(pBuf++) = r & 0xff;
@@ -16231,9 +16282,9 @@ static void sqlite3MemFree(void *pPrior){
}
z = (char*)pBt;
z -= pHdr->nTitle;
- adjustStats(pHdr->iSize, -1);
+ adjustStats((int)pHdr->iSize, -1);
randomFill(z, sizeof(void*)*pHdr->nBacktraceSlots + sizeof(*pHdr) +
- pHdr->iSize + sizeof(int) + pHdr->nTitle);
+ (int)pHdr->iSize + sizeof(int) + pHdr->nTitle);
free(z);
sqlite3_mutex_leave(mem.mutex);
}
@@ -16255,9 +16306,9 @@ static void *sqlite3MemRealloc(void *pPrior, int nByte){
pOldHdr = sqlite3MemsysGetHeader(pPrior);
pNew = sqlite3MemMalloc(nByte);
if( pNew ){
- memcpy(pNew, pPrior, nByte<pOldHdr->iSize ? nByte : pOldHdr->iSize);
+ memcpy(pNew, pPrior, (int)(nByte<pOldHdr->iSize ? nByte : pOldHdr->iSize));
if( nByte>pOldHdr->iSize ){
- randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - pOldHdr->iSize);
+ randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - (int)pOldHdr->iSize);
}
sqlite3MemFree(pPrior);
}
@@ -16372,7 +16423,7 @@ SQLITE_PRIVATE void sqlite3MemdebugSync(){
for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){
void **pBt = (void**)pHdr;
pBt -= pHdr->nBacktraceSlots;
- mem.xBacktrace(pHdr->iSize, pHdr->nBacktrace-1, &pBt[1]);
+ mem.xBacktrace((int)pHdr->iSize, pHdr->nBacktrace-1, &pBt[1]);
}
}
@@ -17256,13 +17307,13 @@ static SQLITE_WSD struct Mem5Global {
} mem5;
/*
-** Access the static variable through a macro for SQLITE_OMIT_WSD
+** Access the static variable through a macro for SQLITE_OMIT_WSD.
*/
#define mem5 GLOBAL(struct Mem5Global, mem5)
/*
** Assuming mem5.zPool is divided up into an array of Mem5Link
-** structures, return a pointer to the idx-th such lik.
+** structures, return a pointer to the idx-th such link.
*/
#define MEM5LINK(idx) ((Mem5Link *)(&mem5.zPool[(idx)*mem5.szAtom]))
@@ -17358,7 +17409,7 @@ static int memsys5UnlinkFirst(int iLogsize){
** Return a block of memory of at least nBytes in size.
** Return NULL if unable. Return NULL if nBytes==0.
**
-** The caller guarantees that nByte positive.
+** The caller guarantees that nByte is positive.
**
** The caller has obtained a mutex prior to invoking this
** routine so there is never any chance that two or more
@@ -17480,7 +17531,7 @@ static void memsys5FreeUnsafe(void *pOld){
}
/*
-** Allocate nBytes of memory
+** Allocate nBytes of memory.
*/
static void *memsys5Malloc(int nBytes){
sqlite3_int64 *p = 0;
@@ -18494,7 +18545,7 @@ struct sqlite3_mutex {
}
return osType==2;
}
-#endif /* SQLITE_OS_WINCE */
+#endif /* SQLITE_OS_WINCE || SQLITE_OS_WINRT */
#endif
#ifdef SQLITE_DEBUG
@@ -18532,7 +18583,7 @@ static int winMutex_isInit = 0;
** processing, the "interlocked" magic is probably not
** strictly necessary.
*/
-static long winMutex_lock = 0;
+static LONG winMutex_lock = 0;
SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */
@@ -19253,6 +19304,7 @@ SQLITE_API void sqlite3_free(void *p){
*/
SQLITE_PRIVATE void sqlite3DbFree(sqlite3 *db, void *p){
assert( db==0 || sqlite3_mutex_held(db->mutex) );
+ if( p==0 ) return;
if( db ){
if( db->pnBytesFreed ){
*db->pnBytesFreed += sqlite3DbMallocSize(db, p);
@@ -19912,7 +19964,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
nOut = precision + 10;
zOut = zExtra = sqlite3Malloc( nOut );
if( zOut==0 ){
- pAccum->mallocFailed = 1;
+ pAccum->accError = STRACCUM_NOMEM;
return;
}
}
@@ -19966,13 +20018,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
else prefix = 0;
}
if( xtype==etGENERIC && precision>0 ) precision--;
-#if 0
- /* Rounding works like BSD when the constant 0.4999 is used. Wierd! */
- for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
-#else
- /* It makes more sense to use 0.5 */
for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1){}
-#endif
if( xtype==etFLOAT ) realvalue += rounder;
/* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
exp = 0;
@@ -20027,10 +20073,10 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
}else{
e2 = exp;
}
- if( e2+precision+width > etBUFSIZE - 15 ){
- bufpt = zExtra = sqlite3Malloc( e2+precision+width+15 );
+ if( MAX(e2,0)+precision+width > etBUFSIZE - 15 ){
+ bufpt = zExtra = sqlite3Malloc( MAX(e2,0)+precision+width+15 );
if( bufpt==0 ){
- pAccum->mallocFailed = 1;
+ pAccum->accError = STRACCUM_NOMEM;
return;
}
}
@@ -20165,7 +20211,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
if( n>etBUFSIZE ){
bufpt = zExtra = sqlite3Malloc( n );
if( bufpt==0 ){
- pAccum->mallocFailed = 1;
+ pAccum->accError = STRACCUM_NOMEM;
return;
}
}else{
@@ -20243,22 +20289,20 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
*/
SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
assert( z!=0 || N==0 );
- if( p->tooBig | p->mallocFailed ){
- testcase(p->tooBig);
- testcase(p->mallocFailed);
+ if( p->accError ){
+ testcase(p->accError==STRACCUM_TOOBIG);
+ testcase(p->accError==STRACCUM_NOMEM);
return;
}
assert( p->zText!=0 || p->nChar==0 );
- if( N<0 ){
+ if( N<=0 ){
+ if( N==0 || z[0]==0 ) return;
N = sqlite3Strlen30(z);
}
- if( N==0 || NEVER(z==0) ){
- return;
- }
if( p->nChar+N >= p->nAlloc ){
char *zNew;
if( !p->useMalloc ){
- p->tooBig = 1;
+ p->accError = STRACCUM_TOOBIG;
N = p->nAlloc - p->nChar - 1;
if( N<=0 ){
return;
@@ -20269,7 +20313,7 @@ SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
szNew += N + 1;
if( szNew > p->mxAlloc ){
sqlite3StrAccumReset(p);
- p->tooBig = 1;
+ p->accError = STRACCUM_TOOBIG;
return;
}else{
p->nAlloc = (int)szNew;
@@ -20283,7 +20327,7 @@ SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
if( zOld==0 && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar);
p->zText = zNew;
}else{
- p->mallocFailed = 1;
+ p->accError = STRACCUM_NOMEM;
sqlite3StrAccumReset(p);
return;
}
@@ -20311,7 +20355,7 @@ SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum *p){
if( p->zText ){
memcpy(p->zText, p->zBase, p->nChar+1);
}else{
- p->mallocFailed = 1;
+ p->accError = STRACCUM_NOMEM;
}
}
}
@@ -20342,8 +20386,7 @@ SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum *p, char *zBase, int n, int mx)
p->nAlloc = n;
p->mxAlloc = mx;
p->useMalloc = 1;
- p->tooBig = 0;
- p->mallocFailed = 0;
+ p->accError = 0;
}
/*
@@ -20360,7 +20403,7 @@ SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list a
acc.db = db;
sqlite3VXPrintf(&acc, 1, zFormat, ap);
z = sqlite3StrAccumFinish(&acc);
- if( acc.mallocFailed ){
+ if( acc.accError==STRACCUM_NOMEM ){
db->mallocFailed = 1;
}
return z;
@@ -20557,24 +20600,11 @@ static SQLITE_WSD struct sqlite3PrngType {
} sqlite3Prng;
/*
-** Get a single 8-bit random value from the RC4 PRNG. The Mutex
-** must be held while executing this routine.
-**
-** Why not just use a library random generator like lrand48() for this?
-** Because the OP_NewRowid opcode in the VDBE depends on having a very
-** good source of random numbers. The lrand48() library function may
-** well be good enough. But maybe not. Or maybe lrand48() has some
-** subtle problems on some systems that could cause problems. It is hard
-** to know. To minimize the risk of problems due to bad lrand48()
-** implementations, SQLite uses this random number generator based
-** on RC4, which we know works very well.
-**
-** (Later): Actually, OP_NewRowid does not depend on a good source of
-** randomness any more. But we will leave this code in all the same.
+** Return N random bytes.
*/
-static u8 randomByte(void){
+SQLITE_API void sqlite3_randomness(int N, void *pBuf){
unsigned char t;
-
+ unsigned char *zBuf = pBuf;
/* The "wsdPrng" macro will resolve to the pseudo-random number generator
** state vector. If writable static data is unsupported on the target,
@@ -20589,6 +20619,10 @@ static u8 randomByte(void){
# define wsdPrng sqlite3Prng
#endif
+#if SQLITE_THREADSAFE
+ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG);
+ sqlite3_mutex_enter(mutex);
+#endif
/* Initialize the state of the random number generator once,
** the first time this routine is called. The seed value does
@@ -20617,28 +20651,14 @@ static u8 randomByte(void){
wsdPrng.isInit = 1;
}
- /* Generate and return single random byte
- */
- wsdPrng.i++;
- t = wsdPrng.s[wsdPrng.i];
- wsdPrng.j += t;
- wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j];
- wsdPrng.s[wsdPrng.j] = t;
- t += wsdPrng.s[wsdPrng.i];
- return wsdPrng.s[t];
-}
-
-/*
-** Return N random bytes.
-*/
-SQLITE_API void sqlite3_randomness(int N, void *pBuf){
- unsigned char *zBuf = pBuf;
-#if SQLITE_THREADSAFE
- sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG);
-#endif
- sqlite3_mutex_enter(mutex);
while( N-- ){
- *(zBuf++) = randomByte();
+ wsdPrng.i++;
+ t = wsdPrng.s[wsdPrng.i];
+ wsdPrng.j += t;
+ wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j];
+ wsdPrng.s[wsdPrng.j] = t;
+ t += wsdPrng.s[wsdPrng.i];
+ *(zBuf++) = wsdPrng.s[t];
}
sqlite3_mutex_leave(mutex);
}
@@ -21126,32 +21146,6 @@ SQLITE_PRIVATE char *sqlite3Utf16to8(sqlite3 *db, const void *z, int nByte, u8 e
}
/*
-** Convert a UTF-8 string to the UTF-16 encoding specified by parameter
-** enc. A pointer to the new string is returned, and the value of *pnOut
-** is set to the length of the returned string in bytes. The call should
-** arrange to call sqlite3DbFree() on the returned pointer when it is
-** no longer required.
-**
-** If a malloc failure occurs, NULL is returned and the db.mallocFailed
-** flag set.
-*/
-#ifdef SQLITE_ENABLE_STAT3
-SQLITE_PRIVATE char *sqlite3Utf8to16(sqlite3 *db, u8 enc, char *z, int n, int *pnOut){
- Mem m;
- memset(&m, 0, sizeof(m));
- m.db = db;
- sqlite3VdbeMemSetStr(&m, z, n, SQLITE_UTF8, SQLITE_STATIC);
- if( sqlite3VdbeMemTranslate(&m, enc) ){
- assert( db->mallocFailed );
- return 0;
- }
- assert( m.z==m.zMalloc );
- *pnOut = m.n;
- return m.z;
-}
-#endif
-
-/*
** zIn is a UTF-16 encoded unicode string at least nChar characters long.
** Return the number of bytes in the first nChar unicode characters
** in pZ. nChar must be non-negative.
@@ -21426,7 +21420,8 @@ SQLITE_PRIVATE int sqlite3Dequote(char *z){
case '[': quote = ']'; break; /* For MS SqlServer compatibility */
default: return -1;
}
- for(i=1, j=0; ALWAYS(z[i]); i++){
+ for(i=1, j=0;; i++){
+ assert( z[i] );
if( z[i]==quote ){
if( z[i+1]==quote ){
z[j++] = quote;
@@ -22441,6 +22436,83 @@ SQLITE_PRIVATE void sqlite3FileSuffix3(const char *zBaseFilename, char *z){
}
#endif
+/*
+** Find (an approximate) sum of two LogEst values. This computation is
+** not a simple "+" operator because LogEst is stored as a logarithmic
+** value.
+**
+*/
+SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst a, LogEst b){
+ static const unsigned char x[] = {
+ 10, 10, /* 0,1 */
+ 9, 9, /* 2,3 */
+ 8, 8, /* 4,5 */
+ 7, 7, 7, /* 6,7,8 */
+ 6, 6, 6, /* 9,10,11 */
+ 5, 5, 5, /* 12-14 */
+ 4, 4, 4, 4, /* 15-18 */
+ 3, 3, 3, 3, 3, 3, /* 19-24 */
+ 2, 2, 2, 2, 2, 2, 2, /* 25-31 */
+ };
+ if( a>=b ){
+ if( a>b+49 ) return a;
+ if( a>b+31 ) return a+1;
+ return a+x[a-b];
+ }else{
+ if( b>a+49 ) return b;
+ if( b>a+31 ) return b+1;
+ return b+x[b-a];
+ }
+}
+
+/*
+** Convert an integer into a LogEst. In other words, compute a
+** good approximatation for 10*log2(x).
+*/
+SQLITE_PRIVATE LogEst sqlite3LogEst(u64 x){
+ static LogEst a[] = { 0, 2, 3, 5, 6, 7, 8, 9 };
+ LogEst y = 40;
+ if( x<8 ){
+ if( x<2 ) return 0;
+ while( x<8 ){ y -= 10; x <<= 1; }
+ }else{
+ while( x>255 ){ y += 40; x >>= 4; }
+ while( x>15 ){ y += 10; x >>= 1; }
+ }
+ return a[x&7] + y - 10;
+}
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+/*
+** Convert a double into a LogEst
+** In other words, compute an approximation for 10*log2(x).
+*/
+SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double x){
+ u64 a;
+ LogEst e;
+ assert( sizeof(x)==8 && sizeof(a)==8 );
+ if( x<=1 ) return 0;
+ if( x<=2000000000 ) return sqlite3LogEst((u64)x);
+ memcpy(&a, &x, 8);
+ e = (a>>52) - 1022;
+ return e*10;
+}
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+/*
+** Convert a LogEst into an integer.
+*/
+SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst x){
+ u64 n;
+ if( x<10 ) return 1;
+ n = x%10;
+ x /= 10;
+ if( n>=5 ) n -= 2;
+ else if( n>=1 ) n -= 1;
+ if( x>=3 ) return (n+8)<<(x-3);
+ return (n+8)>>(3-x);
+}
+
/************** End of util.c ************************************************/
/************** Begin file hash.c ********************************************/
/*
@@ -22731,78 +22803,78 @@ SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, int nKey, voi
#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
static const char *const azName[] = { "?",
- /* 1 */ "Goto",
- /* 2 */ "Gosub",
- /* 3 */ "Return",
- /* 4 */ "Yield",
- /* 5 */ "HaltIfNull",
- /* 6 */ "Halt",
- /* 7 */ "Integer",
- /* 8 */ "Int64",
- /* 9 */ "String",
- /* 10 */ "Null",
- /* 11 */ "Blob",
- /* 12 */ "Variable",
- /* 13 */ "Move",
- /* 14 */ "Copy",
- /* 15 */ "SCopy",
- /* 16 */ "ResultRow",
- /* 17 */ "CollSeq",
- /* 18 */ "Function",
+ /* 1 */ "Function",
+ /* 2 */ "Savepoint",
+ /* 3 */ "AutoCommit",
+ /* 4 */ "Transaction",
+ /* 5 */ "SorterNext",
+ /* 6 */ "Prev",
+ /* 7 */ "Next",
+ /* 8 */ "AggStep",
+ /* 9 */ "Checkpoint",
+ /* 10 */ "JournalMode",
+ /* 11 */ "Vacuum",
+ /* 12 */ "VFilter",
+ /* 13 */ "VUpdate",
+ /* 14 */ "Goto",
+ /* 15 */ "Gosub",
+ /* 16 */ "Return",
+ /* 17 */ "Yield",
+ /* 18 */ "HaltIfNull",
/* 19 */ "Not",
- /* 20 */ "AddImm",
- /* 21 */ "MustBeInt",
- /* 22 */ "RealAffinity",
- /* 23 */ "Permutation",
- /* 24 */ "Compare",
- /* 25 */ "Jump",
- /* 26 */ "Once",
- /* 27 */ "If",
- /* 28 */ "IfNot",
- /* 29 */ "Column",
- /* 30 */ "Affinity",
- /* 31 */ "MakeRecord",
- /* 32 */ "Count",
- /* 33 */ "Savepoint",
- /* 34 */ "AutoCommit",
- /* 35 */ "Transaction",
- /* 36 */ "ReadCookie",
- /* 37 */ "SetCookie",
- /* 38 */ "VerifyCookie",
- /* 39 */ "OpenRead",
- /* 40 */ "OpenWrite",
- /* 41 */ "OpenAutoindex",
- /* 42 */ "OpenEphemeral",
- /* 43 */ "SorterOpen",
- /* 44 */ "OpenPseudo",
- /* 45 */ "Close",
- /* 46 */ "SeekLt",
- /* 47 */ "SeekLe",
- /* 48 */ "SeekGe",
- /* 49 */ "SeekGt",
- /* 50 */ "Seek",
- /* 51 */ "NotFound",
- /* 52 */ "Found",
- /* 53 */ "IsUnique",
- /* 54 */ "NotExists",
- /* 55 */ "Sequence",
- /* 56 */ "NewRowid",
- /* 57 */ "Insert",
- /* 58 */ "InsertInt",
- /* 59 */ "Delete",
- /* 60 */ "ResetCount",
- /* 61 */ "SorterCompare",
- /* 62 */ "SorterData",
- /* 63 */ "RowKey",
- /* 64 */ "RowData",
- /* 65 */ "Rowid",
- /* 66 */ "NullRow",
- /* 67 */ "Last",
+ /* 20 */ "Halt",
+ /* 21 */ "Integer",
+ /* 22 */ "Int64",
+ /* 23 */ "String",
+ /* 24 */ "Null",
+ /* 25 */ "Blob",
+ /* 26 */ "Variable",
+ /* 27 */ "Move",
+ /* 28 */ "Copy",
+ /* 29 */ "SCopy",
+ /* 30 */ "ResultRow",
+ /* 31 */ "CollSeq",
+ /* 32 */ "AddImm",
+ /* 33 */ "MustBeInt",
+ /* 34 */ "RealAffinity",
+ /* 35 */ "Permutation",
+ /* 36 */ "Compare",
+ /* 37 */ "Jump",
+ /* 38 */ "Once",
+ /* 39 */ "If",
+ /* 40 */ "IfNot",
+ /* 41 */ "Column",
+ /* 42 */ "Affinity",
+ /* 43 */ "MakeRecord",
+ /* 44 */ "Count",
+ /* 45 */ "ReadCookie",
+ /* 46 */ "SetCookie",
+ /* 47 */ "VerifyCookie",
+ /* 48 */ "OpenRead",
+ /* 49 */ "OpenWrite",
+ /* 50 */ "OpenAutoindex",
+ /* 51 */ "OpenEphemeral",
+ /* 52 */ "SorterOpen",
+ /* 53 */ "OpenPseudo",
+ /* 54 */ "Close",
+ /* 55 */ "SeekLt",
+ /* 56 */ "SeekLe",
+ /* 57 */ "SeekGe",
+ /* 58 */ "SeekGt",
+ /* 59 */ "Seek",
+ /* 60 */ "NotFound",
+ /* 61 */ "Found",
+ /* 62 */ "IsUnique",
+ /* 63 */ "NotExists",
+ /* 64 */ "Sequence",
+ /* 65 */ "NewRowid",
+ /* 66 */ "Insert",
+ /* 67 */ "InsertInt",
/* 68 */ "Or",
/* 69 */ "And",
- /* 70 */ "SorterSort",
- /* 71 */ "Sort",
- /* 72 */ "Rewind",
+ /* 70 */ "Delete",
+ /* 71 */ "ResetCount",
+ /* 72 */ "SorterCompare",
/* 73 */ "IsNull",
/* 74 */ "NotNull",
/* 75 */ "Ne",
@@ -22811,7 +22883,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 78 */ "Le",
/* 79 */ "Lt",
/* 80 */ "Ge",
- /* 81 */ "SorterNext",
+ /* 81 */ "SorterData",
/* 82 */ "BitAnd",
/* 83 */ "BitOr",
/* 84 */ "ShiftLeft",
@@ -22822,55 +22894,55 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 89 */ "Divide",
/* 90 */ "Remainder",
/* 91 */ "Concat",
- /* 92 */ "Prev",
+ /* 92 */ "RowKey",
/* 93 */ "BitNot",
/* 94 */ "String8",
- /* 95 */ "Next",
- /* 96 */ "SorterInsert",
- /* 97 */ "IdxInsert",
- /* 98 */ "IdxDelete",
- /* 99 */ "IdxRowid",
- /* 100 */ "IdxLT",
- /* 101 */ "IdxGE",
- /* 102 */ "Destroy",
- /* 103 */ "Clear",
- /* 104 */ "CreateIndex",
- /* 105 */ "CreateTable",
- /* 106 */ "ParseSchema",
- /* 107 */ "LoadAnalysis",
- /* 108 */ "DropTable",
- /* 109 */ "DropIndex",
- /* 110 */ "DropTrigger",
- /* 111 */ "IntegrityCk",
- /* 112 */ "RowSetAdd",
- /* 113 */ "RowSetRead",
- /* 114 */ "RowSetTest",
- /* 115 */ "Program",
- /* 116 */ "Param",
- /* 117 */ "FkCounter",
- /* 118 */ "FkIfZero",
- /* 119 */ "MemMax",
- /* 120 */ "IfPos",
- /* 121 */ "IfNeg",
- /* 122 */ "IfZero",
- /* 123 */ "AggStep",
- /* 124 */ "AggFinal",
- /* 125 */ "Checkpoint",
- /* 126 */ "JournalMode",
- /* 127 */ "Vacuum",
- /* 128 */ "IncrVacuum",
- /* 129 */ "Expire",
+ /* 95 */ "RowData",
+ /* 96 */ "Rowid",
+ /* 97 */ "NullRow",
+ /* 98 */ "Last",
+ /* 99 */ "SorterSort",
+ /* 100 */ "Sort",
+ /* 101 */ "Rewind",
+ /* 102 */ "SorterInsert",
+ /* 103 */ "IdxInsert",
+ /* 104 */ "IdxDelete",
+ /* 105 */ "IdxRowid",
+ /* 106 */ "IdxLT",
+ /* 107 */ "IdxGE",
+ /* 108 */ "Destroy",
+ /* 109 */ "Clear",
+ /* 110 */ "CreateIndex",
+ /* 111 */ "CreateTable",
+ /* 112 */ "ParseSchema",
+ /* 113 */ "LoadAnalysis",
+ /* 114 */ "DropTable",
+ /* 115 */ "DropIndex",
+ /* 116 */ "DropTrigger",
+ /* 117 */ "IntegrityCk",
+ /* 118 */ "RowSetAdd",
+ /* 119 */ "RowSetRead",
+ /* 120 */ "RowSetTest",
+ /* 121 */ "Program",
+ /* 122 */ "Param",
+ /* 123 */ "FkCounter",
+ /* 124 */ "FkIfZero",
+ /* 125 */ "MemMax",
+ /* 126 */ "IfPos",
+ /* 127 */ "IfNeg",
+ /* 128 */ "IfZero",
+ /* 129 */ "AggFinal",
/* 130 */ "Real",
- /* 131 */ "TableLock",
- /* 132 */ "VBegin",
- /* 133 */ "VCreate",
- /* 134 */ "VDestroy",
- /* 135 */ "VOpen",
- /* 136 */ "VFilter",
- /* 137 */ "VColumn",
- /* 138 */ "VNext",
- /* 139 */ "VRename",
- /* 140 */ "VUpdate",
+ /* 131 */ "IncrVacuum",
+ /* 132 */ "Expire",
+ /* 133 */ "TableLock",
+ /* 134 */ "VBegin",
+ /* 135 */ "VCreate",
+ /* 136 */ "VDestroy",
+ /* 137 */ "VOpen",
+ /* 138 */ "VColumn",
+ /* 139 */ "VNext",
+ /* 140 */ "VRename",
/* 141 */ "ToText",
/* 142 */ "ToBlob",
/* 143 */ "ToNumeric",
@@ -22935,13 +23007,6 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
*/
#if SQLITE_OS_UNIX /* This file is used on unix only */
-/* Use posix_fallocate() if it is available
-*/
-#if !defined(HAVE_POSIX_FALLOCATE) \
- && (_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L)
-# define HAVE_POSIX_FALLOCATE 1
-#endif
-
/*
** There are various methods for file locking used for concurrency
** control:
@@ -23114,11 +23179,13 @@ struct unixFile {
const char *zPath; /* Name of the file */
unixShm *pShm; /* Shared memory segment information */
int szChunk; /* Configured by FCNTL_CHUNK_SIZE */
+#if SQLITE_MAX_MMAP_SIZE>0
int nFetchOut; /* Number of outstanding xFetch refs */
sqlite3_int64 mmapSize; /* Usable size of mapping at pMapRegion */
sqlite3_int64 mmapSizeActual; /* Actual size of mapping at pMapRegion */
sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */
void *pMapRegion; /* Memory mapped region */
+#endif
#ifdef __QNXNTO__
int sectorSize; /* Device sector size */
int deviceCharacteristics; /* Precomputed device characteristics */
@@ -23553,6 +23620,7 @@ static struct unix_syscall {
{ "fchown", (sqlite3_syscall_ptr)posixFchown, 0 },
#define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent)
+#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
{ "mmap", (sqlite3_syscall_ptr)mmap, 0 },
#define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[21].pCurrent)
@@ -23565,6 +23633,7 @@ static struct unix_syscall {
{ "mremap", (sqlite3_syscall_ptr)0, 0 },
#endif
#define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[23].pCurrent)
+#endif
}; /* End of the overrideable system calls */
@@ -23652,6 +23721,15 @@ static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){
}
/*
+** Do not accept any file descriptor less than this value, in order to avoid
+** opening database file using file descriptors that are commonly used for
+** standard input, output, and error.
+*/
+#ifndef SQLITE_MINIMUM_FILE_DESCRIPTOR
+# define SQLITE_MINIMUM_FILE_DESCRIPTOR 3
+#endif
+
+/*
** Invoke open(). Do so multiple times, until it either succeeds or
** fails for some reason other than EINTR.
**
@@ -23671,13 +23749,23 @@ static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){
static int robust_open(const char *z, int f, mode_t m){
int fd;
mode_t m2 = m ? m : SQLITE_DEFAULT_FILE_PERMISSIONS;
- do{
+ while(1){
#if defined(O_CLOEXEC)
fd = osOpen(z,f|O_CLOEXEC,m2);
#else
fd = osOpen(z,f,m2);
#endif
- }while( fd<0 && errno==EINTR );
+ if( fd<0 ){
+ if( errno==EINTR ) continue;
+ break;
+ }
+ if( fd>=SQLITE_MINIMUM_FILE_DESCRIPTOR ) break;
+ osClose(fd);
+ sqlite3_log(SQLITE_WARNING,
+ "attempt to open \"%s\" as file descriptor %d", z, fd);
+ fd = -1;
+ if( osOpen("/dev/null", f, m)<0 ) break;
+ }
if( fd>=0 ){
if( m!=0 ){
struct stat statbuf;
@@ -24975,8 +25063,10 @@ static int unixUnlock(sqlite3_file *id, int eFileLock){
return posixUnlock(id, eFileLock, 0);
}
+#if SQLITE_MAX_MMAP_SIZE>0
static int unixMapfile(unixFile *pFd, i64 nByte);
static void unixUnmapfile(unixFile *pFd);
+#endif
/*
** This function performs the parts of the "close file" operation
@@ -24990,7 +25080,9 @@ static void unixUnmapfile(unixFile *pFd);
*/
static int closeUnixFile(sqlite3_file *id){
unixFile *pFile = (unixFile*)id;
+#if SQLITE_MAX_MMAP_SIZE>0
unixUnmapfile(pFile);
+#endif
if( pFile->h>=0 ){
robust_close(pFile, pFile->h, __LINE__);
pFile->h = -1;
@@ -26195,6 +26287,7 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
#endif
TIMER_START;
assert( cnt==(cnt&0x1ffff) );
+ assert( id->h>2 );
cnt &= 0x1ffff;
do{
#if defined(USE_PREAD)
@@ -26309,6 +26402,7 @@ static int seekAndWriteFd(
int rc = 0; /* Value returned by system call */
assert( nBuf==(nBuf&0x1ffff) );
+ assert( fd>2 );
nBuf &= 0x1ffff;
TIMER_START;
@@ -26694,6 +26788,7 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){
}
#endif
+#if SQLITE_MAX_MMAP_SIZE>0
/* If the file was just truncated to a size smaller than the currently
** mapped region, reduce the effective mapping size as well. SQLite will
** use read() and write() to access data beyond this point from now on.
@@ -26701,6 +26796,7 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){
if( nByte<pFile->mmapSize ){
pFile->mmapSize = nByte;
}
+#endif
return SQLITE_OK;
}
@@ -26790,6 +26886,7 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
}
}
+#if SQLITE_MAX_MMAP_SIZE>0
if( pFile->mmapSizeMax>0 && nByte>pFile->mmapSize ){
int rc;
if( pFile->szChunk<=0 ){
@@ -26802,6 +26899,7 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
rc = unixMapfile(pFile, nByte);
return rc;
}
+#endif
return SQLITE_OK;
}
@@ -26870,18 +26968,24 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
}
return SQLITE_OK;
}
+#if SQLITE_MAX_MMAP_SIZE>0
case SQLITE_FCNTL_MMAP_SIZE: {
i64 newLimit = *(i64*)pArg;
+ int rc = SQLITE_OK;
if( newLimit>sqlite3GlobalConfig.mxMmap ){
newLimit = sqlite3GlobalConfig.mxMmap;
}
*(i64*)pArg = pFile->mmapSizeMax;
- if( newLimit>=0 ){
+ if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){
pFile->mmapSizeMax = newLimit;
- if( newLimit<pFile->mmapSize ) pFile->mmapSize = newLimit;
+ if( pFile->mmapSize>0 ){
+ unixUnmapfile(pFile);
+ rc = unixMapfile(pFile, -1);
+ }
}
- return SQLITE_OK;
+ return rc;
}
+#endif
#ifdef SQLITE_DEBUG
/* The pager calls this method to signal that it has done
** a rollback and that the database is therefore unchanged and
@@ -27692,22 +27796,20 @@ static int unixShmUnmap(
# define unixShmUnmap 0
#endif /* #ifndef SQLITE_OMIT_WAL */
+#if SQLITE_MAX_MMAP_SIZE>0
/*
** If it is currently memory mapped, unmap file pFd.
*/
static void unixUnmapfile(unixFile *pFd){
assert( pFd->nFetchOut==0 );
-#if SQLITE_MAX_MMAP_SIZE>0
if( pFd->pMapRegion ){
osMunmap(pFd->pMapRegion, pFd->mmapSizeActual);
pFd->pMapRegion = 0;
pFd->mmapSize = 0;
pFd->mmapSizeActual = 0;
}
-#endif
}
-#if SQLITE_MAX_MMAP_SIZE>0
/*
** Return the system page size.
*/
@@ -27720,9 +27822,7 @@ static int unixGetPagesize(void){
return (int)sysconf(_SC_PAGESIZE);
#endif
}
-#endif /* SQLITE_MAX_MMAP_SIZE>0 */
-#if SQLITE_MAX_MMAP_SIZE>0
/*
** Attempt to set the size of the memory mapping maintained by file
** descriptor pFd to nNew bytes. Any existing mapping is discarded.
@@ -27807,7 +27907,6 @@ static void unixRemapfile(
pFd->pMapRegion = (void *)pNew;
pFd->mmapSize = pFd->mmapSizeActual = nNew;
}
-#endif
/*
** Memory map or remap the file opened by file-descriptor pFd (if the file
@@ -27826,7 +27925,6 @@ static void unixRemapfile(
** code otherwise.
*/
static int unixMapfile(unixFile *pFd, i64 nByte){
-#if SQLITE_MAX_MMAP_SIZE>0
i64 nMap = nByte;
int rc;
@@ -27852,10 +27950,10 @@ static int unixMapfile(unixFile *pFd, i64 nByte){
unixUnmapfile(pFd);
}
}
-#endif
return SQLITE_OK;
}
+#endif /* SQLITE_MAX_MMAP_SIZE>0 */
/*
** If possible, return a pointer to a mapping of file fd starting at offset
@@ -27904,6 +28002,7 @@ static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){
unixFile *pFd = (unixFile *)fd; /* The underlying database file */
UNUSED_PARAMETER(iOff);
+#if SQLITE_MAX_MMAP_SIZE>0
/* If p==0 (unmap the entire file) then there must be no outstanding
** xFetch references. Or, if p!=0 (meaning it is an xFetch reference),
** then there must be at least one outstanding. */
@@ -27919,6 +28018,7 @@ static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){
}
assert( pFd->nFetchOut>=0 );
+#endif
return SQLITE_OK;
}
@@ -28250,7 +28350,9 @@ static int fillInUnixFile(
pNew->pVfs = pVfs;
pNew->zPath = zFilename;
pNew->ctrlFlags = (u8)ctrlFlags;
+#if SQLITE_MAX_MMAP_SIZE>0
pNew->mmapSizeMax = sqlite3GlobalConfig.szMmap;
+#endif
if( sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0),
"psow", SQLITE_POWERSAFE_OVERWRITE) ){
pNew->ctrlFlags |= UNIXFILE_PSOW;
@@ -28407,6 +28509,7 @@ static const char *unixTempFileDir(void){
static const char *azDirs[] = {
0,
0,
+ 0,
"/var/tmp",
"/usr/tmp",
"/tmp",
@@ -28417,7 +28520,8 @@ static const char *unixTempFileDir(void){
const char *zDir = 0;
azDirs[0] = sqlite3_temp_directory;
- if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
+ if( !azDirs[1] ) azDirs[1] = getenv("SQLITE_TMPDIR");
+ if( !azDirs[2] ) azDirs[2] = getenv("TMPDIR");
for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
if( zDir==0 ) continue;
if( osStat(zDir, &buf) ) continue;
@@ -30530,6 +30634,7 @@ SQLITE_API int sqlite3_os_end(void){
#ifdef __CYGWIN__
# include <sys/cygwin.h>
+# include <errno.h> /* amalgamator: keep */
#endif
/*
@@ -30750,7 +30855,7 @@ SQLITE_API int sqlite3_open_file_count = 0;
** available in Windows platforms based on the NT kernel.
*/
#if !SQLITE_OS_WINNT && !defined(SQLITE_OMIT_WAL)
-# error "WAL mode requires support from the Windows NT kernel, compile\
+# error "WAL mode requires support from the Windows NT kernel, compile\
with SQLITE_OMIT_WAL."
#endif
@@ -30758,7 +30863,7 @@ SQLITE_API int sqlite3_open_file_count = 0;
** Are most of the Win32 ANSI APIs available (i.e. with certain exceptions
** based on the sub-platform)?
*/
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(SQLITE_WIN32_NO_ANSI)
# define SQLITE_WIN32_HAS_ANSI
#endif
@@ -30766,11 +30871,88 @@ SQLITE_API int sqlite3_open_file_count = 0;
** Are most of the Win32 Unicode APIs available (i.e. with certain exceptions
** based on the sub-platform)?
*/
-#if SQLITE_OS_WINCE || SQLITE_OS_WINNT || SQLITE_OS_WINRT
+#if (SQLITE_OS_WINCE || SQLITE_OS_WINNT || SQLITE_OS_WINRT) && \
+ !defined(SQLITE_WIN32_NO_WIDE)
# define SQLITE_WIN32_HAS_WIDE
#endif
/*
+** Make sure at least one set of Win32 APIs is available.
+*/
+#if !defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_WIN32_HAS_WIDE)
+# error "At least one of SQLITE_WIN32_HAS_ANSI and SQLITE_WIN32_HAS_WIDE\
+ must be defined."
+#endif
+
+/*
+** Maximum pathname length (in chars) for Win32. This should normally be
+** MAX_PATH.
+*/
+#ifndef SQLITE_WIN32_MAX_PATH_CHARS
+# define SQLITE_WIN32_MAX_PATH_CHARS (MAX_PATH)
+#endif
+
+/*
+** Maximum pathname length (in chars) for WinNT. This should normally be
+** 32767.
+*/
+#ifndef SQLITE_WINNT_MAX_PATH_CHARS
+# define SQLITE_WINNT_MAX_PATH_CHARS (32767)
+#endif
+
+/*
+** Maximum pathname length (in bytes) for Win32. The MAX_PATH macro is in
+** characters, so we allocate 3 bytes per character assuming worst-case of
+** 4-bytes-per-character for UTF8.
+*/
+#ifndef SQLITE_WIN32_MAX_PATH_BYTES
+# define SQLITE_WIN32_MAX_PATH_BYTES (SQLITE_WIN32_MAX_PATH_CHARS*4)
+#endif
+
+/*
+** Maximum pathname length (in bytes) for WinNT. This should normally be
+** 32767 * sizeof(WCHAR).
+*/
+#ifndef SQLITE_WINNT_MAX_PATH_BYTES
+# define SQLITE_WINNT_MAX_PATH_BYTES \
+ (sizeof(WCHAR) * SQLITE_WINNT_MAX_PATH_CHARS)
+#endif
+
+/*
+** Maximum error message length (in chars) for WinRT.
+*/
+#ifndef SQLITE_WIN32_MAX_ERRMSG_CHARS
+# define SQLITE_WIN32_MAX_ERRMSG_CHARS (1024)
+#endif
+
+/*
+** Returns non-zero if the character should be treated as a directory
+** separator.
+*/
+#ifndef winIsDirSep
+# define winIsDirSep(a) (((a) == '/') || ((a) == '\\'))
+#endif
+
+/*
+** This macro is used when a local variable is set to a value that is
+** [sometimes] not used by the code (e.g. via conditional compilation).
+*/
+#ifndef UNUSED_VARIABLE_VALUE
+# define UNUSED_VARIABLE_VALUE(x) (void)(x)
+#endif
+
+/*
+** Returns the string that should be used as the directory separator.
+*/
+#ifndef winGetDirDep
+# ifdef __CYGWIN__
+# define winGetDirDep() "/"
+# else
+# define winGetDirDep() "\\"
+# endif
+#endif
+
+/*
** Do we need to manually define the Win32 file mapping APIs for use with WAL
** mode (e.g. these APIs are available in the Windows CE SDK; however, they
** are not present in the header file)?
@@ -30806,13 +30988,6 @@ WINBASEAPI BOOL WINAPI UnmapViewOfFile(LPCVOID);
#endif /* SQLITE_WIN32_FILEMAPPING_API && !defined(SQLITE_OMIT_WAL) */
/*
-** Macro to find the minimum of two numeric values.
-*/
-#ifndef MIN
-# define MIN(x,y) ((x)<(y)?(x):(y))
-#endif
-
-/*
** Some Microsoft compilers lack this definition.
*/
#ifndef INVALID_FILE_ATTRIBUTES
@@ -30828,7 +31003,7 @@ WINBASEAPI BOOL WINAPI UnmapViewOfFile(LPCVOID);
#endif
#ifndef SQLITE_OMIT_WAL
-/* Forward references */
+/* Forward references to structures used for WAL */
typedef struct winShm winShm; /* A connection to shared-memory */
typedef struct winShmNode winShmNode; /* A region of shared-memory */
#endif
@@ -30958,6 +31133,7 @@ struct winFile {
# define SQLITE_WIN32_HEAP_FLAGS (0)
#endif
+
/*
** The winMemData structure stores information required by the Win32-specific
** sqlite3_mem_methods implementation.
@@ -31015,7 +31191,8 @@ SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetWin32(void);
*/
#ifdef SQLITE_TEST
SQLITE_API int sqlite3_os_type = 0;
-#else
+#elif !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \
+ defined(SQLITE_WIN32_HAS_ANSI) && defined(SQLITE_WIN32_HAS_WIDE)
static int sqlite3_os_type = 0;
#endif
@@ -31330,10 +31507,19 @@ static struct win_syscall {
#define osGetVersionExA ((BOOL(WINAPI*)( \
LPOSVERSIONINFOA))aSyscall[34].pCurrent)
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
+ { "GetVersionExW", (SYSCALL)GetVersionExW, 0 },
+#else
+ { "GetVersionExW", (SYSCALL)0, 0 },
+#endif
+
+#define osGetVersionExW ((BOOL(WINAPI*)( \
+ LPOSVERSIONINFOW))aSyscall[35].pCurrent)
+
{ "HeapAlloc", (SYSCALL)HeapAlloc, 0 },
#define osHeapAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD, \
- SIZE_T))aSyscall[35].pCurrent)
+ SIZE_T))aSyscall[36].pCurrent)
#if !SQLITE_OS_WINRT
{ "HeapCreate", (SYSCALL)HeapCreate, 0 },
@@ -31342,7 +31528,7 @@ static struct win_syscall {
#endif
#define osHeapCreate ((HANDLE(WINAPI*)(DWORD,SIZE_T, \
- SIZE_T))aSyscall[36].pCurrent)
+ SIZE_T))aSyscall[37].pCurrent)
#if !SQLITE_OS_WINRT
{ "HeapDestroy", (SYSCALL)HeapDestroy, 0 },
@@ -31350,21 +31536,21 @@ static struct win_syscall {
{ "HeapDestroy", (SYSCALL)0, 0 },
#endif
-#define osHeapDestroy ((BOOL(WINAPI*)(HANDLE))aSyscall[37].pCurrent)
+#define osHeapDestroy ((BOOL(WINAPI*)(HANDLE))aSyscall[38].pCurrent)
{ "HeapFree", (SYSCALL)HeapFree, 0 },
-#define osHeapFree ((BOOL(WINAPI*)(HANDLE,DWORD,LPVOID))aSyscall[38].pCurrent)
+#define osHeapFree ((BOOL(WINAPI*)(HANDLE,DWORD,LPVOID))aSyscall[39].pCurrent)
{ "HeapReAlloc", (SYSCALL)HeapReAlloc, 0 },
#define osHeapReAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD,LPVOID, \
- SIZE_T))aSyscall[39].pCurrent)
+ SIZE_T))aSyscall[40].pCurrent)
{ "HeapSize", (SYSCALL)HeapSize, 0 },
#define osHeapSize ((SIZE_T(WINAPI*)(HANDLE,DWORD, \
- LPCVOID))aSyscall[40].pCurrent)
+ LPCVOID))aSyscall[41].pCurrent)
#if !SQLITE_OS_WINRT
{ "HeapValidate", (SYSCALL)HeapValidate, 0 },
@@ -31373,7 +31559,7 @@ static struct win_syscall {
#endif
#define osHeapValidate ((BOOL(WINAPI*)(HANDLE,DWORD, \
- LPCVOID))aSyscall[41].pCurrent)
+ LPCVOID))aSyscall[42].pCurrent)
#if defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
{ "LoadLibraryA", (SYSCALL)LoadLibraryA, 0 },
@@ -31381,7 +31567,7 @@ static struct win_syscall {
{ "LoadLibraryA", (SYSCALL)0, 0 },
#endif
-#define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[42].pCurrent)
+#define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[43].pCurrent)
#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
!defined(SQLITE_OMIT_LOAD_EXTENSION)
@@ -31390,7 +31576,7 @@ static struct win_syscall {
{ "LoadLibraryW", (SYSCALL)0, 0 },
#endif
-#define osLoadLibraryW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[43].pCurrent)
+#define osLoadLibraryW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[44].pCurrent)
#if !SQLITE_OS_WINRT
{ "LocalFree", (SYSCALL)LocalFree, 0 },
@@ -31398,7 +31584,7 @@ static struct win_syscall {
{ "LocalFree", (SYSCALL)0, 0 },
#endif
-#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[44].pCurrent)
+#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[45].pCurrent)
#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
{ "LockFile", (SYSCALL)LockFile, 0 },
@@ -31408,7 +31594,7 @@ static struct win_syscall {
#ifndef osLockFile
#define osLockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
- DWORD))aSyscall[45].pCurrent)
+ DWORD))aSyscall[46].pCurrent)
#endif
#if !SQLITE_OS_WINCE
@@ -31419,7 +31605,7 @@ static struct win_syscall {
#ifndef osLockFileEx
#define osLockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD,DWORD, \
- LPOVERLAPPED))aSyscall[46].pCurrent)
+ LPOVERLAPPED))aSyscall[47].pCurrent)
#endif
#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL))
@@ -31429,26 +31615,26 @@ static struct win_syscall {
#endif
#define osMapViewOfFile ((LPVOID(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
- SIZE_T))aSyscall[47].pCurrent)
+ SIZE_T))aSyscall[48].pCurrent)
{ "MultiByteToWideChar", (SYSCALL)MultiByteToWideChar, 0 },
#define osMultiByteToWideChar ((int(WINAPI*)(UINT,DWORD,LPCSTR,int,LPWSTR, \
- int))aSyscall[48].pCurrent)
+ int))aSyscall[49].pCurrent)
{ "QueryPerformanceCounter", (SYSCALL)QueryPerformanceCounter, 0 },
#define osQueryPerformanceCounter ((BOOL(WINAPI*)( \
- LARGE_INTEGER*))aSyscall[49].pCurrent)
+ LARGE_INTEGER*))aSyscall[50].pCurrent)
{ "ReadFile", (SYSCALL)ReadFile, 0 },
#define osReadFile ((BOOL(WINAPI*)(HANDLE,LPVOID,DWORD,LPDWORD, \
- LPOVERLAPPED))aSyscall[50].pCurrent)
+ LPOVERLAPPED))aSyscall[51].pCurrent)
{ "SetEndOfFile", (SYSCALL)SetEndOfFile, 0 },
-#define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[51].pCurrent)
+#define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[52].pCurrent)
#if !SQLITE_OS_WINRT
{ "SetFilePointer", (SYSCALL)SetFilePointer, 0 },
@@ -31457,7 +31643,7 @@ static struct win_syscall {
#endif
#define osSetFilePointer ((DWORD(WINAPI*)(HANDLE,LONG,PLONG, \
- DWORD))aSyscall[52].pCurrent)
+ DWORD))aSyscall[53].pCurrent)
#if !SQLITE_OS_WINRT
{ "Sleep", (SYSCALL)Sleep, 0 },
@@ -31465,12 +31651,12 @@ static struct win_syscall {
{ "Sleep", (SYSCALL)0, 0 },
#endif
-#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[53].pCurrent)
+#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[54].pCurrent)
{ "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 },
#define osSystemTimeToFileTime ((BOOL(WINAPI*)(CONST SYSTEMTIME*, \
- LPFILETIME))aSyscall[54].pCurrent)
+ LPFILETIME))aSyscall[55].pCurrent)
#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
{ "UnlockFile", (SYSCALL)UnlockFile, 0 },
@@ -31480,7 +31666,7 @@ static struct win_syscall {
#ifndef osUnlockFile
#define osUnlockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
- DWORD))aSyscall[55].pCurrent)
+ DWORD))aSyscall[56].pCurrent)
#endif
#if !SQLITE_OS_WINCE
@@ -31490,7 +31676,7 @@ static struct win_syscall {
#endif
#define osUnlockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
- LPOVERLAPPED))aSyscall[56].pCurrent)
+ LPOVERLAPPED))aSyscall[57].pCurrent)
#if SQLITE_OS_WINCE || !defined(SQLITE_OMIT_WAL)
{ "UnmapViewOfFile", (SYSCALL)UnmapViewOfFile, 0 },
@@ -31498,17 +31684,17 @@ static struct win_syscall {
{ "UnmapViewOfFile", (SYSCALL)0, 0 },
#endif
-#define osUnmapViewOfFile ((BOOL(WINAPI*)(LPCVOID))aSyscall[57].pCurrent)
+#define osUnmapViewOfFile ((BOOL(WINAPI*)(LPCVOID))aSyscall[58].pCurrent)
{ "WideCharToMultiByte", (SYSCALL)WideCharToMultiByte, 0 },
#define osWideCharToMultiByte ((int(WINAPI*)(UINT,DWORD,LPCWSTR,int,LPSTR,int, \
- LPCSTR,LPBOOL))aSyscall[58].pCurrent)
+ LPCSTR,LPBOOL))aSyscall[59].pCurrent)
{ "WriteFile", (SYSCALL)WriteFile, 0 },
#define osWriteFile ((BOOL(WINAPI*)(HANDLE,LPCVOID,DWORD,LPDWORD, \
- LPOVERLAPPED))aSyscall[59].pCurrent)
+ LPOVERLAPPED))aSyscall[60].pCurrent)
#if SQLITE_OS_WINRT
{ "CreateEventExW", (SYSCALL)CreateEventExW, 0 },
@@ -31517,7 +31703,7 @@ static struct win_syscall {
#endif
#define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \
- DWORD,DWORD))aSyscall[60].pCurrent)
+ DWORD,DWORD))aSyscall[61].pCurrent)
#if !SQLITE_OS_WINRT
{ "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 },
@@ -31526,7 +31712,7 @@ static struct win_syscall {
#endif
#define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \
- DWORD))aSyscall[61].pCurrent)
+ DWORD))aSyscall[62].pCurrent)
#if SQLITE_OS_WINRT
{ "WaitForSingleObjectEx", (SYSCALL)WaitForSingleObjectEx, 0 },
@@ -31535,7 +31721,7 @@ static struct win_syscall {
#endif
#define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \
- BOOL))aSyscall[62].pCurrent)
+ BOOL))aSyscall[63].pCurrent)
#if SQLITE_OS_WINRT
{ "SetFilePointerEx", (SYSCALL)SetFilePointerEx, 0 },
@@ -31544,7 +31730,7 @@ static struct win_syscall {
#endif
#define osSetFilePointerEx ((BOOL(WINAPI*)(HANDLE,LARGE_INTEGER, \
- PLARGE_INTEGER,DWORD))aSyscall[63].pCurrent)
+ PLARGE_INTEGER,DWORD))aSyscall[64].pCurrent)
#if SQLITE_OS_WINRT
{ "GetFileInformationByHandleEx", (SYSCALL)GetFileInformationByHandleEx, 0 },
@@ -31553,7 +31739,7 @@ static struct win_syscall {
#endif
#define osGetFileInformationByHandleEx ((BOOL(WINAPI*)(HANDLE, \
- FILE_INFO_BY_HANDLE_CLASS,LPVOID,DWORD))aSyscall[64].pCurrent)
+ FILE_INFO_BY_HANDLE_CLASS,LPVOID,DWORD))aSyscall[65].pCurrent)
#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL)
{ "MapViewOfFileFromApp", (SYSCALL)MapViewOfFileFromApp, 0 },
@@ -31562,7 +31748,7 @@ static struct win_syscall {
#endif
#define osMapViewOfFileFromApp ((LPVOID(WINAPI*)(HANDLE,ULONG,ULONG64, \
- SIZE_T))aSyscall[65].pCurrent)
+ SIZE_T))aSyscall[66].pCurrent)
#if SQLITE_OS_WINRT
{ "CreateFile2", (SYSCALL)CreateFile2, 0 },
@@ -31571,7 +31757,7 @@ static struct win_syscall {
#endif
#define osCreateFile2 ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD,DWORD, \
- LPCREATEFILE2_EXTENDED_PARAMETERS))aSyscall[66].pCurrent)
+ LPCREATEFILE2_EXTENDED_PARAMETERS))aSyscall[67].pCurrent)
#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_LOAD_EXTENSION)
{ "LoadPackagedLibrary", (SYSCALL)LoadPackagedLibrary, 0 },
@@ -31580,7 +31766,7 @@ static struct win_syscall {
#endif
#define osLoadPackagedLibrary ((HMODULE(WINAPI*)(LPCWSTR, \
- DWORD))aSyscall[67].pCurrent)
+ DWORD))aSyscall[68].pCurrent)
#if SQLITE_OS_WINRT
{ "GetTickCount64", (SYSCALL)GetTickCount64, 0 },
@@ -31588,7 +31774,7 @@ static struct win_syscall {
{ "GetTickCount64", (SYSCALL)0, 0 },
#endif
-#define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[68].pCurrent)
+#define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[69].pCurrent)
#if SQLITE_OS_WINRT
{ "GetNativeSystemInfo", (SYSCALL)GetNativeSystemInfo, 0 },
@@ -31597,7 +31783,7 @@ static struct win_syscall {
#endif
#define osGetNativeSystemInfo ((VOID(WINAPI*)( \
- LPSYSTEM_INFO))aSyscall[69].pCurrent)
+ LPSYSTEM_INFO))aSyscall[70].pCurrent)
#if defined(SQLITE_WIN32_HAS_ANSI)
{ "OutputDebugStringA", (SYSCALL)OutputDebugStringA, 0 },
@@ -31605,7 +31791,7 @@ static struct win_syscall {
{ "OutputDebugStringA", (SYSCALL)0, 0 },
#endif
-#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[70].pCurrent)
+#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[71].pCurrent)
#if defined(SQLITE_WIN32_HAS_WIDE)
{ "OutputDebugStringW", (SYSCALL)OutputDebugStringW, 0 },
@@ -31613,11 +31799,11 @@ static struct win_syscall {
{ "OutputDebugStringW", (SYSCALL)0, 0 },
#endif
-#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[71].pCurrent)
+#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[72].pCurrent)
{ "GetProcessHeap", (SYSCALL)GetProcessHeap, 0 },
-#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[72].pCurrent)
+#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[73].pCurrent)
#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL)
{ "CreateFileMappingFromApp", (SYSCALL)CreateFileMappingFromApp, 0 },
@@ -31626,7 +31812,7 @@ static struct win_syscall {
#endif
#define osCreateFileMappingFromApp ((HANDLE(WINAPI*)(HANDLE, \
- LPSECURITY_ATTRIBUTES,ULONG,ULONG64,LPCWSTR))aSyscall[73].pCurrent)
+ LPSECURITY_ATTRIBUTES,ULONG,ULONG64,LPCWSTR))aSyscall[74].pCurrent)
}; /* End of the overrideable system calls */
@@ -31782,16 +31968,26 @@ SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds){
** WinNT/2K/XP so that we will know whether or not we can safely call
** the LockFileEx() API.
*/
-#if SQLITE_OS_WINCE || SQLITE_OS_WINRT
-# define isNT() (1)
+#ifndef NTDDI_WIN8
+# define NTDDI_WIN8 0x06020000
+#endif
+
+#if SQLITE_OS_WINCE || SQLITE_OS_WINRT || !defined(SQLITE_WIN32_HAS_ANSI)
+# define osIsNT() (1)
#elif !defined(SQLITE_WIN32_HAS_WIDE)
-# define isNT() (0)
+# define osIsNT() (0)
#else
- static int isNT(void){
+ static int osIsNT(void){
if( sqlite3_os_type==0 ){
+#if defined(NTDDI_VERSION) && NTDDI_VERSION >= NTDDI_WIN8
+ OSVERSIONINFOW sInfo;
+ sInfo.dwOSVersionInfoSize = sizeof(sInfo);
+ osGetVersionExW(&sInfo);
+#else
OSVERSIONINFOA sInfo;
sInfo.dwOSVersionInfoSize = sizeof(sInfo);
osGetVersionExA(&sInfo);
+#endif
sqlite3_os_type = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1;
}
return sqlite3_os_type==2;
@@ -31816,7 +32012,7 @@ static void *winMemMalloc(int nBytes){
assert( nBytes>=0 );
p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes);
if( !p ){
- sqlite3_log(SQLITE_NOMEM, "failed to HeapAlloc %u bytes (%d), heap=%p",
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapAlloc %u bytes (%lu), heap=%p",
nBytes, osGetLastError(), (void*)hHeap);
}
return p;
@@ -31837,7 +32033,7 @@ static void winMemFree(void *pPrior){
#endif
if( !pPrior ) return; /* Passing NULL to HeapFree is undefined. */
if( !osHeapFree(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ){
- sqlite3_log(SQLITE_NOMEM, "failed to HeapFree block %p (%d), heap=%p",
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapFree block %p (%lu), heap=%p",
pPrior, osGetLastError(), (void*)hHeap);
}
}
@@ -31863,7 +32059,7 @@ static void *winMemRealloc(void *pPrior, int nBytes){
p = osHeapReAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior, (SIZE_T)nBytes);
}
if( !p ){
- sqlite3_log(SQLITE_NOMEM, "failed to %s %u bytes (%d), heap=%p",
+ sqlite3_log(SQLITE_NOMEM, "failed to %s %u bytes (%lu), heap=%p",
pPrior ? "HeapReAlloc" : "HeapAlloc", nBytes, osGetLastError(),
(void*)hHeap);
}
@@ -31887,7 +32083,7 @@ static int winMemSize(void *p){
if( !p ) return 0;
n = osHeapSize(hHeap, SQLITE_WIN32_HEAP_FLAGS, p);
if( n==(SIZE_T)-1 ){
- sqlite3_log(SQLITE_NOMEM, "failed to HeapSize block %p (%d), heap=%p",
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapSize block %p (%lu), heap=%p",
p, osGetLastError(), (void*)hHeap);
return 0;
}
@@ -31917,7 +32113,7 @@ static int winMemInit(void *pAppData){
SQLITE_WIN32_HEAP_MAX_SIZE);
if( !pWinMemData->hHeap ){
sqlite3_log(SQLITE_NOMEM,
- "failed to HeapCreate (%d), flags=%u, initSize=%u, maxSize=%u",
+ "failed to HeapCreate (%lu), flags=%u, initSize=%u, maxSize=%u",
osGetLastError(), SQLITE_WIN32_HEAP_FLAGS,
SQLITE_WIN32_HEAP_INIT_SIZE, SQLITE_WIN32_HEAP_MAX_SIZE);
return SQLITE_NOMEM;
@@ -31929,7 +32125,7 @@ static int winMemInit(void *pAppData){
pWinMemData->hHeap = osGetProcessHeap();
if( !pWinMemData->hHeap ){
sqlite3_log(SQLITE_NOMEM,
- "failed to GetProcessHeap (%d)", osGetLastError());
+ "failed to GetProcessHeap (%lu)", osGetLastError());
return SQLITE_NOMEM;
}
pWinMemData->bOwned = FALSE;
@@ -31957,7 +32153,7 @@ static void winMemShutdown(void *pAppData){
#endif
if( pWinMemData->bOwned ){
if( !osHeapDestroy(pWinMemData->hHeap) ){
- sqlite3_log(SQLITE_NOMEM, "failed to HeapDestroy (%d), heap=%p",
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapDestroy (%lu), heap=%p",
osGetLastError(), (void*)pWinMemData->hHeap);
}
pWinMemData->bOwned = FALSE;
@@ -31998,7 +32194,7 @@ SQLITE_PRIVATE void sqlite3MemSetDefault(void){
**
** Space to hold the returned string is obtained from malloc.
*/
-static LPWSTR utf8ToUnicode(const char *zFilename){
+static LPWSTR winUtf8ToUnicode(const char *zFilename){
int nChar;
LPWSTR zWideFilename;
@@ -32023,7 +32219,7 @@ static LPWSTR utf8ToUnicode(const char *zFilename){
** Convert Microsoft Unicode to UTF-8. Space to hold the returned string is
** obtained from sqlite3_malloc().
*/
-static char *unicodeToUtf8(LPCWSTR zWideFilename){
+static char *winUnicodeToUtf8(LPCWSTR zWideFilename){
int nByte;
char *zFilename;
@@ -32051,7 +32247,7 @@ static char *unicodeToUtf8(LPCWSTR zWideFilename){
** Space to hold the returned string is obtained
** from sqlite3_malloc.
*/
-static LPWSTR mbcsToUnicode(const char *zFilename){
+static LPWSTR winMbcsToUnicode(const char *zFilename){
int nByte;
LPWSTR zMbcsFilename;
int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP;
@@ -32081,7 +32277,7 @@ static LPWSTR mbcsToUnicode(const char *zFilename){
** Space to hold the returned string is obtained from
** sqlite3_malloc().
*/
-static char *unicodeToMbcs(LPCWSTR zWideFilename){
+static char *winUnicodeToMbcs(LPCWSTR zWideFilename){
int nByte;
char *zFilename;
int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP;
@@ -32111,11 +32307,11 @@ SQLITE_API char *sqlite3_win32_mbcs_to_utf8(const char *zFilename){
char *zFilenameUtf8;
LPWSTR zTmpWide;
- zTmpWide = mbcsToUnicode(zFilename);
+ zTmpWide = winMbcsToUnicode(zFilename);
if( zTmpWide==0 ){
return 0;
}
- zFilenameUtf8 = unicodeToUtf8(zTmpWide);
+ zFilenameUtf8 = winUnicodeToUtf8(zTmpWide);
sqlite3_free(zTmpWide);
return zFilenameUtf8;
}
@@ -32128,11 +32324,11 @@ SQLITE_API char *sqlite3_win32_utf8_to_mbcs(const char *zFilename){
char *zFilenameMbcs;
LPWSTR zTmpWide;
- zTmpWide = utf8ToUnicode(zFilename);
+ zTmpWide = winUtf8ToUnicode(zFilename);
if( zTmpWide==0 ){
return 0;
}
- zFilenameMbcs = unicodeToMbcs(zTmpWide);
+ zFilenameMbcs = winUnicodeToMbcs(zTmpWide);
sqlite3_free(zTmpWide);
return zFilenameMbcs;
}
@@ -32162,7 +32358,7 @@ SQLITE_API int sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){
if( ppDirectory ){
char *zValueUtf8 = 0;
if( zValue && zValue[0] ){
- zValueUtf8 = unicodeToUtf8(zValue);
+ zValueUtf8 = winUnicodeToUtf8(zValue);
if ( zValueUtf8==0 ){
return SQLITE_NOMEM;
}
@@ -32175,11 +32371,11 @@ SQLITE_API int sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){
}
/*
-** The return value of getLastErrorMsg
+** The return value of winGetLastErrorMsg
** is zero if the error message fits in the buffer, or non-zero
** otherwise (if the message was truncated).
*/
-static int getLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){
+static int winGetLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){
/* FormatMessage returns 0 on failure. Otherwise it
** returns the number of TCHARs written to the output
** buffer, excluding the terminating null char.
@@ -32187,16 +32383,16 @@ static int getLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){
DWORD dwLen = 0;
char *zOut = 0;
- if( isNT() ){
+ if( osIsNT() ){
#if SQLITE_OS_WINRT
- WCHAR zTempWide[MAX_PATH+1]; /* NOTE: Somewhat arbitrary. */
+ WCHAR zTempWide[SQLITE_WIN32_MAX_ERRMSG_CHARS+1];
dwLen = osFormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
lastErrno,
0,
zTempWide,
- MAX_PATH,
+ SQLITE_WIN32_MAX_ERRMSG_CHARS,
0);
#else
LPWSTR zTempWide = NULL;
@@ -32213,7 +32409,7 @@ static int getLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){
if( dwLen > 0 ){
/* allocate a buffer and convert to UTF8 */
sqlite3BeginBenignMalloc();
- zOut = unicodeToUtf8(zTempWide);
+ zOut = winUnicodeToUtf8(zTempWide);
sqlite3EndBenignMalloc();
#if !SQLITE_OS_WINRT
/* free the system buffer allocated by FormatMessage */
@@ -32281,7 +32477,7 @@ static int winLogErrorAtLine(
int i; /* Loop counter */
zMsg[0] = 0;
- getLastErrorMsg(lastErrno, sizeof(zMsg), zMsg);
+ winGetLastErrorMsg(lastErrno, sizeof(zMsg), zMsg);
assert( errcode!=SQLITE_OK );
if( zPath==0 ) zPath = "";
for(i=0; zMsg[i] && zMsg[i]!='\r' && zMsg[i]!='\n'; i++){}
@@ -32306,17 +32502,17 @@ static int winLogErrorAtLine(
#ifndef SQLITE_WIN32_IOERR_RETRY_DELAY
# define SQLITE_WIN32_IOERR_RETRY_DELAY 25
#endif
-static int win32IoerrRetry = SQLITE_WIN32_IOERR_RETRY;
-static int win32IoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY;
+static int winIoerrRetry = SQLITE_WIN32_IOERR_RETRY;
+static int winIoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY;
/*
** If a ReadFile() or WriteFile() error occurs, invoke this routine
** to see if it should be retried. Return TRUE to retry. Return FALSE
** to give up with an error.
*/
-static int retryIoerr(int *pnRetry, DWORD *pError){
+static int winRetryIoerr(int *pnRetry, DWORD *pError){
DWORD e = osGetLastError();
- if( *pnRetry>=win32IoerrRetry ){
+ if( *pnRetry>=winIoerrRetry ){
if( pError ){
*pError = e;
}
@@ -32325,7 +32521,7 @@ static int retryIoerr(int *pnRetry, DWORD *pError){
if( e==ERROR_ACCESS_DENIED ||
e==ERROR_LOCK_VIOLATION ||
e==ERROR_SHARING_VIOLATION ){
- sqlite3_win32_sleep(win32IoerrRetryDelay*(1+*pnRetry));
+ sqlite3_win32_sleep(winIoerrRetryDelay*(1+*pnRetry));
++*pnRetry;
return 1;
}
@@ -32338,11 +32534,11 @@ static int retryIoerr(int *pnRetry, DWORD *pError){
/*
** Log a I/O error retry episode.
*/
-static void logIoerr(int nRetry){
+static void winLogIoerr(int nRetry){
if( nRetry ){
sqlite3_log(SQLITE_IOERR,
"delayed %dms for lock/sharing conflict",
- win32IoerrRetryDelay*nRetry*(nRetry+1)/2
+ winIoerrRetryDelay*nRetry*(nRetry+1)/2
);
}
}
@@ -32407,7 +32603,7 @@ static int winceCreateLock(const char *zFilename, winFile *pFile){
BOOL bLogged = FALSE;
BOOL bInit = TRUE;
- zName = utf8ToUnicode(zFilename);
+ zName = winUtf8ToUnicode(zFilename);
if( zName==0 ){
/* out of memory */
return SQLITE_IOERR_NOMEM;
@@ -32427,10 +32623,9 @@ static int winceCreateLock(const char *zFilename, winFile *pFile){
pFile->hMutex = osCreateMutexW(NULL, FALSE, zName);
if (!pFile->hMutex){
pFile->lastErrno = osGetLastError();
- winLogError(SQLITE_IOERR, pFile->lastErrno,
- "winceCreateLock1", zFilename);
sqlite3_free(zName);
- return SQLITE_IOERR;
+ return winLogError(SQLITE_IOERR, pFile->lastErrno,
+ "winceCreateLock1", zFilename);
}
/* Acquire the mutex before continuing */
@@ -32680,7 +32875,7 @@ static BOOL winLockFile(
return winceLockFile(phFile, offsetLow, offsetHigh,
numBytesLow, numBytesHigh);
#else
- if( isNT() ){
+ if( osIsNT() ){
OVERLAPPED ovlp;
memset(&ovlp, 0, sizeof(OVERLAPPED));
ovlp.Offset = offsetLow;
@@ -32711,7 +32906,7 @@ static BOOL winUnlockFile(
return winceUnlockFile(phFile, offsetLow, offsetHigh,
numBytesLow, numBytesHigh);
#else
- if( isNT() ){
+ if( osIsNT() ){
OVERLAPPED ovlp;
memset(&ovlp, 0, sizeof(OVERLAPPED));
ovlp.Offset = offsetLow;
@@ -32741,7 +32936,7 @@ static BOOL winUnlockFile(
** argument to offset iOffset within the file. If successful, return 0.
** Otherwise, set pFile->lastErrno and return non-zero.
*/
-static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
+static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){
#if !SQLITE_OS_WINRT
LONG upperBits; /* Most sig. 32 bits of new offset */
LONG lowerBits; /* Least sig. 32 bits of new offset */
@@ -32766,7 +32961,7 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
&& ((lastErrno = osGetLastError())!=NO_ERROR)) ){
pFile->lastErrno = lastErrno;
winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
- "seekWinFile", pFile->zPath);
+ "winSeekFile", pFile->zPath);
OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h));
return 1;
}
@@ -32787,7 +32982,7 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
if(!bRet){
pFile->lastErrno = osGetLastError();
winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
- "seekWinFile", pFile->zPath);
+ "winSeekFile", pFile->zPath);
OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h));
return 1;
}
@@ -32798,7 +32993,8 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
}
#if SQLITE_MAX_MMAP_SIZE>0
-/* Forward references to VFS methods */
+/* Forward references to VFS helper methods used for memory mapped files */
+static int winMapfile(winFile*, sqlite3_int64);
static int winUnmapfile(winFile*);
#endif
@@ -32825,8 +33021,7 @@ static int winClose(sqlite3_file *id){
OSTRACE(("CLOSE file=%p\n", pFile->h));
#if SQLITE_MAX_MMAP_SIZE>0
- rc = winUnmapfile(pFile);
- if( rc!=SQLITE_OK ) return rc;
+ winUnmapfile(pFile);
#endif
do{
@@ -32902,7 +33097,7 @@ static int winRead(
#endif
#if SQLITE_OS_WINCE
- if( seekWinFile(pFile, offset) ){
+ if( winSeekFile(pFile, offset) ){
OSTRACE(("READ file=%p, rc=SQLITE_FULL\n", pFile->h));
return SQLITE_FULL;
}
@@ -32915,13 +33110,13 @@ static int winRead(
osGetLastError()!=ERROR_HANDLE_EOF ){
#endif
DWORD lastErrno;
- if( retryIoerr(&nRetry, &lastErrno) ) continue;
+ if( winRetryIoerr(&nRetry, &lastErrno) ) continue;
pFile->lastErrno = lastErrno;
OSTRACE(("READ file=%p, rc=SQLITE_IOERR_READ\n", pFile->h));
return winLogError(SQLITE_IOERR_READ, pFile->lastErrno,
- "winRead", pFile->zPath);
+ "winRead", pFile->zPath);
}
- logIoerr(nRetry);
+ winLogIoerr(nRetry);
if( nRead<(DWORD)amt ){
/* Unread parts of the buffer must be zero-filled */
memset(&((char*)pBuf)[nRead], 0, amt-nRead);
@@ -32974,7 +33169,7 @@ static int winWrite(
#endif
#if SQLITE_OS_WINCE
- rc = seekWinFile(pFile, offset);
+ rc = winSeekFile(pFile, offset);
if( rc==0 ){
#else
{
@@ -32999,7 +33194,7 @@ static int winWrite(
#else
if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){
#endif
- if( retryIoerr(&nRetry, &lastErrno) ) continue;
+ if( winRetryIoerr(&nRetry, &lastErrno) ) continue;
break;
}
assert( nWrite==0 || nWrite<=(DWORD)nRem );
@@ -33025,13 +33220,14 @@ static int winWrite(
if( ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL )
|| ( pFile->lastErrno==ERROR_DISK_FULL )){
OSTRACE(("WRITE file=%p, rc=SQLITE_FULL\n", pFile->h));
- return SQLITE_FULL;
+ return winLogError(SQLITE_FULL, pFile->lastErrno,
+ "winWrite1", pFile->zPath);
}
OSTRACE(("WRITE file=%p, rc=SQLITE_IOERR_WRITE\n", pFile->h));
return winLogError(SQLITE_IOERR_WRITE, pFile->lastErrno,
- "winWrite", pFile->zPath);
+ "winWrite2", pFile->zPath);
}else{
- logIoerr(nRetry);
+ winLogIoerr(nRetry);
}
OSTRACE(("WRITE file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
@@ -33060,7 +33256,7 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
}
/* SetEndOfFile() returns non-zero when successful, or zero when it fails. */
- if( seekWinFile(pFile, nByte) ){
+ if( winSeekFile(pFile, nByte) ){
rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno,
"winTruncate1", pFile->zPath);
}else if( 0==osSetEndOfFile(pFile->h) &&
@@ -33141,6 +33337,7 @@ static int winSync(sqlite3_file *id, int flags){
** no-op
*/
#ifdef SQLITE_NO_SYNC
+ OSTRACE(("SYNC-NOP file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
#else
rc = osFlushFileBuffers(pFile->h);
@@ -33152,7 +33349,7 @@ static int winSync(sqlite3_file *id, int flags){
pFile->lastErrno = osGetLastError();
OSTRACE(("SYNC file=%p, rc=SQLITE_IOERR_FSYNC\n", pFile->h));
return winLogError(SQLITE_IOERR_FSYNC, pFile->lastErrno,
- "winSync", pFile->zPath);
+ "winSync", pFile->zPath);
}
#endif
}
@@ -33193,7 +33390,7 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
&& ((lastErrno = osGetLastError())!=NO_ERROR) ){
pFile->lastErrno = lastErrno;
rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno,
- "winFileSize", pFile->zPath);
+ "winFileSize", pFile->zPath);
}
}
#endif
@@ -33238,10 +33435,10 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
** Different API routines are called depending on whether or not this
** is Win9x or WinNT.
*/
-static int getReadLock(winFile *pFile){
+static int winGetReadLock(winFile *pFile){
int res;
OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype));
- if( isNT() ){
+ if( osIsNT() ){
#if SQLITE_OS_WINCE
/*
** NOTE: Windows CE is handled differently here due its lack of the Win32
@@ -33273,11 +33470,11 @@ static int getReadLock(winFile *pFile){
/*
** Undo a readlock
*/
-static int unlockReadLock(winFile *pFile){
+static int winUnlockReadLock(winFile *pFile){
int res;
DWORD lastErrno;
OSTRACE(("READ-UNLOCK file=%p, lock=%d\n", pFile->h, pFile->locktype));
- if( isNT() ){
+ if( osIsNT() ){
res = winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
}
#ifdef SQLITE_WIN32_HAS_ANSI
@@ -33288,7 +33485,7 @@ static int unlockReadLock(winFile *pFile){
if( res==0 && ((lastErrno = osGetLastError())!=ERROR_NOT_LOCKED) ){
pFile->lastErrno = lastErrno;
winLogError(SQLITE_IOERR_UNLOCK, pFile->lastErrno,
- "unlockReadLock", pFile->zPath);
+ "winUnlockReadLock", pFile->zPath);
}
OSTRACE(("READ-UNLOCK file=%p, rc=%s\n", pFile->h, sqlite3ErrName(res)));
return res;
@@ -33379,7 +33576,7 @@ static int winLock(sqlite3_file *id, int locktype){
*/
if( locktype==SHARED_LOCK && res ){
assert( pFile->locktype==NO_LOCK );
- res = getReadLock(pFile);
+ res = winGetReadLock(pFile);
if( res ){
newLocktype = SHARED_LOCK;
}else{
@@ -33410,14 +33607,14 @@ static int winLock(sqlite3_file *id, int locktype){
*/
if( locktype==EXCLUSIVE_LOCK && res ){
assert( pFile->locktype>=SHARED_LOCK );
- res = unlockReadLock(pFile);
+ res = winUnlockReadLock(pFile);
res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, SHARED_FIRST, 0,
SHARED_SIZE, 0);
if( res ){
newLocktype = EXCLUSIVE_LOCK;
}else{
lastErrno = osGetLastError();
- getReadLock(pFile);
+ winGetReadLock(pFile);
}
}
@@ -33434,10 +33631,10 @@ static int winLock(sqlite3_file *id, int locktype){
if( res ){
rc = SQLITE_OK;
}else{
- OSTRACE(("LOCK-FAIL file=%p, wanted=%d, got=%d\n",
- pFile->h, locktype, newLocktype));
pFile->lastErrno = lastErrno;
rc = SQLITE_BUSY;
+ OSTRACE(("LOCK-FAIL file=%p, wanted=%d, got=%d\n",
+ pFile->h, locktype, newLocktype));
}
pFile->locktype = (u8)newLocktype;
OSTRACE(("LOCK file=%p, lock=%d, rc=%s\n",
@@ -33497,18 +33694,18 @@ static int winUnlock(sqlite3_file *id, int locktype){
type = pFile->locktype;
if( type>=EXCLUSIVE_LOCK ){
winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
- if( locktype==SHARED_LOCK && !getReadLock(pFile) ){
+ if( locktype==SHARED_LOCK && !winGetReadLock(pFile) ){
/* This should never happen. We should always be able to
** reacquire the read lock */
rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(),
- "winUnlock", pFile->zPath);
+ "winUnlock", pFile->zPath);
}
}
if( type>=RESERVED_LOCK ){
winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0);
}
if( locktype==NO_LOCK && type>=SHARED_LOCK ){
- unlockReadLock(pFile);
+ winUnlockReadLock(pFile);
}
if( type>=PENDING_LOCK ){
winUnlockFile(&pFile->h, PENDING_BYTE, 0, 1, 0);
@@ -33535,8 +33732,10 @@ static void winModeBit(winFile *pFile, unsigned char mask, int *pArg){
}
}
-/* Forward declaration */
-static int getTempname(int nBuf, char *zBuf);
+/* Forward references to VFS helper methods used for temporary files */
+static int winGetTempname(sqlite3_vfs *, char **);
+static int winIsDir(const void *);
+static BOOL winIsDriveLetterAndColon(const char *);
/*
** Control and query of the open file handle.
@@ -33596,37 +33795,44 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
case SQLITE_FCNTL_WIN32_AV_RETRY: {
int *a = (int*)pArg;
if( a[0]>0 ){
- win32IoerrRetry = a[0];
+ winIoerrRetry = a[0];
}else{
- a[0] = win32IoerrRetry;
+ a[0] = winIoerrRetry;
}
if( a[1]>0 ){
- win32IoerrRetryDelay = a[1];
+ winIoerrRetryDelay = a[1];
}else{
- a[1] = win32IoerrRetryDelay;
+ a[1] = winIoerrRetryDelay;
}
OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
case SQLITE_FCNTL_TEMPFILENAME: {
- char *zTFile = sqlite3MallocZero( pFile->pVfs->mxPathname );
- if( zTFile ){
- getTempname(pFile->pVfs->mxPathname, zTFile);
+ char *zTFile = 0;
+ int rc = winGetTempname(pFile->pVfs, &zTFile);
+ if( rc==SQLITE_OK ){
*(char**)pArg = zTFile;
}
- OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
- return SQLITE_OK;
+ OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
+ return rc;
}
#if SQLITE_MAX_MMAP_SIZE>0
case SQLITE_FCNTL_MMAP_SIZE: {
i64 newLimit = *(i64*)pArg;
+ int rc = SQLITE_OK;
if( newLimit>sqlite3GlobalConfig.mxMmap ){
newLimit = sqlite3GlobalConfig.mxMmap;
}
*(i64*)pArg = pFile->mmapSizeMax;
- if( newLimit>=0 ) pFile->mmapSizeMax = newLimit;
- OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
- return SQLITE_OK;
+ if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){
+ pFile->mmapSizeMax = newLimit;
+ if( pFile->mmapSize>0 ){
+ winUnmapfile(pFile);
+ rc = winMapfile(pFile, -1);
+ }
+ }
+ OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
+ return rc;
}
#endif
}
@@ -33831,7 +34037,6 @@ static int winDelete(sqlite3_vfs *,const char*,int);
static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
winShmNode **pp;
winShmNode *p;
- BOOL bRc;
assert( winShmMutexHeld() );
OSTRACE(("SHM-PURGE pid=%lu, deleteFlag=%d\n",
osGetCurrentProcessId(), deleteFlag));
@@ -33839,14 +34044,16 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
while( (p = *pp)!=0 ){
if( p->nRef==0 ){
int i;
- if( p->mutex ) sqlite3_mutex_free(p->mutex);
+ if( p->mutex ){ sqlite3_mutex_free(p->mutex); }
for(i=0; i<p->nRegion; i++){
- bRc = osUnmapViewOfFile(p->aRegion[i].pMap);
+ BOOL bRc = osUnmapViewOfFile(p->aRegion[i].pMap);
OSTRACE(("SHM-PURGE-UNMAP pid=%lu, region=%d, rc=%s\n",
osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
+ UNUSED_VARIABLE_VALUE(bRc);
bRc = osCloseHandle(p->aRegion[i].hMap);
OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n",
osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
+ UNUSED_VARIABLE_VALUE(bRc);
}
if( p->hFile.h!=NULL && p->hFile.h!=INVALID_HANDLE_VALUE ){
SimulateIOErrorBenign(1);
@@ -33941,7 +34148,7 @@ static int winOpenSharedMemory(winFile *pDbFd){
rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0);
if( rc!=SQLITE_OK ){
rc = winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(),
- "winOpenShm", pDbFd->zPath);
+ "winOpenShm", pDbFd->zPath);
}
}
if( rc==SQLITE_OK ){
@@ -34201,7 +34408,7 @@ static int winShmMap(
rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz);
if( rc!=SQLITE_OK ){
rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(),
- "winShmMap1", pDbFd->zPath);
+ "winShmMap1", pDbFd->zPath);
goto shmpage_out;
}
@@ -34216,7 +34423,7 @@ static int winShmMap(
rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte);
if( rc!=SQLITE_OK ){
rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(),
- "winShmMap2", pDbFd->zPath);
+ "winShmMap2", pDbFd->zPath);
goto shmpage_out;
}
}
@@ -34270,7 +34477,7 @@ static int winShmMap(
if( !pMap ){
pShmNode->lastErrno = osGetLastError();
rc = winLogError(SQLITE_IOERR_SHMMAP, pShmNode->lastErrno,
- "winShmMap3", pDbFd->zPath);
+ "winShmMap3", pDbFd->zPath);
if( hMap ) osCloseHandle(hMap);
goto shmpage_out;
}
@@ -34318,7 +34525,7 @@ static int winUnmapfile(winFile *pFile){
"rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(), pFile,
pFile->pMapRegion));
return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno,
- "winUnmap1", pFile->zPath);
+ "winUnmapfile1", pFile->zPath);
}
pFile->pMapRegion = 0;
pFile->mmapSize = 0;
@@ -34330,7 +34537,7 @@ static int winUnmapfile(winFile *pFile){
OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, rc=SQLITE_IOERR_MMAP\n",
osGetCurrentProcessId(), pFile, pFile->hMap));
return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno,
- "winUnmap2", pFile->zPath);
+ "winUnmapfile2", pFile->zPath);
}
pFile->hMap = NULL;
}
@@ -34405,27 +34612,28 @@ static int winMapfile(winFile *pFd, sqlite3_int64 nByte){
if( pFd->hMap==NULL ){
pFd->lastErrno = osGetLastError();
rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno,
- "winMapfile", pFd->zPath);
+ "winMapfile1", pFd->zPath);
/* Log the error, but continue normal operation using xRead/xWrite */
- OSTRACE(("MAP-FILE-CREATE pid=%lu, pFile=%p, rc=SQLITE_IOERR_MMAP\n",
- osGetCurrentProcessId(), pFd));
+ OSTRACE(("MAP-FILE-CREATE pid=%lu, pFile=%p, rc=%s\n",
+ osGetCurrentProcessId(), pFd, sqlite3ErrName(rc)));
return SQLITE_OK;
}
assert( (nMap % winSysInfo.dwPageSize)==0 );
+ assert( sizeof(SIZE_T)==sizeof(sqlite3_int64) || nMap<=0xffffffff );
#if SQLITE_OS_WINRT
- pNew = osMapViewOfFileFromApp(pFd->hMap, flags, 0, nMap);
+ pNew = osMapViewOfFileFromApp(pFd->hMap, flags, 0, (SIZE_T)nMap);
#else
- assert( sizeof(SIZE_T)==sizeof(sqlite3_int64) || nMap<=0xffffffff );
pNew = osMapViewOfFile(pFd->hMap, flags, 0, 0, (SIZE_T)nMap);
#endif
if( pNew==NULL ){
osCloseHandle(pFd->hMap);
pFd->hMap = NULL;
pFd->lastErrno = osGetLastError();
- winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno,
- "winMapfile", pFd->zPath);
- OSTRACE(("MAP-FILE-MAP pid=%lu, pFile=%p, rc=SQLITE_IOERR_MMAP\n",
- osGetCurrentProcessId(), pFd));
+ rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno,
+ "winMapfile2", pFd->zPath);
+ /* Log the error, but continue normal operation using xRead/xWrite */
+ OSTRACE(("MAP-FILE-MAP pid=%lu, pFile=%p, rc=%s\n",
+ osGetCurrentProcessId(), pFd, sqlite3ErrName(rc)));
return SQLITE_OK;
}
pFd->pMapRegion = pNew;
@@ -34564,16 +34772,37 @@ static const sqlite3_io_methods winIoMethod = {
** sqlite3_vfs object.
*/
+#if 0
+/*
+** Convert a filename from whatever the underlying operating system
+** supports for filenames into UTF-8. Space to hold the result is
+** obtained from malloc and must be freed by the calling function.
+*/
+static char *winConvertToUtf8Filename(const void *zFilename){
+ char *zConverted = 0;
+ if( osIsNT() ){
+ zConverted = winUnicodeToUtf8(zFilename);
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
+ zConverted = sqlite3_win32_mbcs_to_utf8(zFilename);
+ }
+#endif
+ /* caller will handle out of memory */
+ return zConverted;
+}
+#endif
+
/*
** Convert a UTF-8 filename into whatever form the underlying
** operating system wants filenames in. Space to hold the result
** is obtained from malloc and must be freed by the calling
** function.
*/
-static void *convertUtf8Filename(const char *zFilename){
+static void *winConvertFromUtf8Filename(const char *zFilename){
void *zConverted = 0;
- if( isNT() ){
- zConverted = utf8ToUnicode(zFilename);
+ if( osIsNT() ){
+ zConverted = winUtf8ToUnicode(zFilename);
}
#ifdef SQLITE_WIN32_HAS_ANSI
else{
@@ -34585,17 +34814,29 @@ static void *convertUtf8Filename(const char *zFilename){
}
/*
-** Create a temporary file name in zBuf. zBuf must be big enough to
-** hold at pVfs->mxPathname characters.
+** This function returns non-zero if the specified UTF-8 string buffer
+** ends with a directory separator character.
+*/
+static int winEndsInDirSep(char *zBuf){
+ if( zBuf ){
+ int nLen = sqlite3Strlen30(zBuf);
+ return nLen>0 && winIsDirSep(zBuf[nLen-1]);
+ }
+ return 0;
+}
+
+/*
+** Create a temporary file name and store the resulting pointer into pzBuf.
+** The pointer returned in pzBuf must be freed via sqlite3_free().
*/
-static int getTempname(int nBuf, char *zBuf){
+static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
static char zChars[] =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789";
size_t i, j;
- int nTempPath;
- char zTempPath[MAX_PATH+2];
+ int nBuf, nLen;
+ char *zBuf;
/* It's odd to simulate an io-error here, but really this is just
** using the io-error infrastructure to test that SQLite handles this
@@ -34603,21 +34844,136 @@ static int getTempname(int nBuf, char *zBuf){
*/
SimulateIOError( return SQLITE_IOERR );
- memset(zTempPath, 0, MAX_PATH+2);
+ /* Allocate a temporary buffer to store the fully qualified file
+ ** name for the temporary file. If this fails, we cannot continue.
+ */
+ nBuf = pVfs->mxPathname;
+ zBuf = sqlite3MallocZero( nBuf+2 );
+ if( !zBuf ){
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+ /* Figure out the effective temporary directory. First, check if one
+ ** has been explicitly set by the application; otherwise, use the one
+ ** configured by the operating system.
+ */
+ assert( nBuf>30 );
if( sqlite3_temp_directory ){
- sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", sqlite3_temp_directory);
+ sqlite3_snprintf(nBuf-30, zBuf, "%s%s", sqlite3_temp_directory,
+ winEndsInDirSep(sqlite3_temp_directory) ? "" :
+ winGetDirDep());
}
-#if !SQLITE_OS_WINRT
- else if( isNT() ){
+#if defined(__CYGWIN__)
+ else{
+ static const char *azDirs[] = {
+ 0, /* getenv("SQLITE_TMPDIR") */
+ 0, /* getenv("TMPDIR") */
+ 0, /* getenv("TMP") */
+ 0, /* getenv("TEMP") */
+ 0, /* getenv("USERPROFILE") */
+ "/var/tmp",
+ "/usr/tmp",
+ "/tmp",
+ ".",
+ 0 /* List terminator */
+ };
+ unsigned int i;
+ const char *zDir = 0;
+
+ if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR");
+ if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
+ if( !azDirs[2] ) azDirs[2] = getenv("TMP");
+ if( !azDirs[3] ) azDirs[3] = getenv("TEMP");
+ if( !azDirs[4] ) azDirs[4] = getenv("USERPROFILE");
+ for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
+ void *zConverted;
+ if( zDir==0 ) continue;
+ /* If the path starts with a drive letter followed by the colon
+ ** character, assume it is already a native Win32 path; otherwise,
+ ** it must be converted to a native Win32 path prior via the Cygwin
+ ** API prior to using it.
+ */
+ if( winIsDriveLetterAndColon(zDir) ){
+ zConverted = winConvertFromUtf8Filename(zDir);
+ if( !zConverted ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( winIsDir(zConverted) ){
+ sqlite3_snprintf(nBuf-30, zBuf, "%s", zDir);
+ sqlite3_free(zConverted);
+ break;
+ }
+ sqlite3_free(zConverted);
+ }else{
+ zConverted = sqlite3MallocZero( nBuf+1 );
+ if( !zConverted ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( cygwin_conv_path(
+ osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A, zDir,
+ zConverted, nBuf+1)<0 ){
+ sqlite3_free(zConverted);
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_CONVPATH\n"));
+ return winLogError(SQLITE_IOERR_CONVPATH, (DWORD)errno,
+ "winGetTempname1", zDir);
+ }
+ if( winIsDir(zConverted) ){
+ /* At this point, we know the candidate directory exists and should
+ ** be used. However, we may need to convert the string containing
+ ** its name into UTF-8 (i.e. if it is UTF-16 right now).
+ */
+ if( osIsNT() ){
+ char *zUtf8 = winUnicodeToUtf8(zConverted);
+ if( !zUtf8 ){
+ sqlite3_free(zConverted);
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+ sqlite3_snprintf(nBuf-30, zBuf, "%s", zUtf8);
+ sqlite3_free(zUtf8);
+ sqlite3_free(zConverted);
+ break;
+ }else{
+ sqlite3_snprintf(nBuf-30, zBuf, "%s", zConverted);
+ sqlite3_free(zConverted);
+ break;
+ }
+ }
+ sqlite3_free(zConverted);
+ }
+ }
+ }
+#elif !SQLITE_OS_WINRT && !defined(__CYGWIN__)
+ else if( osIsNT() ){
char *zMulti;
- WCHAR zWidePath[MAX_PATH];
- osGetTempPathW(MAX_PATH-30, zWidePath);
- zMulti = unicodeToUtf8(zWidePath);
+ LPWSTR zWidePath = sqlite3MallocZero( nBuf*sizeof(WCHAR) );
+ if( !zWidePath ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( osGetTempPathW(nBuf, zWidePath)==0 ){
+ sqlite3_free(zWidePath);
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n"));
+ return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(),
+ "winGetTempname1", 0);
+ }
+ zMulti = winUnicodeToUtf8(zWidePath);
if( zMulti ){
- sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zMulti);
+ sqlite3_snprintf(nBuf-30, zBuf, "%s", zMulti);
sqlite3_free(zMulti);
+ sqlite3_free(zWidePath);
}else{
+ sqlite3_free(zWidePath);
+ sqlite3_free(zBuf);
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
return SQLITE_IOERR_NOMEM;
}
@@ -34625,36 +34981,44 @@ static int getTempname(int nBuf, char *zBuf){
#ifdef SQLITE_WIN32_HAS_ANSI
else{
char *zUtf8;
- char zMbcsPath[MAX_PATH];
- osGetTempPathA(MAX_PATH-30, zMbcsPath);
+ char *zMbcsPath = sqlite3MallocZero( nBuf );
+ if( !zMbcsPath ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( osGetTempPathA(nBuf, zMbcsPath)==0 ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n"));
+ return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(),
+ "winGetTempname2", 0);
+ }
zUtf8 = sqlite3_win32_mbcs_to_utf8(zMbcsPath);
if( zUtf8 ){
- sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zUtf8);
+ sqlite3_snprintf(nBuf-30, zBuf, "%s", zUtf8);
sqlite3_free(zUtf8);
}else{
+ sqlite3_free(zBuf);
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
return SQLITE_IOERR_NOMEM;
}
}
-#endif
-#endif
+#endif /* SQLITE_WIN32_HAS_ANSI */
+#endif /* !SQLITE_OS_WINRT */
/* Check that the output buffer is large enough for the temporary file
** name. If it is not, return SQLITE_ERROR.
*/
- nTempPath = sqlite3Strlen30(zTempPath);
+ nLen = sqlite3Strlen30(zBuf);
- if( (nTempPath + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 18) >= nBuf ){
+ if( (nLen + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 18) >= nBuf ){
+ sqlite3_free(zBuf);
OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
- return SQLITE_ERROR;
+ return winLogError(SQLITE_ERROR, 0, "winGetTempname3", 0);
}
- for(i=nTempPath; i>0 && zTempPath[i-1]=='\\'; i--){}
- zTempPath[i] = 0;
+ sqlite3_snprintf(nBuf-18-nLen, zBuf+nLen, SQLITE_TEMP_FILE_PREFIX);
- sqlite3_snprintf(nBuf-18, zBuf, (nTempPath > 0) ?
- "%s\\"SQLITE_TEMP_FILE_PREFIX : SQLITE_TEMP_FILE_PREFIX,
- zTempPath);
j = sqlite3Strlen30(zBuf);
sqlite3_randomness(15, &zBuf[j]);
for(i=0; i<15; i++, j++){
@@ -34662,6 +35026,7 @@ static int getTempname(int nBuf, char *zBuf){
}
zBuf[j] = 0;
zBuf[j+1] = 0;
+ *pzBuf = zBuf;
OSTRACE(("TEMP-FILENAME name=%s, rc=SQLITE_OK\n", zBuf));
return SQLITE_OK;
@@ -34677,13 +35042,13 @@ static int winIsDir(const void *zConverted){
int rc = 0;
DWORD lastErrno;
- if( isNT() ){
+ if( osIsNT() ){
int cnt = 0;
WIN32_FILE_ATTRIBUTE_DATA sAttrData;
memset(&sAttrData, 0, sizeof(sAttrData));
while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted,
GetFileExInfoStandard,
- &sAttrData)) && retryIoerr(&cnt, &lastErrno) ){}
+ &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){}
if( !rc ){
return 0; /* Invalid name? */
}
@@ -34700,7 +35065,7 @@ static int winIsDir(const void *zConverted){
** Open a file.
*/
static int winOpen(
- sqlite3_vfs *pVfs, /* Not used */
+ sqlite3_vfs *pVfs, /* Used to get maximum path name length */
const char *zName, /* Name of the file (UTF-8) */
sqlite3_file *id, /* Write the SQLite file handle here */
int flags, /* Open mode flags */
@@ -34723,7 +35088,7 @@ static int winOpen(
/* If argument zPath is a NULL pointer, this function is required to open
** a temporary file. Use this buffer to store the file name in.
*/
- char zTmpname[MAX_PATH+2]; /* Buffer used to create temp filename */
+ char *zTmpname = 0; /* For temporary filename, if necessary. */
int rc = SQLITE_OK; /* Function Return Code */
#if !defined(NDEBUG) || SQLITE_OS_WINCE
@@ -34778,7 +35143,7 @@ static int winOpen(
pFile->h = INVALID_HANDLE_VALUE;
#if SQLITE_OS_WINRT
- if( !sqlite3_temp_directory ){
+ if( !zUtf8Name && !sqlite3_temp_directory ){
sqlite3_log(SQLITE_ERROR,
"sqlite3_temp_directory variable should be set for WinRT");
}
@@ -34788,9 +35153,8 @@ static int winOpen(
** temporary file name to use
*/
if( !zUtf8Name ){
- assert(isDelete && !isOpenJournal);
- memset(zTmpname, 0, MAX_PATH+2);
- rc = getTempname(MAX_PATH+2, zTmpname);
+ assert( isDelete && !isOpenJournal );
+ rc = winGetTempname(pVfs, &zTmpname);
if( rc!=SQLITE_OK ){
OSTRACE(("OPEN name=%s, rc=%s", zUtf8Name, sqlite3ErrName(rc)));
return rc;
@@ -34803,17 +35167,19 @@ static int winOpen(
** sqlite3_uri_parameter().
*/
assert( (eType!=SQLITE_OPEN_MAIN_DB) || (flags & SQLITE_OPEN_URI) ||
- zUtf8Name[strlen(zUtf8Name)+1]==0 );
+ zUtf8Name[sqlite3Strlen30(zUtf8Name)+1]==0 );
/* Convert the filename to the system encoding. */
- zConverted = convertUtf8Filename(zUtf8Name);
+ zConverted = winConvertFromUtf8Filename(zUtf8Name);
if( zConverted==0 ){
+ sqlite3_free(zTmpname);
OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8Name));
return SQLITE_IOERR_NOMEM;
}
if( winIsDir(zConverted) ){
sqlite3_free(zConverted);
+ sqlite3_free(zTmpname);
OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8Name));
return SQLITE_CANTOPEN_ISDIR;
}
@@ -34860,7 +35226,7 @@ static int winOpen(
dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
#endif
- if( isNT() ){
+ if( osIsNT() ){
#if SQLITE_OS_WINRT
CREATEFILE2_EXTENDED_PARAMETERS extendedParameters;
extendedParameters.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
@@ -34875,7 +35241,7 @@ static int winOpen(
dwShareMode,
dwCreationDisposition,
&extendedParameters))==INVALID_HANDLE_VALUE &&
- retryIoerr(&cnt, &lastErrno) ){
+ winRetryIoerr(&cnt, &lastErrno) ){
/* Noop */
}
#else
@@ -34885,7 +35251,7 @@ static int winOpen(
dwCreationDisposition,
dwFlagsAndAttributes,
NULL))==INVALID_HANDLE_VALUE &&
- retryIoerr(&cnt, &lastErrno) ){
+ winRetryIoerr(&cnt, &lastErrno) ){
/* Noop */
}
#endif
@@ -34898,12 +35264,12 @@ static int winOpen(
dwCreationDisposition,
dwFlagsAndAttributes,
NULL))==INVALID_HANDLE_VALUE &&
- retryIoerr(&cnt, &lastErrno) ){
+ winRetryIoerr(&cnt, &lastErrno) ){
/* Noop */
}
}
#endif
- logIoerr(cnt);
+ winLogIoerr(cnt);
OSTRACE(("OPEN file=%p, name=%s, access=%lx, rc=%s\n", h, zUtf8Name,
dwDesiredAccess, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok"));
@@ -34912,6 +35278,7 @@ static int winOpen(
pFile->lastErrno = lastErrno;
winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name);
sqlite3_free(zConverted);
+ sqlite3_free(zTmpname);
if( isReadWrite && !isExclusive ){
return winOpen(pVfs, zName, id,
((flags|SQLITE_OPEN_READONLY) &
@@ -34940,6 +35307,7 @@ static int winOpen(
){
osCloseHandle(h);
sqlite3_free(zConverted);
+ sqlite3_free(zTmpname);
OSTRACE(("OPEN-CE-LOCK name=%s, rc=%s\n", zName, sqlite3ErrName(rc)));
return rc;
}
@@ -34951,6 +35319,7 @@ static int winOpen(
sqlite3_free(zConverted);
}
+ sqlite3_free(zTmpname);
pFile->pMethod = &winIoMethod;
pFile->pVfs = pVfs;
pFile->h = h;
@@ -35002,11 +35371,12 @@ static int winDelete(
SimulateIOError(return SQLITE_IOERR_DELETE);
OSTRACE(("DELETE name=%s, syncDir=%d\n", zFilename, syncDir));
- zConverted = convertUtf8Filename(zFilename);
+ zConverted = winConvertFromUtf8Filename(zFilename);
if( zConverted==0 ){
+ OSTRACE(("DELETE name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename));
return SQLITE_IOERR_NOMEM;
}
- if( isNT() ){
+ if( osIsNT() ){
do {
#if SQLITE_OS_WINRT
WIN32_FILE_ATTRIBUTE_DATA sAttrData;
@@ -35045,7 +35415,7 @@ static int winDelete(
rc = SQLITE_OK; /* Deleted OK. */
break;
}
- if ( !retryIoerr(&cnt, &lastErrno) ){
+ if ( !winRetryIoerr(&cnt, &lastErrno) ){
rc = SQLITE_ERROR; /* No more retries. */
break;
}
@@ -35073,7 +35443,7 @@ static int winDelete(
rc = SQLITE_OK; /* Deleted OK. */
break;
}
- if ( !retryIoerr(&cnt, &lastErrno) ){
+ if ( !winRetryIoerr(&cnt, &lastErrno) ){
rc = SQLITE_ERROR; /* No more retries. */
break;
}
@@ -35081,10 +35451,9 @@ static int winDelete(
}
#endif
if( rc && rc!=SQLITE_IOERR_DELETE_NOENT ){
- rc = winLogError(SQLITE_IOERR_DELETE, lastErrno,
- "winDelete", zFilename);
+ rc = winLogError(SQLITE_IOERR_DELETE, lastErrno, "winDelete", zFilename);
}else{
- logIoerr(cnt);
+ winLogIoerr(cnt);
}
sqlite3_free(zConverted);
OSTRACE(("DELETE name=%s, rc=%s\n", zFilename, sqlite3ErrName(rc)));
@@ -35110,18 +35479,18 @@ static int winAccess(
OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n",
zFilename, flags, pResOut));
- zConverted = convertUtf8Filename(zFilename);
+ zConverted = winConvertFromUtf8Filename(zFilename);
if( zConverted==0 ){
OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename));
return SQLITE_IOERR_NOMEM;
}
- if( isNT() ){
+ if( osIsNT() ){
int cnt = 0;
WIN32_FILE_ATTRIBUTE_DATA sAttrData;
memset(&sAttrData, 0, sizeof(sAttrData));
while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted,
GetFileExInfoStandard,
- &sAttrData)) && retryIoerr(&cnt, &lastErrno) ){}
+ &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){}
if( rc ){
/* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file
** as if it does not exist.
@@ -35134,11 +35503,11 @@ static int winAccess(
attr = sAttrData.dwFileAttributes;
}
}else{
- logIoerr(cnt);
+ winLogIoerr(cnt);
if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){
- winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess", zFilename);
sqlite3_free(zConverted);
- return SQLITE_IOERR_ACCESS;
+ return winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess",
+ zFilename);
}else{
attr = INVALID_FILE_ATTRIBUTES;
}
@@ -35168,6 +35537,15 @@ static int winAccess(
return SQLITE_OK;
}
+/*
+** Returns non-zero if the specified path name starts with a drive letter
+** followed by a colon character.
+*/
+static BOOL winIsDriveLetterAndColon(
+ const char *zPathname
+){
+ return ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' );
+}
/*
** Returns non-zero if the specified path name should be used verbatim. If
@@ -35185,7 +35563,7 @@ static BOOL winIsVerbatimPathname(
** the final two cases; therefore, we return the safer return value of TRUE
** so that callers of this function will simply use it verbatim.
*/
- if ( zPathname[0]=='/' || zPathname[0]=='\\' ){
+ if ( winIsDirSep(zPathname[0]) ){
return TRUE;
}
@@ -35195,7 +35573,7 @@ static BOOL winIsVerbatimPathname(
** attempt to treat it as a relative path name (i.e. they should simply use
** it verbatim).
*/
- if ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' ){
+ if ( winIsDriveLetterAndColon(zPathname) ){
return TRUE;
}
@@ -35221,7 +35599,6 @@ static int winFullPathname(
#if defined(__CYGWIN__)
SimulateIOError( return SQLITE_ERROR );
UNUSED_PARAMETER(nFull);
- assert( pVfs->mxPathname>=MAX_PATH );
assert( nFull>=pVfs->mxPathname );
if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
/*
@@ -35230,14 +35607,24 @@ static int winFullPathname(
** for converting the relative path name to an absolute
** one by prepending the data directory and a slash.
*/
- char zOut[MAX_PATH+1];
- memset(zOut, 0, MAX_PATH+1);
- cygwin_conv_path(CCP_POSIX_TO_WIN_A|CCP_RELATIVE, zRelative, zOut,
- MAX_PATH+1);
- sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s\\%s",
- sqlite3_data_directory, zOut);
+ char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
+ if( !zOut ){
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( cygwin_conv_path(CCP_POSIX_TO_WIN_A|CCP_RELATIVE, zRelative, zOut,
+ pVfs->mxPathname+1)<0 ){
+ sqlite3_free(zOut);
+ return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno,
+ "winFullPathname1", zRelative);
+ }
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%s%s",
+ sqlite3_data_directory, winGetDirDep(), zOut);
+ sqlite3_free(zOut);
}else{
- cygwin_conv_path(CCP_POSIX_TO_WIN_A, zRelative, zFull, nFull);
+ if( cygwin_conv_path(CCP_POSIX_TO_WIN_A, zRelative, zFull, nFull)<0 ){
+ return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno,
+ "winFullPathname2", zRelative);
+ }
}
return SQLITE_OK;
#endif
@@ -35253,8 +35640,8 @@ static int winFullPathname(
** for converting the relative path name to an absolute
** one by prepending the data directory and a backslash.
*/
- sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s\\%s",
- sqlite3_data_directory, zRelative);
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%s%s",
+ sqlite3_data_directory, winGetDirDep(), zRelative);
}else{
sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zRelative);
}
@@ -35269,7 +35656,7 @@ static int winFullPathname(
/* If this path name begins with "/X:", where "X" is any alphabetic
** character, discard the initial "/" from the pathname.
*/
- if( zRelative[0]=='/' && sqlite3Isalpha(zRelative[1]) && zRelative[2]==':' ){
+ if( zRelative[0]=='/' && winIsDriveLetterAndColon(zRelative+1) ){
zRelative++;
}
@@ -35286,22 +35673,21 @@ static int winFullPathname(
** for converting the relative path name to an absolute
** one by prepending the data directory and a backslash.
*/
- sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s\\%s",
- sqlite3_data_directory, zRelative);
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%s%s",
+ sqlite3_data_directory, winGetDirDep(), zRelative);
return SQLITE_OK;
}
- zConverted = convertUtf8Filename(zRelative);
+ zConverted = winConvertFromUtf8Filename(zRelative);
if( zConverted==0 ){
return SQLITE_IOERR_NOMEM;
}
- if( isNT() ){
+ if( osIsNT() ){
LPWSTR zTemp;
nByte = osGetFullPathNameW((LPCWSTR)zConverted, 0, 0, 0);
if( nByte==0 ){
- winLogError(SQLITE_ERROR, osGetLastError(),
- "GetFullPathNameW1", zConverted);
sqlite3_free(zConverted);
- return SQLITE_CANTOPEN_FULLPATH;
+ return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
+ "winFullPathname1", zRelative);
}
nByte += 3;
zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) );
@@ -35311,14 +35697,13 @@ static int winFullPathname(
}
nByte = osGetFullPathNameW((LPCWSTR)zConverted, nByte, zTemp, 0);
if( nByte==0 ){
- winLogError(SQLITE_ERROR, osGetLastError(),
- "GetFullPathNameW2", zConverted);
sqlite3_free(zConverted);
sqlite3_free(zTemp);
- return SQLITE_CANTOPEN_FULLPATH;
+ return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
+ "winFullPathname2", zRelative);
}
sqlite3_free(zConverted);
- zOut = unicodeToUtf8(zTemp);
+ zOut = winUnicodeToUtf8(zTemp);
sqlite3_free(zTemp);
}
#ifdef SQLITE_WIN32_HAS_ANSI
@@ -35326,10 +35711,9 @@ static int winFullPathname(
char *zTemp;
nByte = osGetFullPathNameA((char*)zConverted, 0, 0, 0);
if( nByte==0 ){
- winLogError(SQLITE_ERROR, osGetLastError(),
- "GetFullPathNameA1", zConverted);
sqlite3_free(zConverted);
- return SQLITE_CANTOPEN_FULLPATH;
+ return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
+ "winFullPathname3", zRelative);
}
nByte += 3;
zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) );
@@ -35339,11 +35723,10 @@ static int winFullPathname(
}
nByte = osGetFullPathNameA((char*)zConverted, nByte, zTemp, 0);
if( nByte==0 ){
- winLogError(SQLITE_ERROR, osGetLastError(),
- "GetFullPathNameA2", zConverted);
sqlite3_free(zConverted);
sqlite3_free(zTemp);
- return SQLITE_CANTOPEN_FULLPATH;
+ return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
+ "winFullPathname4", zRelative);
}
sqlite3_free(zConverted);
zOut = sqlite3_win32_mbcs_to_utf8(zTemp);
@@ -35371,12 +35754,12 @@ static int winFullPathname(
*/
static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){
HANDLE h;
- void *zConverted = convertUtf8Filename(zFilename);
+ void *zConverted = winConvertFromUtf8Filename(zFilename);
UNUSED_PARAMETER(pVfs);
if( zConverted==0 ){
return 0;
}
- if( isNT() ){
+ if( osIsNT() ){
#if SQLITE_OS_WINRT
h = osLoadPackagedLibrary((LPCWSTR)zConverted, 0);
#else
@@ -35393,7 +35776,7 @@ static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){
}
static void winDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){
UNUSED_PARAMETER(pVfs);
- getLastErrorMsg(osGetLastError(), nBuf, zBufOut);
+ winGetLastErrorMsg(osGetLastError(), nBuf, zBufOut);
}
static void (*winDlSym(sqlite3_vfs *pVfs,void *pH,const char *zSym))(void){
UNUSED_PARAMETER(pVfs);
@@ -35569,7 +35952,7 @@ static int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
*/
static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
UNUSED_PARAMETER(pVfs);
- return getLastErrorMsg(osGetLastError(), nBuf, zBuf);
+ return winGetLastErrorMsg(osGetLastError(), nBuf, zBuf);
}
/*
@@ -35579,7 +35962,7 @@ SQLITE_API int sqlite3_os_init(void){
static sqlite3_vfs winVfs = {
3, /* iVersion */
sizeof(winFile), /* szOsFile */
- MAX_PATH, /* mxPathname */
+ SQLITE_WIN32_MAX_PATH_BYTES, /* mxPathname */
0, /* pNext */
"win32", /* zName */
0, /* pAppData */
@@ -35600,10 +35983,36 @@ SQLITE_API int sqlite3_os_init(void){
winGetSystemCall, /* xGetSystemCall */
winNextSystemCall, /* xNextSystemCall */
};
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ static sqlite3_vfs winLongPathVfs = {
+ 3, /* iVersion */
+ sizeof(winFile), /* szOsFile */
+ SQLITE_WINNT_MAX_PATH_BYTES, /* mxPathname */
+ 0, /* pNext */
+ "win32-longpath", /* zName */
+ 0, /* pAppData */
+ winOpen, /* xOpen */
+ winDelete, /* xDelete */
+ winAccess, /* xAccess */
+ winFullPathname, /* xFullPathname */
+ winDlOpen, /* xDlOpen */
+ winDlError, /* xDlError */
+ winDlSym, /* xDlSym */
+ winDlClose, /* xDlClose */
+ winRandomness, /* xRandomness */
+ winSleep, /* xSleep */
+ winCurrentTime, /* xCurrentTime */
+ winGetLastError, /* xGetLastError */
+ winCurrentTimeInt64, /* xCurrentTimeInt64 */
+ winSetSystemCall, /* xSetSystemCall */
+ winGetSystemCall, /* xGetSystemCall */
+ winNextSystemCall, /* xNextSystemCall */
+ };
+#endif
/* Double-check that the aSyscall[] array has been constructed
** correctly. See ticket [bb3a86e890c8e96ab] */
- assert( ArraySize(aSyscall)==74 );
+ assert( ArraySize(aSyscall)==75 );
/* get memory map allocation granularity */
memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
@@ -35616,6 +36025,11 @@ SQLITE_API int sqlite3_os_init(void){
assert( winSysInfo.dwPageSize>0 );
sqlite3_vfs_register(&winVfs, 1);
+
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ sqlite3_vfs_register(&winLongPathVfs, 0);
+#endif
+
return SQLITE_OK;
}
@@ -37226,7 +37640,7 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
int sz; /* Bytes of memory required to allocate the new cache */
/*
- ** The seperateCache variable is true if each PCache has its own private
+ ** The separateCache variable is true if each PCache has its own private
** PGroup. In other words, separateCache is true for mode (1) where no
** mutexing is required.
**
@@ -37429,6 +37843,7 @@ static sqlite3_pcache_page *pcache1Fetch(
if( pCache->nPage>=pCache->nHash && pcache1ResizeHash(pCache) ){
goto fetch_out;
}
+ assert( pCache->nHash>0 && pCache->apHash );
/* Step 4. Try to recycle a page. */
if( pCache->bPurgeable && pGroup->pLruTail && (
@@ -38790,6 +39205,13 @@ struct PagerSavepoint {
};
/*
+** Bits of the Pager.doNotSpill flag. See further description below.
+*/
+#define SPILLFLAG_OFF 0x01 /* Never spill cache. Set via pragma */
+#define SPILLFLAG_ROLLBACK 0x02 /* Current rolling back, so do not spill */
+#define SPILLFLAG_NOSYNC 0x04 /* Spill is ok, but do not sync */
+
+/*
** A open page cache is an instance of struct Pager. A description of
** some of the more important member variables follows:
**
@@ -38855,19 +39277,21 @@ struct PagerSavepoint {
** journal file from being successfully finalized, the setMaster flag
** is cleared anyway (and the pager will move to ERROR state).
**
-** doNotSpill, doNotSyncSpill
+** doNotSpill
**
-** These two boolean variables control the behavior of cache-spills
-** (calls made by the pcache module to the pagerStress() routine to
-** write cached data to the file-system in order to free up memory).
+** This variables control the behavior of cache-spills (calls made by
+** the pcache module to the pagerStress() routine to write cached data
+** to the file-system in order to free up memory).
**
-** When doNotSpill is non-zero, writing to the database from pagerStress()
-** is disabled altogether. This is done in a very obscure case that
+** When bits SPILLFLAG_OFF or SPILLFLAG_ROLLBACK of doNotSpill are set,
+** writing to the database from pagerStress() is disabled altogether.
+** The SPILLFLAG_ROLLBACK case is done in a very obscure case that
** comes up during savepoint rollback that requires the pcache module
** to allocate a new page to prevent the journal file from being written
-** while it is being traversed by code in pager_playback().
+** while it is being traversed by code in pager_playback(). The SPILLFLAG_OFF
+** case is a user preference.
**
-** If doNotSyncSpill is non-zero, writing to the database from pagerStress()
+** If the SPILLFLAG_NOSYNC bit is set, writing to the database from pagerStress()
** is permitted, but syncing the journal file is not. This flag is set
** by sqlite3PagerWrite() when the file-system sector-size is larger than
** the database page-size in order to prevent a journal sync from happening
@@ -38971,7 +39395,6 @@ struct Pager {
u8 changeCountDone; /* Set after incrementing the change-counter */
u8 setMaster; /* True if a m-j name has been written to jrnl */
u8 doNotSpill; /* Do not spill the cache when non-zero */
- u8 doNotSyncSpill; /* Do not do a spill that requires jrnl sync */
u8 subjInMemory; /* True to use in-memory sub-journals */
Pgno dbSize; /* Number of pages in the database */
Pgno dbOrigSize; /* dbSize before the current transaction */
@@ -39350,13 +39773,17 @@ static char *print_pager_state(Pager *p){
** PagerSavepoint.pInSavepoint.
*/
static int subjRequiresPage(PgHdr *pPg){
- Pgno pgno = pPg->pgno;
Pager *pPager = pPg->pPager;
+ PagerSavepoint *p;
+ Pgno pgno;
int i;
- for(i=0; i<pPager->nSavepoint; i++){
- PagerSavepoint *p = &pPager->aSavepoint[i];
- if( p->nOrig>=pgno && 0==sqlite3BitvecTest(p->pInSavepoint, pgno) ){
- return 1;
+ if( pPager->nSavepoint ){
+ pgno = pPg->pgno;
+ for(i=0; i<pPager->nSavepoint; i++){
+ p = &pPager->aSavepoint[i];
+ if( p->nOrig>=pgno && 0==sqlite3BitvecTest(p->pInSavepoint, pgno) ){
+ return 1;
+ }
}
}
return 0;
@@ -40148,6 +40575,7 @@ static void pager_unlock(Pager *pPager){
pPager->changeCountDone = pPager->tempFile;
pPager->eState = PAGER_OPEN;
pPager->errCode = SQLITE_OK;
+ if( USEFETCH(pPager) ) sqlite3OsUnfetch(pPager->fd, 0, 0);
}
pPager->journalOff = 0;
@@ -40630,11 +41058,11 @@ static int pager_playback_one_page(
** requiring a journal-sync before it is written.
*/
assert( isSavepnt );
- assert( pPager->doNotSpill==0 );
- pPager->doNotSpill++;
+ assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)==0 );
+ pPager->doNotSpill |= SPILLFLAG_ROLLBACK;
rc = sqlite3PagerAcquire(pPager, pgno, &pPg, 1);
- assert( pPager->doNotSpill==1 );
- pPager->doNotSpill--;
+ assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)!=0 );
+ pPager->doNotSpill &= ~SPILLFLAG_ROLLBACK;
if( rc!=SQLITE_OK ) return rc;
pPg->flags &= ~PGHDR_NEED_READ;
sqlite3PcacheMakeDirty(pPg);
@@ -41201,12 +41629,6 @@ static int readDbPage(PgHdr *pPg, u32 iFrame){
assert( pPager->eState>=PAGER_READER && !MEMDB );
assert( isOpen(pPager->fd) );
- if( NEVER(!isOpen(pPager->fd)) ){
- assert( pPager->tempFile );
- memset(pPg->pData, 0, pPager->pageSize);
- return SQLITE_OK;
- }
-
#ifndef SQLITE_OMIT_WAL
if( iFrame ){
/* Try to pull the page from the write-ahead log. */
@@ -41714,10 +42136,10 @@ SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){
static void pagerFixMaplimit(Pager *pPager){
#if SQLITE_MAX_MMAP_SIZE>0
sqlite3_file *fd = pPager->fd;
- if( isOpen(fd) ){
+ if( isOpen(fd) && fd->pMethods->iVersion>=3 ){
sqlite3_int64 sz;
- pPager->bUseFetch = (fd->pMethods->iVersion>=3) && pPager->szMmap>0;
sz = pPager->szMmap;
+ pPager->bUseFetch = (sz>0);
sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_MMAP_SIZE, &sz);
}
#endif
@@ -41739,9 +42161,12 @@ SQLITE_PRIVATE void sqlite3PagerShrink(Pager *pPager){
}
/*
-** Adjust the robustness of the database to damage due to OS crashes
-** or power failures by changing the number of syncs()s when writing
-** the rollback journal. There are three levels:
+** Adjust settings of the pager to those specified in the pgFlags parameter.
+**
+** The "level" in pgFlags & PAGER_SYNCHRONOUS_MASK sets the robustness
+** of the database to damage due to OS crashes or power failures by
+** changing the number of syncs()s when writing the journals.
+** There are three levels:
**
** OFF sqlite3OsSync() is never called. This is the default
** for temporary and transient files.
@@ -41782,22 +42207,21 @@ SQLITE_PRIVATE void sqlite3PagerShrink(Pager *pPager){
** and FULL=3.
*/
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
-SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(
+SQLITE_PRIVATE void sqlite3PagerSetFlags(
Pager *pPager, /* The pager to set safety level for */
- int level, /* PRAGMA synchronous. 1=OFF, 2=NORMAL, 3=FULL */
- int bFullFsync, /* PRAGMA fullfsync */
- int bCkptFullFsync /* PRAGMA checkpoint_fullfsync */
+ unsigned pgFlags /* Various flags */
){
+ unsigned level = pgFlags & PAGER_SYNCHRONOUS_MASK;
assert( level>=1 && level<=3 );
pPager->noSync = (level==1 || pPager->tempFile) ?1:0;
pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0;
if( pPager->noSync ){
pPager->syncFlags = 0;
pPager->ckptSyncFlags = 0;
- }else if( bFullFsync ){
+ }else if( pgFlags & PAGER_FULLFSYNC ){
pPager->syncFlags = SQLITE_SYNC_FULL;
pPager->ckptSyncFlags = SQLITE_SYNC_FULL;
- }else if( bCkptFullFsync ){
+ }else if( pgFlags & PAGER_CKPT_FULLFSYNC ){
pPager->syncFlags = SQLITE_SYNC_NORMAL;
pPager->ckptSyncFlags = SQLITE_SYNC_FULL;
}else{
@@ -41808,6 +42232,11 @@ SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(
if( pPager->fullSync ){
pPager->walSyncFlags |= WAL_SYNC_TRANSACTIONS;
}
+ if( pgFlags & PAGER_CACHESPILL ){
+ pPager->doNotSpill &= ~SPILLFLAG_OFF;
+ }else{
+ pPager->doNotSpill |= SPILLFLAG_OFF;
+ }
}
#endif
@@ -42550,7 +42979,8 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
*/
assert( rc!=SQLITE_OK || isOpen(pPager->fd) );
if( rc==SQLITE_OK
- && (pList->pDirty ? pPager->dbSize : pList->pgno+1)>pPager->dbHintSize
+ && pPager->dbHintSize<pPager->dbSize
+ && (pList->pDirty || pList->pgno>pPager->dbHintSize)
){
sqlite3_int64 szFile = pPager->pageSize * (sqlite3_int64)pPager->dbSize;
sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile);
@@ -42707,13 +43137,14 @@ static int pagerStress(void *p, PgHdr *pPg){
assert( pPg->pPager==pPager );
assert( pPg->flags&PGHDR_DIRTY );
- /* The doNotSyncSpill flag is set during times when doing a sync of
+ /* The doNotSpill NOSYNC bit is set during times when doing a sync of
** journal (and adding a new header) is not allowed. This occurs
** during calls to sqlite3PagerWrite() while trying to journal multiple
** pages belonging to the same sector.
**
- ** The doNotSpill flag inhibits all cache spilling regardless of whether
- ** or not a sync is required. This is set during a rollback.
+ ** The doNotSpill ROLLBACK and OFF bits inhibits all cache spilling
+ ** regardless of whether or not a sync is required. This is set during
+ ** a rollback or by user request, respectively.
**
** Spilling is also prohibited when in an error state since that could
** lead to database corruption. In the current implementaton it
@@ -42723,8 +43154,13 @@ static int pagerStress(void *p, PgHdr *pPg){
** test for the error state as a safeguard against future changes.
*/
if( NEVER(pPager->errCode) ) return SQLITE_OK;
- if( pPager->doNotSpill ) return SQLITE_OK;
- if( pPager->doNotSyncSpill && (pPg->flags & PGHDR_NEED_SYNC)!=0 ){
+ testcase( pPager->doNotSpill & SPILLFLAG_ROLLBACK );
+ testcase( pPager->doNotSpill & SPILLFLAG_OFF );
+ testcase( pPager->doNotSpill & SPILLFLAG_NOSYNC );
+ if( pPager->doNotSpill
+ && ((pPager->doNotSpill & (SPILLFLAG_ROLLBACK|SPILLFLAG_OFF))!=0
+ || (pPg->flags & PGHDR_NEED_SYNC)!=0)
+ ){
return SQLITE_OK;
}
@@ -43515,7 +43951,7 @@ static void pagerUnlockIfUnused(Pager *pPager){
** page is initialized to all zeros.
**
** If noContent is true, it means that we do not care about the contents
-** of the page. This occurs in two seperate scenarios:
+** of the page. This occurs in two scenarios:
**
** a) When reading a free-list leaf page from the database, and
**
@@ -43546,19 +43982,19 @@ SQLITE_PRIVATE int sqlite3PagerAcquire(
Pager *pPager, /* The pager open on the database file */
Pgno pgno, /* Page number to fetch */
DbPage **ppPage, /* Write a pointer to the page here */
- int flags /* PAGER_ACQUIRE_XXX flags */
+ int flags /* PAGER_GET_XXX flags */
){
int rc = SQLITE_OK;
PgHdr *pPg = 0;
u32 iFrame = 0; /* Frame to read from WAL file */
- const int noContent = (flags & PAGER_ACQUIRE_NOCONTENT);
+ const int noContent = (flags & PAGER_GET_NOCONTENT);
/* It is acceptable to use a read-only (mmap) page for any page except
** page 1 if there is no write-transaction open or the ACQUIRE_READONLY
** flag was specified by the caller. And so long as the db is not a
** temporary or in-memory database. */
const int bMmapOk = (pgno!=1 && USEFETCH(pPager)
- && (pPager->eState==PAGER_READER || (flags & PAGER_ACQUIRE_READONLY))
+ && (pPager->eState==PAGER_READER || (flags & PAGER_GET_READONLY))
#ifdef SQLITE_HAS_CODEC
&& pPager->xCodec==0
#endif
@@ -44078,13 +44514,13 @@ SQLITE_PRIVATE int sqlite3PagerWrite(DbPage *pDbPage){
int ii; /* Loop counter */
int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */
- /* Set the doNotSyncSpill flag to 1. This is because we cannot allow
+ /* Set the doNotSpill NOSYNC bit to 1. This is because we cannot allow
** a journal header to be written between the pages journaled by
** this function.
*/
assert( !MEMDB );
- assert( pPager->doNotSyncSpill==0 );
- pPager->doNotSyncSpill++;
+ assert( (pPager->doNotSpill & SPILLFLAG_NOSYNC)==0 );
+ pPager->doNotSpill |= SPILLFLAG_NOSYNC;
/* This trick assumes that both the page-size and sector-size are
** an integer power of 2. It sets variable pg1 to the identifier
@@ -44143,8 +44579,8 @@ SQLITE_PRIVATE int sqlite3PagerWrite(DbPage *pDbPage){
}
}
- assert( pPager->doNotSyncSpill==1 );
- pPager->doNotSyncSpill--;
+ assert( (pPager->doNotSpill & SPILLFLAG_NOSYNC)!=0 );
+ pPager->doNotSpill &= ~SPILLFLAG_NOSYNC;
}else{
rc = pager_write(pDbPage);
}
@@ -44925,7 +45361,27 @@ SQLITE_PRIVATE void sqlite3PagerSetCodec(
SQLITE_PRIVATE void *sqlite3PagerGetCodec(Pager *pPager){
return pPager->pCodec;
}
-#endif
+
+/*
+** This function is called by the wal module when writing page content
+** into the log file.
+**
+** This function returns a pointer to a buffer containing the encrypted
+** page content. If a malloc fails, this function may return NULL.
+*/
+SQLITE_PRIVATE void *sqlite3PagerCodec(PgHdr *pPg){
+ void *aData = 0;
+ CODEC2(pPg->pPager, pPg->pData, pPg->pgno, 6, return 0, aData);
+ return aData;
+}
+
+/*
+** Return the current pager state
+*/
+SQLITE_PRIVATE int sqlite3PagerState(Pager *pPager){
+ return pPager->eState;
+}
+#endif /* SQLITE_HAS_CODEC */
#ifndef SQLITE_OMIT_AUTOVACUUM
/*
@@ -45480,21 +45936,6 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
}
#endif
-#ifdef SQLITE_HAS_CODEC
-/*
-** This function is called by the wal module when writing page content
-** into the log file.
-**
-** This function returns a pointer to a buffer containing the encrypted
-** page content. If a malloc fails, this function may return NULL.
-*/
-SQLITE_PRIVATE void *sqlite3PagerCodec(PgHdr *pPg){
- void *aData = 0;
- CODEC2(pPg->pPager, pPg->pData, pPg->pgno, 6, return 0, aData);
- return aData;
-}
-#endif /* SQLITE_HAS_CODEC */
-
#endif /* SQLITE_OMIT_DISKIO */
/************** End of pager.c ***********************************************/
@@ -47963,7 +48404,7 @@ SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){
if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){
walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
pWal->writeLock = 0;
- rc = SQLITE_BUSY;
+ rc = SQLITE_BUSY_SNAPSHOT;
}
return rc;
@@ -48672,13 +49113,13 @@ SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal){
**
** OFFSET SIZE DESCRIPTION
** 0 16 Header string: "SQLite format 3\000"
-** 16 2 Page size in bytes.
+** 16 2 Page size in bytes. (1 means 65536)
** 18 1 File format write version
** 19 1 File format read version
** 20 1 Bytes of unused space at the end of each page
-** 21 1 Max embedded payload fraction
-** 22 1 Min embedded payload fraction
-** 23 1 Min leaf payload fraction
+** 21 1 Max embedded payload fraction (must be 64)
+** 22 1 Min embedded payload fraction (must be 32)
+** 23 1 Min leaf payload fraction (must be 32)
** 24 4 File change counter
** 28 4 Reserved for future use
** 32 4 First freelist page
@@ -48692,9 +49133,10 @@ SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal){
** 56 4 1=UTF-8 2=UTF16le 3=UTF16be
** 60 4 User version
** 64 4 Incremental vacuum mode
-** 68 4 unused
-** 72 4 unused
-** 76 4 unused
+** 68 4 Application-ID
+** 72 20 unused
+** 92 4 The version-valid-for number
+** 96 4 SQLITE_VERSION_NUMBER
**
** All of the integer values are big-endian (most significant byte first).
**
@@ -49135,14 +49577,19 @@ struct BtCursor {
/*
** Potential values for BtCursor.eState.
**
-** CURSOR_VALID:
-** Cursor points to a valid entry. getPayload() etc. may be called.
-**
** CURSOR_INVALID:
** Cursor does not point to a valid entry. This can happen (for example)
** because the table is empty or because BtreeCursorFirst() has not been
** called.
**
+** CURSOR_VALID:
+** Cursor points to a valid entry. getPayload() etc. may be called.
+**
+** CURSOR_SKIPNEXT:
+** Cursor is valid except that the Cursor.skipNext field is non-zero
+** indicating that the next sqlite3BtreeNext() or sqlite3BtreePrevious()
+** operation should be a no-op.
+**
** CURSOR_REQUIRESEEK:
** The table that this cursor was opened on still exists, but has been
** modified since the cursor was last used. The cursor position is saved
@@ -49159,8 +49606,9 @@ struct BtCursor {
*/
#define CURSOR_INVALID 0
#define CURSOR_VALID 1
-#define CURSOR_REQUIRESEEK 2
-#define CURSOR_FAULT 3
+#define CURSOR_SKIPNEXT 2
+#define CURSOR_REQUIRESEEK 3
+#define CURSOR_FAULT 4
/*
** The database page the PENDING_BYTE occupies. This page is never used.
@@ -50274,6 +50722,9 @@ static int btreeRestoreCursorPosition(BtCursor *pCur){
sqlite3_free(pCur->pKey);
pCur->pKey = 0;
assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID );
+ if( pCur->skipNext && pCur->eState==CURSOR_VALID ){
+ pCur->eState = CURSOR_SKIPNEXT;
+ }
}
return rc;
}
@@ -50299,7 +50750,7 @@ SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor *pCur, int *pHasMoved){
*pHasMoved = 1;
return rc;
}
- if( pCur->eState!=CURSOR_VALID || pCur->skipNext!=0 ){
+ if( pCur->eState!=CURSOR_VALID || NEVER(pCur->skipNext!=0) ){
*pHasMoved = 1;
}else{
*pHasMoved = 0;
@@ -50487,7 +50938,8 @@ static void btreeParseCellPtr(
assert( n==4-4*pPage->leaf );
if( pPage->intKey ){
if( pPage->hasData ){
- n += getVarint32(&pCell[n], nPayload);
+ assert( n==0 );
+ n = getVarint32(pCell, nPayload);
}else{
nPayload = 0;
}
@@ -50765,7 +51217,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
}else if( gap+2<=top ){
/* Search the freelist looking for a free slot big enough to satisfy
** the request. The allocation is made from the first free slot in
- ** the list that is large enough to accomadate it.
+ ** the list that is large enough to accommodate it.
*/
int pc, addr;
for(addr=hdr+1; (pc = get2byte(&data[addr]))>0; addr=pc){
@@ -51131,15 +51583,12 @@ static int btreeGetPage(
BtShared *pBt, /* The btree */
Pgno pgno, /* Number of the page to fetch */
MemPage **ppPage, /* Return the page in this parameter */
- int noContent, /* Do not load page content if true */
- int bReadonly /* True if a read-only (mmap) page is ok */
+ int flags /* PAGER_GET_NOCONTENT or PAGER_GET_READONLY */
){
int rc;
DbPage *pDbPage;
- int flags = (noContent ? PAGER_ACQUIRE_NOCONTENT : 0)
- | (bReadonly ? PAGER_ACQUIRE_READONLY : 0);
- assert( noContent==0 || bReadonly==0 );
+ assert( flags==0 || flags==PAGER_GET_NOCONTENT || flags==PAGER_GET_READONLY );
assert( sqlite3_mutex_held(pBt->mutex) );
rc = sqlite3PagerAcquire(pBt->pPager, pgno, (DbPage**)&pDbPage, flags);
if( rc ) return rc;
@@ -51187,15 +51636,16 @@ static int getAndInitPage(
BtShared *pBt, /* The database file */
Pgno pgno, /* Number of the page to get */
MemPage **ppPage, /* Write the page pointer here */
- int bReadonly /* True if a read-only (mmap) page is ok */
+ int bReadonly /* PAGER_GET_READONLY or 0 */
){
int rc;
assert( sqlite3_mutex_held(pBt->mutex) );
+ assert( bReadonly==PAGER_GET_READONLY || bReadonly==0 );
if( pgno>btreePagecount(pBt) ){
rc = SQLITE_CORRUPT_BKPT;
}else{
- rc = btreeGetPage(pBt, pgno, ppPage, 0, bReadonly);
+ rc = btreeGetPage(pBt, pgno, ppPage, bReadonly);
if( rc==SQLITE_OK ){
rc = btreeInitPage(*ppPage);
if( rc!=SQLITE_OK ){
@@ -51600,6 +52050,18 @@ static int removeFromSharingList(BtShared *pBt){
static void allocateTempSpace(BtShared *pBt){
if( !pBt->pTmpSpace ){
pBt->pTmpSpace = sqlite3PageMalloc( pBt->pageSize );
+
+ /* One of the uses of pBt->pTmpSpace is to format cells before
+ ** inserting them into a leaf page (function fillInCell()). If
+ ** a cell is less than 4 bytes in size, it is rounded up to 4 bytes
+ ** by the various routines that manipulate binary cells. Which
+ ** can mean that fillInCell() only initializes the first 2 or 3
+ ** bytes of pTmpSpace, but that the first 4 bytes are copied from
+ ** it into a database page. This is not actually a problem, but it
+ ** does cause a valgrind error when the 1 or 2 bytes of unitialized
+ ** data is passed to system call write(). So to avoid this error,
+ ** zero the first 4 bytes of temp space here. */
+ if( pBt->pTmpSpace ) memset(pBt->pTmpSpace, 0, 4);
}
}
@@ -51715,17 +52177,14 @@ SQLITE_PRIVATE int sqlite3BtreeSetMmapLimit(Btree *p, sqlite3_int64 szMmap){
** probability of damage to near zero but with a write performance reduction.
*/
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
-SQLITE_PRIVATE int sqlite3BtreeSetSafetyLevel(
+SQLITE_PRIVATE int sqlite3BtreeSetPagerFlags(
Btree *p, /* The btree to set the safety level on */
- int level, /* PRAGMA synchronous. 1=OFF, 2=NORMAL, 3=FULL */
- int fullSync, /* PRAGMA fullfsync. */
- int ckptFullSync /* PRAGMA checkpoint_fullfync */
+ unsigned pgFlags /* Various PAGER_* flags */
){
BtShared *pBt = p->pBt;
assert( sqlite3_mutex_held(p->db->mutex) );
- assert( level>=1 && level<=3 );
sqlite3BtreeEnter(p);
- sqlite3PagerSetSafetyLevel(pBt->pPager, level, fullSync, ckptFullSync);
+ sqlite3PagerSetFlags(pBt->pPager, pgFlags);
sqlite3BtreeLeave(p);
return SQLITE_OK;
}
@@ -51931,7 +52390,7 @@ static int lockBtree(BtShared *pBt){
assert( pBt->pPage1==0 );
rc = sqlite3PagerSharedLock(pBt->pPager);
if( rc!=SQLITE_OK ) return rc;
- rc = btreeGetPage(pBt, 1, &pPage1, 0, 0);
+ rc = btreeGetPage(pBt, 1, &pPage1, 0);
if( rc!=SQLITE_OK ) return rc;
/* Do some checking to help insure the file we opened really is
@@ -52218,7 +52677,7 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){
goto trans_begun;
}
- assert( IfNotOmitAV(pBt->bDoTruncate)==0 );
+ assert( pBt->inTransaction==TRANS_WRITE || IfNotOmitAV(pBt->bDoTruncate)==0 );
/* Write transactions are not possible on a read-only database */
if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){
@@ -52513,7 +52972,7 @@ static int relocatePage(
** iPtrPage.
*/
if( eType!=PTRMAP_ROOTPAGE ){
- rc = btreeGetPage(pBt, iPtrPage, &pPtrPage, 0, 0);
+ rc = btreeGetPage(pBt, iPtrPage, &pPtrPage, 0);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -52597,7 +53056,7 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){
u8 eMode = BTALLOC_ANY; /* Mode parameter for allocateBtreePage() */
Pgno iNear = 0; /* nearby parameter for allocateBtreePage() */
- rc = btreeGetPage(pBt, iLastPg, &pLastPg, 0, 0);
+ rc = btreeGetPage(pBt, iLastPg, &pLastPg, 0);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -52708,7 +53167,7 @@ SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *p){
/*
** This routine is called prior to sqlite3PagerCommit when a transaction
-** is commited for an auto-vacuum database.
+** is committed for an auto-vacuum database.
**
** If SQLITE_OK is returned, then *pnTrunc is set to the number of pages
** the database file should be truncated to during the commit process.
@@ -52823,12 +53282,13 @@ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
*/
static void btreeEndTransaction(Btree *p){
BtShared *pBt = p->pBt;
+ sqlite3 *db = p->db;
assert( sqlite3BtreeHoldsMutex(p) );
#ifndef SQLITE_OMIT_AUTOVACUUM
pBt->bDoTruncate = 0;
#endif
- if( p->inTrans>TRANS_NONE && p->db->activeVdbeCnt>1 ){
+ if( p->inTrans>TRANS_NONE && db->nVdbeRead>1 ){
/* If there are other active statements that belong to this database
** handle, downgrade to a read-only transaction. The other statements
** may still be reading from the database. */
@@ -52995,7 +53455,7 @@ SQLITE_PRIVATE int sqlite3BtreeRollback(Btree *p, int tripCode){
/* The rollback may have destroyed the pPage1->aData value. So
** call btreeGetPage() on page 1 again to make
** sure pPage1->aData is set correctly. */
- if( btreeGetPage(pBt, 1, &pPage1, 0, 0)==SQLITE_OK ){
+ if( btreeGetPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){
int nPage = get4byte(28+(u8*)pPage1->aData);
testcase( nPage==0 );
if( nPage==0 ) sqlite3PagerPagecount(pBt->pPager, &nPage);
@@ -53430,7 +53890,7 @@ static int getOverflowPage(
assert( next==0 || rc==SQLITE_DONE );
if( rc==SQLITE_OK ){
- rc = btreeGetPage(pBt, ovfl, &pPage, 0, (ppPage==0));
+ rc = btreeGetPage(pBt, ovfl, &pPage, (ppPage==0) ? PAGER_GET_READONLY : 0);
assert( rc==SQLITE_OK || pPage==0 );
if( rc==SQLITE_OK ){
next = get4byte(pPage->aData);
@@ -53652,7 +54112,7 @@ static int accessPayload(
{
DbPage *pDbPage;
rc = sqlite3PagerAcquire(pBt->pPager, nextPage, &pDbPage,
- (eOp==0 ? PAGER_ACQUIRE_READONLY : 0)
+ (eOp==0 ? PAGER_GET_READONLY : 0)
);
if( rc==SQLITE_OK ){
aPayload = sqlite3PagerGetData(pDbPage);
@@ -53836,7 +54296,8 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
if( pCur->iPage>=(BTCURSOR_MAX_DEPTH-1) ){
return SQLITE_CORRUPT_BKPT;
}
- rc = getAndInitPage(pBt, newPgno, &pNewPage, (pCur->wrFlag==0));
+ rc = getAndInitPage(pBt, newPgno, &pNewPage,
+ pCur->wrFlag==0 ? PAGER_GET_READONLY : 0);
if( rc ) return rc;
pCur->apPage[i+1] = pNewPage;
pCur->aiIdx[i+1] = 0;
@@ -53953,7 +54414,8 @@ static int moveToRoot(BtCursor *pCur){
pCur->eState = CURSOR_INVALID;
return SQLITE_OK;
}else{
- rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0], pCur->wrFlag==0);
+ rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0],
+ pCur->wrFlag==0 ? PAGER_GET_READONLY : 0);
if( rc!=SQLITE_OK ){
pCur->eState = CURSOR_INVALID;
return rc;
@@ -54348,21 +54810,29 @@ SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
MemPage *pPage;
assert( cursorHoldsMutex(pCur) );
- rc = restoreCursorPosition(pCur);
- if( rc!=SQLITE_OK ){
- return rc;
- }
assert( pRes!=0 );
- if( CURSOR_INVALID==pCur->eState ){
- *pRes = 1;
- return SQLITE_OK;
- }
- if( pCur->skipNext>0 ){
- pCur->skipNext = 0;
- *pRes = 0;
- return SQLITE_OK;
+ assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
+ if( pCur->eState!=CURSOR_VALID ){
+ rc = restoreCursorPosition(pCur);
+ if( rc!=SQLITE_OK ){
+ *pRes = 0;
+ return rc;
+ }
+ if( CURSOR_INVALID==pCur->eState ){
+ *pRes = 1;
+ return SQLITE_OK;
+ }
+ if( pCur->skipNext ){
+ assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_SKIPNEXT );
+ pCur->eState = CURSOR_VALID;
+ if( pCur->skipNext>0 ){
+ pCur->skipNext = 0;
+ *pRes = 0;
+ return SQLITE_OK;
+ }
+ pCur->skipNext = 0;
+ }
}
- pCur->skipNext = 0;
pPage = pCur->apPage[pCur->iPage];
idx = ++pCur->aiIdx[pCur->iPage];
@@ -54380,7 +54850,10 @@ SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
if( idx>=pPage->nCell ){
if( !pPage->leaf ){
rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8]));
- if( rc ) return rc;
+ if( rc ){
+ *pRes = 0;
+ return rc;
+ }
rc = moveToLeftmost(pCur);
*pRes = 0;
return rc;
@@ -54422,21 +54895,32 @@ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
MemPage *pPage;
assert( cursorHoldsMutex(pCur) );
- rc = restoreCursorPosition(pCur);
- if( rc!=SQLITE_OK ){
- return rc;
- }
+ assert( pRes!=0 );
+ assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
pCur->atLast = 0;
- if( CURSOR_INVALID==pCur->eState ){
- *pRes = 1;
- return SQLITE_OK;
- }
- if( pCur->skipNext<0 ){
- pCur->skipNext = 0;
- *pRes = 0;
- return SQLITE_OK;
+ if( pCur->eState!=CURSOR_VALID ){
+ if( ALWAYS(pCur->eState>=CURSOR_REQUIRESEEK) ){
+ rc = btreeRestoreCursorPosition(pCur);
+ if( rc!=SQLITE_OK ){
+ *pRes = 0;
+ return rc;
+ }
+ }
+ if( CURSOR_INVALID==pCur->eState ){
+ *pRes = 1;
+ return SQLITE_OK;
+ }
+ if( pCur->skipNext ){
+ assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_SKIPNEXT );
+ pCur->eState = CURSOR_VALID;
+ if( pCur->skipNext<0 ){
+ pCur->skipNext = 0;
+ *pRes = 0;
+ return SQLITE_OK;
+ }
+ pCur->skipNext = 0;
+ }
}
- pCur->skipNext = 0;
pPage = pCur->apPage[pCur->iPage];
assert( pPage->isInit );
@@ -54444,6 +54928,7 @@ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
int idx = pCur->aiIdx[pCur->iPage];
rc = moveToChild(pCur, get4byte(findCell(pPage, idx)));
if( rc ){
+ *pRes = 0;
return rc;
}
rc = moveToRightmost(pCur);
@@ -54567,7 +55052,7 @@ static int allocateBtreePage(
if( iTrunk>mxPage ){
rc = SQLITE_CORRUPT_BKPT;
}else{
- rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0, 0);
+ rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0);
}
if( rc ){
pTrunk = 0;
@@ -54631,7 +55116,7 @@ static int allocateBtreePage(
goto end_allocate_page;
}
testcase( iNewTrunk==mxPage );
- rc = btreeGetPage(pBt, iNewTrunk, &pNewTrunk, 0, 0);
+ rc = btreeGetPage(pBt, iNewTrunk, &pNewTrunk, 0);
if( rc!=SQLITE_OK ){
goto end_allocate_page;
}
@@ -54710,8 +55195,8 @@ static int allocateBtreePage(
memcpy(&aData[8+closest*4], &aData[4+k*4], 4);
}
put4byte(&aData[4], k-1);
- noContent = !btreeGetHasContent(pBt, *pPgno);
- rc = btreeGetPage(pBt, *pPgno, ppPage, noContent, 0);
+ noContent = !btreeGetHasContent(pBt, *pPgno) ? PAGER_GET_NOCONTENT : 0;
+ rc = btreeGetPage(pBt, *pPgno, ppPage, noContent);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite((*ppPage)->pDbPage);
if( rc!=SQLITE_OK ){
@@ -54743,7 +55228,7 @@ static int allocateBtreePage(
** here are confined to those pages that lie between the end of the
** database image and the end of the database file.
*/
- int bNoContent = (0==IfNotOmitAV(pBt->bDoTruncate));
+ int bNoContent = (0==IfNotOmitAV(pBt->bDoTruncate)) ? PAGER_GET_NOCONTENT : 0;
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
if( rc ) return rc;
@@ -54759,7 +55244,7 @@ static int allocateBtreePage(
MemPage *pPg = 0;
TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage));
assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) );
- rc = btreeGetPage(pBt, pBt->nPage, &pPg, bNoContent, 0);
+ rc = btreeGetPage(pBt, pBt->nPage, &pPg, bNoContent);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pPg->pDbPage);
releasePage(pPg);
@@ -54773,7 +55258,7 @@ static int allocateBtreePage(
*pPgno = pBt->nPage;
assert( *pPgno!=PENDING_BYTE_PAGE(pBt) );
- rc = btreeGetPage(pBt, *pPgno, ppPage, bNoContent, 0);
+ rc = btreeGetPage(pBt, *pPgno, ppPage, bNoContent);
if( rc ) return rc;
rc = sqlite3PagerWrite((*ppPage)->pDbPage);
if( rc!=SQLITE_OK ){
@@ -54841,7 +55326,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
/* If the secure_delete option is enabled, then
** always fully overwrite deleted information with zeros.
*/
- if( (!pPage && ((rc = btreeGetPage(pBt, iPage, &pPage, 0, 0))!=0) )
+ if( (!pPage && ((rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0) )
|| ((rc = sqlite3PagerWrite(pPage->pDbPage))!=0)
){
goto freepage_out;
@@ -54868,7 +55353,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
u32 nLeaf; /* Initial number of leaf cells on trunk page */
iTrunk = get4byte(&pPage1->aData[32]);
- rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0, 0);
+ rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0);
if( rc!=SQLITE_OK ){
goto freepage_out;
}
@@ -54914,7 +55399,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
** first trunk in the free-list is full. Either way, the page being freed
** will become the new first trunk page in the free-list.
*/
- if( pPage==0 && SQLITE_OK!=(rc = btreeGetPage(pBt, iPage, &pPage, 0, 0)) ){
+ if( pPage==0 && SQLITE_OK!=(rc = btreeGetPage(pBt, iPage, &pPage, 0)) ){
goto freepage_out;
}
rc = sqlite3PagerWrite(pPage->pDbPage);
@@ -56813,7 +57298,7 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
}
/* Move the page currently at pgnoRoot to pgnoMove. */
- rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0, 0);
+ rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -56834,7 +57319,7 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
if( rc!=SQLITE_OK ){
return rc;
}
- rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0, 0);
+ rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -57012,7 +57497,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
return SQLITE_LOCKED_SHAREDCACHE;
}
- rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0, 0);
+ rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0);
if( rc ) return rc;
rc = sqlite3BtreeClearTable(p, iTable, 0);
if( rc ){
@@ -57047,7 +57532,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
*/
MemPage *pMove;
releasePage(pPage);
- rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0, 0);
+ rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -57057,7 +57542,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
return rc;
}
pMove = 0;
- rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0, 0);
+ rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0);
freePage(pMove, &rc);
releasePage(pMove);
if( rc!=SQLITE_OK ){
@@ -57272,7 +57757,7 @@ static void checkAppendMsg(
}
sqlite3VXPrintf(&pCheck->errMsg, 1, zFormat, ap);
va_end(ap);
- if( pCheck->errMsg.mallocFailed ){
+ if( pCheck->errMsg.accError==STRACCUM_NOMEM ){
pCheck->mallocFailed = 1;
}
}
@@ -57469,7 +57954,7 @@ static int checkTreePage(
usableSize = pBt->usableSize;
if( iPage==0 ) return 0;
if( checkRef(pCheck, iPage, zParentContext) ) return 0;
- if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0, 0))!=0 ){
+ if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){
checkAppendMsg(pCheck, zContext,
"unable to get the page. error code=%d", rc);
return 0;
@@ -58051,12 +58536,6 @@ SQLITE_PRIVATE void sqlite3BtreeCursorHints(BtCursor *pCsr, unsigned int mask){
** API functions and the related features.
*/
-/* Macro to find the minimum of two numeric values.
-*/
-#ifndef MIN
-# define MIN(x,y) ((x)<(y)?(x):(y))
-#endif
-
/*
** Structure allocated for each backup operation.
*/
@@ -58434,7 +58913,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){
DbPage *pSrcPg; /* Source page object */
rc = sqlite3PagerAcquire(pSrcPager, iSrcPg, &pSrcPg,
- PAGER_ACQUIRE_READONLY);
+ PAGER_GET_READONLY);
if( rc==SQLITE_OK ){
rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg), 0);
sqlite3PagerUnref(pSrcPg);
@@ -59589,34 +60068,29 @@ SQLITE_PRIVATE int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const C
** if both values are integers.
*/
if( combined_flags&(MEM_Int|MEM_Real) ){
- if( !(f1&(MEM_Int|MEM_Real)) ){
- return 1;
- }
- if( !(f2&(MEM_Int|MEM_Real)) ){
- return -1;
- }
- if( (f1 & f2 & MEM_Int)==0 ){
- double r1, r2;
- if( (f1&MEM_Real)==0 ){
- r1 = (double)pMem1->u.i;
- }else{
- r1 = pMem1->r;
- }
- if( (f2&MEM_Real)==0 ){
- r2 = (double)pMem2->u.i;
- }else{
- r2 = pMem2->r;
- }
- if( r1<r2 ) return -1;
- if( r1>r2 ) return 1;
- return 0;
- }else{
- assert( f1&MEM_Int );
- assert( f2&MEM_Int );
+ double r1, r2;
+ if( (f1 & f2 & MEM_Int)!=0 ){
if( pMem1->u.i < pMem2->u.i ) return -1;
if( pMem1->u.i > pMem2->u.i ) return 1;
return 0;
}
+ if( (f1&MEM_Real)!=0 ){
+ r1 = pMem1->r;
+ }else if( (f1&MEM_Int)!=0 ){
+ r1 = (double)pMem1->u.i;
+ }else{
+ return 1;
+ }
+ if( (f2&MEM_Real)!=0 ){
+ r2 = pMem2->r;
+ }else if( (f2&MEM_Int)!=0 ){
+ r2 = (double)pMem2->u.i;
+ }else{
+ return -1;
+ }
+ if( r1<r2 ) return -1;
+ if( r1>r2 ) return 1;
+ return 0;
}
/* If one value is a string and the other is a blob, the string is less.
@@ -59796,27 +60270,94 @@ SQLITE_PRIVATE sqlite3_value *sqlite3ValueNew(sqlite3 *db){
}
/*
-** Create a new sqlite3_value object, containing the value of pExpr.
+** Context object passed by sqlite3Stat4ProbeSetValue() through to
+** valueNew(). See comments above valueNew() for details.
+*/
+struct ValueNewStat4Ctx {
+ Parse *pParse;
+ Index *pIdx;
+ UnpackedRecord **ppRec;
+ int iVal;
+};
+
+/*
+** Allocate and return a pointer to a new sqlite3_value object. If
+** the second argument to this function is NULL, the object is allocated
+** by calling sqlite3ValueNew().
**
-** This only works for very simple expressions that consist of one constant
-** token (i.e. "5", "5.1", "'a string'"). If the expression can
-** be converted directly into a value, then the value is allocated and
-** a pointer written to *ppVal. The caller is responsible for deallocating
-** the value by passing it to sqlite3ValueFree() later on. If the expression
-** cannot be converted to a value, then *ppVal is set to NULL.
+** Otherwise, if the second argument is non-zero, then this function is
+** being called indirectly by sqlite3Stat4ProbeSetValue(). If it has not
+** already been allocated, allocate the UnpackedRecord structure that
+** that function will return to its caller here. Then return a pointer
+** an sqlite3_value within the UnpackedRecord.a[] array.
*/
-SQLITE_PRIVATE int sqlite3ValueFromExpr(
- sqlite3 *db, /* The database connection */
- Expr *pExpr, /* The expression to evaluate */
- u8 enc, /* Encoding to use */
- u8 affinity, /* Affinity to use */
- sqlite3_value **ppVal /* Write the new value here */
+static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ if( p ){
+ UnpackedRecord *pRec = p->ppRec[0];
+
+ if( pRec==0 ){
+ Index *pIdx = p->pIdx; /* Index being probed */
+ int nByte; /* Bytes of space to allocate */
+ int i; /* Counter variable */
+ int nCol = pIdx->nColumn+1; /* Number of index columns including rowid */
+
+ nByte = sizeof(Mem) * nCol + sizeof(UnpackedRecord);
+ pRec = (UnpackedRecord*)sqlite3DbMallocZero(db, nByte);
+ if( pRec ){
+ pRec->pKeyInfo = sqlite3IndexKeyinfo(p->pParse, pIdx);
+ if( pRec->pKeyInfo ){
+ assert( pRec->pKeyInfo->nField+1==nCol );
+ pRec->pKeyInfo->enc = ENC(db);
+ pRec->flags = UNPACKED_PREFIX_MATCH;
+ pRec->aMem = (Mem *)&pRec[1];
+ for(i=0; i<nCol; i++){
+ pRec->aMem[i].flags = MEM_Null;
+ pRec->aMem[i].type = SQLITE_NULL;
+ pRec->aMem[i].db = db;
+ }
+ }else{
+ sqlite3DbFree(db, pRec);
+ pRec = 0;
+ }
+ }
+ if( pRec==0 ) return 0;
+ p->ppRec[0] = pRec;
+ }
+
+ pRec->nField = p->iVal+1;
+ return &pRec->aMem[p->iVal];
+ }
+#else
+ UNUSED_PARAMETER(p);
+#endif /* defined(SQLITE_ENABLE_STAT3_OR_STAT4) */
+ return sqlite3ValueNew(db);
+}
+
+/*
+** Extract a value from the supplied expression in the manner described
+** above sqlite3ValueFromExpr(). Allocate the sqlite3_value object
+** using valueNew().
+**
+** If pCtx is NULL and an error occurs after the sqlite3_value object
+** has been allocated, it is freed before returning. Or, if pCtx is not
+** NULL, it is assumed that the caller will free any allocated object
+** in all cases.
+*/
+static int valueFromExpr(
+ sqlite3 *db, /* The database connection */
+ Expr *pExpr, /* The expression to evaluate */
+ u8 enc, /* Encoding to use */
+ u8 affinity, /* Affinity to use */
+ sqlite3_value **ppVal, /* Write the new value here */
+ struct ValueNewStat4Ctx *pCtx /* Second argument for valueNew() */
){
int op;
char *zVal = 0;
sqlite3_value *pVal = 0;
int negInt = 1;
const char *zNeg = "";
+ int rc = SQLITE_OK;
if( !pExpr ){
*ppVal = 0;
@@ -59824,11 +60365,11 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr(
}
op = pExpr->op;
- /* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT3.
+ /* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT4.
** The ifdef here is to enable us to achieve 100% branch test coverage even
- ** when SQLITE_ENABLE_STAT3 is omitted.
+ ** when SQLITE_ENABLE_STAT4 is omitted.
*/
-#ifdef SQLITE_ENABLE_STAT3
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
if( op==TK_REGISTER ) op = pExpr->op2;
#else
if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
@@ -59846,7 +60387,7 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr(
}
if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){
- pVal = sqlite3ValueNew(db);
+ pVal = valueNew(db, pCtx);
if( pVal==0 ) goto no_mem;
if( ExprHasProperty(pExpr, EP_IntValue) ){
sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt);
@@ -59863,11 +60404,13 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr(
}
if( pVal->flags & (MEM_Int|MEM_Real) ) pVal->flags &= ~MEM_Str;
if( enc!=SQLITE_UTF8 ){
- sqlite3VdbeChangeEncoding(pVal, enc);
+ rc = sqlite3VdbeChangeEncoding(pVal, enc);
}
}else if( op==TK_UMINUS ) {
/* This branch happens for multiple negative signs. Ex: -(-5) */
- if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){
+ if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal)
+ && pVal!=0
+ ){
sqlite3VdbeMemNumerify(pVal);
if( pVal->u.i==SMALLEST_INT64 ){
pVal->flags &= MEM_Int;
@@ -59880,7 +60423,7 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr(
sqlite3ValueApplyAffinity(pVal, affinity, enc);
}
}else if( op==TK_NULL ){
- pVal = sqlite3ValueNew(db);
+ pVal = valueNew(db, pCtx);
if( pVal==0 ) goto no_mem;
}
#ifndef SQLITE_OMIT_BLOB_LITERAL
@@ -59888,7 +60431,7 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr(
int nVal;
assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' );
assert( pExpr->u.zToken[1]=='\'' );
- pVal = sqlite3ValueNew(db);
+ pVal = valueNew(db, pCtx);
if( !pVal ) goto no_mem;
zVal = &pExpr->u.zToken[2];
nVal = sqlite3Strlen30(zVal)-1;
@@ -59902,17 +60445,205 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr(
sqlite3VdbeMemStoreType(pVal);
}
*ppVal = pVal;
- return SQLITE_OK;
+ return rc;
no_mem:
db->mallocFailed = 1;
sqlite3DbFree(db, zVal);
- sqlite3ValueFree(pVal);
- *ppVal = 0;
+ assert( *ppVal==0 );
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ if( pCtx==0 ) sqlite3ValueFree(pVal);
+#else
+ assert( pCtx==0 ); sqlite3ValueFree(pVal);
+#endif
return SQLITE_NOMEM;
}
/*
+** Create a new sqlite3_value object, containing the value of pExpr.
+**
+** This only works for very simple expressions that consist of one constant
+** token (i.e. "5", "5.1", "'a string'"). If the expression can
+** be converted directly into a value, then the value is allocated and
+** a pointer written to *ppVal. The caller is responsible for deallocating
+** the value by passing it to sqlite3ValueFree() later on. If the expression
+** cannot be converted to a value, then *ppVal is set to NULL.
+*/
+SQLITE_PRIVATE int sqlite3ValueFromExpr(
+ sqlite3 *db, /* The database connection */
+ Expr *pExpr, /* The expression to evaluate */
+ u8 enc, /* Encoding to use */
+ u8 affinity, /* Affinity to use */
+ sqlite3_value **ppVal /* Write the new value here */
+){
+ return valueFromExpr(db, pExpr, enc, affinity, ppVal, 0);
+}
+
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+/*
+** The implementation of the sqlite_record() function. This function accepts
+** a single argument of any type. The return value is a formatted database
+** record (a blob) containing the argument value.
+**
+** This is used to convert the value stored in the 'sample' column of the
+** sqlite_stat3 table to the record format SQLite uses internally.
+*/
+static void recordFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const int file_format = 1;
+ int iSerial; /* Serial type */
+ int nSerial; /* Bytes of space for iSerial as varint */
+ int nVal; /* Bytes of space required for argv[0] */
+ int nRet;
+ sqlite3 *db;
+ u8 *aRet;
+
+ UNUSED_PARAMETER( argc );
+ iSerial = sqlite3VdbeSerialType(argv[0], file_format);
+ nSerial = sqlite3VarintLen(iSerial);
+ nVal = sqlite3VdbeSerialTypeLen(iSerial);
+ db = sqlite3_context_db_handle(context);
+
+ nRet = 1 + nSerial + nVal;
+ aRet = sqlite3DbMallocRaw(db, nRet);
+ if( aRet==0 ){
+ sqlite3_result_error_nomem(context);
+ }else{
+ aRet[0] = nSerial+1;
+ sqlite3PutVarint(&aRet[1], iSerial);
+ sqlite3VdbeSerialPut(&aRet[1+nSerial], nVal, argv[0], file_format);
+ sqlite3_result_blob(context, aRet, nRet, SQLITE_TRANSIENT);
+ sqlite3DbFree(db, aRet);
+ }
+}
+
+/*
+** Register built-in functions used to help read ANALYZE data.
+*/
+SQLITE_PRIVATE void sqlite3AnalyzeFunctions(void){
+ static SQLITE_WSD FuncDef aAnalyzeTableFuncs[] = {
+ FUNCTION(sqlite_record, 1, 0, 0, recordFunc),
+ };
+ int i;
+ FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
+ FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aAnalyzeTableFuncs);
+ for(i=0; i<ArraySize(aAnalyzeTableFuncs); i++){
+ sqlite3FuncDefInsert(pHash, &aFunc[i]);
+ }
+}
+
+/*
+** This function is used to allocate and populate UnpackedRecord
+** structures intended to be compared against sample index keys stored
+** in the sqlite_stat4 table.
+**
+** A single call to this function attempts to populates field iVal (leftmost
+** is 0 etc.) of the unpacked record with a value extracted from expression
+** pExpr. Extraction of values is possible if:
+**
+** * (pExpr==0). In this case the value is assumed to be an SQL NULL,
+**
+** * The expression is a bound variable, and this is a reprepare, or
+**
+** * The sqlite3ValueFromExpr() function is able to extract a value
+** from the expression (i.e. the expression is a literal value).
+**
+** If a value can be extracted, the affinity passed as the 5th argument
+** is applied to it before it is copied into the UnpackedRecord. Output
+** parameter *pbOk is set to true if a value is extracted, or false
+** otherwise.
+**
+** When this function is called, *ppRec must either point to an object
+** allocated by an earlier call to this function, or must be NULL. If it
+** is NULL and a value can be successfully extracted, a new UnpackedRecord
+** is allocated (and *ppRec set to point to it) before returning.
+**
+** Unless an error is encountered, SQLITE_OK is returned. It is not an
+** error if a value cannot be extracted from pExpr. If an error does
+** occur, an SQLite error code is returned.
+*/
+SQLITE_PRIVATE int sqlite3Stat4ProbeSetValue(
+ Parse *pParse, /* Parse context */
+ Index *pIdx, /* Index being probed */
+ UnpackedRecord **ppRec, /* IN/OUT: Probe record */
+ Expr *pExpr, /* The expression to extract a value from */
+ u8 affinity, /* Affinity to use */
+ int iVal, /* Array element to populate */
+ int *pbOk /* OUT: True if value was extracted */
+){
+ int rc = SQLITE_OK;
+ sqlite3_value *pVal = 0;
+ sqlite3 *db = pParse->db;
+
+
+ struct ValueNewStat4Ctx alloc;
+ alloc.pParse = pParse;
+ alloc.pIdx = pIdx;
+ alloc.ppRec = ppRec;
+ alloc.iVal = iVal;
+
+ /* Skip over any TK_COLLATE nodes */
+ pExpr = sqlite3ExprSkipCollate(pExpr);
+
+ if( !pExpr ){
+ pVal = valueNew(db, &alloc);
+ if( pVal ){
+ sqlite3VdbeMemSetNull((Mem*)pVal);
+ *pbOk = 1;
+ }
+ }else if( pExpr->op==TK_VARIABLE
+ || (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE)
+ ){
+ Vdbe *v;
+ int iBindVar = pExpr->iColumn;
+ sqlite3VdbeSetVarmask(pParse->pVdbe, iBindVar);
+ if( (v = pParse->pReprepare)!=0 ){
+ pVal = valueNew(db, &alloc);
+ if( pVal ){
+ rc = sqlite3VdbeMemCopy((Mem*)pVal, &v->aVar[iBindVar-1]);
+ if( rc==SQLITE_OK ){
+ sqlite3ValueApplyAffinity(pVal, affinity, ENC(db));
+ }
+ pVal->db = pParse->db;
+ *pbOk = 1;
+ sqlite3VdbeMemStoreType((Mem*)pVal);
+ }
+ }else{
+ *pbOk = 0;
+ }
+ }else{
+ rc = valueFromExpr(db, pExpr, ENC(db), affinity, &pVal, &alloc);
+ *pbOk = (pVal!=0);
+ }
+
+ assert( pVal==0 || pVal->db==db );
+ return rc;
+}
+
+/*
+** Unless it is NULL, the argument must be an UnpackedRecord object returned
+** by an earlier call to sqlite3Stat4ProbeSetValue(). This call deletes
+** the object.
+*/
+SQLITE_PRIVATE void sqlite3Stat4ProbeFree(UnpackedRecord *pRec){
+ if( pRec ){
+ int i;
+ int nCol = pRec->pKeyInfo->nField+1;
+ Mem *aMem = pRec->aMem;
+ sqlite3 *db = aMem[0].db;
+ for(i=0; i<nCol; i++){
+ sqlite3DbFree(db, aMem[i].zMalloc);
+ }
+ sqlite3DbFree(db, pRec->pKeyInfo);
+ sqlite3DbFree(db, pRec);
+ }
+}
+#endif /* ifdef SQLITE_ENABLE_STAT4 */
+
+/*
** Change the string value of an sqlite3_value object
*/
SQLITE_PRIVATE void sqlite3ValueSetStr(
@@ -60202,8 +60933,8 @@ SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *p){
SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *p, int x){
int j = -1-x;
assert( p->magic==VDBE_MAGIC_INIT );
- assert( j>=0 && j<p->nLabel );
- if( p->aLabel ){
+ assert( j<p->nLabel );
+ if( j>=0 && p->aLabel ){
p->aLabel[j] = p->nOp;
}
}
@@ -60355,32 +61086,64 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
Op *pOp;
int *aLabel = p->aLabel;
p->readOnly = 1;
+ p->bIsReader = 0;
for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){
u8 opcode = pOp->opcode;
- pOp->opflags = sqlite3OpcodeProperty[opcode];
- if( opcode==OP_Function || opcode==OP_AggStep ){
- if( pOp->p5>nMaxArgs ) nMaxArgs = pOp->p5;
- }else if( (opcode==OP_Transaction && pOp->p2!=0) || opcode==OP_Vacuum ){
- p->readOnly = 0;
+ /* NOTE: Be sure to update mkopcodeh.awk when adding or removing
+ ** cases from this switch! */
+ switch( opcode ){
+ case OP_Function:
+ case OP_AggStep: {
+ if( pOp->p5>nMaxArgs ) nMaxArgs = pOp->p5;
+ break;
+ }
+ case OP_Transaction: {
+ if( pOp->p2!=0 ) p->readOnly = 0;
+ /* fall thru */
+ }
+ case OP_AutoCommit:
+ case OP_Savepoint: {
+ p->bIsReader = 1;
+ break;
+ }
+#ifndef SQLITE_OMIT_WAL
+ case OP_Checkpoint:
+#endif
+ case OP_Vacuum:
+ case OP_JournalMode: {
+ p->readOnly = 0;
+ p->bIsReader = 1;
+ break;
+ }
#ifndef SQLITE_OMIT_VIRTUALTABLE
- }else if( opcode==OP_VUpdate ){
- if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
- }else if( opcode==OP_VFilter ){
- int n;
- assert( p->nOp - i >= 3 );
- assert( pOp[-1].opcode==OP_Integer );
- n = pOp[-1].p1;
- if( n>nMaxArgs ) nMaxArgs = n;
+ case OP_VUpdate: {
+ if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
+ break;
+ }
+ case OP_VFilter: {
+ int n;
+ assert( p->nOp - i >= 3 );
+ assert( pOp[-1].opcode==OP_Integer );
+ n = pOp[-1].p1;
+ if( n>nMaxArgs ) nMaxArgs = n;
+ break;
+ }
#endif
- }else if( opcode==OP_Next || opcode==OP_SorterNext ){
- pOp->p4.xAdvance = sqlite3BtreeNext;
- pOp->p4type = P4_ADVANCE;
- }else if( opcode==OP_Prev ){
- pOp->p4.xAdvance = sqlite3BtreePrevious;
- pOp->p4type = P4_ADVANCE;
+ case OP_Next:
+ case OP_SorterNext: {
+ pOp->p4.xAdvance = sqlite3BtreeNext;
+ pOp->p4type = P4_ADVANCE;
+ break;
+ }
+ case OP_Prev: {
+ pOp->p4.xAdvance = sqlite3BtreePrevious;
+ pOp->p4type = P4_ADVANCE;
+ break;
+ }
}
+ pOp->opflags = sqlite3OpcodeProperty[opcode];
if( (pOp->opflags & OPFLG_JUMP)!=0 && pOp->p2<0 ){
assert( -1-pOp->p2<p->nLabel );
pOp->p2 = aLabel[-1-pOp->p2];
@@ -60388,8 +61151,8 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
}
sqlite3DbFree(p->db, p->aLabel);
p->aLabel = 0;
-
*pMaxFuncArgs = nMaxArgs;
+ assert( p->bIsReader!=0 || p->btreeMask==0 );
}
/*
@@ -60515,8 +61278,7 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u8 val){
** the address of the next instruction to be coded.
*/
SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe *p, int addr){
- assert( addr>=0 || p->db->mallocFailed );
- if( addr>=0 ) sqlite3VdbeChangeP2(p, addr, p->nOp);
+ if( ALWAYS(addr>=0) ) sqlite3VdbeChangeP2(p, addr, p->nOp);
}
@@ -60525,7 +61287,7 @@ SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe *p, int addr){
** the FuncDef is not ephermal, then do nothing.
*/
static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef){
- if( ALWAYS(pDef) && (pDef->flags & SQLITE_FUNC_EPHEM)!=0 ){
+ if( ALWAYS(pDef) && (pDef->funcFlags & SQLITE_FUNC_EPHEM)!=0 ){
sqlite3DbFree(db, pDef);
}
}
@@ -60552,13 +61314,6 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
if( db->pnBytesFreed==0 ) sqlite3_free(p4);
break;
}
- case P4_VDBEFUNC: {
- VdbeFunc *pVdbeFunc = (VdbeFunc *)p4;
- freeEphemeralFunction(db, pVdbeFunc->pFunc);
- if( db->pnBytesFreed==0 ) sqlite3VdbeDeleteAuxData(pVdbeFunc, 0);
- sqlite3DbFree(db, pVdbeFunc);
- break;
- }
case P4_FUNCDEF: {
freeEphemeralFunction(db, (FuncDef*)p4);
break;
@@ -60677,20 +61432,13 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int
pOp->p4.p = 0;
pOp->p4type = P4_NOTUSED;
}else if( n==P4_KEYINFO ){
- KeyInfo *pKeyInfo;
- int nField, nByte;
-
- nField = ((KeyInfo*)zP4)->nField;
- nByte = sizeof(*pKeyInfo) + (nField-1)*sizeof(pKeyInfo->aColl[0]) + nField;
- pKeyInfo = sqlite3DbMallocRaw(0, nByte);
- pOp->p4.pKeyInfo = pKeyInfo;
- if( pKeyInfo ){
- u8 *aSortOrder;
- memcpy((char*)pKeyInfo, zP4, nByte - nField);
- aSortOrder = pKeyInfo->aSortOrder;
- assert( aSortOrder!=0 );
- pKeyInfo->aSortOrder = (unsigned char*)&pKeyInfo->aColl[nField];
- memcpy(pKeyInfo->aSortOrder, aSortOrder, nField);
+ KeyInfo *pOrig, *pNew;
+
+ pOrig = (KeyInfo*)zP4;
+ pOp->p4.pKeyInfo = pNew = sqlite3KeyInfoAlloc(db, pOrig->nField);
+ if( pNew ){
+ memcpy(pNew->aColl, pOrig->aColl, pOrig->nField*sizeof(pNew->aColl[0]));
+ memcpy(pNew->aSortOrder, pOrig->aSortOrder, pOrig->nField);
pOp->p4type = P4_KEYINFO;
}else{
p->db->mallocFailed = 1;
@@ -61588,6 +62336,10 @@ static void closeAllCursors(Vdbe *p){
p->pDelFrame = pDel->pParent;
sqlite3VdbeFrameDelete(pDel);
}
+
+ /* Delete any auxdata allocations made by the VM */
+ sqlite3VdbeDeleteAuxData(p, -1, 0);
+ assert( p->pAuxData==0 );
}
/*
@@ -61696,7 +62448,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
** required, as an xSync() callback may add an attached database
** to the transaction.
*/
- rc = sqlite3VtabSync(db, &p->zErrMsg);
+ rc = sqlite3VtabSync(db, p);
/* This loop determines (a) if the commit hook should be invoked and
** (b) how many database files have open write transactions, not
@@ -61915,7 +62667,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
}
/*
-** This routine checks that the sqlite3.activeVdbeCnt count variable
+** This routine checks that the sqlite3.nVdbeActive count variable
** matches the number of vdbe's in the list sqlite3.pVdbe that are
** currently active. An assertion fails if the two counts do not match.
** This is an internal self-check only - it is not an essential processing
@@ -61928,16 +62680,19 @@ static void checkActiveVdbeCnt(sqlite3 *db){
Vdbe *p;
int cnt = 0;
int nWrite = 0;
+ int nRead = 0;
p = db->pVdbe;
while( p ){
if( p->magic==VDBE_MAGIC_RUN && p->pc>=0 ){
cnt++;
if( p->readOnly==0 ) nWrite++;
+ if( p->bIsReader ) nRead++;
}
p = p->pNext;
}
- assert( cnt==db->activeVdbeCnt );
- assert( nWrite==db->writeVdbeCnt );
+ assert( cnt==db->nVdbeActive );
+ assert( nWrite==db->nVdbeWrite );
+ assert( nRead==db->nVdbeRead );
}
#else
#define checkActiveVdbeCnt(x)
@@ -61948,7 +62703,7 @@ static void checkActiveVdbeCnt(sqlite3 *db){
** close it now. Argument eOp must be either SAVEPOINT_ROLLBACK or
** SAVEPOINT_RELEASE. If it is SAVEPOINT_ROLLBACK, then the statement
** transaction is rolled back. If eOp is SAVEPOINT_RELEASE, then the
-** statement transaction is commtted.
+** statement transaction is committed.
**
** If an IO error occurs, an SQLITE_IOERR_XXX error code is returned.
** Otherwise SQLITE_OK.
@@ -62002,6 +62757,7 @@ SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
** the statement transaction was opened. */
if( eOp==SAVEPOINT_ROLLBACK ){
db->nDeferredCons = p->nStmtDefCons;
+ db->nDeferredImmCons = p->nStmtDefImmCons;
}
}
return rc;
@@ -62020,7 +62776,9 @@ SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
#ifndef SQLITE_OMIT_FOREIGN_KEY
SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *p, int deferred){
sqlite3 *db = p->db;
- if( (deferred && db->nDeferredCons>0) || (!deferred && p->nFkConstraint>0) ){
+ if( (deferred && (db->nDeferredCons+db->nDeferredImmCons)>0)
+ || (!deferred && p->nFkConstraint>0)
+ ){
p->rc = SQLITE_CONSTRAINT_FOREIGNKEY;
p->errorAction = OE_Abort;
sqlite3SetString(&p->zErrMsg, db, "foreign key constraint failed");
@@ -62073,8 +62831,9 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
}
checkActiveVdbeCnt(db);
- /* No commit or rollback needed if the program never started */
- if( p->pc>=0 ){
+ /* No commit or rollback needed if the program never started or if the
+ ** SQL statement does not read or write a database file. */
+ if( p->pc>=0 && p->bIsReader ){
int mrc; /* Primary error code from p->rc */
int eStatementOp = 0;
int isSpecialError; /* Set to true if a 'special' error */
@@ -62127,7 +62886,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
*/
if( !sqlite3VtabInSync(db)
&& db->autoCommit
- && db->writeVdbeCnt==(p->readOnly==0)
+ && db->nVdbeWrite==(p->readOnly==0)
){
if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
rc = sqlite3VdbeCheckFk(p, 1);
@@ -62152,6 +62911,8 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
sqlite3RollbackAll(db, SQLITE_OK);
}else{
db->nDeferredCons = 0;
+ db->nDeferredImmCons = 0;
+ db->flags &= ~SQLITE_DeferFKs;
sqlite3CommitInternalChanges(db);
}
}else{
@@ -62208,11 +62969,12 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
/* We have successfully halted and closed the VM. Record this fact. */
if( p->pc>=0 ){
- db->activeVdbeCnt--;
- if( !p->readOnly ){
- db->writeVdbeCnt--;
- }
- assert( db->activeVdbeCnt>=db->writeVdbeCnt );
+ db->nVdbeActive--;
+ if( !p->readOnly ) db->nVdbeWrite--;
+ if( p->bIsReader ) db->nVdbeRead--;
+ assert( db->nVdbeActive>=db->nVdbeRead );
+ assert( db->nVdbeRead>=db->nVdbeWrite );
+ assert( db->nVdbeWrite>=0 );
}
p->magic = VDBE_MAGIC_HALT;
checkActiveVdbeCnt(db);
@@ -62228,7 +62990,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
sqlite3ConnectionUnlocked(db);
}
- assert( db->activeVdbeCnt>0 || db->autoCommit==0 || db->nStatement==0 );
+ assert( db->nVdbeActive>0 || db->autoCommit==0 || db->nStatement==0 );
return (p->rc==SQLITE_BUSY ? SQLITE_BUSY : SQLITE_OK);
}
@@ -62357,6 +63119,7 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){
}
}
#endif
+ p->iCurrentTime = 0;
p->magic = VDBE_MAGIC_INIT;
return p->rc & db->errMask;
}
@@ -62376,20 +63139,35 @@ SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe *p){
}
/*
-** Call the destructor for each auxdata entry in pVdbeFunc for which
-** the corresponding bit in mask is clear. Auxdata entries beyond 31
-** are always destroyed. To destroy all auxdata entries, call this
-** routine with mask==0.
+** If parameter iOp is less than zero, then invoke the destructor for
+** all auxiliary data pointers currently cached by the VM passed as
+** the first argument.
+**
+** Or, if iOp is greater than or equal to zero, then the destructor is
+** only invoked for those auxiliary data pointers created by the user
+** function invoked by the OP_Function opcode at instruction iOp of
+** VM pVdbe, and only then if:
+**
+** * the associated function parameter is the 32nd or later (counting
+** from left to right), or
+**
+** * the corresponding bit in argument mask is clear (where the first
+** function parameter corrsponds to bit 0 etc.).
*/
-SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(VdbeFunc *pVdbeFunc, int mask){
- int i;
- for(i=0; i<pVdbeFunc->nAux; i++){
- struct AuxData *pAux = &pVdbeFunc->apAux[i];
- if( (i>31 || !(mask&(((u32)1)<<i))) && pAux->pAux ){
+SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(Vdbe *pVdbe, int iOp, int mask){
+ AuxData **pp = &pVdbe->pAuxData;
+ while( *pp ){
+ AuxData *pAux = *pp;
+ if( (iOp<0)
+ || (pAux->iOp==iOp && (pAux->iArg>31 || !(mask & ((u32)1<<pAux->iArg))))
+ ){
if( pAux->xDelete ){
pAux->xDelete(pAux->pAux);
}
- pAux->pAux = 0;
+ *pp = pAux->pNext;
+ sqlite3DbFree(pVdbe->db, pAux);
+ }else{
+ pp= &pAux->pNext;
}
}
}
@@ -62908,11 +63686,10 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompare(
int nKey1, const void *pKey1, /* Left key */
UnpackedRecord *pPKey2 /* Right key */
){
- int d1; /* Offset into aKey[] of next data element */
+ u32 d1; /* Offset into aKey[] of next data element */
u32 idx1; /* Offset into aKey[] of next header element */
u32 szHdr1; /* Number of bytes in header */
int i = 0;
- int nField;
int rc = 0;
const unsigned char *aKey1 = (const unsigned char *)pKey1;
KeyInfo *pKeyInfo;
@@ -62935,14 +63712,25 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompare(
idx1 = getVarint32(aKey1, szHdr1);
d1 = szHdr1;
- nField = pKeyInfo->nField;
+ assert( pKeyInfo->nField+1>=pPKey2->nField );
assert( pKeyInfo->aSortOrder!=0 );
while( idx1<szHdr1 && i<pPKey2->nField ){
u32 serial_type1;
/* Read the serial types for the next element in each key. */
idx1 += getVarint32( aKey1+idx1, serial_type1 );
- if( d1>=nKey1 && sqlite3VdbeSerialTypeLen(serial_type1)>0 ) break;
+
+ /* Verify that there is enough key space remaining to avoid
+ ** a buffer overread. The "d1+serial_type1+2" subexpression will
+ ** always be greater than or equal to the amount of required key space.
+ ** Use that approximation to avoid the more expensive call to
+ ** sqlite3VdbeSerialTypeLen() in the common case.
+ */
+ if( d1+serial_type1+2>(u32)nKey1
+ && d1+sqlite3VdbeSerialTypeLen(serial_type1)>(u32)nKey1
+ ){
+ break;
+ }
/* Extract the values to be compared.
*/
@@ -62950,13 +63738,12 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompare(
/* Do the comparison
*/
- rc = sqlite3MemCompare(&mem1, &pPKey2->aMem[i],
- i<nField ? pKeyInfo->aColl[i] : 0);
+ rc = sqlite3MemCompare(&mem1, &pPKey2->aMem[i], pKeyInfo->aColl[i]);
if( rc!=0 ){
assert( mem1.zMalloc==0 ); /* See comment below */
/* Invert the result if we are using DESC sort order. */
- if( i<nField && pKeyInfo->aSortOrder[i] ){
+ if( pKeyInfo->aSortOrder[i] ){
rc = -rc;
}
@@ -63171,7 +63958,7 @@ SQLITE_PRIVATE sqlite3 *sqlite3VdbeDb(Vdbe *v){
**
** The returned value must be freed by the caller using sqlite3ValueFree().
*/
-SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetValue(Vdbe *v, int iVar, u8 aff){
+SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe *v, int iVar, u8 aff){
assert( iVar>0 );
if( v ){
Mem *pMem = &v->aVar[iVar-1];
@@ -63202,6 +63989,21 @@ SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){
}
}
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+/*
+** Transfer error message text from an sqlite3_vtab.zErrMsg (text stored
+** in memory obtained from sqlite3_malloc) into a Vdbe.zErrMsg (text stored
+** in memory obtained from sqlite3DbMalloc).
+*/
+SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe *p, sqlite3_vtab *pVtab){
+ sqlite3 *db = p->db;
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = sqlite3DbStrDup(db, pVtab->zErrMsg);
+ sqlite3_free(pVtab->zErrMsg);
+ pVtab->zErrMsg = 0;
+}
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
/************** End of vdbeaux.c *********************************************/
/************** Begin file vdbeapi.c *****************************************/
/*
@@ -63415,12 +64217,14 @@ SQLITE_API void sqlite3_result_double(sqlite3_context *pCtx, double rVal){
SQLITE_API void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){
assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
pCtx->isError = SQLITE_ERROR;
+ pCtx->fErrorOrAux = 1;
sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF8, SQLITE_TRANSIENT);
}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){
assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
pCtx->isError = SQLITE_ERROR;
+ pCtx->fErrorOrAux = 1;
sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT);
}
#endif
@@ -63484,6 +64288,7 @@ SQLITE_API void sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){
}
SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
pCtx->isError = errCode;
+ pCtx->fErrorOrAux = 1;
if( pCtx->s.flags & MEM_Null ){
sqlite3VdbeMemSetStr(&pCtx->s, sqlite3ErrStr(errCode), -1,
SQLITE_UTF8, SQLITE_STATIC);
@@ -63494,6 +64299,7 @@ SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){
assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
pCtx->isError = SQLITE_TOOBIG;
+ pCtx->fErrorOrAux = 1;
sqlite3VdbeMemSetStr(&pCtx->s, "string or blob too big", -1,
SQLITE_UTF8, SQLITE_STATIC);
}
@@ -63503,6 +64309,7 @@ SQLITE_API void sqlite3_result_error_nomem(sqlite3_context *pCtx){
assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
sqlite3VdbeMemSetNull(&pCtx->s);
pCtx->isError = SQLITE_NOMEM;
+ pCtx->fErrorOrAux = 1;
pCtx->s.db->mallocFailed = 1;
}
@@ -63586,11 +64393,13 @@ static int sqlite3Step(Vdbe *p){
** reset the interrupt flag. This prevents a call to sqlite3_interrupt
** from interrupting a statement that has not yet started.
*/
- if( db->activeVdbeCnt==0 ){
+ if( db->nVdbeActive==0 ){
db->u1.isInterrupted = 0;
}
- assert( db->writeVdbeCnt>0 || db->autoCommit==0 || db->nDeferredCons==0 );
+ assert( db->nVdbeWrite>0 || db->autoCommit==0
+ || (db->nDeferredCons==0 && db->nDeferredImmCons==0)
+ );
#ifndef SQLITE_OMIT_TRACE
if( db->xProfile && !db->init.busy ){
@@ -63598,8 +64407,9 @@ static int sqlite3Step(Vdbe *p){
}
#endif
- db->activeVdbeCnt++;
- if( p->readOnly==0 ) db->writeVdbeCnt++;
+ db->nVdbeActive++;
+ if( p->readOnly==0 ) db->nVdbeWrite++;
+ if( p->bIsReader ) db->nVdbeRead++;
p->pc = 0;
}
#ifndef SQLITE_OMIT_EXPLAIN
@@ -63608,9 +64418,9 @@ static int sqlite3Step(Vdbe *p){
}else
#endif /* SQLITE_OMIT_EXPLAIN */
{
- db->vdbeExecCnt++;
+ db->nVdbeExec++;
rc = sqlite3VdbeExec(p);
- db->vdbeExecCnt--;
+ db->nVdbeExec--;
}
#ifndef SQLITE_OMIT_TRACE
@@ -63706,6 +64516,7 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){
return rc;
}
+
/*
** Extract the user data from a sqlite3_context structure and return a
** pointer to it.
@@ -63731,6 +64542,19 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){
}
/*
+** Return the current time for a statement
+*/
+SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context *p){
+ Vdbe *v = p->pVdbe;
+ int rc;
+ if( v->iCurrentTime==0 ){
+ rc = sqlite3OsCurrentTimeInt64(p->s.db->pVfs, &v->iCurrentTime);
+ if( rc ) v->iCurrentTime = 0;
+ }
+ return v->iCurrentTime;
+}
+
+/*
** The following is the implementation of an SQL function that always
** fails with an error message stating that the function is used in the
** wrong context. The sqlite3_overload_function() API might construct
@@ -63785,14 +64609,14 @@ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){
** the user-function defined by pCtx.
*/
SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){
- VdbeFunc *pVdbeFunc;
+ AuxData *pAuxData;
assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- pVdbeFunc = pCtx->pVdbeFunc;
- if( !pVdbeFunc || iArg>=pVdbeFunc->nAux || iArg<0 ){
- return 0;
+ for(pAuxData=pCtx->pVdbe->pAuxData; pAuxData; pAuxData=pAuxData->pNext){
+ if( pAuxData->iOp==pCtx->iOp && pAuxData->iArg==iArg ) break;
}
- return pVdbeFunc->apAux[iArg].pAux;
+
+ return (pAuxData ? pAuxData->pAux : 0);
}
/*
@@ -63806,29 +64630,30 @@ SQLITE_API void sqlite3_set_auxdata(
void *pAux,
void (*xDelete)(void*)
){
- struct AuxData *pAuxData;
- VdbeFunc *pVdbeFunc;
- if( iArg<0 ) goto failed;
+ AuxData *pAuxData;
+ Vdbe *pVdbe = pCtx->pVdbe;
assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- pVdbeFunc = pCtx->pVdbeFunc;
- if( !pVdbeFunc || pVdbeFunc->nAux<=iArg ){
- int nAux = (pVdbeFunc ? pVdbeFunc->nAux : 0);
- int nMalloc = sizeof(VdbeFunc) + sizeof(struct AuxData)*iArg;
- pVdbeFunc = sqlite3DbRealloc(pCtx->s.db, pVdbeFunc, nMalloc);
- if( !pVdbeFunc ){
- goto failed;
- }
- pCtx->pVdbeFunc = pVdbeFunc;
- memset(&pVdbeFunc->apAux[nAux], 0, sizeof(struct AuxData)*(iArg+1-nAux));
- pVdbeFunc->nAux = iArg+1;
- pVdbeFunc->pFunc = pCtx->pFunc;
- }
+ if( iArg<0 ) goto failed;
- pAuxData = &pVdbeFunc->apAux[iArg];
- if( pAuxData->pAux && pAuxData->xDelete ){
+ for(pAuxData=pVdbe->pAuxData; pAuxData; pAuxData=pAuxData->pNext){
+ if( pAuxData->iOp==pCtx->iOp && pAuxData->iArg==iArg ) break;
+ }
+ if( pAuxData==0 ){
+ pAuxData = sqlite3DbMallocZero(pVdbe->db, sizeof(AuxData));
+ if( !pAuxData ) goto failed;
+ pAuxData->iOp = pCtx->iOp;
+ pAuxData->iArg = iArg;
+ pAuxData->pNext = pVdbe->pAuxData;
+ pVdbe->pAuxData = pAuxData;
+ if( pCtx->fErrorOrAux==0 ){
+ pCtx->isError = 0;
+ pCtx->fErrorOrAux = 1;
+ }
+ }else if( pAuxData->xDelete ){
pAuxData->xDelete(pAuxData->pAux);
}
+
pAuxData->pAux = pAux;
pAuxData->xDelete = xDelete;
return;
@@ -64017,13 +64842,6 @@ SQLITE_API int sqlite3_column_type(sqlite3_stmt *pStmt, int i){
return iType;
}
-/* The following function is experimental and subject to change or
-** removal */
-/*int sqlite3_column_numeric_type(sqlite3_stmt *pStmt, int i){
-** return sqlite3_value_numeric_type( columnMem(pStmt,i) );
-**}
-*/
-
/*
** Convert the N-th element of pStmt->pColName[] into a string using
** xFunc() then return that string. If N is out of range, return 0.
@@ -64500,9 +65318,9 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){
*/
SQLITE_API int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){
Vdbe *pVdbe = (Vdbe*)pStmt;
- int v = pVdbe->aCounter[op-1];
- if( resetFlag ) pVdbe->aCounter[op-1] = 0;
- return v;
+ u32 v = pVdbe->aCounter[op];
+ if( resetFlag ) pVdbe->aCounter[op] = 0;
+ return (int)v;
}
/************** End of vdbeapi.c *********************************************/
@@ -64554,9 +65372,9 @@ static int findNextHostParameter(const char *zSql, int *pnToken){
/*
** This function returns a pointer to a nul-terminated string in memory
-** obtained from sqlite3DbMalloc(). If sqlite3.vdbeExecCnt is 1, then the
+** obtained from sqlite3DbMalloc(). If sqlite3.nVdbeExec is 1, then the
** string contains a copy of zRawSql but with host parameters expanded to
-** their current bindings. Or, if sqlite3.vdbeExecCnt is greater than 1,
+** their current bindings. Or, if sqlite3.nVdbeExec is greater than 1,
** then the returned string holds a copy of zRawSql with "-- " prepended
** to each line of text.
**
@@ -64594,7 +65412,7 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql(
sqlite3StrAccumInit(&out, zBase, sizeof(zBase),
db->aLimit[SQLITE_LIMIT_LENGTH]);
out.db = db;
- if( db->vdbeExecCnt>1 ){
+ if( db->nVdbeExec>1 ){
while( *zRawSql ){
const char *zStart = zRawSql;
while( *(zRawSql++)!='\n' && *zRawSql );
@@ -65389,19 +66207,6 @@ static int checkSavepointCount(sqlite3 *db){
}
#endif
-/*
-** Transfer error message text from an sqlite3_vtab.zErrMsg (text stored
-** in memory obtained from sqlite3_malloc) into a Vdbe.zErrMsg (text stored
-** in memory obtained from sqlite3DbMalloc).
-*/
-static void importVtabErrMsg(Vdbe *p, sqlite3_vtab *pVtab){
- sqlite3 *db = p->db;
- sqlite3DbFree(db, p->zErrMsg);
- p->zErrMsg = sqlite3DbStrDup(db, pVtab->zErrMsg);
- sqlite3_free(pVtab->zErrMsg);
- pVtab->zErrMsg = 0;
-}
-
/*
** Execute as much of a VDBE program as we can then return.
@@ -65444,16 +66249,16 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
sqlite3 *db = p->db; /* The database */
u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */
u8 encoding = ENC(db); /* The database encoding */
+ int iCompare = 0; /* Result of last OP_Compare operation */
+ unsigned nVmStep = 0; /* Number of virtual machine steps */
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
- int checkProgress; /* True if progress callbacks are enabled */
- int nProgressOps = 0; /* Opcodes executed since progress callback. */
+ unsigned nProgressLimit = 0;/* Invoke xProgress() when nVmStep reaches this */
#endif
Mem *aMem = p->aMem; /* Copy of p->aMem */
Mem *pIn1 = 0; /* 1st input operand */
Mem *pIn2 = 0; /* 2nd input operand */
Mem *pIn3 = 0; /* 3rd input operand */
Mem *pOut = 0; /* Output operand */
- int iCompare = 0; /* Result of last OP_Compare operation */
int *aPermute = 0; /* Permutation of columns for OP_Compare */
i64 lastRowid = db->lastRowid; /* Saved value of the last insert ROWID */
#ifdef VDBE_PROFILE
@@ -65899,14 +66704,24 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
goto no_mem;
}
assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY );
+ assert( p->bIsReader || p->readOnly!=0 );
p->rc = SQLITE_OK;
+ p->iCurrentTime = 0;
assert( p->explain==0 );
p->pResultSet = 0;
db->busyHandler.nBusy = 0;
CHECK_FOR_INTERRUPT;
sqlite3VdbeIOTraceSql(p);
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
- checkProgress = db->xProgress!=0;
+ if( db->xProgress ){
+ assert( 0 < db->nProgressOps );
+ nProgressLimit = (unsigned)p->aCounter[SQLITE_STMTSTATUS_VM_STEP];
+ if( nProgressLimit==0 ){
+ nProgressLimit = db->nProgressOps;
+ }else{
+ nProgressLimit %= (unsigned)db->nProgressOps;
+ }
+ }
#endif
#ifdef SQLITE_DEBUG
sqlite3BeginBenignMalloc();
@@ -65927,6 +66742,7 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
origPc = pc;
start = sqlite3Hwtime();
#endif
+ nVmStep++;
pOp = &aOp[pc];
/* Only allow tracing if SQLITE_DEBUG is defined.
@@ -65954,27 +66770,6 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
}
#endif
-#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
- /* Call the progress callback if it is configured and the required number
- ** of VDBE ops have been executed (either since this invocation of
- ** sqlite3VdbeExec() or since last time the progress callback was called).
- ** If the progress callback returns non-zero, exit the virtual machine with
- ** a return code SQLITE_ABORT.
- */
- if( checkProgress ){
- if( db->nProgressOps==nProgressOps ){
- int prc;
- prc = db->xProgress(db->pProgressArg);
- if( prc!=0 ){
- rc = SQLITE_INTERRUPT;
- goto vdbe_error_halt;
- }
- nProgressOps = 0;
- }
- nProgressOps++;
- }
-#endif
-
/* On any opcode with the "out2-prerelease" tag, free any
** external allocations out of mem[p2] and set mem[p2] to be
** an undefined integer. Opcodes will either fill in the integer
@@ -65983,7 +66778,7 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
assert( pOp->opflags==sqlite3OpcodeProperty[pOp->opcode] );
if( pOp->opflags & OPFLG_OUT2_PRERELEASE ){
assert( pOp->p2>0 );
- assert( pOp->p2<=p->nMem );
+ assert( pOp->p2<=(p->nMem-p->nCursor) );
pOut = &aMem[pOp->p2];
memAboutToChange(p, pOut);
VdbeMemRelease(pOut);
@@ -65994,30 +66789,30 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
#ifdef SQLITE_DEBUG
if( (pOp->opflags & OPFLG_IN1)!=0 ){
assert( pOp->p1>0 );
- assert( pOp->p1<=p->nMem );
+ assert( pOp->p1<=(p->nMem-p->nCursor) );
assert( memIsValid(&aMem[pOp->p1]) );
REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]);
}
if( (pOp->opflags & OPFLG_IN2)!=0 ){
assert( pOp->p2>0 );
- assert( pOp->p2<=p->nMem );
+ assert( pOp->p2<=(p->nMem-p->nCursor) );
assert( memIsValid(&aMem[pOp->p2]) );
REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]);
}
if( (pOp->opflags & OPFLG_IN3)!=0 ){
assert( pOp->p3>0 );
- assert( pOp->p3<=p->nMem );
+ assert( pOp->p3<=(p->nMem-p->nCursor) );
assert( memIsValid(&aMem[pOp->p3]) );
REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]);
}
if( (pOp->opflags & OPFLG_OUT2)!=0 ){
assert( pOp->p2>0 );
- assert( pOp->p2<=p->nMem );
+ assert( pOp->p2<=(p->nMem-p->nCursor) );
memAboutToChange(p, &aMem[pOp->p2]);
}
if( (pOp->opflags & OPFLG_OUT3)!=0 ){
assert( pOp->p3>0 );
- assert( pOp->p3<=p->nMem );
+ assert( pOp->p3<=(p->nMem-p->nCursor) );
memAboutToChange(p, &aMem[pOp->p3]);
}
#endif
@@ -66067,8 +66862,40 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
** the program.
*/
case OP_Goto: { /* jump */
- CHECK_FOR_INTERRUPT;
pc = pOp->p2 - 1;
+
+ /* Opcodes that are used as the bottom of a loop (OP_Next, OP_Prev,
+ ** OP_VNext, OP_RowSetNext, or OP_SorterNext) all jump here upon
+ ** completion. Check to see if sqlite3_interrupt() has been called
+ ** or if the progress callback needs to be invoked.
+ **
+ ** This code uses unstructured "goto" statements and does not look clean.
+ ** But that is not due to sloppy coding habits. The code is written this
+ ** way for performance, to avoid having to run the interrupt and progress
+ ** checks on every opcode. This helps sqlite3_step() to run about 1.5%
+ ** faster according to "valgrind --tool=cachegrind" */
+check_for_interrupt:
+ CHECK_FOR_INTERRUPT;
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ /* Call the progress callback if it is configured and the required number
+ ** of VDBE ops have been executed (either since this invocation of
+ ** sqlite3VdbeExec() or since last time the progress callback was called).
+ ** If the progress callback returns non-zero, exit the virtual machine with
+ ** a return code SQLITE_ABORT.
+ */
+ if( db->xProgress!=0 && nVmStep>=nProgressLimit ){
+ int prc;
+ prc = db->xProgress(db->pProgressArg);
+ if( prc!=0 ){
+ rc = SQLITE_INTERRUPT;
+ goto vdbe_error_halt;
+ }
+ if( db->xProgress!=0 ){
+ nProgressLimit = nVmStep + db->nProgressOps - (nVmStep%db->nProgressOps);
+ }
+ }
+#endif
+
break;
}
@@ -66078,7 +66905,7 @@ case OP_Goto: { /* jump */
** and then jump to address P2.
*/
case OP_Gosub: { /* jump */
- assert( pOp->p1>0 && pOp->p1<=p->nMem );
+ assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) );
pIn1 = &aMem[pOp->p1];
assert( (pIn1->flags & MEM_Dyn)==0 );
memAboutToChange(p, pIn1);
@@ -66189,7 +67016,7 @@ case OP_Halt: {
p->rc = rc = SQLITE_BUSY;
}else{
assert( rc==SQLITE_OK || (p->rc&0xff)==SQLITE_CONSTRAINT );
- assert( rc==SQLITE_OK || db->nDeferredCons>0 );
+ assert( rc==SQLITE_OK || db->nDeferredCons>0 || db->nDeferredImmCons>0 );
rc = p->rc ? SQLITE_ERROR : SQLITE_DONE;
}
goto vdbe_return;
@@ -66294,7 +67121,7 @@ case OP_Null: { /* out2-prerelease */
u16 nullFlag;
#endif /* local variables moved into u.ab */
u.ab.cnt = pOp->p3-pOp->p2;
- assert( pOp->p3<=p->nMem );
+ assert( pOp->p3<=(p->nMem-p->nCursor) );
pOut->flags = u.ab.nullFlag = pOp->p1 ? (MEM_Null|MEM_Cleared) : MEM_Null;
while( u.ab.cnt>0 ){
pOut++;
@@ -66367,8 +67194,8 @@ case OP_Move: {
pIn1 = &aMem[u.ad.p1];
pOut = &aMem[u.ad.p2];
while( u.ad.n-- ){
- assert( pOut<=&aMem[p->nMem] );
- assert( pIn1<=&aMem[p->nMem] );
+ assert( pOut<=&aMem[(p->nMem-p->nCursor)] );
+ assert( pIn1<=&aMem[(p->nMem-p->nCursor)] );
assert( memIsValid(pIn1) );
memAboutToChange(p, pOut);
u.ad.zMalloc = pOut->zMalloc;
@@ -66456,7 +67283,7 @@ case OP_ResultRow: {
#endif /* local variables moved into u.af */
assert( p->nResColumn==pOp->p2 );
assert( pOp->p1>0 );
- assert( pOp->p1+pOp->p2<=p->nMem+1 );
+ assert( pOp->p1+pOp->p2<=(p->nMem-p->nCursor)+1 );
/* If this statement has violated immediate foreign key constraints, do
** not return the number of rows modified. And do not RELEASE the statement
@@ -66736,11 +67563,11 @@ case OP_Function: {
u.ai.n = pOp->p5;
u.ai.apVal = p->apArg;
assert( u.ai.apVal || u.ai.n==0 );
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
pOut = &aMem[pOp->p3];
memAboutToChange(p, pOut);
- assert( u.ai.n==0 || (pOp->p2>0 && pOp->p2+u.ai.n<=p->nMem+1) );
+ assert( u.ai.n==0 || (pOp->p2>0 && pOp->p2+u.ai.n<=(p->nMem-p->nCursor)+1) );
assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+u.ai.n );
u.ai.pArg = &aMem[pOp->p2];
for(u.ai.i=0; u.ai.i<u.ai.n; u.ai.i++, u.ai.pArg++){
@@ -66751,19 +67578,14 @@ case OP_Function: {
REGISTER_TRACE(pOp->p2+u.ai.i, u.ai.pArg);
}
- assert( pOp->p4type==P4_FUNCDEF || pOp->p4type==P4_VDBEFUNC );
- if( pOp->p4type==P4_FUNCDEF ){
- u.ai.ctx.pFunc = pOp->p4.pFunc;
- u.ai.ctx.pVdbeFunc = 0;
- }else{
- u.ai.ctx.pVdbeFunc = (VdbeFunc*)pOp->p4.pVdbeFunc;
- u.ai.ctx.pFunc = u.ai.ctx.pVdbeFunc->pFunc;
- }
-
+ assert( pOp->p4type==P4_FUNCDEF );
+ u.ai.ctx.pFunc = pOp->p4.pFunc;
u.ai.ctx.s.flags = MEM_Null;
u.ai.ctx.s.db = db;
u.ai.ctx.s.xDel = 0;
u.ai.ctx.s.zMalloc = 0;
+ u.ai.ctx.iOp = pc;
+ u.ai.ctx.pVdbe = p;
/* The output cell may already have a buffer allocated. Move
** the pointer to u.ai.ctx.s so in case the user-function can use
@@ -66772,8 +67594,8 @@ case OP_Function: {
sqlite3VdbeMemMove(&u.ai.ctx.s, pOut);
MemSetTypeFlag(&u.ai.ctx.s, MEM_Null);
- u.ai.ctx.isError = 0;
- if( u.ai.ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){
+ u.ai.ctx.fErrorOrAux = 0;
+ if( u.ai.ctx.pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
assert( pOp>aOp );
assert( pOp[-1].p4type==P4_COLLSEQ );
assert( pOp[-1].opcode==OP_CollSeq );
@@ -66783,15 +67605,6 @@ case OP_Function: {
(*u.ai.ctx.pFunc->xFunc)(&u.ai.ctx, u.ai.n, u.ai.apVal); /* IMP: R-24505-23230 */
lastRowid = db->lastRowid;
- /* If any auxiliary data functions have been called by this user function,
- ** immediately call the destructor for any non-static values.
- */
- if( u.ai.ctx.pVdbeFunc ){
- sqlite3VdbeDeleteAuxData(u.ai.ctx.pVdbeFunc, pOp->p1);
- pOp->p4.pVdbeFunc = u.ai.ctx.pVdbeFunc;
- pOp->p4type = P4_VDBEFUNC;
- }
-
if( db->mallocFailed ){
/* Even though a malloc() has failed, the implementation of the
** user function may have called an sqlite3_result_XXX() function
@@ -66803,9 +67616,12 @@ case OP_Function: {
}
/* If the function returned an error, throw an exception */
- if( u.ai.ctx.isError ){
- sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.ai.ctx.s));
- rc = u.ai.ctx.isError;
+ if( u.ai.ctx.fErrorOrAux ){
+ if( u.ai.ctx.isError ){
+ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.ai.ctx.s));
+ rc = u.ai.ctx.isError;
+ }
+ sqlite3VdbeDeleteAuxData(p, pc, pOp->p1);
}
/* Copy the result of the function into register P3 */
@@ -67181,12 +67997,12 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
** then the result is always NULL.
** The jump is taken if the SQLITE_JUMPIFNULL bit is set.
*/
- if( pOp->p5 & SQLITE_STOREP2 ){
+ if( pOp->p5 & SQLITE_JUMPIFNULL ){
+ pc = pOp->p2-1;
+ }else if( pOp->p5 & SQLITE_STOREP2 ){
pOut = &aMem[pOp->p2];
MemSetTypeFlag(pOut, MEM_Null);
REGISTER_TRACE(pOp->p2, pOut);
- }else if( pOp->p5 & SQLITE_JUMPIFNULL ){
- pc = pOp->p2-1;
}
break;
}
@@ -67287,11 +68103,11 @@ case OP_Compare: {
if( aPermute ){
int k, mx = 0;
for(k=0; k<u.al.n; k++) if( aPermute[k]>mx ) mx = aPermute[k];
- assert( u.al.p1>0 && u.al.p1+mx<=p->nMem+1 );
- assert( u.al.p2>0 && u.al.p2+mx<=p->nMem+1 );
+ assert( u.al.p1>0 && u.al.p1+mx<=(p->nMem-p->nCursor)+1 );
+ assert( u.al.p2>0 && u.al.p2+mx<=(p->nMem-p->nCursor)+1 );
}else{
- assert( u.al.p1>0 && u.al.p1+u.al.n<=p->nMem+1 );
- assert( u.al.p2>0 && u.al.p2+u.al.n<=p->nMem+1 );
+ assert( u.al.p1>0 && u.al.p1+u.al.n<=(p->nMem-p->nCursor)+1 );
+ assert( u.al.p2>0 && u.al.p2+u.al.n<=(p->nMem-p->nCursor)+1 );
}
#endif /* SQLITE_DEBUG */
for(u.al.i=0; u.al.i<u.al.n; u.al.i++){
@@ -67548,7 +68364,7 @@ case OP_Column: {
u.ao.pC = 0;
memset(&u.ao.sMem, 0, sizeof(u.ao.sMem));
assert( u.ao.p1<p->nCursor );
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
u.ao.pDest = &aMem[pOp->p3];
memAboutToChange(p, u.ao.pDest);
u.ao.zRec = 0;
@@ -67848,7 +68664,7 @@ case OP_Affinity: {
assert( u.ap.zAffinity[pOp->p2]==0 );
pIn1 = &aMem[pOp->p1];
while( (u.ap.cAff = *(u.ap.zAffinity++))!=0 ){
- assert( pIn1 <= &p->aMem[p->nMem] );
+ assert( pIn1 <= &p->aMem[(p->nMem-p->nCursor)] );
assert( memIsValid(pIn1) );
ExpandBlob(pIn1);
applyAffinity(pIn1, u.ap.cAff, encoding);
@@ -67911,7 +68727,7 @@ case OP_MakeRecord: {
u.aq.nZero = 0; /* Number of zero bytes at the end of the record */
u.aq.nField = pOp->p1;
u.aq.zAffinity = pOp->p4.z;
- assert( u.aq.nField>0 && pOp->p2>0 && pOp->p2+u.aq.nField<=p->nMem+1 );
+ assert( u.aq.nField>0 && pOp->p2>0 && pOp->p2+u.aq.nField<=(p->nMem-p->nCursor)+1 );
u.aq.pData0 = &aMem[u.aq.nField];
u.aq.nField = pOp->p2;
u.aq.pLast = &u.aq.pData0[u.aq.nField-1];
@@ -67977,7 +68793,7 @@ case OP_MakeRecord: {
}
assert( u.aq.i==u.aq.nByte );
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
pOut->n = (int)u.aq.nByte;
pOut->flags = MEM_Blob | MEM_Dyn;
pOut->xDel = 0;
@@ -68042,9 +68858,10 @@ case OP_Savepoint: {
assert( u.as.p1==SAVEPOINT_BEGIN||u.as.p1==SAVEPOINT_RELEASE||u.as.p1==SAVEPOINT_ROLLBACK );
assert( db->pSavepoint || db->isTransactionSavepoint==0 );
assert( checkSavepointCount(db) );
+ assert( p->bIsReader );
if( u.as.p1==SAVEPOINT_BEGIN ){
- if( db->writeVdbeCnt>0 ){
+ if( db->nVdbeWrite>0 ){
/* A new savepoint cannot be created if there are active write
** statements (i.e. open read/write incremental blob handles).
*/
@@ -68084,6 +68901,7 @@ case OP_Savepoint: {
u.as.pNew->pNext = db->pSavepoint;
db->pSavepoint = u.as.pNew;
u.as.pNew->nDeferredCons = db->nDeferredCons;
+ u.as.pNew->nDeferredImmCons = db->nDeferredImmCons;
}
}
}else{
@@ -68101,7 +68919,7 @@ case OP_Savepoint: {
if( !u.as.pSavepoint ){
sqlite3SetString(&p->zErrMsg, db, "no such savepoint: %s", u.as.zName);
rc = SQLITE_ERROR;
- }else if( db->writeVdbeCnt>0 && u.as.p1==SAVEPOINT_RELEASE ){
+ }else if( db->nVdbeWrite>0 && u.as.p1==SAVEPOINT_RELEASE ){
/* It is not possible to release (commit) a savepoint if there are
** active write statements.
*/
@@ -68171,6 +68989,7 @@ case OP_Savepoint: {
}
}else{
db->nDeferredCons = u.as.pSavepoint->nDeferredCons;
+ db->nDeferredImmCons = u.as.pSavepoint->nDeferredImmCons;
}
if( !isTransaction ){
@@ -68204,10 +69023,11 @@ case OP_AutoCommit: {
u.at.turnOnAC = u.at.desiredAutoCommit && !db->autoCommit;
assert( u.at.desiredAutoCommit==1 || u.at.desiredAutoCommit==0 );
assert( u.at.desiredAutoCommit==1 || u.at.iRollback==0 );
- assert( db->activeVdbeCnt>0 ); /* At least this one VM is active */
+ assert( db->nVdbeActive>0 ); /* At least this one VM is active */
+ assert( p->bIsReader );
#if 0
- if( u.at.turnOnAC && u.at.iRollback && db->activeVdbeCnt>1 ){
+ if( u.at.turnOnAC && u.at.iRollback && db->nVdbeActive>1 ){
/* If this instruction implements a ROLLBACK and other VMs are
** still running, and a transaction is active, return an error indicating
** that the other VMs must complete first.
@@ -68217,7 +69037,7 @@ case OP_AutoCommit: {
rc = SQLITE_BUSY;
}else
#endif
- if( u.at.turnOnAC && !u.at.iRollback && db->writeVdbeCnt>0 ){
+ if( u.at.turnOnAC && !u.at.iRollback && db->nVdbeWrite>0 ){
/* If this instruction implements a COMMIT and other VMs are writing
** return an error indicating that the other VMs must complete first.
*/
@@ -68275,8 +69095,8 @@ case OP_AutoCommit: {
** other process can start another write transaction while this transaction is
** underway. Starting a write transaction also creates a rollback journal. A
** write transaction must be started before any changes can be made to the
-** database. If P2 is 2 or greater then an EXCLUSIVE lock is also obtained
-** on the file.
+** database. If P2 is greater than or equal to 2 then an EXCLUSIVE lock is
+** also obtained on the file.
**
** If a write-transaction is started and the Vdbe.usesStmtJournal flag is
** true (this flag is set if the Vdbe may modify more than one row and may
@@ -68295,8 +69115,14 @@ case OP_Transaction: {
Btree *pBt;
#endif /* local variables moved into u.au */
+ assert( p->bIsReader );
+ assert( p->readOnly==0 || pOp->p2==0 );
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
+ if( pOp->p2 && (db->flags & SQLITE_QueryOnly)!=0 ){
+ rc = SQLITE_READONLY;
+ goto abort_due_to_error;
+ }
u.au.pBt = db->aDb[pOp->p1].pBt;
if( u.au.pBt ){
@@ -68311,7 +69137,7 @@ case OP_Transaction: {
}
if( pOp->p2 && p->usesStmtJournal
- && (db->autoCommit==0 || db->activeVdbeCnt>1)
+ && (db->autoCommit==0 || db->nVdbeRead>1)
){
assert( sqlite3BtreeIsInTrans(u.au.pBt) );
if( p->iStatement==0 ){
@@ -68329,6 +69155,7 @@ case OP_Transaction: {
** counter. If the statement transaction needs to be rolled back,
** the value of this counter needs to be restored too. */
p->nStmtDefCons = db->nDeferredCons;
+ p->nStmtDefImmCons = db->nDeferredImmCons;
}
}
break;
@@ -68353,6 +69180,7 @@ case OP_ReadCookie: { /* out2-prerelease */
int iCookie;
#endif /* local variables moved into u.av */
+ assert( p->bIsReader );
u.av.iDb = pOp->p1;
u.av.iCookie = pOp->p3;
assert( pOp->p3<SQLITE_N_BTREE_META );
@@ -68382,6 +69210,7 @@ case OP_SetCookie: { /* in3 */
assert( pOp->p2<SQLITE_N_BTREE_META );
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
+ assert( p->readOnly==0 );
u.aw.pDb = &db->aDb[pOp->p1];
assert( u.aw.pDb->pBt!=0 );
assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
@@ -68434,6 +69263,7 @@ case OP_VerifyCookie: {
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
+ assert( p->bIsReader );
u.ax.pBt = db->aDb[pOp->p1].pBt;
if( u.ax.pBt ){
sqlite3BtreeGetMeta(u.ax.pBt, BTREE_SCHEMA_VERSION, (u32 *)&u.ax.iMeta);
@@ -68531,6 +69361,8 @@ case OP_OpenWrite: {
assert( (pOp->p5&(OPFLAG_P2ISREG|OPFLAG_BULKCSR))==pOp->p5 );
assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 );
+ assert( p->bIsReader );
+ assert( pOp->opcode==OP_OpenRead || p->readOnly==0 );
if( p->expired ){
rc = SQLITE_ABORT;
@@ -68557,7 +69389,7 @@ case OP_OpenWrite: {
}
if( pOp->p5 & OPFLAG_P2ISREG ){
assert( u.ay.p2>0 );
- assert( u.ay.p2<=p->nMem );
+ assert( u.ay.p2<=(p->nMem-p->nCursor) );
pIn2 = &aMem[u.ay.p2];
assert( memIsValid(pIn2) );
assert( (pIn2->flags & MEM_Int)!=0 );
@@ -69108,7 +69940,7 @@ case OP_IsUnique: { /* jump, in3 */
u.bf.aMx = &aMem[pOp->p4.i];
/* Assert that the values of parameters P1 and P4 are in range. */
assert( pOp->p4type==P4_INT32 );
- assert( pOp->p4.i>0 && pOp->p4.i<=p->nMem );
+ assert( pOp->p4.i>0 && pOp->p4.i<=(p->nMem-p->nCursor) );
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
/* Find the index cursor. */
@@ -69315,7 +70147,7 @@ case OP_NewRowid: { /* out2-prerelease */
u.bh.pMem = &u.bh.pFrame->aMem[pOp->p3];
}else{
/* Assert that P3 is a valid memory cell. */
- assert( pOp->p3<=p->nMem );
+ assert( pOp->p3<=(p->nMem-p->nCursor) );
u.bh.pMem = &aMem[pOp->p3];
memAboutToChange(p, u.bh.pMem);
}
@@ -69725,7 +70557,7 @@ case OP_Rowid: { /* out2-prerelease */
u.bn.pModule = u.bn.pVtab->pModule;
assert( u.bn.pModule->xRowid );
rc = u.bn.pModule->xRowid(u.bn.pC->pVtabCursor, &u.bn.v);
- importVtabErrMsg(p, u.bn.pVtab);
+ sqlite3VtabImportErrmsg(p, u.bn.pVtab);
#endif /* SQLITE_OMIT_VIRTUALTABLE */
}else{
assert( u.bn.pC->pCursor!=0 );
@@ -69817,7 +70649,7 @@ case OP_Sort: { /* jump */
sqlite3_sort_count++;
sqlite3_search_count--;
#endif
- p->aCounter[SQLITE_STMTSTATUS_SORT-1]++;
+ p->aCounter[SQLITE_STMTSTATUS_SORT]++;
/* Fall through into OP_Rewind */
}
/* Opcode: Rewind P1 P2 * * *
@@ -69899,9 +70731,8 @@ case OP_Next: { /* jump */
int res;
#endif /* local variables moved into u.br */
- CHECK_FOR_INTERRUPT;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- assert( pOp->p5<=ArraySize(p->aCounter) );
+ assert( pOp->p5<ArraySize(p->aCounter) );
u.br.pC = p->apCsr[pOp->p1];
if( u.br.pC==0 ){
break; /* See ticket #2273 */
@@ -69911,7 +70742,7 @@ case OP_Next: { /* jump */
assert( pOp->opcode==OP_SorterNext );
rc = sqlite3VdbeSorterNext(db, u.br.pC, &u.br.res);
}else{
- u.br.res = 1;
+ /* u.br.res = 1; // Always initialized by the xAdvance() call */
assert( u.br.pC->deferredMoveto==0 );
assert( u.br.pC->pCursor );
assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext );
@@ -69922,13 +70753,13 @@ case OP_Next: { /* jump */
u.br.pC->cacheStatus = CACHE_STALE;
if( u.br.res==0 ){
pc = pOp->p2 - 1;
- if( pOp->p5 ) p->aCounter[pOp->p5-1]++;
+ p->aCounter[pOp->p5]++;
#ifdef SQLITE_TEST
sqlite3_search_count++;
#endif
}
u.br.pC->rowidIsValid = 0;
- break;
+ goto check_for_interrupt;
}
/* Opcode: IdxInsert P1 P2 P3 * P5
@@ -69994,7 +70825,7 @@ case OP_IdxDelete: {
#endif /* local variables moved into u.bt */
assert( pOp->p3>0 );
- assert( pOp->p2>0 && pOp->p2+pOp->p3<=p->nMem+1 );
+ assert( pOp->p2>0 && pOp->p2+pOp->p3<=(p->nMem-p->nCursor)+1 );
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
u.bt.pC = p->apCsr[pOp->p1];
assert( u.bt.pC!=0 );
@@ -70149,15 +70980,18 @@ case OP_Destroy: { /* out2-prerelease */
int iDb;
#endif /* local variables moved into u.bw */
+ assert( p->readOnly==0 );
#ifndef SQLITE_OMIT_VIRTUALTABLE
u.bw.iCnt = 0;
for(u.bw.pVdbe=db->pVdbe; u.bw.pVdbe; u.bw.pVdbe = u.bw.pVdbe->pNext){
- if( u.bw.pVdbe->magic==VDBE_MAGIC_RUN && u.bw.pVdbe->inVtabMethod<2 && u.bw.pVdbe->pc>=0 ){
+ if( u.bw.pVdbe->magic==VDBE_MAGIC_RUN && u.bw.pVdbe->bIsReader
+ && u.bw.pVdbe->inVtabMethod<2 && u.bw.pVdbe->pc>=0
+ ){
u.bw.iCnt++;
}
}
#else
- u.bw.iCnt = db->activeVdbeCnt;
+ u.bw.iCnt = db->nVdbeRead;
#endif
pOut->flags = MEM_Null;
if( u.bw.iCnt>1 ){
@@ -70206,6 +71040,8 @@ case OP_Clear: {
#endif /* local variables moved into u.bx */
u.bx.nChange = 0;
+ assert( p->readOnly==0 );
+ assert( pOp->p1!=1 );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p2))!=0 );
rc = sqlite3BtreeClearTable(
db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &u.bx.nChange : 0)
@@ -70254,6 +71090,7 @@ case OP_CreateTable: { /* out2-prerelease */
u.by.pgno = 0;
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
+ assert( p->readOnly==0 );
u.by.pDb = &db->aDb[pOp->p1];
assert( u.by.pDb->pBt!=0 );
if( pOp->opcode==OP_CreateTable ){
@@ -70406,11 +71243,12 @@ case OP_IntegrityCk: {
Mem *pnErr; /* Register keeping track of errors remaining */
#endif /* local variables moved into u.ca */
+ assert( p->bIsReader );
u.ca.nRoot = pOp->p2;
assert( u.ca.nRoot>0 );
u.ca.aRoot = sqlite3DbMallocRaw(db, sizeof(int)*(u.ca.nRoot+1) );
if( u.ca.aRoot==0 ) goto no_mem;
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
u.ca.pnErr = &aMem[pOp->p3];
assert( (u.ca.pnErr->flags & MEM_Int)!=0 );
assert( (u.ca.pnErr->flags & (MEM_Str|MEM_Blob))==0 );
@@ -70468,7 +71306,7 @@ case OP_RowSetRead: { /* jump, in1, out3 */
#if 0 /* local variables moved into u.cb */
i64 val;
#endif /* local variables moved into u.cb */
- CHECK_FOR_INTERRUPT;
+
pIn1 = &aMem[pOp->p1];
if( (pIn1->flags & MEM_RowSet)==0
|| sqlite3RowSetNext(pIn1->u.pRowSet, &u.cb.val)==0
@@ -70480,7 +71318,7 @@ case OP_RowSetRead: { /* jump, in1, out3 */
/* A value was pulled from the index */
sqlite3VdbeMemSetInt64(&aMem[pOp->p3], u.cb.val);
}
- break;
+ goto check_for_interrupt;
}
/* Opcode: RowSetTest P1 P2 P3 P4
@@ -70700,7 +71538,9 @@ case OP_Param: { /* out2-prerelease */
** statement counter is incremented (immediate foreign key constraints).
*/
case OP_FkCounter: {
- if( pOp->p1 ){
+ if( db->flags & SQLITE_DeferFKs ){
+ db->nDeferredImmCons += pOp->p2;
+ }else if( pOp->p1 ){
db->nDeferredCons += pOp->p2;
}else{
p->nFkConstraint += pOp->p2;
@@ -70721,9 +71561,9 @@ case OP_FkCounter: {
*/
case OP_FkIfZero: { /* jump */
if( pOp->p1 ){
- if( db->nDeferredCons==0 ) pc = pOp->p2-1;
+ if( db->nDeferredCons==0 && db->nDeferredImmCons==0 ) pc = pOp->p2-1;
}else{
- if( p->nFkConstraint==0 ) pc = pOp->p2-1;
+ if( p->nFkConstraint==0 && db->nDeferredImmCons==0 ) pc = pOp->p2-1;
}
break;
}
@@ -70844,7 +71684,7 @@ case OP_AggStep: {
sqlite3VdbeMemStoreType(u.cg.pRec);
}
u.cg.ctx.pFunc = pOp->p4.pFunc;
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
u.cg.ctx.pMem = u.cg.pMem = &aMem[pOp->p3];
u.cg.pMem->n++;
u.cg.ctx.s.flags = MEM_Null;
@@ -70855,7 +71695,7 @@ case OP_AggStep: {
u.cg.ctx.isError = 0;
u.cg.ctx.pColl = 0;
u.cg.ctx.skipFlag = 0;
- if( u.cg.ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){
+ if( u.cg.ctx.pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
assert( pOp>p->aOp );
assert( pOp[-1].p4type==P4_COLLSEQ );
assert( pOp[-1].opcode==OP_CollSeq );
@@ -70893,7 +71733,7 @@ case OP_AggFinal: {
#if 0 /* local variables moved into u.ch */
Mem *pMem;
#endif /* local variables moved into u.ch */
- assert( pOp->p1>0 && pOp->p1<=p->nMem );
+ assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) );
u.ch.pMem = &aMem[pOp->p1];
assert( (u.ch.pMem->flags & ~(MEM_Null|MEM_Agg))==0 );
rc = sqlite3VdbeMemFinalize(u.ch.pMem, pOp->p4.pFunc);
@@ -70927,6 +71767,7 @@ case OP_Checkpoint: {
Mem *pMem; /* Write results here */
#endif /* local variables moved into u.ci */
+ assert( p->readOnly==0 );
u.ci.aRes[0] = 0;
u.ci.aRes[1] = u.ci.aRes[2] = -1;
assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE
@@ -70978,6 +71819,7 @@ case OP_JournalMode: { /* out2-prerelease */
|| u.cj.eNew==PAGER_JOURNALMODE_QUERY
);
assert( pOp->p1>=0 && pOp->p1<db->nDb );
+ assert( p->readOnly==0 );
u.cj.pBt = db->aDb[pOp->p1].pBt;
u.cj.pPager = sqlite3BtreePager(u.cj.pBt);
@@ -71001,7 +71843,7 @@ case OP_JournalMode: { /* out2-prerelease */
if( (u.cj.eNew!=u.cj.eOld)
&& (u.cj.eOld==PAGER_JOURNALMODE_WAL || u.cj.eNew==PAGER_JOURNALMODE_WAL)
){
- if( !db->autoCommit || db->activeVdbeCnt>1 ){
+ if( !db->autoCommit || db->nVdbeRead>1 ){
rc = SQLITE_ERROR;
sqlite3SetString(&p->zErrMsg, db,
"cannot change %s wal mode from within a transaction",
@@ -71060,6 +71902,7 @@ case OP_JournalMode: { /* out2-prerelease */
** a transaction.
*/
case OP_Vacuum: {
+ assert( p->readOnly==0 );
rc = sqlite3RunVacuum(&p->zErrMsg, db);
break;
}
@@ -71079,6 +71922,7 @@ case OP_IncrVacuum: { /* jump */
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
+ assert( p->readOnly==0 );
u.ck.pBt = db->aDb[pOp->p1].pBt;
rc = sqlite3BtreeIncrVacuum(u.ck.pBt);
if( rc==SQLITE_DONE ){
@@ -71155,7 +71999,7 @@ case OP_VBegin: {
#endif /* local variables moved into u.cl */
u.cl.pVTab = pOp->p4.pVtab;
rc = sqlite3VtabBegin(db, u.cl.pVTab);
- if( u.cl.pVTab ) importVtabErrMsg(p, u.cl.pVTab->pVtab);
+ if( u.cl.pVTab ) sqlite3VtabImportErrmsg(p, u.cl.pVTab->pVtab);
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -71201,13 +72045,14 @@ case OP_VOpen: {
sqlite3_module *pModule;
#endif /* local variables moved into u.cm */
+ assert( p->bIsReader );
u.cm.pCur = 0;
u.cm.pVtabCursor = 0;
u.cm.pVtab = pOp->p4.pVtab->pVtab;
u.cm.pModule = (sqlite3_module *)u.cm.pVtab->pModule;
assert(u.cm.pVtab && u.cm.pModule);
rc = u.cm.pModule->xOpen(u.cm.pVtab, &u.cm.pVtabCursor);
- importVtabErrMsg(p, u.cm.pVtab);
+ sqlite3VtabImportErrmsg(p, u.cm.pVtab);
if( SQLITE_OK==rc ){
/* Initialize sqlite3_vtab_cursor base class */
u.cm.pVtabCursor->pVtab = u.cm.pVtab;
@@ -71287,7 +72132,7 @@ case OP_VFilter: { /* jump */
p->inVtabMethod = 1;
rc = u.cn.pModule->xFilter(u.cn.pVtabCursor, u.cn.iQuery, pOp->p4.z, u.cn.nArg, u.cn.apArg);
p->inVtabMethod = 0;
- importVtabErrMsg(p, u.cn.pVtab);
+ sqlite3VtabImportErrmsg(p, u.cn.pVtab);
if( rc==SQLITE_OK ){
u.cn.res = u.cn.pModule->xEof(u.cn.pVtabCursor);
}
@@ -71319,7 +72164,7 @@ case OP_VColumn: {
VdbeCursor *pCur = p->apCsr[pOp->p1];
assert( pCur->pVtabCursor );
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
u.co.pDest = &aMem[pOp->p3];
memAboutToChange(p, u.co.pDest);
if( pCur->nullRow ){
@@ -71340,7 +72185,7 @@ case OP_VColumn: {
MemSetTypeFlag(&u.co.sContext.s, MEM_Null);
rc = u.co.pModule->xColumn(pCur->pVtabCursor, &u.co.sContext, pOp->p2);
- importVtabErrMsg(p, u.co.pVtab);
+ sqlite3VtabImportErrmsg(p, u.co.pVtab);
if( u.co.sContext.isError ){
rc = u.co.sContext.isError;
}
@@ -71395,7 +72240,7 @@ case OP_VNext: { /* jump */
p->inVtabMethod = 1;
rc = u.cp.pModule->xNext(u.cp.pCur->pVtabCursor);
p->inVtabMethod = 0;
- importVtabErrMsg(p, u.cp.pVtab);
+ sqlite3VtabImportErrmsg(p, u.cp.pVtab);
if( rc==SQLITE_OK ){
u.cp.res = u.cp.pModule->xEof(u.cp.pCur->pVtabCursor);
}
@@ -71404,7 +72249,7 @@ case OP_VNext: { /* jump */
/* If there is data, jump to P2 */
pc = pOp->p2 - 1;
}
- break;
+ goto check_for_interrupt;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -71425,6 +72270,7 @@ case OP_VRename: {
u.cq.pName = &aMem[pOp->p1];
assert( u.cq.pVtab->pModule->xRename );
assert( memIsValid(u.cq.pName) );
+ assert( p->readOnly==0 );
REGISTER_TRACE(pOp->p1, u.cq.pName);
assert( u.cq.pName->flags & MEM_Str );
testcase( u.cq.pName->enc==SQLITE_UTF8 );
@@ -71433,7 +72279,7 @@ case OP_VRename: {
rc = sqlite3VdbeChangeEncoding(u.cq.pName, SQLITE_UTF8);
if( rc==SQLITE_OK ){
rc = u.cq.pVtab->pModule->xRename(u.cq.pVtab, u.cq.pName->z);
- importVtabErrMsg(p, u.cq.pVtab);
+ sqlite3VtabImportErrmsg(p, u.cq.pVtab);
p->expired = 0;
}
break;
@@ -71478,6 +72324,7 @@ case OP_VUpdate: {
assert( pOp->p2==1 || pOp->p5==OE_Fail || pOp->p5==OE_Rollback
|| pOp->p5==OE_Abort || pOp->p5==OE_Ignore || pOp->p5==OE_Replace
);
+ assert( p->readOnly==0 );
u.cr.pVtab = pOp->p4.pVtab->pVtab;
u.cr.pModule = (sqlite3_module *)u.cr.pVtab->pModule;
u.cr.nArg = pOp->p2;
@@ -71496,7 +72343,7 @@ case OP_VUpdate: {
db->vtabOnConflict = pOp->p5;
rc = u.cr.pModule->xUpdate(u.cr.pVtab, u.cr.nArg, u.cr.apArg, &u.cr.rowid);
db->vtabOnConflict = vtabOnConflict;
- importVtabErrMsg(p, u.cr.pVtab);
+ sqlite3VtabImportErrmsg(p, u.cr.pVtab);
if( rc==SQLITE_OK && pOp->p1 ){
assert( u.cr.nArg>1 && u.cr.apArg[0] && (u.cr.apArg[0]->flags&MEM_Null) );
db->lastRowid = lastRowid = u.cr.rowid;
@@ -71663,6 +72510,8 @@ vdbe_error_halt:
** top. */
vdbe_return:
db->lastRowid = lastRowid;
+ testcase( nVmStep>0 );
+ p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep;
sqlite3VdbeLeave(p);
return rc;
@@ -72230,7 +73079,7 @@ typedef struct FileWriter FileWriter;
** other key value. If the keys are equal (only possible with two EOF
** values), it doesn't matter which index is stored.
**
-** The (N/4) elements of aTree[] that preceed the final (N/2) described
+** The (N/4) elements of aTree[] that precede the final (N/2) described
** above contains the index of the smallest of each block of 4 iterators.
** And so on. So that aTree[1] contains the index of the iterator that
** currently points to the smallest key value. aTree[0] is unused.
@@ -73505,12 +74354,6 @@ typedef struct FileChunk FileChunk;
*/
#define JOURNAL_CHUNKSIZE ((int)(1024-sizeof(FileChunk*)))
-/* Macro to find the minimum of two numeric values.
-*/
-#ifndef MIN
-# define MIN(x,y) ((x)<(y)?(x):(y))
-#endif
-
/*
** The rollback journal is composed of a linked list of these structures.
*/
@@ -73780,7 +74623,7 @@ SQLITE_PRIVATE int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){
testcase( ExprHasProperty(pExpr, EP_Reduced) );
rc = pWalker->xExprCallback(pWalker, pExpr);
if( rc==WRC_Continue
- && !ExprHasAnyProperty(pExpr,EP_TokenOnly) ){
+ && !ExprHasProperty(pExpr,EP_TokenOnly) ){
if( sqlite3WalkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort;
if( sqlite3WalkExpr(pWalker, pExpr->pRight) ) return WRC_Abort;
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
@@ -73947,7 +74790,7 @@ static void incrAggFunctionDepth(Expr *pExpr, int N){
** column reference is so that the column reference will be recognized as
** usable by indices within the WHERE clause processing logic.
**
-** Hack: The TK_AS operator is inhibited if zType[0]=='G'. This means
+** The TK_AS operator is inhibited if zType[0]=='G'. This means
** that in a GROUP BY clause, the expression is evaluated twice. Hence:
**
** SELECT random()%5 AS x, count(*) FROM tab GROUP BY x
@@ -73957,8 +74800,9 @@ static void incrAggFunctionDepth(Expr *pExpr, int N){
** SELECT random()%5 AS x, count(*) FROM tab GROUP BY random()%5
**
** The result of random()%5 in the GROUP BY clause is probably different
-** from the result in the result-set. We might fix this someday. Or
-** then again, we might not...
+** from the result in the result-set. On the other hand Standard SQL does
+** not allow the GROUP BY clause to contain references to result-set columns.
+** So this should never come up in well-formed queries.
**
** If the reference is followed by a COLLATE operator, then make sure
** the COLLATE operator is preserved. For example:
@@ -73998,6 +74842,7 @@ static void resolveAlias(
incrAggFunctionDepth(pDup, nSubquery);
pDup = sqlite3PExpr(pParse, TK_AS, pDup, 0, 0);
if( pDup==0 ) return;
+ ExprSetProperty(pDup, EP_Skip);
if( pEList->a[iCol].iAlias==0 ){
pEList->a[iCol].iAlias = (u16)(++pParse->nAlias);
}
@@ -74020,7 +74865,7 @@ static void resolveAlias(
if( !ExprHasProperty(pExpr, EP_IntValue) && pExpr->u.zToken!=0 ){
assert( (pExpr->flags & (EP_Reduced|EP_TokenOnly))==0 );
pExpr->u.zToken = sqlite3DbStrDup(db, pExpr->u.zToken);
- pExpr->flags2 |= EP2_MallocedToken;
+ pExpr->flags |= EP_MemToken;
}
sqlite3DbFree(db, pDup);
}
@@ -74120,23 +74965,32 @@ static int lookupName(
assert( pNC ); /* the name context cannot be NULL. */
assert( zCol ); /* The Z in X.Y.Z cannot be NULL */
- assert( !ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_Reduced) );
+ assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
/* Initialize the node to no-match */
pExpr->iTable = -1;
pExpr->pTab = 0;
- ExprSetIrreducible(pExpr);
+ ExprSetVVAProperty(pExpr, EP_NoReduce);
/* Translate the schema name in zDb into a pointer to the corresponding
** schema. If not found, pSchema will remain NULL and nothing will match
** resulting in an appropriate error message toward the end of this routine
*/
if( zDb ){
- for(i=0; i<db->nDb; i++){
- assert( db->aDb[i].zName );
- if( sqlite3StrICmp(db->aDb[i].zName,zDb)==0 ){
- pSchema = db->aDb[i].pSchema;
- break;
+ testcase( pNC->ncFlags & NC_PartIdx );
+ testcase( pNC->ncFlags & NC_IsCheck );
+ if( (pNC->ncFlags & (NC_PartIdx|NC_IsCheck))!=0 ){
+ /* Silently ignore database qualifiers inside CHECK constraints and partial
+ ** indices. Do not raise errors because that might break legacy and
+ ** because it does not hurt anything to just ignore the database name. */
+ zDb = 0;
+ }else{
+ for(i=0; i<db->nDb; i++){
+ assert( db->aDb[i].zName );
+ if( sqlite3StrICmp(db->aDb[i].zName,zDb)==0 ){
+ pSchema = db->aDb[i].pSchema;
+ break;
+ }
}
}
}
@@ -74279,10 +75133,16 @@ static int lookupName(
** forms the result set entry ("a+b" in the example) and return immediately.
** Note that the expression in the result set should have already been
** resolved by the time the WHERE clause is resolved.
+ **
+ ** The ability to use an output result-set column in the WHERE, GROUP BY,
+ ** or HAVING clauses, or as part of a larger expression in the ORDRE BY
+ ** clause is not standard SQL. This is a (goofy) SQLite extension, that
+ ** is supported for backwards compatibility only. TO DO: Issue a warning
+ ** on sqlite3_log() whenever the capability is used.
*/
if( (pEList = pNC->pEList)!=0
&& zTab==0
- && ((pNC->ncFlags & NC_AsMaybe)==0 || cnt==0)
+ && cnt==0
){
for(j=0; j<pEList->nExpr; j++){
char *zAs = pEList->a[j].zName;
@@ -74415,6 +75275,52 @@ SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSr
}
/*
+** Report an error that an expression is not valid for a partial index WHERE
+** clause.
+*/
+static void notValidPartIdxWhere(
+ Parse *pParse, /* Leave error message here */
+ NameContext *pNC, /* The name context */
+ const char *zMsg /* Type of error */
+){
+ if( (pNC->ncFlags & NC_PartIdx)!=0 ){
+ sqlite3ErrorMsg(pParse, "%s prohibited in partial index WHERE clauses",
+ zMsg);
+ }
+}
+
+#ifndef SQLITE_OMIT_CHECK
+/*
+** Report an error that an expression is not valid for a CHECK constraint.
+*/
+static void notValidCheckConstraint(
+ Parse *pParse, /* Leave error message here */
+ NameContext *pNC, /* The name context */
+ const char *zMsg /* Type of error */
+){
+ if( (pNC->ncFlags & NC_IsCheck)!=0 ){
+ sqlite3ErrorMsg(pParse,"%s prohibited in CHECK constraints", zMsg);
+ }
+}
+#else
+# define notValidCheckConstraint(P,N,M)
+#endif
+
+/*
+** Expression p should encode a floating point value between 1.0 and 0.0.
+** Return 1024 times this value. Or return -1 if p is not a floating point
+** value between 1.0 and 0.0.
+*/
+static int exprProbability(Expr *p){
+ double r = -1.0;
+ if( p->op!=TK_FLOAT ) return -1;
+ sqlite3AtoF(p->u.zToken, &r, sqlite3Strlen30(p->u.zToken), SQLITE_UTF8);
+ assert( r>=0.0 );
+ if( r>1.0 ) return -1;
+ return (int)(r*1000.0);
+}
+
+/*
** This routine is callback for sqlite3WalkExpr().
**
** Resolve symbolic names into TK_COLUMN operators for the current
@@ -74434,7 +75340,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
pParse = pNC->pParse;
assert( pParse==pWalker->pParse );
- if( ExprHasAnyProperty(pExpr, EP_Resolved) ) return WRC_Prune;
+ if( ExprHasProperty(pExpr, EP_Resolved) ) return WRC_Prune;
ExprSetProperty(pExpr, EP_Resolved);
#ifndef NDEBUG
if( pNC->pSrcList && pNC->pSrcList->nAlloc>0 ){
@@ -74513,6 +75419,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
testcase( pExpr->op==TK_CONST_FUNC );
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
+ notValidPartIdxWhere(pParse, pNC, "functions");
zId = pExpr->u.zToken;
nId = sqlite3Strlen30(zId);
pDef = sqlite3FindFunction(pParse->db, zId, nId, n, enc, 0);
@@ -74525,6 +75432,23 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
}
}else{
is_agg = pDef->xFunc==0;
+ if( pDef->funcFlags & SQLITE_FUNC_UNLIKELY ){
+ ExprSetProperty(pExpr, EP_Unlikely|EP_Skip);
+ if( n==2 ){
+ pExpr->iTable = exprProbability(pList->a[1].pExpr);
+ if( pExpr->iTable<0 ){
+ sqlite3ErrorMsg(pParse, "second argument to likelihood() must be a "
+ "constant between 0.0 and 1.0");
+ pNC->nErr++;
+ }
+ }else{
+ /* EVIDENCE-OF: R-61304-29449 The unlikely(X) function is equivalent to
+ ** likelihood(X, 0.0625).
+ ** EVIDENCE-OF: R-01283-11636 The unlikely(X) function is short-hand for
+ ** likelihood(X,0.0625). */
+ pExpr->iTable = 62; /* TUNING: Default 2nd arg to unlikely() is 0.0625 */
+ }
+ }
}
#ifndef SQLITE_OMIT_AUTHORIZATION
if( pDef ){
@@ -74578,11 +75502,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
testcase( pExpr->op==TK_IN );
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
int nRef = pNC->nRef;
-#ifndef SQLITE_OMIT_CHECK
- if( (pNC->ncFlags & NC_IsCheck)!=0 ){
- sqlite3ErrorMsg(pParse,"subqueries prohibited in CHECK constraints");
- }
-#endif
+ notValidCheckConstraint(pParse, pNC, "subqueries");
+ notValidPartIdxWhere(pParse, pNC, "subqueries");
sqlite3WalkSelect(pWalker, pExpr->x.pSelect);
assert( pNC->nRef>=nRef );
if( nRef!=pNC->nRef ){
@@ -74591,14 +75512,11 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
}
break;
}
-#ifndef SQLITE_OMIT_CHECK
case TK_VARIABLE: {
- if( (pNC->ncFlags & NC_IsCheck)!=0 ){
- sqlite3ErrorMsg(pParse,"parameters prohibited in CHECK constraints");
- }
+ notValidCheckConstraint(pParse, pNC, "parameters");
+ notValidPartIdxWhere(pParse, pNC, "parameters");
break;
}
-#endif
}
return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue;
}
@@ -74689,7 +75607,7 @@ static int resolveOrderByTermToExprList(
** result-set entry.
*/
for(i=0; i<pEList->nExpr; i++){
- if( sqlite3ExprCompare(pEList->a[i].pExpr, pE)<2 ){
+ if( sqlite3ExprCompare(pEList->a[i].pExpr, pE, -1)<2 ){
return i+1;
}
}
@@ -74816,7 +75734,7 @@ static int resolveCompoundOrderBy(
/*
** Check every term in the ORDER BY or GROUP BY clause pOrderBy of
** the SELECT statement pSelect. If any term is reference to a
-** result set expression (as determined by the ExprList.a.iCol field)
+** result set expression (as determined by the ExprList.a.iOrderByCol field)
** then convert that term into a copy of the corresponding result set
** column.
**
@@ -74864,7 +75782,7 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(
** If the order-by term is an integer I between 1 and N (where N is the
** number of columns in the result set of the SELECT) then the expression
** in the resolution is a copy of the I-th result-set expression. If
-** the order-by term is an identify that corresponds to the AS-name of
+** the order-by term is an identifier that corresponds to the AS-name of
** a result-set expression, then the term resolves to a copy of the
** result-set expression. Otherwise, the expression is resolved in
** the usual way - using sqlite3ResolveExprNames().
@@ -74890,16 +75808,19 @@ static int resolveOrderGroupBy(
pParse = pNC->pParse;
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
Expr *pE = pItem->pExpr;
- iCol = resolveAsName(pParse, pSelect->pEList, pE);
- if( iCol>0 ){
- /* If an AS-name match is found, mark this ORDER BY column as being
- ** a copy of the iCol-th result-set column. The subsequent call to
- ** sqlite3ResolveOrderGroupBy() will convert the expression to a
- ** copy of the iCol-th result-set expression. */
- pItem->iOrderByCol = (u16)iCol;
- continue;
+ Expr *pE2 = sqlite3ExprSkipCollate(pE);
+ if( zType[0]!='G' ){
+ iCol = resolveAsName(pParse, pSelect->pEList, pE2);
+ if( iCol>0 ){
+ /* If an AS-name match is found, mark this ORDER BY column as being
+ ** a copy of the iCol-th result-set column. The subsequent call to
+ ** sqlite3ResolveOrderGroupBy() will convert the expression to a
+ ** copy of the iCol-th result-set expression. */
+ pItem->iOrderByCol = (u16)iCol;
+ continue;
+ }
}
- if( sqlite3ExprIsInteger(sqlite3ExprSkipCollate(pE), &iCol) ){
+ if( sqlite3ExprIsInteger(pE2, &iCol) ){
/* The ORDER BY term is an integer constant. Again, set the column
** number so that sqlite3ResolveOrderGroupBy() will convert the
** order-by term to a copy of the result-set expression */
@@ -74917,7 +75838,7 @@ static int resolveOrderGroupBy(
return 1;
}
for(j=0; j<pSelect->pEList->nExpr; j++){
- if( sqlite3ExprCompare(pE, pSelect->pEList->a[j].pExpr)==0 ){
+ if( sqlite3ExprCompare(pE, pSelect->pEList->a[j].pExpr, -1)==0 ){
pItem->iOrderByCol = j+1;
}
}
@@ -75042,7 +75963,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
return WRC_Abort;
}
- /* Add the expression list to the name-context before parsing the
+ /* Add the output column list to the name-context before parsing the
** other expressions in the SELECT statement. This is so that
** expressions in the WHERE clause (etc.) can refer to expressions by
** aliases in the result set.
@@ -75051,10 +75972,8 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
** re-evaluated for each reference to it.
*/
sNC.pEList = p->pEList;
- sNC.ncFlags |= NC_AsMaybe;
if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort;
if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort;
- sNC.ncFlags &= ~NC_AsMaybe;
/* The ORDER BY and GROUP BY clauses may not refer to terms in
** outer queries
@@ -75224,6 +76143,48 @@ SQLITE_PRIVATE void sqlite3ResolveSelectNames(
sqlite3WalkSelect(&w, p);
}
+/*
+** Resolve names in expressions that can only reference a single table:
+**
+** * CHECK constraints
+** * WHERE clauses on partial indices
+**
+** The Expr.iTable value for Expr.op==TK_COLUMN nodes of the expression
+** is set to -1 and the Expr.iColumn value is set to the column number.
+**
+** Any errors cause an error message to be set in pParse.
+*/
+SQLITE_PRIVATE void sqlite3ResolveSelfReference(
+ Parse *pParse, /* Parsing context */
+ Table *pTab, /* The table being referenced */
+ int type, /* NC_IsCheck or NC_PartIdx */
+ Expr *pExpr, /* Expression to resolve. May be NULL. */
+ ExprList *pList /* Expression list to resolve. May be NUL. */
+){
+ SrcList sSrc; /* Fake SrcList for pParse->pNewTable */
+ NameContext sNC; /* Name context for pParse->pNewTable */
+ int i; /* Loop counter */
+
+ assert( type==NC_IsCheck || type==NC_PartIdx );
+ memset(&sNC, 0, sizeof(sNC));
+ memset(&sSrc, 0, sizeof(sSrc));
+ sSrc.nSrc = 1;
+ sSrc.a[0].zName = pTab->zName;
+ sSrc.a[0].pTab = pTab;
+ sSrc.a[0].iCursor = -1;
+ sNC.pParse = pParse;
+ sNC.pSrcList = &sSrc;
+ sNC.ncFlags = type;
+ if( sqlite3ResolveExprNames(&sNC, pExpr) ) return;
+ if( pList ){
+ for(i=0; i<pList->nExpr; i++){
+ if( sqlite3ResolveExprNames(&sNC, pList->a[i].pExpr) ){
+ return;
+ }
+ }
+ }
+}
+
/************** End of resolve.c *********************************************/
/************** Begin file expr.c ********************************************/
/*
@@ -75268,7 +76229,7 @@ SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr){
#ifndef SQLITE_OMIT_CAST
if( op==TK_CAST ){
assert( !ExprHasProperty(pExpr, EP_IntValue) );
- return sqlite3AffinityType(pExpr->u.zToken);
+ return sqlite3AffinityType(pExpr->u.zToken, 0);
}
#endif
if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_REGISTER)
@@ -75297,7 +76258,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr *pExpr, Toke
Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, 1);
if( pNew ){
pNew->pLeft = pExpr;
- pNew->flags |= EP_Collate;
+ pNew->flags |= EP_Collate|EP_Skip;
pExpr = pNew;
}
}
@@ -75312,13 +76273,21 @@ SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, con
}
/*
-** Skip over any TK_COLLATE and/or TK_AS operators at the root of
-** an expression.
+** Skip over any TK_COLLATE or TK_AS operators and any unlikely()
+** or likelihood() function at the root of an expression.
*/
SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){
- while( pExpr && (pExpr->op==TK_COLLATE || pExpr->op==TK_AS) ){
- pExpr = pExpr->pLeft;
- }
+ while( pExpr && ExprHasProperty(pExpr, EP_Skip) ){
+ if( ExprHasProperty(pExpr, EP_Unlikely) ){
+ assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
+ assert( pExpr->x.pList->nExpr>0 );
+ assert( pExpr->op==TK_FUNCTION );
+ pExpr = pExpr->x.pList->a[0].pExpr;
+ }else{
+ assert( pExpr->op==TK_COLLATE || pExpr->op==TK_AS );
+ pExpr = pExpr->pLeft;
+ }
+ }
return pExpr;
}
@@ -75341,8 +76310,7 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
p = p->pLeft;
continue;
}
- assert( op!=TK_REGISTER || p->op2!=TK_COLLATE );
- if( op==TK_COLLATE ){
+ if( op==TK_COLLATE || (op==TK_REGISTER && p->op2==TK_COLLATE) ){
pColl = sqlite3GetCollSeq(pParse, ENC(db), 0, p->u.zToken);
break;
}
@@ -75824,7 +76792,7 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
const char *z;
if( pExpr==0 ) return;
- assert( !ExprHasAnyProperty(pExpr, EP_IntValue|EP_Reduced|EP_TokenOnly) );
+ assert( !ExprHasProperty(pExpr, EP_IntValue|EP_Reduced|EP_TokenOnly) );
z = pExpr->u.zToken;
assert( z!=0 );
assert( z[0]!=0 );
@@ -75894,12 +76862,12 @@ SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){
if( p==0 ) return;
/* Sanity check: Assert that the IntValue is non-negative if it exists */
assert( !ExprHasProperty(p, EP_IntValue) || p->u.iValue>=0 );
- if( !ExprHasAnyProperty(p, EP_TokenOnly) ){
+ if( !ExprHasProperty(p, EP_TokenOnly) ){
+ /* The Expr.x union is never used at the same time as Expr.pRight */
+ assert( p->x.pList==0 || p->pRight==0 );
sqlite3ExprDelete(db, p->pLeft);
sqlite3ExprDelete(db, p->pRight);
- if( !ExprHasProperty(p, EP_Reduced) && (p->flags2 & EP2_MallocedToken)!=0 ){
- sqlite3DbFree(db, p->u.zToken);
- }
+ if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken);
if( ExprHasProperty(p, EP_xIsSelect) ){
sqlite3SelectDelete(db, p->x.pSelect);
}else{
@@ -75959,16 +76927,19 @@ static int exprStructSize(Expr *p){
static int dupedExprStructSize(Expr *p, int flags){
int nSize;
assert( flags==EXPRDUP_REDUCE || flags==0 ); /* Only one flag value allowed */
+ assert( EXPR_FULLSIZE<=0xfff );
+ assert( (0xfff & (EP_Reduced|EP_TokenOnly))==0 );
if( 0==(flags&EXPRDUP_REDUCE) ){
nSize = EXPR_FULLSIZE;
}else{
- assert( !ExprHasAnyProperty(p, EP_TokenOnly|EP_Reduced) );
+ assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
assert( !ExprHasProperty(p, EP_FromJoin) );
- assert( (p->flags2 & EP2_MallocedToken)==0 );
- assert( (p->flags2 & EP2_Irreducible)==0 );
- if( p->pLeft || p->pRight || p->x.pList ){
+ assert( !ExprHasProperty(p, EP_MemToken) );
+ assert( !ExprHasProperty(p, EP_NoReduce) );
+ if( p->pLeft || p->x.pList ){
nSize = EXPR_REDUCEDSIZE | EP_Reduced;
}else{
+ assert( p->pRight==0 );
nSize = EXPR_TOKENONLYSIZE | EP_TokenOnly;
}
}
@@ -76062,7 +77033,7 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int flags, u8 **pzBuffer){
}
/* Set the EP_Reduced, EP_TokenOnly, and EP_Static flags appropriately. */
- pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_Static);
+ pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_Static|EP_MemToken);
pNew->flags |= nStructSize & (EP_Reduced|EP_TokenOnly);
pNew->flags |= staticFlag;
@@ -76082,7 +77053,7 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int flags, u8 **pzBuffer){
}
/* Fill in pNew->pLeft and pNew->pRight. */
- if( ExprHasAnyProperty(pNew, EP_Reduced|EP_TokenOnly) ){
+ if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly) ){
zAlloc += dupedExprNodeSize(p, flags);
if( ExprHasProperty(pNew, EP_Reduced) ){
pNew->pLeft = exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc);
@@ -76092,8 +77063,7 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int flags, u8 **pzBuffer){
*pzBuffer = zAlloc;
}
}else{
- pNew->flags2 = 0;
- if( !ExprHasAnyProperty(p, EP_TokenOnly) ){
+ if( !ExprHasProperty(p, EP_TokenOnly) ){
pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0);
pNew->pRight = sqlite3ExprDup(db, p->pRight, 0);
}
@@ -76147,6 +77117,7 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags)
pItem->zSpan = sqlite3DbStrDup(db, pOldItem->zSpan);
pItem->sortOrder = pOldItem->sortOrder;
pItem->done = 0;
+ pItem->bSpanIsTab = pOldItem->bSpanIsTab;
pItem->iOrderByCol = pOldItem->iOrderByCol;
pItem->iAlias = pOldItem->iAlias;
}
@@ -76402,7 +77373,7 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
/* If pWalker->u.i is 3 then any term of the expression that comes from
** the ON or USING clauses of a join disqualifies the expression
** from being considered constant. */
- if( pWalker->u.i==3 && ExprHasAnyProperty(pExpr, EP_FromJoin) ){
+ if( pWalker->u.i==3 && ExprHasProperty(pExpr, EP_FromJoin) ){
pWalker->u.i = 0;
return WRC_Abort;
}
@@ -76505,6 +77476,7 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr *p, int *pValue){
case TK_UMINUS: {
int v;
if( sqlite3ExprIsInteger(p->pLeft, &v) ){
+ assert( v!=(-2147483647-1) );
*pValue = -v;
rc = 1;
}
@@ -76823,16 +77795,16 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
/* Could not found an existing table or index to use as the RHS b-tree.
** We will have to generate an ephemeral table to do the job.
*/
- double savedNQueryLoop = pParse->nQueryLoop;
+ u32 savedNQueryLoop = pParse->nQueryLoop;
int rMayHaveNull = 0;
eType = IN_INDEX_EPH;
if( prNotFound ){
*prNotFound = rMayHaveNull = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Null, 0, *prNotFound);
}else{
- testcase( pParse->nQueryLoop>(double)1 );
- pParse->nQueryLoop = (double)1;
- if( pX->pLeft->iColumn<0 && !ExprHasAnyProperty(pX, EP_xIsSelect) ){
+ testcase( pParse->nQueryLoop>0 );
+ pParse->nQueryLoop = 0;
+ if( pX->pLeft->iColumn<0 && !ExprHasProperty(pX, EP_xIsSelect) ){
eType = IN_INDEX_ROWID;
}
}
@@ -76873,7 +77845,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
**
** If rMayHaveNull is zero, that means that the subquery is being used
** for membership testing only. There is no need to initialize any
-** registers to indicate the presense or absence of NULLs on the RHS.
+** registers to indicate the presence or absence of NULLs on the RHS.
**
** For a SELECT or EXISTS operator, return the register that holds the
** result. For IN operators or if an error occurs, the return value is 0.
@@ -76901,7 +77873,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
** If all of the above are false, then we can run this code just once
** save the results, and reuse the same result on subsequent invocations.
*/
- if( !ExprHasAnyProperty(pExpr, EP_VarSelect) ){
+ if( !ExprHasProperty(pExpr, EP_VarSelect) ){
testAddr = sqlite3CodeOnce(pParse);
}
@@ -76918,10 +77890,9 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
switch( pExpr->op ){
case TK_IN: {
char affinity; /* Affinity of the LHS of the IN */
- KeyInfo keyInfo; /* Keyinfo for the generated table */
- static u8 sortOrder = 0; /* Fake aSortOrder for keyInfo */
int addr; /* Address of OP_OpenEphemeral instruction */
Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */
+ KeyInfo *pKeyInfo = 0; /* Key information */
if( rMayHaveNull ){
sqlite3VdbeAddOp2(v, OP_Null, 0, rMayHaveNull);
@@ -76945,9 +77916,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
pExpr->iTable = pParse->nTab++;
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid);
if( rMayHaveNull==0 ) sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
- memset(&keyInfo, 0, sizeof(keyInfo));
- keyInfo.nField = 1;
- keyInfo.aSortOrder = &sortOrder;
+ pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, 1);
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
/* Case 1: expr IN (SELECT ...)
@@ -76963,14 +77932,17 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
dest.affSdst = (u8)affinity;
assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
pExpr->x.pSelect->iLimit = 0;
+ testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */
if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){
+ sqlite3DbFree(pParse->db, pKeyInfo);
return 0;
}
pEList = pExpr->x.pSelect->pEList;
- if( ALWAYS(pEList!=0 && pEList->nExpr>0) ){
- keyInfo.aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft,
- pEList->a[0].pExpr);
- }
+ assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */
+ assert( pEList!=0 );
+ assert( pEList->nExpr>0 );
+ pKeyInfo->aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft,
+ pEList->a[0].pExpr);
}else if( ALWAYS(pExpr->x.pList!=0) ){
/* Case 2: expr IN (exprlist)
**
@@ -76987,8 +77959,9 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
if( !affinity ){
affinity = SQLITE_AFF_NONE;
}
- keyInfo.aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
- keyInfo.aSortOrder = &sortOrder;
+ if( pKeyInfo ){
+ pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
+ }
/* Loop through each expression in <exprlist>. */
r1 = sqlite3GetTempReg(pParse);
@@ -77027,8 +78000,8 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
sqlite3ReleaseTempReg(pParse, r1);
sqlite3ReleaseTempReg(pParse, r2);
}
- if( !isRowid ){
- sqlite3VdbeChangeP4(v, addr, (void *)&keyInfo, P4_KEYINFO);
+ if( pKeyInfo ){
+ sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO_HANDOFF);
}
break;
}
@@ -77069,7 +78042,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
return 0;
}
rReg = dest.iSDParm;
- ExprSetIrreducible(pExpr);
+ ExprSetVVAProperty(pExpr, EP_NoReduce);
break;
}
}
@@ -77542,6 +78515,16 @@ static int usedAsColumnCache(Parse *pParse, int iFrom, int iTo){
#endif /* SQLITE_DEBUG || SQLITE_COVERAGE_TEST */
/*
+** Convert an expression node to a TK_REGISTER
+*/
+static void exprToRegister(Expr *p, int iReg){
+ p->op2 = p->op;
+ p->op = TK_REGISTER;
+ p->iTable = iReg;
+ ExprClearProperty(p, EP_Skip);
+}
+
+/*
** Generate code into the current Vdbe to evaluate the given
** expression. Attempt to store the results in register "target".
** Return the register where results are stored.
@@ -77588,15 +78571,20 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
/* Otherwise, fall thru into the TK_COLUMN case */
}
case TK_COLUMN: {
- if( pExpr->iTable<0 ){
- /* This only happens when coding check constraints */
- assert( pParse->ckBase>0 );
- inReg = pExpr->iColumn + pParse->ckBase;
- }else{
- inReg = sqlite3ExprCodeGetColumn(pParse, pExpr->pTab,
- pExpr->iColumn, pExpr->iTable, target,
- pExpr->op2);
+ int iTab = pExpr->iTable;
+ if( iTab<0 ){
+ if( pParse->ckBase>0 ){
+ /* Generating CHECK constraints or inserting into partial index */
+ inReg = pExpr->iColumn + pParse->ckBase;
+ break;
+ }else{
+ /* Deleting from a partial index */
+ iTab = pParse->iPartIdxTab;
+ }
}
+ inReg = sqlite3ExprCodeGetColumn(pParse, pExpr->pTab,
+ pExpr->iColumn, iTab, target,
+ pExpr->op2);
break;
}
case TK_INTEGER: {
@@ -77661,7 +78649,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
int aff, to_op;
inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
assert( !ExprHasProperty(pExpr, EP_IntValue) );
- aff = sqlite3AffinityType(pExpr->u.zToken);
+ aff = sqlite3AffinityType(pExpr->u.zToken, 0);
to_op = aff - SQLITE_AFF_TEXT + OP_ToText;
assert( to_op==OP_ToText || aff!=SQLITE_AFF_TEXT );
assert( to_op==OP_ToBlob || aff!=SQLITE_AFF_NONE );
@@ -77835,7 +78823,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
testcase( op==TK_CONST_FUNC );
testcase( op==TK_FUNCTION );
- if( ExprHasAnyProperty(pExpr, EP_TokenOnly) ){
+ if( ExprHasProperty(pExpr, EP_TokenOnly) ){
pFarg = 0;
}else{
pFarg = pExpr->x.pList;
@@ -77854,7 +78842,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
** IFNULL() functions. This avoids unnecessary evalation of
** arguments past the first non-NULL argument.
*/
- if( pDef->flags & SQLITE_FUNC_COALESCE ){
+ if( pDef->funcFlags & SQLITE_FUNC_COALESCE ){
int endCoalesce = sqlite3VdbeMakeLabel(v);
assert( nFarg>=2 );
sqlite3ExprCode(pParse, pFarg->a[0].pExpr, target);
@@ -77869,6 +78857,14 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
break;
}
+ /* The UNLIKELY() function is a no-op. The result is the value
+ ** of the first argument.
+ */
+ if( pDef->funcFlags & SQLITE_FUNC_UNLIKELY ){
+ assert( nFarg>=1 );
+ sqlite3ExprCode(pParse, pFarg->a[0].pExpr, target);
+ break;
+ }
if( pFarg ){
r1 = sqlite3GetTempRange(pParse, nFarg);
@@ -77878,7 +78874,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
** or OPFLAG_TYPEOFARG respectively, to avoid unnecessary data
** loading.
*/
- if( (pDef->flags & (SQLITE_FUNC_LENGTH|SQLITE_FUNC_TYPEOF))!=0 ){
+ if( (pDef->funcFlags & (SQLITE_FUNC_LENGTH|SQLITE_FUNC_TYPEOF))!=0 ){
u8 exprOp;
assert( nFarg==1 );
assert( pFarg->a[0].pExpr!=0 );
@@ -77886,8 +78882,9 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
if( exprOp==TK_COLUMN || exprOp==TK_AGG_COLUMN ){
assert( SQLITE_FUNC_LENGTH==OPFLAG_LENGTHARG );
assert( SQLITE_FUNC_TYPEOF==OPFLAG_TYPEOFARG );
- testcase( pDef->flags==SQLITE_FUNC_LENGTH );
- pFarg->a[0].pExpr->op2 = pDef->flags;
+ testcase( (pDef->funcFlags&~SQLITE_FUNC_ENCMASK)
+ ==SQLITE_FUNC_LENGTH );
+ pFarg->a[0].pExpr->op2 = pDef->funcFlags&~SQLITE_FUNC_ENCMASK;
}
}
@@ -77920,11 +78917,11 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
if( i<32 && sqlite3ExprIsConstant(pFarg->a[i].pExpr) ){
constMask |= (1<<i);
}
- if( (pDef->flags & SQLITE_FUNC_NEEDCOLL)!=0 && !pColl ){
+ if( (pDef->funcFlags & SQLITE_FUNC_NEEDCOLL)!=0 && !pColl ){
pColl = sqlite3ExprCollSeq(pParse, pFarg->a[i].pExpr);
}
}
- if( pDef->flags & SQLITE_FUNC_NEEDCOLL ){
+ if( pDef->funcFlags & SQLITE_FUNC_NEEDCOLL ){
if( !pColl ) pColl = db->pDfltColl;
sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ);
}
@@ -78065,9 +79062,9 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
** WHEN x=eN THEN rN ELSE y END
**
** X (if it exists) is in pExpr->pLeft.
- ** Y is in pExpr->pRight. The Y is also optional. If there is no
- ** ELSE clause and no other term matches, then the result of the
- ** exprssion is NULL.
+ ** Y is in the last element of pExpr->x.pList if pExpr->x.pList->nExpr is
+ ** odd. The Y is also optional. If the number of elements in x.pList
+ ** is even, then Y is omitted and the "otherwise" result is NULL.
** Ei is in pExpr->pList->a[i*2] and Ri is pExpr->pList->a[i*2+1].
**
** The result of the expression is the Ri for the first matching Ei,
@@ -78088,7 +79085,6 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
VVA_ONLY( int iCacheLevel = pParse->iCacheLevel; )
assert( !ExprHasProperty(pExpr, EP_xIsSelect) && pExpr->x.pList );
- assert((pExpr->x.pList->nExpr % 2) == 0);
assert(pExpr->x.pList->nExpr > 0);
pEList = pExpr->x.pList;
aListelem = pEList->a;
@@ -78098,9 +79094,8 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
cacheX = *pX;
testcase( pX->op==TK_COLUMN );
testcase( pX->op==TK_REGISTER );
- cacheX.iTable = sqlite3ExprCodeTemp(pParse, pX, &regFree1);
+ exprToRegister(&cacheX, sqlite3ExprCodeTemp(pParse, pX, &regFree1));
testcase( regFree1==0 );
- cacheX.op = TK_REGISTER;
opCompare.op = TK_EQ;
opCompare.pLeft = &cacheX;
pTest = &opCompare;
@@ -78110,7 +79105,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
** purposes and possibly overwritten. */
regFree1 = 0;
}
- for(i=0; i<nExpr; i=i+2){
+ for(i=0; i<nExpr-1; i=i+2){
sqlite3ExprCachePush(pParse);
if( pX ){
assert( pTest!=0 );
@@ -78128,9 +79123,9 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
sqlite3ExprCachePop(pParse, 1);
sqlite3VdbeResolveLabel(v, nextCase);
}
- if( pExpr->pRight ){
+ if( (nExpr&1)!=0 ){
sqlite3ExprCachePush(pParse);
- sqlite3ExprCode(pParse, pExpr->pRight, target);
+ sqlite3ExprCode(pParse, pEList->a[nExpr-1].pExpr, target);
sqlite3ExprCachePop(pParse, 1);
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
@@ -78242,9 +79237,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int targe
int iMem;
iMem = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Copy, inReg, iMem);
- pExpr->iTable = iMem;
- pExpr->op2 = pExpr->op;
- pExpr->op = TK_REGISTER;
+ exprToRegister(pExpr, iMem);
}
return inReg;
}
@@ -78323,7 +79316,7 @@ SQLITE_PRIVATE void sqlite3ExplainExpr(Vdbe *pOut, Expr *pExpr){
case TK_CAST: {
/* Expressions of the form: CAST(pLeft AS token) */
const char *zAff = "unk";
- switch( sqlite3AffinityType(pExpr->u.zToken) ){
+ switch( sqlite3AffinityType(pExpr->u.zToken, 0) ){
case SQLITE_AFF_TEXT: zAff = "TEXT"; break;
case SQLITE_AFF_NONE: zAff = "NONE"; break;
case SQLITE_AFF_NUMERIC: zAff = "NUMERIC"; break;
@@ -78374,7 +79367,7 @@ SQLITE_PRIVATE void sqlite3ExplainExpr(Vdbe *pOut, Expr *pExpr){
case TK_CONST_FUNC:
case TK_FUNCTION: {
ExprList *pFarg; /* List of function arguments */
- if( ExprHasAnyProperty(pExpr, EP_TokenOnly) ){
+ if( ExprHasProperty(pExpr, EP_TokenOnly) ){
pFarg = 0;
}else{
pFarg = pExpr->x.pList;
@@ -78623,9 +79616,7 @@ static int evalConstExpr(Walker *pWalker, Expr *pExpr){
** but suboptimal, so we want to know about the situation to fix it.
** Hence the following assert: */
assert( r2==r1 );
- pExpr->op2 = pExpr->op;
- pExpr->op = TK_REGISTER;
- pExpr->iTable = r2;
+ exprToRegister(pExpr, r2);
return WRC_Prune;
}
return WRC_Continue;
@@ -78723,8 +79714,7 @@ static void exprCodeBetween(
compRight.op = TK_LE;
compRight.pLeft = &exprX;
compRight.pRight = pExpr->x.pList->a[1].pExpr;
- exprX.iTable = sqlite3ExprCodeTemp(pParse, &exprX, &regFree1);
- exprX.op = TK_REGISTER;
+ exprToRegister(&exprX, sqlite3ExprCodeTemp(pParse, &exprX, &regFree1));
if( jumpIfTrue ){
sqlite3ExprIfTrue(pParse, &exprAnd, dest, jumpIfNull);
}else{
@@ -79019,6 +80009,12 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
** by a COLLATE operator at the top level. Return 2 if there are differences
** other than the top-level COLLATE operator.
**
+** If any subelement of pB has Expr.iTable==(-1) then it is allowed
+** to compare equal to an equivalent element in pA with Expr.iTable==iTab.
+**
+** The pA side might be using TK_REGISTER. If that is the case and pB is
+** not using TK_REGISTER but is otherwise equivalent, then still return 0.
+**
** Sometimes this routine will return 2 even if the two expressions
** really are equivalent. If we cannot prove that the expressions are
** identical, we return 2 just to be safe. So if this routine
@@ -79029,29 +80025,32 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
** just might result in some slightly slower code. But returning
** an incorrect 0 or 1 could lead to a malfunction.
*/
-SQLITE_PRIVATE int sqlite3ExprCompare(Expr *pA, Expr *pB){
+SQLITE_PRIVATE int sqlite3ExprCompare(Expr *pA, Expr *pB, int iTab){
if( pA==0||pB==0 ){
return pB==pA ? 0 : 2;
}
- assert( !ExprHasAnyProperty(pA, EP_TokenOnly|EP_Reduced) );
- assert( !ExprHasAnyProperty(pB, EP_TokenOnly|EP_Reduced) );
+ assert( !ExprHasProperty(pA, EP_TokenOnly|EP_Reduced) );
+ assert( !ExprHasProperty(pB, EP_TokenOnly|EP_Reduced) );
if( ExprHasProperty(pA, EP_xIsSelect) || ExprHasProperty(pB, EP_xIsSelect) ){
return 2;
}
if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2;
- if( pA->op!=pB->op ){
- if( pA->op==TK_COLLATE && sqlite3ExprCompare(pA->pLeft, pB)<2 ){
+ if( pA->op!=pB->op && (pA->op!=TK_REGISTER || pA->op2!=pB->op) ){
+ if( pA->op==TK_COLLATE && sqlite3ExprCompare(pA->pLeft, pB, iTab)<2 ){
return 1;
}
- if( pB->op==TK_COLLATE && sqlite3ExprCompare(pA, pB->pLeft)<2 ){
+ if( pB->op==TK_COLLATE && sqlite3ExprCompare(pA, pB->pLeft, iTab)<2 ){
return 1;
}
return 2;
}
- if( sqlite3ExprCompare(pA->pLeft, pB->pLeft) ) return 2;
- if( sqlite3ExprCompare(pA->pRight, pB->pRight) ) return 2;
- if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList) ) return 2;
- if( pA->iTable!=pB->iTable || pA->iColumn!=pB->iColumn ) return 2;
+ if( sqlite3ExprCompare(pA->pLeft, pB->pLeft, iTab) ) return 2;
+ if( sqlite3ExprCompare(pA->pRight, pB->pRight, iTab) ) return 2;
+ if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList, iTab) ) return 2;
+ if( pA->iColumn!=pB->iColumn ) return 2;
+ if( pA->iTable!=pB->iTable
+ && pA->op!=TK_REGISTER
+ && (pA->iTable!=iTab || NEVER(pB->iTable>=0)) ) return 2;
if( ExprHasProperty(pA, EP_IntValue) ){
if( !ExprHasProperty(pB, EP_IntValue) || pA->u.iValue!=pB->u.iValue ){
return 2;
@@ -79069,6 +80068,9 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Expr *pA, Expr *pB){
** Compare two ExprList objects. Return 0 if they are identical and
** non-zero if they differ in any way.
**
+** If any subelement of pB has Expr.iTable==(-1) then it is allowed
+** to compare equal to an equivalent element in pA with Expr.iTable==iTab.
+**
** This routine might return non-zero for equivalent ExprLists. The
** only consequence will be disabled optimizations. But this routine
** must never return 0 if the two ExprList objects are different, or
@@ -79077,7 +80079,7 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Expr *pA, Expr *pB){
** Two NULL pointers are considered to be the same. But a NULL pointer
** always differs from a non-NULL pointer.
*/
-SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList *pA, ExprList *pB){
+SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTab){
int i;
if( pA==0 && pB==0 ) return 0;
if( pA==0 || pB==0 ) return 1;
@@ -79086,7 +80088,46 @@ SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList *pA, ExprList *pB){
Expr *pExprA = pA->a[i].pExpr;
Expr *pExprB = pB->a[i].pExpr;
if( pA->a[i].sortOrder!=pB->a[i].sortOrder ) return 1;
- if( sqlite3ExprCompare(pExprA, pExprB) ) return 1;
+ if( sqlite3ExprCompare(pExprA, pExprB, iTab) ) return 1;
+ }
+ return 0;
+}
+
+/*
+** Return true if we can prove the pE2 will always be true if pE1 is
+** true. Return false if we cannot complete the proof or if pE2 might
+** be false. Examples:
+**
+** pE1: x==5 pE2: x==5 Result: true
+** pE1: x>0 pE2: x==5 Result: false
+** pE1: x=21 pE2: x=21 OR y=43 Result: true
+** pE1: x!=123 pE2: x IS NOT NULL Result: true
+** pE1: x!=?1 pE2: x IS NOT NULL Result: true
+** pE1: x IS NULL pE2: x IS NOT NULL Result: false
+** pE1: x IS ?2 pE2: x IS NOT NULL Reuslt: false
+**
+** When comparing TK_COLUMN nodes between pE1 and pE2, if pE2 has
+** Expr.iTable<0 then assume a table number given by iTab.
+**
+** When in doubt, return false. Returning true might give a performance
+** improvement. Returning false might cause a performance reduction, but
+** it will always give the correct answer and is hence always safe.
+*/
+SQLITE_PRIVATE int sqlite3ExprImpliesExpr(Expr *pE1, Expr *pE2, int iTab){
+ if( sqlite3ExprCompare(pE1, pE2, iTab)==0 ){
+ return 1;
+ }
+ if( pE2->op==TK_OR
+ && (sqlite3ExprImpliesExpr(pE1, pE2->pLeft, iTab)
+ || sqlite3ExprImpliesExpr(pE1, pE2->pRight, iTab) )
+ ){
+ return 1;
+ }
+ if( pE2->op==TK_NOTNULL
+ && sqlite3ExprCompare(pE1->pLeft, pE2->pLeft, iTab)==0
+ && (pE1->op!=TK_ISNULL && pE1->op!=TK_IS)
+ ){
+ return 1;
}
return 0;
}
@@ -79203,7 +80244,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
struct SrcList_item *pItem = pSrcList->a;
for(i=0; i<pSrcList->nSrc; i++, pItem++){
struct AggInfo_col *pCol;
- assert( !ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_Reduced) );
+ assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
if( pExpr->iTable==pItem->iCursor ){
/* If we reach this point, it means that pExpr refers to a table
** that is in the FROM clause of the aggregate query.
@@ -79252,7 +80293,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
** Convert the pExpr to be a TK_AGG_COLUMN referring to that
** pAggInfo->aCol[] entry.
*/
- ExprSetIrreducible(pExpr);
+ ExprSetVVAProperty(pExpr, EP_NoReduce);
pExpr->pAggInfo = pAggInfo;
pExpr->op = TK_AGG_COLUMN;
pExpr->iAgg = (i16)k;
@@ -79271,7 +80312,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
*/
struct AggInfo_func *pItem = pAggInfo->aFunc;
for(i=0; i<pAggInfo->nFunc; i++, pItem++){
- if( sqlite3ExprCompare(pItem->pExpr, pExpr)==0 ){
+ if( sqlite3ExprCompare(pItem->pExpr, pExpr, -1)==0 ){
break;
}
}
@@ -79298,8 +80339,8 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
}
/* Make pExpr point to the appropriate pAggInfo->aFunc[] entry
*/
- assert( !ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_Reduced) );
- ExprSetIrreducible(pExpr);
+ assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
+ ExprSetVVAProperty(pExpr, EP_NoReduce);
pExpr->iAgg = (i16)i;
pExpr->pAggInfo = pAggInfo;
return WRC_Prune;
@@ -80106,7 +81147,7 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
** can handle (i.e. not CURRENT_TIME etc.)
*/
if( pDflt ){
- sqlite3_value *pVal;
+ sqlite3_value *pVal = 0;
if( sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_NONE, &pVal) ){
db->mallocFailed = 1;
return;
@@ -80247,7 +81288,7 @@ exit_begin_add_column:
/************** End of alter.c ***********************************************/
/************** Begin file analyze.c *****************************************/
/*
-** 2005 July 8
+** 2005-07-08
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
@@ -80268,15 +81309,23 @@ exit_begin_add_column:
** CREATE TABLE sqlite_stat1(tbl, idx, stat);
** CREATE TABLE sqlite_stat2(tbl, idx, sampleno, sample);
** CREATE TABLE sqlite_stat3(tbl, idx, nEq, nLt, nDLt, sample);
+** CREATE TABLE sqlite_stat4(tbl, idx, nEq, nLt, nDLt, sample);
**
** Additional tables might be added in future releases of SQLite.
** The sqlite_stat2 table is not created or used unless the SQLite version
** is between 3.6.18 and 3.7.8, inclusive, and unless SQLite is compiled
** with SQLITE_ENABLE_STAT2. The sqlite_stat2 table is deprecated.
-** The sqlite_stat2 table is superceded by sqlite_stat3, which is only
+** The sqlite_stat2 table is superseded by sqlite_stat3, which is only
** created and used by SQLite versions 3.7.9 and later and with
-** SQLITE_ENABLE_STAT3 defined. The fucntionality of sqlite_stat3
-** is a superset of sqlite_stat2.
+** SQLITE_ENABLE_STAT3 defined. The functionality of sqlite_stat3
+** is a superset of sqlite_stat2. The sqlite_stat4 is an enhanced
+** version of sqlite_stat3 and is only available when compiled with
+** SQLITE_ENABLE_STAT4 and in SQLite versions 3.8.1 and later. It is
+** not possible to enable both STAT3 and STAT4 at the same time. If they
+** are both enabled, then STAT4 takes precedence.
+**
+** For most applications, sqlite_stat1 provides all the statisics required
+** for the query planner to make good choices.
**
** Format of sqlite_stat1:
**
@@ -80284,7 +81333,8 @@ exit_begin_add_column:
** name in the idx column. The tbl column is the name of the table to
** which the index belongs. In each such row, the stat column will be
** a string consisting of a list of integers. The first integer in this
-** list is the number of rows in the index and in the table. The second
+** list is the number of rows in the index. (This is the same as the
+** number of rows in the table, except for partial indices.) The second
** integer is the average number of rows in the index that have the same
** value in the first column of the index. The third integer is the average
** number of rows in the index that have the same value for the first two
@@ -80331,53 +81381,81 @@ exit_begin_add_column:
**
** Format for sqlite_stat3:
**
-** The sqlite_stat3 is an enhancement to sqlite_stat2. A new name is
-** used to avoid compatibility problems.
+** The sqlite_stat3 format is a subset of sqlite_stat4. Hence, the
+** sqlite_stat4 format will be described first. Further information
+** about sqlite_stat3 follows the sqlite_stat4 description.
+**
+** Format for sqlite_stat4:
**
-** The format of the sqlite_stat3 table is similar to the format of
-** the sqlite_stat2 table. There are multiple entries for each index.
+** As with sqlite_stat2, the sqlite_stat4 table contains histogram data
+** to aid the query planner in choosing good indices based on the values
+** that indexed columns are compared against in the WHERE clauses of
+** queries.
+**
+** The sqlite_stat4 table contains multiple entries for each index.
** The idx column names the index and the tbl column is the table of the
** index. If the idx and tbl columns are the same, then the sample is
-** of the INTEGER PRIMARY KEY. The sample column is a value taken from
-** the left-most column of the index. The nEq column is the approximate
-** number of entires in the index whose left-most column exactly matches
-** the sample. nLt is the approximate number of entires whose left-most
-** column is less than the sample. The nDLt column is the approximate
-** number of distinct left-most entries in the index that are less than
-** the sample.
-**
-** Future versions of SQLite might change to store a string containing
-** multiple integers values in the nDLt column of sqlite_stat3. The first
-** integer will be the number of prior index entires that are distinct in
-** the left-most column. The second integer will be the number of prior index
-** entries that are distinct in the first two columns. The third integer
-** will be the number of prior index entries that are distinct in the first
-** three columns. And so forth. With that extension, the nDLt field is
-** similar in function to the sqlite_stat1.stat field.
-**
-** There can be an arbitrary number of sqlite_stat3 entries per index.
-** The ANALYZE command will typically generate sqlite_stat3 tables
+** of the INTEGER PRIMARY KEY. The sample column is a blob which is the
+** binary encoding of a key from the index. The nEq column is a
+** list of integers. The first integer is the approximate number
+** of entries in the index whose left-most column exactly matches
+** the left-most column of the sample. The second integer in nEq
+** is the approximate number of entries in the index where the
+** first two columns match the first two columns of the sample.
+** And so forth. nLt is another list of integers that show the approximate
+** number of entries that are strictly less than the sample. The first
+** integer in nLt contains the number of entries in the index where the
+** left-most column is less than the left-most column of the sample.
+** The K-th integer in the nLt entry is the number of index entries
+** where the first K columns are less than the first K columns of the
+** sample. The nDLt column is like nLt except that it contains the
+** number of distinct entries in the index that are less than the
+** sample.
+**
+** There can be an arbitrary number of sqlite_stat4 entries per index.
+** The ANALYZE command will typically generate sqlite_stat4 tables
** that contain between 10 and 40 samples which are distributed across
** the key space, though not uniformly, and which include samples with
-** largest possible nEq values.
+** large nEq values.
+**
+** Format for sqlite_stat3 redux:
+**
+** The sqlite_stat3 table is like sqlite_stat4 except that it only
+** looks at the left-most column of the index. The sqlite_stat3.sample
+** column contains the actual value of the left-most column instead
+** of a blob encoding of the complete index key as is found in
+** sqlite_stat4.sample. The nEq, nLt, and nDLt entries of sqlite_stat3
+** all contain just a single integer which is the same as the first
+** integer in the equivalent columns in sqlite_stat4.
*/
#ifndef SQLITE_OMIT_ANALYZE
+#if defined(SQLITE_ENABLE_STAT4)
+# define IsStat4 1
+# define IsStat3 0
+#elif defined(SQLITE_ENABLE_STAT3)
+# define IsStat4 0
+# define IsStat3 1
+#else
+# define IsStat4 0
+# define IsStat3 0
+# undef SQLITE_STAT4_SAMPLES
+# define SQLITE_STAT4_SAMPLES 1
+#endif
+#define IsStat34 (IsStat3+IsStat4) /* 1 for STAT3 or STAT4. 0 otherwise */
+
/*
-** This routine generates code that opens the sqlite_stat1 table for
-** writing with cursor iStatCur. If the library was built with the
-** SQLITE_ENABLE_STAT3 macro defined, then the sqlite_stat3 table is
-** opened for writing using cursor (iStatCur+1)
+** This routine generates code that opens the sqlite_statN tables.
+** The sqlite_stat1 table is always relevant. sqlite_stat2 is now
+** obsolete. sqlite_stat3 and sqlite_stat4 are only opened when
+** appropriate compile-time options are provided.
**
-** If the sqlite_stat1 tables does not previously exist, it is created.
-** Similarly, if the sqlite_stat3 table does not exist and the library
-** is compiled with SQLITE_ENABLE_STAT3 defined, it is created.
+** If the sqlite_statN tables do not previously exist, it is created.
**
** Argument zWhere may be a pointer to a buffer containing a table name,
** or it may be a NULL pointer. If it is not NULL, then all entries in
-** the sqlite_stat1 and (if applicable) sqlite_stat3 tables associated
-** with the named table are deleted. If zWhere==0, then code is generated
-** to delete all stat table entries.
+** the sqlite_statN tables associated with the named table are deleted.
+** If zWhere==0, then code is generated to delete all stat table entries.
*/
static void openStatTable(
Parse *pParse, /* Parsing context */
@@ -80391,18 +81469,24 @@ static void openStatTable(
const char *zCols;
} aTable[] = {
{ "sqlite_stat1", "tbl,idx,stat" },
-#ifdef SQLITE_ENABLE_STAT3
+#if defined(SQLITE_ENABLE_STAT4)
+ { "sqlite_stat4", "tbl,idx,neq,nlt,ndlt,sample" },
+ { "sqlite_stat3", 0 },
+#elif defined(SQLITE_ENABLE_STAT3)
{ "sqlite_stat3", "tbl,idx,neq,nlt,ndlt,sample" },
+ { "sqlite_stat4", 0 },
+#else
+ { "sqlite_stat3", 0 },
+ { "sqlite_stat4", 0 },
#endif
};
-
- int aRoot[] = {0, 0};
- u8 aCreateTbl[] = {0, 0};
-
int i;
sqlite3 *db = pParse->db;
Db *pDb;
Vdbe *v = sqlite3GetVdbe(pParse);
+ int aRoot[ArraySize(aTable)];
+ u8 aCreateTbl[ArraySize(aTable)];
+
if( v==0 ) return;
assert( sqlite3BtreeHoldsAllMutexes(db) );
assert( sqlite3VdbeDb(v)==db );
@@ -80415,34 +81499,39 @@ static void openStatTable(
const char *zTab = aTable[i].zName;
Table *pStat;
if( (pStat = sqlite3FindTable(db, zTab, pDb->zName))==0 ){
- /* The sqlite_stat[12] table does not exist. Create it. Note that a
- ** side-effect of the CREATE TABLE statement is to leave the rootpage
- ** of the new table in register pParse->regRoot. This is important
- ** because the OpenWrite opcode below will be needing it. */
- sqlite3NestedParse(pParse,
- "CREATE TABLE %Q.%s(%s)", pDb->zName, zTab, aTable[i].zCols
- );
- aRoot[i] = pParse->regRoot;
- aCreateTbl[i] = OPFLAG_P2ISREG;
+ if( aTable[i].zCols ){
+ /* The sqlite_statN table does not exist. Create it. Note that a
+ ** side-effect of the CREATE TABLE statement is to leave the rootpage
+ ** of the new table in register pParse->regRoot. This is important
+ ** because the OpenWrite opcode below will be needing it. */
+ sqlite3NestedParse(pParse,
+ "CREATE TABLE %Q.%s(%s)", pDb->zName, zTab, aTable[i].zCols
+ );
+ aRoot[i] = pParse->regRoot;
+ aCreateTbl[i] = OPFLAG_P2ISREG;
+ }
}else{
/* The table already exists. If zWhere is not NULL, delete all entries
** associated with the table zWhere. If zWhere is NULL, delete the
** entire contents of the table. */
aRoot[i] = pStat->tnum;
+ aCreateTbl[i] = 0;
sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab);
if( zWhere ){
sqlite3NestedParse(pParse,
- "DELETE FROM %Q.%s WHERE %s=%Q", pDb->zName, zTab, zWhereType, zWhere
+ "DELETE FROM %Q.%s WHERE %s=%Q",
+ pDb->zName, zTab, zWhereType, zWhere
);
}else{
- /* The sqlite_stat[12] table already exists. Delete all rows. */
+ /* The sqlite_stat[134] table already exists. Delete all rows. */
sqlite3VdbeAddOp2(v, OP_Clear, aRoot[i], iDb);
}
}
}
- /* Open the sqlite_stat[13] tables for writing. */
- for(i=0; i<ArraySize(aTable); i++){
+ /* Open the sqlite_stat[134] tables for writing. */
+ for(i=0; aTable[i].zCols; i++){
+ assert( i<ArraySize(aTable) );
sqlite3VdbeAddOp3(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb);
sqlite3VdbeChangeP4(v, -1, (char *)3, P4_INT32);
sqlite3VdbeChangeP5(v, aCreateTbl[i]);
@@ -80450,223 +81539,591 @@ static void openStatTable(
}
/*
-** Recommended number of samples for sqlite_stat3
+** Recommended number of samples for sqlite_stat4
*/
-#ifndef SQLITE_STAT3_SAMPLES
-# define SQLITE_STAT3_SAMPLES 24
+#ifndef SQLITE_STAT4_SAMPLES
+# define SQLITE_STAT4_SAMPLES 24
#endif
/*
-** Three SQL functions - stat3_init(), stat3_push(), and stat3_pop() -
+** Three SQL functions - stat_init(), stat_push(), and stat_get() -
** share an instance of the following structure to hold their state
** information.
*/
-typedef struct Stat3Accum Stat3Accum;
-struct Stat3Accum {
+typedef struct Stat4Accum Stat4Accum;
+typedef struct Stat4Sample Stat4Sample;
+struct Stat4Sample {
+ tRowcnt *anEq; /* sqlite_stat4.nEq */
+ tRowcnt *anDLt; /* sqlite_stat4.nDLt */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ tRowcnt *anLt; /* sqlite_stat4.nLt */
+ i64 iRowid; /* Rowid in main table of the key */
+ u8 isPSample; /* True if a periodic sample */
+ int iCol; /* If !isPSample, the reason for inclusion */
+ u32 iHash; /* Tiebreaker hash */
+#endif
+};
+struct Stat4Accum {
tRowcnt nRow; /* Number of rows in the entire table */
tRowcnt nPSample; /* How often to do a periodic sample */
- int iMin; /* Index of entry with minimum nEq and hash */
+ int nCol; /* Number of columns in index + rowid */
int mxSample; /* Maximum number of samples to accumulate */
- int nSample; /* Current number of samples */
+ Stat4Sample current; /* Current row as a Stat4Sample */
u32 iPrn; /* Pseudo-random number used for sampling */
- struct Stat3Sample {
- i64 iRowid; /* Rowid in main table of the key */
- tRowcnt nEq; /* sqlite_stat3.nEq */
- tRowcnt nLt; /* sqlite_stat3.nLt */
- tRowcnt nDLt; /* sqlite_stat3.nDLt */
- u8 isPSample; /* True if a periodic sample */
- u32 iHash; /* Tiebreaker hash */
- } *a; /* An array of samples */
+ Stat4Sample *aBest; /* Array of (nCol-1) best samples */
+ int iMin; /* Index in a[] of entry with minimum score */
+ int nSample; /* Current number of samples */
+ int iGet; /* Index of current sample accessed by stat_get() */
+ Stat4Sample *a; /* Array of mxSample Stat4Sample objects */
};
-#ifdef SQLITE_ENABLE_STAT3
/*
-** Implementation of the stat3_init(C,S) SQL function. The two parameters
-** are the number of rows in the table or index (C) and the number of samples
-** to accumulate (S).
-**
-** This routine allocates the Stat3Accum object.
+** Implementation of the stat_init(N,C) SQL function. The two parameters
+** are the number of rows in the table or index (C) and the number of columns
+** in the index (N). The second argument (C) is only used for STAT3 and STAT4.
**
-** The return value is the Stat3Accum object (P).
+** This routine allocates the Stat4Accum object in heap memory. The return
+** value is a pointer to the the Stat4Accum object encoded as a blob (i.e.
+** the size of the blob is sizeof(void*) bytes).
*/
-static void stat3Init(
+static void statInit(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
- Stat3Accum *p;
- tRowcnt nRow;
- int mxSample;
- int n;
+ Stat4Accum *p;
+ int nCol; /* Number of columns in index being sampled */
+ int nColUp; /* nCol rounded up for alignment */
+ int n; /* Bytes of space to allocate */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ int mxSample = SQLITE_STAT4_SAMPLES;
+#endif
+ /* Decode the three function arguments */
UNUSED_PARAMETER(argc);
- nRow = (tRowcnt)sqlite3_value_int64(argv[0]);
- mxSample = sqlite3_value_int(argv[1]);
- n = sizeof(*p) + sizeof(p->a[0])*mxSample;
- p = sqlite3MallocZero( n );
+ nCol = sqlite3_value_int(argv[0]);
+ assert( nCol>1 ); /* >1 because it includes the rowid column */
+ nColUp = sizeof(tRowcnt)<8 ? (nCol+1)&~1 : nCol;
+
+ /* Allocate the space required for the Stat4Accum object */
+ n = sizeof(*p)
+ + sizeof(tRowcnt)*nColUp /* Stat4Accum.anEq */
+ + sizeof(tRowcnt)*nColUp /* Stat4Accum.anDLt */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ + sizeof(tRowcnt)*nColUp /* Stat4Accum.anLt */
+ + sizeof(Stat4Sample)*(nCol+mxSample) /* Stat4Accum.aBest[], a[] */
+ + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample)
+#endif
+ ;
+ p = sqlite3MallocZero(n);
if( p==0 ){
sqlite3_result_error_nomem(context);
return;
}
- p->a = (struct Stat3Sample*)&p[1];
- p->nRow = nRow;
- p->mxSample = mxSample;
- p->nPSample = p->nRow/(mxSample/3+1) + 1;
- sqlite3_randomness(sizeof(p->iPrn), &p->iPrn);
+
+ p->nRow = 0;
+ p->nCol = nCol;
+ p->current.anDLt = (tRowcnt*)&p[1];
+ p->current.anEq = &p->current.anDLt[nColUp];
+
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ {
+ u8 *pSpace; /* Allocated space not yet assigned */
+ int i; /* Used to iterate through p->aSample[] */
+
+ p->iGet = -1;
+ p->mxSample = mxSample;
+ p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[1])/(mxSample/3+1) + 1);
+ p->current.anLt = &p->current.anEq[nColUp];
+ p->iPrn = nCol*0x689e962d ^ sqlite3_value_int(argv[1])*0xd0944565;
+
+ /* Set up the Stat4Accum.a[] and aBest[] arrays */
+ p->a = (struct Stat4Sample*)&p->current.anLt[nColUp];
+ p->aBest = &p->a[mxSample];
+ pSpace = (u8*)(&p->a[mxSample+nCol]);
+ for(i=0; i<(mxSample+nCol); i++){
+ p->a[i].anEq = (tRowcnt *)pSpace; pSpace += (sizeof(tRowcnt) * nColUp);
+ p->a[i].anLt = (tRowcnt *)pSpace; pSpace += (sizeof(tRowcnt) * nColUp);
+ p->a[i].anDLt = (tRowcnt *)pSpace; pSpace += (sizeof(tRowcnt) * nColUp);
+ }
+ assert( (pSpace - (u8*)p)==n );
+
+ for(i=0; i<nCol; i++){
+ p->aBest[i].iCol = i;
+ }
+ }
+#endif
+
+ /* Return a pointer to the allocated object to the caller */
sqlite3_result_blob(context, p, sizeof(p), sqlite3_free);
}
-static const FuncDef stat3InitFuncdef = {
- 2, /* nArg */
- SQLITE_UTF8, /* iPrefEnc */
- 0, /* flags */
- 0, /* pUserData */
- 0, /* pNext */
- stat3Init, /* xFunc */
- 0, /* xStep */
- 0, /* xFinalize */
- "stat3_init", /* zName */
- 0, /* pHash */
- 0 /* pDestructor */
+static const FuncDef statInitFuncdef = {
+ 1+IsStat34, /* nArg */
+ SQLITE_UTF8, /* funcFlags */
+ 0, /* pUserData */
+ 0, /* pNext */
+ statInit, /* xFunc */
+ 0, /* xStep */
+ 0, /* xFinalize */
+ "stat_init", /* zName */
+ 0, /* pHash */
+ 0 /* pDestructor */
};
+#ifdef SQLITE_ENABLE_STAT4
+/*
+** pNew and pOld are both candidate non-periodic samples selected for
+** the same column (pNew->iCol==pOld->iCol). Ignoring this column and
+** considering only any trailing columns and the sample hash value, this
+** function returns true if sample pNew is to be preferred over pOld.
+** In other words, if we assume that the cardinalities of the selected
+** column for pNew and pOld are equal, is pNew to be preferred over pOld.
+**
+** This function assumes that for each argument sample, the contents of
+** the anEq[] array from pSample->anEq[pSample->iCol+1] onwards are valid.
+*/
+static int sampleIsBetterPost(
+ Stat4Accum *pAccum,
+ Stat4Sample *pNew,
+ Stat4Sample *pOld
+){
+ int nCol = pAccum->nCol;
+ int i;
+ assert( pNew->iCol==pOld->iCol );
+ for(i=pNew->iCol+1; i<nCol; i++){
+ if( pNew->anEq[i]>pOld->anEq[i] ) return 1;
+ if( pNew->anEq[i]<pOld->anEq[i] ) return 0;
+ }
+ if( pNew->iHash>pOld->iHash ) return 1;
+ return 0;
+}
+#endif
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
/*
-** Implementation of the stat3_push(nEq,nLt,nDLt,rowid,P) SQL function. The
-** arguments describe a single key instance. This routine makes the
-** decision about whether or not to retain this key for the sqlite_stat3
-** table.
+** Return true if pNew is to be preferred over pOld.
**
-** The return value is NULL.
+** This function assumes that for each argument sample, the contents of
+** the anEq[] array from pSample->anEq[pSample->iCol] onwards are valid.
*/
-static void stat3Push(
- sqlite3_context *context,
- int argc,
- sqlite3_value **argv
+static int sampleIsBetter(
+ Stat4Accum *pAccum,
+ Stat4Sample *pNew,
+ Stat4Sample *pOld
){
- Stat3Accum *p = (Stat3Accum*)sqlite3_value_blob(argv[4]);
- tRowcnt nEq = sqlite3_value_int64(argv[0]);
- tRowcnt nLt = sqlite3_value_int64(argv[1]);
- tRowcnt nDLt = sqlite3_value_int64(argv[2]);
- i64 rowid = sqlite3_value_int64(argv[3]);
- u8 isPSample = 0;
- u8 doInsert = 0;
- int iMin = p->iMin;
- struct Stat3Sample *pSample;
+ tRowcnt nEqNew = pNew->anEq[pNew->iCol];
+ tRowcnt nEqOld = pOld->anEq[pOld->iCol];
+
+ assert( pOld->isPSample==0 && pNew->isPSample==0 );
+ assert( IsStat4 || (pNew->iCol==0 && pOld->iCol==0) );
+
+ if( (nEqNew>nEqOld) ) return 1;
+#ifdef SQLITE_ENABLE_STAT4
+ if( nEqNew==nEqOld ){
+ if( pNew->iCol<pOld->iCol ) return 1;
+ return (pNew->iCol==pOld->iCol && sampleIsBetterPost(pAccum, pNew, pOld));
+ }
+ return 0;
+#else
+ return (nEqNew==nEqOld && pNew->iHash>pOld->iHash);
+#endif
+}
+
+/*
+** Copy the contents of object (*pFrom) into (*pTo).
+*/
+static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){
+ pTo->iRowid = pFrom->iRowid;
+ pTo->isPSample = pFrom->isPSample;
+ pTo->iCol = pFrom->iCol;
+ pTo->iHash = pFrom->iHash;
+ memcpy(pTo->anEq, pFrom->anEq, sizeof(tRowcnt)*p->nCol);
+ memcpy(pTo->anLt, pFrom->anLt, sizeof(tRowcnt)*p->nCol);
+ memcpy(pTo->anDLt, pFrom->anDLt, sizeof(tRowcnt)*p->nCol);
+}
+
+/*
+** Copy the contents of sample *pNew into the p->a[] array. If necessary,
+** remove the least desirable sample from p->a[] to make room.
+*/
+static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
+ Stat4Sample *pSample;
int i;
- u32 h;
- UNUSED_PARAMETER(context);
- UNUSED_PARAMETER(argc);
- if( nEq==0 ) return;
- h = p->iPrn = p->iPrn*1103515245 + 12345;
- if( (nLt/p->nPSample)!=((nEq+nLt)/p->nPSample) ){
- doInsert = isPSample = 1;
- }else if( p->nSample<p->mxSample ){
- doInsert = 1;
- }else{
- if( nEq>p->a[iMin].nEq || (nEq==p->a[iMin].nEq && h>p->a[iMin].iHash) ){
- doInsert = 1;
+ assert( IsStat4 || nEqZero==0 );
+
+#ifdef SQLITE_ENABLE_STAT4
+ if( pNew->isPSample==0 ){
+ Stat4Sample *pUpgrade = 0;
+ assert( pNew->anEq[pNew->iCol]>0 );
+
+ /* This sample is being added because the prefix that ends in column
+ ** iCol occurs many times in the table. However, if we have already
+ ** added a sample that shares this prefix, there is no need to add
+ ** this one. Instead, upgrade the priority of the highest priority
+ ** existing sample that shares this prefix. */
+ for(i=p->nSample-1; i>=0; i--){
+ Stat4Sample *pOld = &p->a[i];
+ if( pOld->anEq[pNew->iCol]==0 ){
+ if( pOld->isPSample ) return;
+ assert( pOld->iCol>pNew->iCol );
+ assert( sampleIsBetter(p, pNew, pOld) );
+ if( pUpgrade==0 || sampleIsBetter(p, pOld, pUpgrade) ){
+ pUpgrade = pOld;
+ }
+ }
+ }
+ if( pUpgrade ){
+ pUpgrade->iCol = pNew->iCol;
+ pUpgrade->anEq[pUpgrade->iCol] = pNew->anEq[pUpgrade->iCol];
+ goto find_new_min;
}
}
- if( !doInsert ) return;
- if( p->nSample==p->mxSample ){
- assert( p->nSample - iMin - 1 >= 0 );
- memmove(&p->a[iMin], &p->a[iMin+1], sizeof(p->a[0])*(p->nSample-iMin-1));
+#endif
+
+ /* If necessary, remove sample iMin to make room for the new sample. */
+ if( p->nSample>=p->mxSample ){
+ Stat4Sample *pMin = &p->a[p->iMin];
+ tRowcnt *anEq = pMin->anEq;
+ tRowcnt *anLt = pMin->anLt;
+ tRowcnt *anDLt = pMin->anDLt;
+ memmove(pMin, &pMin[1], sizeof(p->a[0])*(p->nSample-p->iMin-1));
pSample = &p->a[p->nSample-1];
- }else{
- pSample = &p->a[p->nSample++];
+ pSample->anEq = anEq;
+ pSample->anDLt = anDLt;
+ pSample->anLt = anLt;
+ p->nSample = p->mxSample-1;
}
- pSample->iRowid = rowid;
- pSample->nEq = nEq;
- pSample->nLt = nLt;
- pSample->nDLt = nDLt;
- pSample->iHash = h;
- pSample->isPSample = isPSample;
- /* Find the new minimum */
- if( p->nSample==p->mxSample ){
- pSample = p->a;
- i = 0;
- while( pSample->isPSample ){
- i++;
- pSample++;
- assert( i<p->nSample );
- }
- nEq = pSample->nEq;
- h = pSample->iHash;
- iMin = i;
- for(i++, pSample++; i<p->nSample; i++, pSample++){
- if( pSample->isPSample ) continue;
- if( pSample->nEq<nEq
- || (pSample->nEq==nEq && pSample->iHash<h)
- ){
+ /* The "rows less-than" for the rowid column must be greater than that
+ ** for the last sample in the p->a[] array. Otherwise, the samples would
+ ** be out of order. */
+#ifdef SQLITE_ENABLE_STAT4
+ assert( p->nSample==0
+ || pNew->anLt[p->nCol-1] > p->a[p->nSample-1].anLt[p->nCol-1] );
+#endif
+
+ /* Insert the new sample */
+ pSample = &p->a[p->nSample];
+ sampleCopy(p, pSample, pNew);
+ p->nSample++;
+
+ /* Zero the first nEqZero entries in the anEq[] array. */
+ memset(pSample->anEq, 0, sizeof(tRowcnt)*nEqZero);
+
+#ifdef SQLITE_ENABLE_STAT4
+ find_new_min:
+#endif
+ if( p->nSample>=p->mxSample ){
+ int iMin = -1;
+ for(i=0; i<p->mxSample; i++){
+ if( p->a[i].isPSample ) continue;
+ if( iMin<0 || sampleIsBetter(p, &p->a[iMin], &p->a[i]) ){
iMin = i;
- nEq = pSample->nEq;
- h = pSample->iHash;
}
}
+ assert( iMin>=0 );
p->iMin = iMin;
}
}
-static const FuncDef stat3PushFuncdef = {
- 5, /* nArg */
- SQLITE_UTF8, /* iPrefEnc */
- 0, /* flags */
- 0, /* pUserData */
- 0, /* pNext */
- stat3Push, /* xFunc */
- 0, /* xStep */
- 0, /* xFinalize */
- "stat3_push", /* zName */
- 0, /* pHash */
- 0 /* pDestructor */
-};
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
/*
-** Implementation of the stat3_get(P,N,...) SQL function. This routine is
-** used to query the results. Content is returned for the Nth sqlite_stat3
-** row where N is between 0 and S-1 and S is the number of samples. The
-** value returned depends on the number of arguments.
+** Field iChng of the index being scanned has changed. So at this point
+** p->current contains a sample that reflects the previous row of the
+** index. The value of anEq[iChng] and subsequent anEq[] elements are
+** correct at this point.
+*/
+static void samplePushPrevious(Stat4Accum *p, int iChng){
+#ifdef SQLITE_ENABLE_STAT4
+ int i;
+
+ /* Check if any samples from the aBest[] array should be pushed
+ ** into IndexSample.a[] at this point. */
+ for(i=(p->nCol-2); i>=iChng; i--){
+ Stat4Sample *pBest = &p->aBest[i];
+ pBest->anEq[i] = p->current.anEq[i];
+ if( p->nSample<p->mxSample || sampleIsBetter(p, pBest, &p->a[p->iMin]) ){
+ sampleInsert(p, pBest, i);
+ }
+ }
+
+ /* Update the anEq[] fields of any samples already collected. */
+ for(i=p->nSample-1; i>=0; i--){
+ int j;
+ for(j=iChng; j<p->nCol; j++){
+ if( p->a[i].anEq[j]==0 ) p->a[i].anEq[j] = p->current.anEq[j];
+ }
+ }
+#endif
+
+#if defined(SQLITE_ENABLE_STAT3) && !defined(SQLITE_ENABLE_STAT4)
+ if( iChng==0 ){
+ tRowcnt nLt = p->current.anLt[0];
+ tRowcnt nEq = p->current.anEq[0];
+
+ /* Check if this is to be a periodic sample. If so, add it. */
+ if( (nLt/p->nPSample)!=(nLt+nEq)/p->nPSample ){
+ p->current.isPSample = 1;
+ sampleInsert(p, &p->current, 0);
+ p->current.isPSample = 0;
+ }else
+
+ /* Or if it is a non-periodic sample. Add it in this case too. */
+ if( p->nSample<p->mxSample
+ || sampleIsBetter(p, &p->current, &p->a[p->iMin])
+ ){
+ sampleInsert(p, &p->current, 0);
+ }
+ }
+#endif
+
+#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
+ UNUSED_PARAMETER( p );
+ UNUSED_PARAMETER( iChng );
+#endif
+}
+
+/*
+** Implementation of the stat_push SQL function: stat_push(P,R,C)
+** Arguments:
+**
+** P Pointer to the Stat4Accum object created by stat_init()
+** C Index of left-most column to differ from previous row
+** R Rowid for the current row
**
-** argc==2 result: rowid
-** argc==3 result: nEq
-** argc==4 result: nLt
-** argc==5 result: nDLt
+** The SQL function always returns NULL.
+**
+** The R parameter is only used for STAT3 and STAT4.
*/
-static void stat3Get(
+static void statPush(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
- int n = sqlite3_value_int(argv[1]);
- Stat3Accum *p = (Stat3Accum*)sqlite3_value_blob(argv[0]);
+ int i;
- assert( p!=0 );
- if( p->nSample<=n ) return;
- switch( argc ){
- case 2: sqlite3_result_int64(context, p->a[n].iRowid); break;
- case 3: sqlite3_result_int64(context, p->a[n].nEq); break;
- case 4: sqlite3_result_int64(context, p->a[n].nLt); break;
- default: sqlite3_result_int64(context, p->a[n].nDLt); break;
- }
-}
-static const FuncDef stat3GetFuncdef = {
- -1, /* nArg */
- SQLITE_UTF8, /* iPrefEnc */
- 0, /* flags */
- 0, /* pUserData */
- 0, /* pNext */
- stat3Get, /* xFunc */
- 0, /* xStep */
- 0, /* xFinalize */
- "stat3_get", /* zName */
- 0, /* pHash */
- 0 /* pDestructor */
+ /* The three function arguments */
+ Stat4Accum *p = (Stat4Accum*)sqlite3_value_blob(argv[0]);
+ int iChng = sqlite3_value_int(argv[1]);
+
+ UNUSED_PARAMETER( argc );
+ UNUSED_PARAMETER( context );
+ assert( p->nCol>1 ); /* Includes rowid field */
+ assert( iChng<p->nCol );
+
+ if( p->nRow==0 ){
+ /* This is the first call to this function. Do initialization. */
+ for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1;
+ }else{
+ /* Second and subsequent calls get processed here */
+ samplePushPrevious(p, iChng);
+
+ /* Update anDLt[], anLt[] and anEq[] to reflect the values that apply
+ ** to the current row of the index. */
+ for(i=0; i<iChng; i++){
+ p->current.anEq[i]++;
+ }
+ for(i=iChng; i<p->nCol; i++){
+ p->current.anDLt[i]++;
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ p->current.anLt[i] += p->current.anEq[i];
+#endif
+ p->current.anEq[i] = 1;
+ }
+ }
+ p->nRow++;
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ p->current.iRowid = sqlite3_value_int64(argv[2]);
+ p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345;
+#endif
+
+#ifdef SQLITE_ENABLE_STAT4
+ {
+ tRowcnt nLt = p->current.anLt[p->nCol-1];
+
+ /* Check if this is to be a periodic sample. If so, add it. */
+ if( (nLt/p->nPSample)!=(nLt+1)/p->nPSample ){
+ p->current.isPSample = 1;
+ p->current.iCol = 0;
+ sampleInsert(p, &p->current, p->nCol-1);
+ p->current.isPSample = 0;
+ }
+
+ /* Update the aBest[] array. */
+ for(i=0; i<(p->nCol-1); i++){
+ p->current.iCol = i;
+ if( i>=iChng || sampleIsBetterPost(p, &p->current, &p->aBest[i]) ){
+ sampleCopy(p, &p->aBest[i], &p->current);
+ }
+ }
+ }
+#endif
+}
+static const FuncDef statPushFuncdef = {
+ 2+IsStat34, /* nArg */
+ SQLITE_UTF8, /* funcFlags */
+ 0, /* pUserData */
+ 0, /* pNext */
+ statPush, /* xFunc */
+ 0, /* xStep */
+ 0, /* xFinalize */
+ "stat_push", /* zName */
+ 0, /* pHash */
+ 0 /* pDestructor */
};
-#endif /* SQLITE_ENABLE_STAT3 */
+#define STAT_GET_STAT1 0 /* "stat" column of stat1 table */
+#define STAT_GET_ROWID 1 /* "rowid" column of stat[34] entry */
+#define STAT_GET_NEQ 2 /* "neq" column of stat[34] entry */
+#define STAT_GET_NLT 3 /* "nlt" column of stat[34] entry */
+#define STAT_GET_NDLT 4 /* "ndlt" column of stat[34] entry */
+
+/*
+** Implementation of the stat_get(P,J) SQL function. This routine is
+** used to query the results. Content is returned for parameter J
+** which is one of the STAT_GET_xxxx values defined above.
+**
+** If neither STAT3 nor STAT4 are enabled, then J is always
+** STAT_GET_STAT1 and is hence omitted and this routine becomes
+** a one-parameter function, stat_get(P), that always returns the
+** stat1 table entry information.
+*/
+static void statGet(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ Stat4Accum *p = (Stat4Accum*)sqlite3_value_blob(argv[0]);
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ /* STAT3 and STAT4 have a parameter on this routine. */
+ int eCall = sqlite3_value_int(argv[1]);
+ assert( argc==2 );
+ assert( eCall==STAT_GET_STAT1 || eCall==STAT_GET_NEQ
+ || eCall==STAT_GET_ROWID || eCall==STAT_GET_NLT
+ || eCall==STAT_GET_NDLT
+ );
+ if( eCall==STAT_GET_STAT1 )
+#else
+ assert( argc==1 );
+#endif
+ {
+ /* Return the value to store in the "stat" column of the sqlite_stat1
+ ** table for this index.
+ **
+ ** The value is a string composed of a list of integers describing
+ ** the index. The first integer in the list is the total number of
+ ** entries in the index. There is one additional integer in the list
+ ** for each indexed column. This additional integer is an estimate of
+ ** the number of rows matched by a stabbing query on the index using
+ ** a key with the corresponding number of fields. In other words,
+ ** if the index is on columns (a,b) and the sqlite_stat1 value is
+ ** "100 10 2", then SQLite estimates that:
+ **
+ ** * the index contains 100 rows,
+ ** * "WHERE a=?" matches 10 rows, and
+ ** * "WHERE a=? AND b=?" matches 2 rows.
+ **
+ ** If D is the count of distinct values and K is the total number of
+ ** rows, then each estimate is computed as:
+ **
+ ** I = (K+D-1)/D
+ */
+ char *z;
+ int i;
+
+ char *zRet = sqlite3MallocZero(p->nCol * 25);
+ if( zRet==0 ){
+ sqlite3_result_error_nomem(context);
+ return;
+ }
+ sqlite3_snprintf(24, zRet, "%llu", (u64)p->nRow);
+ z = zRet + sqlite3Strlen30(zRet);
+ for(i=0; i<(p->nCol-1); i++){
+ u64 nDistinct = p->current.anDLt[i] + 1;
+ u64 iVal = (p->nRow + nDistinct - 1) / nDistinct;
+ sqlite3_snprintf(24, z, " %llu", iVal);
+ z += sqlite3Strlen30(z);
+ assert( p->current.anEq[i] );
+ }
+ assert( z[0]=='\0' && z>zRet );
+
+ sqlite3_result_text(context, zRet, -1, sqlite3_free);
+ }
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ else if( eCall==STAT_GET_ROWID ){
+ if( p->iGet<0 ){
+ samplePushPrevious(p, 0);
+ p->iGet = 0;
+ }
+ if( p->iGet<p->nSample ){
+ sqlite3_result_int64(context, p->a[p->iGet].iRowid);
+ }
+ }else{
+ tRowcnt *aCnt = 0;
+
+ assert( p->iGet<p->nSample );
+ switch( eCall ){
+ case STAT_GET_NEQ: aCnt = p->a[p->iGet].anEq; break;
+ case STAT_GET_NLT: aCnt = p->a[p->iGet].anLt; break;
+ default: {
+ aCnt = p->a[p->iGet].anDLt;
+ p->iGet++;
+ break;
+ }
+ }
+
+ if( IsStat3 ){
+ sqlite3_result_int64(context, (i64)aCnt[0]);
+ }else{
+ char *zRet = sqlite3MallocZero(p->nCol * 25);
+ if( zRet==0 ){
+ sqlite3_result_error_nomem(context);
+ }else{
+ int i;
+ char *z = zRet;
+ for(i=0; i<p->nCol; i++){
+ sqlite3_snprintf(24, z, "%llu ", (u64)aCnt[i]);
+ z += sqlite3Strlen30(z);
+ }
+ assert( z[0]=='\0' && z>zRet );
+ z[-1] = '\0';
+ sqlite3_result_text(context, zRet, -1, sqlite3_free);
+ }
+ }
+ }
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#ifndef SQLITE_DEBUG
+ UNUSED_PARAMETER( argc );
+#endif
+}
+static const FuncDef statGetFuncdef = {
+ 1+IsStat34, /* nArg */
+ SQLITE_UTF8, /* funcFlags */
+ 0, /* pUserData */
+ 0, /* pNext */
+ statGet, /* xFunc */
+ 0, /* xStep */
+ 0, /* xFinalize */
+ "stat_get", /* zName */
+ 0, /* pHash */
+ 0 /* pDestructor */
+};
+static void callStatGet(Vdbe *v, int regStat4, int iParam, int regOut){
+ assert( regOut!=regStat4 && regOut!=regStat4+1 );
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ sqlite3VdbeAddOp2(v, OP_Integer, iParam, regStat4+1);
+#elif SQLITE_DEBUG
+ assert( iParam==STAT_GET_STAT1 );
+#else
+ UNUSED_PARAMETER( iParam );
+#endif
+ sqlite3VdbeAddOp3(v, OP_Function, 0, regStat4, regOut);
+ sqlite3VdbeChangeP4(v, -1, (char*)&statGetFuncdef, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, 1 + IsStat34);
+}
/*
** Generate code to do an analysis of all indices associated with
@@ -80677,41 +82134,31 @@ static void analyzeOneTable(
Table *pTab, /* Table whose indices are to be analyzed */
Index *pOnlyIdx, /* If not NULL, only analyze this one index */
int iStatCur, /* Index of VdbeCursor that writes the sqlite_stat1 table */
- int iMem /* Available memory locations begin here */
+ int iMem, /* Available memory locations begin here */
+ int iTab /* Next available cursor */
){
sqlite3 *db = pParse->db; /* Database handle */
Index *pIdx; /* An index to being analyzed */
int iIdxCur; /* Cursor open on index being analyzed */
+ int iTabCur; /* Table cursor */
Vdbe *v; /* The virtual machine being built up */
int i; /* Loop counter */
- int topOfLoop; /* The top of the loop */
- int endOfLoop; /* The end of the loop */
int jZeroRows = -1; /* Jump from here if number of rows is zero */
int iDb; /* Index of database containing pTab */
+ u8 needTableCnt = 1; /* True to count the table */
+ int regNewRowid = iMem++; /* Rowid for the inserted record */
+ int regStat4 = iMem++; /* Register to hold Stat4Accum object */
+ int regChng = iMem++; /* Index of changed index field */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ int regRowid = iMem++; /* Rowid argument passed to stat_push() */
+#endif
+ int regTemp = iMem++; /* Temporary use register */
int regTabname = iMem++; /* Register containing table name */
int regIdxname = iMem++; /* Register containing index name */
- int regStat1 = iMem++; /* The stat column of sqlite_stat1 */
-#ifdef SQLITE_ENABLE_STAT3
- int regNumEq = regStat1; /* Number of instances. Same as regStat1 */
- int regNumLt = iMem++; /* Number of keys less than regSample */
- int regNumDLt = iMem++; /* Number of distinct keys less than regSample */
- int regSample = iMem++; /* The next sample value */
- int regRowid = regSample; /* Rowid of a sample */
- int regAccum = iMem++; /* Register to hold Stat3Accum object */
- int regLoop = iMem++; /* Loop counter */
- int regCount = iMem++; /* Number of rows in the table or index */
- int regTemp1 = iMem++; /* Intermediate register */
- int regTemp2 = iMem++; /* Intermediate register */
- int once = 1; /* One-time initialization */
- int shortJump = 0; /* Instruction address */
- int iTabCur = pParse->nTab++; /* Table cursor */
-#endif
- int regCol = iMem++; /* Content of a column in analyzed table */
- int regRec = iMem++; /* Register holding completed record */
- int regTemp = iMem++; /* Temporary use register */
- int regNewRowid = iMem++; /* Rowid for the inserted record */
-
+ int regStat1 = iMem++; /* Value for the stat column of sqlite_stat1 */
+ int regPrev = iMem; /* MUST BE LAST (see below) */
+ pParse->nMem = MAX(pParse->nMem, iMem);
v = sqlite3GetVdbe(pParse);
if( v==0 || NEVER(pTab==0) ){
return;
@@ -80735,215 +82182,226 @@ static void analyzeOneTable(
}
#endif
- /* Establish a read-lock on the table at the shared-cache level. */
+ /* Establish a read-lock on the table at the shared-cache level.
+ ** Open a read-only cursor on the table. Also allocate a cursor number
+ ** to use for scanning indexes (iIdxCur). No index cursor is opened at
+ ** this time though. */
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
-
- iIdxCur = pParse->nTab++;
+ iTabCur = iTab++;
+ iIdxCur = iTab++;
+ pParse->nTab = MAX(pParse->nTab, iTab);
+ sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead);
sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0);
+
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- int nCol;
- KeyInfo *pKey;
- int addrIfNot = 0; /* address of OP_IfNot */
- int *aChngAddr; /* Array of jump instruction addresses */
+ int nCol; /* Number of columns indexed by pIdx */
+ KeyInfo *pKey; /* KeyInfo structure for pIdx */
+ int *aGotoChng; /* Array of jump instruction addresses */
+ int addrRewind; /* Address of "OP_Rewind iIdxCur" */
+ int addrGotoChng0; /* Address of "Goto addr_chng_0" */
+ int addrNextRow; /* Address of "next_row:" */
if( pOnlyIdx && pOnlyIdx!=pIdx ) continue;
+ if( pIdx->pPartIdxWhere==0 ) needTableCnt = 0;
VdbeNoopComment((v, "Begin analysis of %s", pIdx->zName));
nCol = pIdx->nColumn;
- aChngAddr = sqlite3DbMallocRaw(db, sizeof(int)*nCol);
- if( aChngAddr==0 ) continue;
+ aGotoChng = sqlite3DbMallocRaw(db, sizeof(int)*(nCol+1));
+ if( aGotoChng==0 ) continue;
pKey = sqlite3IndexKeyinfo(pParse, pIdx);
- if( iMem+1+(nCol*2)>pParse->nMem ){
- pParse->nMem = iMem+1+(nCol*2);
- }
-
- /* Open a cursor to the index to be analyzed. */
- assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) );
- sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb,
- (char *)pKey, P4_KEYINFO_HANDOFF);
- VdbeComment((v, "%s", pIdx->zName));
/* Populate the register containing the index name. */
sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0);
-#ifdef SQLITE_ENABLE_STAT3
- if( once ){
- once = 0;
- sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead);
- }
- sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regCount);
- sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_STAT3_SAMPLES, regTemp1);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumEq);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumLt);
- sqlite3VdbeAddOp2(v, OP_Integer, -1, regNumDLt);
- sqlite3VdbeAddOp3(v, OP_Null, 0, regSample, regAccum);
- sqlite3VdbeAddOp4(v, OP_Function, 1, regCount, regAccum,
- (char*)&stat3InitFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 2);
-#endif /* SQLITE_ENABLE_STAT3 */
-
- /* The block of memory cells initialized here is used as follows.
+ /*
+ ** Pseudo-code for loop that calls stat_push():
+ **
+ ** Rewind csr
+ ** if eof(csr) goto end_of_scan;
+ ** regChng = 0
+ ** goto chng_addr_0;
**
- ** iMem:
- ** The total number of rows in the table.
+ ** next_row:
+ ** regChng = 0
+ ** if( idx(0) != regPrev(0) ) goto chng_addr_0
+ ** regChng = 1
+ ** if( idx(1) != regPrev(1) ) goto chng_addr_1
+ ** ...
+ ** regChng = N
+ ** goto chng_addr_N
**
- ** iMem+1 .. iMem+nCol:
- ** Number of distinct entries in index considering the
- ** left-most N columns only, where N is between 1 and nCol,
- ** inclusive.
+ ** chng_addr_0:
+ ** regPrev(0) = idx(0)
+ ** chng_addr_1:
+ ** regPrev(1) = idx(1)
+ ** ...
**
- ** iMem+nCol+1 .. Mem+2*nCol:
- ** Previous value of indexed columns, from left to right.
+ ** chng_addr_N:
+ ** regRowid = idx(rowid)
+ ** stat_push(P, regChng, regRowid)
+ ** Next csr
+ ** if !eof(csr) goto next_row;
**
- ** Cells iMem through iMem+nCol are initialized to 0. The others are
- ** initialized to contain an SQL NULL.
+ ** end_of_scan:
*/
- for(i=0; i<=nCol; i++){
- sqlite3VdbeAddOp2(v, OP_Integer, 0, iMem+i);
- }
- for(i=0; i<nCol; i++){
- sqlite3VdbeAddOp2(v, OP_Null, 0, iMem+nCol+i+1);
- }
-
- /* Start the analysis loop. This loop runs through all the entries in
- ** the index b-tree. */
- endOfLoop = sqlite3VdbeMakeLabel(v);
- sqlite3VdbeAddOp2(v, OP_Rewind, iIdxCur, endOfLoop);
- topOfLoop = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp2(v, OP_AddImm, iMem, 1); /* Increment row counter */
- for(i=0; i<nCol; i++){
- CollSeq *pColl;
- sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regCol);
- if( i==0 ){
- /* Always record the very first row */
- addrIfNot = sqlite3VdbeAddOp1(v, OP_IfNot, iMem+1);
- }
- assert( pIdx->azColl!=0 );
- assert( pIdx->azColl[i]!=0 );
- pColl = sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
- aChngAddr[i] = sqlite3VdbeAddOp4(v, OP_Ne, regCol, 0, iMem+nCol+i+1,
- (char*)pColl, P4_COLLSEQ);
- sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
- VdbeComment((v, "jump if column %d changed", i));
-#ifdef SQLITE_ENABLE_STAT3
- if( i==0 ){
- sqlite3VdbeAddOp2(v, OP_AddImm, regNumEq, 1);
- VdbeComment((v, "incr repeat count"));
- }
-#endif
- }
- sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop);
- for(i=0; i<nCol; i++){
- sqlite3VdbeJumpHere(v, aChngAddr[i]); /* Set jump dest for the OP_Ne */
- if( i==0 ){
- sqlite3VdbeJumpHere(v, addrIfNot); /* Jump dest for OP_IfNot */
-#ifdef SQLITE_ENABLE_STAT3
- sqlite3VdbeAddOp4(v, OP_Function, 1, regNumEq, regTemp2,
- (char*)&stat3PushFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 5);
- sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, pIdx->nColumn, regRowid);
- sqlite3VdbeAddOp3(v, OP_Add, regNumEq, regNumLt, regNumLt);
- sqlite3VdbeAddOp2(v, OP_AddImm, regNumDLt, 1);
- sqlite3VdbeAddOp2(v, OP_Integer, 1, regNumEq);
-#endif
- }
- sqlite3VdbeAddOp2(v, OP_AddImm, iMem+i+1, 1);
- sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, iMem+nCol+i+1);
- }
- sqlite3DbFree(db, aChngAddr);
+ /* Make sure there are enough memory cells allocated to accommodate
+ ** the regPrev array and a trailing rowid (the rowid slot is required
+ ** when building a record to insert into the sample column of
+ ** the sqlite_stat4 table. */
+ pParse->nMem = MAX(pParse->nMem, regPrev+nCol);
- /* Always jump here after updating the iMem+1...iMem+1+nCol counters */
- sqlite3VdbeResolveLabel(v, endOfLoop);
+ /* Open a read-only cursor on the index being analyzed. */
+ assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) );
+ sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb);
+ sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF);
+ VdbeComment((v, "%s", pIdx->zName));
- sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, topOfLoop);
- sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
-#ifdef SQLITE_ENABLE_STAT3
- sqlite3VdbeAddOp4(v, OP_Function, 1, regNumEq, regTemp2,
- (char*)&stat3PushFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 5);
- sqlite3VdbeAddOp2(v, OP_Integer, -1, regLoop);
- shortJump =
- sqlite3VdbeAddOp2(v, OP_AddImm, regLoop, 1);
- sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regTemp1,
- (char*)&stat3GetFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 2);
- sqlite3VdbeAddOp1(v, OP_IsNull, regTemp1);
- sqlite3VdbeAddOp3(v, OP_NotExists, iTabCur, shortJump, regTemp1);
- sqlite3VdbeAddOp3(v, OP_Column, iTabCur, pIdx->aiColumn[0], regSample);
- sqlite3ColumnDefault(v, pTab, pIdx->aiColumn[0], regSample);
- sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumEq,
- (char*)&stat3GetFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 3);
- sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumLt,
- (char*)&stat3GetFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 4);
- sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumDLt,
- (char*)&stat3GetFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 5);
- sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 6, regRec, "bbbbbb", 0);
- sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid);
- sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regRec, regNewRowid);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, shortJump);
- sqlite3VdbeJumpHere(v, shortJump+2);
-#endif
-
- /* Store the results in sqlite_stat1.
+ /* Invoke the stat_init() function. The arguments are:
+ **
+ ** (1) the number of columns in the index including the rowid,
+ ** (2) the number of rows in the index,
**
- ** The result is a single row of the sqlite_stat1 table. The first
- ** two columns are the names of the table and index. The third column
- ** is a string composed of a list of integer statistics about the
- ** index. The first integer in the list is the total number of entries
- ** in the index. There is one additional integer in the list for each
- ** column of the table. This additional integer is a guess of how many
- ** rows of the table the index will select. If D is the count of distinct
- ** values and K is the total number of rows, then the integer is computed
- ** as:
+ ** The second argument is only used for STAT3 and STAT4
+ */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat4+2);
+#endif
+ sqlite3VdbeAddOp2(v, OP_Integer, nCol+1, regStat4+1);
+ sqlite3VdbeAddOp3(v, OP_Function, 0, regStat4+1, regStat4);
+ sqlite3VdbeChangeP4(v, -1, (char*)&statInitFuncdef, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, 1+IsStat34);
+
+ /* Implementation of the following:
**
- ** I = (K+D-1)/D
+ ** Rewind csr
+ ** if eof(csr) goto end_of_scan;
+ ** regChng = 0
+ ** goto next_push_0;
**
- ** If K==0 then no entry is made into the sqlite_stat1 table.
- ** If K>0 then it is always the case the D>0 so division by zero
- ** is never possible.
*/
- sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regStat1);
- if( jZeroRows<0 ){
- jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, iMem);
+ addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng);
+ addrGotoChng0 = sqlite3VdbeAddOp0(v, OP_Goto);
+
+ /*
+ ** next_row:
+ ** regChng = 0
+ ** if( idx(0) != regPrev(0) ) goto chng_addr_0
+ ** regChng = 1
+ ** if( idx(1) != regPrev(1) ) goto chng_addr_1
+ ** ...
+ ** regChng = N
+ ** goto chng_addr_N
+ */
+ addrNextRow = sqlite3VdbeCurrentAddr(v);
+ for(i=0; i<nCol; i++){
+ char *pColl = (char*)sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
+ sqlite3VdbeAddOp2(v, OP_Integer, i, regChng);
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regTemp);
+ aGotoChng[i] =
+ sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0, regPrev+i, pColl, P4_COLLSEQ);
+ sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
}
+ sqlite3VdbeAddOp2(v, OP_Integer, nCol, regChng);
+ aGotoChng[nCol] = sqlite3VdbeAddOp0(v, OP_Goto);
+
+ /*
+ ** chng_addr_0:
+ ** regPrev(0) = idx(0)
+ ** chng_addr_1:
+ ** regPrev(1) = idx(1)
+ ** ...
+ */
+ sqlite3VdbeJumpHere(v, addrGotoChng0);
for(i=0; i<nCol; i++){
- sqlite3VdbeAddOp4(v, OP_String8, 0, regTemp, 0, " ", 0);
- sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regStat1, regStat1);
- sqlite3VdbeAddOp3(v, OP_Add, iMem, iMem+i+1, regTemp);
- sqlite3VdbeAddOp2(v, OP_AddImm, regTemp, -1);
- sqlite3VdbeAddOp3(v, OP_Divide, iMem+i+1, regTemp, regTemp);
- sqlite3VdbeAddOp1(v, OP_ToInt, regTemp);
- sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regStat1, regStat1);
- }
- sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0);
+ sqlite3VdbeJumpHere(v, aGotoChng[i]);
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regPrev+i);
+ }
+
+ /*
+ ** chng_addr_N:
+ ** regRowid = idx(rowid) // STAT34 only
+ ** stat_push(P, regChng, regRowid) // 3rd parameter STAT34 only
+ ** Next csr
+ ** if !eof(csr) goto next_row;
+ */
+ sqlite3VdbeJumpHere(v, aGotoChng[nCol]);
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
+ assert( regRowid==(regStat4+2) );
+#endif
+ assert( regChng==(regStat4+1) );
+ sqlite3VdbeAddOp3(v, OP_Function, 1, regStat4, regTemp);
+ sqlite3VdbeChangeP4(v, -1, (char*)&statPushFuncdef, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, 2+IsStat34);
+ sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow);
+
+ /* Add the entry to the stat1 table. */
+ callStatGet(v, regStat4, STAT_GET_STAT1, regStat1);
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "aaa", 0);
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
- sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regNewRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
+
+ /* Add the entries to the stat3 or stat4 table. */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ {
+ int regEq = regStat1;
+ int regLt = regStat1+1;
+ int regDLt = regStat1+2;
+ int regSample = regStat1+3;
+ int regCol = regStat1+4;
+ int regSampleRowid = regCol + nCol;
+ int addrNext;
+ int addrIsNull;
+
+ pParse->nMem = MAX(pParse->nMem, regCol+nCol+1);
+
+ addrNext = sqlite3VdbeCurrentAddr(v);
+ callStatGet(v, regStat4, STAT_GET_ROWID, regSampleRowid);
+ addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regSampleRowid);
+ callStatGet(v, regStat4, STAT_GET_NEQ, regEq);
+ callStatGet(v, regStat4, STAT_GET_NLT, regLt);
+ callStatGet(v, regStat4, STAT_GET_NDLT, regDLt);
+ sqlite3VdbeAddOp3(v, OP_NotExists, iTabCur, addrNext, regSampleRowid);
+#ifdef SQLITE_ENABLE_STAT3
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur,
+ pIdx->aiColumn[0], regSample);
+#else
+ for(i=0; i<nCol; i++){
+ int iCol = pIdx->aiColumn[i];
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, iCol, regCol+i);
+ }
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regCol, nCol+1, regSample);
+#endif
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 6, regTemp, "bbbbbb", 0);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regTemp, regNewRowid);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNext);
+ sqlite3VdbeJumpHere(v, addrIsNull);
+ }
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+
+ /* End of analysis */
+ sqlite3VdbeJumpHere(v, addrRewind);
+ sqlite3DbFree(db, aGotoChng);
}
- /* If the table has no indices, create a single sqlite_stat1 entry
- ** containing NULL as the index name and the row count as the content.
+
+ /* Create a single sqlite_stat1 entry containing NULL as the index
+ ** name and the row count as the content.
*/
- if( pTab->pIndex==0 ){
- sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pTab->tnum, iDb);
+ if( pOnlyIdx==0 && needTableCnt ){
VdbeComment((v, "%s", pTab->zName));
- sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat1);
- sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
+ sqlite3VdbeAddOp2(v, OP_Count, iTabCur, regStat1);
jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1);
- }else{
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname);
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "aaa", 0);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid);
+ sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
sqlite3VdbeJumpHere(v, jZeroRows);
- jZeroRows = sqlite3VdbeAddOp0(v, OP_Goto);
}
- sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname);
- sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0);
- sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
- sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regNewRowid);
- sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
- if( pParse->nMem<regRec ) pParse->nMem = regRec;
- sqlite3VdbeJumpHere(v, jZeroRows);
}
@@ -80967,16 +82425,18 @@ static void analyzeDatabase(Parse *pParse, int iDb){
HashElem *k;
int iStatCur;
int iMem;
+ int iTab;
sqlite3BeginWriteOperation(pParse, 0, iDb);
iStatCur = pParse->nTab;
pParse->nTab += 3;
openStatTable(pParse, iDb, iStatCur, 0, 0);
iMem = pParse->nMem+1;
+ iTab = pParse->nTab;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
Table *pTab = (Table*)sqliteHashData(k);
- analyzeOneTable(pParse, pTab, 0, iStatCur, iMem);
+ analyzeOneTable(pParse, pTab, 0, iStatCur, iMem, iTab);
}
loadAnalysis(pParse, iDb);
}
@@ -81001,7 +82461,7 @@ static void analyzeTable(Parse *pParse, Table *pTab, Index *pOnlyIdx){
}else{
openStatTable(pParse, iDb, iStatCur, pTab->zName, "tbl");
}
- analyzeOneTable(pParse, pTab, pOnlyIdx, iStatCur, pParse->nMem+1);
+ analyzeOneTable(pParse, pTab, pOnlyIdx, iStatCur,pParse->nMem+1,pParse->nTab);
loadAnalysis(pParse, iDb);
}
@@ -81085,6 +82545,52 @@ struct analysisInfo {
};
/*
+** The first argument points to a nul-terminated string containing a
+** list of space separated integers. Read the first nOut of these into
+** the array aOut[].
+*/
+static void decodeIntArray(
+ char *zIntArray, /* String containing int array to decode */
+ int nOut, /* Number of slots in aOut[] */
+ tRowcnt *aOut, /* Store integers here */
+ Index *pIndex /* Handle extra flags for this index, if not NULL */
+){
+ char *z = zIntArray;
+ int c;
+ int i;
+ tRowcnt v;
+
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ if( z==0 ) z = "";
+#else
+ if( NEVER(z==0) ) z = "";
+#endif
+ for(i=0; *z && i<nOut; i++){
+ v = 0;
+ while( (c=z[0])>='0' && c<='9' ){
+ v = v*10 + c - '0';
+ z++;
+ }
+ aOut[i] = v;
+ if( *z==' ' ) z++;
+ }
+#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
+ assert( pIndex!=0 );
+#else
+ if( pIndex )
+#endif
+ {
+ if( strcmp(z, "unordered")==0 ){
+ pIndex->bUnordered = 1;
+ }else if( sqlite3_strglob("sz=[0-9]*", z)==0 ){
+ int v32 = 0;
+ sqlite3GetInt32(z+3, &v32);
+ pIndex->szIdxRow = sqlite3LogEst(v32);
+ }
+ }
+}
+
+/*
** This callback is invoked once for each index when reading the
** sqlite_stat1 table.
**
@@ -81099,8 +82605,6 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
analysisInfo *pInfo = (analysisInfo*)pData;
Index *pIndex;
Table *pTable;
- int i, c, n;
- tRowcnt v;
const char *z;
assert( argc==3 );
@@ -81118,23 +82622,18 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
}else{
pIndex = 0;
}
- n = pIndex ? pIndex->nColumn : 0;
z = argv[2];
- for(i=0; *z && i<=n; i++){
- v = 0;
- while( (c=z[0])>='0' && c<='9' ){
- v = v*10 + c - '0';
- z++;
- }
- if( i==0 ) pTable->nRowEst = v;
- if( pIndex==0 ) break;
- pIndex->aiRowEst[i] = v;
- if( *z==' ' ) z++;
- if( strcmp(z, "unordered")==0 ){
- pIndex->bUnordered = 1;
- break;
- }
+
+ if( pIndex ){
+ decodeIntArray((char*)z, pIndex->nColumn+1, pIndex->aiRowEst, pIndex);
+ if( pIndex->pPartIdxWhere==0 ) pTable->nRowEst = pIndex->aiRowEst[0];
+ }else{
+ Index fakeIdx;
+ fakeIdx.szIdxRow = pTable->szTabRow;
+ decodeIntArray((char*)z, 1, &pTable->nRowEst, &fakeIdx);
+ pTable->szTabRow = fakeIdx.szIdxRow;
}
+
return 0;
}
@@ -81143,14 +82642,12 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
** and its contents.
*/
SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
-#ifdef SQLITE_ENABLE_STAT3
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
if( pIdx->aSample ){
int j;
for(j=0; j<pIdx->nSample; j++){
IndexSample *p = &pIdx->aSample[j];
- if( p->eType==SQLITE_TEXT || p->eType==SQLITE_BLOB ){
- sqlite3DbFree(db, p->u.z);
- }
+ sqlite3DbFree(db, p->p);
}
sqlite3DbFree(db, pIdx->aSample);
}
@@ -81161,31 +82658,75 @@ SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
#else
UNUSED_PARAMETER(db);
UNUSED_PARAMETER(pIdx);
-#endif
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
}
-#ifdef SQLITE_ENABLE_STAT3
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
/*
-** Load content from the sqlite_stat3 table into the Index.aSample[]
-** arrays of all indices.
+** Populate the pIdx->aAvgEq[] array based on the samples currently
+** stored in pIdx->aSample[].
*/
-static int loadStat3(sqlite3 *db, const char *zDb){
+static void initAvgEq(Index *pIdx){
+ if( pIdx ){
+ IndexSample *aSample = pIdx->aSample;
+ IndexSample *pFinal = &aSample[pIdx->nSample-1];
+ int iCol;
+ for(iCol=0; iCol<pIdx->nColumn; iCol++){
+ int i; /* Used to iterate through samples */
+ tRowcnt sumEq = 0; /* Sum of the nEq values */
+ tRowcnt nSum = 0; /* Number of terms contributing to sumEq */
+ tRowcnt avgEq = 0;
+ tRowcnt nDLt = pFinal->anDLt[iCol];
+
+ /* Set nSum to the number of distinct (iCol+1) field prefixes that
+ ** occur in the stat4 table for this index before pFinal. Set
+ ** sumEq to the sum of the nEq values for column iCol for the same
+ ** set (adding the value only once where there exist dupicate
+ ** prefixes). */
+ for(i=0; i<(pIdx->nSample-1); i++){
+ if( aSample[i].anDLt[iCol]!=aSample[i+1].anDLt[iCol] ){
+ sumEq += aSample[i].anEq[iCol];
+ nSum++;
+ }
+ }
+ if( nDLt>nSum ){
+ avgEq = (pFinal->anLt[iCol] - sumEq)/(nDLt - nSum);
+ }
+ if( avgEq==0 ) avgEq = 1;
+ pIdx->aAvgEq[iCol] = avgEq;
+ if( pIdx->nSampleCol==1 ) break;
+ }
+ }
+}
+
+/*
+** Load the content from either the sqlite_stat4 or sqlite_stat3 table
+** into the relevant Index.aSample[] arrays.
+**
+** Arguments zSql1 and zSql2 must point to SQL statements that return
+** data equivalent to the following (statements are different for stat3,
+** see the caller of this function for details):
+**
+** zSql1: SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx
+** zSql2: SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4
+**
+** where %Q is replaced with the database name before the SQL is executed.
+*/
+static int loadStatTbl(
+ sqlite3 *db, /* Database handle */
+ int bStat3, /* Assume single column records only */
+ const char *zSql1, /* SQL statement 1 (see above) */
+ const char *zSql2, /* SQL statement 2 (see above) */
+ const char *zDb /* Database name (e.g. "main") */
+){
int rc; /* Result codes from subroutines */
sqlite3_stmt *pStmt = 0; /* An SQL statement being run */
char *zSql; /* Text of the SQL statement */
Index *pPrevIdx = 0; /* Previous index in the loop */
- int idx = 0; /* slot in pIdx->aSample[] for next sample */
- int eType; /* Datatype of a sample */
IndexSample *pSample; /* A slot in pIdx->aSample[] */
assert( db->lookaside.bEnabled==0 );
- if( !sqlite3FindTable(db, "sqlite_stat3", zDb) ){
- return SQLITE_OK;
- }
-
- zSql = sqlite3MPrintf(db,
- "SELECT idx,count(*) FROM %Q.sqlite_stat3"
- " GROUP BY idx", zDb);
+ zSql = sqlite3MPrintf(db, zSql1, zDb);
if( !zSql ){
return SQLITE_NOMEM;
}
@@ -81194,30 +82735,51 @@ static int loadStat3(sqlite3 *db, const char *zDb){
if( rc ) return rc;
while( sqlite3_step(pStmt)==SQLITE_ROW ){
+ int nIdxCol = 1; /* Number of columns in stat4 records */
+ int nAvgCol = 1; /* Number of entries in Index.aAvgEq */
+
char *zIndex; /* Index name */
Index *pIdx; /* Pointer to the index object */
int nSample; /* Number of samples */
+ int nByte; /* Bytes of space required */
+ int i; /* Bytes of space required */
+ tRowcnt *pSpace;
zIndex = (char *)sqlite3_column_text(pStmt, 0);
if( zIndex==0 ) continue;
nSample = sqlite3_column_int(pStmt, 1);
pIdx = sqlite3FindIndex(db, zIndex, zDb);
- if( pIdx==0 ) continue;
- assert( pIdx->nSample==0 );
- pIdx->nSample = nSample;
- pIdx->aSample = sqlite3DbMallocZero(db, nSample*sizeof(IndexSample));
- pIdx->avgEq = pIdx->aiRowEst[1];
+ assert( pIdx==0 || bStat3 || pIdx->nSample==0 );
+ /* Index.nSample is non-zero at this point if data has already been
+ ** loaded from the stat4 table. In this case ignore stat3 data. */
+ if( pIdx==0 || pIdx->nSample ) continue;
+ if( bStat3==0 ){
+ nIdxCol = pIdx->nColumn+1;
+ nAvgCol = pIdx->nColumn;
+ }
+ pIdx->nSampleCol = nIdxCol;
+ nByte = sizeof(IndexSample) * nSample;
+ nByte += sizeof(tRowcnt) * nIdxCol * 3 * nSample;
+ nByte += nAvgCol * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */
+
+ pIdx->aSample = sqlite3DbMallocZero(db, nByte);
if( pIdx->aSample==0 ){
- db->mallocFailed = 1;
sqlite3_finalize(pStmt);
return SQLITE_NOMEM;
}
+ pSpace = (tRowcnt*)&pIdx->aSample[nSample];
+ pIdx->aAvgEq = pSpace; pSpace += nAvgCol;
+ for(i=0; i<nSample; i++){
+ pIdx->aSample[i].anEq = pSpace; pSpace += nIdxCol;
+ pIdx->aSample[i].anLt = pSpace; pSpace += nIdxCol;
+ pIdx->aSample[i].anDLt = pSpace; pSpace += nIdxCol;
+ }
+ assert( ((u8*)pSpace)-nByte==(u8*)(pIdx->aSample) );
}
rc = sqlite3_finalize(pStmt);
if( rc ) return rc;
- zSql = sqlite3MPrintf(db,
- "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat3", zDb);
+ zSql = sqlite3MPrintf(db, zSql2, zDb);
if( !zSql ){
return SQLITE_NOMEM;
}
@@ -81226,86 +82788,88 @@ static int loadStat3(sqlite3 *db, const char *zDb){
if( rc ) return rc;
while( sqlite3_step(pStmt)==SQLITE_ROW ){
- char *zIndex; /* Index name */
- Index *pIdx; /* Pointer to the index object */
- int i; /* Loop counter */
- tRowcnt sumEq; /* Sum of the nEq values */
+ char *zIndex; /* Index name */
+ Index *pIdx; /* Pointer to the index object */
+ int nCol = 1; /* Number of columns in index */
zIndex = (char *)sqlite3_column_text(pStmt, 0);
if( zIndex==0 ) continue;
pIdx = sqlite3FindIndex(db, zIndex, zDb);
if( pIdx==0 ) continue;
- if( pIdx==pPrevIdx ){
- idx++;
- }else{
+ /* This next condition is true if data has already been loaded from
+ ** the sqlite_stat4 table. In this case ignore stat3 data. */
+ nCol = pIdx->nSampleCol;
+ if( bStat3 && nCol>1 ) continue;
+ if( pIdx!=pPrevIdx ){
+ initAvgEq(pPrevIdx);
pPrevIdx = pIdx;
- idx = 0;
- }
- assert( idx<pIdx->nSample );
- pSample = &pIdx->aSample[idx];
- pSample->nEq = (tRowcnt)sqlite3_column_int64(pStmt, 1);
- pSample->nLt = (tRowcnt)sqlite3_column_int64(pStmt, 2);
- pSample->nDLt = (tRowcnt)sqlite3_column_int64(pStmt, 3);
- if( idx==pIdx->nSample-1 ){
- if( pSample->nDLt>0 ){
- for(i=0, sumEq=0; i<=idx-1; i++) sumEq += pIdx->aSample[i].nEq;
- pIdx->avgEq = (pSample->nLt - sumEq)/pSample->nDLt;
- }
- if( pIdx->avgEq<=0 ) pIdx->avgEq = 1;
- }
- eType = sqlite3_column_type(pStmt, 4);
- pSample->eType = (u8)eType;
- switch( eType ){
- case SQLITE_INTEGER: {
- pSample->u.i = sqlite3_column_int64(pStmt, 4);
- break;
- }
- case SQLITE_FLOAT: {
- pSample->u.r = sqlite3_column_double(pStmt, 4);
- break;
- }
- case SQLITE_NULL: {
- break;
- }
- default: assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); {
- const char *z = (const char *)(
- (eType==SQLITE_BLOB) ?
- sqlite3_column_blob(pStmt, 4):
- sqlite3_column_text(pStmt, 4)
- );
- int n = z ? sqlite3_column_bytes(pStmt, 4) : 0;
- pSample->nByte = n;
- if( n < 1){
- pSample->u.z = 0;
- }else{
- pSample->u.z = sqlite3DbMallocRaw(db, n);
- if( pSample->u.z==0 ){
- db->mallocFailed = 1;
- sqlite3_finalize(pStmt);
- return SQLITE_NOMEM;
- }
- memcpy(pSample->u.z, z, n);
- }
- }
}
+ pSample = &pIdx->aSample[pIdx->nSample];
+ decodeIntArray((char*)sqlite3_column_text(pStmt,1), nCol, pSample->anEq, 0);
+ decodeIntArray((char*)sqlite3_column_text(pStmt,2), nCol, pSample->anLt, 0);
+ decodeIntArray((char*)sqlite3_column_text(pStmt,3), nCol, pSample->anDLt,0);
+
+ /* Take a copy of the sample. Add two 0x00 bytes the end of the buffer.
+ ** This is in case the sample record is corrupted. In that case, the
+ ** sqlite3VdbeRecordCompare() may read up to two varints past the
+ ** end of the allocated buffer before it realizes it is dealing with
+ ** a corrupt record. Adding the two 0x00 bytes prevents this from causing
+ ** a buffer overread. */
+ pSample->n = sqlite3_column_bytes(pStmt, 4);
+ pSample->p = sqlite3DbMallocZero(db, pSample->n + 2);
+ if( pSample->p==0 ){
+ sqlite3_finalize(pStmt);
+ return SQLITE_NOMEM;
+ }
+ memcpy(pSample->p, sqlite3_column_blob(pStmt, 4), pSample->n);
+ pIdx->nSample++;
}
- return sqlite3_finalize(pStmt);
+ rc = sqlite3_finalize(pStmt);
+ if( rc==SQLITE_OK ) initAvgEq(pPrevIdx);
+ return rc;
}
-#endif /* SQLITE_ENABLE_STAT3 */
/*
-** Load the content of the sqlite_stat1 and sqlite_stat3 tables. The
+** Load content from the sqlite_stat4 and sqlite_stat3 tables into
+** the Index.aSample[] arrays of all indices.
+*/
+static int loadStat4(sqlite3 *db, const char *zDb){
+ int rc = SQLITE_OK; /* Result codes from subroutines */
+
+ assert( db->lookaside.bEnabled==0 );
+ if( sqlite3FindTable(db, "sqlite_stat4", zDb) ){
+ rc = loadStatTbl(db, 0,
+ "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx",
+ "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4",
+ zDb
+ );
+ }
+
+ if( rc==SQLITE_OK && sqlite3FindTable(db, "sqlite_stat3", zDb) ){
+ rc = loadStatTbl(db, 1,
+ "SELECT idx,count(*) FROM %Q.sqlite_stat3 GROUP BY idx",
+ "SELECT idx,neq,nlt,ndlt,sqlite_record(sample) FROM %Q.sqlite_stat3",
+ zDb
+ );
+ }
+
+ return rc;
+}
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+
+/*
+** Load the content of the sqlite_stat1 and sqlite_stat3/4 tables. The
** contents of sqlite_stat1 are used to populate the Index.aiRowEst[]
-** arrays. The contents of sqlite_stat3 are used to populate the
+** arrays. The contents of sqlite_stat3/4 are used to populate the
** Index.aSample[] arrays.
**
** If the sqlite_stat1 table is not present in the database, SQLITE_ERROR
-** is returned. In this case, even if SQLITE_ENABLE_STAT3 was defined
-** during compilation and the sqlite_stat3 table is present, no data is
+** is returned. In this case, even if SQLITE_ENABLE_STAT3/4 was defined
+** during compilation and the sqlite_stat3/4 table is present, no data is
** read from it.
**
-** If SQLITE_ENABLE_STAT3 was defined during compilation and the
-** sqlite_stat3 table is not present in the database, SQLITE_ERROR is
+** If SQLITE_ENABLE_STAT3/4 was defined during compilation and the
+** sqlite_stat4 table is not present in the database, SQLITE_ERROR is
** returned. However, in this case, data is read from the sqlite_stat1
** table (if it is present) before returning.
**
@@ -81327,7 +82891,7 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){
Index *pIdx = sqliteHashData(i);
sqlite3DefaultRowEst(pIdx);
-#ifdef SQLITE_ENABLE_STAT3
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
sqlite3DeleteIndexSamples(db, pIdx);
pIdx->aSample = 0;
#endif
@@ -81351,12 +82915,12 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
}
- /* Load the statistics from the sqlite_stat3 table. */
-#ifdef SQLITE_ENABLE_STAT3
+ /* Load the statistics from the sqlite_stat4 table. */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
if( rc==SQLITE_OK ){
int lookasideEnabled = db->lookaside.bEnabled;
db->lookaside.bEnabled = 0;
- rc = loadStat3(db, sInfo.zDatabase);
+ rc = loadStat4(db, sInfo.zDatabase);
db->lookaside.bEnabled = lookasideEnabled;
}
#endif
@@ -81531,6 +83095,9 @@ static void attachFunc(
sqlite3PagerLockingMode(pPager, db->dfltLockMode);
sqlite3BtreeSecureDelete(aNew->pBt,
sqlite3BtreeSecureDelete(db->aDb[0].pBt,-1) );
+#ifndef SQLITE_OMIT_PAGER_PRAGMAS
+ sqlite3BtreeSetPagerFlags(aNew->pBt, 3 | (db->flags & PAGER_FLAGS_MASK));
+#endif
}
aNew->safety_level = 3;
aNew->zName = sqlite3DbStrDup(db, zName);
@@ -81749,8 +83316,7 @@ attach_end:
SQLITE_PRIVATE void sqlite3Detach(Parse *pParse, Expr *pDbname){
static const FuncDef detach_func = {
1, /* nArg */
- SQLITE_UTF8, /* iPrefEnc */
- 0, /* flags */
+ SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
detachFunc, /* xFunc */
@@ -81771,8 +83337,7 @@ SQLITE_PRIVATE void sqlite3Detach(Parse *pParse, Expr *pDbname){
SQLITE_PRIVATE void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){
static const FuncDef attach_func = {
3, /* nArg */
- SQLITE_UTF8, /* iPrefEnc */
- 0, /* flags */
+ SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
attachFunc, /* xFunc */
@@ -81789,11 +83354,8 @@ SQLITE_PRIVATE void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *p
/*
** Initialize a DbFixer structure. This routine must be called prior
** to passing the structure to one of the sqliteFixAAAA() routines below.
-**
-** The return value indicates whether or not fixation is required. TRUE
-** means we do need to fix the database references, FALSE means we do not.
*/
-SQLITE_PRIVATE int sqlite3FixInit(
+SQLITE_PRIVATE void sqlite3FixInit(
DbFixer *pFix, /* The fixer to be initialized */
Parse *pParse, /* Error messages will be written here */
int iDb, /* This is the database that must be used */
@@ -81802,7 +83364,6 @@ SQLITE_PRIVATE int sqlite3FixInit(
){
sqlite3 *db;
- if( NEVER(iDb<0) || iDb==1 ) return 0;
db = pParse->db;
assert( db->nDb>iDb );
pFix->pParse = pParse;
@@ -81810,7 +83371,7 @@ SQLITE_PRIVATE int sqlite3FixInit(
pFix->pSchema = db->aDb[iDb].pSchema;
pFix->zType = zType;
pFix->pName = pName;
- return 1;
+ pFix->bVarOnly = (iDb==1);
}
/*
@@ -81838,15 +83399,17 @@ SQLITE_PRIVATE int sqlite3FixSrcList(
if( NEVER(pList==0) ) return 0;
zDb = pFix->zDb;
for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
- if( pItem->zDatabase && sqlite3StrICmp(pItem->zDatabase, zDb) ){
- sqlite3ErrorMsg(pFix->pParse,
- "%s %T cannot reference objects in database %s",
- pFix->zType, pFix->pName, pItem->zDatabase);
- return 1;
+ if( pFix->bVarOnly==0 ){
+ if( pItem->zDatabase && sqlite3StrICmp(pItem->zDatabase, zDb) ){
+ sqlite3ErrorMsg(pFix->pParse,
+ "%s %T cannot reference objects in database %s",
+ pFix->zType, pFix->pName, pItem->zDatabase);
+ return 1;
+ }
+ sqlite3DbFree(pFix->pParse->db, pItem->zDatabase);
+ pItem->zDatabase = 0;
+ pItem->pSchema = pFix->pSchema;
}
- sqlite3DbFree(pFix->pParse->db, pItem->zDatabase);
- pItem->zDatabase = 0;
- pItem->pSchema = pFix->pSchema;
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1;
if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1;
@@ -81869,9 +83432,21 @@ SQLITE_PRIVATE int sqlite3FixSelect(
if( sqlite3FixExpr(pFix, pSelect->pWhere) ){
return 1;
}
+ if( sqlite3FixExprList(pFix, pSelect->pGroupBy) ){
+ return 1;
+ }
if( sqlite3FixExpr(pFix, pSelect->pHaving) ){
return 1;
}
+ if( sqlite3FixExprList(pFix, pSelect->pOrderBy) ){
+ return 1;
+ }
+ if( sqlite3FixExpr(pFix, pSelect->pLimit) ){
+ return 1;
+ }
+ if( sqlite3FixExpr(pFix, pSelect->pOffset) ){
+ return 1;
+ }
pSelect = pSelect->pPrior;
}
return 0;
@@ -81881,7 +83456,15 @@ SQLITE_PRIVATE int sqlite3FixExpr(
Expr *pExpr /* The expression to be fixed to one database */
){
while( pExpr ){
- if( ExprHasAnyProperty(pExpr, EP_TokenOnly) ) break;
+ if( pExpr->op==TK_VARIABLE ){
+ if( pFix->pParse->db->init.busy ){
+ pExpr->op = TK_NULL;
+ }else{
+ sqlite3ErrorMsg(pFix->pParse, "%s cannot use variables", pFix->zType);
+ return 1;
+ }
+ }
+ if( ExprHasProperty(pExpr, EP_TokenOnly) ) break;
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
if( sqlite3FixSelect(pFix, pExpr->x.pSelect) ) return 1;
}else{
@@ -82567,6 +84150,7 @@ static void freeIndex(sqlite3 *db, Index *p){
#ifndef SQLITE_OMIT_ANALYZE
sqlite3DeleteIndexSamples(db, p);
#endif
+ sqlite3ExprDelete(db, p->pPartIdxWhere);
sqlite3DbFree(db, p->zColAff);
sqlite3DbFree(db, p);
}
@@ -83063,7 +84647,7 @@ SQLITE_PRIVATE void sqlite3StartTable(
pTable->iPKey = -1;
pTable->pSchema = db->aDb[iDb].pSchema;
pTable->nRef = 1;
- pTable->nRowEst = 1000000;
+ pTable->nRowEst = 1048576;
assert( pParse->pNewTable==0 );
pParse->pNewTable = pTable;
@@ -83210,6 +84794,7 @@ SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token *pName){
** be called next to set pCol->affinity correctly.
*/
pCol->affinity = SQLITE_AFF_NONE;
+ pCol->szEst = 1;
p->nCol++;
}
@@ -83251,15 +84836,18 @@ SQLITE_PRIVATE void sqlite3AddNotNull(Parse *pParse, int onError){
** If none of the substrings in the above table are found,
** SQLITE_AFF_NUMERIC is returned.
*/
-SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn){
+SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn, u8 *pszEst){
u32 h = 0;
char aff = SQLITE_AFF_NUMERIC;
+ const char *zChar = 0;
- if( zIn ) while( zIn[0] ){
+ if( zIn==0 ) return aff;
+ while( zIn[0] ){
h = (h<<8) + sqlite3UpperToLower[(*zIn)&0xff];
zIn++;
if( h==(('c'<<24)+('h'<<16)+('a'<<8)+'r') ){ /* CHAR */
- aff = SQLITE_AFF_TEXT;
+ aff = SQLITE_AFF_TEXT;
+ zChar = zIn;
}else if( h==(('c'<<24)+('l'<<16)+('o'<<8)+'b') ){ /* CLOB */
aff = SQLITE_AFF_TEXT;
}else if( h==(('t'<<24)+('e'<<16)+('x'<<8)+'t') ){ /* TEXT */
@@ -83267,6 +84855,7 @@ SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn){
}else if( h==(('b'<<24)+('l'<<16)+('o'<<8)+'b') /* BLOB */
&& (aff==SQLITE_AFF_NUMERIC || aff==SQLITE_AFF_REAL) ){
aff = SQLITE_AFF_NONE;
+ if( zIn[0]=='(' ) zChar = zIn;
#ifndef SQLITE_OMIT_FLOATING_POINT
}else if( h==(('r'<<24)+('e'<<16)+('a'<<8)+'l') /* REAL */
&& aff==SQLITE_AFF_NUMERIC ){
@@ -83284,6 +84873,28 @@ SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn){
}
}
+ /* If pszEst is not NULL, store an estimate of the field size. The
+ ** estimate is scaled so that the size of an integer is 1. */
+ if( pszEst ){
+ *pszEst = 1; /* default size is approx 4 bytes */
+ if( aff<=SQLITE_AFF_NONE ){
+ if( zChar ){
+ while( zChar[0] ){
+ if( sqlite3Isdigit(zChar[0]) ){
+ int v = 0;
+ sqlite3GetInt32(zChar, &v);
+ v = v/4 + 1;
+ if( v>255 ) v = 255;
+ *pszEst = v; /* BLOB(k), VARCHAR(k), CHAR(k) -> r=(k/4+1) */
+ break;
+ }
+ zChar++;
+ }
+ }else{
+ *pszEst = 5; /* BLOB, TEXT, CLOB -> r=5 (approx 20 bytes)*/
+ }
+ }
+ }
return aff;
}
@@ -83305,7 +84916,7 @@ SQLITE_PRIVATE void sqlite3AddColumnType(Parse *pParse, Token *pType){
pCol = &p->aCol[p->nCol-1];
assert( pCol->zType==0 );
pCol->zType = sqlite3NameFromToken(pParse->db, pType);
- pCol->affinity = sqlite3AffinityType(pCol->zType);
+ pCol->affinity = sqlite3AffinityType(pCol->zType, &pCol->szEst);
}
/*
@@ -83410,7 +85021,8 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey(
#endif
}else{
Index *p;
- p = sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0, 0, sortOrder, 0);
+ p = sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0,
+ 0, sortOrder, 0);
if( p ){
p->autoIndex = 2;
}
@@ -83461,6 +85073,7 @@ SQLITE_PRIVATE void sqlite3AddCollateType(Parse *pParse, Token *pToken){
if( sqlite3LocateCollSeq(pParse, zColl) ){
Index *pIdx;
+ sqlite3DbFree(db, p->aCol[i].zColl);
p->aCol[i].zColl = zColl;
/* If the column is declared as "<name> PRIMARY KEY COLLATE <type>",
@@ -83651,7 +85264,7 @@ static char *createTableStmt(sqlite3 *db, Table *p){
zType = azType[pCol->affinity - SQLITE_AFF_TEXT];
len = sqlite3Strlen30(zType);
assert( pCol->affinity==SQLITE_AFF_NONE
- || pCol->affinity==sqlite3AffinityType(zType) );
+ || pCol->affinity==sqlite3AffinityType(zType, 0) );
memcpy(&zStmt[k], zType, len);
k += len;
assert( k<=n );
@@ -83661,6 +85274,34 @@ static char *createTableStmt(sqlite3 *db, Table *p){
}
/*
+** Estimate the total row width for a table.
+*/
+static void estimateTableWidth(Table *pTab){
+ unsigned wTable = 0;
+ const Column *pTabCol;
+ int i;
+ for(i=pTab->nCol, pTabCol=pTab->aCol; i>0; i--, pTabCol++){
+ wTable += pTabCol->szEst;
+ }
+ if( pTab->iPKey<0 ) wTable++;
+ pTab->szTabRow = sqlite3LogEst(wTable*4);
+}
+
+/*
+** Estimate the average size of a row for an index.
+*/
+static void estimateIndexWidth(Index *pIdx){
+ unsigned wIndex = 1;
+ int i;
+ const Column *aCol = pIdx->pTable->aCol;
+ for(i=0; i<pIdx->nColumn; i++){
+ assert( pIdx->aiColumn[i]>=0 && pIdx->aiColumn[i]<pIdx->pTable->nCol );
+ wIndex += aCol[pIdx->aiColumn[i]].szEst;
+ }
+ pIdx->szIdxRow = sqlite3LogEst(wIndex*4);
+}
+
+/*
** This routine is called to report the final ")" that terminates
** a CREATE TABLE statement.
**
@@ -83686,9 +85327,10 @@ SQLITE_PRIVATE void sqlite3EndTable(
Token *pEnd, /* The final ')' token in the CREATE TABLE */
Select *pSelect /* Select from a "CREATE ... AS SELECT" */
){
- Table *p;
- sqlite3 *db = pParse->db;
- int iDb;
+ Table *p; /* The new table */
+ sqlite3 *db = pParse->db; /* The database connection */
+ int iDb; /* Database in which the table lives */
+ Index *pIdx; /* An implied index of the table */
if( (pEnd==0 && pSelect==0) || db->mallocFailed ){
return;
@@ -83704,29 +85346,16 @@ SQLITE_PRIVATE void sqlite3EndTable(
/* Resolve names in all CHECK constraint expressions.
*/
if( p->pCheck ){
- SrcList sSrc; /* Fake SrcList for pParse->pNewTable */
- NameContext sNC; /* Name context for pParse->pNewTable */
- ExprList *pList; /* List of all CHECK constraints */
- int i; /* Loop counter */
-
- memset(&sNC, 0, sizeof(sNC));
- memset(&sSrc, 0, sizeof(sSrc));
- sSrc.nSrc = 1;
- sSrc.a[0].zName = p->zName;
- sSrc.a[0].pTab = p;
- sSrc.a[0].iCursor = -1;
- sNC.pParse = pParse;
- sNC.pSrcList = &sSrc;
- sNC.ncFlags = NC_IsCheck;
- pList = p->pCheck;
- for(i=0; i<pList->nExpr; i++){
- if( sqlite3ResolveExprNames(&sNC, pList->a[i].pExpr) ){
- return;
- }
- }
+ sqlite3ResolveSelfReference(pParse, p, NC_IsCheck, 0, p->pCheck);
}
#endif /* !defined(SQLITE_OMIT_CHECK) */
+ /* Estimate the average row size for the table and for all implied indices */
+ estimateTableWidth(p);
+ for(pIdx=p->pIndex; pIdx; pIdx=pIdx->pNext){
+ estimateIndexWidth(pIdx);
+ }
+
/* 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
@@ -83923,9 +85552,8 @@ SQLITE_PRIVATE void sqlite3CreateView(
}
sqlite3TwoPartName(pParse, pName1, pName2, &pName);
iDb = sqlite3SchemaToIndex(db, p->pSchema);
- if( sqlite3FixInit(&sFix, pParse, iDb, "view", pName)
- && sqlite3FixSelect(&sFix, pSelect)
- ){
+ sqlite3FixInit(&sFix, pParse, iDb, "view", pName);
+ if( sqlite3FixSelect(&sFix, pSelect) ){
sqlite3SelectDelete(db, pSelect);
return;
}
@@ -84225,7 +85853,7 @@ static void sqlite3ClearStatTables(
){
int i;
const char *zDbName = pParse->db->aDb[iDb].zName;
- for(i=1; i<=3; i++){
+ for(i=1; i<=4; i++){
char zTab[24];
sqlite3_snprintf(sizeof(zTab),zTab,"sqlite_stat%d",i);
if( sqlite3FindTable(pParse->db, zTab, zDbName) ){
@@ -84575,6 +86203,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
int addr1; /* Address of top of loop */
int addr2; /* Address to jump to for next iteration */
int tnum; /* Root page of index */
+ int iPartIdxLabel; /* Jump to this label to skip a row */
Vdbe *v; /* Generate code into this virtual machine */
KeyInfo *pKey; /* KeyInfo for index */
int regRecord; /* Register holding assemblied index record */
@@ -84614,8 +86243,9 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0);
regRecord = sqlite3GetTempReg(pParse);
- sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1);
+ sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1, &iPartIdxLabel);
sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord);
+ sqlite3VdbeResolveLabel(v, iPartIdxLabel);
sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1);
sqlite3VdbeJumpHere(v, addr1);
addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0);
@@ -84666,7 +86296,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
ExprList *pList, /* A list of columns to be indexed */
int onError, /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
Token *pStart, /* The CREATE token that begins this statement */
- Token *pEnd, /* The ")" that closes the CREATE INDEX statement */
+ Expr *pPIWhere, /* WHERE clause for partial indices */
int sortOrder, /* Sort order of primary key when pList==NULL */
int ifNotExist /* Omit error if index already exists */
){
@@ -84684,11 +86314,11 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
int iDb; /* Index of the database that is being written */
Token *pName = 0; /* Unqualified name of the index to create */
struct ExprList_item *pListItem; /* For looping over pList */
- int nCol;
- int nExtra = 0;
- char *zExtra;
+ const Column *pTabCol; /* A column in the table */
+ int nCol; /* Number of columns */
+ int nExtra = 0; /* Space allocated for zExtra[] */
+ char *zExtra; /* Extra space after the Index object */
- assert( pStart==0 || pEnd!=0 ); /* pEnd must be non-NULL if pStart is */
assert( pParse->nErr==0 ); /* Never called with prior errors */
if( db->mallocFailed || IN_DECLARE_VTAB ){
goto exit_create_index;
@@ -84724,9 +86354,8 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
}
#endif
- if( sqlite3FixInit(&sFix, pParse, iDb, "index", pName) &&
- sqlite3FixSrcList(&sFix, pTblName)
- ){
+ sqlite3FixInit(&sFix, pParse, iDb, "index", pName);
+ if( sqlite3FixSrcList(&sFix, pTblName) ){
/* Because the parser constructs pTblName from a single identifier,
** sqlite3FixSrcList can never fail. */
assert(0);
@@ -84734,7 +86363,12 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
pTab = sqlite3LocateTableItem(pParse, 0, &pTblName->a[0]);
assert( db->mallocFailed==0 || pTab==0 );
if( pTab==0 ) goto exit_create_index;
- assert( db->aDb[iDb].pSchema==pTab->pSchema );
+ if( iDb==1 && db->aDb[iDb].pSchema!=pTab->pSchema ){
+ sqlite3ErrorMsg(pParse,
+ "cannot create a TEMP index on non-TEMP table \"%s\"",
+ pTab->zName);
+ goto exit_create_index;
+ }
}else{
assert( pName==0 );
assert( pStart==0 );
@@ -84880,8 +86514,14 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
pIndex->pTable = pTab;
pIndex->nColumn = pList->nExpr;
pIndex->onError = (u8)onError;
+ pIndex->uniqNotNull = onError==OE_Abort;
pIndex->autoIndex = (u8)(pName==0);
pIndex->pSchema = db->aDb[iDb].pSchema;
+ if( pPIWhere ){
+ sqlite3ResolveSelfReference(pParse, pTab, NC_PartIdx, pPIWhere, 0);
+ pIndex->pPartIdxWhere = pPIWhere;
+ pPIWhere = 0;
+ }
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
/* Check to see if we should honor DESC requests on index columns
@@ -84904,7 +86544,6 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
*/
for(i=0, pListItem=pList->a; i<pList->nExpr; i++, pListItem++){
const char *zColName = pListItem->zName;
- Column *pTabCol;
int requestedSortOrder;
char *zColl; /* Collation sequence name */
@@ -84938,8 +86577,10 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
pIndex->azColl[i] = zColl;
requestedSortOrder = pListItem->sortOrder & sortOrderMask;
pIndex->aSortOrder[i] = (u8)requestedSortOrder;
+ if( pTab->aCol[j].notNull==0 ) pIndex->uniqNotNull = 0;
}
sqlite3DefaultRowEst(pIndex);
+ if( pParse->pNewTable==0 ) estimateIndexWidth(pIndex);
if( pTab==pParse->pNewTable ){
/* This routine has been called to create an automatic index as a
@@ -85036,7 +86677,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
** has just been created, it contains no data and the index initialization
** step can be skipped.
*/
- else{ /* if( db->init.busy==0 ) */
+ else if( pParse->nErr==0 ){
Vdbe *v;
char *zStmt;
int iMem = ++pParse->nMem;
@@ -85054,12 +86695,11 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
** the zStmt variable
*/
if( pStart ){
- assert( pEnd!=0 );
+ int n = (int)(pParse->sLastToken.z - pName->z) + pParse->sLastToken.n;
+ if( pName->z[n-1]==';' ) n--;
/* A named index with an explicit CREATE INDEX statement */
zStmt = sqlite3MPrintf(db, "CREATE%s INDEX %.*s",
- onError==OE_None ? "" : " UNIQUE",
- (int)(pEnd->z - pName->z) + 1,
- pName->z);
+ onError==OE_None ? "" : " UNIQUE", n, pName->z);
}else{
/* An automatic index created by a PRIMARY KEY or UNIQUE constraint */
/* zStmt = sqlite3MPrintf(""); */
@@ -85115,10 +86755,8 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
/* Clean up before exiting */
exit_create_index:
- if( pIndex ){
- sqlite3DbFree(db, pIndex->zColAff);
- sqlite3DbFree(db, pIndex);
- }
+ if( pIndex ) freeIndex(db, pIndex);
+ sqlite3ExprDelete(db, pPIWhere);
sqlite3ExprListDelete(db, pList);
sqlite3SrcListDelete(db, pTblName);
sqlite3DbFree(db, zName);
@@ -85369,7 +87007,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(
}
pSrc = pNew;
nGot = (sqlite3DbMallocSize(db, pNew) - sizeof(*pSrc))/sizeof(pSrc->a[0])+1;
- pSrc->nAlloc = (u16)nGot;
+ pSrc->nAlloc = (u8)nGot;
}
/* Move existing slots that come after the newly inserted slots
@@ -85377,7 +87015,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(
for(i=pSrc->nSrc-1; i>=iStart; i--){
pSrc->a[i+nExtra] = pSrc->a[i];
}
- pSrc->nSrc += (i16)nExtra;
+ pSrc->nSrc += (i8)nExtra;
/* Zero the newly allocated slots */
memset(&pSrc->a[iStart], 0, sizeof(pSrc->a[0])*nExtra);
@@ -85996,25 +87634,20 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
SQLITE_PRIVATE KeyInfo *sqlite3IndexKeyinfo(Parse *pParse, Index *pIdx){
int i;
int nCol = pIdx->nColumn;
- int nBytes = sizeof(KeyInfo) + (nCol-1)*sizeof(CollSeq*) + nCol;
- sqlite3 *db = pParse->db;
- KeyInfo *pKey = (KeyInfo *)sqlite3DbMallocZero(db, nBytes);
+ KeyInfo *pKey;
+ pKey = sqlite3KeyInfoAlloc(pParse->db, nCol);
if( pKey ){
- pKey->db = pParse->db;
- pKey->aSortOrder = (u8 *)&(pKey->aColl[nCol]);
- assert( &pKey->aSortOrder[nCol]==&(((u8 *)pKey)[nBytes]) );
for(i=0; i<nCol; i++){
char *zColl = pIdx->azColl[i];
assert( zColl );
pKey->aColl[i] = sqlite3LocateCollSeq(pParse, zColl);
pKey->aSortOrder[i] = pIdx->aSortOrder[i];
}
- pKey->nField = (u16)nCol;
}
if( pParse->nErr ){
- sqlite3DbFree(db, pKey);
+ sqlite3DbFree(pParse->db, pKey);
pKey = 0;
}
return pKey;
@@ -86293,9 +87926,9 @@ static int matchQuality(
}
/* Bonus points if the text encoding matches */
- if( enc==p->iPrefEnc ){
+ if( enc==(p->funcFlags & SQLITE_FUNC_ENCMASK) ){
match += 2; /* Exact encoding match */
- }else if( (enc & p->iPrefEnc & 2)!=0 ){
+ }else if( (enc & p->funcFlags & 2)!=0 ){
match += 1; /* Both are UTF16, but with different byte orders */
}
@@ -86429,7 +88062,7 @@ SQLITE_PRIVATE FuncDef *sqlite3FindFunction(
(pBest = sqlite3DbMallocZero(db, sizeof(*pBest)+nName+1))!=0 ){
pBest->zName = (char *)&pBest[1];
pBest->nArg = (u16)nArg;
- pBest->iPrefEnc = enc;
+ pBest->funcFlags = enc;
memcpy(pBest->zName, zName, nName);
pBest->zName[nName] = 0;
sqlite3FuncDefInsert(&db->aFunc, pBest);
@@ -86853,6 +88486,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
&& 0==sqlite3FkRequired(pParse, pTab, 0, 0)
){
assert( !isView );
+ sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName);
sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt,
pTab->zName, P4_STATIC);
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
@@ -87039,7 +88673,7 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete(
/* Do FK processing. This call checks that any FK constraints that
** refer to this table (i.e. constraints attached to other tables)
** are not violated by deleting this row. */
- sqlite3FkCheck(pParse, pTab, iOld, 0);
+ sqlite3FkCheck(pParse, pTab, iOld, 0, 0, 0);
}
/* Delete the index and table entries. Skip this step if pTab is really
@@ -87056,7 +88690,7 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete(
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
** handle rows (possibly in other tables) that refer via a foreign key
** to the row just deleted. */
- sqlite3FkActions(pParse, pTab, 0, iOld);
+ sqlite3FkActions(pParse, pTab, 0, iOld, 0, 0);
/* Invoke AFTER DELETE trigger programs. */
sqlite3CodeRowTrigger(pParse, pTrigger,
@@ -87094,11 +88728,14 @@ SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(
int i;
Index *pIdx;
int r1;
+ int iPartIdxLabel;
+ Vdbe *v = pParse->pVdbe;
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
if( aRegIdx!=0 && aRegIdx[i-1]==0 ) continue;
- r1 = sqlite3GenerateIndexKey(pParse, pIdx, iCur, 0, 0);
- sqlite3VdbeAddOp3(pParse->pVdbe, OP_IdxDelete, iCur+i, r1,pIdx->nColumn+1);
+ r1 = sqlite3GenerateIndexKey(pParse, pIdx, iCur, 0, 0, &iPartIdxLabel);
+ sqlite3VdbeAddOp3(v, OP_IdxDelete, iCur+i, r1, pIdx->nColumn+1);
+ sqlite3VdbeResolveLabel(v, iPartIdxLabel);
}
}
@@ -87112,13 +88749,21 @@ SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(
** registers that holds the elements of the index key. The
** block of registers has already been deallocated by the time
** this routine returns.
+**
+** If *piPartIdxLabel is not NULL, fill it in with a label and jump
+** to that label if pIdx is a partial index that should be skipped.
+** A partial index should be skipped if its WHERE clause evaluates
+** to false or null. If pIdx is not a partial index, *piPartIdxLabel
+** will be set to zero which is an empty label that is ignored by
+** sqlite3VdbeResolveLabel().
*/
SQLITE_PRIVATE int sqlite3GenerateIndexKey(
- Parse *pParse, /* Parsing context */
- Index *pIdx, /* The index for which to generate a key */
- int iCur, /* Cursor number for the pIdx->pTable table */
- int regOut, /* Write the new index key to this register */
- int doMakeRec /* Run the OP_MakeRecord instruction if true */
+ Parse *pParse, /* Parsing context */
+ Index *pIdx, /* The index for which to generate a key */
+ int iCur, /* Cursor number for the pIdx->pTable table */
+ int regOut, /* Write the new index key to this register */
+ int doMakeRec, /* Run the OP_MakeRecord instruction if true */
+ int *piPartIdxLabel /* OUT: Jump to this label to skip partial index */
){
Vdbe *v = pParse->pVdbe;
int j;
@@ -87126,6 +88771,16 @@ SQLITE_PRIVATE int sqlite3GenerateIndexKey(
int regBase;
int nCol;
+ if( piPartIdxLabel ){
+ if( pIdx->pPartIdxWhere ){
+ *piPartIdxLabel = sqlite3VdbeMakeLabel(v);
+ pParse->iPartIdxTab = iCur;
+ sqlite3ExprIfFalse(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel,
+ SQLITE_JUMPIFNULL);
+ }else{
+ *piPartIdxLabel = 0;
+ }
+ }
nCol = pIdx->nColumn;
regBase = sqlite3GetTempRange(pParse, nCol+1);
sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regBase+nCol);
@@ -87384,7 +89039,7 @@ static void instrFunc(
**
** If p1 is negative, then we begin abs(p1) from the end of x[].
**
-** If p2 is negative, return the p2 characters preceeding p1.
+** If p2 is negative, return the p2 characters preceding p1.
*/
static void substrFunc(
sqlite3_context *context,
@@ -87574,14 +89229,14 @@ static void lowerFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
}
/*
-** The COALESCE() and IFNULL() functions are implemented as VDBE code so
-** that unused argument values do not have to be computed. However, we
-** still need some kind of function implementation for this routines in
-** the function table. That function implementation will never be called
-** so it doesn't matter what the implementation is. We might as well use
-** the "version()" function as a substitute.
+** Some functions like COALESCE() and IFNULL() and UNLIKELY() are implemented
+** as VDBE code so that unused argument values do not have to be computed.
+** However, we still need some kind of function implementation for this
+** routines in the function table. The noopFunc macro provides this.
+** noopFunc will never be called so it doesn't matter what the implementation
+** is. We might as well use the "version()" function as a substitute.
*/
-#define ifnullFunc versionFunc /* Substitute function - never called */
+#define noopFunc versionFunc /* Substitute function - never called */
/*
** Implementation of random(). Return a random integer.
@@ -87700,9 +89355,9 @@ struct compareInfo {
*/
#if defined(SQLITE_EBCDIC)
# define sqlite3Utf8Read(A) (*((*A)++))
-# define GlogUpperToLower(A) A = sqlite3UpperToLower[A]
+# define GlobUpperToLower(A) A = sqlite3UpperToLower[A]
#else
-# define GlogUpperToLower(A) if( !((A)&~0x7f) ){ A = sqlite3UpperToLower[A]; }
+# define GlobUpperToLower(A) if( !((A)&~0x7f) ){ A = sqlite3UpperToLower[A]; }
#endif
static const struct compareInfo globInfo = { '*', '?', '[', 0 };
@@ -87781,11 +89436,11 @@ static int patternCompare(
}
while( (c2 = sqlite3Utf8Read(&zString))!=0 ){
if( noCase ){
- GlogUpperToLower(c2);
- GlogUpperToLower(c);
+ GlobUpperToLower(c2);
+ GlobUpperToLower(c);
while( c2 != 0 && c2 != c ){
c2 = sqlite3Utf8Read(&zString);
- GlogUpperToLower(c2);
+ GlobUpperToLower(c2);
}
}else{
while( c2 != 0 && c2 != c ){
@@ -87837,8 +89492,8 @@ static int patternCompare(
}else{
c2 = sqlite3Utf8Read(&zString);
if( noCase ){
- GlogUpperToLower(c);
- GlogUpperToLower(c2);
+ GlobUpperToLower(c);
+ GlobUpperToLower(c2);
}
if( c!=c2 ){
return 0;
@@ -88043,10 +89698,6 @@ static const char hexdigits[] = {
};
/*
-** EXPERIMENTAL - This is not an official function. The interface may
-** change. This function may disappear. Do not write code that depends
-** on this function.
-**
** Implementation of the QUOTE() function. This function takes a single
** argument. If the argument is numeric, the return value is the same as
** the argument. If the argument is NULL, the return value is the string
@@ -88235,7 +89886,7 @@ static void zeroblobFunc(
/*
** The replace() function. Three arguments are all strings: call
** them A, B, and C. The result is also a string which is derived
-** from A by replacing every occurance of B with C. The match
+** from A by replacing every occurrence of B with C. The match
** must be exact. Collating sequences are not used.
*/
static void replaceFunc(
@@ -88682,9 +90333,9 @@ static void groupConcatFinalize(sqlite3_context *context){
StrAccum *pAccum;
pAccum = sqlite3_aggregate_context(context, 0);
if( pAccum ){
- if( pAccum->tooBig ){
+ if( pAccum->accError==STRACCUM_TOOBIG ){
sqlite3_result_error_toobig(context);
- }else if( pAccum->mallocFailed ){
+ }else if( pAccum->accError==STRACCUM_NOMEM ){
sqlite3_result_error_nomem(context);
}else{
sqlite3_result_text(context, sqlite3StrAccumFinish(pAccum), -1,
@@ -88714,7 +90365,7 @@ static void setLikeOptFlag(sqlite3 *db, const char *zName, u8 flagVal){
pDef = sqlite3FindFunction(db, zName, sqlite3Strlen30(zName),
2, SQLITE_UTF8, 0);
if( ALWAYS(pDef) ){
- pDef->flags = flagVal;
+ pDef->funcFlags |= flagVal;
}
}
@@ -88758,7 +90409,7 @@ SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocas
pDef = sqlite3FindFunction(db, pExpr->u.zToken,
sqlite3Strlen30(pExpr->u.zToken),
2, SQLITE_UTF8, 0);
- if( NEVER(pDef==0) || (pDef->flags & SQLITE_FUNC_LIKE)==0 ){
+ if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_FUNC_LIKE)==0 ){
return 0;
}
@@ -88770,7 +90421,7 @@ SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocas
assert( (char*)&likeInfoAlt == (char*)&likeInfoAlt.matchAll );
assert( &((char*)&likeInfoAlt)[1] == (char*)&likeInfoAlt.matchOne );
assert( &((char*)&likeInfoAlt)[2] == (char*)&likeInfoAlt.matchSet );
- *pIsNocase = (pDef->flags & SQLITE_FUNC_CASE)==0;
+ *pIsNocase = (pDef->funcFlags & SQLITE_FUNC_CASE)==0;
return 1;
}
@@ -88819,9 +90470,11 @@ SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){
FUNCTION(lower, 1, 0, 0, lowerFunc ),
FUNCTION(coalesce, 1, 0, 0, 0 ),
FUNCTION(coalesce, 0, 0, 0, 0 ),
- FUNCTION2(coalesce, -1, 0, 0, ifnullFunc, SQLITE_FUNC_COALESCE),
+ FUNCTION2(coalesce, -1, 0, 0, noopFunc, SQLITE_FUNC_COALESCE),
FUNCTION(hex, 1, 0, 0, hexFunc ),
- FUNCTION2(ifnull, 2, 0, 0, ifnullFunc, SQLITE_FUNC_COALESCE),
+ FUNCTION2(ifnull, 2, 0, 0, noopFunc, SQLITE_FUNC_COALESCE),
+ FUNCTION2(unlikely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
+ FUNCTION2(likelihood, 2, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
FUNCTION(random, 0, 0, 0, randomFunc ),
FUNCTION(randomblob, 1, 0, 0, randomBlob ),
FUNCTION(nullif, 2, 0, 1, nullifFunc ),
@@ -88849,7 +90502,7 @@ SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){
AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ),
AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ),
/* AGGREGATE(count, 0, 0, 0, countStep, countFinalize ), */
- {0,SQLITE_UTF8,SQLITE_FUNC_COUNT,0,0,0,countStep,countFinalize,"count",0,0},
+ {0,SQLITE_UTF8|SQLITE_FUNC_COUNT,0,0,0,countStep,countFinalize,"count",0,0},
AGGREGATE(count, 1, 0, 0, countStep, countFinalize ),
AGGREGATE(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize),
AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize),
@@ -88875,6 +90528,9 @@ SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){
#ifndef SQLITE_OMIT_ALTERTABLE
sqlite3AlterFunctions();
#endif
+#if defined(SQLITE_ENABLE_STAT3) || defined(SQLITE_ENABLE_STAT4)
+ sqlite3AnalyzeFunctions();
+#endif
}
/************** End of func.c ************************************************/
@@ -89302,7 +90958,10 @@ static void fkLookupParent(
}
}
- if( !pFKey->isDeferred && !pParse->pToplevel && !pParse->isMultiWrite ){
+ if( !pFKey->isDeferred && !(pParse->db->flags & SQLITE_DeferFKs)
+ && !pParse->pToplevel
+ && !pParse->isMultiWrite
+ ){
/* Special case: If this is an INSERT statement that will insert exactly
** one row into the table, raise a constraint immediately instead of
** incrementing a counter. This is necessary as the VM code is being
@@ -89533,7 +91192,7 @@ SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTa
** when this statement is run. */
FKey *p;
for(p=pTab->pFKey; p; p=p->pNextFrom){
- if( p->isDeferred ) break;
+ if( p->isDeferred || (db->flags & SQLITE_DeferFKs) ) break;
}
if( !p ) return;
iSkip = sqlite3VdbeMakeLabel(v);
@@ -89547,11 +91206,18 @@ SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTa
/* If the DELETE has generated immediate foreign key constraint
** violations, halt the VDBE and return an error at this point, before
** any modifications to the schema are made. This is because statement
- ** transactions are not able to rollback schema changes. */
- sqlite3VdbeAddOp2(v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr(v)+2);
- sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY,
- OE_Abort, "foreign key constraint failed", P4_STATIC
- );
+ ** transactions are not able to rollback schema changes.
+ **
+ ** If the SQLITE_DeferFKs flag is set, then this is not required, as
+ ** the statement transaction will not be rolled back even if FK
+ ** constraints are violated.
+ */
+ if( (db->flags & SQLITE_DeferFKs)==0 ){
+ sqlite3VdbeAddOp2(v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr(v)+2);
+ sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY,
+ OE_Abort, "foreign key constraint failed", P4_STATIC
+ );
+ }
if( iSkip ){
sqlite3VdbeResolveLabel(v, iSkip);
@@ -89559,6 +91225,70 @@ SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTa
}
}
+
+/*
+** The second argument points to an FKey object representing a foreign key
+** for which pTab is the child table. An UPDATE statement against pTab
+** is currently being processed. For each column of the table that is
+** actually updated, the corresponding element in the aChange[] array
+** is zero or greater (if a column is unmodified the corresponding element
+** is set to -1). If the rowid column is modified by the UPDATE statement
+** the bChngRowid argument is non-zero.
+**
+** This function returns true if any of the columns that are part of the
+** child key for FK constraint *p are modified.
+*/
+static int fkChildIsModified(
+ Table *pTab, /* Table being updated */
+ FKey *p, /* Foreign key for which pTab is the child */
+ int *aChange, /* Array indicating modified columns */
+ int bChngRowid /* True if rowid is modified by this update */
+){
+ int i;
+ for(i=0; i<p->nCol; i++){
+ int iChildKey = p->aCol[i].iFrom;
+ if( aChange[iChildKey]>=0 ) return 1;
+ if( iChildKey==pTab->iPKey && bChngRowid ) return 1;
+ }
+ return 0;
+}
+
+/*
+** The second argument points to an FKey object representing a foreign key
+** for which pTab is the parent table. An UPDATE statement against pTab
+** is currently being processed. For each column of the table that is
+** actually updated, the corresponding element in the aChange[] array
+** is zero or greater (if a column is unmodified the corresponding element
+** is set to -1). If the rowid column is modified by the UPDATE statement
+** the bChngRowid argument is non-zero.
+**
+** This function returns true if any of the columns that are part of the
+** parent key for FK constraint *p are modified.
+*/
+static int fkParentIsModified(
+ Table *pTab,
+ FKey *p,
+ int *aChange,
+ int bChngRowid
+){
+ int i;
+ for(i=0; i<p->nCol; i++){
+ char *zKey = p->aCol[i].zCol;
+ int iKey;
+ for(iKey=0; iKey<pTab->nCol; iKey++){
+ if( aChange[iKey]>=0 || (iKey==pTab->iPKey && bChngRowid) ){
+ Column *pCol = &pTab->aCol[iKey];
+ if( zKey ){
+ if( 0==sqlite3StrICmp(pCol->zName, zKey) ) return 1;
+ }else if( pCol->colFlags & COLFLAG_PRIMKEY ){
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
/*
** This function is called when inserting, deleting or updating a row of
** table pTab to generate VDBE code to perform foreign key constraint
@@ -89583,7 +91313,9 @@ SQLITE_PRIVATE void sqlite3FkCheck(
Parse *pParse, /* Parse context */
Table *pTab, /* Row is being deleted from this table */
int regOld, /* Previous row data is stored here */
- int regNew /* New row data is stored here */
+ int regNew, /* New row data is stored here */
+ int *aChange, /* Array indicating UPDATEd columns (or 0) */
+ int bChngRowid /* True if rowid is UPDATEd */
){
sqlite3 *db = pParse->db; /* Database handle */
FKey *pFKey; /* Used to iterate through FKs */
@@ -89611,6 +91343,13 @@ SQLITE_PRIVATE void sqlite3FkCheck(
int i;
int isIgnore = 0;
+ if( aChange
+ && sqlite3_stricmp(pTab->zName, pFKey->zTo)!=0
+ && fkChildIsModified(pTab, pFKey, aChange, bChngRowid)==0
+ ){
+ continue;
+ }
+
/* Find the parent table of this foreign key. Also find a unique index
** on the parent key columns in the parent table. If either of these
** schema items cannot be located, set an error in pParse and return
@@ -89693,7 +91432,13 @@ SQLITE_PRIVATE void sqlite3FkCheck(
SrcList *pSrc;
int *aiCol = 0;
- if( !pFKey->isDeferred && !pParse->pToplevel && !pParse->isMultiWrite ){
+ if( aChange && fkParentIsModified(pTab, pFKey, aChange, bChngRowid)==0 ){
+ continue;
+ }
+
+ if( !pFKey->isDeferred && !(db->flags & SQLITE_DeferFKs)
+ && !pParse->pToplevel && !pParse->isMultiWrite
+ ){
assert( regOld==0 && regNew!=0 );
/* Inserting a single row into a parent table cannot cause an immediate
** foreign key violation. So do nothing in this case. */
@@ -89764,6 +91509,7 @@ SQLITE_PRIVATE u32 sqlite3FkOldmask(
return mask;
}
+
/*
** This function is called before generating code to update or delete a
** row contained in table pTab. If the operation is a DELETE, then
@@ -89793,32 +91539,16 @@ SQLITE_PRIVATE int sqlite3FkRequired(
}else{
/* This is an UPDATE. Foreign key processing is only required if the
** operation modifies one or more child or parent key columns. */
- int i;
FKey *p;
/* Check if any child key columns are being modified. */
for(p=pTab->pFKey; p; p=p->pNextFrom){
- for(i=0; i<p->nCol; i++){
- int iChildKey = p->aCol[i].iFrom;
- if( aChange[iChildKey]>=0 ) return 1;
- if( iChildKey==pTab->iPKey && chngRowid ) return 1;
- }
+ if( fkChildIsModified(pTab, p, aChange, chngRowid) ) return 1;
}
/* Check if any parent key columns are being modified. */
for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
- for(i=0; i<p->nCol; i++){
- char *zKey = p->aCol[i].zCol;
- int iKey;
- for(iKey=0; iKey<pTab->nCol; iKey++){
- Column *pCol = &pTab->aCol[iKey];
- if( (zKey ? !sqlite3StrICmp(pCol->zName, zKey)
- : (pCol->colFlags & COLFLAG_PRIMKEY)!=0) ){
- if( aChange[iKey]>=0 ) return 1;
- if( iKey==pTab->iPKey && chngRowid ) return 1;
- }
- }
- }
+ if( fkParentIsModified(pTab, p, aChange, chngRowid) ) return 1;
}
}
}
@@ -90044,7 +91774,9 @@ SQLITE_PRIVATE void sqlite3FkActions(
Parse *pParse, /* Parse context */
Table *pTab, /* Table being updated or deleted from */
ExprList *pChanges, /* Change-list for UPDATE, NULL for DELETE */
- int regOld /* Address of array containing old row */
+ int regOld, /* Address of array containing old row */
+ int *aChange, /* Array indicating UPDATEd columns (or 0) */
+ int bChngRowid /* True if rowid is UPDATEd */
){
/* If foreign-key support is enabled, iterate through all FKs that
** refer to table pTab. If there is an action associated with the FK
@@ -90053,9 +91785,11 @@ SQLITE_PRIVATE void sqlite3FkActions(
if( pParse->db->flags&SQLITE_ForeignKeys ){
FKey *pFKey; /* Iterator variable */
for(pFKey = sqlite3FkReferences(pTab); pFKey; pFKey=pFKey->pNextTo){
- Trigger *pAction = fkActionTrigger(pParse, pTab, pFKey, pChanges);
- if( pAction ){
- sqlite3CodeRowTriggerDirect(pParse, pAction, pTab, regOld, OE_Abort, 0);
+ if( aChange==0 || fkParentIsModified(pTab, pFKey, aChange, bChngRowid) ){
+ Trigger *pAct = fkActionTrigger(pParse, pTab, pFKey, pChanges);
+ if( pAct ){
+ sqlite3CodeRowTriggerDirect(pParse, pAct, pTab, regOld, OE_Abort, 0);
+ }
}
}
}
@@ -91140,7 +92874,7 @@ SQLITE_PRIVATE void sqlite3Insert(
sqlite3GenerateConstraintChecks(pParse, pTab, baseCur, regIns, aRegIdx,
keyColumn>=0, 0, onError, endOfLoop, &isReplace
);
- sqlite3FkCheck(pParse, pTab, 0, regIns);
+ sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0);
sqlite3CompleteInsertion(
pParse, pTab, baseCur, regIns, aRegIdx, 0, appendFlag, isReplace==0
);
@@ -91488,9 +93222,19 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){
int regIdx;
int regR;
+ int addrSkipRow = 0;
if( aRegIdx[iCur]==0 ) continue; /* Skip unused indices */
+ if( pIdx->pPartIdxWhere ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, aRegIdx[iCur]);
+ addrSkipRow = sqlite3VdbeMakeLabel(v);
+ pParse->ckBase = regData;
+ sqlite3ExprIfFalse(pParse, pIdx->pPartIdxWhere, addrSkipRow,
+ SQLITE_JUMPIFNULL);
+ pParse->ckBase = 0;
+ }
+
/* Create a key for accessing the index entry */
regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn+1);
for(i=0; i<pIdx->nColumn; i++){
@@ -91510,6 +93254,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
onError = pIdx->onError;
if( onError==OE_None ){
sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1);
+ sqlite3VdbeResolveLabel(v, addrSkipRow);
continue; /* pIdx is not a UNIQUE index */
}
if( overrideError!=OE_Default ){
@@ -91579,6 +93324,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
}
}
sqlite3VdbeJumpHere(v, j3);
+ sqlite3VdbeResolveLabel(v, addrSkipRow);
sqlite3ReleaseTempReg(pParse, regR);
}
@@ -91608,7 +93354,6 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion(
){
int i;
Vdbe *v;
- int nIdx;
Index *pIdx;
u8 pik_flags;
int regData;
@@ -91617,9 +93362,11 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion(
v = sqlite3GetVdbe(pParse);
assert( v!=0 );
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
- for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){}
- for(i=nIdx-1; i>=0; i--){
+ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
if( aRegIdx[i]==0 ) continue;
+ if( pIdx->pPartIdxWhere ){
+ sqlite3VdbeAddOp2(v, OP_IsNull, aRegIdx[i], sqlite3VdbeCurrentAddr(v)+2);
+ }
sqlite3VdbeAddOp2(v, OP_IdxInsert, baseCur+i+1, aRegIdx[i]);
if( useSeekResult ){
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
@@ -91721,6 +93468,7 @@ static int xferCompatibleCollation(const char *z1, const char *z2){
** * The same DESC and ASC markings occurs on all columns
** * The same onError processing (OE_Abort, OE_Ignore, etc)
** * The same collating sequence on each column
+** * The index has the exact same WHERE clause
*/
static int xferCompatibleIndex(Index *pDest, Index *pSrc){
int i;
@@ -91743,6 +93491,9 @@ static int xferCompatibleIndex(Index *pDest, Index *pSrc){
return 0; /* Different collating sequences */
}
}
+ if( sqlite3ExprCompare(pSrc->pPartIdxWhere, pDest->pPartIdxWhere, -1) ){
+ return 0; /* Different WHERE clauses */
+ }
/* If no test above fails then the indices must be compatible */
return 1;
@@ -91898,7 +93649,7 @@ static int xferOptimization(
}
}
#ifndef SQLITE_OMIT_CHECK
- if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck, pDest->pCheck) ){
+ if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) ){
return 0; /* Tables have different CHECK constraints. Ticket #2252 */
}
#endif
@@ -91992,6 +93743,7 @@ static int xferOptimization(
pKey = sqlite3IndexKeyinfo(pParse, pDestIdx);
sqlite3VdbeAddOp4(v, OP_OpenWrite, iDest, pDestIdx->tnum, iDbDest,
(char*)pKey, P4_KEYINFO_HANDOFF);
+ sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR);
VdbeComment((v, "%s", pDestIdx->zName));
addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0);
sqlite3VdbeAddOp2(v, OP_RowKey, iSrc, regData);
@@ -92655,11 +94407,14 @@ struct sqlite3_api_routines {
** extension */
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
+# define SQLITE_EXTENSION_INIT3 \
+ extern const sqlite3_api_routines *sqlite3_api;
#else
/* This case when the file is being statically linked into the
** application */
# define SQLITE_EXTENSION_INIT1 /*no-op*/
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
+# define SQLITE_EXTENSION_INIT3 /*no-op*/
#endif
#endif /* _SQLITE3EXT_H_ */
@@ -93317,6 +95072,35 @@ SQLITE_API int sqlite3_auto_extension(void (*xInit)(void)){
}
/*
+** Cancel a prior call to sqlite3_auto_extension. Remove xInit from the
+** set of routines that is invoked for each new database connection, if it
+** is currently on the list. If xInit is not on the list, then this
+** routine is a no-op.
+**
+** Return 1 if xInit was found on the list and removed. Return 0 if xInit
+** was not on the list.
+*/
+SQLITE_API int sqlite3_cancel_auto_extension(void (*xInit)(void)){
+#if SQLITE_THREADSAFE
+ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+#endif
+ int i;
+ int n = 0;
+ wsdAutoextInit;
+ sqlite3_mutex_enter(mutex);
+ for(i=wsdAutoext.nExt-1; i>=0; i--){
+ if( wsdAutoext.aExt[i]==xInit ){
+ wsdAutoext.nExt--;
+ wsdAutoext.aExt[i] = wsdAutoext.aExt[wsdAutoext.nExt];
+ n++;
+ break;
+ }
+ }
+ sqlite3_mutex_leave(mutex);
+ return n;
+}
+
+/*
** Reset the automatic extension loading mechanism.
*/
SQLITE_API void sqlite3_reset_auto_extension(void){
@@ -93392,6 +95176,424 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){
** This file contains code used to implement the PRAGMA command.
*/
+#if !defined(SQLITE_ENABLE_LOCKING_STYLE)
+# if defined(__APPLE__)
+# define SQLITE_ENABLE_LOCKING_STYLE 1
+# else
+# define SQLITE_ENABLE_LOCKING_STYLE 0
+# endif
+#endif
+
+/***************************************************************************
+** The next block of code, including the PragTyp_XXXX macro definitions and
+** the aPragmaName[] object is composed of generated code. DO NOT EDIT.
+**
+** To add new pragmas, edit the code in ../tool/mkpragmatab.tcl and rerun
+** that script. Then copy/paste the output in place of the following:
+*/
+#define PragTyp_HEADER_VALUE 0
+#define PragTyp_AUTO_VACUUM 1
+#define PragTyp_FLAG 2
+#define PragTyp_BUSY_TIMEOUT 3
+#define PragTyp_CACHE_SIZE 4
+#define PragTyp_CASE_SENSITIVE_LIKE 5
+#define PragTyp_COLLATION_LIST 6
+#define PragTyp_COMPILE_OPTIONS 7
+#define PragTyp_DATA_STORE_DIRECTORY 8
+#define PragTyp_DATABASE_LIST 9
+#define PragTyp_DEFAULT_CACHE_SIZE 10
+#define PragTyp_ENCODING 11
+#define PragTyp_FOREIGN_KEY_CHECK 12
+#define PragTyp_FOREIGN_KEY_LIST 13
+#define PragTyp_INCREMENTAL_VACUUM 14
+#define PragTyp_INDEX_INFO 15
+#define PragTyp_INDEX_LIST 16
+#define PragTyp_INTEGRITY_CHECK 17
+#define PragTyp_JOURNAL_MODE 18
+#define PragTyp_JOURNAL_SIZE_LIMIT 19
+#define PragTyp_LOCK_PROXY_FILE 20
+#define PragTyp_LOCKING_MODE 21
+#define PragTyp_PAGE_COUNT 22
+#define PragTyp_MMAP_SIZE 23
+#define PragTyp_PAGE_SIZE 24
+#define PragTyp_SECURE_DELETE 25
+#define PragTyp_SHRINK_MEMORY 26
+#define PragTyp_SOFT_HEAP_LIMIT 27
+#define PragTyp_STATS 28
+#define PragTyp_SYNCHRONOUS 29
+#define PragTyp_TABLE_INFO 30
+#define PragTyp_TEMP_STORE 31
+#define PragTyp_TEMP_STORE_DIRECTORY 32
+#define PragTyp_WAL_AUTOCHECKPOINT 33
+#define PragTyp_WAL_CHECKPOINT 34
+#define PragTyp_ACTIVATE_EXTENSIONS 35
+#define PragTyp_HEXKEY 36
+#define PragTyp_KEY 37
+#define PragTyp_REKEY 38
+#define PragTyp_LOCK_STATUS 39
+#define PragTyp_PARSER_TRACE 40
+#define PragFlag_NeedSchema 0x01
+static const struct sPragmaNames {
+ const char *const zName; /* Name of pragma */
+ u8 ePragTyp; /* PragTyp_XXX value */
+ u8 mPragFlag; /* Zero or more PragFlag_XXX values */
+ u32 iArg; /* Extra argument */
+} aPragmaNames[] = {
+#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
+ { /* zName: */ "activate_extensions",
+ /* ePragTyp: */ PragTyp_ACTIVATE_EXTENSIONS,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+ { /* zName: */ "application_id",
+ /* ePragTyp: */ PragTyp_HEADER_VALUE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_AUTOVACUUM)
+ { /* zName: */ "auto_vacuum",
+ /* ePragTyp: */ PragTyp_AUTO_VACUUM,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_AUTOMATIC_INDEX)
+ { /* zName: */ "automatic_index",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_AutoIndex },
+#endif
+ { /* zName: */ "busy_timeout",
+ /* ePragTyp: */ PragTyp_BUSY_TIMEOUT,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+ { /* zName: */ "cache_size",
+ /* ePragTyp: */ PragTyp_CACHE_SIZE,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+ { /* zName: */ "cache_spill",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_CacheSpill },
+ { /* zName: */ "case_sensitive_like",
+ /* ePragTyp: */ PragTyp_CASE_SENSITIVE_LIKE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+ { /* zName: */ "checkpoint_fullfsync",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_CkptFullFSync },
+#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+ { /* zName: */ "collation_list",
+ /* ePragTyp: */ PragTyp_COLLATION_LIST,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS)
+ { /* zName: */ "compile_options",
+ /* ePragTyp: */ PragTyp_COMPILE_OPTIONS,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+ { /* zName: */ "count_changes",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_CountRows },
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_OS_WIN
+ { /* zName: */ "data_store_directory",
+ /* ePragTyp: */ PragTyp_DATA_STORE_DIRECTORY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+ { /* zName: */ "database_list",
+ /* ePragTyp: */ PragTyp_DATABASE_LIST,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
+ { /* zName: */ "default_cache_size",
+ /* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
+ { /* zName: */ "defer_foreign_keys",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_DeferFKs },
+#endif
+ { /* zName: */ "empty_result_callbacks",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_NullCallback },
+#if !defined(SQLITE_OMIT_UTF16)
+ { /* zName: */ "encoding",
+ /* ePragTyp: */ PragTyp_ENCODING,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
+ { /* zName: */ "foreign_key_check",
+ /* ePragTyp: */ PragTyp_FOREIGN_KEY_CHECK,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FOREIGN_KEY)
+ { /* zName: */ "foreign_key_list",
+ /* ePragTyp: */ PragTyp_FOREIGN_KEY_LIST,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
+ { /* zName: */ "foreign_keys",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_ForeignKeys },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+ { /* zName: */ "freelist_count",
+ /* ePragTyp: */ PragTyp_HEADER_VALUE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+ { /* zName: */ "full_column_names",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_FullColNames },
+ { /* zName: */ "fullfsync",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_FullFSync },
+#if defined(SQLITE_HAS_CODEC)
+ { /* zName: */ "hexkey",
+ /* ePragTyp: */ PragTyp_HEXKEY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+ { /* zName: */ "hexrekey",
+ /* ePragTyp: */ PragTyp_HEXKEY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_CHECK)
+ { /* zName: */ "ignore_check_constraints",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_IgnoreChecks },
+#endif
+#if !defined(SQLITE_OMIT_AUTOVACUUM)
+ { /* zName: */ "incremental_vacuum",
+ /* ePragTyp: */ PragTyp_INCREMENTAL_VACUUM,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+ { /* zName: */ "index_info",
+ /* ePragTyp: */ PragTyp_INDEX_INFO,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+ { /* zName: */ "index_list",
+ /* ePragTyp: */ PragTyp_INDEX_LIST,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_INTEGRITY_CHECK)
+ { /* zName: */ "integrity_check",
+ /* ePragTyp: */ PragTyp_INTEGRITY_CHECK,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+ { /* zName: */ "journal_mode",
+ /* ePragTyp: */ PragTyp_JOURNAL_MODE,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+ { /* zName: */ "journal_size_limit",
+ /* ePragTyp: */ PragTyp_JOURNAL_SIZE_LIMIT,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if defined(SQLITE_HAS_CODEC)
+ { /* zName: */ "key",
+ /* ePragTyp: */ PragTyp_KEY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+ { /* zName: */ "legacy_file_format",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_LegacyFileFmt },
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_ENABLE_LOCKING_STYLE
+ { /* zName: */ "lock_proxy_file",
+ /* ePragTyp: */ PragTyp_LOCK_PROXY_FILE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
+ { /* zName: */ "lock_status",
+ /* ePragTyp: */ PragTyp_LOCK_STATUS,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+ { /* zName: */ "locking_mode",
+ /* ePragTyp: */ PragTyp_LOCKING_MODE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+ { /* zName: */ "max_page_count",
+ /* ePragTyp: */ PragTyp_PAGE_COUNT,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+ { /* zName: */ "mmap_size",
+ /* ePragTyp: */ PragTyp_MMAP_SIZE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+ { /* zName: */ "page_count",
+ /* ePragTyp: */ PragTyp_PAGE_COUNT,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+ { /* zName: */ "page_size",
+ /* ePragTyp: */ PragTyp_PAGE_SIZE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if defined(SQLITE_DEBUG)
+ { /* zName: */ "parser_trace",
+ /* ePragTyp: */ PragTyp_PARSER_TRACE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+ { /* zName: */ "query_only",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_QueryOnly },
+#if !defined(SQLITE_OMIT_INTEGRITY_CHECK)
+ { /* zName: */ "quick_check",
+ /* ePragTyp: */ PragTyp_INTEGRITY_CHECK,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+ { /* zName: */ "read_uncommitted",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_ReadUncommitted },
+ { /* zName: */ "recursive_triggers",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_RecTriggers },
+#if defined(SQLITE_HAS_CODEC)
+ { /* zName: */ "rekey",
+ /* ePragTyp: */ PragTyp_REKEY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+ { /* zName: */ "reverse_unordered_selects",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_ReverseOrder },
+#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+ { /* zName: */ "schema_version",
+ /* ePragTyp: */ PragTyp_HEADER_VALUE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+ { /* zName: */ "secure_delete",
+ /* ePragTyp: */ PragTyp_SECURE_DELETE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+ { /* zName: */ "short_column_names",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_ShortColNames },
+ { /* zName: */ "shrink_memory",
+ /* ePragTyp: */ PragTyp_SHRINK_MEMORY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+ { /* zName: */ "soft_heap_limit",
+ /* ePragTyp: */ PragTyp_SOFT_HEAP_LIMIT,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#if defined(SQLITE_DEBUG)
+ { /* zName: */ "sql_trace",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_SqlTrace },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+ { /* zName: */ "stats",
+ /* ePragTyp: */ PragTyp_STATS,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+ { /* zName: */ "synchronous",
+ /* ePragTyp: */ PragTyp_SYNCHRONOUS,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+ { /* zName: */ "table_info",
+ /* ePragTyp: */ PragTyp_TABLE_INFO,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+ { /* zName: */ "temp_store",
+ /* ePragTyp: */ PragTyp_TEMP_STORE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+ { /* zName: */ "temp_store_directory",
+ /* ePragTyp: */ PragTyp_TEMP_STORE_DIRECTORY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+ { /* zName: */ "user_version",
+ /* ePragTyp: */ PragTyp_HEADER_VALUE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if defined(SQLITE_DEBUG)
+ { /* zName: */ "vdbe_addoptrace",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_VdbeAddopTrace },
+ { /* zName: */ "vdbe_debug",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_SqlTrace|SQLITE_VdbeListing|SQLITE_VdbeTrace },
+ { /* zName: */ "vdbe_listing",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_VdbeListing },
+ { /* zName: */ "vdbe_trace",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_VdbeTrace },
+#endif
+#if !defined(SQLITE_OMIT_WAL)
+ { /* zName: */ "wal_autocheckpoint",
+ /* ePragTyp: */ PragTyp_WAL_AUTOCHECKPOINT,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+ { /* zName: */ "wal_checkpoint",
+ /* ePragTyp: */ PragTyp_WAL_CHECKPOINT,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+ { /* zName: */ "writable_schema",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_WriteSchema|SQLITE_RecoveryMode },
+};
+/* Number of pragmas: 56 on by default, 68 total. */
+/* End of the automatically generated pragma table.
+***************************************************************************/
+
/*
** Interpret the given string as a safety level. Return 0 for OFF,
** 1 for ON or NORMAL and 2 for FULL. Return 1 for an empty or
@@ -93537,92 +95739,35 @@ static void returnSingleInt(Parse *pParse, const char *zLabel, i64 value){
sqlite3VdbeAddOp2(v, OP_ResultRow, mem, 1);
}
-#ifndef SQLITE_OMIT_FLAG_PRAGMAS
-/*
-** Check to see if zRight and zLeft refer to a pragma that queries
-** or changes one of the flags in db->flags. Return 1 if so and 0 if not.
-** Also, implement the pragma.
-*/
-static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
- static const struct sPragmaType {
- const char *zName; /* Name of the pragma */
- int mask; /* Mask for the db->flags value */
- } aPragma[] = {
- { "full_column_names", SQLITE_FullColNames },
- { "short_column_names", SQLITE_ShortColNames },
- { "count_changes", SQLITE_CountRows },
- { "empty_result_callbacks", SQLITE_NullCallback },
- { "legacy_file_format", SQLITE_LegacyFileFmt },
- { "fullfsync", SQLITE_FullFSync },
- { "checkpoint_fullfsync", SQLITE_CkptFullFSync },
- { "reverse_unordered_selects", SQLITE_ReverseOrder },
-#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
- { "automatic_index", SQLITE_AutoIndex },
-#endif
-#ifdef SQLITE_DEBUG
- { "sql_trace", SQLITE_SqlTrace },
- { "vdbe_listing", SQLITE_VdbeListing },
- { "vdbe_trace", SQLITE_VdbeTrace },
- { "vdbe_addoptrace", SQLITE_VdbeAddopTrace},
- { "vdbe_debug", SQLITE_SqlTrace | SQLITE_VdbeListing
- | SQLITE_VdbeTrace },
-#endif
-#ifndef SQLITE_OMIT_CHECK
- { "ignore_check_constraints", SQLITE_IgnoreChecks },
-#endif
- /* The following is VERY experimental */
- { "writable_schema", SQLITE_WriteSchema|SQLITE_RecoveryMode },
-
- /* TODO: Maybe it shouldn't be possible to change the ReadUncommitted
- ** flag if there are any active statements. */
- { "read_uncommitted", SQLITE_ReadUncommitted },
- { "recursive_triggers", SQLITE_RecTriggers },
-
- /* This flag may only be set if both foreign-key and trigger support
- ** are present in the build. */
-#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
- { "foreign_keys", SQLITE_ForeignKeys },
-#endif
- };
- int i;
- const struct sPragmaType *p;
- for(i=0, p=aPragma; i<ArraySize(aPragma); i++, p++){
- if( sqlite3StrICmp(zLeft, p->zName)==0 ){
- sqlite3 *db = pParse->db;
- Vdbe *v;
- v = sqlite3GetVdbe(pParse);
- assert( v!=0 ); /* Already allocated by sqlite3Pragma() */
- if( ALWAYS(v) ){
- if( zRight==0 ){
- returnSingleInt(pParse, p->zName, (db->flags & p->mask)!=0 );
- }else{
- int mask = p->mask; /* 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. */
- mask &= ~(SQLITE_ForeignKeys);
- }
-
- if( sqlite3GetBoolean(zRight, 0) ){
- db->flags |= mask;
- }else{
- db->flags &= ~mask;
- }
- /* Many of the flag-pragmas modify the code generated by the SQL
- ** compiler (eg. count_changes). So add an opcode to expire all
- ** compiled SQL statements after modifying a pragma value.
- */
- sqlite3VdbeAddOp2(v, OP_Expire, 0, 0);
- }
+/*
+** Set the safety_level and pager flags for pager iDb. Or if iDb<0
+** set these values for all pagers.
+*/
+#ifndef SQLITE_OMIT_PAGER_PRAGMAS
+static void setAllPagerFlags(sqlite3 *db){
+ if( db->autoCommit ){
+ Db *pDb = db->aDb;
+ int n = db->nDb;
+ assert( SQLITE_FullFSync==PAGER_FULLFSYNC );
+ assert( SQLITE_CkptFullFSync==PAGER_CKPT_FULLFSYNC );
+ assert( SQLITE_CacheSpill==PAGER_CACHESPILL );
+ assert( (PAGER_FULLFSYNC | PAGER_CKPT_FULLFSYNC | PAGER_CACHESPILL)
+ == PAGER_FLAGS_MASK );
+ assert( (pDb->safety_level & PAGER_SYNCHRONOUS_MASK)==pDb->safety_level );
+ while( (n--) > 0 ){
+ if( pDb->pBt ){
+ sqlite3BtreeSetPagerFlags(pDb->pBt,
+ pDb->safety_level | (db->flags & PAGER_FLAGS_MASK) );
}
-
- return 1;
+ pDb++;
}
}
- return 0;
}
-#endif /* SQLITE_OMIT_FLAG_PRAGMAS */
+#else
+# define setAllPagerFlags(X) /* no-op */
+#endif
+
/*
** Return a human-readable name for a constraint resolution action.
@@ -93693,8 +95838,9 @@ SQLITE_PRIVATE void sqlite3Pragma(
char *zRight = 0; /* Nul-terminated UTF-8 string <value>, or NULL */
const char *zDb = 0; /* The database name */
Token *pId; /* Pointer to <id> token */
- int iDb; /* Database index for <database> */
char *aFcntl[4]; /* Argument to SQLITE_FCNTL_PRAGMA */
+ int iDb; /* Database index for <database> */
+ int lwr, upr, mid; /* Binary search bounds */
int rc; /* return value form SQLITE_FCNTL_PRAGMA */
sqlite3 *db = pParse->db; /* The database connection */
Db *pDb; /* The specific database being pragmaed */
@@ -93750,16 +95896,41 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeAddOp2(v, OP_ResultRow, mem, 1);
sqlite3_free(aFcntl[0]);
}
- }else if( rc!=SQLITE_NOTFOUND ){
+ goto pragma_out;
+ }
+ if( rc!=SQLITE_NOTFOUND ){
if( aFcntl[0] ){
sqlite3ErrorMsg(pParse, "%s", aFcntl[0]);
sqlite3_free(aFcntl[0]);
}
pParse->nErr++;
pParse->rc = rc;
- }else
-
-
+ goto pragma_out;
+ }
+
+ /* Locate the pragma in the lookup table */
+ lwr = 0;
+ upr = ArraySize(aPragmaNames)-1;
+ while( lwr<=upr ){
+ mid = (lwr+upr)/2;
+ rc = sqlite3_stricmp(zLeft, aPragmaNames[mid].zName);
+ if( rc==0 ) break;
+ if( rc<0 ){
+ upr = mid - 1;
+ }else{
+ lwr = mid + 1;
+ }
+ }
+ if( lwr>upr ) goto pragma_out;
+
+ /* Make sure the database schema is loaded if the pragma requires that */
+ if( (aPragmaNames[mid].mPragFlag & PragFlag_NeedSchema)!=0 ){
+ if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ }
+
+ /* Jump to the appropriate pragma handler */
+ switch( aPragmaNames[mid].ePragTyp ){
+
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
/*
** PRAGMA [database.]default_cache_size
@@ -93777,7 +95948,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** size. But continue to take the absolute value of the default cache
** size of historical compatibility.
*/
- if( sqlite3StrICmp(zLeft,"default_cache_size")==0 ){
+ case PragTyp_DEFAULT_CACHE_SIZE: {
static const VdbeOpList getCacheSize[] = {
{ OP_Transaction, 0, 0, 0}, /* 0 */
{ OP_ReadCookie, 0, 1, BTREE_DEFAULT_CACHE_SIZE}, /* 1 */
@@ -93790,7 +95961,6 @@ SQLITE_PRIVATE void sqlite3Pragma(
{ OP_ResultRow, 1, 1, 0},
};
int addr;
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
sqlite3VdbeUsesBtree(v, iDb);
if( !zRight ){
sqlite3VdbeSetNumCols(v, 1);
@@ -93809,7 +95979,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
pDb->pSchema->cache_size = size;
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
}
- }else
+ break;
+ }
#endif /* !SQLITE_OMIT_PAGER_PRAGMAS && !SQLITE_OMIT_DEPRECATED */
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
@@ -93822,7 +95993,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** database page size value. The value can only be set if
** the database has not yet been created.
*/
- if( sqlite3StrICmp(zLeft,"page_size")==0 ){
+ case PragTyp_PAGE_SIZE: {
Btree *pBt = pDb->pBt;
assert( pBt!=0 );
if( !zRight ){
@@ -93837,7 +96008,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
db->mallocFailed = 1;
}
}
- }else
+ break;
+ }
/*
** PRAGMA [database.]secure_delete
@@ -93847,7 +96019,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** secure_delete flag. The second form changes the secure_delete
** flag setting and reports thenew value.
*/
- if( sqlite3StrICmp(zLeft,"secure_delete")==0 ){
+ case PragTyp_SECURE_DELETE: {
Btree *pBt = pDb->pBt;
int b = -1;
assert( pBt!=0 );
@@ -93862,7 +96034,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
b = sqlite3BtreeSecureDelete(pBt, b);
returnSingleInt(pParse, "secure_delete", b);
- }else
+ break;
+ }
/*
** PRAGMA [database.]max_page_count
@@ -93881,11 +96054,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
**
** Return the number of pages in the specified database.
*/
- if( sqlite3StrICmp(zLeft,"page_count")==0
- || sqlite3StrICmp(zLeft,"max_page_count")==0
- ){
+ case PragTyp_PAGE_COUNT: {
int iReg;
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
sqlite3CodeVerifySchema(pParse, iDb);
iReg = ++pParse->nMem;
if( sqlite3Tolower(zLeft[0])=='p' ){
@@ -93897,13 +96067,14 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeAddOp2(v, OP_ResultRow, iReg, 1);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, SQLITE_TRANSIENT);
- }else
+ break;
+ }
/*
** PRAGMA [database.]locking_mode
** PRAGMA [database.]locking_mode = (normal|exclusive)
*/
- if( sqlite3StrICmp(zLeft,"locking_mode")==0 ){
+ case PragTyp_LOCKING_MODE: {
const char *zRet = "normal";
int eMode = getLockingMode(zRight);
@@ -93936,7 +96107,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
eMode = sqlite3PagerLockingMode(pPager, eMode);
}
- assert(eMode==PAGER_LOCKINGMODE_NORMAL||eMode==PAGER_LOCKINGMODE_EXCLUSIVE);
+ assert( eMode==PAGER_LOCKINGMODE_NORMAL
+ || eMode==PAGER_LOCKINGMODE_EXCLUSIVE );
if( eMode==PAGER_LOCKINGMODE_EXCLUSIVE ){
zRet = "exclusive";
}
@@ -93944,25 +96116,18 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "locking_mode", SQLITE_STATIC);
sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, zRet, 0);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
- }else
+ break;
+ }
/*
** PRAGMA [database.]journal_mode
** PRAGMA [database.]journal_mode =
** (delete|persist|off|truncate|memory|wal|off)
*/
- if( sqlite3StrICmp(zLeft,"journal_mode")==0 ){
+ case PragTyp_JOURNAL_MODE: {
int eMode; /* One of the PAGER_JOURNALMODE_XXX symbols */
int ii; /* Loop counter */
- /* Force the schema to be loaded on all databases. This causes all
- ** database files to be opened and the journal_modes set. This is
- ** necessary because subsequent processing must know if the databases
- ** are in WAL mode. */
- if( sqlite3ReadSchema(pParse) ){
- goto pragma_out;
- }
-
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "journal_mode", SQLITE_STATIC);
@@ -93994,7 +96159,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
}
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
- }else
+ break;
+ }
/*
** PRAGMA [database.]journal_size_limit
@@ -94002,16 +96168,17 @@ SQLITE_PRIVATE void sqlite3Pragma(
**
** Get or set the size limit on rollback journal files.
*/
- if( sqlite3StrICmp(zLeft,"journal_size_limit")==0 ){
+ case PragTyp_JOURNAL_SIZE_LIMIT: {
Pager *pPager = sqlite3BtreePager(pDb->pBt);
i64 iLimit = -2;
if( zRight ){
- sqlite3Atoi64(zRight, &iLimit, 1000000, SQLITE_UTF8);
+ sqlite3Atoi64(zRight, &iLimit, sqlite3Strlen30(zRight), SQLITE_UTF8);
if( iLimit<-1 ) iLimit = -1;
}
iLimit = sqlite3PagerJournalSizeLimit(pPager, iLimit);
returnSingleInt(pParse, "journal_size_limit", iLimit);
- }else
+ break;
+ }
#endif /* SQLITE_OMIT_PAGER_PRAGMAS */
@@ -94023,57 +96190,47 @@ SQLITE_PRIVATE void sqlite3Pragma(
** The value is one of: 0 NONE 1 FULL 2 INCREMENTAL
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
- if( sqlite3StrICmp(zLeft,"auto_vacuum")==0 ){
+ case PragTyp_AUTO_VACUUM: {
Btree *pBt = pDb->pBt;
assert( pBt!=0 );
- if( sqlite3ReadSchema(pParse) ){
- goto pragma_out;
- }
if( !zRight ){
- int auto_vacuum;
- if( ALWAYS(pBt) ){
- auto_vacuum = sqlite3BtreeGetAutoVacuum(pBt);
- }else{
- auto_vacuum = SQLITE_DEFAULT_AUTOVACUUM;
- }
- returnSingleInt(pParse, "auto_vacuum", auto_vacuum);
+ returnSingleInt(pParse, "auto_vacuum", sqlite3BtreeGetAutoVacuum(pBt));
}else{
int eAuto = getAutoVacuum(zRight);
assert( eAuto>=0 && eAuto<=2 );
db->nextAutovac = (u8)eAuto;
- if( ALWAYS(eAuto>=0) ){
- /* Call SetAutoVacuum() to set initialize the internal auto and
- ** incr-vacuum flags. This is required in case this connection
- ** creates the database file. It is important that it is created
- ** as an auto-vacuum capable db.
+ /* Call SetAutoVacuum() to set initialize the internal auto and
+ ** incr-vacuum flags. This is required in case this connection
+ ** creates the database file. It is important that it is created
+ ** as an auto-vacuum capable db.
+ */
+ rc = sqlite3BtreeSetAutoVacuum(pBt, eAuto);
+ if( rc==SQLITE_OK && (eAuto==1 || eAuto==2) ){
+ /* When setting the auto_vacuum mode to either "full" or
+ ** "incremental", write the value of meta[6] in the database
+ ** file. Before writing to meta[6], check that meta[3] indicates
+ ** that this really is an auto-vacuum capable database.
*/
- rc = sqlite3BtreeSetAutoVacuum(pBt, eAuto);
- if( rc==SQLITE_OK && (eAuto==1 || eAuto==2) ){
- /* When setting the auto_vacuum mode to either "full" or
- ** "incremental", write the value of meta[6] in the database
- ** file. Before writing to meta[6], check that meta[3] indicates
- ** that this really is an auto-vacuum capable database.
- */
- static const VdbeOpList setMeta6[] = {
- { OP_Transaction, 0, 1, 0}, /* 0 */
- { OP_ReadCookie, 0, 1, BTREE_LARGEST_ROOT_PAGE},
- { OP_If, 1, 0, 0}, /* 2 */
- { OP_Halt, SQLITE_OK, OE_Abort, 0}, /* 3 */
- { OP_Integer, 0, 1, 0}, /* 4 */
- { OP_SetCookie, 0, BTREE_INCR_VACUUM, 1}, /* 5 */
- };
- int iAddr;
- iAddr = sqlite3VdbeAddOpList(v, ArraySize(setMeta6), setMeta6);
- sqlite3VdbeChangeP1(v, iAddr, iDb);
- sqlite3VdbeChangeP1(v, iAddr+1, iDb);
- sqlite3VdbeChangeP2(v, iAddr+2, iAddr+4);
- sqlite3VdbeChangeP1(v, iAddr+4, eAuto-1);
- sqlite3VdbeChangeP1(v, iAddr+5, iDb);
- sqlite3VdbeUsesBtree(v, iDb);
- }
+ static const VdbeOpList setMeta6[] = {
+ { OP_Transaction, 0, 1, 0}, /* 0 */
+ { OP_ReadCookie, 0, 1, BTREE_LARGEST_ROOT_PAGE},
+ { OP_If, 1, 0, 0}, /* 2 */
+ { OP_Halt, SQLITE_OK, OE_Abort, 0}, /* 3 */
+ { OP_Integer, 0, 1, 0}, /* 4 */
+ { OP_SetCookie, 0, BTREE_INCR_VACUUM, 1}, /* 5 */
+ };
+ int iAddr;
+ iAddr = sqlite3VdbeAddOpList(v, ArraySize(setMeta6), setMeta6);
+ sqlite3VdbeChangeP1(v, iAddr, iDb);
+ sqlite3VdbeChangeP1(v, iAddr+1, iDb);
+ sqlite3VdbeChangeP2(v, iAddr+2, iAddr+4);
+ sqlite3VdbeChangeP1(v, iAddr+4, eAuto-1);
+ sqlite3VdbeChangeP1(v, iAddr+5, iDb);
+ sqlite3VdbeUsesBtree(v, iDb);
}
}
- }else
+ break;
+ }
#endif
/*
@@ -94082,11 +96239,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
** Do N steps of incremental vacuuming on a database.
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
- if( sqlite3StrICmp(zLeft,"incremental_vacuum")==0 ){
+ case PragTyp_INCREMENTAL_VACUUM: {
int iLimit, addr;
- if( sqlite3ReadSchema(pParse) ){
- goto pragma_out;
- }
if( zRight==0 || !sqlite3GetInt32(zRight, &iLimit) || iLimit<=0 ){
iLimit = 0x7fffffff;
}
@@ -94097,7 +96251,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1);
sqlite3VdbeAddOp2(v, OP_IfPos, 1, addr);
sqlite3VdbeJumpHere(v, addr);
- }else
+ break;
+ }
#endif
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
@@ -94112,8 +96267,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** number of pages is adjusted so that the cache uses -N kibibytes
** of memory.
*/
- if( sqlite3StrICmp(zLeft,"cache_size")==0 ){
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ case PragTyp_CACHE_SIZE: {
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( !zRight ){
returnSingleInt(pParse, "cache_size", pDb->pSchema->cache_size);
@@ -94122,7 +96276,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
pDb->pSchema->cache_size = size;
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
}
- }else
+ break;
+ }
/*
** PRAGMA [database.]mmap_size(N)
@@ -94138,12 +96293,13 @@ SQLITE_PRIVATE void sqlite3Pragma(
** as little or as much as it wants. Except, if N is set to 0 then the
** upper layers will never invoke the xFetch interfaces to the VFS.
*/
- if( sqlite3StrICmp(zLeft,"mmap_size")==0 ){
+ case PragTyp_MMAP_SIZE: {
sqlite3_int64 sz;
+#if SQLITE_MAX_MMAP_SIZE>0
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( zRight ){
int ii;
- sqlite3Atoi64(zRight, &sz, 1000, SQLITE_UTF8);
+ sqlite3Atoi64(zRight, &sz, sqlite3Strlen30(zRight), SQLITE_UTF8);
if( sz<0 ) sz = sqlite3GlobalConfig.szMmap;
if( pId2->n==0 ) db->szMmap = sz;
for(ii=db->nDb-1; ii>=0; ii--){
@@ -94153,13 +96309,19 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
}
sz = -1;
- if( sqlite3_file_control(db,zDb,SQLITE_FCNTL_MMAP_SIZE,&sz)==SQLITE_OK ){
-#if SQLITE_MAX_MMAP_SIZE==0
- sz = 0;
+ rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_MMAP_SIZE, &sz);
+#else
+ sz = 0;
+ rc = SQLITE_OK;
#endif
+ if( rc==SQLITE_OK ){
returnSingleInt(pParse, "mmap_size", sz);
+ }else if( rc!=SQLITE_NOTFOUND ){
+ pParse->nErr++;
+ pParse->rc = rc;
}
- }else
+ break;
+ }
/*
** PRAGMA temp_store
@@ -94172,13 +96334,14 @@ SQLITE_PRIVATE void sqlite3Pragma(
** Note that it is possible for the library compile-time options to
** override this setting
*/
- if( sqlite3StrICmp(zLeft, "temp_store")==0 ){
+ case PragTyp_TEMP_STORE: {
if( !zRight ){
returnSingleInt(pParse, "temp_store", db->temp_store);
}else{
changeTempStorage(pParse, zRight);
}
- }else
+ break;
+ }
/*
** PRAGMA temp_store_directory
@@ -94190,7 +96353,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** If temporary directory is changed, then invalidateTempStorage.
**
*/
- if( sqlite3StrICmp(zLeft, "temp_store_directory")==0 ){
+ case PragTyp_TEMP_STORE_DIRECTORY: {
if( !zRight ){
if( sqlite3_temp_directory ){
sqlite3VdbeSetNumCols(v, 1);
@@ -94223,7 +96386,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
#endif /* SQLITE_OMIT_WSD */
}
- }else
+ break;
+ }
#if SQLITE_OS_WIN
/*
@@ -94239,7 +96403,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** by this setting, regardless of its value.
**
*/
- if( sqlite3StrICmp(zLeft, "data_store_directory")==0 ){
+ case PragTyp_DATA_STORE_DIRECTORY: {
if( !zRight ){
if( sqlite3_data_directory ){
sqlite3VdbeSetNumCols(v, 1);
@@ -94266,26 +96430,20 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
#endif /* SQLITE_OMIT_WSD */
}
- }else
+ break;
+ }
#endif
-#if !defined(SQLITE_ENABLE_LOCKING_STYLE)
-# if defined(__APPLE__)
-# define SQLITE_ENABLE_LOCKING_STYLE 1
-# else
-# define SQLITE_ENABLE_LOCKING_STYLE 0
-# endif
-#endif
#if SQLITE_ENABLE_LOCKING_STYLE
/*
- ** PRAGMA [database.]lock_proxy_file
- ** PRAGMA [database.]lock_proxy_file = ":auto:"|"lock_file_path"
- **
- ** Return or set the value of the lock_proxy_file flag. Changing
- ** the value sets a specific file to be used for database access locks.
- **
- */
- if( sqlite3StrICmp(zLeft, "lock_proxy_file")==0 ){
+ ** PRAGMA [database.]lock_proxy_file
+ ** PRAGMA [database.]lock_proxy_file = ":auto:"|"lock_file_path"
+ **
+ ** Return or set the value of the lock_proxy_file flag. Changing
+ ** the value sets a specific file to be used for database access locks.
+ **
+ */
+ case PragTyp_LOCK_PROXY_FILE: {
if( !zRight ){
Pager *pPager = sqlite3BtreePager(pDb->pBt);
char *proxy_file_path = NULL;
@@ -94316,7 +96474,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
goto pragma_out;
}
}
- }else
+ break;
+ }
#endif /* SQLITE_ENABLE_LOCKING_STYLE */
/*
@@ -94328,8 +96487,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** default value will be restored the next time the database is
** opened.
*/
- if( sqlite3StrICmp(zLeft,"synchronous")==0 ){
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ case PragTyp_SYNCHRONOUS: {
if( !zRight ){
returnSingleInt(pParse, "synchronous", pDb->safety_level-1);
}else{
@@ -94338,16 +96496,42 @@ SQLITE_PRIVATE void sqlite3Pragma(
"Safety level may not be changed inside a transaction");
}else{
pDb->safety_level = getSafetyLevel(zRight,0,1)+1;
+ setAllPagerFlags(db);
}
}
- }else
+ break;
+ }
#endif /* SQLITE_OMIT_PAGER_PRAGMAS */
#ifndef SQLITE_OMIT_FLAG_PRAGMAS
- if( flagPragma(pParse, zLeft, zRight) ){
- /* The flagPragma() subroutine also generates any necessary code
- ** there is nothing more to do here */
- }else
+ case PragTyp_FLAG: {
+ if( zRight==0 ){
+ returnSingleInt(pParse, aPragmaNames[mid].zName,
+ (db->flags & aPragmaNames[mid].iArg)!=0 );
+ }else{
+ int mask = aPragmaNames[mid].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. */
+ mask &= ~(SQLITE_ForeignKeys);
+ }
+
+ if( sqlite3GetBoolean(zRight, 0) ){
+ db->flags |= mask;
+ }else{
+ db->flags &= ~mask;
+ if( mask==SQLITE_DeferFKs ) db->nDeferredImmCons = 0;
+ }
+
+ /* Many of the flag-pragmas modify the code generated by the SQL
+ ** compiler (eg. count_changes). So add an opcode to expire all
+ ** compiled SQL statements after modifying a pragma value.
+ */
+ sqlite3VdbeAddOp2(v, OP_Expire, 0, 0);
+ setAllPagerFlags(db);
+ }
+ break;
+ }
#endif /* SQLITE_OMIT_FLAG_PRAGMAS */
#ifndef SQLITE_OMIT_SCHEMA_PRAGMAS
@@ -94363,9 +96547,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
** notnull: True if 'NOT NULL' is part of column declaration
** dflt_value: The default value for the column, if any.
*/
- if( sqlite3StrICmp(zLeft, "table_info")==0 && zRight ){
+ case PragTyp_TABLE_INFO: if( zRight ){
Table *pTab;
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
pTab = sqlite3FindTable(db, zRight, zDb);
if( pTab ){
int i, k;
@@ -94409,12 +96592,42 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6);
}
}
- }else
+ }
+ break;
+
+ case PragTyp_STATS: {
+ Index *pIdx;
+ HashElem *i;
+ v = sqlite3GetVdbe(pParse);
+ sqlite3VdbeSetNumCols(v, 4);
+ pParse->nMem = 4;
+ sqlite3CodeVerifySchema(pParse, iDb);
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "table", SQLITE_STATIC);
+ sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "index", SQLITE_STATIC);
+ sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "width", SQLITE_STATIC);
+ sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "height", SQLITE_STATIC);
+ for(i=sqliteHashFirst(&pDb->pSchema->tblHash); i; i=sqliteHashNext(i)){
+ Table *pTab = sqliteHashData(i);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, pTab->zName, 0);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, 2);
+ sqlite3VdbeAddOp2(v, OP_Integer,
+ (int)sqlite3LogEstToInt(pTab->szTabRow), 3);
+ sqlite3VdbeAddOp2(v, OP_Integer, (int)pTab->nRowEst, 4);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 4);
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pIdx->zName, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer,
+ (int)sqlite3LogEstToInt(pIdx->szIdxRow), 3);
+ sqlite3VdbeAddOp2(v, OP_Integer, (int)pIdx->aiRowEst[0], 4);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 4);
+ }
+ }
+ }
+ break;
- if( sqlite3StrICmp(zLeft, "index_info")==0 && zRight ){
+ case PragTyp_INDEX_INFO: if( zRight ){
Index *pIdx;
Table *pTab;
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
pIdx = sqlite3FindIndex(db, zRight, zDb);
if( pIdx ){
int i;
@@ -94434,39 +96647,34 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
}
}
- }else
+ }
+ break;
- if( sqlite3StrICmp(zLeft, "index_list")==0 && zRight ){
+ case PragTyp_INDEX_LIST: if( zRight ){
Index *pIdx;
Table *pTab;
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ int i;
pTab = sqlite3FindTable(db, zRight, zDb);
if( pTab ){
v = sqlite3GetVdbe(pParse);
- pIdx = pTab->pIndex;
- if( pIdx ){
- int i = 0;
- sqlite3VdbeSetNumCols(v, 3);
- pParse->nMem = 3;
- sqlite3CodeVerifySchema(pParse, iDb);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "unique", SQLITE_STATIC);
- while(pIdx){
- sqlite3VdbeAddOp2(v, OP_Integer, i, 1);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pIdx->zName, 0);
- sqlite3VdbeAddOp2(v, OP_Integer, pIdx->onError!=OE_None, 3);
- sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
- ++i;
- pIdx = pIdx->pNext;
- }
+ sqlite3VdbeSetNumCols(v, 3);
+ pParse->nMem = 3;
+ sqlite3CodeVerifySchema(pParse, iDb);
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC);
+ sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC);
+ sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "unique", SQLITE_STATIC);
+ for(pIdx=pTab->pIndex, i=0; pIdx; pIdx=pIdx->pNext, i++){
+ sqlite3VdbeAddOp2(v, OP_Integer, i, 1);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pIdx->zName, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer, pIdx->onError!=OE_None, 3);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
}
}
- }else
+ }
+ break;
- if( sqlite3StrICmp(zLeft, "database_list")==0 ){
+ case PragTyp_DATABASE_LIST: {
int i;
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
sqlite3VdbeSetNumCols(v, 3);
pParse->nMem = 3;
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC);
@@ -94481,9 +96689,10 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3BtreeGetFilename(db->aDb[i].pBt), 0);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
}
- }else
+ }
+ break;
- if( sqlite3StrICmp(zLeft, "collation_list")==0 ){
+ case PragTyp_COLLATION_LIST: {
int i = 0;
HashElem *p;
sqlite3VdbeSetNumCols(v, 2);
@@ -94496,14 +96705,14 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pColl->zName, 0);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 2);
}
- }else
+ }
+ break;
#endif /* SQLITE_OMIT_SCHEMA_PRAGMAS */
#ifndef SQLITE_OMIT_FOREIGN_KEY
- if( sqlite3StrICmp(zLeft, "foreign_key_list")==0 && zRight ){
+ case PragTyp_FOREIGN_KEY_LIST: if( zRight ){
FKey *pFK;
Table *pTab;
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
pTab = sqlite3FindTable(db, zRight, zDb);
if( pTab ){
v = sqlite3GetVdbe(pParse);
@@ -94543,12 +96752,13 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
}
}
- }else
+ }
+ break;
#endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */
#ifndef SQLITE_OMIT_FOREIGN_KEY
#ifndef SQLITE_OMIT_TRIGGER
- if( sqlite3StrICmp(zLeft, "foreign_key_check")==0 ){
+ case PragTyp_FOREIGN_KEY_CHECK: {
FKey *pFK; /* A foreign key constraint */
Table *pTab; /* Child table contain "REFERENCES" keyword */
Table *pParent; /* Parent table that child points to */
@@ -94564,7 +96774,6 @@ SQLITE_PRIVATE void sqlite3Pragma(
int addrOk; /* Jump here if the key is OK */
int *aiCols; /* child to parent column mapping */
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
regResult = pParse->nMem+1;
pParse->nMem += 4;
regKey = ++pParse->nMem;
@@ -94592,8 +96801,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeAddOp4(v, OP_String8, 0, regResult, 0, pTab->zName,
P4_TRANSIENT);
for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){
- pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb);
- if( pParent==0 ) break;
+ pParent = sqlite3FindTable(db, pFK->zTo, zDb);
+ if( pParent==0 ) continue;
pIdx = 0;
sqlite3TableLock(pParse, iDb, pParent->tnum, 0, pParent->zName);
x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, 0);
@@ -94610,18 +96819,20 @@ SQLITE_PRIVATE void sqlite3Pragma(
break;
}
}
+ assert( pParse->nErr>0 || pFK==0 );
if( pFK ) break;
if( pParse->nTab<i ) pParse->nTab = i;
addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, 0);
for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){
- pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb);
- assert( pParent!=0 );
+ pParent = sqlite3FindTable(db, pFK->zTo, zDb);
pIdx = 0;
aiCols = 0;
- x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols);
- assert( x==0 );
+ if( pParent ){
+ x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols);
+ assert( x==0 );
+ }
addrOk = sqlite3VdbeMakeLabel(v);
- if( pIdx==0 ){
+ if( pParent && pIdx==0 ){
int iKey = pFK->aCol[0].iFrom;
assert( iKey>=0 && iKey<pTab->nCol );
if( iKey!=pTab->iPKey ){
@@ -94639,13 +96850,15 @@ SQLITE_PRIVATE void sqlite3Pragma(
}else{
for(j=0; j<pFK->nCol; j++){
sqlite3ExprCodeGetColumnOfTable(v, pTab, 0,
- aiCols ? aiCols[j] : pFK->aCol[0].iFrom, regRow+j);
+ aiCols ? aiCols[j] : pFK->aCol[j].iFrom, regRow+j);
sqlite3VdbeAddOp2(v, OP_IsNull, regRow+j, addrOk);
}
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regRow, pFK->nCol, regKey);
- sqlite3VdbeChangeP4(v, -1,
- sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT);
- sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0);
+ if( pParent ){
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regRow, pFK->nCol, regKey);
+ sqlite3VdbeChangeP4(v, -1,
+ sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT);
+ sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0);
+ }
}
sqlite3VdbeAddOp2(v, OP_Rowid, 0, regResult+1);
sqlite3VdbeAddOp4(v, OP_String8, 0, regResult+2, 0,
@@ -94658,12 +96871,13 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeAddOp2(v, OP_Next, 0, addrTop+1);
sqlite3VdbeJumpHere(v, addrTop);
}
- }else
+ }
+ break;
#endif /* !defined(SQLITE_OMIT_TRIGGER) */
#endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */
#ifndef NDEBUG
- if( sqlite3StrICmp(zLeft, "parser_trace")==0 ){
+ case PragTyp_PARSER_TRACE: {
if( zRight ){
if( sqlite3GetBoolean(zRight, 0) ){
sqlite3ParserTrace(stderr, "parser: ");
@@ -94671,30 +96885,30 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3ParserTrace(0, 0);
}
}
- }else
+ }
+ break;
#endif
/* Reinstall the LIKE and GLOB functions. The variant of LIKE
** used will be case sensitive or not depending on the RHS.
*/
- if( sqlite3StrICmp(zLeft, "case_sensitive_like")==0 ){
+ case PragTyp_CASE_SENSITIVE_LIKE: {
if( zRight ){
sqlite3RegisterLikeFunctions(db, sqlite3GetBoolean(zRight, 0));
}
- }else
+ }
+ break;
#ifndef SQLITE_INTEGRITY_CHECK_ERROR_MAX
# define SQLITE_INTEGRITY_CHECK_ERROR_MAX 100
#endif
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
- /* Pragma "quick_check" is an experimental reduced version of
+ /* Pragma "quick_check" is reduced version of
** integrity_check designed to detect most database corruption
** without most of the overhead of a full integrity-check.
*/
- if( sqlite3StrICmp(zLeft, "integrity_check")==0
- || sqlite3StrICmp(zLeft, "quick_check")==0
- ){
+ case PragTyp_INTEGRITY_CHECK: {
int i, j, addr, mxErr;
/* Code that appears at the end of the integrity check. If no error
@@ -94724,7 +96938,6 @@ SQLITE_PRIVATE void sqlite3Pragma(
if( pId2->z==0 ) iDb = -1;
/* Initialize the VDBE program */
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
pParse->nMem = 6;
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "integrity_check", SQLITE_STATIC);
@@ -94772,9 +96985,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
/* Make sure sufficient number of registers have been allocated */
- if( pParse->nMem < cnt+4 ){
- pParse->nMem = cnt+4;
- }
+ pParse->nMem = MAX( pParse->nMem, cnt+7 );
/* Do the b-tree integrity checks */
sqlite3VdbeAddOp3(v, OP_IntegrityCk, 2, cnt, 1);
@@ -94799,12 +97010,15 @@ SQLITE_PRIVATE void sqlite3Pragma(
addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1); /* Stop if out of errors */
sqlite3VdbeAddOp2(v, OP_Halt, 0, 0);
sqlite3VdbeJumpHere(v, addr);
+ sqlite3ExprCacheClear(pParse);
sqlite3OpenTableAndIndices(pParse, pTab, 1, OP_OpenRead);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, 2); /* reg(2) will count entries */
- loopTop = sqlite3VdbeAddOp2(v, OP_Rewind, 1, 0);
- sqlite3VdbeAddOp2(v, OP_AddImm, 2, 1); /* increment entry count */
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
- int jmp2;
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, 7+j); /* index entries counter */
+ }
+ pParse->nMem = MAX(pParse->nMem, 7+j);
+ loopTop = sqlite3VdbeAddOp2(v, OP_Rewind, 1, 0) + 1;
+ for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
+ int jmp2, jmp3;
int r1;
static const VdbeOpList idxErr[] = {
{ OP_AddImm, 1, -1, 0},
@@ -94819,7 +97033,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
{ OP_IfPos, 1, 0, 0}, /* 9 */
{ OP_Halt, 0, 0, 0},
};
- r1 = sqlite3GenerateIndexKey(pParse, pIdx, 1, 3, 0);
+ r1 = sqlite3GenerateIndexKey(pParse, pIdx, 1, 3, 0, &jmp3);
+ sqlite3VdbeAddOp2(v, OP_AddImm, 7+j, 1); /* increment entry count */
jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, j+2, 0, r1, pIdx->nColumn+1);
addr = sqlite3VdbeAddOpList(v, ArraySize(idxErr), idxErr);
sqlite3VdbeChangeP4(v, addr+1, "rowid ", P4_STATIC);
@@ -94827,42 +97042,33 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeChangeP4(v, addr+4, pIdx->zName, P4_TRANSIENT);
sqlite3VdbeJumpHere(v, addr+9);
sqlite3VdbeJumpHere(v, jmp2);
+ sqlite3VdbeResolveLabel(v, jmp3);
}
- sqlite3VdbeAddOp2(v, OP_Next, 1, loopTop+1);
- sqlite3VdbeJumpHere(v, loopTop);
+ sqlite3VdbeAddOp2(v, OP_Next, 1, loopTop);
+ sqlite3VdbeJumpHere(v, loopTop-1);
+#ifndef SQLITE_OMIT_BTREECOUNT
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0,
+ "wrong # of entries in index ", P4_STATIC);
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
- static const VdbeOpList cntIdx[] = {
- { OP_Integer, 0, 3, 0},
- { OP_Rewind, 0, 0, 0}, /* 1 */
- { OP_AddImm, 3, 1, 0},
- { OP_Next, 0, 0, 0}, /* 3 */
- { OP_Eq, 2, 0, 3}, /* 4 */
- { OP_AddImm, 1, -1, 0},
- { OP_String8, 0, 2, 0}, /* 6 */
- { OP_String8, 0, 3, 0}, /* 7 */
- { OP_Concat, 3, 2, 2},
- { OP_ResultRow, 2, 1, 0},
- };
- addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1);
+ addr = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp2(v, OP_IfPos, 1, addr+2);
sqlite3VdbeAddOp2(v, OP_Halt, 0, 0);
- sqlite3VdbeJumpHere(v, addr);
- addr = sqlite3VdbeAddOpList(v, ArraySize(cntIdx), cntIdx);
- sqlite3VdbeChangeP1(v, addr+1, j+2);
- sqlite3VdbeChangeP2(v, addr+1, addr+4);
- sqlite3VdbeChangeP1(v, addr+3, j+2);
- sqlite3VdbeChangeP2(v, addr+3, addr+2);
- sqlite3VdbeJumpHere(v, addr+4);
- sqlite3VdbeChangeP4(v, addr+6,
- "wrong # of entries in index ", P4_STATIC);
- sqlite3VdbeChangeP4(v, addr+7, pIdx->zName, P4_TRANSIENT);
+ sqlite3VdbeAddOp2(v, OP_Count, j+2, 3);
+ sqlite3VdbeAddOp3(v, OP_Eq, 7+j, addr+8, 3);
+ sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pIdx->zName, P4_TRANSIENT);
+ sqlite3VdbeAddOp3(v, OP_Concat, 3, 2, 7);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 7, 1);
}
+#endif /* SQLITE_OMIT_BTREECOUNT */
}
}
addr = sqlite3VdbeAddOpList(v, ArraySize(endCode), endCode);
sqlite3VdbeChangeP2(v, addr, -mxErr);
sqlite3VdbeJumpHere(v, addr+1);
sqlite3VdbeChangeP4(v, addr+2, "ok", P4_STATIC);
- }else
+ }
+ break;
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
#ifndef SQLITE_OMIT_UTF16
@@ -94888,7 +97094,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** new database files created using this database handle. It is only
** useful if invoked immediately after the main database i
*/
- if( sqlite3StrICmp(zLeft, "encoding")==0 ){
+ case PragTyp_ENCODING: {
static const struct EncName {
char *zName;
u8 enc;
@@ -94935,7 +97141,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
}
}
- }else
+ }
+ break;
#endif /* SQLITE_OMIT_UTF16 */
#ifndef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS
@@ -94969,11 +97176,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** The user-version is not used internally by SQLite. It may be used by
** applications for any purpose.
*/
- if( sqlite3StrICmp(zLeft, "schema_version")==0
- || sqlite3StrICmp(zLeft, "user_version")==0
- || sqlite3StrICmp(zLeft, "freelist_count")==0
- || sqlite3StrICmp(zLeft, "application_id")==0
- ){
+ case PragTyp_HEADER_VALUE: {
int iCookie; /* Cookie index. 1 for schema-cookie, 6 for user-cookie. */
sqlite3VdbeUsesBtree(v, iDb);
switch( zLeft[0] ){
@@ -95017,7 +97220,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, SQLITE_TRANSIENT);
}
- }else
+ }
+ break;
#endif /* SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS */
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
@@ -95027,7 +97231,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** Return the names of all compile-time options used in this build,
** one option per row.
*/
- if( sqlite3StrICmp(zLeft, "compile_options")==0 ){
+ case PragTyp_COMPILE_OPTIONS: {
int i = 0;
const char *zOpt;
sqlite3VdbeSetNumCols(v, 1);
@@ -95037,7 +97241,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, zOpt, 0);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
}
- }else
+ }
+ break;
#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
#ifndef SQLITE_OMIT_WAL
@@ -95046,7 +97251,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
**
** Checkpoint the database.
*/
- if( sqlite3StrICmp(zLeft, "wal_checkpoint")==0 ){
+ case PragTyp_WAL_CHECKPOINT: {
int iBt = (pId2->z?iDb:SQLITE_MAX_ATTACHED);
int eMode = SQLITE_CHECKPOINT_PASSIVE;
if( zRight ){
@@ -95056,7 +97261,6 @@ SQLITE_PRIVATE void sqlite3Pragma(
eMode = SQLITE_CHECKPOINT_RESTART;
}
}
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
sqlite3VdbeSetNumCols(v, 3);
pParse->nMem = 3;
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "busy", SQLITE_STATIC);
@@ -95065,7 +97269,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeAddOp3(v, OP_Checkpoint, iBt, eMode, 1);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
- }else
+ }
+ break;
/*
** PRAGMA wal_autocheckpoint
@@ -95075,14 +97280,15 @@ SQLITE_PRIVATE void sqlite3Pragma(
** after accumulating N frames in the log. Or query for the current value
** of N.
*/
- if( sqlite3StrICmp(zLeft, "wal_autocheckpoint")==0 ){
+ case PragTyp_WAL_AUTOCHECKPOINT: {
if( zRight ){
sqlite3_wal_autocheckpoint(db, sqlite3Atoi(zRight));
}
returnSingleInt(pParse, "wal_autocheckpoint",
db->xWalCallback==sqlite3WalDefaultHook ?
SQLITE_PTR_TO_INT(db->pWalArg) : 0);
- }else
+ }
+ break;
#endif
/*
@@ -95091,9 +97297,10 @@ SQLITE_PRIVATE void sqlite3Pragma(
** This pragma attempts to free as much memory as possible from the
** current database connection.
*/
- if( sqlite3StrICmp(zLeft, "shrink_memory")==0 ){
+ case PragTyp_SHRINK_MEMORY: {
sqlite3_db_release_memory(db);
- }else
+ break;
+ }
/*
** PRAGMA busy_timeout
@@ -95104,18 +97311,36 @@ SQLITE_PRIVATE void sqlite3Pragma(
** then 0 is returned. Setting the busy_timeout to 0 or negative
** disables the timeout.
*/
- if( sqlite3StrICmp(zLeft, "busy_timeout")==0 ){
+ /*case PragTyp_BUSY_TIMEOUT*/ default: {
+ assert( aPragmaNames[mid].ePragTyp==PragTyp_BUSY_TIMEOUT );
if( zRight ){
sqlite3_busy_timeout(db, sqlite3Atoi(zRight));
}
returnSingleInt(pParse, "timeout", db->busyTimeout);
- }else
+ break;
+ }
+
+ /*
+ ** PRAGMA soft_heap_limit
+ ** PRAGMA soft_heap_limit = N
+ **
+ ** Call sqlite3_soft_heap_limit64(N). Return the result. If N is omitted,
+ ** use -1.
+ */
+ case PragTyp_SOFT_HEAP_LIMIT: {
+ sqlite3_int64 N;
+ if( zRight && sqlite3Atoi64(zRight, &N, 1000000, SQLITE_UTF8)==SQLITE_OK ){
+ sqlite3_soft_heap_limit64(N);
+ }
+ returnSingleInt(pParse, "soft_heap_limit", sqlite3_soft_heap_limit64(-1));
+ break;
+ }
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
/*
** Report the current state of file logs for all databases
*/
- if( sqlite3StrICmp(zLeft, "lock_status")==0 ){
+ case PragTyp_LOCK_STATUS: {
static const char *const azLockName[] = {
"unlocked", "shared", "reserved", "pending", "exclusive"
};
@@ -95140,35 +97365,39 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, zState, P4_STATIC);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 2);
}
-
- }else
+ break;
+ }
#endif
#ifdef SQLITE_HAS_CODEC
- if( sqlite3StrICmp(zLeft, "key")==0 && zRight ){
- sqlite3_key(db, zRight, sqlite3Strlen30(zRight));
- }else
- if( sqlite3StrICmp(zLeft, "rekey")==0 && zRight ){
- sqlite3_rekey(db, zRight, sqlite3Strlen30(zRight));
- }else
- if( zRight && (sqlite3StrICmp(zLeft, "hexkey")==0 ||
- sqlite3StrICmp(zLeft, "hexrekey")==0) ){
- int i, h1, h2;
- char zKey[40];
- for(i=0; (h1 = zRight[i])!=0 && (h2 = zRight[i+1])!=0; i+=2){
- h1 += 9*(1&(h1>>6));
- h2 += 9*(1&(h2>>6));
- zKey[i/2] = (h2 & 0x0f) | ((h1 & 0xf)<<4);
- }
- if( (zLeft[3] & 0xf)==0xb ){
- sqlite3_key(db, zKey, i/2);
- }else{
- sqlite3_rekey(db, zKey, i/2);
+ 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));
+ break;
+ }
+ case PragTyp_HEXKEY: {
+ if( zRight ){
+ u8 iByte;
+ int i;
+ char zKey[40];
+ for(i=0, iByte=0; i<sizeof(zKey)*2 && sqlite3Isxdigit(zRight[i]); i++){
+ iByte = (iByte<<4) + sqlite3HexToInt(zRight[i]);
+ if( (i&1)!=0 ) zKey[i/2] = iByte;
+ }
+ if( (zLeft[3] & 0xf)==0xb ){
+ sqlite3_key_v2(db, zDb, zKey, i/2);
+ }else{
+ sqlite3_rekey_v2(db, zDb, zKey, i/2);
+ }
}
- }else
+ break;
+ }
#endif
#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
- if( sqlite3StrICmp(zLeft, "activate_extensions")==0 && zRight ){
+ case PragTyp_ACTIVATE_EXTENSIONS: if( zRight ){
#ifdef SQLITE_HAS_CODEC
if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){
sqlite3_activate_see(&zRight[4]);
@@ -95179,23 +97408,12 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3_activate_cerod(&zRight[6]);
}
#endif
- }else
+ }
+ break;
#endif
-
- {/* Empty ELSE clause */}
+ } /* End of the PRAGMA switch */
- /*
- ** Reset the safety level, in case the fullfsync flag or synchronous
- ** setting changed.
- */
-#ifndef SQLITE_OMIT_PAGER_PRAGMAS
- if( db->autoCommit ){
- sqlite3BtreeSetSafetyLevel(pDb->pBt, pDb->safety_level,
- (db->flags&SQLITE_FullFSync)!=0,
- (db->flags&SQLITE_CkptFullFSync)!=0);
- }
-#endif
pragma_out:
sqlite3DbFree(db, zLeft);
sqlite3DbFree(db, zRight);
@@ -95798,7 +98016,7 @@ static int sqlite3Prepare(
sqlite3VtabUnlockList(db);
pParse->db = db;
- pParse->nQueryLoop = (double)1;
+ pParse->nQueryLoop = 0; /* Logarithmic, so 0 really means 1 */
if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){
char *zSqlCopy;
int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
@@ -95820,7 +98038,7 @@ static int sqlite3Prepare(
}else{
sqlite3RunParser(pParse, zSql, &zErrMsg);
}
- assert( 1==(int)pParse->nQueryLoop );
+ assert( 0==pParse->nQueryLoop );
if( db->mallocFailed ){
pParse->rc = SQLITE_NOMEM;
@@ -96016,6 +98234,12 @@ static int sqlite3Prepare16(
if( !sqlite3SafetyCheckOk(db) ){
return SQLITE_MISUSE_BKPT;
}
+ if( nBytes>=0 ){
+ int sz;
+ const char *z = (const char*)zSql;
+ for(sz=0; sz<nBytes && (z[sz]!=0 || z[sz+1]!=0); sz += 2){}
+ nBytes = sz;
+ }
sqlite3_mutex_enter(db->mutex);
zSql8 = sqlite3Utf16to8(db, zSql, nBytes, SQLITE_UTF16NATIVE);
if( zSql8 ){
@@ -96184,7 +98408,7 @@ SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){
}
/*
-** Given 1 to 3 identifiers preceeding the JOIN keyword, determine the
+** Given 1 to 3 identifiers preceding the JOIN keyword, determine the
** type of join. Return an integer constant that expresses that type
** in terms of the following bit values:
**
@@ -96339,8 +98563,8 @@ static void addWhereTerm(
pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2, 0);
if( pEq && isOuterJoin ){
ExprSetProperty(pEq, EP_FromJoin);
- assert( !ExprHasAnyProperty(pEq, EP_TokenOnly|EP_Reduced) );
- ExprSetIrreducible(pEq);
+ assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) );
+ ExprSetVVAProperty(pEq, EP_NoReduce);
pEq->iRightJoinTable = (i16)pE2->iTable;
}
*ppWhere = sqlite3ExprAnd(db, *ppWhere, pEq);
@@ -96375,8 +98599,8 @@ static void addWhereTerm(
static void setJoinExpr(Expr *p, int iTable){
while( p ){
ExprSetProperty(p, EP_FromJoin);
- assert( !ExprHasAnyProperty(p, EP_TokenOnly|EP_Reduced) );
- ExprSetIrreducible(p);
+ assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
+ ExprSetVVAProperty(p, EP_NoReduce);
p->iRightJoinTable = (i16)iTable;
setJoinExpr(p->pLeft, iTable);
p = p->pRight;
@@ -96878,6 +99102,25 @@ static void selectInnerLoop(
}
/*
+** Allocate a KeyInfo object sufficient for an index of N columns.
+**
+** Actually, always allocate one extra column for the rowid at the end
+** of the index. So the KeyInfo returned will have space sufficient for
+** N+1 columns.
+*/
+SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N){
+ KeyInfo *p = sqlite3DbMallocZero(db,
+ sizeof(KeyInfo) + (N+1)*(sizeof(CollSeq*)+1));
+ if( p ){
+ p->aSortOrder = (u8*)&p->aColl[N+1];
+ p->nField = (u16)N;
+ p->enc = ENC(db);
+ p->db = db;
+ }
+ return p;
+}
+
+/*
** Given an expression list, generate a KeyInfo structure that records
** the collating sequence for each expression in that expression list.
**
@@ -96893,25 +99136,19 @@ static void selectInnerLoop(
** P4_KEYINFO_HANDOFF is the usual way of dealing with this.
*/
static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList){
- sqlite3 *db = pParse->db;
int nExpr;
KeyInfo *pInfo;
struct ExprList_item *pItem;
+ sqlite3 *db = pParse->db;
int i;
nExpr = pList->nExpr;
- pInfo = sqlite3DbMallocZero(db, sizeof(*pInfo) + nExpr*(sizeof(CollSeq*)+1) );
+ pInfo = sqlite3KeyInfoAlloc(db, nExpr);
if( pInfo ){
- pInfo->aSortOrder = (u8*)&pInfo->aColl[nExpr];
- pInfo->nField = (u16)nExpr;
- pInfo->enc = ENC(db);
- pInfo->db = db;
for(i=0, pItem=pList->a; i<nExpr; i++, pItem++){
CollSeq *pColl;
pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr);
- if( !pColl ){
- pColl = db->pDfltColl;
- }
+ if( !pColl ) pColl = db->pDfltColl;
pInfo->aColl[i] = pColl;
pInfo->aSortOrder[i] = pItem->sortOrder;
}
@@ -97123,6 +99360,9 @@ static void generateSortTail(
** Return a pointer to a string containing the 'declaration type' of the
** expression pExpr. The string may be treated as static by the caller.
**
+** Also try to estimate the size of the returned value and return that
+** result in *pEstWidth.
+**
** The declaration type is the exact datatype definition extracted from the
** original CREATE TABLE statement if the expression is a column. The
** declaration type for a ROWID field is INTEGER. Exactly when an expression
@@ -97136,21 +99376,36 @@ static void generateSortTail(
** SELECT abc FROM (SELECT col AS abc FROM tbl);
**
** The declaration type for any expression other than a column is NULL.
+**
+** This routine has either 3 or 6 parameters depending on whether or not
+** the SQLITE_ENABLE_COLUMN_METADATA compile-time option is used.
*/
-static const char *columnType(
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
+# define columnType(A,B,C,D,E,F) columnTypeImpl(A,B,C,D,E,F)
+static const char *columnTypeImpl(
+ NameContext *pNC,
+ Expr *pExpr,
+ const char **pzOrigDb,
+ const char **pzOrigTab,
+ const char **pzOrigCol,
+ u8 *pEstWidth
+){
+ char const *zOrigDb = 0;
+ char const *zOrigTab = 0;
+ char const *zOrigCol = 0;
+#else /* if !defined(SQLITE_ENABLE_COLUMN_METADATA) */
+# define columnType(A,B,C,D,E,F) columnTypeImpl(A,B,F)
+static const char *columnTypeImpl(
NameContext *pNC,
Expr *pExpr,
- const char **pzOriginDb,
- const char **pzOriginTab,
- const char **pzOriginCol
+ u8 *pEstWidth
){
+#endif /* !defined(SQLITE_ENABLE_COLUMN_METADATA) */
char const *zType = 0;
- char const *zOriginDb = 0;
- char const *zOriginTab = 0;
- char const *zOriginCol = 0;
int j;
- if( NEVER(pExpr==0) || pNC->pSrcList==0 ) return 0;
+ u8 estWidth = 1;
+ if( NEVER(pExpr==0) || pNC->pSrcList==0 ) return 0;
switch( pExpr->op ){
case TK_AGG_COLUMN:
case TK_COLUMN: {
@@ -97211,25 +99466,35 @@ static const char *columnType(
sNC.pSrcList = pS->pSrc;
sNC.pNext = pNC;
sNC.pParse = pNC->pParse;
- zType = columnType(&sNC, p, &zOriginDb, &zOriginTab, &zOriginCol);
+ zType = columnType(&sNC, p,&zOrigDb,&zOrigTab,&zOrigCol, &estWidth);
}
}else if( ALWAYS(pTab->pSchema) ){
/* A real table */
assert( !pS );
if( iCol<0 ) iCol = pTab->iPKey;
assert( iCol==-1 || (iCol>=0 && iCol<pTab->nCol) );
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
if( iCol<0 ){
zType = "INTEGER";
- zOriginCol = "rowid";
+ zOrigCol = "rowid";
}else{
zType = pTab->aCol[iCol].zType;
- zOriginCol = pTab->aCol[iCol].zName;
+ zOrigCol = pTab->aCol[iCol].zName;
+ estWidth = pTab->aCol[iCol].szEst;
}
- zOriginTab = pTab->zName;
+ zOrigTab = pTab->zName;
if( pNC->pParse ){
int iDb = sqlite3SchemaToIndex(pNC->pParse->db, pTab->pSchema);
- zOriginDb = pNC->pParse->db->aDb[iDb].zName;
+ zOrigDb = pNC->pParse->db->aDb[iDb].zName;
+ }
+#else
+ if( iCol<0 ){
+ zType = "INTEGER";
+ }else{
+ zType = pTab->aCol[iCol].zType;
+ estWidth = pTab->aCol[iCol].szEst;
}
+#endif
}
break;
}
@@ -97246,18 +99511,21 @@ static const char *columnType(
sNC.pSrcList = pS->pSrc;
sNC.pNext = pNC;
sNC.pParse = pNC->pParse;
- zType = columnType(&sNC, p, &zOriginDb, &zOriginTab, &zOriginCol);
+ zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol, &estWidth);
break;
}
#endif
}
-
- if( pzOriginDb ){
- assert( pzOriginTab && pzOriginCol );
- *pzOriginDb = zOriginDb;
- *pzOriginTab = zOriginTab;
- *pzOriginCol = zOriginCol;
+
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
+ if( pzOrigDb ){
+ assert( pzOrigTab && pzOrigCol );
+ *pzOrigDb = zOrigDb;
+ *pzOrigTab = zOrigTab;
+ *pzOrigCol = zOrigCol;
}
+#endif
+ if( pEstWidth ) *pEstWidth = estWidth;
return zType;
}
@@ -97283,7 +99551,7 @@ static void generateColumnTypes(
const char *zOrigDb = 0;
const char *zOrigTab = 0;
const char *zOrigCol = 0;
- zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol);
+ zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol, 0);
/* The vdbe must make its own copy of the column-type and other
** column specific strings, in case the schema is reset before this
@@ -97293,11 +99561,11 @@ static void generateColumnTypes(
sqlite3VdbeSetColName(v, i, COLNAME_TABLE, zOrigTab, SQLITE_TRANSIENT);
sqlite3VdbeSetColName(v, i, COLNAME_COLUMN, zOrigCol, SQLITE_TRANSIENT);
#else
- zType = columnType(&sNC, p, 0, 0, 0);
+ zType = columnType(&sNC, p, 0, 0, 0, 0);
#endif
sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, zType, SQLITE_TRANSIENT);
}
-#endif /* SQLITE_OMIT_DECLTYPE */
+#endif /* !defined(SQLITE_OMIT_DECLTYPE) */
}
/*
@@ -97486,8 +99754,7 @@ static int selectColumnsFromExprList(
*/
static void selectAddColumnTypeAndCollation(
Parse *pParse, /* Parsing contexts */
- int nCol, /* Number of columns */
- Column *aCol, /* List of columns */
+ Table *pTab, /* Add column type information to this table */
Select *pSelect /* SELECT used to determine types and collations */
){
sqlite3 *db = pParse->db;
@@ -97497,17 +99764,19 @@ static void selectAddColumnTypeAndCollation(
int i;
Expr *p;
struct ExprList_item *a;
+ u64 szAll = 0;
assert( pSelect!=0 );
assert( (pSelect->selFlags & SF_Resolved)!=0 );
- assert( nCol==pSelect->pEList->nExpr || db->mallocFailed );
+ assert( pTab->nCol==pSelect->pEList->nExpr || db->mallocFailed );
if( db->mallocFailed ) return;
memset(&sNC, 0, sizeof(sNC));
sNC.pSrcList = pSelect->pSrc;
a = pSelect->pEList->a;
- for(i=0, pCol=aCol; i<nCol; i++, pCol++){
+ for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
p = a[i].pExpr;
- pCol->zType = sqlite3DbStrDup(db, columnType(&sNC, p, 0, 0, 0));
+ pCol->zType = sqlite3DbStrDup(db, columnType(&sNC, p,0,0,0, &pCol->szEst));
+ szAll += pCol->szEst;
pCol->affinity = sqlite3ExprAffinity(p);
if( pCol->affinity==0 ) pCol->affinity = SQLITE_AFF_NONE;
pColl = sqlite3ExprCollSeq(pParse, p);
@@ -97515,6 +99784,7 @@ static void selectAddColumnTypeAndCollation(
pCol->zColl = sqlite3DbStrDup(db, pColl->zName);
}
}
+ pTab->szTabRow = sqlite3LogEst(szAll*4);
}
/*
@@ -97542,9 +99812,9 @@ SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
assert( db->lookaside.bEnabled==0 );
pTab->nRef = 1;
pTab->zName = 0;
- pTab->nRowEst = 1000000;
+ pTab->nRowEst = 1048576;
selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
- selectAddColumnTypeAndCollation(pParse, pTab->nCol, pTab->aCol, pSelect);
+ selectAddColumnTypeAndCollation(pParse, pTab, pSelect);
pTab->iPKey = -1;
if( db->mallocFailed ){
sqlite3DeleteTable(db, pTab);
@@ -97598,7 +99868,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
/*
** "LIMIT -1" always shows all rows. There is some
- ** contraversy about what the correct behavior should be.
+ ** controversy about what the correct behavior should be.
** The current implementation interprets "LIMIT 0" to mean
** no rows.
*/
@@ -97613,8 +99883,8 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
VdbeComment((v, "LIMIT counter"));
if( n==0 ){
sqlite3VdbeAddOp2(v, OP_Goto, 0, iBreak);
- }else{
- if( p->nSelectRow > (double)n ) p->nSelectRow = (double)n;
+ }else if( n>=0 && p->nSelectRow>(u64)n ){
+ p->nSelectRow = n;
}
}else{
sqlite3ExprCode(pParse, p->pLimit, iLimit);
@@ -97808,9 +100078,9 @@ static int multiSelect(
p->nSelectRow += pPrior->nSelectRow;
if( pPrior->pLimit
&& sqlite3ExprIsInteger(pPrior->pLimit, &nLimit)
- && p->nSelectRow > (double)nLimit
+ && nLimit>0 && p->nSelectRow > (u64)nLimit
){
- p->nSelectRow = (double)nLimit;
+ p->nSelectRow = nLimit;
}
if( addr ){
sqlite3VdbeJumpHere(v, addr);
@@ -98017,23 +100287,17 @@ static int multiSelect(
assert( p->pRightmost==p );
nCol = p->pEList->nExpr;
- pKeyInfo = sqlite3DbMallocZero(db,
- sizeof(*pKeyInfo)+nCol*(sizeof(CollSeq*) + 1));
+ pKeyInfo = sqlite3KeyInfoAlloc(db, nCol);
if( !pKeyInfo ){
rc = SQLITE_NOMEM;
goto multi_select_end;
}
-
- pKeyInfo->enc = ENC(db);
- pKeyInfo->nField = (u16)nCol;
-
for(i=0, apColl=pKeyInfo->aColl; i<nCol; i++, apColl++){
*apColl = multiSelectCollSeq(pParse, p, i);
if( 0==*apColl ){
*apColl = db->pDfltColl;
}
}
- pKeyInfo->aSortOrder = (u8*)apColl;
for(pLoop=p; pLoop; pLoop=pLoop->pPrior){
for(i=0; i<2; i++){
@@ -98402,12 +100666,8 @@ static int multiSelectOrderBy(
assert( pItem->iOrderByCol>0 && pItem->iOrderByCol<=p->pEList->nExpr );
aPermute[i] = pItem->iOrderByCol - 1;
}
- pKeyMerge =
- sqlite3DbMallocRaw(db, sizeof(*pKeyMerge)+nOrderBy*(sizeof(CollSeq*)+1));
+ pKeyMerge = sqlite3KeyInfoAlloc(db, nOrderBy);
if( pKeyMerge ){
- pKeyMerge->aSortOrder = (u8*)&pKeyMerge->aColl[nOrderBy];
- pKeyMerge->nField = (u16)nOrderBy;
- pKeyMerge->enc = ENC(db);
for(i=0; i<nOrderBy; i++){
CollSeq *pColl;
Expr *pTerm = pOrderBy->a[i].pExpr;
@@ -98444,12 +100704,8 @@ static int multiSelectOrderBy(
regPrev = pParse->nMem+1;
pParse->nMem += nExpr+1;
sqlite3VdbeAddOp2(v, OP_Integer, 0, regPrev);
- pKeyDup = sqlite3DbMallocZero(db,
- sizeof(*pKeyDup) + nExpr*(sizeof(CollSeq*)+1) );
+ pKeyDup = sqlite3KeyInfoAlloc(db, nExpr);
if( pKeyDup ){
- pKeyDup->aSortOrder = (u8*)&pKeyDup->aColl[nExpr];
- pKeyDup->nField = (u16)nExpr;
- pKeyDup->enc = ENC(db);
for(i=0; i<nExpr; i++){
pKeyDup->aColl[i] = multiSelectCollSeq(pParse, p, i);
pKeyDup->aSortOrder[i] = 0;
@@ -99304,7 +101560,7 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
if( IsVirtual(pTab) ) return 0;
if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
if( NEVER(pAggInfo->nFunc==0) ) return 0;
- if( (pAggInfo->aFunc[0].pFunc->flags&SQLITE_FUNC_COUNT)==0 ) return 0;
+ if( (pAggInfo->aFunc[0].pFunc->funcFlags&SQLITE_FUNC_COUNT)==0 ) return 0;
if( pExpr->flags&EP_Distinct ) return 0;
return pTab;
@@ -99470,11 +101726,11 @@ static int selectExpander(Walker *pWalker, Select *p){
pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
if( pTab==0 ) return WRC_Abort;
pTab->nRef = 1;
- pTab->zName = sqlite3MPrintf(db, "sqlite_subquery_%p_", (void*)pTab);
+ pTab->zName = sqlite3MPrintf(db, "sqlite_sq_%p", (void*)pTab);
while( pSel->pPrior ){ pSel = pSel->pPrior; }
selectColumnsFromExprList(pParse, pSel->pEList, &pTab->nCol, &pTab->aCol);
pTab->iPKey = -1;
- pTab->nRowEst = 1000000;
+ pTab->nRowEst = 1048576;
pTab->tabFlags |= TF_Ephemeral;
#endif
}else{
@@ -99715,10 +101971,12 @@ static int exprWalkNoop(Walker *NotUsed, Expr *NotUsed2){
static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){
Walker w;
memset(&w, 0, sizeof(w));
- w.xSelectCallback = convertCompoundSelectToSubquery;
w.xExprCallback = exprWalkNoop;
w.pParse = pParse;
- sqlite3WalkSelect(&w, pSelect);
+ if( pParse->hasCompound ){
+ w.xSelectCallback = convertCompoundSelectToSubquery;
+ sqlite3WalkSelect(&w, pSelect);
+ }
w.xSelectCallback = selectExpander;
sqlite3WalkSelect(&w, pSelect);
}
@@ -99756,7 +102014,7 @@ static int selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
Select *pSel = pFrom->pSelect;
assert( pSel );
while( pSel->pPrior ) pSel = pSel->pPrior;
- selectAddColumnTypeAndCollation(pParse, pTab->nCol, pTab->aCol, pSel);
+ selectAddColumnTypeAndCollation(pParse, pTab, pSel);
}
}
}
@@ -99899,7 +102157,7 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
assert( nArg==1 );
codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg);
}
- if( pF->pFunc->flags & SQLITE_FUNC_NEEDCOLL ){
+ if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
CollSeq *pColl = 0;
struct ExprList_item *pItem;
int j;
@@ -99959,11 +102217,10 @@ static void explainSimpleCount(
Index *pIdx /* Index used to optimize scan, or NULL */
){
if( pParse->explain==2 ){
- char *zEqp = sqlite3MPrintf(pParse->db, "SCAN TABLE %s %s%s(~%d rows)",
+ char *zEqp = sqlite3MPrintf(pParse->db, "SCAN TABLE %s%s%s",
pTab->zName,
- pIdx ? "USING COVERING INDEX " : "",
- pIdx ? pIdx->zName : "",
- pTab->nRowEst
+ pIdx ? " USING COVERING INDEX " : "",
+ pIdx ? pIdx->zName : ""
);
sqlite3VdbeAddOp4(
pParse->pVdbe, OP_Explain, pParse->iSelectId, 0, 0, zEqp, P4_DYNAMIC
@@ -100121,7 +102378,7 @@ SQLITE_PRIVATE int sqlite3Select(
}
/* Increment Parse.nHeight by the height of the largest expression
- ** tree refered to by this, the parent select. The child select
+ ** tree referred to by this, the parent select. The child select
** may contain expression trees of at most
** (SQLITE_MAX_EXPR_DEPTH-Parse.nHeight) height. This is a bit
** more conservative than necessary, but much easier than enforcing
@@ -100253,7 +102510,7 @@ SQLITE_PRIVATE int sqlite3Select(
** Use the SQLITE_GroupByOrder flag with SQLITE_TESTCTRL_OPTIMIZER
** to disable this optimization for testing purposes.
*/
- if( sqlite3ExprListCompare(p->pGroupBy, pOrderBy)==0
+ if( sqlite3ExprListCompare(p->pGroupBy, pOrderBy, -1)==0
&& OptimizationEnabled(db, SQLITE_GroupByOrder) ){
pOrderBy = 0;
}
@@ -100274,7 +102531,7 @@ SQLITE_PRIVATE int sqlite3Select(
** BY and DISTINCT, and an index or separate temp-table for the other.
*/
if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct
- && sqlite3ExprListCompare(pOrderBy, p->pEList)==0
+ && sqlite3ExprListCompare(pOrderBy, p->pEList, -1)==0
){
p->selFlags &= ~SF_Distinct;
p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0);
@@ -100314,7 +102571,7 @@ SQLITE_PRIVATE int sqlite3Select(
/* Set the limiter.
*/
iEnd = sqlite3VdbeMakeLabel(v);
- p->nSelectRow = (double)LARGEST_INT64;
+ p->nSelectRow = LARGEST_INT64;
computeLimitRegisters(pParse, p, iEnd);
if( p->iLimit==0 && addrSortIndex>=0 ){
sqlite3VdbeGetOp(v, addrSortIndex)->opcode = OP_SorterOpen;
@@ -100337,14 +102594,19 @@ SQLITE_PRIVATE int sqlite3Select(
if( !isAgg && pGroupBy==0 ){
/* No aggregate functions and no GROUP BY clause */
- ExprList *pDist = (sDistinct.isTnct ? p->pEList : 0);
+ u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0);
/* Begin the database scan. */
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pOrderBy, pDist, 0,0);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pOrderBy, p->pEList,
+ wctrlFlags, 0);
if( pWInfo==0 ) goto select_end;
- if( pWInfo->nRowOut < p->nSelectRow ) p->nSelectRow = pWInfo->nRowOut;
- if( pWInfo->eDistinct ) sDistinct.eTnctType = pWInfo->eDistinct;
- if( pOrderBy && pWInfo->nOBSat==pOrderBy->nExpr ) pOrderBy = 0;
+ if( sqlite3WhereOutputRowCount(pWInfo) < p->nSelectRow ){
+ p->nSelectRow = sqlite3WhereOutputRowCount(pWInfo);
+ }
+ if( sDistinct.isTnct && sqlite3WhereIsDistinct(pWInfo) ){
+ sDistinct.eTnctType = sqlite3WhereIsDistinct(pWInfo);
+ }
+ if( pOrderBy && sqlite3WhereIsOrdered(pWInfo) ) pOrderBy = 0;
/* If sorting index that was created by a prior OP_OpenEphemeral
** instruction ended up not being needed, then change the OP_OpenEphemeral
@@ -100357,7 +102619,8 @@ SQLITE_PRIVATE int sqlite3Select(
/* Use the standard inner loop. */
selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, &sDistinct, pDest,
- pWInfo->iContinue, pWInfo->iBreak);
+ sqlite3WhereContinueLabel(pWInfo),
+ sqlite3WhereBreakLabel(pWInfo));
/* End the database scan loop.
*/
@@ -100390,9 +102653,9 @@ SQLITE_PRIVATE int sqlite3Select(
for(k=pGroupBy->nExpr, pItem=pGroupBy->a; k>0; k--, pItem++){
pItem->iAlias = 0;
}
- if( p->nSelectRow>(double)100 ) p->nSelectRow = (double)100;
+ if( p->nSelectRow>100 ) p->nSelectRow = 100;
}else{
- p->nSelectRow = (double)1;
+ p->nSelectRow = 1;
}
@@ -100472,9 +102735,10 @@ SQLITE_PRIVATE int sqlite3Select(
** in the right order to begin with.
*/
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0, 0, 0);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0,
+ WHERE_GROUPBY, 0);
if( pWInfo==0 ) goto select_end;
- if( pWInfo->nOBSat==pGroupBy->nExpr ){
+ if( sqlite3WhereIsOrdered(pWInfo) ){
/* The optimizer is able to deliver rows in group by order so
** we do not have to sort. The OP_OpenEphemeral table will be
** cancelled later because we still need to use the pKeyInfo
@@ -100665,25 +102929,25 @@ SQLITE_PRIVATE int sqlite3Select(
sqlite3CodeVerifySchema(pParse, iDb);
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
- /* Search for the index that has the least amount of columns. If
- ** there is such an index, and it has less columns than the table
- ** does, then we can assume that it consumes less space on disk and
- ** will therefore be cheaper to scan to determine the query result.
- ** In this case set iRoot to the root page number of the index b-tree
- ** and pKeyInfo to the KeyInfo structure required to navigate the
- ** index.
+ /* Search for the index that has the lowest scan cost.
**
** (2011-04-15) Do not do a full scan of an unordered index.
**
+ ** (2013-10-03) Do not count the entires in a partial index.
+ **
** In practice the KeyInfo structure will not be used. It is only
** passed to keep OP_OpenRead happy.
*/
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- if( pIdx->bUnordered==0 && (!pBest || pIdx->nColumn<pBest->nColumn) ){
+ if( pIdx->bUnordered==0
+ && pIdx->szIdxRow<pTab->szTabRow
+ && pIdx->pPartIdxWhere==0
+ && (!pBest || pIdx->szIdxRow<pBest->szIdxRow)
+ ){
pBest = pIdx;
}
}
- if( pBest && pBest->nColumn<pTab->nCol ){
+ if( pBest ){
iRoot = pBest->tnum;
pKeyInfo = sqlite3IndexKeyinfo(pParse, pBest);
}
@@ -100755,8 +103019,8 @@ SQLITE_PRIVATE int sqlite3Select(
}
updateAccumulator(pParse, &sAggInfo);
assert( pMinMax==0 || pMinMax->nExpr==1 );
- if( pWInfo->nOBSat>0 ){
- sqlite3VdbeAddOp2(v, OP_Goto, 0, pWInfo->iBreak);
+ if( sqlite3WhereIsOrdered(pWInfo) ){
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3WhereBreakLabel(pWInfo));
VdbeComment((v, "%s() by index",
(flag==WHERE_ORDERBY_MIN?"min":"max")));
}
@@ -101261,8 +103525,8 @@ SQLITE_PRIVATE void sqlite3BeginTrigger(
/* Ensure the table name matches database name and that the table exists */
if( db->mallocFailed ) goto trigger_cleanup;
assert( pTableName->nSrc==1 );
- if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", pName) &&
- sqlite3FixSrcList(&sFix, pTableName) ){
+ sqlite3FixInit(&sFix, pParse, iDb, "trigger", pName);
+ if( sqlite3FixSrcList(&sFix, pTableName) ){
goto trigger_cleanup;
}
pTab = sqlite3SrcListLookup(pParse, pTableName);
@@ -101404,8 +103668,10 @@ SQLITE_PRIVATE void sqlite3FinishTrigger(
}
nameToken.z = pTrig->zName;
nameToken.n = sqlite3Strlen30(nameToken.z);
- if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", &nameToken)
- && sqlite3FixTriggerStep(&sFix, pTrig->step_list) ){
+ sqlite3FixInit(&sFix, pParse, iDb, "trigger", &nameToken);
+ if( sqlite3FixTriggerStep(&sFix, pTrig->step_list)
+ || sqlite3FixExpr(&sFix, pTrig->pWhen)
+ ){
goto triggerfinish_cleanup;
}
@@ -102115,7 +104381,7 @@ SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect(
/*
** This is called to code the required FOR EACH ROW triggers for an operation
** on table pTab. The operation to code triggers for (INSERT, UPDATE or DELETE)
-** is given by the op paramater. The tr_tm parameter determines whether the
+** is given by the op parameter. The tr_tm parameter determines whether the
** BEFORE or AFTER triggers are coded. If the operation is an UPDATE, then
** parameter pChanges is passed the list of columns being modified.
**
@@ -102309,7 +104575,7 @@ static void updateVirtualTable(
SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){
assert( pTab!=0 );
if( !pTab->pSelect ){
- sqlite3_value *pValue;
+ sqlite3_value *pValue = 0;
u8 enc = ENC(sqlite3VdbeDb(v));
Column *pCol = &pTab->aCol[i];
VdbeComment((v, "%s.%s", pTab->zName, pCol->zName));
@@ -102494,7 +104760,7 @@ SQLITE_PRIVATE void sqlite3Update(
}
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
int reg;
- if( hasFK || chngRowid ){
+ if( hasFK || chngRowid || pIdx->pPartIdxWhere ){
reg = ++pParse->nMem;
}else{
reg = 0;
@@ -102566,7 +104832,7 @@ SQLITE_PRIVATE void sqlite3Update(
pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, 0
);
if( pWInfo==0 ) goto update_cleanup;
- okOnePass = pWInfo->okOnePass;
+ okOnePass = sqlite3WhereOkOnePass(pWInfo);
/* Remember the rowid of every item to be updated.
*/
@@ -102736,7 +105002,7 @@ SQLITE_PRIVATE void sqlite3Update(
/* Do FK constraint checks. */
if( hasFK ){
- sqlite3FkCheck(pParse, pTab, regOldRowid, 0);
+ sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngRowid);
}
/* Delete the index entries associated with the current record. */
@@ -102750,7 +105016,7 @@ SQLITE_PRIVATE void sqlite3Update(
sqlite3VdbeJumpHere(v, j1);
if( hasFK ){
- sqlite3FkCheck(pParse, pTab, 0, regNewRowid);
+ sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngRowid);
}
/* Insert the new index entries and the new record. */
@@ -102760,7 +105026,7 @@ SQLITE_PRIVATE void sqlite3Update(
** handle rows (possibly in other tables) that refer via a foreign key
** to the row just updated. */
if( hasFK ){
- sqlite3FkActions(pParse, pTab, pChanges, regOldRowid);
+ sqlite3FkActions(pParse, pTab, pChanges, regOldRowid, aXRef, chngRowid);
}
}
@@ -102996,14 +105262,34 @@ static int execExecSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
}
/*
-** The non-standard VACUUM command is used to clean up the database,
+** The VACUUM command is used to clean up the database,
** collapse free space, etc. It is modelled after the VACUUM command
-** in PostgreSQL.
-**
-** In version 1.0.x of SQLite, the VACUUM command would call
-** gdbm_reorganize() on all the database tables. But beginning
-** with 2.0.0, SQLite no longer uses GDBM so this command has
-** become a no-op.
+** in PostgreSQL. The VACUUM command works as follows:
+**
+** (1) Create a new transient database file
+** (2) Copy all content from the database being vacuumed into
+** the new transient database file
+** (3) Copy content from the transient database back into the
+** original database.
+**
+** The transient database requires temporary disk space approximately
+** equal to the size of the original database. The copy operation of
+** step (3) requires additional temporary disk space approximately equal
+** to the size of the original database for the rollback journal.
+** Hence, temporary disk space that is approximately 2x the size of the
+** orginal database is required. Every page of the database is written
+** approximately 3 times: Once for step (2) and twice for step (3).
+** Two writes per page are required in step (3) because the original
+** database content must be written into the rollback journal prior to
+** overwriting the database with the vacuumed content.
+**
+** Only 1x temporary space and only 1x writes would be required if
+** the copy of step (3) were replace by deleting the original database
+** and renaming the transient database as the original. But that will
+** not work if other processes are attached to the original database.
+** And a power loss in between deleting the original and renaming the
+** transient would cause the database file to appear to be deleted
+** following reboot.
*/
SQLITE_PRIVATE void sqlite3Vacuum(Parse *pParse){
Vdbe *v = sqlite3GetVdbe(pParse);
@@ -103035,7 +105321,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction");
return SQLITE_ERROR;
}
- if( db->activeVdbeCnt>1 ){
+ if( db->nVdbeActive>1 ){
sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress");
return SQLITE_ERROR;
}
@@ -104085,10 +106371,9 @@ static void callFinaliser(sqlite3 *db, int offset){
** array. Return the error code for the first error that occurs, or
** SQLITE_OK if all xSync operations are successful.
**
-** Set *pzErrmsg to point to a buffer that should be released using
-** sqlite3DbFree() containing an error message, if one is available.
+** If an error message is available, leave it in p->zErrMsg.
*/
-SQLITE_PRIVATE int sqlite3VtabSync(sqlite3 *db, char **pzErrmsg){
+SQLITE_PRIVATE int sqlite3VtabSync(sqlite3 *db, Vdbe *p){
int i;
int rc = SQLITE_OK;
VTable **aVTrans = db->aVTrans;
@@ -104099,9 +106384,7 @@ SQLITE_PRIVATE int sqlite3VtabSync(sqlite3 *db, char **pzErrmsg){
sqlite3_vtab *pVtab = aVTrans[i]->pVtab;
if( pVtab && (x = pVtab->pModule->xSync)!=0 ){
rc = x(pVtab);
- sqlite3DbFree(db, *pzErrmsg);
- *pzErrmsg = sqlite3DbStrDup(db, pVtab->zErrMsg);
- sqlite3_free(pVtab->zErrMsg);
+ sqlite3VtabImportErrmsg(p, pVtab);
}
}
db->aVTrans = aVTrans;
@@ -104291,7 +106574,7 @@ SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(
memcpy(pNew->zName, pDef->zName, sqlite3Strlen30(pDef->zName)+1);
pNew->xFunc = xFunc;
pNew->pUserData = pArg;
- pNew->flags |= SQLITE_FUNC_EPHEM;
+ pNew->funcFlags |= SQLITE_FUNC_EPHEM;
return pNew;
}
@@ -104403,18 +106686,168 @@ SQLITE_API int sqlite3_vtab_config(sqlite3 *db, int op, ...){
#endif
#if defined(SQLITE_DEBUG) \
&& (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE))
-# define WHERETRACE(X) if(sqlite3WhereTrace) sqlite3DebugPrintf X
+# define WHERETRACE(K,X) if(sqlite3WhereTrace&(K)) sqlite3DebugPrintf X
+# define WHERETRACE_ENABLED 1
#else
-# define WHERETRACE(X)
+# define WHERETRACE(K,X)
#endif
-/* Forward reference
+/* Forward references
*/
typedef struct WhereClause WhereClause;
typedef struct WhereMaskSet WhereMaskSet;
typedef struct WhereOrInfo WhereOrInfo;
typedef struct WhereAndInfo WhereAndInfo;
-typedef struct WhereCost WhereCost;
+typedef struct WhereLevel WhereLevel;
+typedef struct WhereLoop WhereLoop;
+typedef struct WherePath WherePath;
+typedef struct WhereTerm WhereTerm;
+typedef struct WhereLoopBuilder WhereLoopBuilder;
+typedef struct WhereScan WhereScan;
+typedef struct WhereOrCost WhereOrCost;
+typedef struct WhereOrSet WhereOrSet;
+
+/*
+** This object contains information needed to implement a single nested
+** loop in WHERE clause.
+**
+** Contrast this object with WhereLoop. This object describes the
+** implementation of the loop. WhereLoop describes the algorithm.
+** This object contains a pointer to the WhereLoop algorithm as one of
+** its elements.
+**
+** The WhereInfo object contains a single instance of this object for
+** each term in the FROM clause (which is to say, for each of the
+** nested loops as implemented). The order of WhereLevel objects determines
+** the loop nested order, with WhereInfo.a[0] being the outer loop and
+** WhereInfo.a[WhereInfo.nLevel-1] being the inner loop.
+*/
+struct WhereLevel {
+ int iLeftJoin; /* Memory cell used to implement LEFT OUTER JOIN */
+ int iTabCur; /* The VDBE cursor used to access the table */
+ int iIdxCur; /* The VDBE cursor used to access pIdx */
+ int addrBrk; /* Jump here to break out of the loop */
+ int addrNxt; /* Jump here to start the next IN combination */
+ int addrCont; /* Jump here to continue with the next loop cycle */
+ int addrFirst; /* First instruction of interior of the loop */
+ int addrBody; /* Beginning of the body of this loop */
+ u8 iFrom; /* Which entry in the FROM clause */
+ u8 op, p5; /* Opcode and P5 of the opcode that ends the loop */
+ int p1, p2; /* Operands of the opcode used to ends the loop */
+ union { /* Information that depends on pWLoop->wsFlags */
+ struct {
+ int nIn; /* Number of entries in aInLoop[] */
+ struct InLoop {
+ int iCur; /* The VDBE cursor used by this IN operator */
+ int addrInTop; /* Top of the IN loop */
+ u8 eEndLoopOp; /* IN Loop terminator. OP_Next or OP_Prev */
+ } *aInLoop; /* Information about each nested IN operator */
+ } in; /* Used when pWLoop->wsFlags&WHERE_IN_ABLE */
+ Index *pCovidx; /* Possible covering index for WHERE_MULTI_OR */
+ } u;
+ struct WhereLoop *pWLoop; /* The selected WhereLoop object */
+ Bitmask notReady; /* FROM entries not usable at this level */
+};
+
+/*
+** Each instance of this object represents an algorithm for evaluating one
+** term of a join. Every term of the FROM clause will have at least
+** one corresponding WhereLoop object (unless INDEXED BY constraints
+** prevent a query solution - which is an error) and many terms of the
+** FROM clause will have multiple WhereLoop objects, each describing a
+** potential way of implementing that FROM-clause term, together with
+** dependencies and cost estimates for using the chosen algorithm.
+**
+** Query planning consists of building up a collection of these WhereLoop
+** objects, then computing a particular sequence of WhereLoop objects, with
+** one WhereLoop object per FROM clause term, that satisfy all dependencies
+** and that minimize the overall cost.
+*/
+struct WhereLoop {
+ Bitmask prereq; /* Bitmask of other loops that must run first */
+ Bitmask maskSelf; /* Bitmask identifying table iTab */
+#ifdef SQLITE_DEBUG
+ char cId; /* Symbolic ID of this loop for debugging use */
+#endif
+ u8 iTab; /* Position in FROM clause of table for this loop */
+ u8 iSortIdx; /* Sorting index number. 0==None */
+ LogEst rSetup; /* One-time setup cost (ex: create transient index) */
+ LogEst rRun; /* Cost of running each loop */
+ LogEst nOut; /* Estimated number of output rows */
+ union {
+ struct { /* Information for internal btree tables */
+ int nEq; /* Number of equality constraints */
+ Index *pIndex; /* Index used, or NULL */
+ } btree;
+ struct { /* Information for virtual tables */
+ int idxNum; /* Index number */
+ u8 needFree; /* True if sqlite3_free(idxStr) is needed */
+ u8 isOrdered; /* True if satisfies ORDER BY */
+ u16 omitMask; /* Terms that may be omitted */
+ char *idxStr; /* Index identifier string */
+ } vtab;
+ } u;
+ u32 wsFlags; /* WHERE_* flags describing the plan */
+ u16 nLTerm; /* Number of entries in aLTerm[] */
+ /**** whereLoopXfer() copies fields above ***********************/
+# define WHERE_LOOP_XFER_SZ offsetof(WhereLoop,nLSlot)
+ u16 nLSlot; /* Number of slots allocated for aLTerm[] */
+ WhereTerm **aLTerm; /* WhereTerms used */
+ WhereLoop *pNextLoop; /* Next WhereLoop object in the WhereClause */
+ WhereTerm *aLTermSpace[4]; /* Initial aLTerm[] space */
+};
+
+/* This object holds the prerequisites and the cost of running a
+** subquery on one operand of an OR operator in the WHERE clause.
+** See WhereOrSet for additional information
+*/
+struct WhereOrCost {
+ Bitmask prereq; /* Prerequisites */
+ LogEst rRun; /* Cost of running this subquery */
+ LogEst nOut; /* Number of outputs for this subquery */
+};
+
+/* The WhereOrSet object holds a set of possible WhereOrCosts that
+** correspond to the subquery(s) of OR-clause processing. Only the
+** best N_OR_COST elements are retained.
+*/
+#define N_OR_COST 3
+struct WhereOrSet {
+ u16 n; /* Number of valid a[] entries */
+ WhereOrCost a[N_OR_COST]; /* Set of best costs */
+};
+
+
+/* Forward declaration of methods */
+static int whereLoopResize(sqlite3*, WhereLoop*, int);
+
+/*
+** Each instance of this object holds a sequence of WhereLoop objects
+** that implement some or all of a query plan.
+**
+** Think of each WhereLoop object as a node in a graph with arcs
+** showing dependencies and costs for travelling between nodes. (That is
+** not a completely accurate description because WhereLoop costs are a
+** vector, not a scalar, and because dependencies are many-to-one, not
+** one-to-one as are graph nodes. But it is a useful visualization aid.)
+** Then a WherePath object is a path through the graph that visits some
+** or all of the WhereLoop objects once.
+**
+** The "solver" works by creating the N best WherePath objects of length
+** 1. Then using those as a basis to compute the N best WherePath objects
+** of length 2. And so forth until the length of WherePaths equals the
+** number of nodes in the FROM clause. The best (lowest cost) WherePath
+** at the end is the choosen query plan.
+*/
+struct WherePath {
+ Bitmask maskLoop; /* Bitmask of all WhereLoop objects in this path */
+ Bitmask revLoop; /* aLoop[]s that should be reversed for ORDER BY */
+ LogEst nRow; /* Estimated number of rows generated by this path */
+ LogEst rCost; /* Total cost of this path */
+ u8 isOrdered; /* True if this path satisfies ORDER BY */
+ u8 isOrderedValid; /* True if the isOrdered field is valid */
+ WhereLoop **aLoop; /* Array of WhereLoop objects implementing this path */
+};
/*
** The query generator uses an array of instances of this structure to
@@ -104442,9 +106875,9 @@ typedef struct WhereCost WhereCost;
**
** (t1.X <op> <expr>) OR (t1.Y <op> <expr>) OR ....
**
-** In this second case, wtFlag as the TERM_ORINFO set and eOperator==WO_OR
+** In this second case, wtFlag has the TERM_ORINFO bit set and eOperator==WO_OR
** and the WhereTerm.u.pOrInfo field points to auxiliary information that
-** is collected about the
+** is collected about the OR clause.
**
** If a term in the WHERE clause does not match either of the two previous
** categories, then eOperator==0. The WhereTerm.pExpr field is still set
@@ -104467,7 +106900,6 @@ typedef struct WhereCost WhereCost;
** in prereqRight and prereqAll. The default is 64 bits, hence SQLite
** is only able to process joins with 64 or fewer tables.
*/
-typedef struct WhereTerm WhereTerm;
struct WhereTerm {
Expr *pExpr; /* Pointer to the subexpression that is this term */
int iParent; /* Disable pWC->a[iParent] when this term disabled */
@@ -104477,6 +106909,7 @@ struct WhereTerm {
WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */
WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */
} u;
+ LogEst truthProb; /* Probability of truth for this expression */
u16 eOperator; /* A WO_xx value describing <op> */
u8 wtFlags; /* TERM_xxx bit flags. See below */
u8 nChild; /* Number of children that must disable us */
@@ -104495,13 +106928,29 @@ struct WhereTerm {
#define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */
#define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */
#define TERM_OR_OK 0x40 /* Used during OR-clause processing */
-#ifdef SQLITE_ENABLE_STAT3
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
# define TERM_VNULL 0x80 /* Manufactured x>NULL or x<=NULL term */
#else
# define TERM_VNULL 0x00 /* Disabled if not using stat3 */
#endif
/*
+** An instance of the WhereScan object is used as an iterator for locating
+** terms in the WHERE clause that are useful to the query planner.
+*/
+struct WhereScan {
+ WhereClause *pOrigWC; /* Original, innermost WhereClause */
+ WhereClause *pWC; /* WhereClause currently being scanned */
+ char *zCollName; /* Required collating sequence, if not NULL */
+ char idxaff; /* Must match this affinity, if zCollName!=NULL */
+ unsigned char nEquiv; /* Number of entries in aEquiv[] */
+ unsigned char iEquiv; /* Next unused slot in aEquiv[] */
+ u32 opMask; /* Acceptable operators */
+ int k; /* Resume scanning at this->pWC->a[this->k] */
+ int aEquiv[22]; /* Cursor,Column pairs for equivalence classes */
+};
+
+/*
** An instance of the following structure holds all information about a
** WHERE clause. Mostly this is a container for one or more WhereTerms.
**
@@ -104514,11 +106963,9 @@ struct WhereTerm {
** subclauses points to the WhereClause object for the whole clause.
*/
struct WhereClause {
- Parse *pParse; /* The parser context */
- WhereMaskSet *pMaskSet; /* Mapping of table cursor numbers to bitmasks */
+ WhereInfo *pWInfo; /* WHERE clause processing context */
WhereClause *pOuter; /* Outer conjunction */
u8 op; /* Split operator. TK_AND or TK_OR */
- u16 wctrlFlags; /* Might include WHERE_AND_ONLY */
int nTerm; /* Number of terms */
int nSlot; /* Number of entries in a[] */
WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */
@@ -104578,19 +107025,59 @@ struct WhereMaskSet {
};
/*
-** A WhereCost object records a lookup strategy and the estimated
-** cost of pursuing that strategy.
+** This object is a convenience wrapper holding all information needed
+** to construct WhereLoop objects for a particular query.
*/
-struct WhereCost {
- WherePlan plan; /* The lookup strategy */
- double rCost; /* Overall cost of pursuing this search strategy */
- Bitmask used; /* Bitmask of cursors used by this plan */
+struct WhereLoopBuilder {
+ WhereInfo *pWInfo; /* Information about this WHERE */
+ WhereClause *pWC; /* WHERE clause terms */
+ ExprList *pOrderBy; /* ORDER BY clause */
+ WhereLoop *pNew; /* Template WhereLoop */
+ WhereOrSet *pOrSet; /* Record best loops here, if not NULL */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ UnpackedRecord *pRec; /* Probe for stat4 (if required) */
+ int nRecValid; /* Number of valid fields currently in pRec */
+#endif
+};
+
+/*
+** The WHERE clause processing routine has two halves. The
+** first part does the start of the WHERE loop and the second
+** half does the tail of the WHERE loop. An instance of
+** this structure is returned by the first half and passed
+** into the second half to give some continuity.
+**
+** An instance of this object holds the complete state of the query
+** planner.
+*/
+struct WhereInfo {
+ Parse *pParse; /* Parsing and code generating context */
+ SrcList *pTabList; /* List of tables in the join */
+ ExprList *pOrderBy; /* The ORDER BY clause or NULL */
+ ExprList *pResultSet; /* Result set. DISTINCT operates on these */
+ WhereLoop *pLoops; /* List of all WhereLoop objects */
+ Bitmask revMask; /* Mask of ORDER BY terms that need reversing */
+ LogEst nRowOut; /* Estimated number of output rows */
+ u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */
+ u8 bOBSat; /* ORDER BY satisfied by indices */
+ u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE/DELETE */
+ u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */
+ u8 eDistinct; /* One of the WHERE_DISTINCT_* values below */
+ u8 nLevel; /* Number of nested loop */
+ int iTop; /* The very beginning of the WHERE loop */
+ int iContinue; /* Jump here to continue with next record */
+ int iBreak; /* Jump here to break out of the loop */
+ int savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */
+ WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */
+ WhereClause sWC; /* Decomposition of the WHERE clause */
+ WhereLevel a[1]; /* Information about each nest loop in WHERE */
};
/*
-** Bitmasks for the operators that indices are able to exploit. An
+** Bitmasks for the operators on WhereTerm objects. These are all
+** operators that are of interest to the query planner. An
** OR-ed combination of these values can be used when searching for
-** terms in the where clause.
+** particular WhereTerms within a WhereClause.
*/
#define WO_IN 0x001
#define WO_EQ 0x002
@@ -104609,74 +107096,121 @@ struct WhereCost {
#define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */
/*
-** Value for wsFlags returned by bestIndex() and stored in
-** WhereLevel.wsFlags. These flags determine which search
-** strategies are appropriate.
-**
-** The least significant 12 bits is reserved as a mask for WO_ values above.
-** The WhereLevel.wsFlags field is usually set to WO_IN|WO_EQ|WO_ISNULL.
-** But if the table is the right table of a left join, WhereLevel.wsFlags
-** is set to WO_IN|WO_EQ. The WhereLevel.wsFlags field can then be used as
-** the "op" parameter to findTerm when we are resolving equality constraints.
-** ISNULL constraints will then not be used on the right table of a left
-** join. Tickets #2177 and #2189.
-*/
-#define WHERE_ROWID_EQ 0x00001000 /* rowid=EXPR or rowid IN (...) */
-#define WHERE_ROWID_RANGE 0x00002000 /* rowid<EXPR and/or rowid>EXPR */
-#define WHERE_COLUMN_EQ 0x00010000 /* x=EXPR or x IN (...) or x IS NULL */
-#define WHERE_COLUMN_RANGE 0x00020000 /* x<EXPR and/or x>EXPR */
-#define WHERE_COLUMN_IN 0x00040000 /* x IN (...) */
-#define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */
-#define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */
-#define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */
-#define WHERE_IN_ABLE 0x080f1000 /* Able to support an IN operator */
-#define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */
-#define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */
-#define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and x<EXPR */
-#define WHERE_IDX_ONLY 0x00400000 /* Use index only - omit table */
-#define WHERE_ORDERED 0x00800000 /* Output will appear in correct order */
-#define WHERE_REVERSE 0x01000000 /* Scan in reverse order */
-#define WHERE_UNIQUE 0x02000000 /* Selects no more than one row */
-#define WHERE_ALL_UNIQUE 0x04000000 /* This and all prior have one row */
-#define WHERE_OB_UNIQUE 0x00004000 /* Values in ORDER BY columns are
- ** different for every output row */
-#define WHERE_VIRTUALTABLE 0x08000000 /* Use virtual-table processing */
-#define WHERE_MULTI_OR 0x10000000 /* OR using multiple indices */
-#define WHERE_TEMP_INDEX 0x20000000 /* Uses an ephemeral index */
-#define WHERE_DISTINCT 0x40000000 /* Correct order for DISTINCT */
-#define WHERE_COVER_SCAN 0x80000000 /* Full scan of a covering index */
-
-/*
-** This module contains many separate subroutines that work together to
-** find the best indices to use for accessing a particular table in a query.
-** An instance of the following structure holds context information about the
-** index search so that it can be more easily passed between the various
-** routines.
+** These are definitions of bits in the WhereLoop.wsFlags field.
+** The particular combination of bits in each WhereLoop help to
+** determine the algorithm that WhereLoop represents.
*/
-typedef struct WhereBestIdx WhereBestIdx;
-struct WhereBestIdx {
- Parse *pParse; /* Parser context */
- WhereClause *pWC; /* The WHERE clause */
- struct SrcList_item *pSrc; /* The FROM clause term to search */
- Bitmask notReady; /* Mask of cursors not available */
- Bitmask notValid; /* Cursors not available for any purpose */
- ExprList *pOrderBy; /* The ORDER BY clause */
- ExprList *pDistinct; /* The select-list if query is DISTINCT */
- sqlite3_index_info **ppIdxInfo; /* Index information passed to xBestIndex */
- int i, n; /* Which loop is being coded; # of loops */
- WhereLevel *aLevel; /* Info about outer loops */
- WhereCost cost; /* Lowest cost query plan */
-};
+#define WHERE_COLUMN_EQ 0x00000001 /* x=EXPR */
+#define WHERE_COLUMN_RANGE 0x00000002 /* x<EXPR and/or x>EXPR */
+#define WHERE_COLUMN_IN 0x00000004 /* x IN (...) */
+#define WHERE_COLUMN_NULL 0x00000008 /* x IS NULL */
+#define WHERE_CONSTRAINT 0x0000000f /* Any of the WHERE_COLUMN_xxx values */
+#define WHERE_TOP_LIMIT 0x00000010 /* x<EXPR or x<=EXPR constraint */
+#define WHERE_BTM_LIMIT 0x00000020 /* x>EXPR or x>=EXPR constraint */
+#define WHERE_BOTH_LIMIT 0x00000030 /* Both x>EXPR and x<EXPR */
+#define WHERE_IDX_ONLY 0x00000040 /* Use index only - omit table */
+#define WHERE_IPK 0x00000100 /* x is the INTEGER PRIMARY KEY */
+#define WHERE_INDEXED 0x00000200 /* WhereLoop.u.btree.pIndex is valid */
+#define WHERE_VIRTUALTABLE 0x00000400 /* WhereLoop.u.vtab is valid */
+#define WHERE_IN_ABLE 0x00000800 /* Able to support an IN operator */
+#define WHERE_ONEROW 0x00001000 /* Selects no more than one row */
+#define WHERE_MULTI_OR 0x00002000 /* OR using multiple indices */
+#define WHERE_AUTO_INDEX 0x00004000 /* Uses an ephemeral index */
/*
-** Return TRUE if the probe cost is less than the baseline cost
+** Return the estimated number of output rows from a WHERE clause
*/
-static int compareCost(const WhereCost *pProbe, const WhereCost *pBaseline){
- if( pProbe->rCost<pBaseline->rCost ) return 1;
- if( pProbe->rCost>pBaseline->rCost ) return 0;
- if( pProbe->plan.nOBSat>pBaseline->plan.nOBSat ) return 1;
- if( pProbe->plan.nRow<pBaseline->plan.nRow ) return 1;
- return 0;
+SQLITE_PRIVATE u64 sqlite3WhereOutputRowCount(WhereInfo *pWInfo){
+ return sqlite3LogEstToInt(pWInfo->nRowOut);
+}
+
+/*
+** Return one of the WHERE_DISTINCT_xxxxx values to indicate how this
+** WHERE clause returns outputs for DISTINCT processing.
+*/
+SQLITE_PRIVATE int sqlite3WhereIsDistinct(WhereInfo *pWInfo){
+ return pWInfo->eDistinct;
+}
+
+/*
+** Return TRUE if the WHERE clause returns rows in ORDER BY order.
+** Return FALSE if the output needs to be sorted.
+*/
+SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo *pWInfo){
+ return pWInfo->bOBSat!=0;
+}
+
+/*
+** Return the VDBE address or label to jump to in order to continue
+** immediately with the next row of a WHERE clause.
+*/
+SQLITE_PRIVATE int sqlite3WhereContinueLabel(WhereInfo *pWInfo){
+ return pWInfo->iContinue;
+}
+
+/*
+** Return the VDBE address or label to jump to in order to break
+** out of a WHERE loop.
+*/
+SQLITE_PRIVATE int sqlite3WhereBreakLabel(WhereInfo *pWInfo){
+ return pWInfo->iBreak;
+}
+
+/*
+** Return TRUE if an UPDATE or DELETE statement can operate directly on
+** the rowids returned by a WHERE clause. Return FALSE if doing an
+** UPDATE or DELETE might change subsequent WHERE clause results.
+*/
+SQLITE_PRIVATE int sqlite3WhereOkOnePass(WhereInfo *pWInfo){
+ return pWInfo->okOnePass;
+}
+
+/*
+** Move the content of pSrc into pDest
+*/
+static void whereOrMove(WhereOrSet *pDest, WhereOrSet *pSrc){
+ pDest->n = pSrc->n;
+ memcpy(pDest->a, pSrc->a, pDest->n*sizeof(pDest->a[0]));
+}
+
+/*
+** Try to insert a new prerequisite/cost entry into the WhereOrSet pSet.
+**
+** The new entry might overwrite an existing entry, or it might be
+** appended, or it might be discarded. Do whatever is the right thing
+** so that pSet keeps the N_OR_COST best entries seen so far.
+*/
+static int whereOrInsert(
+ WhereOrSet *pSet, /* The WhereOrSet to be updated */
+ Bitmask prereq, /* Prerequisites of the new entry */
+ LogEst rRun, /* Run-cost of the new entry */
+ LogEst nOut /* Number of outputs for the new entry */
+){
+ u16 i;
+ WhereOrCost *p;
+ for(i=pSet->n, p=pSet->a; i>0; i--, p++){
+ if( rRun<=p->rRun && (prereq & p->prereq)==prereq ){
+ goto whereOrInsert_done;
+ }
+ if( p->rRun<=rRun && (p->prereq & prereq)==p->prereq ){
+ return 0;
+ }
+ }
+ if( pSet->n<N_OR_COST ){
+ p = &pSet->a[pSet->n++];
+ p->nOut = nOut;
+ }else{
+ p = pSet->a;
+ for(i=1; i<pSet->n; i++){
+ if( p->rRun>pSet->a[i].rRun ) p = pSet->a + i;
+ }
+ if( p->rRun<=rRun ) return 0;
+ }
+whereOrInsert_done:
+ p->prereq = prereq;
+ p->rRun = rRun;
+ if( p->nOut>nOut ) p->nOut = nOut;
+ return 1;
}
/*
@@ -104684,17 +107218,13 @@ static int compareCost(const WhereCost *pProbe, const WhereCost *pBaseline){
*/
static void whereClauseInit(
WhereClause *pWC, /* The WhereClause to be initialized */
- Parse *pParse, /* The parsing context */
- WhereMaskSet *pMaskSet, /* Mapping from table cursor numbers to bitmasks */
- u16 wctrlFlags /* Might include WHERE_AND_ONLY */
+ WhereInfo *pWInfo /* The WHERE processing context */
){
- pWC->pParse = pParse;
- pWC->pMaskSet = pMaskSet;
+ pWC->pWInfo = pWInfo;
pWC->pOuter = 0;
pWC->nTerm = 0;
pWC->nSlot = ArraySize(pWC->aStatic);
pWC->a = pWC->aStatic;
- pWC->wctrlFlags = wctrlFlags;
}
/* Forward reference */
@@ -104723,7 +107253,7 @@ static void whereAndInfoDelete(sqlite3 *db, WhereAndInfo *p){
static void whereClauseClear(WhereClause *pWC){
int i;
WhereTerm *a;
- sqlite3 *db = pWC->pParse->db;
+ sqlite3 *db = pWC->pWInfo->pParse->db;
for(i=pWC->nTerm-1, a=pWC->a; i>=0; i--, a++){
if( a->wtFlags & TERM_DYNAMIC ){
sqlite3ExprDelete(db, a->pExpr);
@@ -104761,10 +107291,10 @@ static void whereClauseClear(WhereClause *pWC){
static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){
WhereTerm *pTerm;
int idx;
- testcase( wtFlags & TERM_VIRTUAL ); /* EV: R-00211-15100 */
+ testcase( wtFlags & TERM_VIRTUAL );
if( pWC->nTerm>=pWC->nSlot ){
WhereTerm *pOld = pWC->a;
- sqlite3 *db = pWC->pParse->db;
+ sqlite3 *db = pWC->pWInfo->pParse->db;
pWC->a = sqlite3DbMallocRaw(db, sizeof(pWC->a[0])*pWC->nSlot*2 );
if( pWC->a==0 ){
if( wtFlags & TERM_DYNAMIC ){
@@ -104780,6 +107310,11 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){
pWC->nSlot = sqlite3DbMallocSize(db, pWC->a)/sizeof(pWC->a[0]);
}
pTerm = &pWC->a[idx = pWC->nTerm++];
+ if( p && ExprHasProperty(p, EP_Unlikely) ){
+ pTerm->truthProb = sqlite3LogEst(p->iTable) - 99;
+ }else{
+ pTerm->truthProb = -1;
+ }
pTerm->pExpr = sqlite3ExprSkipCollate(p);
pTerm->wtFlags = wtFlags;
pTerm->pWC = pWC;
@@ -104804,8 +107339,8 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){
** the WhereClause.a[] array. The slot[] array grows as needed to contain
** all terms of the WHERE clause.
*/
-static void whereSplit(WhereClause *pWC, Expr *pExpr, int op){
- pWC->op = (u8)op;
+static void whereSplit(WhereClause *pWC, Expr *pExpr, u8 op){
+ pWC->op = op;
if( pExpr==0 ) return;
if( pExpr->op!=op ){
whereClauseInsert(pWC, pExpr, 0);
@@ -104816,9 +107351,9 @@ static void whereSplit(WhereClause *pWC, Expr *pExpr, int op){
}
/*
-** Initialize an expression mask set (a WhereMaskSet object)
+** Initialize a WhereMaskSet object
*/
-#define initMaskSet(P) memset(P, 0, sizeof(*P))
+#define initMaskSet(P) (P)->n=0
/*
** Return the bitmask for the given cursor number. Return 0 if
@@ -104829,7 +107364,7 @@ static Bitmask getMask(WhereMaskSet *pMaskSet, int iCursor){
assert( pMaskSet->n<=(int)sizeof(Bitmask)*8 );
for(i=0; i<pMaskSet->n; i++){
if( pMaskSet->ix[i]==iCursor ){
- return ((Bitmask)1)<<i;
+ return MASKBIT(i);
}
}
return 0;
@@ -104849,18 +107384,9 @@ static void createMask(WhereMaskSet *pMaskSet, int iCursor){
}
/*
-** This routine walks (recursively) an expression tree and generates
+** These routines walk (recursively) an expression tree and generate
** a bitmask indicating which tables are used in that expression
** tree.
-**
-** In order for this routine to work, the calling function must have
-** previously invoked sqlite3ResolveExprNames() on the expression. See
-** the header comment on that routine for additional information.
-** The sqlite3ResolveExprNames() routines looks for column names and
-** sets their opcodes to TK_COLUMN and their Expr.iTable fields to
-** the VDBE cursor number of the table. This routine just has to
-** translate the cursor numbers into bitmask values and OR all
-** the bitmasks together.
*/
static Bitmask exprListTableUsage(WhereMaskSet*, ExprList*);
static Bitmask exprSelectTableUsage(WhereMaskSet*, Select*);
@@ -104914,14 +107440,7 @@ static Bitmask exprSelectTableUsage(WhereMaskSet *pMaskSet, Select *pS){
/*
** Return TRUE if the given operator is one of the operators that is
** allowed for an indexable WHERE clause term. The allowed operators are
-** "=", "<", ">", "<=", ">=", and "IN".
-**
-** IMPLEMENTATION-OF: R-59926-26393 To be usable by an index a term must be
-** of one of the following forms: column = expression column > expression
-** column >= expression column < expression column <= expression
-** expression = column expression > column expression >= column
-** expression < column expression <= column column IN
-** (expression-list) column IN (subquery) column IS NULL
+** "=", "<", ">", "<=", ">=", "IN", and "IS NULL"
*/
static int allowedOp(int op){
assert( TK_GT>TK_EQ && TK_GT<TK_GE );
@@ -104941,10 +107460,9 @@ static int allowedOp(int op){
** are converted into "Y op X".
**
** If left/right precedence rules come into play when determining the
-** collating
-** side of the comparison, it remains associated with the same side after
-** the commutation. So "Y collate NOCASE op X" becomes
-** "X op Y". This is because any collation sequence on
+** collating sequence, then COLLATE operators are adjusted to ensure
+** that the collating sequence does not change. For example:
+** "Y collate NOCASE op X" becomes "X op Y" because any collation sequence on
** the left hand side of a comparison overrides any collation sequence
** attached to the right. For the same reason the EP_Collate flag
** is not commuted.
@@ -105002,6 +107520,130 @@ static u16 operatorMask(int op){
}
/*
+** Advance to the next WhereTerm that matches according to the criteria
+** established when the pScan object was initialized by whereScanInit().
+** Return NULL if there are no more matching WhereTerms.
+*/
+static WhereTerm *whereScanNext(WhereScan *pScan){
+ int iCur; /* The cursor on the LHS of the term */
+ int iColumn; /* The column on the LHS of the term. -1 for IPK */
+ Expr *pX; /* An expression being tested */
+ WhereClause *pWC; /* Shorthand for pScan->pWC */
+ WhereTerm *pTerm; /* The term being tested */
+ int k = pScan->k; /* Where to start scanning */
+
+ while( pScan->iEquiv<=pScan->nEquiv ){
+ iCur = pScan->aEquiv[pScan->iEquiv-2];
+ iColumn = pScan->aEquiv[pScan->iEquiv-1];
+ while( (pWC = pScan->pWC)!=0 ){
+ for(pTerm=pWC->a+k; k<pWC->nTerm; k++, pTerm++){
+ if( pTerm->leftCursor==iCur && pTerm->u.leftColumn==iColumn ){
+ if( (pTerm->eOperator & WO_EQUIV)!=0
+ && pScan->nEquiv<ArraySize(pScan->aEquiv)
+ ){
+ int j;
+ pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight);
+ assert( pX->op==TK_COLUMN );
+ for(j=0; j<pScan->nEquiv; j+=2){
+ if( pScan->aEquiv[j]==pX->iTable
+ && pScan->aEquiv[j+1]==pX->iColumn ){
+ break;
+ }
+ }
+ if( j==pScan->nEquiv ){
+ pScan->aEquiv[j] = pX->iTable;
+ pScan->aEquiv[j+1] = pX->iColumn;
+ pScan->nEquiv += 2;
+ }
+ }
+ if( (pTerm->eOperator & pScan->opMask)!=0 ){
+ /* Verify the affinity and collating sequence match */
+ if( pScan->zCollName && (pTerm->eOperator & WO_ISNULL)==0 ){
+ CollSeq *pColl;
+ Parse *pParse = pWC->pWInfo->pParse;
+ pX = pTerm->pExpr;
+ if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){
+ continue;
+ }
+ assert(pX->pLeft);
+ pColl = sqlite3BinaryCompareCollSeq(pParse,
+ pX->pLeft, pX->pRight);
+ if( pColl==0 ) pColl = pParse->db->pDfltColl;
+ if( sqlite3StrICmp(pColl->zName, pScan->zCollName) ){
+ continue;
+ }
+ }
+ if( (pTerm->eOperator & WO_EQ)!=0
+ && (pX = pTerm->pExpr->pRight)->op==TK_COLUMN
+ && pX->iTable==pScan->aEquiv[0]
+ && pX->iColumn==pScan->aEquiv[1]
+ ){
+ continue;
+ }
+ pScan->k = k+1;
+ return pTerm;
+ }
+ }
+ }
+ pScan->pWC = pScan->pWC->pOuter;
+ k = 0;
+ }
+ pScan->pWC = pScan->pOrigWC;
+ k = 0;
+ pScan->iEquiv += 2;
+ }
+ return 0;
+}
+
+/*
+** Initialize a WHERE clause scanner object. Return a pointer to the
+** first match. Return NULL if there are no matches.
+**
+** The scanner will be searching the WHERE clause pWC. It will look
+** for terms of the form "X <op> <expr>" where X is column iColumn of table
+** iCur. The <op> must be one of the operators described by opMask.
+**
+** If the search is for X and the WHERE clause contains terms of the
+** form X=Y then this routine might also return terms of the form
+** "Y <op> <expr>". The number of levels of transitivity is limited,
+** but is enough to handle most commonly occurring SQL statements.
+**
+** If X is not the INTEGER PRIMARY KEY then X must be compatible with
+** index pIdx.
+*/
+static WhereTerm *whereScanInit(
+ WhereScan *pScan, /* The WhereScan object being initialized */
+ WhereClause *pWC, /* The WHERE clause to be scanned */
+ int iCur, /* Cursor to scan for */
+ int iColumn, /* Column to scan for */
+ u32 opMask, /* Operator(s) to scan for */
+ Index *pIdx /* Must be compatible with this index */
+){
+ int j;
+
+ /* memset(pScan, 0, sizeof(*pScan)); */
+ pScan->pOrigWC = pWC;
+ pScan->pWC = pWC;
+ if( pIdx && iColumn>=0 ){
+ pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity;
+ for(j=0; pIdx->aiColumn[j]!=iColumn; j++){
+ if( NEVER(j>=pIdx->nColumn) ) return 0;
+ }
+ pScan->zCollName = pIdx->azColl[j];
+ }else{
+ pScan->idxaff = 0;
+ pScan->zCollName = 0;
+ }
+ pScan->opMask = opMask;
+ pScan->k = 0;
+ pScan->aEquiv[0] = iCur;
+ pScan->aEquiv[1] = iColumn;
+ pScan->nEquiv = 2;
+ pScan->iEquiv = 2;
+ return whereScanNext(pScan);
+}
+
+/*
** Search for a term in the WHERE clause that is of the form "X <op> <expr>"
** where X is a reference to the iColumn of table iCur and <op> is one of
** the WO_xx operator codes specified by the op parameter.
@@ -105032,84 +107674,20 @@ static WhereTerm *findTerm(
u32 op, /* Mask of WO_xx values describing operator */
Index *pIdx /* Must be compatible with this index, if not NULL */
){
- WhereTerm *pTerm; /* Term being examined as possible result */
- WhereTerm *pResult = 0; /* The answer to return */
- WhereClause *pWCOrig = pWC; /* Original pWC value */
- int j, k; /* Loop counters */
- Expr *pX; /* Pointer to an expression */
- Parse *pParse; /* Parsing context */
- int iOrigCol = iColumn; /* Original value of iColumn */
- int nEquiv = 2; /* Number of entires in aEquiv[] */
- int iEquiv = 2; /* Number of entries of aEquiv[] processed so far */
- int aEquiv[22]; /* iCur,iColumn and up to 10 other equivalents */
-
- assert( iCur>=0 );
- aEquiv[0] = iCur;
- aEquiv[1] = iColumn;
- for(;;){
- for(pWC=pWCOrig; pWC; pWC=pWC->pOuter){
- for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){
- if( pTerm->leftCursor==iCur
- && pTerm->u.leftColumn==iColumn
- ){
- if( (pTerm->prereqRight & notReady)==0
- && (pTerm->eOperator & op & WO_ALL)!=0
- ){
- if( iOrigCol>=0 && pIdx && (pTerm->eOperator & WO_ISNULL)==0 ){
- CollSeq *pColl;
- char idxaff;
-
- pX = pTerm->pExpr;
- pParse = pWC->pParse;
- idxaff = pIdx->pTable->aCol[iOrigCol].affinity;
- if( !sqlite3IndexAffinityOk(pX, idxaff) ){
- continue;
- }
-
- /* Figure out the collation sequence required from an index for
- ** it to be useful for optimising expression pX. Store this
- ** value in variable pColl.
- */
- assert(pX->pLeft);
- pColl = sqlite3BinaryCompareCollSeq(pParse,pX->pLeft,pX->pRight);
- if( pColl==0 ) pColl = pParse->db->pDfltColl;
-
- for(j=0; pIdx->aiColumn[j]!=iOrigCol; j++){
- if( NEVER(j>=pIdx->nColumn) ) return 0;
- }
- if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ){
- continue;
- }
- }
- if( pTerm->prereqRight==0 && (pTerm->eOperator&WO_EQ)!=0 ){
- pResult = pTerm;
- goto findTerm_success;
- }else if( pResult==0 ){
- pResult = pTerm;
- }
- }
- if( (pTerm->eOperator & WO_EQUIV)!=0
- && nEquiv<ArraySize(aEquiv)
- ){
- pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight);
- assert( pX->op==TK_COLUMN );
- for(j=0; j<nEquiv; j+=2){
- if( aEquiv[j]==pX->iTable && aEquiv[j+1]==pX->iColumn ) break;
- }
- if( j==nEquiv ){
- aEquiv[j] = pX->iTable;
- aEquiv[j+1] = pX->iColumn;
- nEquiv += 2;
- }
- }
- }
+ WhereTerm *pResult = 0;
+ WhereTerm *p;
+ WhereScan scan;
+
+ p = whereScanInit(&scan, pWC, iCur, iColumn, op, pIdx);
+ while( p ){
+ if( (p->prereqRight & notReady)==0 ){
+ if( p->prereqRight==0 && (p->eOperator&WO_EQ)!=0 ){
+ return p;
}
+ if( pResult==0 ) pResult = p;
}
- if( iEquiv>=nEquiv ) break;
- iCur = aEquiv[iEquiv++];
- iColumn = aEquiv[iEquiv++];
+ p = whereScanNext(&scan);
}
-findTerm_success:
return pResult;
}
@@ -105118,8 +107696,6 @@ static void exprAnalyze(SrcList*, WhereClause*, int);
/*
** Call exprAnalyze on all terms in a WHERE clause.
-**
-**
*/
static void exprAnalyzeAll(
SrcList *pTabList, /* the FROM clause */
@@ -105183,7 +107759,7 @@ static int isLikeOrGlob(
if( op==TK_VARIABLE ){
Vdbe *pReprepare = pParse->pReprepare;
int iCol = pRight->iColumn;
- pVal = sqlite3VdbeGetValue(pReprepare, iCol, SQLITE_AFF_NONE);
+ pVal = sqlite3VdbeGetBoundValue(pReprepare, iCol, SQLITE_AFF_NONE);
if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){
z = (char *)sqlite3_value_text(pVal);
}
@@ -105265,8 +107841,10 @@ static int isMatchOfColumn(
** a join, then transfer the appropriate markings over to derived.
*/
static void transferJoinMarkings(Expr *pDerived, Expr *pBase){
- pDerived->flags |= pBase->flags & EP_FromJoin;
- pDerived->iRightJoinTable = pBase->iRightJoinTable;
+ if( pDerived ){
+ pDerived->flags |= pBase->flags & EP_FromJoin;
+ pDerived->iRightJoinTable = pBase->iRightJoinTable;
+ }
}
#if !defined(SQLITE_OMIT_OR_OPTIMIZATION) && !defined(SQLITE_OMIT_SUBQUERY)
@@ -105325,10 +107903,10 @@ static void transferJoinMarkings(Expr *pDerived, Expr *pBase){
** From another point of view, "indexable" means that the subterm could
** potentially be used with an index if an appropriate index exists.
** This analysis does not consider whether or not the index exists; that
-** is something the bestIndex() routine will determine. This analysis
-** only looks at whether subterms appropriate for indexing exist.
+** is decided elsewhere. This analysis only looks at whether subterms
+** appropriate for indexing exist.
**
-** All examples A through E above all satisfy case 2. But if a term
+** All examples A through E above satisfy case 2. But if a term
** also statisfies case 1 (such as B) we know that the optimizer will
** always prefer case 1, so in that case we pretend that case 2 is not
** satisfied.
@@ -105351,11 +107929,11 @@ static void exprAnalyzeOrTerm(
WhereClause *pWC, /* the complete WHERE clause */
int idxTerm /* Index of the OR-term to be analyzed */
){
- Parse *pParse = pWC->pParse; /* Parser context */
+ WhereInfo *pWInfo = pWC->pWInfo; /* WHERE clause processing context */
+ Parse *pParse = pWInfo->pParse; /* Parser context */
sqlite3 *db = pParse->db; /* Database connection */
WhereTerm *pTerm = &pWC->a[idxTerm]; /* The term to be analyzed */
Expr *pExpr = pTerm->pExpr; /* The expression of the term */
- WhereMaskSet *pMaskSet = pWC->pMaskSet; /* Table use masks */
int i; /* Loop counters */
WhereClause *pOrWc; /* Breakup of pTerm into subterms */
WhereTerm *pOrTerm; /* A Sub-term within the pOrWc */
@@ -105374,7 +107952,7 @@ static void exprAnalyzeOrTerm(
if( pOrInfo==0 ) return;
pTerm->wtFlags |= TERM_ORINFO;
pOrWc = &pOrInfo->wc;
- whereClauseInit(pOrWc, pWC->pParse, pMaskSet, pWC->wctrlFlags);
+ whereClauseInit(pOrWc, pWInfo);
whereSplit(pOrWc, pExpr, TK_OR);
exprAnalyzeAll(pSrc, pOrWc);
if( db->mallocFailed ) return;
@@ -105400,7 +107978,7 @@ static void exprAnalyzeOrTerm(
pOrTerm->wtFlags |= TERM_ANDINFO;
pOrTerm->eOperator = WO_AND;
pAndWC = &pAndInfo->wc;
- whereClauseInit(pAndWC, pWC->pParse, pMaskSet, pWC->wctrlFlags);
+ whereClauseInit(pAndWC, pWC->pWInfo);
whereSplit(pAndWC, pOrTerm->pExpr, TK_AND);
exprAnalyzeAll(pSrc, pAndWC);
pAndWC->pOuter = pWC;
@@ -105409,7 +107987,7 @@ static void exprAnalyzeOrTerm(
for(j=0, pAndTerm=pAndWC->a; j<pAndWC->nTerm; j++, pAndTerm++){
assert( pAndTerm->pExpr );
if( allowedOp(pAndTerm->pExpr->op) ){
- b |= getMask(pMaskSet, pAndTerm->leftCursor);
+ b |= getMask(&pWInfo->sMaskSet, pAndTerm->leftCursor);
}
}
}
@@ -105420,10 +107998,10 @@ static void exprAnalyzeOrTerm(
** corresponding TERM_VIRTUAL term */
}else{
Bitmask b;
- b = getMask(pMaskSet, pOrTerm->leftCursor);
+ b = getMask(&pWInfo->sMaskSet, pOrTerm->leftCursor);
if( pOrTerm->wtFlags & TERM_VIRTUAL ){
WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent];
- b |= getMask(pMaskSet, pOther->leftCursor);
+ b |= getMask(&pWInfo->sMaskSet, pOther->leftCursor);
}
indexable &= b;
if( (pOrTerm->eOperator & WO_EQ)==0 ){
@@ -105485,7 +108063,7 @@ static void exprAnalyzeOrTerm(
assert( j==1 );
continue;
}
- if( (chngToIN & getMask(pMaskSet, pOrTerm->leftCursor))==0 ){
+ if( (chngToIN & getMask(&pWInfo->sMaskSet, pOrTerm->leftCursor))==0 ){
/* This term must be of the form t1.a==t2.b where t2 is in the
** chngToIN set but t1 is not. This term will be either preceeded
** or follwed by an inverted copy (t2.b==t1.a). Skip this term
@@ -105504,7 +108082,7 @@ static void exprAnalyzeOrTerm(
** on the second iteration */
assert( j==1 );
assert( IsPowerOfTwo(chngToIN) );
- assert( chngToIN==getMask(pMaskSet, iCursor) );
+ assert( chngToIN==getMask(&pWInfo->sMaskSet, iCursor) );
break;
}
testcase( j==1 );
@@ -105538,8 +108116,6 @@ static void exprAnalyzeOrTerm(
/* At this point, okToChngToIN is true if original pTerm satisfies
** case 1. In that case, construct a new virtual term that is
** pTerm converted into an IN operator.
- **
- ** EV: R-00211-15100
*/
if( okToChngToIN ){
Expr *pDup; /* A transient duplicate expression */
@@ -105553,7 +108129,7 @@ static void exprAnalyzeOrTerm(
assert( pOrTerm->leftCursor==iCursor );
assert( pOrTerm->u.leftColumn==iColumn );
pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0);
- pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup);
+ pList = sqlite3ExprListAppend(pWInfo->pParse, pList, pDup);
pLeft = pOrTerm->pExpr->pLeft;
}
assert( pLeft!=0 );
@@ -105602,6 +108178,7 @@ static void exprAnalyze(
WhereClause *pWC, /* the WHERE clause */
int idxTerm /* Index of the term to be analyzed */
){
+ WhereInfo *pWInfo = pWC->pWInfo; /* WHERE clause processing context */
WhereTerm *pTerm; /* The term to be analyzed */
WhereMaskSet *pMaskSet; /* Set of table index masks */
Expr *pExpr; /* The expression to be analyzed */
@@ -105612,14 +108189,14 @@ static void exprAnalyze(
int isComplete = 0; /* RHS of LIKE/GLOB ends with wildcard */
int noCase = 0; /* LIKE/GLOB distinguishes case */
int op; /* Top-level operator. pExpr->op */
- Parse *pParse = pWC->pParse; /* Parsing context */
+ Parse *pParse = pWInfo->pParse; /* Parsing context */
sqlite3 *db = pParse->db; /* Database connection */
if( db->mallocFailed ){
return;
}
pTerm = &pWC->a[idxTerm];
- pMaskSet = pWC->pMaskSet;
+ pMaskSet = &pWInfo->sMaskSet;
pExpr = pTerm->pExpr;
assert( pExpr->op!=TK_AS && pExpr->op!=TK_COLLATE );
prereqLeft = exprTableUsage(pMaskSet, pExpr->pLeft);
@@ -105724,6 +108301,7 @@ static void exprAnalyze(
pNewExpr = sqlite3PExpr(pParse, ops[i],
sqlite3ExprDup(db, pExpr->pLeft, 0),
sqlite3ExprDup(db, pList->a[i].pExpr, 0), 0);
+ transferJoinMarkings(pNewExpr, pExpr);
idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC);
testcase( idxNew==0 );
exprAnalyze(pSrc, pWC, idxNew);
@@ -105780,9 +108358,7 @@ static void exprAnalyze(
** inequality. To avoid this, make sure to also run the full
** LIKE on all candidate expressions by clearing the isComplete flag
*/
- if( c=='A'-1 ) isComplete = 0; /* EV: R-64339-08207 */
-
-
+ if( c=='A'-1 ) isComplete = 0;
c = sqlite3UpperToLower[c];
}
*pC = c + 1;
@@ -105793,6 +108369,7 @@ static void exprAnalyze(
pNewExpr1 = sqlite3PExpr(pParse, TK_GE,
sqlite3ExprAddCollateToken(pParse,pNewExpr1,&sCollSeqName),
pStr1, 0);
+ transferJoinMarkings(pNewExpr1, pExpr);
idxNew1 = whereClauseInsert(pWC, pNewExpr1, TERM_VIRTUAL|TERM_DYNAMIC);
testcase( idxNew1==0 );
exprAnalyze(pSrc, pWC, idxNew1);
@@ -105800,6 +108377,7 @@ static void exprAnalyze(
pNewExpr2 = sqlite3PExpr(pParse, TK_LT,
sqlite3ExprAddCollateToken(pParse,pNewExpr2,&sCollSeqName),
pStr2, 0);
+ transferJoinMarkings(pNewExpr2, pExpr);
idxNew2 = whereClauseInsert(pWC, pNewExpr2, TERM_VIRTUAL|TERM_DYNAMIC);
testcase( idxNew2==0 );
exprAnalyze(pSrc, pWC, idxNew2);
@@ -105849,7 +108427,7 @@ static void exprAnalyze(
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
-#ifdef SQLITE_ENABLE_STAT3
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
/* When sqlite_stat3 histogram data is available an operator of the
** form "x IS NOT NULL" can sometimes be evaluated more efficiently
** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a
@@ -105863,6 +108441,7 @@ static void exprAnalyze(
if( pExpr->op==TK_NOTNULL
&& pExpr->pLeft->op==TK_COLUMN
&& pExpr->pLeft->iColumn>=0
+ && OptimizationEnabled(db, SQLITE_Stat3)
){
Expr *pNewExpr;
Expr *pLeft = pExpr->pLeft;
@@ -105888,7 +108467,7 @@ static void exprAnalyze(
pNewTerm->prereqAll = pTerm->prereqAll;
}
}
-#endif /* SQLITE_ENABLE_STAT */
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
/* Prevent ON clause terms of a LEFT JOIN from being used to drive
** an index for tables to the left of the join.
@@ -105897,11 +108476,8 @@ static void exprAnalyze(
}
/*
-** This function searches the expression list passed as the second argument
-** for an expression of type TK_COLUMN that refers to the same column and
-** uses the same collation sequence as the iCol'th column of index pIdx.
-** Argument iBase is the cursor number used for the table that pIdx refers
-** to.
+** This function searches pList for a entry that matches the iCol-th column
+** of index pIdx.
**
** If such an expression is found, its index in pList->a[] is returned. If
** no expression is found, -1 is returned.
@@ -105933,76 +108509,17 @@ static int findIndexCol(
}
/*
-** This routine determines if pIdx can be used to assist in processing a
-** DISTINCT qualifier. In other words, it tests whether or not using this
-** index for the outer loop guarantees that rows with equal values for
-** all expressions in the pDistinct list are delivered grouped together.
-**
-** For example, the query
-**
-** SELECT DISTINCT a, b, c FROM tbl WHERE a = ?
-**
-** can benefit from any index on columns "b" and "c".
-*/
-static int isDistinctIndex(
- Parse *pParse, /* Parsing context */
- WhereClause *pWC, /* The WHERE clause */
- Index *pIdx, /* The index being considered */
- int base, /* Cursor number for the table pIdx is on */
- ExprList *pDistinct, /* The DISTINCT expressions */
- int nEqCol /* Number of index columns with == */
-){
- Bitmask mask = 0; /* Mask of unaccounted for pDistinct exprs */
- int i; /* Iterator variable */
-
- assert( pDistinct!=0 );
- if( pIdx->zName==0 || pDistinct->nExpr>=BMS ) return 0;
- testcase( pDistinct->nExpr==BMS-1 );
-
- /* Loop through all the expressions in the distinct list. If any of them
- ** are not simple column references, return early. Otherwise, test if the
- ** WHERE clause contains a "col=X" clause. If it does, the expression
- ** can be ignored. If it does not, and the column does not belong to the
- ** same table as index pIdx, return early. Finally, if there is no
- ** matching "col=X" expression and the column is on the same table as pIdx,
- ** set the corresponding bit in variable mask.
- */
- for(i=0; i<pDistinct->nExpr; i++){
- WhereTerm *pTerm;
- Expr *p = sqlite3ExprSkipCollate(pDistinct->a[i].pExpr);
- if( p->op!=TK_COLUMN ) return 0;
- pTerm = findTerm(pWC, p->iTable, p->iColumn, ~(Bitmask)0, WO_EQ, 0);
- if( pTerm ){
- Expr *pX = pTerm->pExpr;
- CollSeq *p1 = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
- CollSeq *p2 = sqlite3ExprCollSeq(pParse, p);
- if( p1==p2 ) continue;
- }
- if( p->iTable!=base ) return 0;
- mask |= (((Bitmask)1) << i);
- }
-
- for(i=nEqCol; mask && i<pIdx->nColumn; i++){
- int iExpr = findIndexCol(pParse, pDistinct, base, pIdx, i);
- if( iExpr<0 ) break;
- mask &= ~(((Bitmask)1) << iExpr);
- }
-
- return (mask==0);
-}
-
-
-/*
** Return true if the DISTINCT expression-list passed as the third argument
-** is redundant. A DISTINCT list is redundant if the database contains a
-** UNIQUE index that guarantees that the result of the query will be distinct
-** anyway.
+** is redundant.
+**
+** A DISTINCT list is redundant if the database contains some subset of
+** columns that are unique and non-null.
*/
static int isDistinctRedundant(
- Parse *pParse,
- SrcList *pTabList,
- WhereClause *pWC,
- ExprList *pDistinct
+ Parse *pParse, /* Parsing context */
+ SrcList *pTabList, /* The FROM clause */
+ WhereClause *pWC, /* The WHERE clause */
+ ExprList *pDistinct /* The result set that needs to be DISTINCT */
){
Table *pTab;
Index *pIdx;
@@ -106058,21 +108575,13 @@ static int isDistinctRedundant(
return 0;
}
+
/*
-** Prepare a crude estimate of the logarithm of the input value.
-** The results need not be exact. This is only used for estimating
-** the total cost of performing operations with O(logN) or O(NlogN)
-** complexity. Because N is just a guess, it is no great tragedy if
-** logN is a little off.
+** Estimate the logarithm of the input value to base 2.
*/
-static double estLog(double N){
- double logN = 1;
- double x = 10;
- while( N>x ){
- logN += 1;
- x *= 10;
- }
- return logN;
+static LogEst estLog(LogEst N){
+ LogEst x = sqlite3LogEst(N);
+ return x>33 ? x - 33 : 0;
}
/*
@@ -106081,7 +108590,7 @@ static double estLog(double N){
** SQLITE_TEST or SQLITE_DEBUG are defined, then these routines
** are no-ops.
*/
-#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_DEBUG)
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(WHERETRACE_ENABLED)
static void TRACE_IDX_INPUTS(sqlite3_index_info *p){
int i;
if( !sqlite3WhereTrace ) return;
@@ -106119,107 +108628,6 @@ static void TRACE_IDX_OUTPUTS(sqlite3_index_info *p){
#define TRACE_IDX_OUTPUTS(A)
#endif
-/*
-** Required because bestIndex() is called by bestOrClauseIndex()
-*/
-static void bestIndex(WhereBestIdx*);
-
-/*
-** This routine attempts to find an scanning strategy that can be used
-** to optimize an 'OR' expression that is part of a WHERE clause.
-**
-** The table associated with FROM clause term pSrc may be either a
-** regular B-Tree table or a virtual table.
-*/
-static void bestOrClauseIndex(WhereBestIdx *p){
-#ifndef SQLITE_OMIT_OR_OPTIMIZATION
- WhereClause *pWC = p->pWC; /* The WHERE clause */
- struct SrcList_item *pSrc = p->pSrc; /* The FROM clause term to search */
- const int iCur = pSrc->iCursor; /* The cursor of the table */
- const Bitmask maskSrc = getMask(pWC->pMaskSet, iCur); /* Bitmask for pSrc */
- WhereTerm * const pWCEnd = &pWC->a[pWC->nTerm]; /* End of pWC->a[] */
- WhereTerm *pTerm; /* A single term of the WHERE clause */
-
- /* The OR-clause optimization is disallowed if the INDEXED BY or
- ** NOT INDEXED clauses are used or if the WHERE_AND_ONLY bit is set. */
- if( pSrc->notIndexed || pSrc->pIndex!=0 ){
- return;
- }
- if( pWC->wctrlFlags & WHERE_AND_ONLY ){
- return;
- }
-
- /* Search the WHERE clause terms for a usable WO_OR term. */
- for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
- if( (pTerm->eOperator & WO_OR)!=0
- && ((pTerm->prereqAll & ~maskSrc) & p->notReady)==0
- && (pTerm->u.pOrInfo->indexable & maskSrc)!=0
- ){
- WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc;
- WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm];
- WhereTerm *pOrTerm;
- int flags = WHERE_MULTI_OR;
- double rTotal = 0;
- double nRow = 0;
- Bitmask used = 0;
- WhereBestIdx sBOI;
-
- sBOI = *p;
- sBOI.pOrderBy = 0;
- sBOI.pDistinct = 0;
- sBOI.ppIdxInfo = 0;
- for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){
- WHERETRACE(("... Multi-index OR testing for term %d of %d....\n",
- (pOrTerm - pOrWC->a), (pTerm - pWC->a)
- ));
- if( (pOrTerm->eOperator& WO_AND)!=0 ){
- sBOI.pWC = &pOrTerm->u.pAndInfo->wc;
- bestIndex(&sBOI);
- }else if( pOrTerm->leftCursor==iCur ){
- WhereClause tempWC;
- tempWC.pParse = pWC->pParse;
- tempWC.pMaskSet = pWC->pMaskSet;
- tempWC.pOuter = pWC;
- tempWC.op = TK_AND;
- tempWC.a = pOrTerm;
- tempWC.wctrlFlags = 0;
- tempWC.nTerm = 1;
- sBOI.pWC = &tempWC;
- bestIndex(&sBOI);
- }else{
- continue;
- }
- rTotal += sBOI.cost.rCost;
- nRow += sBOI.cost.plan.nRow;
- used |= sBOI.cost.used;
- if( rTotal>=p->cost.rCost ) break;
- }
-
- /* If there is an ORDER BY clause, increase the scan cost to account
- ** for the cost of the sort. */
- if( p->pOrderBy!=0 ){
- WHERETRACE(("... sorting increases OR cost %.9g to %.9g\n",
- rTotal, rTotal+nRow*estLog(nRow)));
- rTotal += nRow*estLog(nRow);
- }
-
- /* If the cost of scanning using this OR term for optimization is
- ** less than the current cost stored in pCost, replace the contents
- ** of pCost. */
- WHERETRACE(("... multi-index OR cost=%.9g nrow=%.9g\n", rTotal, nRow));
- if( rTotal<p->cost.rCost ){
- p->cost.rCost = rTotal;
- p->cost.used = used;
- p->cost.plan.nRow = nRow;
- p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0;
- p->cost.plan.wsFlags = flags;
- p->cost.plan.u.pTerm = pTerm;
- }
- }
- }
-#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
-}
-
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
/*
** Return TRUE if the WHERE clause term pTerm is of a form where it
@@ -106235,88 +108643,13 @@ static int termCanDriveIndex(
if( pTerm->leftCursor!=pSrc->iCursor ) return 0;
if( (pTerm->eOperator & WO_EQ)==0 ) return 0;
if( (pTerm->prereqRight & notReady)!=0 ) return 0;
+ if( pTerm->u.leftColumn<0 ) return 0;
aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity;
if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0;
return 1;
}
#endif
-#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
-/*
-** If the query plan for pSrc specified in pCost is a full table scan
-** and indexing is allows (if there is no NOT INDEXED clause) and it
-** possible to construct a transient index that would perform better
-** than a full table scan even when the cost of constructing the index
-** is taken into account, then alter the query plan to use the
-** transient index.
-*/
-static void bestAutomaticIndex(WhereBestIdx *p){
- Parse *pParse = p->pParse; /* The parsing context */
- WhereClause *pWC = p->pWC; /* The WHERE clause */
- struct SrcList_item *pSrc = p->pSrc; /* The FROM clause term to search */
- double nTableRow; /* Rows in the input table */
- double logN; /* log(nTableRow) */
- double costTempIdx; /* per-query cost of the transient index */
- WhereTerm *pTerm; /* A single term of the WHERE clause */
- WhereTerm *pWCEnd; /* End of pWC->a[] */
- Table *pTable; /* Table tht might be indexed */
-
- if( pParse->nQueryLoop<=(double)1 ){
- /* There is no point in building an automatic index for a single scan */
- return;
- }
- if( (pParse->db->flags & SQLITE_AutoIndex)==0 ){
- /* Automatic indices are disabled at run-time */
- return;
- }
- if( (p->cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0
- && (p->cost.plan.wsFlags & WHERE_COVER_SCAN)==0
- ){
- /* We already have some kind of index in use for this query. */
- return;
- }
- if( pSrc->viaCoroutine ){
- /* Cannot index a co-routine */
- return;
- }
- if( pSrc->notIndexed ){
- /* The NOT INDEXED clause appears in the SQL. */
- return;
- }
- if( pSrc->isCorrelated ){
- /* The source is a correlated sub-query. No point in indexing it. */
- return;
- }
-
- assert( pParse->nQueryLoop >= (double)1 );
- pTable = pSrc->pTab;
- nTableRow = pTable->nRowEst;
- logN = estLog(nTableRow);
- costTempIdx = 2*logN*(nTableRow/pParse->nQueryLoop + 1);
- if( costTempIdx>=p->cost.rCost ){
- /* The cost of creating the transient table would be greater than
- ** doing the full table scan */
- return;
- }
-
- /* Search for any equality comparison term */
- pWCEnd = &pWC->a[pWC->nTerm];
- for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
- if( termCanDriveIndex(pTerm, pSrc, p->notReady) ){
- WHERETRACE(("auto-index reduces cost from %.1f to %.1f\n",
- p->cost.rCost, costTempIdx));
- p->cost.rCost = costTempIdx;
- p->cost.plan.nRow = logN + 1;
- p->cost.plan.wsFlags = WHERE_TEMP_INDEX;
- p->cost.used = pTerm->prereqRight;
- break;
- }
- }
-}
-#else
-# define bestAutomaticIndex(A) /* no-op */
-#endif /* SQLITE_OMIT_AUTOMATIC_INDEX */
-
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
/*
@@ -106346,8 +108679,10 @@ static void constructAutomaticIndex(
int i; /* Loop counter */
int mxBitCol; /* Maximum column in pSrc->colUsed */
CollSeq *pColl; /* Collating sequence to on a column */
+ WhereLoop *pLoop; /* The Loop object */
Bitmask idxCols; /* Bitmap of columns used for indexing */
Bitmask extraCols; /* Bitmap of additional columns */
+ u8 sentWarning = 0; /* True if a warnning has been issued */
/* Generate code to skip over the creation and initialization of the
** transient index on 2nd and subsequent iterations of the loop. */
@@ -106360,21 +108695,31 @@ static void constructAutomaticIndex(
nColumn = 0;
pTable = pSrc->pTab;
pWCEnd = &pWC->a[pWC->nTerm];
+ pLoop = pLevel->pWLoop;
idxCols = 0;
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
if( termCanDriveIndex(pTerm, pSrc, notReady) ){
int iCol = pTerm->u.leftColumn;
- Bitmask cMask = iCol>=BMS ? ((Bitmask)1)<<(BMS-1) : ((Bitmask)1)<<iCol;
+ Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol);
testcase( iCol==BMS );
testcase( iCol==BMS-1 );
+ if( !sentWarning ){
+ sqlite3_log(SQLITE_WARNING_AUTOINDEX,
+ "automatic index on %s(%s)", pTable->zName,
+ pTable->aCol[iCol].zName);
+ sentWarning = 1;
+ }
if( (idxCols & cMask)==0 ){
- nColumn++;
+ if( whereLoopResize(pParse->db, pLoop, nColumn+1) ) return;
+ pLoop->aLTerm[nColumn++] = pTerm;
idxCols |= cMask;
}
}
}
assert( nColumn>0 );
- pLevel->plan.nEq = nColumn;
+ pLoop->u.btree.nEq = pLoop->nLTerm = nColumn;
+ pLoop->wsFlags = WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WHERE_INDEXED
+ | WHERE_AUTO_INDEX;
/* Count the number of additional columns needed to create a
** covering index. A "covering index" is an index that contains all
@@ -106384,17 +108729,17 @@ static void constructAutomaticIndex(
** original table changes and the index and table cannot both be used
** if they go out of sync.
*/
- extraCols = pSrc->colUsed & (~idxCols | (((Bitmask)1)<<(BMS-1)));
+ extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1));
mxBitCol = (pTable->nCol >= BMS-1) ? BMS-1 : pTable->nCol;
testcase( pTable->nCol==BMS-1 );
testcase( pTable->nCol==BMS-2 );
for(i=0; i<mxBitCol; i++){
- if( extraCols & (((Bitmask)1)<<i) ) nColumn++;
+ if( extraCols & MASKBIT(i) ) nColumn++;
}
- if( pSrc->colUsed & (((Bitmask)1)<<(BMS-1)) ){
+ if( pSrc->colUsed & MASKBIT(BMS-1) ){
nColumn += pTable->nCol - BMS + 1;
}
- pLevel->plan.wsFlags |= WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WO_EQ;
+ pLoop->wsFlags |= WHERE_COLUMN_EQ | WHERE_IDX_ONLY;
/* Construct the Index object to describe this index */
nByte = sizeof(Index);
@@ -106403,7 +108748,7 @@ static void constructAutomaticIndex(
nByte += nColumn; /* Index.aSortOrder */
pIdx = sqlite3DbMallocZero(pParse->db, nByte);
if( pIdx==0 ) return;
- pLevel->plan.u.pIdx = pIdx;
+ pLoop->u.btree.pIndex = pIdx;
pIdx->azColl = (char**)&pIdx[1];
pIdx->aiColumn = (int*)&pIdx->azColl[nColumn];
pIdx->aSortOrder = (u8*)&pIdx->aiColumn[nColumn];
@@ -106415,7 +108760,9 @@ static void constructAutomaticIndex(
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
if( termCanDriveIndex(pTerm, pSrc, notReady) ){
int iCol = pTerm->u.leftColumn;
- Bitmask cMask = iCol>=BMS ? ((Bitmask)1)<<(BMS-1) : ((Bitmask)1)<<iCol;
+ Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol);
+ testcase( iCol==BMS-1 );
+ testcase( iCol==BMS );
if( (idxCols & cMask)==0 ){
Expr *pX = pTerm->pExpr;
idxCols |= cMask;
@@ -106426,18 +108773,18 @@ static void constructAutomaticIndex(
}
}
}
- assert( (u32)n==pLevel->plan.nEq );
+ assert( (u32)n==pLoop->u.btree.nEq );
/* Add additional columns needed to make the automatic index into
** a covering index */
for(i=0; i<mxBitCol; i++){
- if( extraCols & (((Bitmask)1)<<i) ){
+ if( extraCols & MASKBIT(i) ){
pIdx->aiColumn[n] = i;
pIdx->azColl[n] = "BINARY";
n++;
}
}
- if( pSrc->colUsed & (((Bitmask)1)<<(BMS-1)) ){
+ if( pSrc->colUsed & MASKBIT(BMS-1) ){
for(i=BMS-1; i<pTable->nCol; i++){
pIdx->aiColumn[n] = i;
pIdx->azColl[n] = "BINARY";
@@ -106449,6 +108796,7 @@ static void constructAutomaticIndex(
/* Create the automatic index */
pKeyinfo = sqlite3IndexKeyinfo(pParse, pIdx);
assert( pLevel->iIdxCur>=0 );
+ pLevel->iIdxCur = pParse->nTab++;
sqlite3VdbeAddOp4(v, OP_OpenAutoindex, pLevel->iIdxCur, nColumn+1, 0,
(char*)pKeyinfo, P4_KEYINFO_HANDOFF);
VdbeComment((v, "for %s", pTable->zName));
@@ -106456,7 +108804,7 @@ static void constructAutomaticIndex(
/* Fill the automatic index with content */
addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur);
regRecord = sqlite3GetTempReg(pParse);
- sqlite3GenerateIndexKey(pParse, pIdx, pLevel->iTabCur, regRecord, 1);
+ sqlite3GenerateIndexKey(pParse, pIdx, pLevel->iTabCur, regRecord, 1, 0);
sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1);
@@ -106475,11 +108823,12 @@ static void constructAutomaticIndex(
** responsibility of the caller to eventually release the structure
** by passing the pointer returned by this function to sqlite3_free().
*/
-static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){
- Parse *pParse = p->pParse;
- WhereClause *pWC = p->pWC;
- struct SrcList_item *pSrc = p->pSrc;
- ExprList *pOrderBy = p->pOrderBy;
+static sqlite3_index_info *allocateIndexInfo(
+ Parse *pParse,
+ WhereClause *pWC,
+ struct SrcList_item *pSrc,
+ ExprList *pOrderBy
+){
int i, j;
int nTerm;
struct sqlite3_index_constraint *pIdxCons;
@@ -106489,8 +108838,6 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){
int nOrderBy;
sqlite3_index_info *pIdxInfo;
- WHERETRACE(("Recomputing index info for %s...\n", pSrc->pTab->zName));
-
/* Count the number of possible WHERE clause constraints referring
** to this virtual table */
for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
@@ -106526,7 +108873,6 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){
+ sizeof(*pIdxOrderBy)*nOrderBy );
if( pIdxInfo==0 ){
sqlite3ErrorMsg(pParse, "out of memory");
- /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
return 0;
}
@@ -106582,8 +108928,8 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){
/*
** The table object reference passed as the second argument to this function
** must represent a virtual table. This function invokes the xBestIndex()
-** method of the virtual table with the sqlite3_index_info pointer passed
-** as the argument.
+** 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
@@ -106598,7 +108944,6 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
int i;
int rc;
- WHERETRACE(("xBestIndex for %s\n", pTab->zName));
TRACE_IDX_INPUTS(p);
rc = pVtab->pModule->xBestIndex(pVtab, p);
TRACE_IDX_OUTPUTS(p);
@@ -106624,209 +108969,10 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
return pParse->nErr;
}
+#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
-/*
-** Compute the best index for a virtual table.
-**
-** The best index is computed by the xBestIndex method of the virtual
-** table module. This routine is really just a wrapper that sets up
-** the sqlite3_index_info structure that is used to communicate with
-** xBestIndex.
-**
-** In a join, this routine might be called multiple times for the
-** same virtual table. The sqlite3_index_info structure is created
-** and initialized on the first invocation and reused on all subsequent
-** invocations. The sqlite3_index_info structure is also used when
-** code is generated to access the virtual table. The whereInfoDelete()
-** routine takes care of freeing the sqlite3_index_info structure after
-** everybody has finished with it.
-*/
-static void bestVirtualIndex(WhereBestIdx *p){
- Parse *pParse = p->pParse; /* The parsing context */
- WhereClause *pWC = p->pWC; /* The WHERE clause */
- struct SrcList_item *pSrc = p->pSrc; /* The FROM clause term to search */
- Table *pTab = pSrc->pTab;
- sqlite3_index_info *pIdxInfo;
- struct sqlite3_index_constraint *pIdxCons;
- struct sqlite3_index_constraint_usage *pUsage;
- WhereTerm *pTerm;
- int i, j;
- int nOrderBy;
- int bAllowIN; /* Allow IN optimizations */
- double rCost;
-
- /* Make sure wsFlags is initialized to some sane value. Otherwise, if the
- ** malloc in allocateIndexInfo() fails and this function returns leaving
- ** wsFlags in an uninitialized state, the caller may behave unpredictably.
- */
- memset(&p->cost, 0, sizeof(p->cost));
- p->cost.plan.wsFlags = WHERE_VIRTUALTABLE;
-
- /* If the sqlite3_index_info structure has not been previously
- ** allocated and initialized, then allocate and initialize it now.
- */
- pIdxInfo = *p->ppIdxInfo;
- if( pIdxInfo==0 ){
- *p->ppIdxInfo = pIdxInfo = allocateIndexInfo(p);
- }
- if( pIdxInfo==0 ){
- return;
- }
-
- /* At this point, the sqlite3_index_info structure that pIdxInfo points
- ** to will have been initialized, either during the current invocation or
- ** during some prior invocation. Now we just have to customize the
- ** details of pIdxInfo for the current invocation and pass it to
- ** xBestIndex.
- */
-
- /* The module name must be defined. Also, by this point there must
- ** be a pointer to an sqlite3_vtab structure. Otherwise
- ** sqlite3ViewGetColumnNames() would have picked up the error.
- */
- assert( pTab->azModuleArg && pTab->azModuleArg[0] );
- assert( sqlite3GetVTable(pParse->db, pTab) );
-
- /* Try once or twice. On the first attempt, allow IN optimizations.
- ** If an IN optimization is accepted by the virtual table xBestIndex
- ** method, but the pInfo->aConstrainUsage.omit flag is not set, then
- ** the query will not work because it might allow duplicate rows in
- ** output. In that case, run the xBestIndex method a second time
- ** without the IN constraints. Usually this loop only runs once.
- ** The loop will exit using a "break" statement.
- */
- for(bAllowIN=1; 1; bAllowIN--){
- assert( bAllowIN==0 || bAllowIN==1 );
-
- /* Set the aConstraint[].usable fields and initialize all
- ** output variables to zero.
- **
- ** aConstraint[].usable is true for constraints where the right-hand
- ** side contains only references to tables to the left of the current
- ** table. In other words, if the constraint is of the form:
- **
- ** column = expr
- **
- ** and we are evaluating a join, then the constraint on column is
- ** only valid if all tables referenced in expr occur to the left
- ** of the table containing column.
- **
- ** The aConstraints[] array contains entries for all constraints
- ** on the current table. That way we only have to compute it once
- ** even though we might try to pick the best index multiple times.
- ** For each attempt at picking an index, the order of tables in the
- ** join might be different so we have to recompute the usable flag
- ** each time.
- */
- pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
- pUsage = pIdxInfo->aConstraintUsage;
- for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
- j = pIdxCons->iTermOffset;
- pTerm = &pWC->a[j];
- if( (pTerm->prereqRight&p->notReady)==0
- && (bAllowIN || (pTerm->eOperator & WO_IN)==0)
- ){
- pIdxCons->usable = 1;
- }else{
- pIdxCons->usable = 0;
- }
- }
- memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint);
- if( pIdxInfo->needToFreeIdxStr ){
- sqlite3_free(pIdxInfo->idxStr);
- }
- pIdxInfo->idxStr = 0;
- pIdxInfo->idxNum = 0;
- pIdxInfo->needToFreeIdxStr = 0;
- pIdxInfo->orderByConsumed = 0;
- /* ((double)2) In case of SQLITE_OMIT_FLOATING_POINT... */
- pIdxInfo->estimatedCost = SQLITE_BIG_DBL / ((double)2);
- nOrderBy = pIdxInfo->nOrderBy;
- if( !p->pOrderBy ){
- pIdxInfo->nOrderBy = 0;
- }
-
- if( vtabBestIndex(pParse, pTab, pIdxInfo) ){
- return;
- }
-
- pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
- for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
- if( pUsage[i].argvIndex>0 ){
- j = pIdxCons->iTermOffset;
- pTerm = &pWC->a[j];
- p->cost.used |= pTerm->prereqRight;
- if( (pTerm->eOperator & WO_IN)!=0 ){
- if( pUsage[i].omit==0 ){
- /* Do not attempt to use an IN constraint if the virtual table
- ** says that the equivalent EQ constraint cannot be safely omitted.
- ** If we do attempt to use such a constraint, some rows might be
- ** repeated in the output. */
- break;
- }
- /* A virtual table that is constrained by an IN clause may not
- ** consume the ORDER BY clause because (1) the order of IN terms
- ** is not necessarily related to the order of output terms and
- ** (2) Multiple outputs from a single IN value will not merge
- ** together. */
- pIdxInfo->orderByConsumed = 0;
- }
- }
- }
- if( i>=pIdxInfo->nConstraint ) break;
- }
-
- /* The orderByConsumed signal is only valid if all outer loops collectively
- ** generate just a single row of output.
- */
- if( pIdxInfo->orderByConsumed ){
- for(i=0; i<p->i; i++){
- if( (p->aLevel[i].plan.wsFlags & WHERE_UNIQUE)==0 ){
- pIdxInfo->orderByConsumed = 0;
- }
- }
- }
-
- /* If there is an ORDER BY clause, and the selected virtual table index
- ** does not satisfy it, increase the cost of the scan accordingly. This
- ** matches the processing for non-virtual tables in bestBtreeIndex().
- */
- rCost = pIdxInfo->estimatedCost;
- if( p->pOrderBy && pIdxInfo->orderByConsumed==0 ){
- rCost += estLog(rCost)*rCost;
- }
-
- /* The cost is not allowed to be larger than SQLITE_BIG_DBL (the
- ** inital value of lowestCost in this loop. If it is, then the
- ** (cost<lowestCost) test below will never be true.
- **
- ** Use "(double)2" instead of "2.0" in case OMIT_FLOATING_POINT
- ** is defined.
- */
- if( (SQLITE_BIG_DBL/((double)2))<rCost ){
- p->cost.rCost = (SQLITE_BIG_DBL/((double)2));
- }else{
- p->cost.rCost = rCost;
- }
- p->cost.plan.u.pVtabIdx = pIdxInfo;
- if( pIdxInfo->orderByConsumed ){
- p->cost.plan.wsFlags |= WHERE_ORDERED;
- p->cost.plan.nOBSat = nOrderBy;
- }else{
- p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0;
- }
- p->cost.plan.nEq = 0;
- pIdxInfo->nOrderBy = nOrderBy;
-
- /* Try to find a more efficient access pattern by using multiple indexes
- ** to optimize an OR expression within the WHERE clause.
- */
- bestOrClauseIndex(p);
-}
-#endif /* SQLITE_OMIT_VIRTUALTABLE */
-
-#ifdef SQLITE_ENABLE_STAT3
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
/*
** Estimate the location of a particular key among all keys in an
** index. Store the results in aStat as follows:
@@ -106836,140 +108982,76 @@ static void bestVirtualIndex(WhereBestIdx *p){
**
** Return SQLITE_OK on success.
*/
-static int whereKeyStats(
+static void whereKeyStats(
Parse *pParse, /* Database connection */
Index *pIdx, /* Index to consider domain of */
- sqlite3_value *pVal, /* Value to consider */
+ UnpackedRecord *pRec, /* Vector of values to consider */
int roundUp, /* Round up if true. Round down if false */
tRowcnt *aStat /* OUT: stats written here */
){
- tRowcnt n;
- IndexSample *aSample;
- int i, eType;
- int isEq = 0;
- i64 v;
- double r, rS;
+ IndexSample *aSample = pIdx->aSample;
+ int iCol; /* Index of required stats in anEq[] etc. */
+ int iMin = 0; /* Smallest sample not yet tested */
+ int i = pIdx->nSample; /* Smallest sample larger than or equal to pRec */
+ int iTest; /* Next sample to test */
+ int res; /* Result of comparison operation */
- assert( roundUp==0 || roundUp==1 );
+#ifndef SQLITE_DEBUG
+ UNUSED_PARAMETER( pParse );
+#endif
+ assert( pRec!=0 || pParse->db->mallocFailed );
+ if( pRec==0 ) return;
+ iCol = pRec->nField - 1;
assert( pIdx->nSample>0 );
- if( pVal==0 ) return SQLITE_ERROR;
- n = pIdx->aiRowEst[0];
- aSample = pIdx->aSample;
- eType = sqlite3_value_type(pVal);
-
- if( eType==SQLITE_INTEGER ){
- v = sqlite3_value_int64(pVal);
- r = (i64)v;
- for(i=0; i<pIdx->nSample; i++){
- if( aSample[i].eType==SQLITE_NULL ) continue;
- if( aSample[i].eType>=SQLITE_TEXT ) break;
- if( aSample[i].eType==SQLITE_INTEGER ){
- if( aSample[i].u.i>=v ){
- isEq = aSample[i].u.i==v;
- break;
- }
- }else{
- assert( aSample[i].eType==SQLITE_FLOAT );
- if( aSample[i].u.r>=r ){
- isEq = aSample[i].u.r==r;
- break;
- }
- }
- }
- }else if( eType==SQLITE_FLOAT ){
- r = sqlite3_value_double(pVal);
- for(i=0; i<pIdx->nSample; i++){
- if( aSample[i].eType==SQLITE_NULL ) continue;
- if( aSample[i].eType>=SQLITE_TEXT ) break;
- if( aSample[i].eType==SQLITE_FLOAT ){
- rS = aSample[i].u.r;
- }else{
- rS = aSample[i].u.i;
- }
- if( rS>=r ){
- isEq = rS==r;
- break;
- }
+ assert( pRec->nField>0 && iCol<pIdx->nSampleCol );
+ do{
+ iTest = (iMin+i)/2;
+ res = sqlite3VdbeRecordCompare(aSample[iTest].n, aSample[iTest].p, pRec);
+ if( res<0 ){
+ iMin = iTest+1;
+ }else{
+ i = iTest;
}
- }else if( eType==SQLITE_NULL ){
- i = 0;
- if( aSample[0].eType==SQLITE_NULL ) isEq = 1;
+ }while( res && iMin<i );
+
+#ifdef SQLITE_DEBUG
+ /* The following assert statements check that the binary search code
+ ** above found the right answer. This block serves no purpose other
+ ** than to invoke the asserts. */
+ if( res==0 ){
+ /* If (res==0) is true, then sample $i must be equal to pRec */
+ assert( i<pIdx->nSample );
+ assert( 0==sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)
+ || pParse->db->mallocFailed );
}else{
- assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
- for(i=0; i<pIdx->nSample; i++){
- if( aSample[i].eType==SQLITE_TEXT || aSample[i].eType==SQLITE_BLOB ){
- break;
- }
- }
- if( i<pIdx->nSample ){
- sqlite3 *db = pParse->db;
- CollSeq *pColl;
- const u8 *z;
- if( eType==SQLITE_BLOB ){
- z = (const u8 *)sqlite3_value_blob(pVal);
- pColl = db->pDfltColl;
- assert( pColl->enc==SQLITE_UTF8 );
- }else{
- pColl = sqlite3GetCollSeq(pParse, SQLITE_UTF8, 0, *pIdx->azColl);
- if( pColl==0 ){
- return SQLITE_ERROR;
- }
- z = (const u8 *)sqlite3ValueText(pVal, pColl->enc);
- if( !z ){
- return SQLITE_NOMEM;
- }
- assert( z && pColl && pColl->xCmp );
- }
- n = sqlite3ValueBytes(pVal, pColl->enc);
-
- for(; i<pIdx->nSample; i++){
- int c;
- int eSampletype = aSample[i].eType;
- if( eSampletype<eType ) continue;
- if( eSampletype!=eType ) break;
-#ifndef SQLITE_OMIT_UTF16
- if( pColl->enc!=SQLITE_UTF8 ){
- int nSample;
- char *zSample = sqlite3Utf8to16(
- db, pColl->enc, aSample[i].u.z, aSample[i].nByte, &nSample
- );
- if( !zSample ){
- assert( db->mallocFailed );
- return SQLITE_NOMEM;
- }
- c = pColl->xCmp(pColl->pUser, nSample, zSample, n, z);
- sqlite3DbFree(db, zSample);
- }else
-#endif
- {
- c = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z);
- }
- if( c>=0 ){
- if( c==0 ) isEq = 1;
- break;
- }
- }
- }
+ /* Otherwise, pRec must be smaller than sample $i and larger than
+ ** sample ($i-1). */
+ assert( i==pIdx->nSample
+ || sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)>0
+ || pParse->db->mallocFailed );
+ assert( i==0
+ || sqlite3VdbeRecordCompare(aSample[i-1].n, aSample[i-1].p, pRec)<0
+ || pParse->db->mallocFailed );
}
+#endif /* ifdef SQLITE_DEBUG */
/* At this point, aSample[i] is the first sample that is greater than
** or equal to pVal. Or if i==pIdx->nSample, then all samples are less
- ** than pVal. If aSample[i]==pVal, then isEq==1.
+ ** than pVal. If aSample[i]==pVal, then res==0.
*/
- if( isEq ){
- assert( i<pIdx->nSample );
- aStat[0] = aSample[i].nLt;
- aStat[1] = aSample[i].nEq;
+ if( res==0 ){
+ aStat[0] = aSample[i].anLt[iCol];
+ aStat[1] = aSample[i].anEq[iCol];
}else{
tRowcnt iLower, iUpper, iGap;
if( i==0 ){
iLower = 0;
- iUpper = aSample[0].nLt;
+ iUpper = aSample[0].anLt[iCol];
}else{
- iUpper = i>=pIdx->nSample ? n : aSample[i].nLt;
- iLower = aSample[i-1].nEq + aSample[i-1].nLt;
+ iUpper = i>=pIdx->nSample ? pIdx->aiRowEst[0] : aSample[i].anLt[iCol];
+ iLower = aSample[i-1].anEq[iCol] + aSample[i-1].anLt[iCol];
}
- aStat[1] = pIdx->avgEq;
+ aStat[1] = (pIdx->nColumn>iCol ? pIdx->aAvgEq[iCol] : 1);
if( iLower>=iUpper ){
iGap = 0;
}else{
@@ -106982,44 +109064,8 @@ static int whereKeyStats(
}
aStat[0] = iLower + iGap;
}
- return SQLITE_OK;
-}
-#endif /* SQLITE_ENABLE_STAT3 */
-
-/*
-** If expression pExpr represents a literal value, set *pp to point to
-** an sqlite3_value structure containing the same value, with affinity
-** aff applied to it, before returning. It is the responsibility of the
-** caller to eventually release this structure by passing it to
-** sqlite3ValueFree().
-**
-** If the current parse is a recompile (sqlite3Reprepare()) and pExpr
-** is an SQL variable that currently has a non-NULL value bound to it,
-** create an sqlite3_value structure containing this value, again with
-** affinity aff applied to it, instead.
-**
-** If neither of the above apply, set *pp to NULL.
-**
-** If an error occurs, return an error code. Otherwise, SQLITE_OK.
-*/
-#ifdef SQLITE_ENABLE_STAT3
-static int valueFromExpr(
- Parse *pParse,
- Expr *pExpr,
- u8 aff,
- sqlite3_value **pp
-){
- if( pExpr->op==TK_VARIABLE
- || (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE)
- ){
- int iVar = pExpr->iColumn;
- sqlite3VdbeSetVarmask(pParse->pVdbe, iVar);
- *pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff);
- return SQLITE_OK;
- }
- return sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, aff, pp);
}
-#endif
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
/*
** This function is used to estimate the number of rows that will be visited
@@ -107036,97 +109082,161 @@ static int valueFromExpr(
** If either of the upper or lower bound is not present, then NULL is passed in
** place of the corresponding WhereTerm.
**
-** The nEq parameter is passed the index of the index column subject to the
-** range constraint. Or, equivalently, the number of equality constraints
-** optimized by the proposed index scan. For example, assuming index p is
-** on t1(a, b), and the SQL query is:
+** The value in (pBuilder->pNew->u.btree.nEq) is the index of the index
+** column subject to the range constraint. Or, equivalently, the number of
+** equality constraints optimized by the proposed index scan. For example,
+** assuming index p is on t1(a, b), and the SQL query is:
**
** ... FROM t1 WHERE a = ? AND b > ? AND b < ? ...
**
-** then nEq should be passed the value 1 (as the range restricted column,
-** b, is the second left-most column of the index). Or, if the query is:
+** then nEq is set to 1 (as the range restricted column, b, is the second
+** left-most column of the index). Or, if the query is:
**
** ... FROM t1 WHERE a > ? AND a < ? ...
**
-** then nEq should be passed 0.
+** then nEq is set to 0.
**
-** The returned value is an integer divisor to reduce the estimated
-** search space. A return value of 1 means that range constraints are
-** no help at all. A return value of 2 means range constraints are
-** expected to reduce the search space by half. And so forth...
-**
-** In the absence of sqlite_stat3 ANALYZE data, each range inequality
-** reduces the search space by a factor of 4. Hence a single constraint (x>?)
-** results in a return of 4 and a range constraint (x>? AND x<?) results
-** in a return of 16.
+** When this function is called, *pnOut is set to the sqlite3LogEst() of the
+** number of rows that the index scan is expected to visit without
+** considering the range constraints. If nEq is 0, this is the number of
+** rows in the index. Assuming no error occurs, *pnOut is adjusted (reduced)
+** to account for the range contraints pLower and pUpper.
+**
+** In the absence of sqlite_stat4 ANALYZE data, or if such data cannot be
+** used, each range inequality reduces the search space by a factor of 4.
+** Hence a pair of constraints (x>? AND x<?) reduces the expected number of
+** rows visited by a factor of 16.
*/
static int whereRangeScanEst(
Parse *pParse, /* Parsing & code generating context */
- Index *p, /* The index containing the range-compared column; "x" */
- int nEq, /* index into p->aCol[] of the range-compared column */
+ WhereLoopBuilder *pBuilder,
WhereTerm *pLower, /* Lower bound on the range. ex: "x>123" Might be NULL */
WhereTerm *pUpper, /* Upper bound on the range. ex: "x<455" Might be NULL */
- double *pRangeDiv /* OUT: Reduce search space by this divisor */
+ WhereLoop *pLoop /* Modify the .nOut and maybe .rRun fields */
){
int rc = SQLITE_OK;
+ int nOut = pLoop->nOut;
+ LogEst nNew;
-#ifdef SQLITE_ENABLE_STAT3
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ Index *p = pLoop->u.btree.pIndex;
+ int nEq = pLoop->u.btree.nEq;
- if( nEq==0 && p->nSample ){
- sqlite3_value *pRangeVal;
- tRowcnt iLower = 0;
- tRowcnt iUpper = p->aiRowEst[0];
+ if( p->nSample>0
+ && nEq==pBuilder->nRecValid
+ && nEq<p->nSampleCol
+ && OptimizationEnabled(pParse->db, SQLITE_Stat3)
+ ){
+ UnpackedRecord *pRec = pBuilder->pRec;
tRowcnt a[2];
- u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity;
+ u8 aff;
+ /* Variable iLower will be set to the estimate of the number of rows in
+ ** the index that are less than the lower bound of the range query. The
+ ** lower bound being the concatenation of $P and $L, where $P is the
+ ** key-prefix formed by the nEq values matched against the nEq left-most
+ ** columns of the index, and $L is the value in pLower.
+ **
+ ** Or, if pLower is NULL or $L cannot be extracted from it (because it
+ ** is not a simple variable or literal value), the lower bound of the
+ ** range is $P. Due to a quirk in the way whereKeyStats() works, even
+ ** if $L is available, whereKeyStats() is called for both ($P) and
+ ** ($P:$L) and the larger of the two returned values used.
+ **
+ ** Similarly, iUpper is to be set to the estimate of the number of rows
+ ** less than the upper bound of the range query. Where the upper bound
+ ** is either ($P) or ($P:$U). Again, even if $U is available, both values
+ ** of iUpper are requested of whereKeyStats() and the smaller used.
+ */
+ tRowcnt iLower;
+ tRowcnt iUpper;
+
+ if( nEq==p->nColumn ){
+ aff = SQLITE_AFF_INTEGER;
+ }else{
+ aff = p->pTable->aCol[p->aiColumn[nEq]].affinity;
+ }
+ /* Determine iLower and iUpper using ($P) only. */
+ if( nEq==0 ){
+ iLower = 0;
+ iUpper = p->aiRowEst[0];
+ }else{
+ /* Note: this call could be optimized away - since the same values must
+ ** have been requested when testing key $P in whereEqualScanEst(). */
+ whereKeyStats(pParse, p, pRec, 0, a);
+ iLower = a[0];
+ iUpper = a[0] + a[1];
+ }
+
+ /* If possible, improve on the iLower estimate using ($P:$L). */
if( pLower ){
+ int bOk; /* True if value is extracted from pExpr */
Expr *pExpr = pLower->pExpr->pRight;
- rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
assert( (pLower->eOperator & (WO_GT|WO_GE))!=0 );
- if( rc==SQLITE_OK
- && whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK
- ){
- iLower = a[0];
- if( (pLower->eOperator & WO_GT)!=0 ) iLower += a[1];
+ rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk);
+ if( rc==SQLITE_OK && bOk ){
+ tRowcnt iNew;
+ whereKeyStats(pParse, p, pRec, 0, a);
+ iNew = a[0] + ((pLower->eOperator & WO_GT) ? a[1] : 0);
+ if( iNew>iLower ) iLower = iNew;
+ nOut--;
}
- sqlite3ValueFree(pRangeVal);
}
- if( rc==SQLITE_OK && pUpper ){
+
+ /* If possible, improve on the iUpper estimate using ($P:$U). */
+ if( pUpper ){
+ int bOk; /* True if value is extracted from pExpr */
Expr *pExpr = pUpper->pExpr->pRight;
- rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
assert( (pUpper->eOperator & (WO_LT|WO_LE))!=0 );
- if( rc==SQLITE_OK
- && whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK
- ){
- iUpper = a[0];
- if( (pUpper->eOperator & WO_LE)!=0 ) iUpper += a[1];
+ rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk);
+ if( rc==SQLITE_OK && bOk ){
+ tRowcnt iNew;
+ whereKeyStats(pParse, p, pRec, 1, a);
+ iNew = a[0] + ((pUpper->eOperator & WO_LE) ? a[1] : 0);
+ if( iNew<iUpper ) iUpper = iNew;
+ nOut--;
}
- sqlite3ValueFree(pRangeVal);
}
+
+ pBuilder->pRec = pRec;
if( rc==SQLITE_OK ){
- if( iUpper<=iLower ){
- *pRangeDiv = (double)p->aiRowEst[0];
+ if( iUpper>iLower ){
+ nNew = sqlite3LogEst(iUpper - iLower);
}else{
- *pRangeDiv = (double)p->aiRowEst[0]/(double)(iUpper - iLower);
+ nNew = 10; assert( 10==sqlite3LogEst(2) );
+ }
+ if( nNew<nOut ){
+ nOut = nNew;
}
- WHERETRACE(("range scan regions: %u..%u div=%g\n",
- (u32)iLower, (u32)iUpper, *pRangeDiv));
+ pLoop->nOut = (LogEst)nOut;
+ WHERETRACE(0x100, ("range scan regions: %u..%u est=%d\n",
+ (u32)iLower, (u32)iUpper, nOut));
return SQLITE_OK;
}
}
#else
UNUSED_PARAMETER(pParse);
- UNUSED_PARAMETER(p);
- UNUSED_PARAMETER(nEq);
+ UNUSED_PARAMETER(pBuilder);
#endif
assert( pLower || pUpper );
- *pRangeDiv = (double)1;
- if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ) *pRangeDiv *= (double)4;
- if( pUpper ) *pRangeDiv *= (double)4;
+ /* TUNING: Each inequality constraint reduces the search space 4-fold.
+ ** A BETWEEN operator, therefore, reduces the search space 16-fold */
+ nNew = nOut;
+ if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ){
+ nNew -= 20; assert( 20==sqlite3LogEst(4) );
+ nOut--;
+ }
+ if( pUpper ){
+ nNew -= 20; assert( 20==sqlite3LogEst(4) );
+ nOut--;
+ }
+ if( nNew<10 ) nNew = 10;
+ if( nNew<nOut ) nOut = nNew;
+ pLoop->nOut = (LogEst)nOut;
return rc;
}
-#ifdef SQLITE_ENABLE_STAT3
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
/*
** Estimate the number of rows that will be returned based on
** an equality constraint x=VALUE and where that VALUE occurs in
@@ -107146,37 +109256,53 @@ static int whereRangeScanEst(
*/
static int whereEqualScanEst(
Parse *pParse, /* Parsing & code generating context */
- Index *p, /* The index whose left-most column is pTerm */
+ WhereLoopBuilder *pBuilder,
Expr *pExpr, /* Expression for VALUE in the x=VALUE constraint */
- double *pnRow /* Write the revised row estimate here */
+ tRowcnt *pnRow /* Write the revised row estimate here */
){
- sqlite3_value *pRhs = 0; /* VALUE on right-hand side of pTerm */
+ Index *p = pBuilder->pNew->u.btree.pIndex;
+ int nEq = pBuilder->pNew->u.btree.nEq;
+ UnpackedRecord *pRec = pBuilder->pRec;
u8 aff; /* Column affinity */
int rc; /* Subfunction return code */
tRowcnt a[2]; /* Statistics */
+ int bOk;
+ assert( nEq>=1 );
+ assert( nEq<=(p->nColumn+1) );
assert( p->aSample!=0 );
assert( p->nSample>0 );
- aff = p->pTable->aCol[p->aiColumn[0]].affinity;
- if( pExpr ){
- rc = valueFromExpr(pParse, pExpr, aff, &pRhs);
- if( rc ) goto whereEqualScanEst_cancel;
- }else{
- pRhs = sqlite3ValueNew(pParse->db);
+ assert( pBuilder->nRecValid<nEq );
+
+ /* If values are not available for all fields of the index to the left
+ ** of this one, no estimate can be made. Return SQLITE_NOTFOUND. */
+ if( pBuilder->nRecValid<(nEq-1) ){
+ return SQLITE_NOTFOUND;
}
- if( pRhs==0 ) return SQLITE_NOTFOUND;
- rc = whereKeyStats(pParse, p, pRhs, 0, a);
- if( rc==SQLITE_OK ){
- WHERETRACE(("equality scan regions: %d\n", (int)a[1]));
- *pnRow = a[1];
+
+ /* This is an optimization only. The call to sqlite3Stat4ProbeSetValue()
+ ** below would return the same value. */
+ if( nEq>p->nColumn ){
+ *pnRow = 1;
+ return SQLITE_OK;
}
-whereEqualScanEst_cancel:
- sqlite3ValueFree(pRhs);
+
+ aff = p->pTable->aCol[p->aiColumn[nEq-1]].affinity;
+ rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq-1, &bOk);
+ pBuilder->pRec = pRec;
+ if( rc!=SQLITE_OK ) return rc;
+ if( bOk==0 ) return SQLITE_NOTFOUND;
+ pBuilder->nRecValid = nEq;
+
+ whereKeyStats(pParse, p, pRec, 0, a);
+ WHERETRACE(0x100,("equality scan regions: %d\n", (int)a[1]));
+ *pnRow = a[1];
+
return rc;
}
-#endif /* defined(SQLITE_ENABLE_STAT3) */
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
-#ifdef SQLITE_ENABLE_STAT3
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
/*
** Estimate the number of rows that will be returned based on
** an IN constraint where the right-hand side of the IN operator
@@ -107195,902 +109321,34 @@ whereEqualScanEst_cancel:
*/
static int whereInScanEst(
Parse *pParse, /* Parsing & code generating context */
- Index *p, /* The index whose left-most column is pTerm */
+ WhereLoopBuilder *pBuilder,
ExprList *pList, /* The value list on the RHS of "x IN (v1,v2,v3,...)" */
- double *pnRow /* Write the revised row estimate here */
+ tRowcnt *pnRow /* Write the revised row estimate here */
){
- int rc = SQLITE_OK; /* Subfunction return code */
- double nEst; /* Number of rows for a single term */
- double nRowEst = (double)0; /* New estimate of the number of rows */
- int i; /* Loop counter */
+ Index *p = pBuilder->pNew->u.btree.pIndex;
+ int nRecValid = pBuilder->nRecValid;
+ int rc = SQLITE_OK; /* Subfunction return code */
+ tRowcnt nEst; /* Number of rows for a single term */
+ tRowcnt nRowEst = 0; /* New estimate of the number of rows */
+ int i; /* Loop counter */
assert( p->aSample!=0 );
for(i=0; rc==SQLITE_OK && i<pList->nExpr; i++){
nEst = p->aiRowEst[0];
- rc = whereEqualScanEst(pParse, p, pList->a[i].pExpr, &nEst);
+ rc = whereEqualScanEst(pParse, pBuilder, pList->a[i].pExpr, &nEst);
nRowEst += nEst;
+ pBuilder->nRecValid = nRecValid;
}
+
if( rc==SQLITE_OK ){
if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0];
*pnRow = nRowEst;
- WHERETRACE(("IN row estimate: est=%g\n", nRowEst));
+ WHERETRACE(0x100,("IN row estimate: est=%g\n", nRowEst));
}
+ assert( pBuilder->nRecValid==nRecValid );
return rc;
}
-#endif /* defined(SQLITE_ENABLE_STAT3) */
-
-/*
-** Check to see if column iCol of the table with cursor iTab will appear
-** in sorted order according to the current query plan.
-**
-** Return values:
-**
-** 0 iCol is not ordered
-** 1 iCol has only a single value
-** 2 iCol is in ASC order
-** 3 iCol is in DESC order
-*/
-static int isOrderedColumn(
- WhereBestIdx *p,
- int iTab,
- int iCol
-){
- int i, j;
- WhereLevel *pLevel = &p->aLevel[p->i-1];
- Index *pIdx;
- u8 sortOrder;
- for(i=p->i-1; i>=0; i--, pLevel--){
- if( pLevel->iTabCur!=iTab ) continue;
- if( (pLevel->plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
- return 1;
- }
- assert( (pLevel->plan.wsFlags & WHERE_ORDERED)!=0 );
- if( (pIdx = pLevel->plan.u.pIdx)!=0 ){
- if( iCol<0 ){
- sortOrder = 0;
- testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 );
- }else{
- int n = pIdx->nColumn;
- for(j=0; j<n; j++){
- if( iCol==pIdx->aiColumn[j] ) break;
- }
- if( j>=n ) return 0;
- sortOrder = pIdx->aSortOrder[j];
- testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 );
- }
- }else{
- if( iCol!=(-1) ) return 0;
- sortOrder = 0;
- testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 );
- }
- if( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 ){
- assert( sortOrder==0 || sortOrder==1 );
- testcase( sortOrder==1 );
- sortOrder = 1 - sortOrder;
- }
- return sortOrder+2;
- }
- return 0;
-}
-
-/*
-** This routine decides if pIdx can be used to satisfy the ORDER BY
-** clause, either in whole or in part. The return value is the
-** cumulative number of terms in the ORDER BY clause that are satisfied
-** by the index pIdx and other indices in outer loops.
-**
-** The table being queried has a cursor number of "base". pIdx is the
-** index that is postulated for use to access the table.
-**
-** The *pbRev value is set to 0 order 1 depending on whether or not
-** pIdx should be run in the forward order or in reverse order.
-*/
-static int isSortingIndex(
- WhereBestIdx *p, /* Best index search context */
- Index *pIdx, /* The index we are testing */
- int base, /* Cursor number for the table to be sorted */
- int *pbRev, /* Set to 1 for reverse-order scan of pIdx */
- int *pbObUnique /* ORDER BY column values will different in every row */
-){
- int i; /* Number of pIdx terms used */
- int j; /* Number of ORDER BY terms satisfied */
- int sortOrder = 2; /* 0: forward. 1: backward. 2: unknown */
- int nTerm; /* Number of ORDER BY terms */
- struct ExprList_item *pOBItem;/* A term of the ORDER BY clause */
- Table *pTab = pIdx->pTable; /* Table that owns index pIdx */
- ExprList *pOrderBy; /* The ORDER BY clause */
- Parse *pParse = p->pParse; /* Parser context */
- sqlite3 *db = pParse->db; /* Database connection */
- int nPriorSat; /* ORDER BY terms satisfied by outer loops */
- int seenRowid = 0; /* True if an ORDER BY rowid term is seen */
- int uniqueNotNull; /* pIdx is UNIQUE with all terms are NOT NULL */
- int outerObUnique; /* Outer loops generate different values in
- ** every row for the ORDER BY columns */
-
- if( p->i==0 ){
- nPriorSat = 0;
- outerObUnique = 1;
- }else{
- u32 wsFlags = p->aLevel[p->i-1].plan.wsFlags;
- nPriorSat = p->aLevel[p->i-1].plan.nOBSat;
- if( (wsFlags & WHERE_ORDERED)==0 ){
- /* This loop cannot be ordered unless the next outer loop is
- ** also ordered */
- return nPriorSat;
- }
- if( OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ){
- /* Only look at the outer-most loop if the OrderByIdxJoin
- ** optimization is disabled */
- return nPriorSat;
- }
- testcase( wsFlags & WHERE_OB_UNIQUE );
- testcase( wsFlags & WHERE_ALL_UNIQUE );
- outerObUnique = (wsFlags & (WHERE_OB_UNIQUE|WHERE_ALL_UNIQUE))!=0;
- }
- pOrderBy = p->pOrderBy;
- assert( pOrderBy!=0 );
- if( pIdx->bUnordered ){
- /* Hash indices (indicated by the "unordered" tag on sqlite_stat1) cannot
- ** be used for sorting */
- return nPriorSat;
- }
- nTerm = pOrderBy->nExpr;
- uniqueNotNull = pIdx->onError!=OE_None;
- assert( nTerm>0 );
-
- /* Argument pIdx must either point to a 'real' named index structure,
- ** or an index structure allocated on the stack by bestBtreeIndex() to
- ** represent the rowid index that is part of every table. */
- assert( pIdx->zName || (pIdx->nColumn==1 && pIdx->aiColumn[0]==-1) );
-
- /* Match terms of the ORDER BY clause against columns of
- ** the index.
- **
- ** Note that indices have pIdx->nColumn regular columns plus
- ** one additional column containing the rowid. The rowid column
- ** of the index is also allowed to match against the ORDER BY
- ** clause.
- */
- j = nPriorSat;
- for(i=0,pOBItem=&pOrderBy->a[j]; j<nTerm && i<=pIdx->nColumn; i++){
- Expr *pOBExpr; /* The expression of the ORDER BY pOBItem */
- CollSeq *pColl; /* The collating sequence of pOBExpr */
- int termSortOrder; /* Sort order for this term */
- int iColumn; /* The i-th column of the index. -1 for rowid */
- int iSortOrder; /* 1 for DESC, 0 for ASC on the i-th index term */
- int isEq; /* Subject to an == or IS NULL constraint */
- int isMatch; /* ORDER BY term matches the index term */
- const char *zColl; /* Name of collating sequence for i-th index term */
- WhereTerm *pConstraint; /* A constraint in the WHERE clause */
-
- /* If the next term of the ORDER BY clause refers to anything other than
- ** a column in the "base" table, then this index will not be of any
- ** further use in handling the ORDER BY. */
- pOBExpr = sqlite3ExprSkipCollate(pOBItem->pExpr);
- if( pOBExpr->op!=TK_COLUMN || pOBExpr->iTable!=base ){
- break;
- }
-
- /* Find column number and collating sequence for the next entry
- ** in the index */
- if( pIdx->zName && i<pIdx->nColumn ){
- iColumn = pIdx->aiColumn[i];
- if( iColumn==pIdx->pTable->iPKey ){
- iColumn = -1;
- }
- iSortOrder = pIdx->aSortOrder[i];
- zColl = pIdx->azColl[i];
- assert( zColl!=0 );
- }else{
- iColumn = -1;
- iSortOrder = 0;
- zColl = 0;
- }
-
- /* Check to see if the column number and collating sequence of the
- ** index match the column number and collating sequence of the ORDER BY
- ** clause entry. Set isMatch to 1 if they both match. */
- if( pOBExpr->iColumn==iColumn ){
- if( zColl ){
- pColl = sqlite3ExprCollSeq(pParse, pOBItem->pExpr);
- if( !pColl ) pColl = db->pDfltColl;
- isMatch = sqlite3StrICmp(pColl->zName, zColl)==0;
- }else{
- isMatch = 1;
- }
- }else{
- isMatch = 0;
- }
-
- /* termSortOrder is 0 or 1 for whether or not the access loop should
- ** run forward or backwards (respectively) in order to satisfy this
- ** term of the ORDER BY clause. */
- assert( pOBItem->sortOrder==0 || pOBItem->sortOrder==1 );
- assert( iSortOrder==0 || iSortOrder==1 );
- termSortOrder = iSortOrder ^ pOBItem->sortOrder;
-
- /* If X is the column in the index and ORDER BY clause, check to see
- ** if there are any X= or X IS NULL constraints in the WHERE clause. */
- pConstraint = findTerm(p->pWC, base, iColumn, p->notReady,
- WO_EQ|WO_ISNULL|WO_IN, pIdx);
- if( pConstraint==0 ){
- isEq = 0;
- }else if( (pConstraint->eOperator & WO_IN)!=0 ){
- isEq = 0;
- }else if( (pConstraint->eOperator & WO_ISNULL)!=0 ){
- uniqueNotNull = 0;
- isEq = 1; /* "X IS NULL" means X has only a single value */
- }else if( pConstraint->prereqRight==0 ){
- isEq = 1; /* Constraint "X=constant" means X has only a single value */
- }else{
- Expr *pRight = pConstraint->pExpr->pRight;
- if( pRight->op==TK_COLUMN ){
- WHERETRACE((" .. isOrderedColumn(tab=%d,col=%d)",
- pRight->iTable, pRight->iColumn));
- isEq = isOrderedColumn(p, pRight->iTable, pRight->iColumn);
- WHERETRACE((" -> isEq=%d\n", isEq));
-
- /* If the constraint is of the form X=Y where Y is an ordered value
- ** in an outer loop, then make sure the sort order of Y matches the
- ** sort order required for X. */
- if( isMatch && isEq>=2 && isEq!=pOBItem->sortOrder+2 ){
- testcase( isEq==2 );
- testcase( isEq==3 );
- break;
- }
- }else{
- isEq = 0; /* "X=expr" places no ordering constraints on X */
- }
- }
- if( !isMatch ){
- if( isEq==0 ){
- break;
- }else{
- continue;
- }
- }else if( isEq!=1 ){
- if( sortOrder==2 ){
- sortOrder = termSortOrder;
- }else if( termSortOrder!=sortOrder ){
- break;
- }
- }
- j++;
- pOBItem++;
- if( iColumn<0 ){
- seenRowid = 1;
- break;
- }else if( pTab->aCol[iColumn].notNull==0 && isEq!=1 ){
- testcase( isEq==0 );
- testcase( isEq==2 );
- testcase( isEq==3 );
- uniqueNotNull = 0;
- }
- }
- if( seenRowid ){
- uniqueNotNull = 1;
- }else if( uniqueNotNull==0 || i<pIdx->nColumn ){
- uniqueNotNull = 0;
- }
-
- /* If we have not found at least one ORDER BY term that matches the
- ** index, then show no progress. */
- if( pOBItem==&pOrderBy->a[nPriorSat] ) return nPriorSat;
-
- /* Either the outer queries must generate rows where there are no two
- ** rows with the same values in all ORDER BY columns, or else this
- ** loop must generate just a single row of output. Example: Suppose
- ** the outer loops generate A=1 and A=1, and this loop generates B=3
- ** and B=4. Then without the following test, ORDER BY A,B would
- ** generate the wrong order output: 1,3 1,4 1,3 1,4
- */
- if( outerObUnique==0 && uniqueNotNull==0 ) return nPriorSat;
- *pbObUnique = uniqueNotNull;
-
- /* Return the necessary scan order back to the caller */
- *pbRev = sortOrder & 1;
-
- /* If there was an "ORDER BY rowid" term that matched, or it is only
- ** possible for a single row from this table to match, then skip over
- ** any additional ORDER BY terms dealing with this table.
- */
- if( uniqueNotNull ){
- /* Advance j over additional ORDER BY terms associated with base */
- WhereMaskSet *pMS = p->pWC->pMaskSet;
- Bitmask m = ~getMask(pMS, base);
- while( j<nTerm && (exprTableUsage(pMS, pOrderBy->a[j].pExpr)&m)==0 ){
- j++;
- }
- }
- return j;
-}
-
-/*
-** Find the best query plan for accessing a particular table. Write the
-** best query plan and its cost into the p->cost.
-**
-** The lowest cost plan wins. The cost is an estimate of the amount of
-** CPU and disk I/O needed to process the requested result.
-** Factors that influence cost include:
-**
-** * The estimated number of rows that will be retrieved. (The
-** fewer the better.)
-**
-** * Whether or not sorting must occur.
-**
-** * Whether or not there must be separate lookups in the
-** index and in the main table.
-**
-** If there was an INDEXED BY clause (pSrc->pIndex) attached to the table in
-** the SQL statement, then this function only considers plans using the
-** named index. If no such plan is found, then the returned cost is
-** SQLITE_BIG_DBL. If a plan is found that uses the named index,
-** then the cost is calculated in the usual way.
-**
-** If a NOT INDEXED clause was attached to the table
-** in the SELECT statement, then no indexes are considered. However, the
-** selected plan may still take advantage of the built-in rowid primary key
-** index.
-*/
-static void bestBtreeIndex(WhereBestIdx *p){
- Parse *pParse = p->pParse; /* The parsing context */
- WhereClause *pWC = p->pWC; /* The WHERE clause */
- struct SrcList_item *pSrc = p->pSrc; /* The FROM clause term to search */
- int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */
- Index *pProbe; /* An index we are evaluating */
- Index *pIdx; /* Copy of pProbe, or zero for IPK index */
- int eqTermMask; /* Current mask of valid equality operators */
- int idxEqTermMask; /* Index mask of valid equality operators */
- Index sPk; /* A fake index object for the primary key */
- tRowcnt aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */
- int aiColumnPk = -1; /* The aColumn[] value for the sPk index */
- int wsFlagMask; /* Allowed flags in p->cost.plan.wsFlag */
- int nPriorSat; /* ORDER BY terms satisfied by outer loops */
- int nOrderBy; /* Number of ORDER BY terms */
- char bSortInit; /* Initializer for bSort in inner loop */
- char bDistInit; /* Initializer for bDist in inner loop */
-
-
- /* Initialize the cost to a worst-case value */
- memset(&p->cost, 0, sizeof(p->cost));
- p->cost.rCost = SQLITE_BIG_DBL;
-
- /* If the pSrc table is the right table of a LEFT JOIN then we may not
- ** use an index to satisfy IS NULL constraints on that table. This is
- ** because columns might end up being NULL if the table does not match -
- ** a circumstance which the index cannot help us discover. Ticket #2177.
- */
- if( pSrc->jointype & JT_LEFT ){
- idxEqTermMask = WO_EQ|WO_IN;
- }else{
- idxEqTermMask = WO_EQ|WO_IN|WO_ISNULL;
- }
-
- if( pSrc->pIndex ){
- /* An INDEXED BY clause specifies a particular index to use */
- pIdx = pProbe = pSrc->pIndex;
- wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE);
- eqTermMask = idxEqTermMask;
- }else{
- /* There is no INDEXED BY clause. Create a fake Index object in local
- ** variable sPk to represent the rowid primary key index. Make this
- ** fake index the first in a chain of Index objects with all of the real
- ** indices to follow */
- Index *pFirst; /* First of real indices on the table */
- memset(&sPk, 0, sizeof(Index));
- sPk.nColumn = 1;
- sPk.aiColumn = &aiColumnPk;
- sPk.aiRowEst = aiRowEstPk;
- sPk.onError = OE_Replace;
- sPk.pTable = pSrc->pTab;
- aiRowEstPk[0] = pSrc->pTab->nRowEst;
- aiRowEstPk[1] = 1;
- pFirst = pSrc->pTab->pIndex;
- if( pSrc->notIndexed==0 ){
- /* The real indices of the table are only considered if the
- ** NOT INDEXED qualifier is omitted from the FROM clause */
- sPk.pNext = pFirst;
- }
- pProbe = &sPk;
- wsFlagMask = ~(
- WHERE_COLUMN_IN|WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_RANGE
- );
- eqTermMask = WO_EQ|WO_IN;
- pIdx = 0;
- }
-
- nOrderBy = p->pOrderBy ? p->pOrderBy->nExpr : 0;
- if( p->i ){
- nPriorSat = p->aLevel[p->i-1].plan.nOBSat;
- bSortInit = nPriorSat<nOrderBy;
- bDistInit = 0;
- }else{
- nPriorSat = 0;
- bSortInit = nOrderBy>0;
- bDistInit = p->pDistinct!=0;
- }
-
- /* Loop over all indices looking for the best one to use
- */
- for(; pProbe; pIdx=pProbe=pProbe->pNext){
- const tRowcnt * const aiRowEst = pProbe->aiRowEst;
- WhereCost pc; /* Cost of using pProbe */
- double log10N = (double)1; /* base-10 logarithm of nRow (inexact) */
-
- /* The following variables are populated based on the properties of
- ** index being evaluated. They are then used to determine the expected
- ** cost and number of rows returned.
- **
- ** pc.plan.nEq:
- ** Number of equality terms that can be implemented using the index.
- ** In other words, the number of initial fields in the index that
- ** are used in == or IN or NOT NULL constraints of the WHERE clause.
- **
- ** nInMul:
- ** The "in-multiplier". This is an estimate of how many seek operations
- ** SQLite must perform on the index in question. For example, if the
- ** WHERE clause is:
- **
- ** WHERE a IN (1, 2, 3) AND b IN (4, 5, 6)
- **
- ** SQLite must perform 9 lookups on an index on (a, b), so nInMul is
- ** set to 9. Given the same schema and either of the following WHERE
- ** clauses:
- **
- ** WHERE a = 1
- ** WHERE a >= 2
- **
- ** nInMul is set to 1.
- **
- ** If there exists a WHERE term of the form "x IN (SELECT ...)", then
- ** the sub-select is assumed to return 25 rows for the purposes of
- ** determining nInMul.
- **
- ** bInEst:
- ** Set to true if there was at least one "x IN (SELECT ...)" term used
- ** in determining the value of nInMul. Note that the RHS of the
- ** IN operator must be a SELECT, not a value list, for this variable
- ** to be true.
- **
- ** rangeDiv:
- ** An estimate of a divisor by which to reduce the search space due
- ** to inequality constraints. In the absence of sqlite_stat3 ANALYZE
- ** data, a single inequality reduces the search space to 1/4rd its
- ** original size (rangeDiv==4). Two inequalities reduce the search
- ** space to 1/16th of its original size (rangeDiv==16).
- **
- ** bSort:
- ** Boolean. True if there is an ORDER BY clause that will require an
- ** external sort (i.e. scanning the index being evaluated will not
- ** correctly order records).
- **
- ** bDist:
- ** Boolean. True if there is a DISTINCT clause that will require an
- ** external btree.
- **
- ** bLookup:
- ** Boolean. True if a table lookup is required for each index entry
- ** visited. In other words, true if this is not a covering index.
- ** This is always false for the rowid primary key index of a table.
- ** For other indexes, it is true unless all the columns of the table
- ** used by the SELECT statement are present in the index (such an
- ** index is sometimes described as a covering index).
- ** For example, given the index on (a, b), the second of the following
- ** two queries requires table b-tree lookups in order to find the value
- ** of column c, but the first does not because columns a and b are
- ** both available in the index.
- **
- ** SELECT a, b FROM tbl WHERE a = 1;
- ** SELECT a, b, c FROM tbl WHERE a = 1;
- */
- int bInEst = 0; /* True if "x IN (SELECT...)" seen */
- int nInMul = 1; /* Number of distinct equalities to lookup */
- double rangeDiv = (double)1; /* Estimated reduction in search space */
- int nBound = 0; /* Number of range constraints seen */
- char bSort = bSortInit; /* True if external sort required */
- char bDist = bDistInit; /* True if index cannot help with DISTINCT */
- char bLookup = 0; /* True if not a covering index */
- WhereTerm *pTerm; /* A single term of the WHERE clause */
-#ifdef SQLITE_ENABLE_STAT3
- WhereTerm *pFirstTerm = 0; /* First term matching the index */
-#endif
-
- WHERETRACE((
- " %s(%s):\n",
- pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk")
- ));
- memset(&pc, 0, sizeof(pc));
- pc.plan.nOBSat = nPriorSat;
-
- /* Determine the values of pc.plan.nEq and nInMul */
- for(pc.plan.nEq=0; pc.plan.nEq<pProbe->nColumn; pc.plan.nEq++){
- int j = pProbe->aiColumn[pc.plan.nEq];
- pTerm = findTerm(pWC, iCur, j, p->notReady, eqTermMask, pIdx);
- if( pTerm==0 ) break;
- pc.plan.wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ);
- testcase( pTerm->pWC!=pWC );
- if( pTerm->eOperator & WO_IN ){
- Expr *pExpr = pTerm->pExpr;
- pc.plan.wsFlags |= WHERE_COLUMN_IN;
- if( ExprHasProperty(pExpr, EP_xIsSelect) ){
- /* "x IN (SELECT ...)": Assume the SELECT returns 25 rows */
- nInMul *= 25;
- bInEst = 1;
- }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){
- /* "x IN (value, value, ...)" */
- nInMul *= pExpr->x.pList->nExpr;
- }
- }else if( pTerm->eOperator & WO_ISNULL ){
- pc.plan.wsFlags |= WHERE_COLUMN_NULL;
- }
-#ifdef SQLITE_ENABLE_STAT3
- if( pc.plan.nEq==0 && pProbe->aSample ) pFirstTerm = pTerm;
-#endif
- pc.used |= pTerm->prereqRight;
- }
-
- /* If the index being considered is UNIQUE, and there is an equality
- ** constraint for all columns in the index, then this search will find
- ** at most a single row. In this case set the WHERE_UNIQUE flag to
- ** indicate this to the caller.
- **
- ** Otherwise, if the search may find more than one row, test to see if
- ** there is a range constraint on indexed column (pc.plan.nEq+1) that
- ** can be optimized using the index.
- */
- if( pc.plan.nEq==pProbe->nColumn && pProbe->onError!=OE_None ){
- testcase( pc.plan.wsFlags & WHERE_COLUMN_IN );
- testcase( pc.plan.wsFlags & WHERE_COLUMN_NULL );
- if( (pc.plan.wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){
- pc.plan.wsFlags |= WHERE_UNIQUE;
- if( p->i==0 || (p->aLevel[p->i-1].plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
- pc.plan.wsFlags |= WHERE_ALL_UNIQUE;
- }
- }
- }else if( pProbe->bUnordered==0 ){
- int j;
- j = (pc.plan.nEq==pProbe->nColumn ? -1 : pProbe->aiColumn[pc.plan.nEq]);
- if( findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){
- WhereTerm *pTop, *pBtm;
- pTop = findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE, pIdx);
- pBtm = findTerm(pWC, iCur, j, p->notReady, WO_GT|WO_GE, pIdx);
- whereRangeScanEst(pParse, pProbe, pc.plan.nEq, pBtm, pTop, &rangeDiv);
- if( pTop ){
- nBound = 1;
- pc.plan.wsFlags |= WHERE_TOP_LIMIT;
- pc.used |= pTop->prereqRight;
- testcase( pTop->pWC!=pWC );
- }
- if( pBtm ){
- nBound++;
- pc.plan.wsFlags |= WHERE_BTM_LIMIT;
- pc.used |= pBtm->prereqRight;
- testcase( pBtm->pWC!=pWC );
- }
- pc.plan.wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE);
- }
- }
-
- /* If there is an ORDER BY clause and the index being considered will
- ** naturally scan rows in the required order, set the appropriate flags
- ** in pc.plan.wsFlags. Otherwise, if there is an ORDER BY clause but
- ** the index will scan rows in a different order, set the bSort
- ** variable. */
- if( bSort && (pSrc->jointype & JT_LEFT)==0 ){
- int bRev = 2;
- int bObUnique = 0;
- WHERETRACE((" --> before isSortIndex: nPriorSat=%d\n",nPriorSat));
- pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev, &bObUnique);
- WHERETRACE((" --> after isSortIndex: bRev=%d bObU=%d nOBSat=%d\n",
- bRev, bObUnique, pc.plan.nOBSat));
- if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
- pc.plan.wsFlags |= WHERE_ORDERED;
- if( bObUnique ) pc.plan.wsFlags |= WHERE_OB_UNIQUE;
- }
- if( nOrderBy==pc.plan.nOBSat ){
- bSort = 0;
- pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE;
- }
- if( bRev & 1 ) pc.plan.wsFlags |= WHERE_REVERSE;
- }
-
- /* If there is a DISTINCT qualifier and this index will scan rows in
- ** order of the DISTINCT expressions, clear bDist and set the appropriate
- ** flags in pc.plan.wsFlags. */
- if( bDist
- && isDistinctIndex(pParse, pWC, pProbe, iCur, p->pDistinct, pc.plan.nEq)
- && (pc.plan.wsFlags & WHERE_COLUMN_IN)==0
- ){
- bDist = 0;
- pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_DISTINCT;
- }
-
- /* If currently calculating the cost of using an index (not the IPK
- ** index), determine if all required column data may be obtained without
- ** using the main table (i.e. if the index is a covering
- ** index for this query). If it is, set the WHERE_IDX_ONLY flag in
- ** pc.plan.wsFlags. Otherwise, set the bLookup variable to true. */
- if( pIdx ){
- Bitmask m = pSrc->colUsed;
- int j;
- for(j=0; j<pIdx->nColumn; j++){
- int x = pIdx->aiColumn[j];
- if( x<BMS-1 ){
- m &= ~(((Bitmask)1)<<x);
- }
- }
- if( m==0 ){
- pc.plan.wsFlags |= WHERE_IDX_ONLY;
- }else{
- bLookup = 1;
- }
- }
-
- /*
- ** Estimate the number of rows of output. For an "x IN (SELECT...)"
- ** constraint, do not let the estimate exceed half the rows in the table.
- */
- pc.plan.nRow = (double)(aiRowEst[pc.plan.nEq] * nInMul);
- if( bInEst && pc.plan.nRow*2>aiRowEst[0] ){
- pc.plan.nRow = aiRowEst[0]/2;
- nInMul = (int)(pc.plan.nRow / aiRowEst[pc.plan.nEq]);
- }
-
-#ifdef SQLITE_ENABLE_STAT3
- /* If the constraint is of the form x=VALUE or x IN (E1,E2,...)
- ** and we do not think that values of x are unique and if histogram
- ** data is available for column x, then it might be possible
- ** to get a better estimate on the number of rows based on
- ** VALUE and how common that value is according to the histogram.
- */
- if( pc.plan.nRow>(double)1 && pc.plan.nEq==1
- && pFirstTerm!=0 && aiRowEst[1]>1 ){
- assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 );
- if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){
- testcase( pFirstTerm->eOperator & WO_EQ );
- testcase( pFirstTerm->eOperator & WO_EQUIV );
- testcase( pFirstTerm->eOperator & WO_ISNULL );
- whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight,
- &pc.plan.nRow);
- }else if( bInEst==0 ){
- assert( pFirstTerm->eOperator & WO_IN );
- whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList,
- &pc.plan.nRow);
- }
- }
-#endif /* SQLITE_ENABLE_STAT3 */
-
- /* Adjust the number of output rows and downward to reflect rows
- ** that are excluded by range constraints.
- */
- pc.plan.nRow = pc.plan.nRow/rangeDiv;
- if( pc.plan.nRow<1 ) pc.plan.nRow = 1;
-
- /* Experiments run on real SQLite databases show that the time needed
- ** to do a binary search to locate a row in a table or index is roughly
- ** log10(N) times the time to move from one row to the next row within
- ** a table or index. The actual times can vary, with the size of
- ** records being an important factor. Both moves and searches are
- ** slower with larger records, presumably because fewer records fit
- ** on one page and hence more pages have to be fetched.
- **
- ** The ANALYZE command and the sqlite_stat1 and sqlite_stat3 tables do
- ** not give us data on the relative sizes of table and index records.
- ** So this computation assumes table records are about twice as big
- ** as index records
- */
- if( (pc.plan.wsFlags&~(WHERE_REVERSE|WHERE_ORDERED|WHERE_OB_UNIQUE))
- ==WHERE_IDX_ONLY
- && (pWC->wctrlFlags & WHERE_ONEPASS_DESIRED)==0
- && sqlite3GlobalConfig.bUseCis
- && OptimizationEnabled(pParse->db, SQLITE_CoverIdxScan)
- ){
- /* This index is not useful for indexing, but it is a covering index.
- ** A full-scan of the index might be a little faster than a full-scan
- ** of the table, so give this case a cost slightly less than a table
- ** scan. */
- pc.rCost = aiRowEst[0]*3 + pProbe->nColumn;
- pc.plan.wsFlags |= WHERE_COVER_SCAN|WHERE_COLUMN_RANGE;
- }else if( (pc.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){
- /* The cost of a full table scan is a number of move operations equal
- ** to the number of rows in the table.
- **
- ** We add an additional 4x penalty to full table scans. This causes
- ** the cost function to err on the side of choosing an index over
- ** choosing a full scan. This 4x full-scan penalty is an arguable
- ** decision and one which we expect to revisit in the future. But
- ** it seems to be working well enough at the moment.
- */
- pc.rCost = aiRowEst[0]*4;
- pc.plan.wsFlags &= ~WHERE_IDX_ONLY;
- if( pIdx ){
- pc.plan.wsFlags &= ~WHERE_ORDERED;
- pc.plan.nOBSat = nPriorSat;
- }
- }else{
- log10N = estLog(aiRowEst[0]);
- pc.rCost = pc.plan.nRow;
- if( pIdx ){
- if( bLookup ){
- /* For an index lookup followed by a table lookup:
- ** nInMul index searches to find the start of each index range
- ** + nRow steps through the index
- ** + nRow table searches to lookup the table entry using the rowid
- */
- pc.rCost += (nInMul + pc.plan.nRow)*log10N;
- }else{
- /* For a covering index:
- ** nInMul index searches to find the initial entry
- ** + nRow steps through the index
- */
- pc.rCost += nInMul*log10N;
- }
- }else{
- /* For a rowid primary key lookup:
- ** nInMult table searches to find the initial entry for each range
- ** + nRow steps through the table
- */
- pc.rCost += nInMul*log10N;
- }
- }
-
- /* Add in the estimated cost of sorting the result. Actual experimental
- ** measurements of sorting performance in SQLite show that sorting time
- ** adds C*N*log10(N) to the cost, where N is the number of rows to be
- ** sorted and C is a factor between 1.95 and 4.3. We will split the
- ** difference and select C of 3.0.
- */
- if( bSort ){
- double m = estLog(pc.plan.nRow*(nOrderBy - pc.plan.nOBSat)/nOrderBy);
- m *= (double)(pc.plan.nOBSat ? 2 : 3);
- pc.rCost += pc.plan.nRow*m;
- }
- if( bDist ){
- pc.rCost += pc.plan.nRow*estLog(pc.plan.nRow)*3;
- }
-
- /**** Cost of using this index has now been computed ****/
-
- /* If there are additional constraints on this table that cannot
- ** be used with the current index, but which might lower the number
- ** of output rows, adjust the nRow value accordingly. This only
- ** matters if the current index is the least costly, so do not bother
- ** with this step if we already know this index will not be chosen.
- ** Also, never reduce the output row count below 2 using this step.
- **
- ** It is critical that the notValid mask be used here instead of
- ** the notReady mask. When computing an "optimal" index, the notReady
- ** mask will only have one bit set - the bit for the current table.
- ** The notValid mask, on the other hand, always has all bits set for
- ** tables that are not in outer loops. If notReady is used here instead
- ** of notValid, then a optimal index that depends on inner joins loops
- ** might be selected even when there exists an optimal index that has
- ** no such dependency.
- */
- if( pc.plan.nRow>2 && pc.rCost<=p->cost.rCost ){
- int k; /* Loop counter */
- int nSkipEq = pc.plan.nEq; /* Number of == constraints to skip */
- int nSkipRange = nBound; /* Number of < constraints to skip */
- Bitmask thisTab; /* Bitmap for pSrc */
-
- thisTab = getMask(pWC->pMaskSet, iCur);
- for(pTerm=pWC->a, k=pWC->nTerm; pc.plan.nRow>2 && k; k--, pTerm++){
- if( pTerm->wtFlags & TERM_VIRTUAL ) continue;
- if( (pTerm->prereqAll & p->notValid)!=thisTab ) continue;
- if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){
- if( nSkipEq ){
- /* Ignore the first pc.plan.nEq equality matches since the index
- ** has already accounted for these */
- nSkipEq--;
- }else{
- /* Assume each additional equality match reduces the result
- ** set size by a factor of 10 */
- pc.plan.nRow /= 10;
- }
- }else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GT|WO_GE) ){
- if( nSkipRange ){
- /* Ignore the first nSkipRange range constraints since the index
- ** has already accounted for these */
- nSkipRange--;
- }else{
- /* Assume each additional range constraint reduces the result
- ** set size by a factor of 3. Indexed range constraints reduce
- ** the search space by a larger factor: 4. We make indexed range
- ** more selective intentionally because of the subjective
- ** observation that indexed range constraints really are more
- ** selective in practice, on average. */
- pc.plan.nRow /= 3;
- }
- }else if( (pTerm->eOperator & WO_NOOP)==0 ){
- /* Any other expression lowers the output row count by half */
- pc.plan.nRow /= 2;
- }
- }
- if( pc.plan.nRow<2 ) pc.plan.nRow = 2;
- }
-
-
- WHERETRACE((
- " nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%08x\n"
- " notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f\n"
- " used=0x%llx nOBSat=%d\n",
- pc.plan.nEq, nInMul, (int)rangeDiv, bSort, bLookup, pc.plan.wsFlags,
- p->notReady, log10N, pc.plan.nRow, pc.rCost, pc.used,
- pc.plan.nOBSat
- ));
-
- /* If this index is the best we have seen so far, then record this
- ** index and its cost in the p->cost structure.
- */
- if( (!pIdx || pc.plan.wsFlags) && compareCost(&pc, &p->cost) ){
- p->cost = pc;
- p->cost.plan.wsFlags &= wsFlagMask;
- p->cost.plan.u.pIdx = pIdx;
- }
-
- /* If there was an INDEXED BY clause, then only that one index is
- ** considered. */
- if( pSrc->pIndex ) break;
-
- /* Reset masks for the next index in the loop */
- wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE);
- eqTermMask = idxEqTermMask;
- }
-
- /* If there is no ORDER BY clause and the SQLITE_ReverseOrder flag
- ** is set, then reverse the order that the index will be scanned
- ** in. This is used for application testing, to help find cases
- ** where application behavior depends on the (undefined) order that
- ** SQLite outputs rows in in the absence of an ORDER BY clause. */
- if( !p->pOrderBy && pParse->db->flags & SQLITE_ReverseOrder ){
- p->cost.plan.wsFlags |= WHERE_REVERSE;
- }
-
- assert( p->pOrderBy || (p->cost.plan.wsFlags&WHERE_ORDERED)==0 );
- assert( p->cost.plan.u.pIdx==0 || (p->cost.plan.wsFlags&WHERE_ROWID_EQ)==0 );
- assert( pSrc->pIndex==0
- || p->cost.plan.u.pIdx==0
- || p->cost.plan.u.pIdx==pSrc->pIndex
- );
-
- WHERETRACE((" best index is %s cost=%.1f\n",
- p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk",
- p->cost.rCost));
-
- bestOrClauseIndex(p);
- bestAutomaticIndex(p);
- p->cost.plan.wsFlags |= eqTermMask;
-}
-
-/*
-** Find the query plan for accessing table pSrc->pTab. Write the
-** best query plan and its cost into the WhereCost object supplied
-** as the last parameter. This function may calculate the cost of
-** both real and virtual table scans.
-**
-** This function does not take ORDER BY or DISTINCT into account. Nor
-** does it remember the virtual table query plan. All it does is compute
-** the cost while determining if an OR optimization is applicable. The
-** details will be reconsidered later if the optimization is found to be
-** applicable.
-*/
-static void bestIndex(WhereBestIdx *p){
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( IsVirtual(p->pSrc->pTab) ){
- sqlite3_index_info *pIdxInfo = 0;
- p->ppIdxInfo = &pIdxInfo;
- bestVirtualIndex(p);
- assert( pIdxInfo!=0 || p->pParse->db->mallocFailed );
- if( pIdxInfo && pIdxInfo->needToFreeIdxStr ){
- sqlite3_free(pIdxInfo->idxStr);
- }
- sqlite3DbFree(p->pParse->db, pIdxInfo);
- }else
-#endif
- {
- bestBtreeIndex(p);
- }
-}
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
/*
** Disable a term in the WHERE clause. Except, do not disable the term
@@ -108107,9 +109365,6 @@ static void bestIndex(WhereBestIdx *p){
** in the ON clause. The term is disabled in (3) because it is not part
** of a LEFT OUTER JOIN. In (1), the term is not disabled.
**
-** IMPLEMENTATION-OF: R-24597-58655 No tests are done for terms that are
-** completely satisfied by indices.
-**
** Disabling a term causes that term to not be tested in the inner loop
** of the join. Disabling is an optimization. When terms are satisfied
** by indices, we disable them to prevent redundant tests in the inner
@@ -108122,6 +109377,7 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
if( pTerm
&& (pTerm->wtFlags & TERM_CODED)==0
&& (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin))
+ && (pLevel->notReady & pTerm->prereqAll)==0
){
pTerm->wtFlags |= TERM_CODED;
if( pTerm->iParent>=0 ){
@@ -108189,6 +109445,7 @@ static int codeEqualityTerm(
WhereTerm *pTerm, /* The term of the WHERE clause to be coded */
WhereLevel *pLevel, /* The level of the FROM clause we are working on */
int iEq, /* Index of the equality term within this level */
+ int bRev, /* True for reverse-order IN operations */
int iTarget /* Attempt to leave results in this register */
){
Expr *pX = pTerm->pExpr;
@@ -108206,14 +109463,13 @@ static int codeEqualityTerm(
int eType;
int iTab;
struct InLoop *pIn;
- u8 bRev = (pLevel->plan.wsFlags & WHERE_REVERSE)!=0;
+ WhereLoop *pLoop = pLevel->pWLoop;
- if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0
- && pLevel->plan.u.pIdx->aSortOrder[iEq]
+ if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0
+ && pLoop->u.btree.pIndex!=0
+ && pLoop->u.btree.pIndex->aSortOrder[iEq]
){
testcase( iEq==0 );
- testcase( iEq==pLevel->plan.u.pIdx->nColumn-1 );
- testcase( iEq>0 && iEq+1<pLevel->plan.u.pIdx->nColumn );
testcase( bRev );
bRev = !bRev;
}
@@ -108226,7 +109482,8 @@ static int codeEqualityTerm(
}
iTab = pX->iTable;
sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0);
- assert( pLevel->plan.wsFlags & WHERE_IN_ABLE );
+ assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 );
+ pLoop->wsFlags |= WHERE_IN_ABLE;
if( pLevel->u.in.nIn==0 ){
pLevel->addrNxt = sqlite3VdbeMakeLabel(v);
}
@@ -108296,29 +109553,31 @@ static int codeEqualityTerm(
static int codeAllEqualityTerms(
Parse *pParse, /* Parsing context */
WhereLevel *pLevel, /* Which nested loop of the FROM we are coding */
- WhereClause *pWC, /* The WHERE clause */
- Bitmask notReady, /* Which parts of FROM have not yet been coded */
+ int bRev, /* Reverse the order of IN operators */
int nExtraReg, /* Number of extra registers to allocate */
char **pzAff /* OUT: Set to point to affinity string */
){
- int nEq = pLevel->plan.nEq; /* The number of == or IN constraints to code */
+ int nEq; /* The number of == or IN constraints to code */
Vdbe *v = pParse->pVdbe; /* The vm under construction */
Index *pIdx; /* The index being used for this loop */
- int iCur = pLevel->iTabCur; /* The cursor of the table */
WhereTerm *pTerm; /* A single constraint term */
+ WhereLoop *pLoop; /* The WhereLoop object */
int j; /* Loop counter */
int regBase; /* Base register */
int nReg; /* Number of registers to allocate */
char *zAff; /* Affinity string to return */
/* This module is only called on query plans that use an index. */
- assert( pLevel->plan.wsFlags & WHERE_INDEXED );
- pIdx = pLevel->plan.u.pIdx;
+ pLoop = pLevel->pWLoop;
+ assert( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 );
+ nEq = pLoop->u.btree.nEq;
+ pIdx = pLoop->u.btree.pIndex;
+ assert( pIdx!=0 );
/* Figure out how many memory cells we will need then allocate them.
*/
regBase = pParse->nMem + 1;
- nReg = pLevel->plan.nEq + nExtraReg;
+ nReg = pLoop->u.btree.nEq + nExtraReg;
pParse->nMem += nReg;
zAff = sqlite3DbStrDup(pParse->db, sqlite3IndexAffinityStr(v, pIdx));
@@ -108328,17 +109587,16 @@ static int codeAllEqualityTerms(
/* Evaluate the equality constraints
*/
- assert( pIdx->nColumn>=nEq );
+ assert( zAff==0 || (int)strlen(zAff)>=nEq );
for(j=0; j<nEq; j++){
int r1;
- int k = pIdx->aiColumn[j];
- pTerm = findTerm(pWC, iCur, k, notReady, pLevel->plan.wsFlags, pIdx);
- if( pTerm==0 ) break;
+ pTerm = pLoop->aLTerm[j];
+ assert( pTerm!=0 );
/* The following true for indices with redundant columns.
** Ex: CREATE INDEX i1 ON t1(a,b,a); SELECT * FROM t1 WHERE a=0 AND b=0; */
testcase( (pTerm->wtFlags & TERM_CODED)!=0 );
- testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
- r1 = codeEqualityTerm(pParse, pTerm, pLevel, j, regBase+j);
+ testcase( pTerm->wtFlags & TERM_VIRTUAL );
+ r1 = codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, regBase+j);
if( r1!=regBase+j ){
if( nReg==1 ){
sqlite3ReleaseTempReg(pParse, regBase);
@@ -108406,31 +109664,31 @@ static void explainAppendTerm(
** It is the responsibility of the caller to free the buffer when it is
** no longer required.
*/
-static char *explainIndexRange(sqlite3 *db, WhereLevel *pLevel, Table *pTab){
- WherePlan *pPlan = &pLevel->plan;
- Index *pIndex = pPlan->u.pIdx;
- int nEq = pPlan->nEq;
+static char *explainIndexRange(sqlite3 *db, WhereLoop *pLoop, Table *pTab){
+ Index *pIndex = pLoop->u.btree.pIndex;
+ int nEq = pLoop->u.btree.nEq;
int i, j;
Column *aCol = pTab->aCol;
int *aiColumn = pIndex->aiColumn;
StrAccum txt;
- if( nEq==0 && (pPlan->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ){
+ if( nEq==0 && (pLoop->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ){
return 0;
}
sqlite3StrAccumInit(&txt, 0, 0, SQLITE_MAX_LENGTH);
txt.db = db;
sqlite3StrAccumAppend(&txt, " (", 2);
for(i=0; i<nEq; i++){
- explainAppendTerm(&txt, i, aCol[aiColumn[i]].zName, "=");
+ char *z = (i==pIndex->nColumn ) ? "rowid" : aCol[aiColumn[i]].zName;
+ explainAppendTerm(&txt, i, z, "=");
}
j = i;
- if( pPlan->wsFlags&WHERE_BTM_LIMIT ){
+ if( pLoop->wsFlags&WHERE_BTM_LIMIT ){
char *z = (j==pIndex->nColumn ) ? "rowid" : aCol[aiColumn[j]].zName;
explainAppendTerm(&txt, i++, z, ">");
}
- if( pPlan->wsFlags&WHERE_TOP_LIMIT ){
+ if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
char *z = (j==pIndex->nColumn ) ? "rowid" : aCol[aiColumn[j]].zName;
explainAppendTerm(&txt, i, z, "<");
}
@@ -108453,20 +109711,22 @@ static void explainOneScan(
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
){
if( pParse->explain==2 ){
- u32 flags = pLevel->plan.wsFlags;
struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
Vdbe *v = pParse->pVdbe; /* VM being constructed */
sqlite3 *db = pParse->db; /* Database handle */
char *zMsg; /* Text to add to EQP output */
- sqlite3_int64 nRow; /* Expected number of rows visited by scan */
int iId = pParse->iSelectId; /* Select id (left-most output column) */
int isSearch; /* True for a SEARCH. False for SCAN. */
+ WhereLoop *pLoop; /* The controlling WhereLoop object */
+ u32 flags; /* Flags that describe this loop */
+ pLoop = pLevel->pWLoop;
+ flags = pLoop->wsFlags;
if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return;
- isSearch = (pLevel->plan.nEq>0)
- || (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
- || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
+ isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
+ || ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0))
+ || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
zMsg = sqlite3MPrintf(db, "%s", isSearch?"SEARCH":"SCAN");
if( pItem->pSelect ){
@@ -108478,43 +109738,37 @@ static void explainOneScan(
if( pItem->zAlias ){
zMsg = sqlite3MAppendf(db, zMsg, "%s AS %s", zMsg, pItem->zAlias);
}
- if( (flags & WHERE_INDEXED)!=0 ){
- char *zWhere = explainIndexRange(db, pLevel, pItem->pTab);
- zMsg = sqlite3MAppendf(db, zMsg, "%s USING %s%sINDEX%s%s%s", zMsg,
- ((flags & WHERE_TEMP_INDEX)?"AUTOMATIC ":""),
- ((flags & WHERE_IDX_ONLY)?"COVERING ":""),
- ((flags & WHERE_TEMP_INDEX)?"":" "),
- ((flags & WHERE_TEMP_INDEX)?"": pLevel->plan.u.pIdx->zName),
- zWhere
- );
+ if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0
+ && ALWAYS(pLoop->u.btree.pIndex!=0)
+ ){
+ char *zWhere = explainIndexRange(db, pLoop, pItem->pTab);
+ zMsg = sqlite3MAppendf(db, zMsg,
+ ((flags & WHERE_AUTO_INDEX) ?
+ "%s USING AUTOMATIC %sINDEX%.0s%s" :
+ "%s USING %sINDEX %s%s"),
+ zMsg, ((flags & WHERE_IDX_ONLY) ? "COVERING " : ""),
+ pLoop->u.btree.pIndex->zName, zWhere);
sqlite3DbFree(db, zWhere);
- }else if( flags & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){
+ }else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){
zMsg = sqlite3MAppendf(db, zMsg, "%s USING INTEGER PRIMARY KEY", zMsg);
- if( flags&WHERE_ROWID_EQ ){
+ if( flags&(WHERE_COLUMN_EQ|WHERE_COLUMN_IN) ){
zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid=?)", zMsg);
}else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){
zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>? AND rowid<?)", zMsg);
}else if( flags&WHERE_BTM_LIMIT ){
zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>?)", zMsg);
- }else if( flags&WHERE_TOP_LIMIT ){
+ }else if( ALWAYS(flags&WHERE_TOP_LIMIT) ){
zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid<?)", zMsg);
}
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
else if( (flags & WHERE_VIRTUALTABLE)!=0 ){
- sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx;
zMsg = sqlite3MAppendf(db, zMsg, "%s VIRTUAL TABLE INDEX %d:%s", zMsg,
- pVtabIdx->idxNum, pVtabIdx->idxStr);
+ pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
}
#endif
- if( wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX) ){
- testcase( wctrlFlags & WHERE_ORDERBY_MIN );
- nRow = 1;
- }else{
- nRow = (sqlite3_int64)pLevel->plan.nRow;
- }
- zMsg = sqlite3MAppendf(db, zMsg, "%s (~%lld rows)", zMsg, nRow);
+ zMsg = sqlite3MAppendf(db, zMsg, "%s", zMsg);
sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg, P4_DYNAMIC);
}
}
@@ -108530,7 +109784,6 @@ static void explainOneScan(
static Bitmask codeOneLoopStart(
WhereInfo *pWInfo, /* Complete information about the WHERE clause */
int iLevel, /* Which level of pWInfo->a[] should be coded */
- u16 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */
Bitmask notReady /* Which tables are currently available */
){
int j, k; /* Loop counters */
@@ -108539,26 +109792,30 @@ static Bitmask codeOneLoopStart(
int omitTable; /* True if we use the index only */
int bRev; /* True if we need to scan in reverse order */
WhereLevel *pLevel; /* The where level to be coded */
+ WhereLoop *pLoop; /* The WhereLoop object being coded */
WhereClause *pWC; /* Decomposition of the entire WHERE clause */
WhereTerm *pTerm; /* A WHERE clause term */
Parse *pParse; /* Parsing context */
+ sqlite3 *db; /* Database connection */
Vdbe *v; /* The prepared stmt under constructions */
struct SrcList_item *pTabItem; /* FROM clause term being coded */
int addrBrk; /* Jump here to break out of the loop */
int addrCont; /* Jump here to continue with next cycle */
int iRowidReg = 0; /* Rowid is stored in this register, if not zero */
int iReleaseReg = 0; /* Temp register to free before returning */
- Bitmask newNotReady; /* Return value */
pParse = pWInfo->pParse;
v = pParse->pVdbe;
- pWC = pWInfo->pWC;
+ pWC = &pWInfo->sWC;
+ db = pParse->db;
pLevel = &pWInfo->a[iLevel];
+ pLoop = pLevel->pWLoop;
pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
iCur = pTabItem->iCursor;
- bRev = (pLevel->plan.wsFlags & WHERE_REVERSE)!=0;
- omitTable = (pLevel->plan.wsFlags & WHERE_IDX_ONLY)!=0
- && (wctrlFlags & WHERE_FORCE_TABLE)==0;
+ pLevel->notReady = notReady & ~getMask(&pWInfo->sMaskSet, iCur);
+ bRev = (pWInfo->revMask>>iLevel)&1;
+ omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0
+ && (pWInfo->wctrlFlags & WHERE_FORCE_TABLE)==0;
VdbeNoopComment((v, "Begin Join Loop %d", iLevel));
/* Create labels for the "break" and "continue" instructions
@@ -108595,47 +109852,37 @@ static Bitmask codeOneLoopStart(
}else
#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){
- /* Case 0: The table is a virtual-table. Use the VFilter and VNext
+ if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)!=0 ){
+ /* Case 1: The table is a virtual-table. Use the VFilter and VNext
** to access the data.
*/
int iReg; /* P3 Value for OP_VFilter */
int addrNotFound;
- sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx;
- int nConstraint = pVtabIdx->nConstraint;
- struct sqlite3_index_constraint_usage *aUsage =
- pVtabIdx->aConstraintUsage;
- const struct sqlite3_index_constraint *aConstraint =
- pVtabIdx->aConstraint;
+ int nConstraint = pLoop->nLTerm;
sqlite3ExprCachePush(pParse);
iReg = sqlite3GetTempRange(pParse, nConstraint+2);
addrNotFound = pLevel->addrBrk;
- for(j=1; j<=nConstraint; j++){
- for(k=0; k<nConstraint; k++){
- if( aUsage[k].argvIndex==j ){
- int iTarget = iReg+j+1;
- pTerm = &pWC->a[aConstraint[k].iTermOffset];
- if( pTerm->eOperator & WO_IN ){
- codeEqualityTerm(pParse, pTerm, pLevel, k, iTarget);
- addrNotFound = pLevel->addrNxt;
- }else{
- sqlite3ExprCode(pParse, pTerm->pExpr->pRight, iTarget);
- }
- break;
- }
+ for(j=0; j<nConstraint; j++){
+ int iTarget = iReg+j+2;
+ pTerm = pLoop->aLTerm[j];
+ if( pTerm==0 ) continue;
+ if( pTerm->eOperator & WO_IN ){
+ codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget);
+ addrNotFound = pLevel->addrNxt;
+ }else{
+ sqlite3ExprCode(pParse, pTerm->pExpr->pRight, iTarget);
}
- if( k==nConstraint ) break;
}
- sqlite3VdbeAddOp2(v, OP_Integer, pVtabIdx->idxNum, iReg);
- sqlite3VdbeAddOp2(v, OP_Integer, j-1, iReg+1);
- sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg, pVtabIdx->idxStr,
- pVtabIdx->needToFreeIdxStr ? P4_MPRINTF : P4_STATIC);
- pVtabIdx->needToFreeIdxStr = 0;
- for(j=0; j<nConstraint; j++){
- if( aUsage[j].omit ){
- int iTerm = aConstraint[j].iTermOffset;
- disableTerm(pLevel, &pWC->a[iTerm]);
+ sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg);
+ sqlite3VdbeAddOp2(v, OP_Integer, nConstraint, iReg+1);
+ sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg,
+ pLoop->u.vtab.idxStr,
+ pLoop->u.vtab.needFree ? P4_MPRINTF : P4_STATIC);
+ pLoop->u.vtab.needFree = 0;
+ for(j=0; j<nConstraint && j<16; j++){
+ if( (pLoop->u.vtab.omitMask>>j)&1 ){
+ disableTerm(pLevel, pLoop->aLTerm[j]);
}
}
pLevel->op = OP_VNext;
@@ -108646,19 +109893,22 @@ static Bitmask codeOneLoopStart(
}else
#endif /* SQLITE_OMIT_VIRTUALTABLE */
- if( pLevel->plan.wsFlags & WHERE_ROWID_EQ ){
- /* Case 1: We can directly reference a single row using an
+ if( (pLoop->wsFlags & WHERE_IPK)!=0
+ && (pLoop->wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_EQ))!=0
+ ){
+ /* Case 2: We can directly reference a single row using an
** equality comparison against the ROWID field. Or
** we reference multiple rows using a "rowid IN (...)"
** construct.
*/
+ assert( pLoop->u.btree.nEq==1 );
iReleaseReg = sqlite3GetTempReg(pParse);
- pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0);
+ pTerm = pLoop->aLTerm[0];
assert( pTerm!=0 );
assert( pTerm->pExpr!=0 );
assert( omitTable==0 );
- testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
- iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, iReleaseReg);
+ testcase( pTerm->wtFlags & TERM_VIRTUAL );
+ iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg);
addrNxt = pLevel->addrNxt;
sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt);
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addrNxt, iRowidReg);
@@ -108666,8 +109916,10 @@ static Bitmask codeOneLoopStart(
sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
VdbeComment((v, "pk"));
pLevel->op = OP_Noop;
- }else if( pLevel->plan.wsFlags & WHERE_ROWID_RANGE ){
- /* Case 2: We have an inequality comparison against the ROWID field.
+ }else if( (pLoop->wsFlags & WHERE_IPK)!=0
+ && (pLoop->wsFlags & WHERE_COLUMN_RANGE)!=0
+ ){
+ /* Case 3: We have an inequality comparison against the ROWID field.
*/
int testOp = OP_Noop;
int start;
@@ -108675,8 +109927,11 @@ static Bitmask codeOneLoopStart(
WhereTerm *pStart, *pEnd;
assert( omitTable==0 );
- pStart = findTerm(pWC, iCur, -1, notReady, WO_GT|WO_GE, 0);
- pEnd = findTerm(pWC, iCur, -1, notReady, WO_LT|WO_LE, 0);
+ j = 0;
+ pStart = pEnd = 0;
+ if( pLoop->wsFlags & WHERE_BTM_LIMIT ) pStart = pLoop->aLTerm[j++];
+ if( pLoop->wsFlags & WHERE_TOP_LIMIT ) pEnd = pLoop->aLTerm[j++];
+ assert( pStart!=0 || pEnd!=0 );
if( bRev ){
pTerm = pStart;
pStart = pEnd;
@@ -108699,10 +109954,11 @@ static Bitmask codeOneLoopStart(
assert( TK_LT==TK_GT+2 ); /* ... of the TK_xx values... */
assert( TK_GE==TK_GT+3 ); /* ... is correcct. */
- testcase( pStart->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
+ assert( (pStart->wtFlags & TERM_VNULL)==0 );
+ testcase( pStart->wtFlags & TERM_VIRTUAL );
pX = pStart->pExpr;
assert( pX!=0 );
- assert( pStart->leftCursor==iCur );
+ testcase( pStart->leftCursor!=iCur ); /* transitive constraints */
r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp);
sqlite3VdbeAddOp3(v, aMoveOp[pX->op-TK_GT], iCur, addrBrk, r1);
VdbeComment((v, "pk"));
@@ -108716,8 +109972,9 @@ static Bitmask codeOneLoopStart(
Expr *pX;
pX = pEnd->pExpr;
assert( pX!=0 );
- assert( pEnd->leftCursor==iCur );
- testcase( pEnd->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
+ assert( (pEnd->wtFlags & TERM_VNULL)==0 );
+ testcase( pEnd->leftCursor!=iCur ); /* Transitive constraints */
+ testcase( pEnd->wtFlags & TERM_VIRTUAL );
memEndValue = ++pParse->nMem;
sqlite3ExprCode(pParse, pX->pRight, memEndValue);
if( pX->op==TK_LT || pX->op==TK_GT ){
@@ -108731,11 +109988,7 @@ static Bitmask codeOneLoopStart(
pLevel->op = bRev ? OP_Prev : OP_Next;
pLevel->p1 = iCur;
pLevel->p2 = start;
- if( pStart==0 && pEnd==0 ){
- pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
- }else{
- assert( pLevel->p5==0 );
- }
+ assert( pLevel->p5==0 );
if( testOp!=OP_Noop ){
iRowidReg = iReleaseReg = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp2(v, OP_Rowid, iCur, iRowidReg);
@@ -108743,8 +109996,8 @@ static Bitmask codeOneLoopStart(
sqlite3VdbeAddOp3(v, testOp, memEndValue, addrBrk, iRowidReg);
sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL);
}
- }else if( pLevel->plan.wsFlags & (WHERE_COLUMN_RANGE|WHERE_COLUMN_EQ) ){
- /* Case 3: A scan using an index.
+ }else if( pLoop->wsFlags & WHERE_INDEXED ){
+ /* Case 4: A scan using an index.
**
** The WHERE clause may contain zero or more equality
** terms ("==" or "IN" operators) that refer to the N
@@ -108790,8 +110043,8 @@ static Bitmask codeOneLoopStart(
OP_IdxGE, /* 1: (end_constraints && !bRev) */
OP_IdxLT /* 2: (end_constraints && bRev) */
};
- int nEq = pLevel->plan.nEq; /* Number of == or IN terms */
- int isMinQuery = 0; /* If this is an optimized SELECT min(x).. */
+ int nEq = pLoop->u.btree.nEq; /* Number of == or IN terms */
+ int isMinQuery = 0; /* If this is an optimized SELECT min(x).. */
int regBase; /* Base register holding constraint values */
int r1; /* Temp register */
WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */
@@ -108807,9 +110060,8 @@ static Bitmask codeOneLoopStart(
char *zStartAff; /* Affinity for start of range constraint */
char *zEndAff; /* Affinity for end of range constraint */
- pIdx = pLevel->plan.u.pIdx;
+ pIdx = pLoop->u.btree.pIndex;
iIdxCur = pLevel->iIdxCur;
- k = (nEq==pIdx->nColumn ? -1 : pIdx->aiColumn[nEq]);
/* If this loop satisfies a sort order (pOrderBy) request that
** was passed to this function to implement a "SELECT min(x) ..."
@@ -108819,8 +110071,8 @@ static Bitmask codeOneLoopStart(
** the first one after the nEq equality constraints in the index,
** this requires some special handling.
*/
- if( (wctrlFlags&WHERE_ORDERBY_MIN)!=0
- && (pLevel->plan.wsFlags&WHERE_ORDERED)
+ if( (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)!=0
+ && (pWInfo->bOBSat!=0)
&& (pIdx->nColumn>nEq)
){
/* assert( pOrderBy->nExpr==1 ); */
@@ -108832,12 +110084,13 @@ static Bitmask codeOneLoopStart(
/* Find any inequality constraint terms for the start and end
** of the range.
*/
- if( pLevel->plan.wsFlags & WHERE_TOP_LIMIT ){
- pRangeEnd = findTerm(pWC, iCur, k, notReady, (WO_LT|WO_LE), pIdx);
+ j = nEq;
+ if( pLoop->wsFlags & WHERE_BTM_LIMIT ){
+ pRangeStart = pLoop->aLTerm[j++];
nExtraReg = 1;
}
- if( pLevel->plan.wsFlags & WHERE_BTM_LIMIT ){
- pRangeStart = findTerm(pWC, iCur, k, notReady, (WO_GT|WO_GE), pIdx);
+ if( pLoop->wsFlags & WHERE_TOP_LIMIT ){
+ pRangeEnd = pLoop->aLTerm[j++];
nExtraReg = 1;
}
@@ -108845,10 +110098,8 @@ static Bitmask codeOneLoopStart(
** and store the values of those terms in an array of registers
** starting at regBase.
*/
- regBase = codeAllEqualityTerms(
- pParse, pLevel, pWC, notReady, nExtraReg, &zStartAff
- );
- zEndAff = sqlite3DbStrDup(pParse->db, zStartAff);
+ regBase = codeAllEqualityTerms(pParse,pLevel,bRev,nExtraReg,&zStartAff);
+ zEndAff = sqlite3DbStrDup(db, zStartAff);
addrNxt = pLevel->addrNxt;
/* If we are doing a reverse order scan on an ascending index, or
@@ -108861,10 +110112,10 @@ static Bitmask codeOneLoopStart(
SWAP(WhereTerm *, pRangeEnd, pRangeStart);
}
- testcase( pRangeStart && pRangeStart->eOperator & WO_LE );
- testcase( pRangeStart && pRangeStart->eOperator & WO_GE );
- testcase( pRangeEnd && pRangeEnd->eOperator & WO_LE );
- testcase( pRangeEnd && pRangeEnd->eOperator & WO_GE );
+ testcase( pRangeStart && (pRangeStart->eOperator & WO_LE)!=0 );
+ testcase( pRangeStart && (pRangeStart->eOperator & WO_GE)!=0 );
+ testcase( pRangeEnd && (pRangeEnd->eOperator & WO_LE)!=0 );
+ testcase( pRangeEnd && (pRangeEnd->eOperator & WO_GE)!=0 );
startEq = !pRangeStart || pRangeStart->eOperator & (WO_LE|WO_GE);
endEq = !pRangeEnd || pRangeEnd->eOperator & (WO_LE|WO_GE);
start_constraints = pRangeStart || nEq>0;
@@ -108889,7 +110140,7 @@ static Bitmask codeOneLoopStart(
}
}
nConstraint++;
- testcase( pRangeStart->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
+ testcase( pRangeStart->wtFlags & TERM_VIRTUAL );
}else if( isMinQuery ){
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
nConstraint++;
@@ -108931,10 +110182,10 @@ static Bitmask codeOneLoopStart(
}
codeApplyAffinity(pParse, regBase, nEq+1, zEndAff);
nConstraint++;
- testcase( pRangeEnd->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
+ testcase( pRangeEnd->wtFlags & TERM_VIRTUAL );
}
- sqlite3DbFree(pParse->db, zStartAff);
- sqlite3DbFree(pParse->db, zEndAff);
+ sqlite3DbFree(db, zStartAff);
+ sqlite3DbFree(db, zEndAff);
/* Top of the loop body */
pLevel->p2 = sqlite3VdbeCurrentAddr(v);
@@ -108954,9 +110205,9 @@ static Bitmask codeOneLoopStart(
** If it is, jump to the next iteration of the loop.
*/
r1 = sqlite3GetTempReg(pParse);
- testcase( pLevel->plan.wsFlags & WHERE_BTM_LIMIT );
- testcase( pLevel->plan.wsFlags & WHERE_TOP_LIMIT );
- if( (pLevel->plan.wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 ){
+ testcase( pLoop->wsFlags & WHERE_BTM_LIMIT );
+ testcase( pLoop->wsFlags & WHERE_TOP_LIMIT );
+ if( (pLoop->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 ){
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, nEq, r1);
sqlite3VdbeAddOp2(v, OP_IsNull, r1, addrCont);
}
@@ -108975,7 +110226,7 @@ static Bitmask codeOneLoopStart(
/* Record the instruction used to terminate the loop. Disable
** WHERE clause terms made redundant by the index range scan.
*/
- if( pLevel->plan.wsFlags & WHERE_UNIQUE ){
+ if( pLoop->wsFlags & WHERE_ONEROW ){
pLevel->op = OP_Noop;
}else if( bRev ){
pLevel->op = OP_Prev;
@@ -108983,7 +110234,7 @@ static Bitmask codeOneLoopStart(
pLevel->op = OP_Next;
}
pLevel->p1 = iIdxCur;
- if( pLevel->plan.wsFlags & WHERE_COVER_SCAN ){
+ if( (pLoop->wsFlags & WHERE_CONSTRAINT)==0 ){
pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
}else{
assert( pLevel->p5==0 );
@@ -108991,8 +110242,8 @@ static Bitmask codeOneLoopStart(
}else
#ifndef SQLITE_OMIT_OR_OPTIMIZATION
- if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){
- /* Case 4: Two or more separately indexed terms connected by OR
+ if( pLoop->wsFlags & WHERE_MULTI_OR ){
+ /* Case 5: Two or more separately indexed terms connected by OR
**
** Example:
**
@@ -109045,7 +110296,7 @@ static Bitmask codeOneLoopStart(
int ii; /* Loop counter */
Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
- pTerm = pLevel->plan.u.pTerm;
+ pTerm = pLoop->aLTerm[0];
assert( pTerm!=0 );
assert( pTerm->eOperator & WO_OR );
assert( (pTerm->wtFlags & TERM_ORINFO)!=0 );
@@ -109061,10 +110312,10 @@ static Bitmask codeOneLoopStart(
int nNotReady; /* The number of notReady tables */
struct SrcList_item *origSrc; /* Original list of tables */
nNotReady = pWInfo->nLevel - iLevel - 1;
- pOrTab = sqlite3StackAllocRaw(pParse->db,
+ pOrTab = sqlite3StackAllocRaw(db,
sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0]));
if( pOrTab==0 ) return notReady;
- pOrTab->nAlloc = (i16)(nNotReady + 1);
+ pOrTab->nAlloc = (u8)(nNotReady + 1);
pOrTab->nSrc = pOrTab->nAlloc;
memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem));
origSrc = pWInfo->pTabList->a;
@@ -109086,7 +110337,7 @@ static Bitmask codeOneLoopStart(
** fall through to the next instruction, just as an OP_Next does if
** called on an uninitialized cursor.
*/
- if( (wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
+ if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
regRowset = ++pParse->nMem;
regRowid = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset);
@@ -109111,11 +110362,12 @@ static Bitmask codeOneLoopStart(
int iTerm;
for(iTerm=0; iTerm<pWC->nTerm; iTerm++){
Expr *pExpr = pWC->a[iTerm].pExpr;
+ if( &pWC->a[iTerm] == pTerm ) continue;
if( ExprHasProperty(pExpr, EP_FromJoin) ) continue;
- if( pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_ORINFO) ) continue;
+ if( pWC->a[iTerm].wtFlags & (TERM_ORINFO) ) continue;
if( (pWC->a[iTerm].eOperator & WO_ALL)==0 ) continue;
- pExpr = sqlite3ExprDup(pParse->db, pExpr, 0);
- pAndExpr = sqlite3ExprAnd(pParse->db, pAndExpr, pExpr);
+ pExpr = sqlite3ExprDup(db, pExpr, 0);
+ pAndExpr = sqlite3ExprAnd(db, pAndExpr, pExpr);
}
if( pAndExpr ){
pAndExpr = sqlite3PExpr(pParse, TK_AND, 0, pAndExpr, 0);
@@ -109135,13 +110387,13 @@ static Bitmask codeOneLoopStart(
pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0,
WHERE_OMIT_OPEN_CLOSE | WHERE_AND_ONLY |
WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY, iCovCur);
- assert( pSubWInfo || pParse->nErr || pParse->db->mallocFailed );
+ assert( pSubWInfo || pParse->nErr || db->mallocFailed );
if( pSubWInfo ){
- WhereLevel *pLvl;
+ WhereLoop *pSubLoop;
explainOneScan(
pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
);
- if( (wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
+ if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
int iSet = ((ii==pOrWc->nTerm-1)?-1:ii);
int r;
r = sqlite3ExprCodeGetColumn(pParse, pTabItem->pTab, -1, iCur,
@@ -109170,13 +110422,13 @@ static Bitmask codeOneLoopStart(
** pCov to NULL to indicate that no candidate covering index will
** be available.
*/
- pLvl = &pSubWInfo->a[0];
- if( (pLvl->plan.wsFlags & WHERE_INDEXED)!=0
- && (pLvl->plan.wsFlags & WHERE_TEMP_INDEX)==0
- && (ii==0 || pLvl->plan.u.pIdx==pCov)
+ pSubLoop = pSubWInfo->a[0].pWLoop;
+ assert( (pSubLoop->wsFlags & WHERE_AUTO_INDEX)==0 );
+ if( (pSubLoop->wsFlags & WHERE_INDEXED)!=0
+ && (ii==0 || pSubLoop->u.btree.pIndex==pCov)
){
- assert( pLvl->iIdxCur==iCovCur );
- pCov = pLvl->plan.u.pIdx;
+ assert( pSubWInfo->a[0].iIdxCur==iCovCur );
+ pCov = pSubLoop->u.btree.pIndex;
}else{
pCov = 0;
}
@@ -109190,45 +110442,39 @@ static Bitmask codeOneLoopStart(
if( pCov ) pLevel->iIdxCur = iCovCur;
if( pAndExpr ){
pAndExpr->pLeft = 0;
- sqlite3ExprDelete(pParse->db, pAndExpr);
+ sqlite3ExprDelete(db, pAndExpr);
}
sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v));
sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk);
sqlite3VdbeResolveLabel(v, iLoopBody);
- if( pWInfo->nLevel>1 ) sqlite3StackFree(pParse->db, pOrTab);
+ if( pWInfo->nLevel>1 ) sqlite3StackFree(db, pOrTab);
if( !untestedTerms ) disableTerm(pLevel, pTerm);
}else
#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
{
- /* Case 5: There is no usable index. We must do a complete
+ /* Case 6: There is no usable index. We must do a complete
** scan of the entire table.
*/
static const u8 aStep[] = { OP_Next, OP_Prev };
static const u8 aStart[] = { OP_Rewind, OP_Last };
assert( bRev==0 || bRev==1 );
- assert( omitTable==0 );
pLevel->op = aStep[bRev];
pLevel->p1 = iCur;
pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk);
pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
}
- newNotReady = notReady & ~getMask(pWC->pMaskSet, iCur);
/* Insert code to test every subexpression that can be completely
** computed using the current set of tables.
- **
- ** IMPLEMENTATION-OF: R-49525-50935 Terms that cannot be satisfied through
- ** the use of indices become tests that are evaluated against each row of
- ** the relevant input tables.
*/
for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){
Expr *pE;
- testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* IMP: R-30575-11662 */
+ testcase( pTerm->wtFlags & TERM_VIRTUAL );
testcase( pTerm->wtFlags & TERM_CODED );
if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
- if( (pTerm->prereqAll & newNotReady)!=0 ){
+ if( (pTerm->prereqAll & pLevel->notReady)!=0 ){
testcase( pWInfo->untestedTerms==0
&& (pWInfo->wctrlFlags & WHERE_ONETABLE_ONLY)!=0 );
pWInfo->untestedTerms = 1;
@@ -109252,22 +110498,28 @@ static Bitmask codeOneLoopStart(
** the implied "t1.a=123" constraint.
*/
for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){
- Expr *pE;
+ Expr *pE, *pEAlt;
WhereTerm *pAlt;
- Expr sEq;
if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
if( pTerm->eOperator!=(WO_EQUIV|WO_EQ) ) continue;
if( pTerm->leftCursor!=iCur ) continue;
+ if( pLevel->iLeftJoin ) continue;
pE = pTerm->pExpr;
assert( !ExprHasProperty(pE, EP_FromJoin) );
- assert( (pTerm->prereqRight & newNotReady)!=0 );
+ assert( (pTerm->prereqRight & pLevel->notReady)!=0 );
pAlt = findTerm(pWC, iCur, pTerm->u.leftColumn, notReady, WO_EQ|WO_IN, 0);
if( pAlt==0 ) continue;
if( pAlt->wtFlags & (TERM_CODED) ) continue;
+ testcase( pAlt->eOperator & WO_EQ );
+ testcase( pAlt->eOperator & WO_IN );
VdbeNoopComment((v, "begin transitive constraint"));
- sEq = *pAlt->pExpr;
- sEq.pLeft = pE->pLeft;
- sqlite3ExprIfFalse(pParse, &sEq, addrCont, SQLITE_JUMPIFNULL);
+ pEAlt = sqlite3StackAllocRaw(db, sizeof(*pEAlt));
+ if( pEAlt ){
+ *pEAlt = *pAlt->pExpr;
+ pEAlt->pLeft = pE->pLeft;
+ sqlite3ExprIfFalse(pParse, pEAlt, addrCont, SQLITE_JUMPIFNULL);
+ sqlite3StackFree(db, pEAlt);
+ }
}
/* For a LEFT OUTER JOIN, generate code that will record the fact that
@@ -109279,10 +110531,10 @@ static Bitmask codeOneLoopStart(
VdbeComment((v, "record LEFT JOIN hit"));
sqlite3ExprCacheClear(pParse);
for(pTerm=pWC->a, j=0; j<pWC->nTerm; j++, pTerm++){
- testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* IMP: R-30575-11662 */
+ testcase( pTerm->wtFlags & TERM_VIRTUAL );
testcase( pTerm->wtFlags & TERM_CODED );
if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
- if( (pTerm->prereqAll & newNotReady)!=0 ){
+ if( (pTerm->prereqAll & pLevel->notReady)!=0 ){
assert( pWInfo->untestedTerms );
continue;
}
@@ -109293,50 +110545,1678 @@ static Bitmask codeOneLoopStart(
}
sqlite3ReleaseTempReg(pParse, iReleaseReg);
- return newNotReady;
+ return pLevel->notReady;
}
-#if defined(SQLITE_TEST)
+#ifdef WHERETRACE_ENABLED
/*
-** The following variable holds a text description of query plan generated
-** by the most recent call to sqlite3WhereBegin(). Each call to WhereBegin
-** overwrites the previous. This information is used for testing and
-** analysis only.
+** Print a WhereLoop object for debugging purposes
*/
-SQLITE_API char sqlite3_query_plan[BMS*2*40]; /* Text of the join */
-static int nQPlan = 0; /* Next free slow in _query_plan[] */
+static void whereLoopPrint(WhereLoop *p, SrcList *pTabList){
+ int nb = 1+(pTabList->nSrc+7)/8;
+ struct SrcList_item *pItem = pTabList->a + p->iTab;
+ Table *pTab = pItem->pTab;
+ sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId,
+ p->iTab, nb, p->maskSelf, nb, p->prereq);
+ sqlite3DebugPrintf(" %12s",
+ pItem->zAlias ? pItem->zAlias : pTab->zName);
+ if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
+ if( p->u.btree.pIndex ){
+ const char *zName = p->u.btree.pIndex->zName;
+ if( zName==0 ) zName = "ipk";
+ if( strncmp(zName, "sqlite_autoindex_", 17)==0 ){
+ int i = sqlite3Strlen30(zName) - 1;
+ while( zName[i]!='_' ) i--;
+ zName += i;
+ }
+ sqlite3DebugPrintf(".%-16s %2d", zName, p->u.btree.nEq);
+ }else{
+ sqlite3DebugPrintf("%20s","");
+ }
+ }else{
+ char *z;
+ if( p->u.vtab.idxStr ){
+ z = sqlite3_mprintf("(%d,\"%s\",%x)",
+ p->u.vtab.idxNum, p->u.vtab.idxStr, p->u.vtab.omitMask);
+ }else{
+ z = sqlite3_mprintf("(%d,%x)", p->u.vtab.idxNum, p->u.vtab.omitMask);
+ }
+ sqlite3DebugPrintf(" %-19s", z);
+ sqlite3_free(z);
+ }
+ sqlite3DebugPrintf(" f %04x N %d", p->wsFlags, p->nLTerm);
+ sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut);
+}
+#endif
-#endif /* SQLITE_TEST */
+/*
+** Convert bulk memory into a valid WhereLoop that can be passed
+** to whereLoopClear harmlessly.
+*/
+static void whereLoopInit(WhereLoop *p){
+ p->aLTerm = p->aLTermSpace;
+ p->nLTerm = 0;
+ p->nLSlot = ArraySize(p->aLTermSpace);
+ p->wsFlags = 0;
+}
+
+/*
+** Clear the WhereLoop.u union. Leave WhereLoop.pLTerm intact.
+*/
+static void whereLoopClearUnion(sqlite3 *db, WhereLoop *p){
+ if( p->wsFlags & (WHERE_VIRTUALTABLE|WHERE_AUTO_INDEX) ){
+ if( (p->wsFlags & WHERE_VIRTUALTABLE)!=0 && p->u.vtab.needFree ){
+ sqlite3_free(p->u.vtab.idxStr);
+ p->u.vtab.needFree = 0;
+ p->u.vtab.idxStr = 0;
+ }else if( (p->wsFlags & WHERE_AUTO_INDEX)!=0 && p->u.btree.pIndex!=0 ){
+ sqlite3DbFree(db, p->u.btree.pIndex->zColAff);
+ sqlite3DbFree(db, p->u.btree.pIndex);
+ p->u.btree.pIndex = 0;
+ }
+ }
+}
+/*
+** Deallocate internal memory used by a WhereLoop object
+*/
+static void whereLoopClear(sqlite3 *db, WhereLoop *p){
+ if( p->aLTerm!=p->aLTermSpace ) sqlite3DbFree(db, p->aLTerm);
+ whereLoopClearUnion(db, p);
+ whereLoopInit(p);
+}
+
+/*
+** Increase the memory allocation for pLoop->aLTerm[] to be at least n.
+*/
+static int whereLoopResize(sqlite3 *db, WhereLoop *p, int n){
+ WhereTerm **paNew;
+ if( p->nLSlot>=n ) return SQLITE_OK;
+ n = (n+7)&~7;
+ paNew = sqlite3DbMallocRaw(db, sizeof(p->aLTerm[0])*n);
+ if( paNew==0 ) return SQLITE_NOMEM;
+ memcpy(paNew, p->aLTerm, sizeof(p->aLTerm[0])*p->nLSlot);
+ if( p->aLTerm!=p->aLTermSpace ) sqlite3DbFree(db, p->aLTerm);
+ p->aLTerm = paNew;
+ p->nLSlot = n;
+ return SQLITE_OK;
+}
+
+/*
+** Transfer content from the second pLoop into the first.
+*/
+static int whereLoopXfer(sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){
+ whereLoopClearUnion(db, pTo);
+ if( whereLoopResize(db, pTo, pFrom->nLTerm) ){
+ memset(&pTo->u, 0, sizeof(pTo->u));
+ return SQLITE_NOMEM;
+ }
+ memcpy(pTo, pFrom, WHERE_LOOP_XFER_SZ);
+ memcpy(pTo->aLTerm, pFrom->aLTerm, pTo->nLTerm*sizeof(pTo->aLTerm[0]));
+ if( pFrom->wsFlags & WHERE_VIRTUALTABLE ){
+ pFrom->u.vtab.needFree = 0;
+ }else if( (pFrom->wsFlags & WHERE_AUTO_INDEX)!=0 ){
+ pFrom->u.btree.pIndex = 0;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Delete a WhereLoop object
+*/
+static void whereLoopDelete(sqlite3 *db, WhereLoop *p){
+ whereLoopClear(db, p);
+ sqlite3DbFree(db, p);
+}
/*
** Free a WhereInfo structure
*/
static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
if( ALWAYS(pWInfo) ){
- int i;
- for(i=0; i<pWInfo->nLevel; i++){
- sqlite3_index_info *pInfo = pWInfo->a[i].pIdxInfo;
- if( pInfo ){
- /* assert( pInfo->needToFreeIdxStr==0 || db->mallocFailed ); */
- if( pInfo->needToFreeIdxStr ){
- sqlite3_free(pInfo->idxStr);
+ whereClauseClear(&pWInfo->sWC);
+ while( pWInfo->pLoops ){
+ WhereLoop *p = pWInfo->pLoops;
+ pWInfo->pLoops = p->pNextLoop;
+ whereLoopDelete(db, p);
+ }
+ sqlite3DbFree(db, pWInfo);
+ }
+}
+
+/*
+** Insert or replace a WhereLoop entry using the template supplied.
+**
+** An existing WhereLoop entry might be overwritten if the new template
+** is better and has fewer dependencies. Or the template will be ignored
+** and no insert will occur if an existing WhereLoop is faster and has
+** fewer dependencies than the template. Otherwise a new WhereLoop is
+** added based on the template.
+**
+** If pBuilder->pOrSet is not NULL then we only care about only the
+** prerequisites and rRun and nOut costs of the N best loops. That
+** information is gathered in the pBuilder->pOrSet object. This special
+** processing mode is used only for OR clause processing.
+**
+** When accumulating multiple loops (when pBuilder->pOrSet is NULL) we
+** still might overwrite similar loops with the new template if the
+** template is better. Loops may be overwritten if the following
+** conditions are met:
+**
+** (1) They have the same iTab.
+** (2) They have the same iSortIdx.
+** (3) The template has same or fewer dependencies than the current loop
+** (4) The template has the same or lower cost than the current loop
+** (5) The template uses more terms of the same index but has no additional
+** dependencies
+*/
+static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
+ WhereLoop **ppPrev, *p, *pNext = 0;
+ WhereInfo *pWInfo = pBuilder->pWInfo;
+ sqlite3 *db = pWInfo->pParse->db;
+
+ /* If pBuilder->pOrSet is defined, then only keep track of the costs
+ ** and prereqs.
+ */
+ if( pBuilder->pOrSet!=0 ){
+#if WHERETRACE_ENABLED
+ u16 n = pBuilder->pOrSet->n;
+ int x =
+#endif
+ whereOrInsert(pBuilder->pOrSet, pTemplate->prereq, pTemplate->rRun,
+ pTemplate->nOut);
+#if WHERETRACE_ENABLED
+ if( sqlite3WhereTrace & 0x8 ){
+ sqlite3DebugPrintf(x?" or-%d: ":" or-X: ", n);
+ whereLoopPrint(pTemplate, pWInfo->pTabList);
+ }
+#endif
+ return SQLITE_OK;
+ }
+
+ /* Search for an existing WhereLoop to overwrite, or which takes
+ ** priority over pTemplate.
+ */
+ for(ppPrev=&pWInfo->pLoops, p=*ppPrev; p; ppPrev=&p->pNextLoop, p=*ppPrev){
+ if( p->iTab!=pTemplate->iTab || p->iSortIdx!=pTemplate->iSortIdx ){
+ /* If either the iTab or iSortIdx values for two WhereLoop are different
+ ** then those WhereLoops need to be considered separately. Neither is
+ ** a candidate to replace the other. */
+ continue;
+ }
+ /* In the current implementation, the rSetup value is either zero
+ ** or the cost of building an automatic index (NlogN) and the NlogN
+ ** is the same for compatible WhereLoops. */
+ assert( p->rSetup==0 || pTemplate->rSetup==0
+ || p->rSetup==pTemplate->rSetup );
+
+ /* whereLoopAddBtree() always generates and inserts the automatic index
+ ** case first. Hence compatible candidate WhereLoops never have a larger
+ ** rSetup. Call this SETUP-INVARIANT */
+ assert( p->rSetup>=pTemplate->rSetup );
+
+ if( (p->prereq & pTemplate->prereq)==p->prereq
+ && p->rSetup<=pTemplate->rSetup
+ && p->rRun<=pTemplate->rRun
+ && p->nOut<=pTemplate->nOut
+ ){
+ /* This branch taken when p is equal or better than pTemplate in
+ ** all of (1) dependencies (2) setup-cost, (3) run-cost, and
+ ** (4) number of output rows. */
+ assert( p->rSetup==pTemplate->rSetup );
+ if( p->prereq==pTemplate->prereq
+ && p->nLTerm<pTemplate->nLTerm
+ && (p->wsFlags & pTemplate->wsFlags & WHERE_INDEXED)!=0
+ && (p->u.btree.pIndex==pTemplate->u.btree.pIndex
+ || pTemplate->rRun+p->nLTerm<=p->rRun+pTemplate->nLTerm)
+ ){
+ /* Overwrite an existing WhereLoop with an similar one that uses
+ ** more terms of the index */
+ pNext = p->pNextLoop;
+ break;
+ }else{
+ /* pTemplate is not helpful.
+ ** Return without changing or adding anything */
+ goto whereLoopInsert_noop;
+ }
+ }
+ if( (p->prereq & pTemplate->prereq)==pTemplate->prereq
+ && p->rRun>=pTemplate->rRun
+ && p->nOut>=pTemplate->nOut
+ ){
+ /* Overwrite an existing WhereLoop with a better one: one that is
+ ** better at one of (1) dependencies, (2) setup-cost, (3) run-cost
+ ** or (4) number of output rows, and is no worse in any of those
+ ** categories. */
+ assert( p->rSetup>=pTemplate->rSetup ); /* SETUP-INVARIANT above */
+ pNext = p->pNextLoop;
+ break;
+ }
+ }
+
+ /* If we reach this point it means that either p[] should be overwritten
+ ** with pTemplate[] if p[] exists, or if p==NULL then allocate a new
+ ** WhereLoop and insert it.
+ */
+#if WHERETRACE_ENABLED
+ if( sqlite3WhereTrace & 0x8 ){
+ if( p!=0 ){
+ sqlite3DebugPrintf("ins-del: ");
+ whereLoopPrint(p, pWInfo->pTabList);
+ }
+ sqlite3DebugPrintf("ins-new: ");
+ whereLoopPrint(pTemplate, pWInfo->pTabList);
+ }
+#endif
+ if( p==0 ){
+ p = sqlite3DbMallocRaw(db, sizeof(WhereLoop));
+ if( p==0 ) return SQLITE_NOMEM;
+ whereLoopInit(p);
+ }
+ whereLoopXfer(db, p, pTemplate);
+ p->pNextLoop = pNext;
+ *ppPrev = p;
+ if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
+ Index *pIndex = p->u.btree.pIndex;
+ if( pIndex && pIndex->tnum==0 ){
+ p->u.btree.pIndex = 0;
+ }
+ }
+ return SQLITE_OK;
+
+ /* Jump here if the insert is a no-op */
+whereLoopInsert_noop:
+#if WHERETRACE_ENABLED
+ if( sqlite3WhereTrace & 0x8 ){
+ sqlite3DebugPrintf("ins-noop: ");
+ whereLoopPrint(pTemplate, pWInfo->pTabList);
+ }
+#endif
+ return SQLITE_OK;
+}
+
+/*
+** Adjust the WhereLoop.nOut value downward to account for terms of the
+** WHERE clause that reference the loop but which are not used by an
+** index.
+**
+** In the current implementation, the first extra WHERE clause term reduces
+** the number of output rows by a factor of 10 and each additional term
+** reduces the number of output rows by sqrt(2).
+*/
+static void whereLoopOutputAdjust(WhereClause *pWC, WhereLoop *pLoop){
+ WhereTerm *pTerm, *pX;
+ Bitmask notAllowed = ~(pLoop->prereq|pLoop->maskSelf);
+ int i, j;
+
+ if( !OptimizationEnabled(pWC->pWInfo->pParse->db, SQLITE_AdjustOutEst) ){
+ return;
+ }
+ for(i=pWC->nTerm, pTerm=pWC->a; i>0; i--, pTerm++){
+ if( (pTerm->wtFlags & TERM_VIRTUAL)!=0 ) break;
+ if( (pTerm->prereqAll & pLoop->maskSelf)==0 ) continue;
+ if( (pTerm->prereqAll & notAllowed)!=0 ) continue;
+ for(j=pLoop->nLTerm-1; j>=0; j--){
+ pX = pLoop->aLTerm[j];
+ if( pX==pTerm ) break;
+ if( pX->iParent>=0 && (&pWC->a[pX->iParent])==pTerm ) break;
+ }
+ if( j<0 ) pLoop->nOut += pTerm->truthProb;
+ }
+}
+
+/*
+** We have so far matched pBuilder->pNew->u.btree.nEq terms of the index pIndex.
+** Try to match one more.
+**
+** If pProbe->tnum==0, that means pIndex is a fake index used for the
+** INTEGER PRIMARY KEY.
+*/
+static int whereLoopAddBtreeIndex(
+ WhereLoopBuilder *pBuilder, /* The WhereLoop factory */
+ struct SrcList_item *pSrc, /* FROM clause term being analyzed */
+ Index *pProbe, /* An index on pSrc */
+ LogEst nInMul /* log(Number of iterations due to IN) */
+){
+ WhereInfo *pWInfo = pBuilder->pWInfo; /* WHERE analyse context */
+ Parse *pParse = pWInfo->pParse; /* Parsing context */
+ sqlite3 *db = pParse->db; /* Database connection malloc context */
+ WhereLoop *pNew; /* Template WhereLoop under construction */
+ WhereTerm *pTerm; /* A WhereTerm under consideration */
+ int opMask; /* Valid operators for constraints */
+ WhereScan scan; /* Iterator for WHERE terms */
+ Bitmask saved_prereq; /* Original value of pNew->prereq */
+ u16 saved_nLTerm; /* Original value of pNew->nLTerm */
+ int saved_nEq; /* Original value of pNew->u.btree.nEq */
+ u32 saved_wsFlags; /* Original value of pNew->wsFlags */
+ LogEst saved_nOut; /* Original value of pNew->nOut */
+ int iCol; /* Index of the column in the table */
+ int rc = SQLITE_OK; /* Return code */
+ LogEst nRowEst; /* Estimated index selectivity */
+ LogEst rLogSize; /* Logarithm of table size */
+ WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */
+
+ pNew = pBuilder->pNew;
+ if( db->mallocFailed ) return SQLITE_NOMEM;
+
+ assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 );
+ assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 );
+ if( pNew->wsFlags & WHERE_BTM_LIMIT ){
+ opMask = WO_LT|WO_LE;
+ }else if( pProbe->tnum<=0 || (pSrc->jointype & JT_LEFT)!=0 ){
+ opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE;
+ }else{
+ opMask = WO_EQ|WO_IN|WO_ISNULL|WO_GT|WO_GE|WO_LT|WO_LE;
+ }
+ if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
+
+ assert( pNew->u.btree.nEq<=pProbe->nColumn );
+ if( pNew->u.btree.nEq < pProbe->nColumn ){
+ iCol = pProbe->aiColumn[pNew->u.btree.nEq];
+ nRowEst = sqlite3LogEst(pProbe->aiRowEst[pNew->u.btree.nEq+1]);
+ if( nRowEst==0 && pProbe->onError==OE_None ) nRowEst = 1;
+ }else{
+ iCol = -1;
+ nRowEst = 0;
+ }
+ pTerm = whereScanInit(&scan, pBuilder->pWC, pSrc->iCursor, iCol,
+ opMask, pProbe);
+ saved_nEq = pNew->u.btree.nEq;
+ saved_nLTerm = pNew->nLTerm;
+ saved_wsFlags = pNew->wsFlags;
+ saved_prereq = pNew->prereq;
+ saved_nOut = pNew->nOut;
+ pNew->rSetup = 0;
+ rLogSize = estLog(sqlite3LogEst(pProbe->aiRowEst[0]));
+ for(; rc==SQLITE_OK && pTerm!=0; pTerm = whereScanNext(&scan)){
+ int nIn = 0;
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ int nRecValid = pBuilder->nRecValid;
+#endif
+ if( (pTerm->eOperator==WO_ISNULL || (pTerm->wtFlags&TERM_VNULL)!=0)
+ && (iCol<0 || pSrc->pTab->aCol[iCol].notNull)
+ ){
+ continue; /* ignore IS [NOT] NULL constraints on NOT NULL columns */
+ }
+ if( pTerm->prereqRight & pNew->maskSelf ) continue;
+
+ assert( pNew->nOut==saved_nOut );
+
+ pNew->wsFlags = saved_wsFlags;
+ pNew->u.btree.nEq = saved_nEq;
+ pNew->nLTerm = saved_nLTerm;
+ if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
+ pNew->aLTerm[pNew->nLTerm++] = pTerm;
+ pNew->prereq = (saved_prereq | pTerm->prereqRight) & ~pNew->maskSelf;
+ pNew->rRun = rLogSize; /* Baseline cost is log2(N). Adjustments below */
+ if( pTerm->eOperator & WO_IN ){
+ Expr *pExpr = pTerm->pExpr;
+ pNew->wsFlags |= WHERE_COLUMN_IN;
+ if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ /* "x IN (SELECT ...)": TUNING: the SELECT returns 25 rows */
+ nIn = 46; assert( 46==sqlite3LogEst(25) );
+ }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){
+ /* "x IN (value, value, ...)" */
+ nIn = sqlite3LogEst(pExpr->x.pList->nExpr);
+ }
+ pNew->rRun += nIn;
+ pNew->u.btree.nEq++;
+ pNew->nOut = nRowEst + nInMul + nIn;
+ }else if( pTerm->eOperator & (WO_EQ) ){
+ assert( (pNew->wsFlags & (WHERE_COLUMN_NULL|WHERE_COLUMN_IN))!=0
+ || nInMul==0 );
+ pNew->wsFlags |= WHERE_COLUMN_EQ;
+ if( iCol<0
+ || (pProbe->onError!=OE_None && nInMul==0
+ && pNew->u.btree.nEq==pProbe->nColumn-1)
+ ){
+ assert( (pNew->wsFlags & WHERE_COLUMN_IN)==0 || iCol<0 );
+ pNew->wsFlags |= WHERE_ONEROW;
+ }
+ pNew->u.btree.nEq++;
+ pNew->nOut = nRowEst + nInMul;
+ }else if( pTerm->eOperator & (WO_ISNULL) ){
+ pNew->wsFlags |= WHERE_COLUMN_NULL;
+ pNew->u.btree.nEq++;
+ /* TUNING: IS NULL selects 2 rows */
+ nIn = 10; assert( 10==sqlite3LogEst(2) );
+ pNew->nOut = nRowEst + nInMul + nIn;
+ }else if( pTerm->eOperator & (WO_GT|WO_GE) ){
+ testcase( pTerm->eOperator & WO_GT );
+ testcase( pTerm->eOperator & WO_GE );
+ pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT;
+ pBtm = pTerm;
+ pTop = 0;
+ }else{
+ assert( pTerm->eOperator & (WO_LT|WO_LE) );
+ testcase( pTerm->eOperator & WO_LT );
+ testcase( pTerm->eOperator & WO_LE );
+ pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT;
+ pTop = pTerm;
+ pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ?
+ pNew->aLTerm[pNew->nLTerm-2] : 0;
+ }
+ if( pNew->wsFlags & WHERE_COLUMN_RANGE ){
+ /* Adjust nOut and rRun for STAT3 range values */
+ assert( pNew->nOut==saved_nOut );
+ whereRangeScanEst(pParse, pBuilder, pBtm, pTop, pNew);
+ }
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ if( nInMul==0
+ && pProbe->nSample
+ && pNew->u.btree.nEq<=pProbe->nSampleCol
+ && OptimizationEnabled(db, SQLITE_Stat3)
+ ){
+ Expr *pExpr = pTerm->pExpr;
+ tRowcnt nOut = 0;
+ if( (pTerm->eOperator & (WO_EQ|WO_ISNULL))!=0 ){
+ testcase( pTerm->eOperator & WO_EQ );
+ testcase( pTerm->eOperator & WO_ISNULL );
+ rc = whereEqualScanEst(pParse, pBuilder, pExpr->pRight, &nOut);
+ }else if( (pTerm->eOperator & WO_IN)
+ && !ExprHasProperty(pExpr, EP_xIsSelect) ){
+ rc = whereInScanEst(pParse, pBuilder, pExpr->x.pList, &nOut);
+ }
+ assert( nOut==0 || rc==SQLITE_OK );
+ if( nOut ){
+ pNew->nOut = sqlite3LogEst(nOut);
+ if( pNew->nOut>saved_nOut ) pNew->nOut = saved_nOut;
+ }
+ }
+#endif
+ if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK))==0 ){
+ /* Each row involves a step of the index, then a binary search of
+ ** the main table */
+ pNew->rRun = sqlite3LogEstAdd(pNew->rRun,rLogSize>27 ? rLogSize-17 : 10);
+ }
+ /* Step cost for each output row */
+ pNew->rRun = sqlite3LogEstAdd(pNew->rRun, pNew->nOut);
+ whereLoopOutputAdjust(pBuilder->pWC, pNew);
+ rc = whereLoopInsert(pBuilder, pNew);
+ if( (pNew->wsFlags & WHERE_TOP_LIMIT)==0
+ && pNew->u.btree.nEq<(pProbe->nColumn + (pProbe->zName!=0))
+ ){
+ whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn);
+ }
+ pNew->nOut = saved_nOut;
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ pBuilder->nRecValid = nRecValid;
+#endif
+ }
+ pNew->prereq = saved_prereq;
+ pNew->u.btree.nEq = saved_nEq;
+ pNew->wsFlags = saved_wsFlags;
+ pNew->nOut = saved_nOut;
+ pNew->nLTerm = saved_nLTerm;
+ return rc;
+}
+
+/*
+** Return True if it is possible that pIndex might be useful in
+** implementing the ORDER BY clause in pBuilder.
+**
+** Return False if pBuilder does not contain an ORDER BY clause or
+** if there is no way for pIndex to be useful in implementing that
+** ORDER BY clause.
+*/
+static int indexMightHelpWithOrderBy(
+ WhereLoopBuilder *pBuilder,
+ Index *pIndex,
+ int iCursor
+){
+ ExprList *pOB;
+ int ii, jj;
+
+ if( pIndex->bUnordered ) return 0;
+ if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0;
+ for(ii=0; ii<pOB->nExpr; ii++){
+ Expr *pExpr = sqlite3ExprSkipCollate(pOB->a[ii].pExpr);
+ if( pExpr->op!=TK_COLUMN ) return 0;
+ if( pExpr->iTable==iCursor ){
+ for(jj=0; jj<pIndex->nColumn; jj++){
+ if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+** Return a bitmask where 1s indicate that the corresponding column of
+** the table is used by an index. Only the first 63 columns are considered.
+*/
+static Bitmask columnsInIndex(Index *pIdx){
+ Bitmask m = 0;
+ int j;
+ for(j=pIdx->nColumn-1; j>=0; j--){
+ int x = pIdx->aiColumn[j];
+ assert( x>=0 );
+ testcase( x==BMS-1 );
+ testcase( x==BMS-2 );
+ if( x<BMS-1 ) m |= MASKBIT(x);
+ }
+ return m;
+}
+
+/* Check to see if a partial index with pPartIndexWhere can be used
+** in the current query. Return true if it can be and false if not.
+*/
+static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){
+ int i;
+ WhereTerm *pTerm;
+ for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
+ if( sqlite3ExprImpliesExpr(pTerm->pExpr, pWhere, iTab) ) return 1;
+ }
+ return 0;
+}
+
+/*
+** Add all WhereLoop objects for a single table of the join where the table
+** is idenfied by pBuilder->pNew->iTab. That table is guaranteed to be
+** a b-tree table, not a virtual table.
+*/
+static int whereLoopAddBtree(
+ WhereLoopBuilder *pBuilder, /* WHERE clause information */
+ Bitmask mExtra /* Extra prerequesites for using this table */
+){
+ WhereInfo *pWInfo; /* WHERE analysis context */
+ Index *pProbe; /* An index we are evaluating */
+ Index sPk; /* A fake index object for the primary key */
+ tRowcnt aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */
+ int aiColumnPk = -1; /* The aColumn[] value for the sPk index */
+ SrcList *pTabList; /* The FROM clause */
+ struct SrcList_item *pSrc; /* The FROM clause btree term to add */
+ WhereLoop *pNew; /* Template WhereLoop object */
+ int rc = SQLITE_OK; /* Return code */
+ int iSortIdx = 1; /* Index number */
+ int b; /* A boolean value */
+ LogEst rSize; /* number of rows in the table */
+ LogEst rLogSize; /* Logarithm of the number of rows in the table */
+ WhereClause *pWC; /* The parsed WHERE clause */
+ Table *pTab; /* Table being queried */
+
+ pNew = pBuilder->pNew;
+ pWInfo = pBuilder->pWInfo;
+ pTabList = pWInfo->pTabList;
+ pSrc = pTabList->a + pNew->iTab;
+ pTab = pSrc->pTab;
+ pWC = pBuilder->pWC;
+ assert( !IsVirtual(pSrc->pTab) );
+
+ if( pSrc->pIndex ){
+ /* An INDEXED BY clause specifies a particular index to use */
+ pProbe = pSrc->pIndex;
+ }else{
+ /* There is no INDEXED BY clause. Create a fake Index object in local
+ ** variable sPk to represent the rowid primary key index. Make this
+ ** fake index the first in a chain of Index objects with all of the real
+ ** indices to follow */
+ Index *pFirst; /* First of real indices on the table */
+ memset(&sPk, 0, sizeof(Index));
+ sPk.nColumn = 1;
+ sPk.aiColumn = &aiColumnPk;
+ sPk.aiRowEst = aiRowEstPk;
+ sPk.onError = OE_Replace;
+ sPk.pTable = pTab;
+ aiRowEstPk[0] = pTab->nRowEst;
+ aiRowEstPk[1] = 1;
+ pFirst = pSrc->pTab->pIndex;
+ if( pSrc->notIndexed==0 ){
+ /* The real indices of the table are only considered if the
+ ** NOT INDEXED qualifier is omitted from the FROM clause */
+ sPk.pNext = pFirst;
+ }
+ pProbe = &sPk;
+ }
+ rSize = sqlite3LogEst(pTab->nRowEst);
+ rLogSize = estLog(rSize);
+
+#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
+ /* Automatic indexes */
+ if( !pBuilder->pOrSet
+ && (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0
+ && pSrc->pIndex==0
+ && !pSrc->viaCoroutine
+ && !pSrc->notIndexed
+ && !pSrc->isCorrelated
+ ){
+ /* Generate auto-index WhereLoops */
+ WhereTerm *pTerm;
+ WhereTerm *pWCEnd = pWC->a + pWC->nTerm;
+ for(pTerm=pWC->a; rc==SQLITE_OK && pTerm<pWCEnd; pTerm++){
+ if( pTerm->prereqRight & pNew->maskSelf ) continue;
+ if( termCanDriveIndex(pTerm, pSrc, 0) ){
+ pNew->u.btree.nEq = 1;
+ pNew->u.btree.pIndex = 0;
+ pNew->nLTerm = 1;
+ pNew->aLTerm[0] = pTerm;
+ /* TUNING: One-time cost for computing the automatic index is
+ ** approximately 7*N*log2(N) where N is the number of rows in
+ ** the table being indexed. */
+ pNew->rSetup = rLogSize + rSize + 28; assert( 28==sqlite3LogEst(7) );
+ /* TUNING: Each index lookup yields 20 rows in the table. This
+ ** is more than the usual guess of 10 rows, since we have no way
+ ** of knowning how selective the index will ultimately be. It would
+ ** not be unreasonable to make this value much larger. */
+ pNew->nOut = 43; assert( 43==sqlite3LogEst(20) );
+ pNew->rRun = sqlite3LogEstAdd(rLogSize,pNew->nOut);
+ pNew->wsFlags = WHERE_AUTO_INDEX;
+ pNew->prereq = mExtra | pTerm->prereqRight;
+ rc = whereLoopInsert(pBuilder, pNew);
+ }
+ }
+ }
+#endif /* SQLITE_OMIT_AUTOMATIC_INDEX */
+
+ /* Loop over all indices
+ */
+ for(; rc==SQLITE_OK && pProbe; pProbe=pProbe->pNext, iSortIdx++){
+ if( pProbe->pPartIdxWhere!=0
+ && !whereUsablePartialIndex(pNew->iTab, pWC, pProbe->pPartIdxWhere) ){
+ continue; /* Partial index inappropriate for this query */
+ }
+ pNew->u.btree.nEq = 0;
+ pNew->nLTerm = 0;
+ pNew->iSortIdx = 0;
+ pNew->rSetup = 0;
+ pNew->prereq = mExtra;
+ pNew->nOut = rSize;
+ pNew->u.btree.pIndex = pProbe;
+ b = indexMightHelpWithOrderBy(pBuilder, pProbe, pSrc->iCursor);
+ /* The ONEPASS_DESIRED flags never occurs together with ORDER BY */
+ assert( (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || b==0 );
+ if( pProbe->tnum<=0 ){
+ /* Integer primary key index */
+ pNew->wsFlags = WHERE_IPK;
+
+ /* Full table scan */
+ pNew->iSortIdx = b ? iSortIdx : 0;
+ /* TUNING: Cost of full table scan is 3*(N + log2(N)).
+ ** + The extra 3 factor is to encourage the use of indexed lookups
+ ** over full scans. FIXME */
+ pNew->rRun = sqlite3LogEstAdd(rSize,rLogSize) + 16;
+ whereLoopOutputAdjust(pWC, pNew);
+ rc = whereLoopInsert(pBuilder, pNew);
+ pNew->nOut = rSize;
+ if( rc ) break;
+ }else{
+ Bitmask m = pSrc->colUsed & ~columnsInIndex(pProbe);
+ pNew->wsFlags = (m==0) ? (WHERE_IDX_ONLY|WHERE_INDEXED) : WHERE_INDEXED;
+
+ /* Full scan via index */
+ if( b
+ || ( m==0
+ && pProbe->bUnordered==0
+ && pProbe->szIdxRow<pTab->szTabRow
+ && (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0
+ && sqlite3GlobalConfig.bUseCis
+ && OptimizationEnabled(pWInfo->pParse->db, SQLITE_CoverIdxScan)
+ )
+ ){
+ pNew->iSortIdx = b ? iSortIdx : 0;
+ if( m==0 ){
+ /* TUNING: Cost of a covering index scan is K*(N + log2(N)).
+ ** + The extra factor K of between 1.1 and 3.0 that depends
+ ** on the relative sizes of the table and the index. K
+ ** is smaller for smaller indices, thus favoring them.
+ */
+ pNew->rRun = sqlite3LogEstAdd(rSize,rLogSize) + 1 +
+ (15*pProbe->szIdxRow)/pTab->szTabRow;
+ }else{
+ assert( b!=0 );
+ /* TUNING: Cost of scanning a non-covering index is (N+1)*log2(N)
+ ** which we will simplify to just N*log2(N) */
+ pNew->rRun = rSize + rLogSize;
+ }
+ whereLoopOutputAdjust(pWC, pNew);
+ rc = whereLoopInsert(pBuilder, pNew);
+ pNew->nOut = rSize;
+ if( rc ) break;
+ }
+ }
+
+ rc = whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 0);
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ sqlite3Stat4ProbeFree(pBuilder->pRec);
+ pBuilder->nRecValid = 0;
+ pBuilder->pRec = 0;
+#endif
+
+ /* If there was an INDEXED BY clause, then only that one index is
+ ** considered. */
+ if( pSrc->pIndex ) break;
+ }
+ return rc;
+}
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+/*
+** Add all WhereLoop objects for a table of the join identified by
+** pBuilder->pNew->iTab. That table is guaranteed to be a virtual table.
+*/
+static int whereLoopAddVirtual(
+ WhereLoopBuilder *pBuilder /* WHERE clause information */
+){
+ WhereInfo *pWInfo; /* WHERE analysis context */
+ Parse *pParse; /* The parsing context */
+ WhereClause *pWC; /* The WHERE clause */
+ struct SrcList_item *pSrc; /* The FROM clause term to search */
+ Table *pTab;
+ sqlite3 *db;
+ sqlite3_index_info *pIdxInfo;
+ struct sqlite3_index_constraint *pIdxCons;
+ struct sqlite3_index_constraint_usage *pUsage;
+ WhereTerm *pTerm;
+ int i, j;
+ int iTerm, mxTerm;
+ int nConstraint;
+ int seenIn = 0; /* True if an IN operator is seen */
+ int seenVar = 0; /* True if a non-constant constraint is seen */
+ int iPhase; /* 0: const w/o IN, 1: const, 2: no IN, 2: IN */
+ WhereLoop *pNew;
+ int rc = SQLITE_OK;
+
+ pWInfo = pBuilder->pWInfo;
+ pParse = pWInfo->pParse;
+ db = pParse->db;
+ pWC = pBuilder->pWC;
+ pNew = pBuilder->pNew;
+ pSrc = &pWInfo->pTabList->a[pNew->iTab];
+ pTab = pSrc->pTab;
+ assert( IsVirtual(pTab) );
+ pIdxInfo = allocateIndexInfo(pParse, pWC, pSrc, pBuilder->pOrderBy);
+ if( pIdxInfo==0 ) return SQLITE_NOMEM;
+ pNew->prereq = 0;
+ pNew->rSetup = 0;
+ pNew->wsFlags = WHERE_VIRTUALTABLE;
+ pNew->nLTerm = 0;
+ pNew->u.vtab.needFree = 0;
+ pUsage = pIdxInfo->aConstraintUsage;
+ nConstraint = pIdxInfo->nConstraint;
+ if( whereLoopResize(db, pNew, nConstraint) ){
+ sqlite3DbFree(db, pIdxInfo);
+ return SQLITE_NOMEM;
+ }
+
+ for(iPhase=0; iPhase<=3; iPhase++){
+ if( !seenIn && (iPhase&1)!=0 ){
+ iPhase++;
+ if( iPhase>3 ) break;
+ }
+ if( !seenVar && iPhase>1 ) break;
+ pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
+ for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
+ j = pIdxCons->iTermOffset;
+ pTerm = &pWC->a[j];
+ switch( iPhase ){
+ case 0: /* Constants without IN operator */
+ pIdxCons->usable = 0;
+ if( (pTerm->eOperator & WO_IN)!=0 ){
+ seenIn = 1;
+ }
+ if( pTerm->prereqRight!=0 ){
+ seenVar = 1;
+ }else if( (pTerm->eOperator & WO_IN)==0 ){
+ pIdxCons->usable = 1;
+ }
+ break;
+ case 1: /* Constants with IN operators */
+ assert( seenIn );
+ pIdxCons->usable = (pTerm->prereqRight==0);
+ break;
+ case 2: /* Variables without IN */
+ assert( seenVar );
+ pIdxCons->usable = (pTerm->eOperator & WO_IN)==0;
+ break;
+ default: /* Variables with IN */
+ assert( seenVar && seenIn );
+ pIdxCons->usable = 1;
+ break;
+ }
+ }
+ memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint);
+ if( pIdxInfo->needToFreeIdxStr ) sqlite3_free(pIdxInfo->idxStr);
+ pIdxInfo->idxStr = 0;
+ pIdxInfo->idxNum = 0;
+ pIdxInfo->needToFreeIdxStr = 0;
+ pIdxInfo->orderByConsumed = 0;
+ pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2;
+ rc = vtabBestIndex(pParse, pTab, pIdxInfo);
+ if( rc ) goto whereLoopAddVtab_exit;
+ pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
+ pNew->prereq = 0;
+ mxTerm = -1;
+ assert( pNew->nLSlot>=nConstraint );
+ for(i=0; i<nConstraint; i++) pNew->aLTerm[i] = 0;
+ pNew->u.vtab.omitMask = 0;
+ for(i=0; i<nConstraint; i++, pIdxCons++){
+ if( (iTerm = pUsage[i].argvIndex - 1)>=0 ){
+ j = pIdxCons->iTermOffset;
+ if( iTerm>=nConstraint
+ || j<0
+ || j>=pWC->nTerm
+ || pNew->aLTerm[iTerm]!=0
+ ){
+ rc = SQLITE_ERROR;
+ sqlite3ErrorMsg(pParse, "%s.xBestIndex() malfunction", pTab->zName);
+ goto whereLoopAddVtab_exit;
+ }
+ testcase( iTerm==nConstraint-1 );
+ testcase( j==0 );
+ testcase( j==pWC->nTerm-1 );
+ pTerm = &pWC->a[j];
+ pNew->prereq |= pTerm->prereqRight;
+ assert( iTerm<pNew->nLSlot );
+ pNew->aLTerm[iTerm] = pTerm;
+ if( iTerm>mxTerm ) mxTerm = iTerm;
+ testcase( iTerm==15 );
+ testcase( iTerm==16 );
+ if( iTerm<16 && pUsage[i].omit ) pNew->u.vtab.omitMask |= 1<<iTerm;
+ if( (pTerm->eOperator & WO_IN)!=0 ){
+ if( pUsage[i].omit==0 ){
+ /* Do not attempt to use an IN constraint if the virtual table
+ ** says that the equivalent EQ constraint cannot be safely omitted.
+ ** If we do attempt to use such a constraint, some rows might be
+ ** repeated in the output. */
+ break;
+ }
+ /* A virtual table that is constrained by an IN clause may not
+ ** consume the ORDER BY clause because (1) the order of IN terms
+ ** is not necessarily related to the order of output terms and
+ ** (2) Multiple outputs from a single IN value will not merge
+ ** together. */
+ pIdxInfo->orderByConsumed = 0;
}
- sqlite3DbFree(db, pInfo);
}
- if( pWInfo->a[i].plan.wsFlags & WHERE_TEMP_INDEX ){
- Index *pIdx = pWInfo->a[i].plan.u.pIdx;
- if( pIdx ){
- sqlite3DbFree(db, pIdx->zColAff);
- sqlite3DbFree(db, pIdx);
+ }
+ if( i>=nConstraint ){
+ pNew->nLTerm = mxTerm+1;
+ assert( pNew->nLTerm<=pNew->nLSlot );
+ pNew->u.vtab.idxNum = pIdxInfo->idxNum;
+ pNew->u.vtab.needFree = pIdxInfo->needToFreeIdxStr;
+ pIdxInfo->needToFreeIdxStr = 0;
+ pNew->u.vtab.idxStr = pIdxInfo->idxStr;
+ pNew->u.vtab.isOrdered = (u8)((pIdxInfo->nOrderBy!=0)
+ && pIdxInfo->orderByConsumed);
+ pNew->rSetup = 0;
+ pNew->rRun = sqlite3LogEstFromDouble(pIdxInfo->estimatedCost);
+ /* TUNING: Every virtual table query returns 25 rows */
+ pNew->nOut = 46; assert( 46==sqlite3LogEst(25) );
+ whereLoopInsert(pBuilder, pNew);
+ if( pNew->u.vtab.needFree ){
+ sqlite3_free(pNew->u.vtab.idxStr);
+ pNew->u.vtab.needFree = 0;
+ }
+ }
+ }
+
+whereLoopAddVtab_exit:
+ if( pIdxInfo->needToFreeIdxStr ) sqlite3_free(pIdxInfo->idxStr);
+ sqlite3DbFree(db, pIdxInfo);
+ return rc;
+}
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+/*
+** Add WhereLoop entries to handle OR terms. This works for either
+** btrees or virtual tables.
+*/
+static int whereLoopAddOr(WhereLoopBuilder *pBuilder, Bitmask mExtra){
+ WhereInfo *pWInfo = pBuilder->pWInfo;
+ WhereClause *pWC;
+ WhereLoop *pNew;
+ WhereTerm *pTerm, *pWCEnd;
+ int rc = SQLITE_OK;
+ int iCur;
+ WhereClause tempWC;
+ WhereLoopBuilder sSubBuild;
+ WhereOrSet sSum, sCur, sPrev;
+ struct SrcList_item *pItem;
+
+ pWC = pBuilder->pWC;
+ if( pWInfo->wctrlFlags & WHERE_AND_ONLY ) return SQLITE_OK;
+ pWCEnd = pWC->a + pWC->nTerm;
+ pNew = pBuilder->pNew;
+ memset(&sSum, 0, sizeof(sSum));
+ pItem = pWInfo->pTabList->a + pNew->iTab;
+ iCur = pItem->iCursor;
+
+ for(pTerm=pWC->a; pTerm<pWCEnd && rc==SQLITE_OK; pTerm++){
+ if( (pTerm->eOperator & WO_OR)!=0
+ && (pTerm->u.pOrInfo->indexable & pNew->maskSelf)!=0
+ ){
+ WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc;
+ WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm];
+ WhereTerm *pOrTerm;
+ int once = 1;
+ int i, j;
+
+ sSubBuild = *pBuilder;
+ sSubBuild.pOrderBy = 0;
+ sSubBuild.pOrSet = &sCur;
+
+ for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){
+ if( (pOrTerm->eOperator & WO_AND)!=0 ){
+ sSubBuild.pWC = &pOrTerm->u.pAndInfo->wc;
+ }else if( pOrTerm->leftCursor==iCur ){
+ tempWC.pWInfo = pWC->pWInfo;
+ tempWC.pOuter = pWC;
+ tempWC.op = TK_AND;
+ tempWC.nTerm = 1;
+ tempWC.a = pOrTerm;
+ sSubBuild.pWC = &tempWC;
+ }else{
+ continue;
+ }
+ sCur.n = 0;
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ if( IsVirtual(pItem->pTab) ){
+ rc = whereLoopAddVirtual(&sSubBuild);
+ for(i=0; i<sCur.n; i++) sCur.a[i].prereq |= mExtra;
+ }else
+#endif
+ {
+ rc = whereLoopAddBtree(&sSubBuild, mExtra);
+ }
+ assert( rc==SQLITE_OK || sCur.n==0 );
+ if( sCur.n==0 ){
+ sSum.n = 0;
+ break;
+ }else if( once ){
+ whereOrMove(&sSum, &sCur);
+ once = 0;
+ }else{
+ whereOrMove(&sPrev, &sSum);
+ sSum.n = 0;
+ for(i=0; i<sPrev.n; i++){
+ for(j=0; j<sCur.n; j++){
+ whereOrInsert(&sSum, sPrev.a[i].prereq | sCur.a[j].prereq,
+ sqlite3LogEstAdd(sPrev.a[i].rRun, sCur.a[j].rRun),
+ sqlite3LogEstAdd(sPrev.a[i].nOut, sCur.a[j].nOut));
+ }
+ }
}
}
+ pNew->nLTerm = 1;
+ pNew->aLTerm[0] = pTerm;
+ pNew->wsFlags = WHERE_MULTI_OR;
+ pNew->rSetup = 0;
+ pNew->iSortIdx = 0;
+ memset(&pNew->u, 0, sizeof(pNew->u));
+ for(i=0; rc==SQLITE_OK && i<sSum.n; i++){
+ /* TUNING: Multiple by 3.5 for the secondary table lookup */
+ pNew->rRun = sSum.a[i].rRun + 18;
+ pNew->nOut = sSum.a[i].nOut;
+ pNew->prereq = sSum.a[i].prereq;
+ rc = whereLoopInsert(pBuilder, pNew);
+ }
}
- whereClauseClear(pWInfo->pWC);
- sqlite3DbFree(db, pWInfo);
}
+ return rc;
+}
+
+/*
+** Add all WhereLoop objects for all tables
+*/
+static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
+ WhereInfo *pWInfo = pBuilder->pWInfo;
+ Bitmask mExtra = 0;
+ Bitmask mPrior = 0;
+ int iTab;
+ SrcList *pTabList = pWInfo->pTabList;
+ struct SrcList_item *pItem;
+ sqlite3 *db = pWInfo->pParse->db;
+ int nTabList = pWInfo->nLevel;
+ int rc = SQLITE_OK;
+ u8 priorJoinType = 0;
+ WhereLoop *pNew;
+
+ /* Loop over the tables in the join, from left to right */
+ pNew = pBuilder->pNew;
+ whereLoopInit(pNew);
+ for(iTab=0, pItem=pTabList->a; iTab<nTabList; iTab++, pItem++){
+ pNew->iTab = iTab;
+ pNew->maskSelf = getMask(&pWInfo->sMaskSet, pItem->iCursor);
+ if( ((pItem->jointype|priorJoinType) & (JT_LEFT|JT_CROSS))!=0 ){
+ mExtra = mPrior;
+ }
+ priorJoinType = pItem->jointype;
+ if( IsVirtual(pItem->pTab) ){
+ rc = whereLoopAddVirtual(pBuilder);
+ }else{
+ rc = whereLoopAddBtree(pBuilder, mExtra);
+ }
+ if( rc==SQLITE_OK ){
+ rc = whereLoopAddOr(pBuilder, mExtra);
+ }
+ mPrior |= pNew->maskSelf;
+ if( rc || db->mallocFailed ) break;
+ }
+ whereLoopClear(db, pNew);
+ return rc;
+}
+
+/*
+** Examine a WherePath (with the addition of the extra WhereLoop of the 5th
+** parameters) to see if it outputs rows in the requested ORDER BY
+** (or GROUP BY) without requiring a separate sort operation. Return:
+**
+** 0: ORDER BY is not satisfied. Sorting required
+** 1: ORDER BY is satisfied. Omit sorting
+** -1: Unknown at this time
+**
+** Note that processing for WHERE_GROUPBY and WHERE_DISTINCTBY is not as
+** strict. With GROUP BY and DISTINCT the only requirement is that
+** equivalent rows appear immediately adjacent to one another. GROUP BY
+** and DISTINT do not require rows to appear in any particular order as long
+** as equivelent rows are grouped together. Thus for GROUP BY and DISTINCT
+** the pOrderBy terms can be matched in any order. With ORDER BY, the
+** pOrderBy terms must be matched in strict left-to-right order.
+*/
+static int wherePathSatisfiesOrderBy(
+ WhereInfo *pWInfo, /* The WHERE clause */
+ ExprList *pOrderBy, /* ORDER BY or GROUP BY or DISTINCT clause to check */
+ WherePath *pPath, /* The WherePath to check */
+ u16 wctrlFlags, /* Might contain WHERE_GROUPBY or WHERE_DISTINCTBY */
+ u16 nLoop, /* Number of entries in pPath->aLoop[] */
+ WhereLoop *pLast, /* Add this WhereLoop to the end of pPath->aLoop[] */
+ Bitmask *pRevMask /* OUT: Mask of WhereLoops to run in reverse order */
+){
+ u8 revSet; /* True if rev is known */
+ u8 rev; /* Composite sort order */
+ u8 revIdx; /* Index sort order */
+ u8 isOrderDistinct; /* All prior WhereLoops are order-distinct */
+ u8 distinctColumns; /* True if the loop has UNIQUE NOT NULL columns */
+ u8 isMatch; /* iColumn matches a term of the ORDER BY clause */
+ u16 nColumn; /* Number of columns in pIndex */
+ u16 nOrderBy; /* Number terms in the ORDER BY clause */
+ int iLoop; /* Index of WhereLoop in pPath being processed */
+ int i, j; /* Loop counters */
+ int iCur; /* Cursor number for current WhereLoop */
+ int iColumn; /* A column number within table iCur */
+ WhereLoop *pLoop = 0; /* Current WhereLoop being processed. */
+ WhereTerm *pTerm; /* A single term of the WHERE clause */
+ Expr *pOBExpr; /* An expression from the ORDER BY clause */
+ CollSeq *pColl; /* COLLATE function from an ORDER BY clause term */
+ Index *pIndex; /* The index associated with pLoop */
+ sqlite3 *db = pWInfo->pParse->db; /* Database connection */
+ Bitmask obSat = 0; /* Mask of ORDER BY terms satisfied so far */
+ Bitmask obDone; /* Mask of all ORDER BY terms */
+ Bitmask orderDistinctMask; /* Mask of all well-ordered loops */
+ Bitmask ready; /* Mask of inner loops */
+
+ /*
+ ** We say the WhereLoop is "one-row" if it generates no more than one
+ ** row of output. A WhereLoop is one-row if all of the following are true:
+ ** (a) All index columns match with WHERE_COLUMN_EQ.
+ ** (b) The index is unique
+ ** Any WhereLoop with an WHERE_COLUMN_EQ constraint on the rowid is one-row.
+ ** Every one-row WhereLoop will have the WHERE_ONEROW bit set in wsFlags.
+ **
+ ** We say the WhereLoop is "order-distinct" if the set of columns from
+ ** that WhereLoop that are in the ORDER BY clause are different for every
+ ** row of the WhereLoop. Every one-row WhereLoop is automatically
+ ** order-distinct. A WhereLoop that has no columns in the ORDER BY clause
+ ** is not order-distinct. To be order-distinct is not quite the same as being
+ ** UNIQUE since a UNIQUE column or index can have multiple rows that
+ ** are NULL and NULL values are equivalent for the purpose of order-distinct.
+ ** To be order-distinct, the columns must be UNIQUE and NOT NULL.
+ **
+ ** The rowid for a table is always UNIQUE and NOT NULL so whenever the
+ ** rowid appears in the ORDER BY clause, the corresponding WhereLoop is
+ ** automatically order-distinct.
+ */
+
+ assert( pOrderBy!=0 );
+
+ /* Sortability of virtual tables is determined by the xBestIndex method
+ ** of the virtual table itself */
+ if( pLast->wsFlags & WHERE_VIRTUALTABLE ){
+ testcase( nLoop>0 ); /* True when outer loops are one-row and match
+ ** no ORDER BY terms */
+ return pLast->u.vtab.isOrdered;
+ }
+ if( nLoop && OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ) return 0;
+
+ nOrderBy = pOrderBy->nExpr;
+ testcase( nOrderBy==BMS-1 );
+ if( nOrderBy>BMS-1 ) return 0; /* Cannot optimize overly large ORDER BYs */
+ isOrderDistinct = 1;
+ obDone = MASKBIT(nOrderBy)-1;
+ orderDistinctMask = 0;
+ ready = 0;
+ for(iLoop=0; isOrderDistinct && obSat<obDone && iLoop<=nLoop; iLoop++){
+ if( iLoop>0 ) ready |= pLoop->maskSelf;
+ pLoop = iLoop<nLoop ? pPath->aLoop[iLoop] : pLast;
+ assert( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 );
+ iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor;
+
+ /* Mark off any ORDER BY term X that is a column in the table of
+ ** the current loop for which there is term in the WHERE
+ ** clause of the form X IS NULL or X=? that reference only outer
+ ** loops.
+ */
+ for(i=0; i<nOrderBy; i++){
+ if( MASKBIT(i) & obSat ) continue;
+ pOBExpr = sqlite3ExprSkipCollate(pOrderBy->a[i].pExpr);
+ if( pOBExpr->op!=TK_COLUMN ) continue;
+ if( pOBExpr->iTable!=iCur ) continue;
+ pTerm = findTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn,
+ ~ready, WO_EQ|WO_ISNULL, 0);
+ if( pTerm==0 ) continue;
+ if( (pTerm->eOperator&WO_EQ)!=0 && pOBExpr->iColumn>=0 ){
+ const char *z1, *z2;
+ pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr);
+ if( !pColl ) pColl = db->pDfltColl;
+ z1 = pColl->zName;
+ pColl = sqlite3ExprCollSeq(pWInfo->pParse, pTerm->pExpr);
+ if( !pColl ) pColl = db->pDfltColl;
+ z2 = pColl->zName;
+ if( sqlite3StrICmp(z1, z2)!=0 ) continue;
+ }
+ obSat |= MASKBIT(i);
+ }
+
+ if( (pLoop->wsFlags & WHERE_ONEROW)==0 ){
+ if( pLoop->wsFlags & WHERE_IPK ){
+ pIndex = 0;
+ nColumn = 0;
+ }else if( (pIndex = pLoop->u.btree.pIndex)==0 || pIndex->bUnordered ){
+ return 0;
+ }else{
+ nColumn = pIndex->nColumn;
+ isOrderDistinct = pIndex->onError!=OE_None;
+ }
+
+ /* Loop through all columns of the index and deal with the ones
+ ** that are not constrained by == or IN.
+ */
+ rev = revSet = 0;
+ distinctColumns = 0;
+ for(j=0; j<=nColumn; j++){
+ u8 bOnce; /* True to run the ORDER BY search loop */
+
+ /* Skip over == and IS NULL terms */
+ if( j<pLoop->u.btree.nEq
+ && ((i = pLoop->aLTerm[j]->eOperator) & (WO_EQ|WO_ISNULL))!=0
+ ){
+ if( i & WO_ISNULL ){
+ testcase( isOrderDistinct );
+ isOrderDistinct = 0;
+ }
+ continue;
+ }
+
+ /* Get the column number in the table (iColumn) and sort order
+ ** (revIdx) for the j-th column of the index.
+ */
+ if( j<nColumn ){
+ /* Normal index columns */
+ iColumn = pIndex->aiColumn[j];
+ revIdx = pIndex->aSortOrder[j];
+ if( iColumn==pIndex->pTable->iPKey ) iColumn = -1;
+ }else{
+ /* The ROWID column at the end */
+ assert( j==nColumn );
+ iColumn = -1;
+ revIdx = 0;
+ }
+
+ /* An unconstrained column that might be NULL means that this
+ ** WhereLoop is not well-ordered
+ */
+ if( isOrderDistinct
+ && iColumn>=0
+ && j>=pLoop->u.btree.nEq
+ && pIndex->pTable->aCol[iColumn].notNull==0
+ ){
+ isOrderDistinct = 0;
+ }
+
+ /* Find the ORDER BY term that corresponds to the j-th column
+ ** of the index and and mark that ORDER BY term off
+ */
+ bOnce = 1;
+ isMatch = 0;
+ for(i=0; bOnce && i<nOrderBy; i++){
+ if( MASKBIT(i) & obSat ) continue;
+ pOBExpr = sqlite3ExprSkipCollate(pOrderBy->a[i].pExpr);
+ testcase( wctrlFlags & WHERE_GROUPBY );
+ testcase( wctrlFlags & WHERE_DISTINCTBY );
+ if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0;
+ if( pOBExpr->op!=TK_COLUMN ) continue;
+ if( pOBExpr->iTable!=iCur ) continue;
+ if( pOBExpr->iColumn!=iColumn ) continue;
+ if( iColumn>=0 ){
+ pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr);
+ if( !pColl ) pColl = db->pDfltColl;
+ if( sqlite3StrICmp(pColl->zName, pIndex->azColl[j])!=0 ) continue;
+ }
+ isMatch = 1;
+ break;
+ }
+ if( isMatch ){
+ if( iColumn<0 ){
+ testcase( distinctColumns==0 );
+ distinctColumns = 1;
+ }
+ obSat |= MASKBIT(i);
+ if( (pWInfo->wctrlFlags & WHERE_GROUPBY)==0 ){
+ /* Make sure the sort order is compatible in an ORDER BY clause.
+ ** Sort order is irrelevant for a GROUP BY clause. */
+ if( revSet ){
+ if( (rev ^ revIdx)!=pOrderBy->a[i].sortOrder ) return 0;
+ }else{
+ rev = revIdx ^ pOrderBy->a[i].sortOrder;
+ if( rev ) *pRevMask |= MASKBIT(iLoop);
+ revSet = 1;
+ }
+ }
+ }else{
+ /* No match found */
+ if( j==0 || j<nColumn ){
+ testcase( isOrderDistinct!=0 );
+ isOrderDistinct = 0;
+ }
+ break;
+ }
+ } /* end Loop over all index columns */
+ if( distinctColumns ){
+ testcase( isOrderDistinct==0 );
+ isOrderDistinct = 1;
+ }
+ } /* end-if not one-row */
+
+ /* Mark off any other ORDER BY terms that reference pLoop */
+ if( isOrderDistinct ){
+ orderDistinctMask |= pLoop->maskSelf;
+ for(i=0; i<nOrderBy; i++){
+ Expr *p;
+ if( MASKBIT(i) & obSat ) continue;
+ p = pOrderBy->a[i].pExpr;
+ if( (exprTableUsage(&pWInfo->sMaskSet, p)&~orderDistinctMask)==0 ){
+ obSat |= MASKBIT(i);
+ }
+ }
+ }
+ } /* End the loop over all WhereLoops from outer-most down to inner-most */
+ if( obSat==obDone ) return 1;
+ if( !isOrderDistinct ) return 0;
+ return -1;
+}
+
+#ifdef WHERETRACE_ENABLED
+/* For debugging use only: */
+static const char *wherePathName(WherePath *pPath, int nLoop, WhereLoop *pLast){
+ static char zName[65];
+ int i;
+ for(i=0; i<nLoop; i++){ zName[i] = pPath->aLoop[i]->cId; }
+ if( pLast ) zName[i++] = pLast->cId;
+ zName[i] = 0;
+ return zName;
+}
+#endif
+
+
+/*
+** Given the list of WhereLoop objects at pWInfo->pLoops, this routine
+** attempts to find the lowest cost path that visits each WhereLoop
+** once. This path is then loaded into the pWInfo->a[].pWLoop fields.
+**
+** Assume that the total number of output rows that will need to be sorted
+** will be nRowEst (in the 10*log2 representation). Or, ignore sorting
+** costs if nRowEst==0.
+**
+** Return SQLITE_OK on success or SQLITE_NOMEM of a memory allocation
+** error occurs.
+*/
+static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
+ int mxChoice; /* Maximum number of simultaneous paths tracked */
+ int nLoop; /* Number of terms in the join */
+ Parse *pParse; /* Parsing context */
+ sqlite3 *db; /* The database connection */
+ int iLoop; /* Loop counter over the terms of the join */
+ int ii, jj; /* Loop counters */
+ int mxI = 0; /* Index of next entry to replace */
+ LogEst rCost; /* Cost of a path */
+ LogEst nOut; /* Number of outputs */
+ LogEst mxCost = 0; /* Maximum cost of a set of paths */
+ LogEst mxOut = 0; /* Maximum nOut value on the set of paths */
+ LogEst rSortCost; /* Cost to do a sort */
+ int nTo, nFrom; /* Number of valid entries in aTo[] and aFrom[] */
+ WherePath *aFrom; /* All nFrom paths at the previous level */
+ WherePath *aTo; /* The nTo best paths at the current level */
+ WherePath *pFrom; /* An element of aFrom[] that we are working on */
+ WherePath *pTo; /* An element of aTo[] that we are working on */
+ WhereLoop *pWLoop; /* One of the WhereLoop objects */
+ WhereLoop **pX; /* Used to divy up the pSpace memory */
+ char *pSpace; /* Temporary memory used by this routine */
+
+ pParse = pWInfo->pParse;
+ db = pParse->db;
+ nLoop = pWInfo->nLevel;
+ /* TUNING: For simple queries, only the best path is tracked.
+ ** For 2-way joins, the 5 best paths are followed.
+ ** For joins of 3 or more tables, track the 10 best paths */
+ mxChoice = (nLoop==1) ? 1 : (nLoop==2 ? 5 : 10);
+ assert( nLoop<=pWInfo->pTabList->nSrc );
+ WHERETRACE(0x002, ("---- begin solver\n"));
+
+ /* Allocate and initialize space for aTo and aFrom */
+ ii = (sizeof(WherePath)+sizeof(WhereLoop*)*nLoop)*mxChoice*2;
+ pSpace = sqlite3DbMallocRaw(db, ii);
+ if( pSpace==0 ) return SQLITE_NOMEM;
+ aTo = (WherePath*)pSpace;
+ aFrom = aTo+mxChoice;
+ memset(aFrom, 0, sizeof(aFrom[0]));
+ pX = (WhereLoop**)(aFrom+mxChoice);
+ for(ii=mxChoice*2, pFrom=aTo; ii>0; ii--, pFrom++, pX += nLoop){
+ pFrom->aLoop = pX;
+ }
+
+ /* Seed the search with a single WherePath containing zero WhereLoops.
+ **
+ ** TUNING: Do not let the number of iterations go above 25. If the cost
+ ** of computing an automatic index is not paid back within the first 25
+ ** rows, then do not use the automatic index. */
+ aFrom[0].nRow = MIN(pParse->nQueryLoop, 46); assert( 46==sqlite3LogEst(25) );
+ nFrom = 1;
+
+ /* Precompute the cost of sorting the final result set, if the caller
+ ** to sqlite3WhereBegin() was concerned about sorting */
+ rSortCost = 0;
+ if( pWInfo->pOrderBy==0 || nRowEst==0 ){
+ aFrom[0].isOrderedValid = 1;
+ }else{
+ /* TUNING: Estimated cost of sorting is 48*N*log2(N) where N is the
+ ** number of output rows. The 48 is the expected size of a row to sort.
+ ** FIXME: compute a better estimate of the 48 multiplier based on the
+ ** result set expressions. */
+ rSortCost = nRowEst + estLog(nRowEst);
+ WHERETRACE(0x002,("---- sort cost=%-3d\n", rSortCost));
+ }
+
+ /* Compute successively longer WherePaths using the previous generation
+ ** of WherePaths as the basis for the next. Keep track of the mxChoice
+ ** best paths at each generation */
+ for(iLoop=0; iLoop<nLoop; iLoop++){
+ nTo = 0;
+ for(ii=0, pFrom=aFrom; ii<nFrom; ii++, pFrom++){
+ for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){
+ Bitmask maskNew;
+ Bitmask revMask = 0;
+ u8 isOrderedValid = pFrom->isOrderedValid;
+ u8 isOrdered = pFrom->isOrdered;
+ if( (pWLoop->prereq & ~pFrom->maskLoop)!=0 ) continue;
+ if( (pWLoop->maskSelf & pFrom->maskLoop)!=0 ) continue;
+ /* At this point, pWLoop is a candidate to be the next loop.
+ ** Compute its cost */
+ rCost = sqlite3LogEstAdd(pWLoop->rSetup,pWLoop->rRun + pFrom->nRow);
+ rCost = sqlite3LogEstAdd(rCost, pFrom->rCost);
+ nOut = pFrom->nRow + pWLoop->nOut;
+ maskNew = pFrom->maskLoop | pWLoop->maskSelf;
+ if( !isOrderedValid ){
+ switch( wherePathSatisfiesOrderBy(pWInfo,
+ pWInfo->pOrderBy, pFrom, pWInfo->wctrlFlags,
+ iLoop, pWLoop, &revMask) ){
+ case 1: /* Yes. pFrom+pWLoop does satisfy the ORDER BY clause */
+ isOrdered = 1;
+ isOrderedValid = 1;
+ break;
+ case 0: /* No. pFrom+pWLoop will require a separate sort */
+ isOrdered = 0;
+ isOrderedValid = 1;
+ rCost = sqlite3LogEstAdd(rCost, rSortCost);
+ break;
+ default: /* Cannot tell yet. Try again on the next iteration */
+ break;
+ }
+ }else{
+ revMask = pFrom->revLoop;
+ }
+ /* Check to see if pWLoop should be added to the mxChoice best so far */
+ for(jj=0, pTo=aTo; jj<nTo; jj++, pTo++){
+ if( pTo->maskLoop==maskNew
+ && pTo->isOrderedValid==isOrderedValid
+ && ((pTo->rCost<=rCost && pTo->nRow<=nOut) ||
+ (pTo->rCost>=rCost && pTo->nRow>=nOut))
+ ){
+ testcase( jj==nTo-1 );
+ break;
+ }
+ }
+ if( jj>=nTo ){
+ if( nTo>=mxChoice && rCost>=mxCost ){
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace&0x4 ){
+ sqlite3DebugPrintf("Skip %s cost=%-3d,%3d order=%c\n",
+ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut,
+ isOrderedValid ? (isOrdered ? 'Y' : 'N') : '?');
+ }
+#endif
+ continue;
+ }
+ /* Add a new Path to the aTo[] set */
+ if( nTo<mxChoice ){
+ /* Increase the size of the aTo set by one */
+ jj = nTo++;
+ }else{
+ /* New path replaces the prior worst to keep count below mxChoice */
+ jj = mxI;
+ }
+ pTo = &aTo[jj];
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace&0x4 ){
+ sqlite3DebugPrintf("New %s cost=%-3d,%3d order=%c\n",
+ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut,
+ isOrderedValid ? (isOrdered ? 'Y' : 'N') : '?');
+ }
+#endif
+ }else{
+ if( pTo->rCost<=rCost && pTo->nRow<=nOut ){
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace&0x4 ){
+ sqlite3DebugPrintf(
+ "Skip %s cost=%-3d,%3d order=%c",
+ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut,
+ isOrderedValid ? (isOrdered ? 'Y' : 'N') : '?');
+ sqlite3DebugPrintf(" vs %s cost=%-3d,%d order=%c\n",
+ wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow,
+ pTo->isOrderedValid ? (pTo->isOrdered ? 'Y' : 'N') : '?');
+ }
+#endif
+ testcase( pTo->rCost==rCost );
+ continue;
+ }
+ testcase( pTo->rCost==rCost+1 );
+ /* A new and better score for a previously created equivalent path */
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace&0x4 ){
+ sqlite3DebugPrintf(
+ "Update %s cost=%-3d,%3d order=%c",
+ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut,
+ isOrderedValid ? (isOrdered ? 'Y' : 'N') : '?');
+ sqlite3DebugPrintf(" was %s cost=%-3d,%3d order=%c\n",
+ wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow,
+ pTo->isOrderedValid ? (pTo->isOrdered ? 'Y' : 'N') : '?');
+ }
+#endif
+ }
+ /* pWLoop is a winner. Add it to the set of best so far */
+ pTo->maskLoop = pFrom->maskLoop | pWLoop->maskSelf;
+ pTo->revLoop = revMask;
+ pTo->nRow = nOut;
+ pTo->rCost = rCost;
+ pTo->isOrderedValid = isOrderedValid;
+ pTo->isOrdered = isOrdered;
+ memcpy(pTo->aLoop, pFrom->aLoop, sizeof(WhereLoop*)*iLoop);
+ pTo->aLoop[iLoop] = pWLoop;
+ if( nTo>=mxChoice ){
+ mxI = 0;
+ mxCost = aTo[0].rCost;
+ mxOut = aTo[0].nRow;
+ for(jj=1, pTo=&aTo[1]; jj<mxChoice; jj++, pTo++){
+ if( pTo->rCost>mxCost || (pTo->rCost==mxCost && pTo->nRow>mxOut) ){
+ mxCost = pTo->rCost;
+ mxOut = pTo->nRow;
+ mxI = jj;
+ }
+ }
+ }
+ }
+ }
+
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace>=2 ){
+ sqlite3DebugPrintf("---- after round %d ----\n", iLoop);
+ for(ii=0, pTo=aTo; ii<nTo; ii++, pTo++){
+ sqlite3DebugPrintf(" %s cost=%-3d nrow=%-3d order=%c",
+ wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow,
+ pTo->isOrderedValid ? (pTo->isOrdered ? 'Y' : 'N') : '?');
+ if( pTo->isOrderedValid && pTo->isOrdered ){
+ sqlite3DebugPrintf(" rev=0x%llx\n", pTo->revLoop);
+ }else{
+ sqlite3DebugPrintf("\n");
+ }
+ }
+ }
+#endif
+
+ /* Swap the roles of aFrom and aTo for the next generation */
+ pFrom = aTo;
+ aTo = aFrom;
+ aFrom = pFrom;
+ nFrom = nTo;
+ }
+
+ if( nFrom==0 ){
+ sqlite3ErrorMsg(pParse, "no query solution");
+ sqlite3DbFree(db, pSpace);
+ return SQLITE_ERROR;
+ }
+
+ /* Find the lowest cost path. pFrom will be left pointing to that path */
+ pFrom = aFrom;
+ for(ii=1; ii<nFrom; ii++){
+ if( pFrom->rCost>aFrom[ii].rCost ) pFrom = &aFrom[ii];
+ }
+ assert( pWInfo->nLevel==nLoop );
+ /* Load the lowest cost path into pWInfo */
+ for(iLoop=0; iLoop<nLoop; iLoop++){
+ WhereLevel *pLevel = pWInfo->a + iLoop;
+ pLevel->pWLoop = pWLoop = pFrom->aLoop[iLoop];
+ pLevel->iFrom = pWLoop->iTab;
+ pLevel->iTabCur = pWInfo->pTabList->a[pLevel->iFrom].iCursor;
+ }
+ if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)!=0
+ && (pWInfo->wctrlFlags & WHERE_DISTINCTBY)==0
+ && pWInfo->eDistinct==WHERE_DISTINCT_NOOP
+ && nRowEst
+ ){
+ Bitmask notUsed;
+ int rc = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pResultSet, pFrom,
+ WHERE_DISTINCTBY, nLoop-1, pFrom->aLoop[nLoop-1], &notUsed);
+ if( rc==1 ) pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
+ }
+ if( pFrom->isOrdered ){
+ if( pWInfo->wctrlFlags & WHERE_DISTINCTBY ){
+ pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
+ }else{
+ pWInfo->bOBSat = 1;
+ pWInfo->revMask = pFrom->revLoop;
+ }
+ }
+ pWInfo->nRowOut = pFrom->nRow;
+
+ /* Free temporary memory and return success */
+ sqlite3DbFree(db, pSpace);
+ return SQLITE_OK;
}
+/*
+** Most queries use only a single table (they are not joins) and have
+** simple == constraints against indexed fields. This routine attempts
+** to plan those simple cases using much less ceremony than the
+** general-purpose query planner, and thereby yield faster sqlite3_prepare()
+** times for the common case.
+**
+** Return non-zero on success, if this query can be handled by this
+** no-frills query planner. Return zero if this query needs the
+** general-purpose query planner.
+*/
+static int whereShortCut(WhereLoopBuilder *pBuilder){
+ WhereInfo *pWInfo;
+ struct SrcList_item *pItem;
+ WhereClause *pWC;
+ WhereTerm *pTerm;
+ WhereLoop *pLoop;
+ int iCur;
+ int j;
+ Table *pTab;
+ Index *pIdx;
+
+ pWInfo = pBuilder->pWInfo;
+ if( pWInfo->wctrlFlags & WHERE_FORCE_TABLE ) return 0;
+ assert( pWInfo->pTabList->nSrc>=1 );
+ pItem = pWInfo->pTabList->a;
+ pTab = pItem->pTab;
+ if( IsVirtual(pTab) ) return 0;
+ if( pItem->zIndex ) return 0;
+ iCur = pItem->iCursor;
+ pWC = &pWInfo->sWC;
+ pLoop = pBuilder->pNew;
+ pLoop->wsFlags = 0;
+ pTerm = findTerm(pWC, iCur, -1, 0, WO_EQ, 0);
+ if( pTerm ){
+ pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_IPK|WHERE_ONEROW;
+ pLoop->aLTerm[0] = pTerm;
+ pLoop->nLTerm = 1;
+ pLoop->u.btree.nEq = 1;
+ /* TUNING: Cost of a rowid lookup is 10 */
+ pLoop->rRun = 33; /* 33==sqlite3LogEst(10) */
+ }else{
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ assert( pLoop->aLTermSpace==pLoop->aLTerm );
+ assert( ArraySize(pLoop->aLTermSpace)==4 );
+ if( pIdx->onError==OE_None
+ || pIdx->pPartIdxWhere!=0
+ || pIdx->nColumn>ArraySize(pLoop->aLTermSpace)
+ ) continue;
+ for(j=0; j<pIdx->nColumn; j++){
+ pTerm = findTerm(pWC, iCur, pIdx->aiColumn[j], 0, WO_EQ, pIdx);
+ if( pTerm==0 ) break;
+ pLoop->aLTerm[j] = pTerm;
+ }
+ if( j!=pIdx->nColumn ) continue;
+ pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_ONEROW|WHERE_INDEXED;
+ if( (pItem->colUsed & ~columnsInIndex(pIdx))==0 ){
+ pLoop->wsFlags |= WHERE_IDX_ONLY;
+ }
+ pLoop->nLTerm = j;
+ pLoop->u.btree.nEq = j;
+ pLoop->u.btree.pIndex = pIdx;
+ /* TUNING: Cost of a unique index lookup is 15 */
+ pLoop->rRun = 39; /* 39==sqlite3LogEst(15) */
+ break;
+ }
+ }
+ if( pLoop->wsFlags ){
+ pLoop->nOut = (LogEst)1;
+ pWInfo->a[0].pWLoop = pLoop;
+ pLoop->maskSelf = getMask(&pWInfo->sMaskSet, iCur);
+ pWInfo->a[0].iTabCur = iCur;
+ pWInfo->nRowOut = 1;
+ if( pWInfo->pOrderBy ) pWInfo->bOBSat = 1;
+ if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){
+ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
+ }
+#ifdef SQLITE_DEBUG
+ pLoop->cId = '0';
+#endif
+ return 1;
+ }
+ return 0;
+}
/*
** Generate the beginning of the loop used for WHERE clause processing.
@@ -109413,25 +112293,17 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
**
** ORDER BY CLAUSE PROCESSING
**
-** pOrderBy is a pointer to the ORDER BY clause of a SELECT statement,
+** pOrderBy is a pointer to the ORDER BY clause (or the GROUP BY clause
+** if the WHERE_GROUPBY flag is set in wctrlFlags) of a SELECT statement
** if there is one. If there is no ORDER BY clause or if this routine
** is called from an UPDATE or DELETE statement, then pOrderBy is NULL.
-**
-** If an index can be used so that the natural output order of the table
-** scan is correct for the ORDER BY clause, then that index is used and
-** the returned WhereInfo.nOBSat field is set to pOrderBy->nExpr. This
-** is an optimization that prevents an unnecessary sort of the result set
-** if an index appropriate for the ORDER BY clause already exists.
-**
-** If the where clause loops cannot be arranged to provide the correct
-** output order, then WhereInfo.nOBSat is 0.
*/
SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
Parse *pParse, /* The parser context */
- SrcList *pTabList, /* A list of all tables to be scanned */
+ SrcList *pTabList, /* FROM clause: A list of all tables to be scanned */
Expr *pWhere, /* The WHERE clause */
ExprList *pOrderBy, /* An ORDER BY clause, or NULL */
- ExprList *pDistinct, /* The select-list for DISTINCT queries - or NULL */
+ ExprList *pResultSet, /* Result set of the query */
u16 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */
int iIdxCur /* If WHERE_ONETABLE_ONLY is set, index cursor number */
){
@@ -109440,18 +112312,25 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
WhereInfo *pWInfo; /* Will become the return value of this function */
Vdbe *v = pParse->pVdbe; /* The virtual database engine */
Bitmask notReady; /* Cursors that are not yet positioned */
- WhereBestIdx sWBI; /* Best index search context */
+ WhereLoopBuilder sWLB; /* The WhereLoop builder */
WhereMaskSet *pMaskSet; /* The expression mask set */
WhereLevel *pLevel; /* A single level in pWInfo->a[] */
- int iFrom; /* First unused FROM clause element */
- int andFlags; /* AND-ed combination of all pWC->a[].wtFlags */
+ WhereLoop *pLoop; /* Pointer to a single WhereLoop object */
int ii; /* Loop counter */
sqlite3 *db; /* Database connection */
+ int rc; /* Return code */
/* Variable initialization */
- memset(&sWBI, 0, sizeof(sWBI));
- sWBI.pParse = pParse;
+ db = pParse->db;
+ memset(&sWLB, 0, sizeof(sWLB));
+ sWLB.pOrderBy = pOrderBy;
+
+ /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via
+ ** sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */
+ if( OptimizationDisabled(db, SQLITE_DistinctOpt) ){
+ wctrlFlags &= ~WHERE_WANT_DISTINCT;
+ }
/* The number of tables in the FROM clause is limited by the number of
** bits in a Bitmask
@@ -109476,13 +112355,8 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
** field (type Bitmask) it must be aligned on an 8-byte boundary on
** some architectures. Hence the ROUND8() below.
*/
- db = pParse->db;
nByteWInfo = ROUND8(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel));
- pWInfo = sqlite3DbMallocZero(db,
- nByteWInfo +
- sizeof(WhereClause) +
- sizeof(WhereMaskSet)
- );
+ pWInfo = sqlite3DbMallocZero(db, nByteWInfo + sizeof(WhereLoop));
if( db->mallocFailed ){
sqlite3DbFree(db, pWInfo);
pWInfo = 0;
@@ -109491,24 +112365,29 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
pWInfo->nLevel = nTabList;
pWInfo->pParse = pParse;
pWInfo->pTabList = pTabList;
+ pWInfo->pOrderBy = pOrderBy;
+ pWInfo->pResultSet = pResultSet;
pWInfo->iBreak = sqlite3VdbeMakeLabel(v);
- pWInfo->pWC = sWBI.pWC = (WhereClause *)&((u8 *)pWInfo)[nByteWInfo];
pWInfo->wctrlFlags = wctrlFlags;
pWInfo->savedNQueryLoop = pParse->nQueryLoop;
- pMaskSet = (WhereMaskSet*)&sWBI.pWC[1];
- sWBI.aLevel = pWInfo->a;
-
- /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via
- ** sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */
- if( OptimizationDisabled(db, SQLITE_DistinctOpt) ) pDistinct = 0;
+ pMaskSet = &pWInfo->sMaskSet;
+ sWLB.pWInfo = pWInfo;
+ sWLB.pWC = &pWInfo->sWC;
+ sWLB.pNew = (WhereLoop*)(((char*)pWInfo)+nByteWInfo);
+ assert( EIGHT_BYTE_ALIGNMENT(sWLB.pNew) );
+ whereLoopInit(sWLB.pNew);
+#ifdef SQLITE_DEBUG
+ sWLB.pNew->cId = '*';
+#endif
/* Split the WHERE clause into separate subexpressions where each
** subexpression is separated by an AND operator.
*/
initMaskSet(pMaskSet);
- whereClauseInit(sWBI.pWC, pParse, pMaskSet, wctrlFlags);
+ whereClauseInit(&pWInfo->sWC, pWInfo);
sqlite3ExprCodeConstants(pParse, pWhere);
- whereSplit(sWBI.pWC, pWhere, TK_AND); /* IMP: R-15842-53296 */
+ whereSplit(&pWInfo->sWC, pWhere, TK_AND);
+ sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */
/* Special case: a WHERE clause that is constant. Evaluate the
** expression and either jump over all of the code or fall thru.
@@ -109518,6 +112397,15 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
pWhere = 0;
}
+ /* Special case: No FROM clause
+ */
+ if( nTabList==0 ){
+ if( pOrderBy ) pWInfo->bOBSat = 1;
+ if( wctrlFlags & WHERE_WANT_DISTINCT ){
+ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
+ }
+ }
+
/* Assign a bit from the bitmask to every term in the FROM clause.
**
** When assigning bitmask values to FROM clause cursors, it must be
@@ -109553,306 +112441,148 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
** want to analyze these virtual terms, so start analyzing at the end
** and work forward so that the added virtual terms are never processed.
*/
- exprAnalyzeAll(pTabList, sWBI.pWC);
+ exprAnalyzeAll(pTabList, &pWInfo->sWC);
if( db->mallocFailed ){
goto whereBeginError;
}
- /* Check if the DISTINCT qualifier, if there is one, is redundant.
- ** If it is, then set pDistinct to NULL and WhereInfo.eDistinct to
- ** WHERE_DISTINCT_UNIQUE to tell the caller to ignore the DISTINCT.
+ /* If the ORDER BY (or GROUP BY) clause contains references to general
+ ** expressions, then we won't be able to satisfy it using indices, so
+ ** go ahead and disable it now.
*/
- if( pDistinct && isDistinctRedundant(pParse, pTabList, sWBI.pWC, pDistinct) ){
- pDistinct = 0;
- pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
+ if( pOrderBy && (wctrlFlags & WHERE_WANT_DISTINCT)!=0 ){
+ for(ii=0; ii<pOrderBy->nExpr; ii++){
+ Expr *pExpr = sqlite3ExprSkipCollate(pOrderBy->a[ii].pExpr);
+ if( pExpr->op!=TK_COLUMN ){
+ pWInfo->pOrderBy = pOrderBy = 0;
+ break;
+ }else if( pExpr->iColumn<0 ){
+ break;
+ }
+ }
}
- /* Chose the best index to use for each table in the FROM clause.
- **
- ** This loop fills in the following fields:
- **
- ** pWInfo->a[].pIdx The index to use for this level of the loop.
- ** pWInfo->a[].wsFlags WHERE_xxx flags associated with pIdx
- ** pWInfo->a[].nEq The number of == and IN constraints
- ** pWInfo->a[].iFrom Which term of the FROM clause is being coded
- ** pWInfo->a[].iTabCur The VDBE cursor for the database table
- ** pWInfo->a[].iIdxCur The VDBE cursor for the index
- ** pWInfo->a[].pTerm When wsFlags==WO_OR, the OR-clause term
- **
- ** This loop also figures out the nesting order of tables in the FROM
- ** clause.
- */
- sWBI.notValid = ~(Bitmask)0;
- sWBI.pOrderBy = pOrderBy;
- sWBI.n = nTabList;
- sWBI.pDistinct = pDistinct;
- andFlags = ~0;
- WHERETRACE(("*** Optimizer Start ***\n"));
- for(sWBI.i=iFrom=0, pLevel=pWInfo->a; sWBI.i<nTabList; sWBI.i++, pLevel++){
- WhereCost bestPlan; /* Most efficient plan seen so far */
- Index *pIdx; /* Index for FROM table at pTabItem */
- int j; /* For looping over FROM tables */
- int bestJ = -1; /* The value of j */
- Bitmask m; /* Bitmask value for j or bestJ */
- int isOptimal; /* Iterator for optimal/non-optimal search */
- int ckOptimal; /* Do the optimal scan check */
- int nUnconstrained; /* Number tables without INDEXED BY */
- Bitmask notIndexed; /* Mask of tables that cannot use an index */
-
- memset(&bestPlan, 0, sizeof(bestPlan));
- bestPlan.rCost = SQLITE_BIG_DBL;
- WHERETRACE(("*** Begin search for loop %d ***\n", sWBI.i));
-
- /* Loop through the remaining entries in the FROM clause to find the
- ** next nested loop. The loop tests all FROM clause entries
- ** either once or twice.
- **
- ** The first test is always performed if there are two or more entries
- ** remaining and never performed if there is only one FROM clause entry
- ** to choose from. The first test looks for an "optimal" scan. In
- ** this context an optimal scan is one that uses the same strategy
- ** for the given FROM clause entry as would be selected if the entry
- ** were used as the innermost nested loop. In other words, a table
- ** is chosen such that the cost of running that table cannot be reduced
- ** by waiting for other tables to run first. This "optimal" test works
- ** by first assuming that the FROM clause is on the inner loop and finding
- ** its query plan, then checking to see if that query plan uses any
- ** other FROM clause terms that are sWBI.notValid. If no notValid terms
- ** are used then the "optimal" query plan works.
- **
- ** Note that the WhereCost.nRow parameter for an optimal scan might
- ** not be as small as it would be if the table really were the innermost
- ** join. The nRow value can be reduced by WHERE clause constraints
- ** that do not use indices. But this nRow reduction only happens if the
- ** table really is the innermost join.
- **
- ** The second loop iteration is only performed if no optimal scan
- ** strategies were found by the first iteration. This second iteration
- ** is used to search for the lowest cost scan overall.
- **
- ** Without the optimal scan step (the first iteration) a suboptimal
- ** plan might be chosen for queries like this:
- **
- ** CREATE TABLE t1(a, b);
- ** CREATE TABLE t2(c, d);
- ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a;
- **
- ** The best strategy is to iterate through table t1 first. However it
- ** is not possible to determine this with a simple greedy algorithm.
- ** Since the cost of a linear scan through table t2 is the same
- ** as the cost of a linear scan through table t1, a simple greedy
- ** algorithm may choose to use t2 for the outer loop, which is a much
- ** costlier approach.
- */
- nUnconstrained = 0;
- notIndexed = 0;
-
- /* The optimal scan check only occurs if there are two or more tables
- ** available to be reordered */
- if( iFrom==nTabList-1 ){
- ckOptimal = 0; /* Common case of just one table in the FROM clause */
- }else{
- ckOptimal = -1;
- for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){
- m = getMask(pMaskSet, sWBI.pSrc->iCursor);
- if( (m & sWBI.notValid)==0 ){
- if( j==iFrom ) iFrom++;
- continue;
- }
- if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ) break;
- if( ++ckOptimal ) break;
- if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break;
- }
+ if( wctrlFlags & WHERE_WANT_DISTINCT ){
+ if( isDistinctRedundant(pParse, pTabList, &pWInfo->sWC, pResultSet) ){
+ /* The DISTINCT marking is pointless. Ignore it. */
+ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
+ }else if( pOrderBy==0 ){
+ /* Try to ORDER BY the result set to make distinct processing easier */
+ pWInfo->wctrlFlags |= WHERE_DISTINCTBY;
+ pWInfo->pOrderBy = pResultSet;
}
- assert( ckOptimal==0 || ckOptimal==1 );
+ }
- for(isOptimal=ckOptimal; isOptimal>=0 && bestJ<0; isOptimal--){
- for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){
- if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ){
- /* This break and one like it in the ckOptimal computation loop
- ** above prevent table reordering across LEFT and CROSS JOINs.
- ** The LEFT JOIN case is necessary for correctness. The prohibition
- ** against reordering across a CROSS JOIN is an SQLite feature that
- ** allows the developer to control table reordering */
- break;
- }
- m = getMask(pMaskSet, sWBI.pSrc->iCursor);
- if( (m & sWBI.notValid)==0 ){
- assert( j>iFrom );
- continue;
- }
- sWBI.notReady = (isOptimal ? m : sWBI.notValid);
- if( sWBI.pSrc->pIndex==0 ) nUnconstrained++;
+ /* Construct the WhereLoop objects */
+ WHERETRACE(0xffff,("*** Optimizer Start ***\n"));
+ if( nTabList!=1 || whereShortCut(&sWLB)==0 ){
+ rc = whereLoopAddAll(&sWLB);
+ if( rc ) goto whereBeginError;
- WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n",
- j, sWBI.pSrc->pTab->zName, isOptimal));
- assert( sWBI.pSrc->pTab );
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( IsVirtual(sWBI.pSrc->pTab) ){
- sWBI.ppIdxInfo = &pWInfo->a[j].pIdxInfo;
- bestVirtualIndex(&sWBI);
- }else
-#endif
- {
- bestBtreeIndex(&sWBI);
- }
- assert( isOptimal || (sWBI.cost.used&sWBI.notValid)==0 );
-
- /* If an INDEXED BY clause is present, then the plan must use that
- ** index if it uses any index at all */
- assert( sWBI.pSrc->pIndex==0
- || (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0
- || sWBI.cost.plan.u.pIdx==sWBI.pSrc->pIndex );
-
- if( isOptimal && (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){
- notIndexed |= m;
- }
- if( isOptimal ){
- pWInfo->a[j].rOptCost = sWBI.cost.rCost;
- }else if( ckOptimal ){
- /* If two or more tables have nearly the same outer loop cost, but
- ** very different inner loop (optimal) cost, we want to choose
- ** for the outer loop that table which benefits the least from
- ** being in the inner loop. The following code scales the
- ** outer loop cost estimate to accomplish that. */
- WHERETRACE((" scaling cost from %.1f to %.1f\n",
- sWBI.cost.rCost,
- sWBI.cost.rCost/pWInfo->a[j].rOptCost));
- sWBI.cost.rCost /= pWInfo->a[j].rOptCost;
- }
-
- /* Conditions under which this table becomes the best so far:
- **
- ** (1) The table must not depend on other tables that have not
- ** yet run. (In other words, it must not depend on tables
- ** in inner loops.)
- **
- ** (2) (This rule was removed on 2012-11-09. The scaling of the
- ** cost using the optimal scan cost made this rule obsolete.)
- **
- ** (3) All tables have an INDEXED BY clause or this table lacks an
- ** INDEXED BY clause or this table uses the specific
- ** index specified by its INDEXED BY clause. This rule ensures
- ** that a best-so-far is always selected even if an impossible
- ** combination of INDEXED BY clauses are given. The error
- ** will be detected and relayed back to the application later.
- ** The NEVER() comes about because rule (2) above prevents
- ** An indexable full-table-scan from reaching rule (3).
- **
- ** (4) The plan cost must be lower than prior plans, where "cost"
- ** is defined by the compareCost() function above.
- */
- if( (sWBI.cost.used&sWBI.notValid)==0 /* (1) */
- && (nUnconstrained==0 || sWBI.pSrc->pIndex==0 /* (3) */
- || NEVER((sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0))
- && (bestJ<0 || compareCost(&sWBI.cost, &bestPlan)) /* (4) */
- ){
- WHERETRACE((" === table %d (%s) is best so far\n"
- " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=%08x\n",
- j, sWBI.pSrc->pTab->zName,
- sWBI.cost.rCost, sWBI.cost.plan.nRow,
- sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags));
- bestPlan = sWBI.cost;
- bestJ = j;
- }
-
- /* In a join like "w JOIN x LEFT JOIN y JOIN z" make sure that
- ** table y (and not table z) is always the next inner loop inside
- ** of table x. */
- if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break;
- }
- }
- assert( bestJ>=0 );
- assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) );
- assert( bestJ==iFrom || (pTabList->a[iFrom].jointype & JT_LEFT)==0 );
- testcase( bestJ>iFrom && (pTabList->a[iFrom].jointype & JT_CROSS)!=0 );
- testcase( bestJ>iFrom && bestJ<nTabList-1
- && (pTabList->a[bestJ+1].jointype & JT_LEFT)!=0 );
- WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n"
- " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n",
- bestJ, pTabList->a[bestJ].pTab->zName,
- pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow,
- bestPlan.plan.nOBSat, bestPlan.plan.wsFlags));
- if( (bestPlan.plan.wsFlags & WHERE_DISTINCT)!=0 ){
- assert( pWInfo->eDistinct==0 );
- pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
- }
- andFlags &= bestPlan.plan.wsFlags;
- pLevel->plan = bestPlan.plan;
- pLevel->iTabCur = pTabList->a[bestJ].iCursor;
- testcase( bestPlan.plan.wsFlags & WHERE_INDEXED );
- testcase( bestPlan.plan.wsFlags & WHERE_TEMP_INDEX );
- if( bestPlan.plan.wsFlags & (WHERE_INDEXED|WHERE_TEMP_INDEX) ){
- if( (wctrlFlags & WHERE_ONETABLE_ONLY)
- && (bestPlan.plan.wsFlags & WHERE_TEMP_INDEX)==0
- ){
- pLevel->iIdxCur = iIdxCur;
- }else{
- pLevel->iIdxCur = pParse->nTab++;
+ /* Display all of the WhereLoop objects if wheretrace is enabled */
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace ){
+ WhereLoop *p;
+ int i;
+ static char zLabel[] = "0123456789abcdefghijklmnopqrstuvwyxz"
+ "ABCDEFGHIJKLMNOPQRSTUVWYXZ";
+ for(p=pWInfo->pLoops, i=0; p; p=p->pNextLoop, i++){
+ p->cId = zLabel[i%sizeof(zLabel)];
+ whereLoopPrint(p, pTabList);
}
- }else{
- pLevel->iIdxCur = -1;
}
- sWBI.notValid &= ~getMask(pMaskSet, pTabList->a[bestJ].iCursor);
- pLevel->iFrom = (u8)bestJ;
- if( bestPlan.plan.nRow>=(double)1 ){
- pParse->nQueryLoop *= bestPlan.plan.nRow;
- }
-
- /* Check that if the table scanned by this loop iteration had an
- ** INDEXED BY clause attached to it, that the named index is being
- ** used for the scan. If not, then query compilation has failed.
- ** Return an error.
- */
- pIdx = pTabList->a[bestJ].pIndex;
- if( pIdx ){
- if( (bestPlan.plan.wsFlags & WHERE_INDEXED)==0 ){
- sqlite3ErrorMsg(pParse, "cannot use index: %s", pIdx->zName);
- goto whereBeginError;
- }else{
- /* If an INDEXED BY clause is used, the bestIndex() function is
- ** guaranteed to find the index specified in the INDEXED BY clause
- ** if it find an index at all. */
- assert( bestPlan.plan.u.pIdx==pIdx );
- }
+#endif
+
+ wherePathSolver(pWInfo, 0);
+ if( db->mallocFailed ) goto whereBeginError;
+ if( pWInfo->pOrderBy ){
+ wherePathSolver(pWInfo, pWInfo->nRowOut+1);
+ if( db->mallocFailed ) goto whereBeginError;
}
}
- WHERETRACE(("*** Optimizer Finished ***\n"));
- if( pParse->nErr || db->mallocFailed ){
+ if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){
+ pWInfo->revMask = (Bitmask)(-1);
+ }
+ if( pParse->nErr || NEVER(db->mallocFailed) ){
goto whereBeginError;
}
- if( nTabList ){
- pLevel--;
- pWInfo->nOBSat = pLevel->plan.nOBSat;
- }else{
- pWInfo->nOBSat = 0;
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace ){
+ int ii;
+ sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut);
+ if( pWInfo->bOBSat ){
+ sqlite3DebugPrintf(" ORDERBY=0x%llx", pWInfo->revMask);
+ }
+ switch( pWInfo->eDistinct ){
+ case WHERE_DISTINCT_UNIQUE: {
+ sqlite3DebugPrintf(" DISTINCT=unique");
+ break;
+ }
+ case WHERE_DISTINCT_ORDERED: {
+ sqlite3DebugPrintf(" DISTINCT=ordered");
+ break;
+ }
+ case WHERE_DISTINCT_UNORDERED: {
+ sqlite3DebugPrintf(" DISTINCT=unordered");
+ break;
+ }
+ }
+ sqlite3DebugPrintf("\n");
+ for(ii=0; ii<pWInfo->nLevel; ii++){
+ whereLoopPrint(pWInfo->a[ii].pWLoop, pTabList);
+ }
}
-
- /* If the total query only selects a single row, then the ORDER BY
- ** clause is irrelevant.
- */
- if( (andFlags & WHERE_UNIQUE)!=0 && pOrderBy ){
- assert( nTabList==0 || (pLevel->plan.wsFlags & WHERE_ALL_UNIQUE)!=0 );
- pWInfo->nOBSat = pOrderBy->nExpr;
+#endif
+ /* Attempt to omit tables from the join that do not effect the result */
+ if( pWInfo->nLevel>=2
+ && pResultSet!=0
+ && OptimizationEnabled(db, SQLITE_OmitNoopJoin)
+ ){
+ Bitmask tabUsed = exprListTableUsage(pMaskSet, pResultSet);
+ if( sWLB.pOrderBy ) tabUsed |= exprListTableUsage(pMaskSet, sWLB.pOrderBy);
+ while( pWInfo->nLevel>=2 ){
+ WhereTerm *pTerm, *pEnd;
+ pLoop = pWInfo->a[pWInfo->nLevel-1].pWLoop;
+ if( (pWInfo->pTabList->a[pLoop->iTab].jointype & JT_LEFT)==0 ) break;
+ if( (wctrlFlags & WHERE_WANT_DISTINCT)==0
+ && (pLoop->wsFlags & WHERE_ONEROW)==0
+ ){
+ break;
+ }
+ if( (tabUsed & pLoop->maskSelf)!=0 ) break;
+ pEnd = sWLB.pWC->a + sWLB.pWC->nTerm;
+ for(pTerm=sWLB.pWC->a; pTerm<pEnd; pTerm++){
+ if( (pTerm->prereqAll & pLoop->maskSelf)!=0
+ && !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
+ ){
+ break;
+ }
+ }
+ if( pTerm<pEnd ) break;
+ WHERETRACE(0xffff, ("-> drop loop %c not used\n", pLoop->cId));
+ pWInfo->nLevel--;
+ nTabList--;
+ }
}
+ WHERETRACE(0xffff,("*** Optimizer Finished ***\n"));
+ pWInfo->pParse->nQueryLoop += pWInfo->nRowOut;
/* If the caller is an UPDATE or DELETE statement that is requesting
** to use a one-pass algorithm, determine if this is appropriate.
- ** The one-pass algorithm only works if the WHERE clause constraints
+ ** The one-pass algorithm only works if the WHERE clause constrains
** the statement to update a single row.
*/
assert( (wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || pWInfo->nLevel==1 );
- if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 && (andFlags & WHERE_UNIQUE)!=0 ){
+ if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0
+ && (pWInfo->a[0].pWLoop->wsFlags & WHERE_ONEROW)!=0 ){
pWInfo->okOnePass = 1;
- pWInfo->a[0].plan.wsFlags &= ~WHERE_IDX_ONLY;
+ pWInfo->a[0].pWLoop->wsFlags &= ~WHERE_IDX_ONLY;
}
/* Open all tables in the pTabList and any indices selected for
** searching those tables.
*/
- sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */
notReady = ~(Bitmask)0;
- pWInfo->nRowOut = (double)1;
for(ii=0, pLevel=pWInfo->a; ii<nTabList; ii++, pLevel++){
Table *pTab; /* Table to open */
int iDb; /* Index of database containing table/index */
@@ -109860,13 +112590,13 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
pTabItem = &pTabList->a[pLevel->iFrom];
pTab = pTabItem->pTab;
- pWInfo->nRowOut *= pLevel->plan.nRow;
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+ pLoop = pLevel->pWLoop;
if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){
/* Do nothing */
}else
#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){
+ if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)!=0 ){
const char *pVTab = (const char *)sqlite3GetVTable(db, pTab);
int iCur = pTabItem->iCursor;
sqlite3VdbeAddOp4(v, OP_VOpen, iCur, 0, 0, pVTab, P4_VTAB);
@@ -109874,12 +112604,12 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
/* noop */
}else
#endif
- if( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0
+ if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0
&& (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 ){
int op = pWInfo->okOnePass ? OP_OpenWrite : OP_OpenRead;
sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, op);
- testcase( pTab->nCol==BMS-1 );
- testcase( pTab->nCol==BMS );
+ testcase( !pWInfo->okOnePass && pTab->nCol==BMS-1 );
+ testcase( !pWInfo->okOnePass && pTab->nCol==BMS );
if( !pWInfo->okOnePass && pTab->nCol<BMS ){
Bitmask b = pTabItem->colUsed;
int n = 0;
@@ -109891,15 +112621,11 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
}else{
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
}
-#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
- if( (pLevel->plan.wsFlags & WHERE_TEMP_INDEX)!=0 ){
- constructAutomaticIndex(pParse, sWBI.pWC, pTabItem, notReady, pLevel);
- }else
-#endif
- if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){
- Index *pIx = pLevel->plan.u.pIdx;
+ if( pLoop->wsFlags & WHERE_INDEXED ){
+ Index *pIx = pLoop->u.btree.pIndex;
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIx);
- int iIndexCur = pLevel->iIdxCur;
+ /* FIXME: As an optimization use pTabItem->iCursor if WHERE_IDX_ONLY */
+ int iIndexCur = pLevel->iIdxCur = iIdxCur ? iIdxCur : pParse->nTab++;
assert( pIx->pSchema==pTab->pSchema );
assert( iIndexCur>=0 );
sqlite3VdbeAddOp4(v, OP_OpenRead, iIndexCur, pIx->tnum, iDb,
@@ -109907,7 +112633,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
VdbeComment((v, "%s", pIx->zName));
}
sqlite3CodeVerifySchema(pParse, iDb);
- notReady &= ~getMask(sWBI.pWC->pMaskSet, pTabItem->iCursor);
+ notReady &= ~getMask(&pWInfo->sMaskSet, pTabItem->iCursor);
}
pWInfo->iTop = sqlite3VdbeCurrentAddr(v);
if( db->mallocFailed ) goto whereBeginError;
@@ -109919,67 +112645,20 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
notReady = ~(Bitmask)0;
for(ii=0; ii<nTabList; ii++){
pLevel = &pWInfo->a[ii];
+#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
+ if( (pLevel->pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 ){
+ constructAutomaticIndex(pParse, &pWInfo->sWC,
+ &pTabList->a[pLevel->iFrom], notReady, pLevel);
+ if( db->mallocFailed ) goto whereBeginError;
+ }
+#endif
explainOneScan(pParse, pTabList, pLevel, ii, pLevel->iFrom, wctrlFlags);
- notReady = codeOneLoopStart(pWInfo, ii, wctrlFlags, notReady);
+ pLevel->addrBody = sqlite3VdbeCurrentAddr(v);
+ notReady = codeOneLoopStart(pWInfo, ii, notReady);
pWInfo->iContinue = pLevel->addrCont;
}
-#ifdef SQLITE_TEST /* For testing and debugging use only */
- /* Record in the query plan information about the current table
- ** and the index used to access it (if any). If the table itself
- ** is not used, its name is just '{}'. If no index is used
- ** the index is listed as "{}". If the primary key is used the
- ** index name is '*'.
- */
- for(ii=0; ii<nTabList; ii++){
- char *z;
- int n;
- int w;
- struct SrcList_item *pTabItem;
-
- pLevel = &pWInfo->a[ii];
- w = pLevel->plan.wsFlags;
- pTabItem = &pTabList->a[pLevel->iFrom];
- z = pTabItem->zAlias;
- if( z==0 ) z = pTabItem->pTab->zName;
- n = sqlite3Strlen30(z);
- if( n+nQPlan < sizeof(sqlite3_query_plan)-10 ){
- if( (w & WHERE_IDX_ONLY)!=0 && (w & WHERE_COVER_SCAN)==0 ){
- memcpy(&sqlite3_query_plan[nQPlan], "{}", 2);
- nQPlan += 2;
- }else{
- memcpy(&sqlite3_query_plan[nQPlan], z, n);
- nQPlan += n;
- }
- sqlite3_query_plan[nQPlan++] = ' ';
- }
- testcase( w & WHERE_ROWID_EQ );
- testcase( w & WHERE_ROWID_RANGE );
- if( w & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){
- memcpy(&sqlite3_query_plan[nQPlan], "* ", 2);
- nQPlan += 2;
- }else if( (w & WHERE_INDEXED)!=0 && (w & WHERE_COVER_SCAN)==0 ){
- n = sqlite3Strlen30(pLevel->plan.u.pIdx->zName);
- if( n+nQPlan < sizeof(sqlite3_query_plan)-2 ){
- memcpy(&sqlite3_query_plan[nQPlan], pLevel->plan.u.pIdx->zName, n);
- nQPlan += n;
- sqlite3_query_plan[nQPlan++] = ' ';
- }
- }else{
- memcpy(&sqlite3_query_plan[nQPlan], "{} ", 3);
- nQPlan += 3;
- }
- }
- while( nQPlan>0 && sqlite3_query_plan[nQPlan-1]==' ' ){
- sqlite3_query_plan[--nQPlan] = 0;
- }
- sqlite3_query_plan[nQPlan] = 0;
- nQPlan = 0;
-#endif /* SQLITE_TEST // Testing and debugging use only */
-
- /* Record the continuation address in the WhereInfo structure. Then
- ** clean up and return.
- */
+ /* Done. */
return pWInfo;
/* Jump here if malloc fails */
@@ -110000,6 +112679,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
Vdbe *v = pParse->pVdbe;
int i;
WhereLevel *pLevel;
+ WhereLoop *pLoop;
SrcList *pTabList = pWInfo->pTabList;
sqlite3 *db = pParse->db;
@@ -110008,12 +112688,13 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
sqlite3ExprCacheClear(pParse);
for(i=pWInfo->nLevel-1; i>=0; i--){
pLevel = &pWInfo->a[i];
+ pLoop = pLevel->pWLoop;
sqlite3VdbeResolveLabel(v, pLevel->addrCont);
if( pLevel->op!=OP_Noop ){
sqlite3VdbeAddOp2(v, pLevel->op, pLevel->p1, pLevel->p2);
sqlite3VdbeChangeP5(v, pLevel->p5);
}
- if( pLevel->plan.wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){
+ if( pLoop->wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){
struct InLoop *pIn;
int j;
sqlite3VdbeResolveLabel(v, pLevel->addrNxt);
@@ -110028,12 +112709,12 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
if( pLevel->iLeftJoin ){
int addr;
addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin);
- assert( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0
- || (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 );
- if( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 ){
+ assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0
+ || (pLoop->wsFlags & WHERE_INDEXED)!=0 );
+ if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 ){
sqlite3VdbeAddOp1(v, OP_NullRow, pTabList->a[i].iCursor);
}
- if( pLevel->iIdxCur>=0 ){
+ if( pLoop->wsFlags & WHERE_INDEXED ){
sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iIdxCur);
}
if( pLevel->op==OP_Return ){
@@ -110052,31 +112733,30 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
/* Close all of the cursors that were opened by sqlite3WhereBegin.
*/
- assert( pWInfo->nLevel==1 || pWInfo->nLevel==pTabList->nSrc );
+ assert( pWInfo->nLevel<=pTabList->nSrc );
for(i=0, pLevel=pWInfo->a; i<pWInfo->nLevel; i++, pLevel++){
Index *pIdx = 0;
struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom];
Table *pTab = pTabItem->pTab;
assert( pTab!=0 );
+ pLoop = pLevel->pWLoop;
if( (pTab->tabFlags & TF_Ephemeral)==0
&& pTab->pSelect==0
&& (pWInfo->wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0
){
- int ws = pLevel->plan.wsFlags;
+ int ws = pLoop->wsFlags;
if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){
sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor);
}
- if( (ws & WHERE_INDEXED)!=0 && (ws & WHERE_TEMP_INDEX)==0 ){
+ if( (ws & WHERE_INDEXED)!=0 && (ws & (WHERE_IPK|WHERE_AUTO_INDEX))==0 ){
sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur);
}
}
- /* If this scan uses an index, make code substitutions to read data
- ** from the index in preference to the table. Sometimes, this means
- ** the table need never be read from. This is a performance boost,
- ** as the vdbe level waits until the table is read before actually
- ** seeking the table cursor to the record corresponding to the current
- ** position in the index.
+ /* If this scan uses an index, make VDBE code substitutions to read data
+ ** from the index instead of from the table where possible. In some cases
+ ** this optimization prevents the table from ever being read, which can
+ ** yield a significant performance boost.
**
** Calls to the code generator in between sqlite3WhereBegin and
** sqlite3WhereEnd will have created code that references the table
@@ -110084,18 +112764,19 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
** that reference the table and converts them into opcodes that
** reference the index.
*/
- if( pLevel->plan.wsFlags & WHERE_INDEXED ){
- pIdx = pLevel->plan.u.pIdx;
- }else if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){
+ if( pLoop->wsFlags & (WHERE_INDEXED|WHERE_IDX_ONLY) ){
+ pIdx = pLoop->u.btree.pIndex;
+ }else if( pLoop->wsFlags & WHERE_MULTI_OR ){
pIdx = pLevel->u.pCovidx;
}
- if( pIdx && !db->mallocFailed){
+ if( pIdx && !db->mallocFailed ){
int k, j, last;
VdbeOp *pOp;
- pOp = sqlite3VdbeGetOp(v, pWInfo->iTop);
last = sqlite3VdbeCurrentAddr(v);
- for(k=pWInfo->iTop; k<last; k++, pOp++){
+ k = pLevel->addrBody;
+ pOp = sqlite3VdbeGetOp(v, k);
+ for(; k<last; k++, pOp++){
if( pOp->p1!=pLevel->iTabCur ) continue;
if( pOp->opcode==OP_Column ){
for(j=0; j<pIdx->nColumn; j++){
@@ -110105,8 +112786,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
break;
}
}
- assert( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0
- || j<pIdx->nColumn );
+ assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || j<pIdx->nColumn );
}else if( pOp->opcode==OP_Rowid ){
pOp->p1 = pLevel->iIdxCur;
pOp->opcode = OP_IdxRowid;
@@ -110344,7 +113024,7 @@ typedef union {
#define sqlite3ParserARG_PDECL ,Parse *pParse
#define sqlite3ParserARG_FETCH Parse *pParse = yypParser->pParse
#define sqlite3ParserARG_STORE yypParser->pParse = pParse
-#define YYNSTATE 627
+#define YYNSTATE 628
#define YYNRULE 327
#define YYFALLBACK 1
#define YY_NO_ACTION (YYNSTATE+YYNRULE+2)
@@ -110417,163 +113097,163 @@ static const YYMINORTYPE yyzerominor = { 0 };
*/
#define YY_ACTTAB_COUNT (1564)
static const YYACTIONTYPE yy_action[] = {
- /* 0 */ 309, 955, 184, 417, 2, 171, 624, 594, 56, 56,
+ /* 0 */ 310, 956, 184, 418, 2, 171, 625, 595, 56, 56,
/* 10 */ 56, 56, 49, 54, 54, 54, 54, 53, 53, 52,
- /* 20 */ 52, 52, 51, 233, 620, 619, 298, 620, 619, 234,
- /* 30 */ 587, 581, 56, 56, 56, 56, 19, 54, 54, 54,
- /* 40 */ 54, 53, 53, 52, 52, 52, 51, 233, 605, 57,
- /* 50 */ 58, 48, 579, 578, 580, 580, 55, 55, 56, 56,
- /* 60 */ 56, 56, 541, 54, 54, 54, 54, 53, 53, 52,
- /* 70 */ 52, 52, 51, 233, 309, 594, 325, 196, 195, 194,
+ /* 20 */ 52, 52, 51, 233, 621, 620, 299, 621, 620, 234,
+ /* 30 */ 588, 582, 56, 56, 56, 56, 19, 54, 54, 54,
+ /* 40 */ 54, 53, 53, 52, 52, 52, 51, 233, 606, 57,
+ /* 50 */ 58, 48, 580, 579, 581, 581, 55, 55, 56, 56,
+ /* 60 */ 56, 56, 542, 54, 54, 54, 54, 53, 53, 52,
+ /* 70 */ 52, 52, 51, 233, 310, 595, 326, 196, 195, 194,
/* 80 */ 33, 54, 54, 54, 54, 53, 53, 52, 52, 52,
- /* 90 */ 51, 233, 617, 616, 165, 617, 616, 380, 377, 376,
- /* 100 */ 407, 532, 576, 576, 587, 581, 303, 422, 375, 59,
+ /* 90 */ 51, 233, 618, 617, 165, 618, 617, 381, 378, 377,
+ /* 100 */ 408, 533, 577, 577, 588, 582, 304, 423, 376, 59,
/* 110 */ 53, 53, 52, 52, 52, 51, 233, 50, 47, 146,
- /* 120 */ 574, 545, 65, 57, 58, 48, 579, 578, 580, 580,
+ /* 120 */ 575, 546, 65, 57, 58, 48, 580, 579, 581, 581,
/* 130 */ 55, 55, 56, 56, 56, 56, 213, 54, 54, 54,
- /* 140 */ 54, 53, 53, 52, 52, 52, 51, 233, 309, 223,
- /* 150 */ 539, 420, 170, 176, 138, 280, 383, 275, 382, 168,
- /* 160 */ 489, 551, 409, 668, 620, 619, 271, 438, 409, 438,
- /* 170 */ 550, 604, 67, 482, 507, 618, 599, 412, 587, 581,
- /* 180 */ 600, 483, 618, 412, 618, 598, 91, 439, 440, 439,
- /* 190 */ 335, 598, 73, 669, 222, 266, 480, 57, 58, 48,
- /* 200 */ 579, 578, 580, 580, 55, 55, 56, 56, 56, 56,
- /* 210 */ 670, 54, 54, 54, 54, 53, 53, 52, 52, 52,
- /* 220 */ 51, 233, 309, 279, 232, 231, 1, 132, 200, 385,
- /* 230 */ 620, 619, 617, 616, 278, 435, 289, 563, 175, 262,
- /* 240 */ 409, 264, 437, 497, 436, 166, 441, 568, 336, 568,
- /* 250 */ 201, 537, 587, 581, 599, 412, 165, 594, 600, 380,
- /* 260 */ 377, 376, 597, 598, 92, 523, 618, 569, 569, 592,
- /* 270 */ 375, 57, 58, 48, 579, 578, 580, 580, 55, 55,
- /* 280 */ 56, 56, 56, 56, 597, 54, 54, 54, 54, 53,
- /* 290 */ 53, 52, 52, 52, 51, 233, 309, 463, 617, 616,
- /* 300 */ 590, 590, 590, 174, 272, 396, 409, 272, 409, 548,
- /* 310 */ 397, 620, 619, 68, 326, 620, 619, 620, 619, 618,
- /* 320 */ 546, 412, 618, 412, 471, 594, 587, 581, 472, 598,
- /* 330 */ 92, 598, 92, 52, 52, 52, 51, 233, 513, 512,
- /* 340 */ 206, 322, 363, 464, 221, 57, 58, 48, 579, 578,
- /* 350 */ 580, 580, 55, 55, 56, 56, 56, 56, 529, 54,
+ /* 140 */ 54, 53, 53, 52, 52, 52, 51, 233, 310, 223,
+ /* 150 */ 540, 421, 170, 176, 138, 281, 384, 276, 383, 168,
+ /* 160 */ 490, 552, 410, 669, 621, 620, 272, 439, 410, 439,
+ /* 170 */ 551, 605, 67, 483, 508, 619, 600, 413, 588, 582,
+ /* 180 */ 601, 484, 619, 413, 619, 599, 91, 440, 441, 440,
+ /* 190 */ 336, 599, 73, 670, 222, 267, 481, 57, 58, 48,
+ /* 200 */ 580, 579, 581, 581, 55, 55, 56, 56, 56, 56,
+ /* 210 */ 671, 54, 54, 54, 54, 53, 53, 52, 52, 52,
+ /* 220 */ 51, 233, 310, 280, 232, 231, 1, 132, 200, 386,
+ /* 230 */ 621, 620, 618, 617, 279, 436, 290, 564, 175, 263,
+ /* 240 */ 410, 265, 438, 498, 437, 166, 442, 569, 337, 569,
+ /* 250 */ 201, 538, 588, 582, 600, 413, 165, 595, 601, 381,
+ /* 260 */ 378, 377, 598, 599, 92, 524, 619, 570, 570, 593,
+ /* 270 */ 376, 57, 58, 48, 580, 579, 581, 581, 55, 55,
+ /* 280 */ 56, 56, 56, 56, 598, 54, 54, 54, 54, 53,
+ /* 290 */ 53, 52, 52, 52, 51, 233, 310, 464, 618, 617,
+ /* 300 */ 591, 591, 591, 174, 273, 397, 410, 273, 410, 549,
+ /* 310 */ 398, 621, 620, 68, 327, 621, 620, 621, 620, 619,
+ /* 320 */ 547, 413, 619, 413, 472, 595, 588, 582, 473, 599,
+ /* 330 */ 92, 599, 92, 52, 52, 52, 51, 233, 514, 513,
+ /* 340 */ 206, 323, 364, 465, 221, 57, 58, 48, 580, 579,
+ /* 350 */ 581, 581, 55, 55, 56, 56, 56, 56, 530, 54,
/* 360 */ 54, 54, 54, 53, 53, 52, 52, 52, 51, 233,
- /* 370 */ 309, 396, 409, 396, 597, 372, 386, 530, 347, 617,
- /* 380 */ 616, 575, 202, 617, 616, 617, 616, 412, 620, 619,
- /* 390 */ 145, 255, 346, 254, 577, 598, 74, 351, 45, 489,
- /* 400 */ 587, 581, 235, 189, 464, 544, 167, 296, 187, 469,
- /* 410 */ 479, 67, 62, 39, 618, 546, 597, 345, 573, 57,
- /* 420 */ 58, 48, 579, 578, 580, 580, 55, 55, 56, 56,
+ /* 370 */ 310, 397, 410, 397, 598, 373, 387, 531, 348, 618,
+ /* 380 */ 617, 576, 202, 618, 617, 618, 617, 413, 621, 620,
+ /* 390 */ 145, 255, 347, 254, 578, 599, 74, 352, 45, 490,
+ /* 400 */ 588, 582, 235, 189, 465, 545, 167, 297, 187, 470,
+ /* 410 */ 480, 67, 62, 39, 619, 547, 598, 346, 574, 57,
+ /* 420 */ 58, 48, 580, 579, 581, 581, 55, 55, 56, 56,
/* 430 */ 56, 56, 6, 54, 54, 54, 54, 53, 53, 52,
- /* 440 */ 52, 52, 51, 233, 309, 562, 558, 407, 528, 576,
- /* 450 */ 576, 344, 255, 346, 254, 182, 617, 616, 503, 504,
- /* 460 */ 314, 409, 557, 235, 166, 271, 409, 352, 564, 181,
- /* 470 */ 407, 546, 576, 576, 587, 581, 412, 537, 556, 561,
- /* 480 */ 517, 412, 618, 249, 598, 16, 7, 36, 467, 598,
- /* 490 */ 92, 516, 618, 57, 58, 48, 579, 578, 580, 580,
- /* 500 */ 55, 55, 56, 56, 56, 56, 541, 54, 54, 54,
- /* 510 */ 54, 53, 53, 52, 52, 52, 51, 233, 309, 327,
- /* 520 */ 572, 571, 525, 558, 560, 394, 871, 246, 409, 248,
- /* 530 */ 171, 392, 594, 219, 407, 409, 576, 576, 502, 557,
- /* 540 */ 364, 145, 510, 412, 407, 229, 576, 576, 587, 581,
- /* 550 */ 412, 598, 92, 381, 269, 556, 166, 400, 598, 69,
- /* 560 */ 501, 419, 945, 199, 945, 198, 546, 57, 58, 48,
- /* 570 */ 579, 578, 580, 580, 55, 55, 56, 56, 56, 56,
- /* 580 */ 568, 54, 54, 54, 54, 53, 53, 52, 52, 52,
- /* 590 */ 51, 233, 309, 317, 419, 944, 508, 944, 308, 597,
- /* 600 */ 594, 565, 490, 212, 173, 247, 423, 615, 614, 613,
- /* 610 */ 323, 197, 143, 405, 572, 571, 489, 66, 50, 47,
- /* 620 */ 146, 594, 587, 581, 232, 231, 559, 427, 67, 555,
- /* 630 */ 15, 618, 186, 543, 303, 421, 35, 206, 432, 423,
- /* 640 */ 552, 57, 58, 48, 579, 578, 580, 580, 55, 55,
+ /* 440 */ 52, 52, 51, 233, 310, 563, 559, 408, 529, 577,
+ /* 450 */ 577, 345, 255, 347, 254, 182, 618, 617, 504, 505,
+ /* 460 */ 315, 410, 558, 235, 166, 272, 410, 353, 565, 181,
+ /* 470 */ 408, 547, 577, 577, 588, 582, 413, 538, 557, 562,
+ /* 480 */ 518, 413, 619, 249, 599, 16, 7, 36, 468, 599,
+ /* 490 */ 92, 517, 619, 57, 58, 48, 580, 579, 581, 581,
+ /* 500 */ 55, 55, 56, 56, 56, 56, 542, 54, 54, 54,
+ /* 510 */ 54, 53, 53, 52, 52, 52, 51, 233, 310, 328,
+ /* 520 */ 573, 572, 526, 559, 561, 395, 872, 246, 410, 248,
+ /* 530 */ 171, 393, 595, 219, 408, 410, 577, 577, 503, 558,
+ /* 540 */ 365, 145, 511, 413, 408, 229, 577, 577, 588, 582,
+ /* 550 */ 413, 599, 92, 382, 270, 557, 166, 401, 599, 69,
+ /* 560 */ 502, 420, 946, 199, 946, 198, 547, 57, 58, 48,
+ /* 570 */ 580, 579, 581, 581, 55, 55, 56, 56, 56, 56,
+ /* 580 */ 569, 54, 54, 54, 54, 53, 53, 52, 52, 52,
+ /* 590 */ 51, 233, 310, 318, 420, 945, 509, 945, 309, 598,
+ /* 600 */ 595, 566, 491, 212, 173, 247, 424, 616, 615, 614,
+ /* 610 */ 324, 197, 143, 406, 573, 572, 490, 66, 50, 47,
+ /* 620 */ 146, 595, 588, 582, 232, 231, 560, 428, 67, 556,
+ /* 630 */ 15, 619, 186, 544, 304, 422, 35, 206, 433, 424,
+ /* 640 */ 553, 57, 58, 48, 580, 579, 581, 581, 55, 55,
/* 650 */ 56, 56, 56, 56, 205, 54, 54, 54, 54, 53,
- /* 660 */ 53, 52, 52, 52, 51, 233, 309, 569, 569, 260,
- /* 670 */ 268, 597, 12, 373, 568, 166, 409, 313, 409, 420,
- /* 680 */ 409, 473, 473, 365, 618, 50, 47, 146, 597, 594,
- /* 690 */ 468, 412, 166, 412, 351, 412, 587, 581, 32, 598,
- /* 700 */ 94, 598, 97, 598, 95, 627, 625, 329, 142, 50,
- /* 710 */ 47, 146, 333, 349, 358, 57, 58, 48, 579, 578,
- /* 720 */ 580, 580, 55, 55, 56, 56, 56, 56, 409, 54,
+ /* 660 */ 53, 52, 52, 52, 51, 233, 310, 570, 570, 261,
+ /* 670 */ 269, 598, 12, 374, 569, 166, 410, 314, 410, 421,
+ /* 680 */ 410, 474, 474, 366, 619, 50, 47, 146, 598, 595,
+ /* 690 */ 256, 413, 166, 413, 352, 413, 588, 582, 32, 599,
+ /* 700 */ 94, 599, 97, 599, 95, 628, 626, 330, 142, 50,
+ /* 710 */ 47, 146, 334, 350, 359, 57, 58, 48, 580, 579,
+ /* 720 */ 581, 581, 55, 55, 56, 56, 56, 56, 410, 54,
/* 730 */ 54, 54, 54, 53, 53, 52, 52, 52, 51, 233,
- /* 740 */ 309, 409, 388, 412, 409, 22, 565, 404, 212, 362,
- /* 750 */ 389, 598, 104, 359, 409, 156, 412, 409, 603, 412,
- /* 760 */ 537, 331, 569, 569, 598, 103, 493, 598, 105, 412,
- /* 770 */ 587, 581, 412, 260, 549, 618, 11, 598, 106, 521,
- /* 780 */ 598, 133, 169, 457, 456, 170, 35, 601, 618, 57,
- /* 790 */ 58, 48, 579, 578, 580, 580, 55, 55, 56, 56,
- /* 800 */ 56, 56, 409, 54, 54, 54, 54, 53, 53, 52,
- /* 810 */ 52, 52, 51, 233, 309, 409, 259, 412, 409, 50,
- /* 820 */ 47, 146, 357, 318, 355, 598, 134, 527, 352, 337,
- /* 830 */ 412, 409, 356, 412, 357, 409, 357, 618, 598, 98,
- /* 840 */ 129, 598, 102, 618, 587, 581, 412, 21, 235, 618,
- /* 850 */ 412, 618, 211, 143, 598, 101, 30, 167, 598, 93,
- /* 860 */ 350, 535, 203, 57, 58, 48, 579, 578, 580, 580,
- /* 870 */ 55, 55, 56, 56, 56, 56, 409, 54, 54, 54,
- /* 880 */ 54, 53, 53, 52, 52, 52, 51, 233, 309, 409,
- /* 890 */ 526, 412, 409, 425, 215, 305, 597, 551, 141, 598,
- /* 900 */ 100, 40, 409, 38, 412, 409, 550, 412, 409, 228,
- /* 910 */ 220, 314, 598, 77, 500, 598, 96, 412, 587, 581,
- /* 920 */ 412, 338, 253, 412, 218, 598, 137, 379, 598, 136,
- /* 930 */ 28, 598, 135, 270, 715, 210, 481, 57, 58, 48,
- /* 940 */ 579, 578, 580, 580, 55, 55, 56, 56, 56, 56,
- /* 950 */ 409, 54, 54, 54, 54, 53, 53, 52, 52, 52,
- /* 960 */ 51, 233, 309, 409, 272, 412, 409, 315, 147, 597,
- /* 970 */ 272, 626, 2, 598, 76, 209, 409, 127, 412, 618,
- /* 980 */ 126, 412, 409, 621, 235, 618, 598, 90, 374, 598,
- /* 990 */ 89, 412, 587, 581, 27, 260, 350, 412, 618, 598,
- /* 1000 */ 75, 321, 541, 541, 125, 598, 88, 320, 278, 597,
- /* 1010 */ 618, 57, 46, 48, 579, 578, 580, 580, 55, 55,
- /* 1020 */ 56, 56, 56, 56, 409, 54, 54, 54, 54, 53,
- /* 1030 */ 53, 52, 52, 52, 51, 233, 309, 409, 450, 412,
- /* 1040 */ 164, 284, 282, 272, 609, 424, 304, 598, 87, 370,
- /* 1050 */ 409, 477, 412, 409, 608, 409, 607, 602, 618, 618,
- /* 1060 */ 598, 99, 586, 585, 122, 412, 587, 581, 412, 618,
- /* 1070 */ 412, 618, 618, 598, 86, 366, 598, 17, 598, 85,
- /* 1080 */ 319, 185, 519, 518, 583, 582, 58, 48, 579, 578,
- /* 1090 */ 580, 580, 55, 55, 56, 56, 56, 56, 409, 54,
+ /* 740 */ 310, 410, 389, 413, 410, 22, 566, 405, 212, 363,
+ /* 750 */ 390, 599, 104, 360, 410, 156, 413, 410, 604, 413,
+ /* 760 */ 538, 332, 570, 570, 599, 103, 494, 599, 105, 413,
+ /* 770 */ 588, 582, 413, 261, 550, 619, 11, 599, 106, 522,
+ /* 780 */ 599, 133, 169, 458, 457, 170, 35, 602, 619, 57,
+ /* 790 */ 58, 48, 580, 579, 581, 581, 55, 55, 56, 56,
+ /* 800 */ 56, 56, 410, 54, 54, 54, 54, 53, 53, 52,
+ /* 810 */ 52, 52, 51, 233, 310, 410, 260, 413, 410, 50,
+ /* 820 */ 47, 146, 358, 319, 356, 599, 134, 528, 353, 338,
+ /* 830 */ 413, 410, 357, 413, 358, 410, 358, 619, 599, 98,
+ /* 840 */ 129, 599, 102, 619, 588, 582, 413, 21, 235, 619,
+ /* 850 */ 413, 619, 211, 143, 599, 101, 30, 167, 599, 93,
+ /* 860 */ 351, 536, 203, 57, 58, 48, 580, 579, 581, 581,
+ /* 870 */ 55, 55, 56, 56, 56, 56, 410, 54, 54, 54,
+ /* 880 */ 54, 53, 53, 52, 52, 52, 51, 233, 310, 410,
+ /* 890 */ 527, 413, 410, 426, 215, 306, 598, 552, 141, 599,
+ /* 900 */ 100, 40, 410, 38, 413, 410, 551, 413, 410, 228,
+ /* 910 */ 220, 315, 599, 77, 501, 599, 96, 413, 588, 582,
+ /* 920 */ 413, 339, 253, 413, 218, 599, 137, 380, 599, 136,
+ /* 930 */ 28, 599, 135, 271, 716, 210, 482, 57, 58, 48,
+ /* 940 */ 580, 579, 581, 581, 55, 55, 56, 56, 56, 56,
+ /* 950 */ 410, 54, 54, 54, 54, 53, 53, 52, 52, 52,
+ /* 960 */ 51, 233, 310, 410, 273, 413, 410, 316, 147, 598,
+ /* 970 */ 273, 627, 2, 599, 76, 209, 410, 127, 413, 619,
+ /* 980 */ 126, 413, 410, 622, 235, 619, 599, 90, 375, 599,
+ /* 990 */ 89, 413, 588, 582, 27, 261, 351, 413, 619, 599,
+ /* 1000 */ 75, 322, 542, 542, 125, 599, 88, 321, 279, 598,
+ /* 1010 */ 619, 57, 46, 48, 580, 579, 581, 581, 55, 55,
+ /* 1020 */ 56, 56, 56, 56, 410, 54, 54, 54, 54, 53,
+ /* 1030 */ 53, 52, 52, 52, 51, 233, 310, 410, 451, 413,
+ /* 1040 */ 164, 285, 283, 273, 610, 425, 305, 599, 87, 371,
+ /* 1050 */ 410, 478, 413, 410, 609, 410, 608, 603, 619, 619,
+ /* 1060 */ 599, 99, 587, 586, 122, 413, 588, 582, 413, 619,
+ /* 1070 */ 413, 619, 619, 599, 86, 367, 599, 17, 599, 85,
+ /* 1080 */ 320, 185, 520, 519, 584, 583, 58, 48, 580, 579,
+ /* 1090 */ 581, 581, 55, 55, 56, 56, 56, 56, 410, 54,
/* 1100 */ 54, 54, 54, 53, 53, 52, 52, 52, 51, 233,
- /* 1110 */ 309, 584, 409, 412, 409, 260, 260, 260, 408, 591,
- /* 1120 */ 474, 598, 84, 170, 409, 466, 518, 412, 121, 412,
- /* 1130 */ 618, 618, 618, 618, 618, 598, 83, 598, 72, 412,
- /* 1140 */ 587, 581, 51, 233, 625, 329, 470, 598, 71, 257,
- /* 1150 */ 159, 120, 14, 462, 157, 158, 117, 260, 448, 447,
- /* 1160 */ 446, 48, 579, 578, 580, 580, 55, 55, 56, 56,
- /* 1170 */ 56, 56, 618, 54, 54, 54, 54, 53, 53, 52,
- /* 1180 */ 52, 52, 51, 233, 44, 403, 260, 3, 409, 459,
- /* 1190 */ 260, 413, 619, 118, 398, 10, 25, 24, 554, 348,
- /* 1200 */ 217, 618, 406, 412, 409, 618, 4, 44, 403, 618,
- /* 1210 */ 3, 598, 82, 618, 413, 619, 455, 542, 115, 412,
- /* 1220 */ 538, 401, 536, 274, 506, 406, 251, 598, 81, 216,
- /* 1230 */ 273, 563, 618, 243, 453, 618, 154, 618, 618, 618,
- /* 1240 */ 449, 416, 623, 110, 401, 618, 409, 236, 64, 123,
- /* 1250 */ 487, 41, 42, 531, 563, 204, 409, 267, 43, 411,
- /* 1260 */ 410, 412, 265, 592, 108, 618, 107, 434, 332, 598,
- /* 1270 */ 80, 412, 618, 263, 41, 42, 443, 618, 409, 598,
- /* 1280 */ 70, 43, 411, 410, 433, 261, 592, 149, 618, 597,
- /* 1290 */ 256, 237, 188, 412, 590, 590, 590, 589, 588, 13,
- /* 1300 */ 618, 598, 18, 328, 235, 618, 44, 403, 360, 3,
- /* 1310 */ 418, 461, 339, 413, 619, 227, 124, 590, 590, 590,
- /* 1320 */ 589, 588, 13, 618, 406, 409, 618, 409, 139, 34,
- /* 1330 */ 403, 387, 3, 148, 622, 312, 413, 619, 311, 330,
- /* 1340 */ 412, 460, 412, 401, 180, 353, 412, 406, 598, 79,
- /* 1350 */ 598, 78, 250, 563, 598, 9, 618, 612, 611, 610,
- /* 1360 */ 618, 8, 452, 442, 242, 415, 401, 618, 239, 235,
- /* 1370 */ 179, 238, 428, 41, 42, 288, 563, 618, 618, 618,
- /* 1380 */ 43, 411, 410, 618, 144, 592, 618, 618, 177, 61,
- /* 1390 */ 618, 596, 391, 620, 619, 287, 41, 42, 414, 618,
- /* 1400 */ 293, 30, 393, 43, 411, 410, 292, 618, 592, 31,
- /* 1410 */ 618, 395, 291, 60, 230, 37, 590, 590, 590, 589,
- /* 1420 */ 588, 13, 214, 553, 183, 290, 172, 301, 300, 299,
- /* 1430 */ 178, 297, 595, 563, 451, 29, 285, 390, 540, 590,
- /* 1440 */ 590, 590, 589, 588, 13, 283, 520, 534, 150, 533,
- /* 1450 */ 241, 281, 384, 192, 191, 324, 515, 514, 276, 240,
- /* 1460 */ 510, 523, 307, 511, 128, 592, 509, 225, 226, 486,
- /* 1470 */ 485, 224, 152, 491, 464, 306, 484, 163, 153, 371,
- /* 1480 */ 478, 151, 162, 258, 369, 161, 367, 208, 475, 476,
- /* 1490 */ 26, 160, 465, 140, 361, 131, 590, 590, 590, 116,
- /* 1500 */ 119, 454, 343, 155, 114, 342, 113, 112, 445, 111,
- /* 1510 */ 130, 109, 431, 316, 426, 430, 23, 429, 20, 606,
- /* 1520 */ 190, 507, 255, 341, 244, 63, 294, 593, 310, 570,
- /* 1530 */ 277, 402, 354, 235, 567, 496, 495, 492, 494, 302,
- /* 1540 */ 458, 378, 286, 245, 566, 5, 252, 547, 193, 444,
- /* 1550 */ 233, 340, 207, 524, 368, 505, 334, 522, 499, 399,
- /* 1560 */ 295, 498, 956, 488,
+ /* 1110 */ 310, 585, 410, 413, 410, 261, 261, 261, 409, 592,
+ /* 1120 */ 475, 599, 84, 170, 410, 467, 519, 413, 121, 413,
+ /* 1130 */ 619, 619, 619, 619, 619, 599, 83, 599, 72, 413,
+ /* 1140 */ 588, 582, 51, 233, 626, 330, 471, 599, 71, 258,
+ /* 1150 */ 159, 120, 14, 463, 157, 158, 117, 261, 449, 448,
+ /* 1160 */ 447, 48, 580, 579, 581, 581, 55, 55, 56, 56,
+ /* 1170 */ 56, 56, 619, 54, 54, 54, 54, 53, 53, 52,
+ /* 1180 */ 52, 52, 51, 233, 44, 404, 261, 3, 410, 460,
+ /* 1190 */ 261, 414, 620, 118, 399, 10, 25, 24, 555, 349,
+ /* 1200 */ 217, 619, 407, 413, 410, 619, 4, 44, 404, 619,
+ /* 1210 */ 3, 599, 82, 619, 414, 620, 456, 543, 115, 413,
+ /* 1220 */ 539, 402, 537, 275, 507, 407, 251, 599, 81, 216,
+ /* 1230 */ 274, 564, 619, 243, 454, 619, 154, 619, 619, 619,
+ /* 1240 */ 450, 417, 624, 110, 402, 619, 410, 236, 64, 123,
+ /* 1250 */ 488, 41, 42, 532, 564, 204, 410, 268, 43, 412,
+ /* 1260 */ 411, 413, 266, 593, 108, 619, 107, 435, 333, 599,
+ /* 1270 */ 80, 413, 619, 264, 41, 42, 444, 619, 410, 599,
+ /* 1280 */ 70, 43, 412, 411, 434, 262, 593, 149, 619, 598,
+ /* 1290 */ 257, 237, 188, 413, 591, 591, 591, 590, 589, 13,
+ /* 1300 */ 619, 599, 18, 329, 235, 619, 44, 404, 361, 3,
+ /* 1310 */ 419, 462, 340, 414, 620, 227, 124, 591, 591, 591,
+ /* 1320 */ 590, 589, 13, 619, 407, 410, 619, 410, 139, 34,
+ /* 1330 */ 404, 388, 3, 148, 623, 313, 414, 620, 312, 331,
+ /* 1340 */ 413, 461, 413, 402, 180, 354, 413, 407, 599, 79,
+ /* 1350 */ 599, 78, 250, 564, 599, 9, 619, 613, 612, 611,
+ /* 1360 */ 619, 8, 453, 443, 242, 416, 402, 619, 239, 235,
+ /* 1370 */ 179, 238, 429, 41, 42, 289, 564, 619, 619, 619,
+ /* 1380 */ 43, 412, 411, 619, 144, 593, 619, 619, 177, 61,
+ /* 1390 */ 619, 597, 392, 621, 620, 288, 41, 42, 415, 619,
+ /* 1400 */ 294, 30, 394, 43, 412, 411, 293, 619, 593, 31,
+ /* 1410 */ 619, 396, 292, 60, 230, 37, 591, 591, 591, 590,
+ /* 1420 */ 589, 13, 214, 554, 183, 291, 172, 302, 301, 300,
+ /* 1430 */ 178, 298, 596, 564, 452, 29, 286, 391, 541, 591,
+ /* 1440 */ 591, 591, 590, 589, 13, 284, 521, 535, 150, 534,
+ /* 1450 */ 241, 282, 385, 192, 191, 325, 516, 515, 277, 240,
+ /* 1460 */ 511, 524, 308, 512, 128, 593, 510, 225, 226, 487,
+ /* 1470 */ 486, 224, 152, 492, 465, 307, 485, 163, 153, 372,
+ /* 1480 */ 479, 151, 162, 259, 370, 161, 368, 208, 476, 477,
+ /* 1490 */ 26, 160, 469, 466, 362, 140, 591, 591, 591, 116,
+ /* 1500 */ 119, 455, 344, 155, 114, 343, 113, 112, 446, 111,
+ /* 1510 */ 131, 109, 432, 317, 130, 431, 23, 20, 430, 427,
+ /* 1520 */ 190, 63, 255, 342, 244, 607, 295, 287, 311, 594,
+ /* 1530 */ 278, 508, 496, 235, 493, 571, 497, 568, 495, 403,
+ /* 1540 */ 459, 379, 355, 245, 193, 303, 567, 296, 341, 5,
+ /* 1550 */ 445, 548, 506, 207, 525, 500, 335, 489, 252, 369,
+ /* 1560 */ 400, 499, 523, 233,
};
static const YYCODETYPE yy_lookahead[] = {
/* 0 */ 19, 142, 143, 144, 145, 24, 1, 26, 77, 78,
@@ -110725,17 +113405,17 @@ static const YYCODETYPE yy_lookahead[] = {
/* 1460 */ 103, 94, 178, 177, 22, 98, 175, 92, 228, 175,
/* 1470 */ 175, 228, 55, 183, 57, 178, 175, 156, 61, 18,
/* 1480 */ 157, 64, 156, 235, 157, 156, 45, 157, 236, 157,
- /* 1490 */ 135, 156, 189, 68, 157, 218, 129, 130, 131, 22,
+ /* 1490 */ 135, 156, 199, 189, 157, 68, 129, 130, 131, 22,
/* 1500 */ 189, 199, 157, 156, 192, 18, 192, 192, 199, 192,
- /* 1510 */ 218, 189, 40, 157, 38, 157, 240, 157, 240, 153,
- /* 1520 */ 196, 181, 105, 106, 107, 243, 198, 166, 111, 230,
- /* 1530 */ 176, 226, 239, 116, 230, 176, 166, 166, 176, 148,
- /* 1540 */ 199, 177, 209, 209, 166, 196, 239, 208, 185, 199,
- /* 1550 */ 92, 209, 233, 173, 234, 182, 139, 173, 182, 191,
- /* 1560 */ 195, 182, 250, 186,
+ /* 1510 */ 218, 189, 40, 157, 218, 157, 240, 240, 157, 38,
+ /* 1520 */ 196, 243, 105, 106, 107, 153, 198, 209, 111, 166,
+ /* 1530 */ 176, 181, 166, 116, 166, 230, 176, 230, 176, 226,
+ /* 1540 */ 199, 177, 239, 209, 185, 148, 166, 195, 209, 196,
+ /* 1550 */ 199, 208, 182, 233, 173, 182, 139, 186, 239, 234,
+ /* 1560 */ 191, 182, 173, 92,
};
#define YY_SHIFT_USE_DFLT (-70)
-#define YY_SHIFT_COUNT (416)
+#define YY_SHIFT_COUNT (417)
#define YY_SHIFT_MIN (-69)
#define YY_SHIFT_MAX (1487)
static const short yy_shift_ofst[] = {
@@ -110752,7 +113432,7 @@ static const short yy_shift_ofst[] = {
/* 100 */ -45, -45, -45, -45, -1, 24, 245, 362, 362, 362,
/* 110 */ 362, 362, 362, 362, 362, 362, 362, 362, 362, 362,
/* 120 */ 362, 362, 362, 388, 356, 362, 362, 362, 362, 362,
- /* 130 */ 732, 868, 231, 1051, 1458, -70, -70, -70, 1367, 57,
+ /* 130 */ 732, 868, 231, 1051, 1471, -70, -70, -70, 1367, 57,
/* 140 */ 434, 434, 289, 291, 285, 1, 204, 572, 539, 362,
/* 150 */ 362, 362, 362, 362, 362, 362, 362, 362, 362, 362,
/* 160 */ 362, 362, 362, 362, 362, 362, 362, 362, 362, 362,
@@ -110762,30 +113442,30 @@ static const short yy_shift_ofst[] = {
/* 200 */ 422, 358, 335, -12, -12, -12, -12, 576, 294, -12,
/* 210 */ -12, 295, 595, 141, 600, 730, 723, 723, 805, 730,
/* 220 */ 805, 439, 911, 231, 865, 231, 865, 807, 865, 723,
- /* 230 */ 766, 633, 633, 231, 284, 63, 608, 1476, 1308, 1308,
- /* 240 */ 1472, 1472, 1308, 1477, 1425, 1275, 1487, 1487, 1487, 1487,
- /* 250 */ 1308, 1461, 1275, 1477, 1425, 1425, 1308, 1461, 1355, 1441,
- /* 260 */ 1308, 1308, 1461, 1308, 1461, 1308, 1461, 1442, 1348, 1348,
- /* 270 */ 1348, 1408, 1375, 1375, 1442, 1348, 1357, 1348, 1408, 1348,
- /* 280 */ 1348, 1316, 1331, 1316, 1331, 1316, 1331, 1308, 1308, 1280,
- /* 290 */ 1288, 1289, 1285, 1279, 1275, 1253, 1336, 1346, 1346, 1338,
- /* 300 */ 1338, 1338, 1338, -70, -70, -70, -70, -70, -70, 1013,
- /* 310 */ 467, 612, 84, 179, -28, 870, 410, 761, 760, 667,
- /* 320 */ 650, 531, 220, 361, 331, 125, 127, 97, 1306, 1300,
- /* 330 */ 1270, 1151, 1272, 1203, 1232, 1261, 1244, 1148, 1174, 1139,
- /* 340 */ 1156, 1124, 1220, 1115, 1210, 1233, 1099, 1193, 1184, 1174,
- /* 350 */ 1173, 1029, 1121, 1120, 1085, 1162, 1119, 1037, 1152, 1147,
- /* 360 */ 1129, 1046, 1011, 1093, 1098, 1075, 1061, 1032, 960, 1057,
- /* 370 */ 1031, 1030, 899, 938, 982, 936, 972, 958, 910, 955,
- /* 380 */ 875, 885, 908, 857, 859, 867, 804, 590, 834, 747,
- /* 390 */ 818, 513, 611, 741, 673, 637, 611, 606, 603, 579,
- /* 400 */ 501, 541, 468, 386, 445, 395, 376, 281, 185, 120,
- /* 410 */ 92, 75, 45, 114, 25, 11, 5,
+ /* 230 */ 766, 633, 633, 231, 284, 63, 608, 1481, 1308, 1308,
+ /* 240 */ 1472, 1472, 1308, 1477, 1427, 1275, 1487, 1487, 1487, 1487,
+ /* 250 */ 1308, 1461, 1275, 1477, 1427, 1427, 1275, 1308, 1461, 1355,
+ /* 260 */ 1441, 1308, 1308, 1461, 1308, 1461, 1308, 1461, 1442, 1348,
+ /* 270 */ 1348, 1348, 1408, 1375, 1375, 1442, 1348, 1357, 1348, 1408,
+ /* 280 */ 1348, 1348, 1316, 1331, 1316, 1331, 1316, 1331, 1308, 1308,
+ /* 290 */ 1280, 1288, 1289, 1285, 1279, 1275, 1253, 1336, 1346, 1346,
+ /* 300 */ 1338, 1338, 1338, 1338, -70, -70, -70, -70, -70, -70,
+ /* 310 */ 1013, 467, 612, 84, 179, -28, 870, 410, 761, 760,
+ /* 320 */ 667, 650, 531, 220, 361, 331, 125, 127, 97, 1306,
+ /* 330 */ 1300, 1270, 1151, 1272, 1203, 1232, 1261, 1244, 1148, 1174,
+ /* 340 */ 1139, 1156, 1124, 1220, 1115, 1210, 1233, 1099, 1193, 1184,
+ /* 350 */ 1174, 1173, 1029, 1121, 1120, 1085, 1162, 1119, 1037, 1152,
+ /* 360 */ 1147, 1129, 1046, 1011, 1093, 1098, 1075, 1061, 1032, 960,
+ /* 370 */ 1057, 1031, 1030, 899, 938, 982, 936, 972, 958, 910,
+ /* 380 */ 955, 875, 885, 908, 857, 859, 867, 804, 590, 834,
+ /* 390 */ 747, 818, 513, 611, 741, 673, 637, 611, 606, 603,
+ /* 400 */ 579, 501, 541, 468, 386, 445, 395, 376, 281, 185,
+ /* 410 */ 120, 92, 75, 45, 114, 25, 11, 5,
};
#define YY_REDUCE_USE_DFLT (-169)
-#define YY_REDUCE_COUNT (308)
+#define YY_REDUCE_COUNT (309)
#define YY_REDUCE_MIN (-168)
-#define YY_REDUCE_MAX (1391)
+#define YY_REDUCE_MAX (1397)
static const short yy_reduce_ofst[] = {
/* 0 */ -141, 90, 1095, 222, 158, 156, 19, 17, 10, -104,
/* 10 */ 378, 316, 311, 12, 180, 249, 598, 464, 397, 1181,
@@ -110806,83 +113486,83 @@ static const short yy_reduce_ofst[] = {
/* 160 */ 1140, 1135, 1123, 1112, 1107, 1100, 1080, 1074, 1073, 1072,
/* 170 */ 1070, 1067, 1048, 1044, 969, 968, 907, 906, 904, 894,
/* 180 */ 833, 837, 836, 340, 827, 815, 775, 68, 722, 646,
- /* 190 */ -168, 1384, 1380, 1377, 1379, 1376, 1373, 1339, 1365, 1368,
- /* 200 */ 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1320, 1319, 1365,
- /* 210 */ 1365, 1339, 1378, 1349, 1391, 1350, 1342, 1334, 1307, 1341,
- /* 220 */ 1293, 1364, 1363, 1371, 1362, 1370, 1359, 1340, 1354, 1333,
- /* 230 */ 1305, 1304, 1299, 1361, 1328, 1324, 1366, 1282, 1360, 1358,
- /* 240 */ 1278, 1276, 1356, 1292, 1322, 1309, 1317, 1315, 1314, 1312,
- /* 250 */ 1345, 1347, 1302, 1277, 1311, 1303, 1337, 1335, 1252, 1248,
- /* 260 */ 1332, 1330, 1329, 1327, 1326, 1323, 1321, 1297, 1301, 1295,
- /* 270 */ 1294, 1290, 1243, 1240, 1284, 1291, 1286, 1283, 1274, 1281,
- /* 280 */ 1271, 1238, 1241, 1236, 1235, 1227, 1226, 1267, 1266, 1189,
- /* 290 */ 1229, 1223, 1211, 1206, 1201, 1197, 1239, 1237, 1219, 1216,
- /* 300 */ 1209, 1208, 1185, 1089, 1086, 1087, 1137, 1136, 1164,
+ /* 190 */ -168, 1389, 1381, 1371, 1379, 1373, 1370, 1343, 1352, 1369,
+ /* 200 */ 1352, 1352, 1352, 1352, 1352, 1352, 1352, 1325, 1320, 1352,
+ /* 210 */ 1352, 1343, 1380, 1353, 1397, 1351, 1339, 1334, 1319, 1341,
+ /* 220 */ 1303, 1364, 1359, 1368, 1362, 1366, 1360, 1350, 1354, 1318,
+ /* 230 */ 1313, 1307, 1305, 1363, 1328, 1324, 1372, 1278, 1361, 1358,
+ /* 240 */ 1277, 1276, 1356, 1296, 1322, 1309, 1317, 1315, 1314, 1312,
+ /* 250 */ 1345, 1347, 1302, 1292, 1311, 1304, 1293, 1337, 1335, 1252,
+ /* 260 */ 1248, 1332, 1330, 1329, 1327, 1326, 1323, 1321, 1297, 1301,
+ /* 270 */ 1295, 1294, 1290, 1243, 1240, 1284, 1291, 1286, 1283, 1274,
+ /* 280 */ 1281, 1271, 1238, 1241, 1236, 1235, 1227, 1226, 1267, 1266,
+ /* 290 */ 1189, 1229, 1223, 1211, 1206, 1201, 1197, 1239, 1237, 1219,
+ /* 300 */ 1216, 1209, 1208, 1185, 1089, 1086, 1087, 1137, 1136, 1164,
};
static const YYACTIONTYPE yy_default[] = {
- /* 0 */ 632, 866, 954, 954, 866, 866, 954, 954, 954, 756,
- /* 10 */ 954, 954, 954, 864, 954, 954, 784, 784, 928, 954,
- /* 20 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 954,
- /* 30 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 954,
- /* 40 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 954,
- /* 50 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 954,
- /* 60 */ 954, 954, 954, 954, 954, 954, 954, 671, 760, 790,
- /* 70 */ 954, 954, 954, 954, 954, 954, 954, 954, 927, 929,
- /* 80 */ 798, 797, 907, 771, 795, 788, 792, 867, 860, 861,
- /* 90 */ 859, 863, 868, 954, 791, 827, 844, 826, 838, 843,
- /* 100 */ 850, 842, 839, 829, 828, 830, 831, 954, 954, 954,
- /* 110 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 954,
- /* 120 */ 954, 954, 954, 658, 725, 954, 954, 954, 954, 954,
- /* 130 */ 954, 954, 954, 832, 833, 847, 846, 845, 954, 663,
- /* 140 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 954,
- /* 150 */ 934, 932, 954, 879, 954, 954, 954, 954, 954, 954,
- /* 160 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 954,
- /* 170 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 954,
- /* 180 */ 638, 756, 756, 756, 632, 954, 954, 954, 946, 760,
- /* 190 */ 750, 954, 954, 954, 954, 954, 954, 954, 954, 954,
- /* 200 */ 954, 954, 954, 800, 739, 917, 919, 954, 900, 737,
- /* 210 */ 660, 758, 673, 748, 640, 794, 773, 773, 912, 794,
- /* 220 */ 912, 696, 719, 954, 784, 954, 784, 693, 784, 773,
- /* 230 */ 862, 954, 954, 954, 757, 748, 954, 939, 764, 764,
- /* 240 */ 931, 931, 764, 806, 729, 794, 736, 736, 736, 736,
- /* 250 */ 764, 655, 794, 806, 729, 729, 764, 655, 906, 904,
- /* 260 */ 764, 764, 655, 764, 655, 764, 655, 872, 727, 727,
- /* 270 */ 727, 711, 876, 876, 872, 727, 696, 727, 711, 727,
- /* 280 */ 727, 777, 772, 777, 772, 777, 772, 764, 764, 954,
- /* 290 */ 789, 778, 787, 785, 794, 954, 714, 648, 648, 637,
- /* 300 */ 637, 637, 637, 951, 951, 946, 698, 698, 681, 954,
- /* 310 */ 954, 954, 954, 954, 954, 954, 881, 954, 954, 954,
- /* 320 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 633,
- /* 330 */ 941, 954, 954, 938, 954, 954, 954, 954, 799, 954,
- /* 340 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 916,
- /* 350 */ 954, 954, 954, 954, 954, 954, 954, 910, 954, 954,
- /* 360 */ 954, 954, 954, 954, 903, 902, 954, 954, 954, 954,
- /* 370 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 954,
- /* 380 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 954,
- /* 390 */ 954, 954, 786, 954, 779, 954, 865, 954, 954, 954,
- /* 400 */ 954, 954, 954, 954, 954, 954, 954, 742, 815, 954,
- /* 410 */ 814, 818, 813, 665, 954, 646, 954, 629, 634, 950,
- /* 420 */ 953, 952, 949, 948, 947, 942, 940, 937, 936, 935,
- /* 430 */ 933, 930, 926, 885, 883, 890, 889, 888, 887, 886,
- /* 440 */ 884, 882, 880, 801, 796, 793, 925, 878, 738, 735,
- /* 450 */ 734, 654, 943, 909, 918, 805, 804, 807, 915, 914,
- /* 460 */ 913, 911, 908, 895, 803, 802, 730, 870, 869, 657,
- /* 470 */ 899, 898, 897, 901, 905, 896, 766, 656, 653, 662,
- /* 480 */ 717, 718, 726, 724, 723, 722, 721, 720, 716, 664,
- /* 490 */ 672, 710, 695, 694, 875, 877, 874, 873, 703, 702,
- /* 500 */ 708, 707, 706, 705, 704, 701, 700, 699, 692, 691,
- /* 510 */ 697, 690, 713, 712, 709, 689, 733, 732, 731, 728,
- /* 520 */ 688, 687, 686, 818, 685, 684, 824, 823, 811, 854,
- /* 530 */ 753, 752, 751, 763, 762, 775, 774, 809, 808, 776,
- /* 540 */ 761, 755, 754, 770, 769, 768, 767, 759, 749, 781,
- /* 550 */ 783, 782, 780, 856, 765, 853, 924, 923, 922, 921,
- /* 560 */ 920, 858, 857, 825, 822, 676, 677, 893, 892, 894,
- /* 570 */ 891, 679, 678, 675, 674, 855, 744, 743, 851, 848,
- /* 580 */ 840, 836, 852, 849, 841, 837, 835, 834, 820, 819,
- /* 590 */ 817, 816, 812, 821, 667, 745, 741, 740, 810, 747,
- /* 600 */ 746, 683, 682, 680, 661, 659, 652, 650, 649, 651,
- /* 610 */ 647, 645, 644, 643, 642, 641, 670, 669, 668, 666,
- /* 620 */ 665, 639, 636, 635, 631, 630, 628,
+ /* 0 */ 633, 867, 955, 955, 867, 867, 955, 955, 955, 757,
+ /* 10 */ 955, 955, 955, 865, 955, 955, 785, 785, 929, 955,
+ /* 20 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955,
+ /* 30 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955,
+ /* 40 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955,
+ /* 50 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955,
+ /* 60 */ 955, 955, 955, 955, 955, 955, 955, 672, 761, 791,
+ /* 70 */ 955, 955, 955, 955, 955, 955, 955, 955, 928, 930,
+ /* 80 */ 799, 798, 908, 772, 796, 789, 793, 868, 861, 862,
+ /* 90 */ 860, 864, 869, 955, 792, 828, 845, 827, 839, 844,
+ /* 100 */ 851, 843, 840, 830, 829, 831, 832, 955, 955, 955,
+ /* 110 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955,
+ /* 120 */ 955, 955, 955, 659, 726, 955, 955, 955, 955, 955,
+ /* 130 */ 955, 955, 955, 833, 834, 848, 847, 846, 955, 664,
+ /* 140 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955,
+ /* 150 */ 935, 933, 955, 880, 955, 955, 955, 955, 955, 955,
+ /* 160 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955,
+ /* 170 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955,
+ /* 180 */ 639, 757, 757, 757, 633, 955, 955, 955, 947, 761,
+ /* 190 */ 751, 955, 955, 955, 955, 955, 955, 955, 955, 955,
+ /* 200 */ 955, 955, 955, 801, 740, 918, 920, 955, 901, 738,
+ /* 210 */ 661, 759, 674, 749, 641, 795, 774, 774, 913, 795,
+ /* 220 */ 913, 697, 720, 955, 785, 955, 785, 694, 785, 774,
+ /* 230 */ 863, 955, 955, 955, 758, 749, 955, 940, 765, 765,
+ /* 240 */ 932, 932, 765, 807, 730, 795, 737, 737, 737, 737,
+ /* 250 */ 765, 656, 795, 807, 730, 730, 795, 765, 656, 907,
+ /* 260 */ 905, 765, 765, 656, 765, 656, 765, 656, 873, 728,
+ /* 270 */ 728, 728, 712, 877, 877, 873, 728, 697, 728, 712,
+ /* 280 */ 728, 728, 778, 773, 778, 773, 778, 773, 765, 765,
+ /* 290 */ 955, 790, 779, 788, 786, 795, 955, 715, 649, 649,
+ /* 300 */ 638, 638, 638, 638, 952, 952, 947, 699, 699, 682,
+ /* 310 */ 955, 955, 955, 955, 955, 955, 955, 882, 955, 955,
+ /* 320 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955,
+ /* 330 */ 634, 942, 955, 955, 939, 955, 955, 955, 955, 800,
+ /* 340 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955,
+ /* 350 */ 917, 955, 955, 955, 955, 955, 955, 955, 911, 955,
+ /* 360 */ 955, 955, 955, 955, 955, 904, 903, 955, 955, 955,
+ /* 370 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955,
+ /* 380 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955,
+ /* 390 */ 955, 955, 955, 787, 955, 780, 955, 866, 955, 955,
+ /* 400 */ 955, 955, 955, 955, 955, 955, 955, 955, 743, 816,
+ /* 410 */ 955, 815, 819, 814, 666, 955, 647, 955, 630, 635,
+ /* 420 */ 951, 954, 953, 950, 949, 948, 943, 941, 938, 937,
+ /* 430 */ 936, 934, 931, 927, 886, 884, 891, 890, 889, 888,
+ /* 440 */ 887, 885, 883, 881, 802, 797, 794, 926, 879, 739,
+ /* 450 */ 736, 735, 655, 944, 910, 919, 806, 805, 808, 916,
+ /* 460 */ 915, 914, 912, 909, 896, 804, 803, 731, 871, 870,
+ /* 470 */ 658, 900, 899, 898, 902, 906, 897, 767, 657, 654,
+ /* 480 */ 663, 718, 719, 727, 725, 724, 723, 722, 721, 717,
+ /* 490 */ 665, 673, 711, 696, 695, 876, 878, 875, 874, 704,
+ /* 500 */ 703, 709, 708, 707, 706, 705, 702, 701, 700, 693,
+ /* 510 */ 692, 698, 691, 714, 713, 710, 690, 734, 733, 732,
+ /* 520 */ 729, 689, 688, 687, 819, 686, 685, 825, 824, 812,
+ /* 530 */ 855, 754, 753, 752, 764, 763, 776, 775, 810, 809,
+ /* 540 */ 777, 762, 756, 755, 771, 770, 769, 768, 760, 750,
+ /* 550 */ 782, 784, 783, 781, 857, 766, 854, 925, 924, 923,
+ /* 560 */ 922, 921, 859, 858, 826, 823, 677, 678, 894, 893,
+ /* 570 */ 895, 892, 680, 679, 676, 675, 856, 745, 744, 852,
+ /* 580 */ 849, 841, 837, 853, 850, 842, 838, 836, 835, 821,
+ /* 590 */ 820, 818, 817, 813, 822, 668, 746, 742, 741, 811,
+ /* 600 */ 748, 747, 684, 683, 681, 662, 660, 653, 651, 650,
+ /* 610 */ 652, 648, 646, 645, 644, 643, 642, 671, 670, 669,
+ /* 620 */ 667, 666, 640, 637, 636, 632, 631, 629,
};
/* The next table maps tokens into fallback tokens. If a construct
@@ -111354,7 +114034,7 @@ static const char *const yyRuleName[] = {
/* 239 */ "exprlist ::=",
/* 240 */ "nexprlist ::= nexprlist COMMA expr",
/* 241 */ "nexprlist ::= expr",
- /* 242 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP",
+ /* 242 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP where_opt",
/* 243 */ "uniqueflag ::= UNIQUE",
/* 244 */ "uniqueflag ::=",
/* 245 */ "idxlist_opt ::=",
@@ -112073,7 +114753,7 @@ static const struct {
{ 220, 0 },
{ 215, 3 },
{ 215, 1 },
- { 147, 11 },
+ { 147, 12 },
{ 227, 1 },
{ 227, 0 },
{ 178, 0 },
@@ -112515,6 +115195,7 @@ static void yy_reduce(
if( yymsp[0].minor.yy159 ){
yymsp[0].minor.yy159->op = (u8)yymsp[-1].minor.yy392;
yymsp[0].minor.yy159->pPrior = yymsp[-2].minor.yy159;
+ if( yymsp[-1].minor.yy392!=TK_ALL ) pParse->hasCompound = 1;
}else{
sqlite3SelectDelete(pParse->db, yymsp[-2].minor.yy159);
}
@@ -113048,12 +115729,13 @@ static void yy_reduce(
break;
case 231: /* expr ::= CASE case_operand case_exprlist case_else END */
{
- yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy122, yymsp[-1].minor.yy122, 0);
+ yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy122, 0, 0);
if( yygotominor.yy342.pExpr ){
- yygotominor.yy342.pExpr->x.pList = yymsp[-2].minor.yy442;
+ yygotominor.yy342.pExpr->x.pList = yymsp[-1].minor.yy122 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy442,yymsp[-1].minor.yy122) : yymsp[-2].minor.yy442;
sqlite3ExprSetHeight(pParse, yygotominor.yy342.pExpr);
}else{
sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy442);
+ sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy122);
}
yygotominor.yy342.zStart = yymsp[-4].minor.yy0.z;
yygotominor.yy342.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
@@ -113077,11 +115759,11 @@ static void yy_reduce(
case 241: /* nexprlist ::= expr */
{yygotominor.yy442 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy342.pExpr);}
break;
- case 242: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP */
+ case 242: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP where_opt */
{
- sqlite3CreateIndex(pParse, &yymsp[-6].minor.yy0, &yymsp[-5].minor.yy0,
- sqlite3SrcListAppend(pParse->db,0,&yymsp[-3].minor.yy0,0), yymsp[-1].minor.yy442, yymsp[-9].minor.yy392,
- &yymsp[-10].minor.yy0, &yymsp[0].minor.yy0, SQLITE_SO_ASC, yymsp[-7].minor.yy392);
+ sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0,
+ sqlite3SrcListAppend(pParse->db,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy442, yymsp[-10].minor.yy392,
+ &yymsp[-11].minor.yy0, yymsp[0].minor.yy122, SQLITE_SO_ASC, yymsp[-8].minor.yy392);
}
break;
case 243: /* uniqueflag ::= UNIQUE */
@@ -114007,7 +116689,6 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
}
case '-': {
if( z[1]=='-' ){
- /* IMP: R-50417-27976 -- syntax diagram for comments */
for(i=2; (c=z[i])!=0 && c!='\n'; i++){}
*tokenType = TK_SPACE; /* IMP: R-22934-25134 */
return i;
@@ -114040,7 +116721,6 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
*tokenType = TK_SLASH;
return 1;
}
- /* IMP: R-50417-27976 -- syntax diagram for comments */
for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){}
if( c ) i++;
*tokenType = TK_SPACE; /* IMP: R-22934-25134 */
@@ -114280,7 +116960,7 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr
mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
- if( db->activeVdbeCnt==0 ){
+ if( db->nVdbeActive==0 ){
db->u1.isInterrupted = 0;
}
pParse->rc = SQLITE_OK;
@@ -114902,6 +117582,9 @@ SQLITE_API char *sqlite3_data_directory = 0;
SQLITE_API int sqlite3_initialize(void){
MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */
int rc; /* Result code */
+#ifdef SQLITE_EXTRA_INIT
+ int bRunExtraInit = 0; /* Extra initialization needed */
+#endif
#ifdef SQLITE_OMIT_WSD
rc = sqlite3_wsd_init(4096, 24);
@@ -114999,6 +117682,9 @@ SQLITE_API int sqlite3_initialize(void){
sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage,
sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage);
sqlite3GlobalConfig.isInit = 1;
+#ifdef SQLITE_EXTRA_INIT
+ bRunExtraInit = 1;
+#endif
}
sqlite3GlobalConfig.inProgress = 0;
}
@@ -115039,7 +117725,7 @@ SQLITE_API int sqlite3_initialize(void){
** compile-time option.
*/
#ifdef SQLITE_EXTRA_INIT
- if( rc==SQLITE_OK && sqlite3GlobalConfig.isInit ){
+ if( bRunExtraInit ){
int SQLITE_EXTRA_INIT(const char*);
rc = SQLITE_EXTRA_INIT(0);
}
@@ -115227,8 +117913,8 @@ SQLITE_API int sqlite3_config(int op, ...){
memset(&sqlite3GlobalConfig.m, 0, sizeof(sqlite3GlobalConfig.m));
}else{
/* The heap pointer is not NULL, then install one of the
- ** mem5.c/mem3.c methods. If neither ENABLE_MEMSYS3 nor
- ** ENABLE_MEMSYS5 is defined, return an error.
+ ** mem5.c/mem3.c methods. The enclosing #if guarantees at
+ ** least one of these methods is currently enabled.
*/
#ifdef SQLITE_ENABLE_MEMSYS3
sqlite3GlobalConfig.m = *sqlite3MemGetMemsys3();
@@ -115247,7 +117933,7 @@ SQLITE_API int sqlite3_config(int op, ...){
break;
}
- /* Record a pointer to the logger funcction and its first argument.
+ /* Record a pointer to the logger function and its first argument.
** The default is NULL. Logging is disabled if the function pointer is
** NULL.
*/
@@ -115486,7 +118172,7 @@ static int binCollFunc(
/*
** Another built-in collating sequence: NOCASE.
**
-** This collating sequence is intended to be used for "case independant
+** This collating sequence is intended to be used for "case independent
** comparison". SQLite's knowledge of upper and lower case equivalents
** extends only to the 26 characters used in the English language.
**
@@ -115809,7 +118495,6 @@ SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db, int tripCode){
inTrans = 1;
}
sqlite3BtreeRollback(p, tripCode);
- db->aDb[i].inTrans = 0;
}
}
sqlite3VtabRollback(db);
@@ -115823,6 +118508,8 @@ SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db, int tripCode){
/* Any deferred constraint violations have now been resolved. */
db->nDeferredCons = 0;
+ db->nDeferredImmCons = 0;
+ db->flags &= ~SQLITE_DeferFKs;
/* If one has been configured, invoke the rollback-hook callback */
if( db->xRollbackCallback && (inTrans || !db->autoCommit) ){
@@ -115849,6 +118536,7 @@ SQLITE_PRIVATE const char *sqlite3ErrName(int rc){
case SQLITE_ABORT_ROLLBACK: zName = "SQLITE_ABORT_ROLLBACK"; break;
case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
case SQLITE_BUSY_RECOVERY: zName = "SQLITE_BUSY_RECOVERY"; break;
+ case SQLITE_BUSY_SNAPSHOT: zName = "SQLITE_BUSY_SNAPSHOT"; break;
case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break;
case SQLITE_LOCKED_SHAREDCACHE: zName = "SQLITE_LOCKED_SHAREDCACHE";break;
case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break;
@@ -115883,6 +118571,8 @@ SQLITE_PRIVATE const char *sqlite3ErrName(int rc){
case SQLITE_IOERR_SEEK: zName = "SQLITE_IOERR_SEEK"; break;
case SQLITE_IOERR_DELETE_NOENT: zName = "SQLITE_IOERR_DELETE_NOENT";break;
case SQLITE_IOERR_MMAP: zName = "SQLITE_IOERR_MMAP"; break;
+ case SQLITE_IOERR_GETTEMPPATH: zName = "SQLITE_IOERR_GETTEMPPATH"; break;
+ case SQLITE_IOERR_CONVPATH: zName = "SQLITE_IOERR_CONVPATH"; break;
case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break;
case SQLITE_CORRUPT_VTAB: zName = "SQLITE_CORRUPT_VTAB"; break;
case SQLITE_NOTFOUND: zName = "SQLITE_NOTFOUND"; break;
@@ -115891,6 +118581,7 @@ SQLITE_PRIVATE const char *sqlite3ErrName(int rc){
case SQLITE_CANTOPEN_NOTEMPDIR: zName = "SQLITE_CANTOPEN_NOTEMPDIR";break;
case SQLITE_CANTOPEN_ISDIR: zName = "SQLITE_CANTOPEN_ISDIR"; break;
case SQLITE_CANTOPEN_FULLPATH: zName = "SQLITE_CANTOPEN_FULLPATH"; break;
+ case SQLITE_CANTOPEN_CONVPATH: zName = "SQLITE_CANTOPEN_CONVPATH"; break;
case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break;
case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break;
case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break;
@@ -115922,6 +118613,7 @@ SQLITE_PRIVATE const char *sqlite3ErrName(int rc){
case SQLITE_NOTICE_RECOVER_ROLLBACK:
zName = "SQLITE_NOTICE_RECOVER_ROLLBACK"; break;
case SQLITE_WARNING: zName = "SQLITE_WARNING"; break;
+ case SQLITE_WARNING_AUTOINDEX: zName = "SQLITE_WARNING_AUTOINDEX"; break;
case SQLITE_DONE: zName = "SQLITE_DONE"; break;
}
}
@@ -116082,7 +118774,7 @@ SQLITE_API void sqlite3_progress_handler(
sqlite3_mutex_enter(db->mutex);
if( nOps>0 ){
db->xProgress = xProgress;
- db->nProgressOps = nOps;
+ db->nProgressOps = (unsigned)nOps;
db->pProgressArg = pArg;
}else{
db->xProgress = 0;
@@ -116179,8 +118871,8 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
** operation to continue but invalidate all precompiled statements.
*/
p = sqlite3FindFunction(db, zFunctionName, nName, nArg, (u8)enc, 0);
- if( p && p->iPrefEnc==enc && p->nArg==nArg ){
- if( db->activeVdbeCnt ){
+ if( p && (p->funcFlags & SQLITE_FUNC_ENCMASK)==enc && p->nArg==nArg ){
+ if( db->nVdbeActive ){
sqlite3Error(db, SQLITE_BUSY,
"unable to delete/modify user-function due to active statements");
assert( !db->mallocFailed );
@@ -116204,7 +118896,7 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
pDestructor->nRef++;
}
p->pDestructor = pDestructor;
- p->flags = 0;
+ p->funcFlags &= SQLITE_FUNC_ENCMASK;
p->xFunc = xFunc;
p->xStep = xStep;
p->xFinalize = xFinal;
@@ -116761,7 +119453,7 @@ static int createCollation(
*/
pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, 0);
if( pColl && pColl->xCmp ){
- if( db->activeVdbeCnt ){
+ if( db->nVdbeActive ){
sqlite3Error(db, SQLITE_BUSY,
"unable to delete/modify collation sequence due to active statements");
return SQLITE_BUSY;
@@ -116959,20 +119651,20 @@ SQLITE_PRIVATE int sqlite3ParseUri(
zFile = sqlite3_malloc(nByte);
if( !zFile ) return SQLITE_NOMEM;
+ iIn = 5;
+#ifndef SQLITE_ALLOW_URI_AUTHORITY
/* Discard the scheme and authority segments of the URI. */
if( zUri[5]=='/' && zUri[6]=='/' ){
iIn = 7;
while( zUri[iIn] && zUri[iIn]!='/' ) iIn++;
-
if( iIn!=7 && (iIn!=16 || memcmp("localhost", &zUri[7], 9)) ){
*pzErrMsg = sqlite3_mprintf("invalid uri authority: %.*s",
iIn-7, &zUri[7]);
rc = SQLITE_ERROR;
goto parse_uri_out;
}
- }else{
- iIn = 5;
}
+#endif
/* Copy the filename and any query parameters into the zFile buffer.
** Decode %HH escape codes along the way.
@@ -117236,7 +119928,10 @@ static int openDatabase(
db->nextAutovac = -1;
db->szMmap = sqlite3GlobalConfig.szMmap;
db->nextPagesize = 0;
- db->flags |= SQLITE_ShortColNames | SQLITE_AutoIndex | SQLITE_EnableTrigger
+ db->flags |= SQLITE_ShortColNames | SQLITE_EnableTrigger | SQLITE_CacheSpill
+#if !defined(SQLITE_DEFAULT_AUTOMATIC_INDEX) || SQLITE_DEFAULT_AUTOMATIC_INDEX
+ | SQLITE_AutoIndex
+#endif
#if SQLITE_DEFAULT_FILE_FORMAT<4
| SQLITE_LegacyFileFmt
#endif
@@ -117576,8 +120271,6 @@ SQLITE_API int sqlite3_global_recover(void){
** mode. Return TRUE if it is and FALSE if not. Autocommit mode is on
** by default. Autocommit is disabled by a BEGIN statement and reenabled
** by the next COMMIT or ROLLBACK.
-**
-******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ******
*/
SQLITE_API int sqlite3_get_autocommit(sqlite3 *db){
return db->autoCommit;
@@ -118780,7 +121473,7 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){
/* If not building as part of the core, include sqlite3ext.h. */
#ifndef SQLITE_CORE
-SQLITE_API extern const sqlite3_api_routines *sqlite3_api;
+SQLITE_EXTENSION_INIT3
#endif
/************** Include fts3_tokenizer.h in the middle of fts3Int.h **********/
@@ -119067,6 +121760,18 @@ SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem(const Fts3Hash *, const voi
/************** Continuing where we left off in fts3Int.h ********************/
/*
+** This constant determines the maximum depth of an FTS expression tree
+** that the library will create and use. FTS uses recursion to perform
+** various operations on the query tree, so the disadvantage of a large
+** limit is that it may allow very large queries to use large amounts
+** of stack space (perhaps causing a stack overflow).
+*/
+#ifndef SQLITE_FTS3_MAX_EXPR_DEPTH
+# define SQLITE_FTS3_MAX_EXPR_DEPTH 12
+#endif
+
+
+/*
** This constant controls how often segments are merged. Once there are
** FTS3_MERGE_COUNT segments of level N, they are merged into a single
** segment of level N+1.
@@ -119221,6 +121926,7 @@ struct Fts3Table {
const char *zName; /* virtual table name */
int nColumn; /* number of named columns in virtual table */
char **azColumn; /* column names. malloced */
+ u8 *abNotindexed; /* True for 'notindexed' columns */
sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */
char *zContentTbl; /* content=xxx option, or NULL */
char *zLanguageid; /* languageid=xxx option, or NULL */
@@ -119281,6 +121987,12 @@ struct Fts3Table {
int inTransaction; /* True after xBegin but before xCommit/xRollback */
int mxSavepoint; /* Largest valid xSavepoint integer */
#endif
+
+#ifdef SQLITE_TEST
+ /* True to disable the incremental doclist optimization. This is controled
+ ** by special insert command 'test-no-incr-doclist'. */
+ int bNoIncrDoclist;
+#endif
};
/*
@@ -119306,7 +122018,8 @@ struct Fts3Cursor {
int eEvalmode; /* An FTS3_EVAL_XX constant */
int nRowAvg; /* Average size of database rows, in pages */
sqlite3_int64 nDoc; /* Documents in table */
-
+ i64 iMinDocid; /* Minimum docid to return */
+ i64 iMaxDocid; /* Maximum docid to return */
int isMatchinfoNeeded; /* True when aMatchinfo[] needs filling in */
u32 *aMatchinfo; /* Information about most recent match */
int nMatchinfo; /* Number of elements in aMatchinfo[] */
@@ -119336,6 +122049,15 @@ struct Fts3Cursor {
#define FTS3_DOCID_SEARCH 1 /* Lookup by rowid on %_content table */
#define FTS3_FULLTEXT_SEARCH 2 /* Full-text index search */
+/*
+** The lower 16-bits of the sqlite3_index_info.idxNum value set by
+** the xBestIndex() method contains the Fts3Cursor.eSearch value described
+** above. The upper 16-bits contain a combination of the following
+** bits, used to describe extra constraints on full-text searches.
+*/
+#define FTS3_HAVE_LANGID 0x00010000 /* languageid=? */
+#define FTS3_HAVE_DOCID_GE 0x00020000 /* docid>=? */
+#define FTS3_HAVE_DOCID_LE 0x00040000 /* docid<=? */
struct Fts3Doclist {
char *aAll; /* Array containing doclist (or NULL) */
@@ -119448,7 +122170,6 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(
Fts3Table*,int,const char*,int,int,Fts3SegReader**);
SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3SegReader *);
SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(Fts3Table*, int, int, int, sqlite3_stmt **);
-SQLITE_PRIVATE int sqlite3Fts3ReadLock(Fts3Table *);
SQLITE_PRIVATE int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*, int*);
SQLITE_PRIVATE int sqlite3Fts3SelectDoctotal(Fts3Table *, sqlite3_stmt **);
@@ -120381,6 +123102,8 @@ static int fts3InitVtab(
char *zUncompress = 0; /* uncompress=? parameter (or NULL) */
char *zContent = 0; /* content=? parameter (or NULL) */
char *zLanguageid = 0; /* languageid=? parameter (or NULL) */
+ char **azNotindexed = 0; /* The set of notindexed= columns */
+ int nNotindexed = 0; /* Size of azNotindexed[] array */
assert( strlen(argv[0])==4 );
assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4)
@@ -120390,9 +123113,19 @@ static int fts3InitVtab(
nDb = (int)strlen(argv[1]) + 1;
nName = (int)strlen(argv[2]) + 1;
- aCol = (const char **)sqlite3_malloc(sizeof(const char *) * (argc-2) );
- if( !aCol ) return SQLITE_NOMEM;
- memset((void *)aCol, 0, sizeof(const char *) * (argc-2));
+ nByte = sizeof(const char *) * (argc-2);
+ aCol = (const char **)sqlite3_malloc(nByte);
+ if( aCol ){
+ memset((void*)aCol, 0, nByte);
+ azNotindexed = (char **)sqlite3_malloc(nByte);
+ }
+ if( azNotindexed ){
+ memset(azNotindexed, 0, nByte);
+ }
+ if( !aCol || !azNotindexed ){
+ rc = SQLITE_NOMEM;
+ goto fts3_init_out;
+ }
/* Loop through all of the arguments passed by the user to the FTS3/4
** module (i.e. all the column names and special arguments). This loop
@@ -120431,7 +123164,8 @@ static int fts3InitVtab(
{ "uncompress", 10 }, /* 3 -> UNCOMPRESS */
{ "order", 5 }, /* 4 -> ORDER */
{ "content", 7 }, /* 5 -> CONTENT */
- { "languageid", 10 } /* 6 -> LANGUAGEID */
+ { "languageid", 10 }, /* 6 -> LANGUAGEID */
+ { "notindexed", 10 } /* 7 -> NOTINDEXED */
};
int iOpt;
@@ -120497,6 +123231,11 @@ static int fts3InitVtab(
zLanguageid = zVal;
zVal = 0;
break;
+
+ case 7: /* NOTINDEXED */
+ azNotindexed[nNotindexed++] = zVal;
+ zVal = 0;
+ break;
}
}
sqlite3_free(zVal);
@@ -120568,6 +123307,7 @@ static int fts3InitVtab(
nByte = sizeof(Fts3Table) + /* Fts3Table */
nCol * sizeof(char *) + /* azColumn */
nIndex * sizeof(struct Fts3Index) + /* aIndex */
+ nCol * sizeof(u8) + /* abNotindexed */
nName + /* zName */
nDb + /* zDb */
nString; /* Space for azColumn strings */
@@ -120601,9 +123341,10 @@ static int fts3InitVtab(
for(i=0; i<nIndex; i++){
fts3HashInit(&p->aIndex[i].hPending, FTS3_HASH_STRING, 1);
}
+ p->abNotindexed = (u8 *)&p->aIndex[nIndex];
/* Fill in the zName and zDb fields of the vtab structure. */
- zCsr = (char *)&p->aIndex[nIndex];
+ zCsr = (char *)&p->abNotindexed[nCol];
p->zName = zCsr;
memcpy(zCsr, argv[2], nName);
zCsr += nName;
@@ -120624,7 +123365,26 @@ static int fts3InitVtab(
assert( zCsr <= &((char *)p)[nByte] );
}
- if( (zCompress==0)!=(zUncompress==0) ){
+ /* Fill in the abNotindexed array */
+ for(iCol=0; iCol<nCol; iCol++){
+ int n = (int)strlen(p->azColumn[iCol]);
+ for(i=0; i<nNotindexed; i++){
+ char *zNot = azNotindexed[i];
+ if( zNot && 0==sqlite3_strnicmp(p->azColumn[iCol], zNot, n) ){
+ p->abNotindexed[iCol] = 1;
+ sqlite3_free(zNot);
+ azNotindexed[i] = 0;
+ }
+ }
+ }
+ for(i=0; i<nNotindexed; i++){
+ if( azNotindexed[i] ){
+ *pzErr = sqlite3_mprintf("no such column: %s", azNotindexed[i]);
+ rc = SQLITE_ERROR;
+ }
+ }
+
+ if( rc==SQLITE_OK && (zCompress==0)!=(zUncompress==0) ){
char const *zMiss = (zCompress==0 ? "compress" : "uncompress");
rc = SQLITE_ERROR;
*pzErr = sqlite3_mprintf("missing %s parameter in fts4 constructor", zMiss);
@@ -120665,7 +123425,9 @@ fts3_init_out:
sqlite3_free(zUncompress);
sqlite3_free(zContent);
sqlite3_free(zLanguageid);
+ for(i=0; i<nNotindexed; i++) sqlite3_free(azNotindexed[i]);
sqlite3_free((void *)aCol);
+ sqlite3_free((void *)azNotindexed);
if( rc!=SQLITE_OK ){
if( p ){
fts3DisconnectMethod((sqlite3_vtab *)p);
@@ -120716,23 +123478,27 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
Fts3Table *p = (Fts3Table *)pVTab;
int i; /* Iterator variable */
int iCons = -1; /* Index of constraint to use */
+
int iLangidCons = -1; /* Index of langid=x constraint, if present */
+ int iDocidGe = -1; /* Index of docid>=x constraint, if present */
+ int iDocidLe = -1; /* Index of docid<=x constraint, if present */
+ int iIdx;
/* By default use a full table scan. This is an expensive option,
** so search through the constraints to see if a more efficient
** strategy is possible.
*/
pInfo->idxNum = FTS3_FULLSCAN_SEARCH;
- pInfo->estimatedCost = 500000;
+ pInfo->estimatedCost = 5000000;
for(i=0; i<pInfo->nConstraint; i++){
+ int bDocid; /* True if this constraint is on docid */
struct sqlite3_index_constraint *pCons = &pInfo->aConstraint[i];
if( pCons->usable==0 ) continue;
+ bDocid = (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1);
+
/* A direct lookup on the rowid or docid column. Assign a cost of 1.0. */
- if( iCons<0
- && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ
- && (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1 )
- ){
+ if( iCons<0 && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ && bDocid ){
pInfo->idxNum = FTS3_DOCID_SEARCH;
pInfo->estimatedCost = 1.0;
iCons = i;
@@ -120761,14 +123527,38 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
){
iLangidCons = i;
}
+
+ if( bDocid ){
+ switch( pCons->op ){
+ case SQLITE_INDEX_CONSTRAINT_GE:
+ case SQLITE_INDEX_CONSTRAINT_GT:
+ iDocidGe = i;
+ break;
+
+ case SQLITE_INDEX_CONSTRAINT_LE:
+ case SQLITE_INDEX_CONSTRAINT_LT:
+ iDocidLe = i;
+ break;
+ }
+ }
}
+ iIdx = 1;
if( iCons>=0 ){
- pInfo->aConstraintUsage[iCons].argvIndex = 1;
+ pInfo->aConstraintUsage[iCons].argvIndex = iIdx++;
pInfo->aConstraintUsage[iCons].omit = 1;
}
if( iLangidCons>=0 ){
- pInfo->aConstraintUsage[iLangidCons].argvIndex = 2;
+ pInfo->idxNum |= FTS3_HAVE_LANGID;
+ pInfo->aConstraintUsage[iLangidCons].argvIndex = iIdx++;
+ }
+ if( iDocidGe>=0 ){
+ pInfo->idxNum |= FTS3_HAVE_DOCID_GE;
+ pInfo->aConstraintUsage[iDocidGe].argvIndex = iIdx++;
+ }
+ if( iDocidLe>=0 ){
+ pInfo->idxNum |= FTS3_HAVE_DOCID_LE;
+ pInfo->aConstraintUsage[iDocidLe].argvIndex = iIdx++;
}
/* Regardless of the strategy selected, FTS can deliver rows in rowid (or
@@ -122216,6 +125006,33 @@ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){
}
/*
+** The following are copied from sqliteInt.h.
+**
+** Constants for the largest and smallest possible 64-bit signed integers.
+** These macros are designed to work correctly on both 32-bit and 64-bit
+** compilers.
+*/
+#ifndef SQLITE_AMALGAMATION
+# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
+# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
+#endif
+
+/*
+** If the numeric type of argument pVal is "integer", then return it
+** converted to a 64-bit signed integer. Otherwise, return a copy of
+** the second parameter, iDefault.
+*/
+static sqlite3_int64 fts3DocidRange(sqlite3_value *pVal, i64 iDefault){
+ if( pVal ){
+ int eType = sqlite3_value_numeric_type(pVal);
+ if( eType==SQLITE_INTEGER ){
+ return sqlite3_value_int64(pVal);
+ }
+ }
+ return iDefault;
+}
+
+/*
** This is the xFilter interface for the virtual table. See
** the virtual table xFilter method documentation for additional
** information.
@@ -122240,40 +125057,58 @@ static int fts3FilterMethod(
){
int rc;
char *zSql; /* SQL statement used to access %_content */
+ int eSearch;
Fts3Table *p = (Fts3Table *)pCursor->pVtab;
Fts3Cursor *pCsr = (Fts3Cursor *)pCursor;
+ sqlite3_value *pCons = 0; /* The MATCH or rowid constraint, if any */
+ sqlite3_value *pLangid = 0; /* The "langid = ?" constraint, if any */
+ sqlite3_value *pDocidGe = 0; /* The "docid >= ?" constraint, if any */
+ sqlite3_value *pDocidLe = 0; /* The "docid <= ?" constraint, if any */
+ int iIdx;
+
UNUSED_PARAMETER(idxStr);
UNUSED_PARAMETER(nVal);
- assert( idxNum>=0 && idxNum<=(FTS3_FULLTEXT_SEARCH+p->nColumn) );
- assert( nVal==0 || nVal==1 || nVal==2 );
- assert( (nVal==0)==(idxNum==FTS3_FULLSCAN_SEARCH) );
+ eSearch = (idxNum & 0x0000FFFF);
+ assert( eSearch>=0 && eSearch<=(FTS3_FULLTEXT_SEARCH+p->nColumn) );
assert( p->pSegments==0 );
+ /* Collect arguments into local variables */
+ iIdx = 0;
+ if( eSearch!=FTS3_FULLSCAN_SEARCH ) pCons = apVal[iIdx++];
+ if( idxNum & FTS3_HAVE_LANGID ) pLangid = apVal[iIdx++];
+ if( idxNum & FTS3_HAVE_DOCID_GE ) pDocidGe = apVal[iIdx++];
+ if( idxNum & FTS3_HAVE_DOCID_LE ) pDocidLe = apVal[iIdx++];
+ assert( iIdx==nVal );
+
/* In case the cursor has been used before, clear it now. */
sqlite3_finalize(pCsr->pStmt);
sqlite3_free(pCsr->aDoclist);
sqlite3Fts3ExprFree(pCsr->pExpr);
memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor));
+ /* Set the lower and upper bounds on docids to return */
+ pCsr->iMinDocid = fts3DocidRange(pDocidGe, SMALLEST_INT64);
+ pCsr->iMaxDocid = fts3DocidRange(pDocidLe, LARGEST_INT64);
+
if( idxStr ){
pCsr->bDesc = (idxStr[0]=='D');
}else{
pCsr->bDesc = p->bDescIdx;
}
- pCsr->eSearch = (i16)idxNum;
+ pCsr->eSearch = (i16)eSearch;
- if( idxNum!=FTS3_DOCID_SEARCH && idxNum!=FTS3_FULLSCAN_SEARCH ){
- int iCol = idxNum-FTS3_FULLTEXT_SEARCH;
- const char *zQuery = (const char *)sqlite3_value_text(apVal[0]);
+ if( eSearch!=FTS3_DOCID_SEARCH && eSearch!=FTS3_FULLSCAN_SEARCH ){
+ int iCol = eSearch-FTS3_FULLTEXT_SEARCH;
+ const char *zQuery = (const char *)sqlite3_value_text(pCons);
- if( zQuery==0 && sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
+ if( zQuery==0 && sqlite3_value_type(pCons)!=SQLITE_NULL ){
return SQLITE_NOMEM;
}
pCsr->iLangid = 0;
- if( nVal==2 ) pCsr->iLangid = sqlite3_value_int(apVal[1]);
+ if( pLangid ) pCsr->iLangid = sqlite3_value_int(pLangid);
assert( p->base.zErrMsg==0 );
rc = sqlite3Fts3ExprParse(p->pTokenizer, pCsr->iLangid,
@@ -122284,11 +125119,7 @@ static int fts3FilterMethod(
return rc;
}
- rc = sqlite3Fts3ReadLock(p);
- if( rc!=SQLITE_OK ) return rc;
-
rc = fts3EvalStart(pCsr);
-
sqlite3Fts3SegmentsClose(p);
if( rc!=SQLITE_OK ) return rc;
pCsr->pNextId = pCsr->aDoclist;
@@ -122300,7 +125131,7 @@ static int fts3FilterMethod(
** full-text query or docid lookup, the statement retrieves a single
** row by docid.
*/
- if( idxNum==FTS3_FULLSCAN_SEARCH ){
+ if( eSearch==FTS3_FULLSCAN_SEARCH ){
zSql = sqlite3_mprintf(
"SELECT %s ORDER BY rowid %s",
p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC")
@@ -122311,10 +125142,10 @@ static int fts3FilterMethod(
}else{
rc = SQLITE_NOMEM;
}
- }else if( idxNum==FTS3_DOCID_SEARCH ){
+ }else if( eSearch==FTS3_DOCID_SEARCH ){
rc = fts3CursorSeekStmt(pCsr, &pCsr->pStmt);
if( rc==SQLITE_OK ){
- rc = sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
+ rc = sqlite3_bind_value(pCsr->pStmt, 1, pCons);
}
}
if( rc!=SQLITE_OK ) return rc;
@@ -123206,6 +126037,12 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
}
/*
+** Maximum number of tokens a phrase may have to be considered for the
+** incremental doclists strategy.
+*/
+#define MAX_INCR_PHRASE_TOKENS 4
+
+/*
** This function is called for each Fts3Phrase in a full-text query
** expression to initialize the mechanism for returning rows. Once this
** function has been called successfully on an Fts3Phrase, it may be
@@ -123218,23 +126055,43 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code.
*/
static int fts3EvalPhraseStart(Fts3Cursor *pCsr, int bOptOk, Fts3Phrase *p){
- int rc; /* Error code */
- Fts3PhraseToken *pFirst = &p->aToken[0];
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
+ int rc = SQLITE_OK; /* Error code */
+ int i;
- if( pCsr->bDesc==pTab->bDescIdx
- && bOptOk==1
- && p->nToken==1
- && pFirst->pSegcsr
- && pFirst->pSegcsr->bLookup
- && pFirst->bFirst==0
- ){
+ /* Determine if doclists may be loaded from disk incrementally. This is
+ ** possible if the bOptOk argument is true, the FTS doclists will be
+ ** scanned in forward order, and the phrase consists of
+ ** MAX_INCR_PHRASE_TOKENS or fewer tokens, none of which are are "^first"
+ ** tokens or prefix tokens that cannot use a prefix-index. */
+ int bHaveIncr = 0;
+ int bIncrOk = (bOptOk
+ && pCsr->bDesc==pTab->bDescIdx
+ && p->nToken<=MAX_INCR_PHRASE_TOKENS && p->nToken>0
+ && p->nToken<=MAX_INCR_PHRASE_TOKENS && p->nToken>0
+#ifdef SQLITE_TEST
+ && pTab->bNoIncrDoclist==0
+#endif
+ );
+ for(i=0; bIncrOk==1 && i<p->nToken; i++){
+ Fts3PhraseToken *pToken = &p->aToken[i];
+ if( pToken->bFirst || (pToken->pSegcsr!=0 && !pToken->pSegcsr->bLookup) ){
+ bIncrOk = 0;
+ }
+ if( pToken->pSegcsr ) bHaveIncr = 1;
+ }
+
+ if( bIncrOk && bHaveIncr ){
/* Use the incremental approach. */
int iCol = (p->iColumn >= pTab->nColumn ? -1 : p->iColumn);
- rc = sqlite3Fts3MsrIncrStart(
- pTab, pFirst->pSegcsr, iCol, pFirst->z, pFirst->n);
+ for(i=0; rc==SQLITE_OK && i<p->nToken; i++){
+ Fts3PhraseToken *pToken = &p->aToken[i];
+ Fts3MultiSegReader *pSegcsr = pToken->pSegcsr;
+ if( pSegcsr ){
+ rc = sqlite3Fts3MsrIncrStart(pTab, pSegcsr, iCol, pToken->z, pToken->n);
+ }
+ }
p->bIncr = 1;
-
}else{
/* Load the full doclist for the phrase into memory. */
rc = fts3EvalPhraseLoad(pCsr, p);
@@ -123344,15 +126201,125 @@ SQLITE_PRIVATE void sqlite3Fts3DoclistNext(
}
/*
-** Attempt to move the phrase iterator to point to the next matching docid.
+** Advance the iterator pDL to the next entry in pDL->aAll/nAll. Set *pbEof
+** to true if EOF is reached.
+*/
+static void fts3EvalDlPhraseNext(
+ Fts3Table *pTab,
+ Fts3Doclist *pDL,
+ u8 *pbEof
+){
+ char *pIter; /* Used to iterate through aAll */
+ char *pEnd = &pDL->aAll[pDL->nAll]; /* 1 byte past end of aAll */
+
+ if( pDL->pNextDocid ){
+ pIter = pDL->pNextDocid;
+ }else{
+ pIter = pDL->aAll;
+ }
+
+ if( pIter>=pEnd ){
+ /* We have already reached the end of this doclist. EOF. */
+ *pbEof = 1;
+ }else{
+ sqlite3_int64 iDelta;
+ pIter += sqlite3Fts3GetVarint(pIter, &iDelta);
+ if( pTab->bDescIdx==0 || pDL->pNextDocid==0 ){
+ pDL->iDocid += iDelta;
+ }else{
+ pDL->iDocid -= iDelta;
+ }
+ pDL->pList = pIter;
+ fts3PoslistCopy(0, &pIter);
+ pDL->nList = (int)(pIter - pDL->pList);
+
+ /* pIter now points just past the 0x00 that terminates the position-
+ ** list for document pDL->iDocid. However, if this position-list was
+ ** edited in place by fts3EvalNearTrim(), then pIter may not actually
+ ** point to the start of the next docid value. The following line deals
+ ** with this case by advancing pIter past the zero-padding added by
+ ** fts3EvalNearTrim(). */
+ while( pIter<pEnd && *pIter==0 ) pIter++;
+
+ pDL->pNextDocid = pIter;
+ assert( pIter>=&pDL->aAll[pDL->nAll] || *pIter );
+ *pbEof = 0;
+ }
+}
+
+/*
+** Helper type used by fts3EvalIncrPhraseNext() and incrPhraseTokenNext().
+*/
+typedef struct TokenDoclist TokenDoclist;
+struct TokenDoclist {
+ int bIgnore;
+ sqlite3_int64 iDocid;
+ char *pList;
+ int nList;
+};
+
+/*
+** Token pToken is an incrementally loaded token that is part of a
+** multi-token phrase. Advance it to the next matching document in the
+** database and populate output variable *p with the details of the new
+** entry. Or, if the iterator has reached EOF, set *pbEof to true.
+**
** If an error occurs, return an SQLite error code. Otherwise, return
** SQLITE_OK.
+*/
+static int incrPhraseTokenNext(
+ Fts3Table *pTab, /* Virtual table handle */
+ Fts3Phrase *pPhrase, /* Phrase to advance token of */
+ int iToken, /* Specific token to advance */
+ TokenDoclist *p, /* OUT: Docid and doclist for new entry */
+ u8 *pbEof /* OUT: True if iterator is at EOF */
+){
+ int rc = SQLITE_OK;
+
+ if( pPhrase->iDoclistToken==iToken ){
+ assert( p->bIgnore==0 );
+ assert( pPhrase->aToken[iToken].pSegcsr==0 );
+ fts3EvalDlPhraseNext(pTab, &pPhrase->doclist, pbEof);
+ p->pList = pPhrase->doclist.pList;
+ p->nList = pPhrase->doclist.nList;
+ p->iDocid = pPhrase->doclist.iDocid;
+ }else{
+ Fts3PhraseToken *pToken = &pPhrase->aToken[iToken];
+ assert( pToken->pDeferred==0 );
+ assert( pToken->pSegcsr || pPhrase->iDoclistToken>=0 );
+ if( pToken->pSegcsr ){
+ assert( p->bIgnore==0 );
+ rc = sqlite3Fts3MsrIncrNext(
+ pTab, pToken->pSegcsr, &p->iDocid, &p->pList, &p->nList
+ );
+ if( p->pList==0 ) *pbEof = 1;
+ }else{
+ p->bIgnore = 1;
+ }
+ }
+
+ return rc;
+}
+
+
+/*
+** The phrase iterator passed as the second argument:
+**
+** * features at least one token that uses an incremental doclist, and
+**
+** * does not contain any deferred tokens.
+**
+** Advance it to the next matching documnent in the database and populate
+** the Fts3Doclist.pList and nList fields.
**
** If there is no "next" entry and no error occurs, then *pbEof is set to
** 1 before returning. Otherwise, if no error occurs and the iterator is
** successfully advanced, *pbEof is set to 0.
+**
+** If an error occurs, return an SQLite error code. Otherwise, return
+** SQLITE_OK.
*/
-static int fts3EvalPhraseNext(
+static int fts3EvalIncrPhraseNext(
Fts3Cursor *pCsr, /* FTS Cursor handle */
Fts3Phrase *p, /* Phrase object to advance to next docid */
u8 *pbEof /* OUT: Set to 1 if EOF */
@@ -123360,57 +126327,116 @@ static int fts3EvalPhraseNext(
int rc = SQLITE_OK;
Fts3Doclist *pDL = &p->doclist;
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
+ u8 bEof = 0;
- if( p->bIncr ){
- assert( p->nToken==1 );
- assert( pDL->pNextDocid==0 );
+ /* This is only called if it is guaranteed that the phrase has at least
+ ** one incremental token. In which case the bIncr flag is set. */
+ assert( p->bIncr==1 );
+
+ if( p->nToken==1 && p->bIncr ){
rc = sqlite3Fts3MsrIncrNext(pTab, p->aToken[0].pSegcsr,
&pDL->iDocid, &pDL->pList, &pDL->nList
);
- if( rc==SQLITE_OK && !pDL->pList ){
- *pbEof = 1;
+ if( pDL->pList==0 ) bEof = 1;
+ }else{
+ int bDescDoclist = pCsr->bDesc;
+ struct TokenDoclist a[MAX_INCR_PHRASE_TOKENS];
+
+ memset(a, 0, sizeof(a));
+ assert( p->nToken<=MAX_INCR_PHRASE_TOKENS );
+ assert( p->iDoclistToken<MAX_INCR_PHRASE_TOKENS );
+
+ while( bEof==0 ){
+ int bMaxSet = 0;
+ sqlite3_int64 iMax = 0; /* Largest docid for all iterators */
+ int i; /* Used to iterate through tokens */
+
+ /* Advance the iterator for each token in the phrase once. */
+ for(i=0; rc==SQLITE_OK && i<p->nToken && bEof==0; i++){
+ rc = incrPhraseTokenNext(pTab, p, i, &a[i], &bEof);
+ if( a[i].bIgnore==0 && (bMaxSet==0 || DOCID_CMP(iMax, a[i].iDocid)<0) ){
+ iMax = a[i].iDocid;
+ bMaxSet = 1;
+ }
+ }
+ assert( rc!=SQLITE_OK || a[p->nToken-1].bIgnore==0 );
+ assert( rc!=SQLITE_OK || bMaxSet );
+
+ /* Keep advancing iterators until they all point to the same document */
+ for(i=0; i<p->nToken; i++){
+ while( rc==SQLITE_OK && bEof==0
+ && a[i].bIgnore==0 && DOCID_CMP(a[i].iDocid, iMax)<0
+ ){
+ rc = incrPhraseTokenNext(pTab, p, i, &a[i], &bEof);
+ if( DOCID_CMP(a[i].iDocid, iMax)>0 ){
+ iMax = a[i].iDocid;
+ i = 0;
+ }
+ }
+ }
+
+ /* Check if the current entries really are a phrase match */
+ if( bEof==0 ){
+ int nList = 0;
+ int nByte = a[p->nToken-1].nList;
+ char *aDoclist = sqlite3_malloc(nByte+1);
+ if( !aDoclist ) return SQLITE_NOMEM;
+ memcpy(aDoclist, a[p->nToken-1].pList, nByte+1);
+
+ for(i=0; i<(p->nToken-1); i++){
+ if( a[i].bIgnore==0 ){
+ char *pL = a[i].pList;
+ char *pR = aDoclist;
+ char *pOut = aDoclist;
+ int nDist = p->nToken-1-i;
+ int res = fts3PoslistPhraseMerge(&pOut, nDist, 0, 1, &pL, &pR);
+ if( res==0 ) break;
+ nList = (int)(pOut - aDoclist);
+ }
+ }
+ if( i==(p->nToken-1) ){
+ pDL->iDocid = iMax;
+ pDL->pList = aDoclist;
+ pDL->nList = nList;
+ pDL->bFreeList = 1;
+ break;
+ }
+ sqlite3_free(aDoclist);
+ }
}
+ }
+
+ *pbEof = bEof;
+ return rc;
+}
+
+/*
+** Attempt to move the phrase iterator to point to the next matching docid.
+** If an error occurs, return an SQLite error code. Otherwise, return
+** SQLITE_OK.
+**
+** If there is no "next" entry and no error occurs, then *pbEof is set to
+** 1 before returning. Otherwise, if no error occurs and the iterator is
+** successfully advanced, *pbEof is set to 0.
+*/
+static int fts3EvalPhraseNext(
+ Fts3Cursor *pCsr, /* FTS Cursor handle */
+ Fts3Phrase *p, /* Phrase object to advance to next docid */
+ u8 *pbEof /* OUT: Set to 1 if EOF */
+){
+ int rc = SQLITE_OK;
+ Fts3Doclist *pDL = &p->doclist;
+ Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
+
+ if( p->bIncr ){
+ rc = fts3EvalIncrPhraseNext(pCsr, p, pbEof);
}else if( pCsr->bDesc!=pTab->bDescIdx && pDL->nAll ){
sqlite3Fts3DoclistPrev(pTab->bDescIdx, pDL->aAll, pDL->nAll,
&pDL->pNextDocid, &pDL->iDocid, &pDL->nList, pbEof
);
pDL->pList = pDL->pNextDocid;
}else{
- char *pIter; /* Used to iterate through aAll */
- char *pEnd = &pDL->aAll[pDL->nAll]; /* 1 byte past end of aAll */
- if( pDL->pNextDocid ){
- pIter = pDL->pNextDocid;
- }else{
- pIter = pDL->aAll;
- }
-
- if( pIter>=pEnd ){
- /* We have already reached the end of this doclist. EOF. */
- *pbEof = 1;
- }else{
- sqlite3_int64 iDelta;
- pIter += sqlite3Fts3GetVarint(pIter, &iDelta);
- if( pTab->bDescIdx==0 || pDL->pNextDocid==0 ){
- pDL->iDocid += iDelta;
- }else{
- pDL->iDocid -= iDelta;
- }
- pDL->pList = pIter;
- fts3PoslistCopy(0, &pIter);
- pDL->nList = (int)(pIter - pDL->pList);
-
- /* pIter now points just past the 0x00 that terminates the position-
- ** list for document pDL->iDocid. However, if this position-list was
- ** edited in place by fts3EvalNearTrim(), then pIter may not actually
- ** point to the start of the next docid value. The following line deals
- ** with this case by advancing pIter past the zero-padding added by
- ** fts3EvalNearTrim(). */
- while( pIter<pEnd && *pIter==0 ) pIter++;
-
- pDL->pNextDocid = pIter;
- assert( pIter>=&pDL->aAll[pDL->nAll] || *pIter );
- *pbEof = 0;
- }
+ fts3EvalDlPhraseNext(pTab, pDL, pbEof);
}
return rc;
@@ -123435,7 +126461,6 @@ static int fts3EvalPhraseNext(
static void fts3EvalStartReaders(
Fts3Cursor *pCsr, /* FTS Cursor handle */
Fts3Expr *pExpr, /* Expression to initialize phrases in */
- int bOptOk, /* True to enable incremental loading */
int *pRc /* IN/OUT: Error code */
){
if( pExpr && SQLITE_OK==*pRc ){
@@ -123446,10 +126471,10 @@ static void fts3EvalStartReaders(
if( pExpr->pPhrase->aToken[i].pDeferred==0 ) break;
}
pExpr->bDeferred = (i==nToken);
- *pRc = fts3EvalPhraseStart(pCsr, bOptOk, pExpr->pPhrase);
+ *pRc = fts3EvalPhraseStart(pCsr, 1, pExpr->pPhrase);
}else{
- fts3EvalStartReaders(pCsr, pExpr->pLeft, bOptOk, pRc);
- fts3EvalStartReaders(pCsr, pExpr->pRight, bOptOk, pRc);
+ fts3EvalStartReaders(pCsr, pExpr->pLeft, pRc);
+ fts3EvalStartReaders(pCsr, pExpr->pRight, pRc);
pExpr->bDeferred = (pExpr->pLeft->bDeferred && pExpr->pRight->bDeferred);
}
}
@@ -123691,7 +126716,7 @@ static int fts3EvalSelectDeferred(
** overflowing the 32-bit integer it is stored in. */
if( ii<12 ) nLoad4 = nLoad4*4;
- if( ii==0 || pTC->pPhrase->nToken>1 ){
+ if( ii==0 || (pTC->pPhrase->nToken>1 && ii!=nToken-1) ){
/* Either this is the cheapest token in the entire query, or it is
** part of a multi-token phrase. Either way, the entire doclist will
** (eventually) be loaded into memory. It may as well be now. */
@@ -123771,7 +126796,7 @@ static int fts3EvalStart(Fts3Cursor *pCsr){
}
#endif
- fts3EvalStartReaders(pCsr, pCsr->pExpr, 1, &rc);
+ fts3EvalStartReaders(pCsr, pCsr->pExpr, &rc);
return rc;
}
@@ -124254,6 +127279,16 @@ static int fts3EvalNext(Fts3Cursor *pCsr){
pCsr->iPrevId = pExpr->iDocid;
}while( pCsr->isEof==0 && fts3EvalTestDeferredAndNear(pCsr, &rc) );
}
+
+ /* Check if the cursor is past the end of the docid range specified
+ ** by Fts3Cursor.iMinDocid/iMaxDocid. If so, set the EOF flag. */
+ if( rc==SQLITE_OK && (
+ (pCsr->bDesc==0 && pCsr->iPrevId>pCsr->iMaxDocid)
+ || (pCsr->bDesc!=0 && pCsr->iPrevId<pCsr->iMinDocid)
+ )){
+ pCsr->isEof = 1;
+ }
+
return rc;
}
@@ -124277,12 +127312,16 @@ static void fts3EvalRestart(
if( pPhrase ){
fts3EvalInvalidatePoslist(pPhrase);
if( pPhrase->bIncr ){
- assert( pPhrase->nToken==1 );
- assert( pPhrase->aToken[0].pSegcsr );
- sqlite3Fts3MsrIncrRestart(pPhrase->aToken[0].pSegcsr);
+ int i;
+ for(i=0; i<pPhrase->nToken; i++){
+ Fts3PhraseToken *pToken = &pPhrase->aToken[i];
+ assert( pToken->pDeferred==0 );
+ if( pToken->pSegcsr ){
+ sqlite3Fts3MsrIncrRestart(pToken->pSegcsr);
+ }
+ }
*pRc = fts3EvalPhraseStart(pCsr, 0, pPhrase);
}
-
pPhrase->doclist.pNextDocid = 0;
pPhrase->doclist.iDocid = 0;
}
@@ -124531,15 +127570,23 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(
pIter = pPhrase->doclist.pList;
if( iDocid!=pCsr->iPrevId || pExpr->bEof ){
int bDescDoclist = pTab->bDescIdx; /* For DOCID_CMP macro */
+ int iMul; /* +1 if csr dir matches index dir, else -1 */
int bOr = 0;
u8 bEof = 0;
- Fts3Expr *p;
+ u8 bTreeEof = 0;
+ Fts3Expr *p; /* Used to iterate from pExpr to root */
+ Fts3Expr *pNear; /* Most senior NEAR ancestor (or pExpr) */
/* Check if this phrase descends from an OR expression node. If not,
** return NULL. Otherwise, the entry that corresponds to docid
- ** pCsr->iPrevId may lie earlier in the doclist buffer. */
+ ** pCsr->iPrevId may lie earlier in the doclist buffer. Or, if the
+ ** tree that the node is part of has been marked as EOF, but the node
+ ** itself is not EOF, then it may point to an earlier entry. */
+ pNear = pExpr;
for(p=pExpr->pParent; p; p=p->pParent){
if( p->eType==FTSQUERY_OR ) bOr = 1;
+ if( p->eType==FTSQUERY_NEAR ) pNear = p;
+ if( p->bEof ) bTreeEof = 1;
}
if( bOr==0 ) return SQLITE_OK;
@@ -124558,29 +127605,59 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(
assert( rc!=SQLITE_OK || pPhrase->bIncr==0 );
if( rc!=SQLITE_OK ) return rc;
}
-
- if( pExpr->bEof ){
- pIter = 0;
- iDocid = 0;
+
+ iMul = ((pCsr->bDesc==bDescDoclist) ? 1 : -1);
+ while( bTreeEof==1
+ && pNear->bEof==0
+ && (DOCID_CMP(pNear->iDocid, pCsr->iPrevId) * iMul)<0
+ ){
+ int rc = SQLITE_OK;
+ fts3EvalNextRow(pCsr, pExpr, &rc);
+ if( rc!=SQLITE_OK ) return rc;
+ iDocid = pExpr->iDocid;
+ pIter = pPhrase->doclist.pList;
}
+
bEof = (pPhrase->doclist.nAll==0);
assert( bDescDoclist==0 || bDescDoclist==1 );
assert( pCsr->bDesc==0 || pCsr->bDesc==1 );
- if( pCsr->bDesc==bDescDoclist ){
- int dummy;
- while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)>0 ) && bEof==0 ){
- sqlite3Fts3DoclistPrev(
- bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll,
- &pIter, &iDocid, &dummy, &bEof
- );
- }
- }else{
- while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)<0 ) && bEof==0 ){
- sqlite3Fts3DoclistNext(
- bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll,
- &pIter, &iDocid, &bEof
- );
+ if( bEof==0 ){
+ if( pCsr->bDesc==bDescDoclist ){
+ int dummy;
+ if( pNear->bEof ){
+ /* This expression is already at EOF. So position it to point to the
+ ** last entry in the doclist at pPhrase->doclist.aAll[]. Variable
+ ** iDocid is already set for this entry, so all that is required is
+ ** to set pIter to point to the first byte of the last position-list
+ ** in the doclist.
+ **
+ ** It would also be correct to set pIter and iDocid to zero. In
+ ** this case, the first call to sqltie3Fts4DoclistPrev() below
+ ** would also move the iterator to point to the last entry in the
+ ** doclist. However, this is expensive, as to do so it has to
+ ** iterate through the entire doclist from start to finish (since
+ ** it does not know the docid for the last entry). */
+ pIter = &pPhrase->doclist.aAll[pPhrase->doclist.nAll-1];
+ fts3ReversePoslist(pPhrase->doclist.aAll, &pIter);
+ }
+ while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)>0 ) && bEof==0 ){
+ sqlite3Fts3DoclistPrev(
+ bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll,
+ &pIter, &iDocid, &dummy, &bEof
+ );
+ }
+ }else{
+ if( pNear->bEof ){
+ pIter = 0;
+ iDocid = 0;
+ }
+ while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)<0 ) && bEof==0 ){
+ sqlite3Fts3DoclistNext(
+ bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll,
+ &pIter, &iDocid, &bEof
+ );
+ }
}
}
@@ -124639,7 +127716,10 @@ SQLITE_PRIVATE int sqlite3Fts3Corrupt(){
/*
** Initialize API pointer table, if required.
*/
-SQLITE_API int sqlite3_extension_init(
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+SQLITE_API int sqlite3_fts3_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
@@ -124685,6 +127765,7 @@ struct Fts3auxCursor {
Fts3SegFilter filter;
char *zStop;
int nStop; /* Byte-length of string zStop */
+ int iLangid; /* Language id to query */
int isEof; /* True if cursor is at EOF */
sqlite3_int64 iRowid; /* Current rowid */
@@ -124699,7 +127780,8 @@ struct Fts3auxCursor {
/*
** Schema of the terms table.
*/
-#define FTS3_TERMS_SCHEMA "CREATE TABLE x(term, col, documents, occurrences)"
+#define FTS3_AUX_SCHEMA \
+ "CREATE TABLE x(term, col, documents, occurrences, languageid HIDDEN)"
/*
** This function does all the work for both the xConnect and xCreate methods.
@@ -124746,7 +127828,7 @@ static int fts3auxConnectMethod(
}
nFts3 = (int)strlen(zFts3);
- rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA);
+ rc = sqlite3_declare_vtab(db, FTS3_AUX_SCHEMA);
if( rc!=SQLITE_OK ) return rc;
nByte = sizeof(Fts3auxTable) + sizeof(Fts3Table) + nDb + nFts3 + 2;
@@ -124806,6 +127888,8 @@ static int fts3auxBestIndexMethod(
int iEq = -1;
int iGe = -1;
int iLe = -1;
+ int iLangid = -1;
+ int iNext = 1; /* Next free argvIndex value */
UNUSED_PARAMETER(pVTab);
@@ -124817,36 +127901,48 @@ static int fts3auxBestIndexMethod(
pInfo->orderByConsumed = 1;
}
- /* Search for equality and range constraints on the "term" column. */
+ /* Search for equality and range constraints on the "term" column.
+ ** And equality constraints on the hidden "languageid" column. */
for(i=0; i<pInfo->nConstraint; i++){
- if( pInfo->aConstraint[i].usable && pInfo->aConstraint[i].iColumn==0 ){
+ if( pInfo->aConstraint[i].usable ){
int op = pInfo->aConstraint[i].op;
- if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iEq = i;
- if( op==SQLITE_INDEX_CONSTRAINT_LT ) iLe = i;
- if( op==SQLITE_INDEX_CONSTRAINT_LE ) iLe = i;
- if( op==SQLITE_INDEX_CONSTRAINT_GT ) iGe = i;
- if( op==SQLITE_INDEX_CONSTRAINT_GE ) iGe = i;
+ int iCol = pInfo->aConstraint[i].iColumn;
+
+ if( iCol==0 ){
+ if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iEq = i;
+ if( op==SQLITE_INDEX_CONSTRAINT_LT ) iLe = i;
+ if( op==SQLITE_INDEX_CONSTRAINT_LE ) iLe = i;
+ if( op==SQLITE_INDEX_CONSTRAINT_GT ) iGe = i;
+ if( op==SQLITE_INDEX_CONSTRAINT_GE ) iGe = i;
+ }
+ if( iCol==4 ){
+ if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iLangid = i;
+ }
}
}
if( iEq>=0 ){
pInfo->idxNum = FTS4AUX_EQ_CONSTRAINT;
- pInfo->aConstraintUsage[iEq].argvIndex = 1;
+ pInfo->aConstraintUsage[iEq].argvIndex = iNext++;
pInfo->estimatedCost = 5;
}else{
pInfo->idxNum = 0;
pInfo->estimatedCost = 20000;
if( iGe>=0 ){
pInfo->idxNum += FTS4AUX_GE_CONSTRAINT;
- pInfo->aConstraintUsage[iGe].argvIndex = 1;
+ pInfo->aConstraintUsage[iGe].argvIndex = iNext++;
pInfo->estimatedCost /= 2;
}
if( iLe>=0 ){
pInfo->idxNum += FTS4AUX_LE_CONSTRAINT;
- pInfo->aConstraintUsage[iLe].argvIndex = 1 + (iGe>=0);
+ pInfo->aConstraintUsage[iLe].argvIndex = iNext++;
pInfo->estimatedCost /= 2;
}
}
+ if( iLangid>=0 ){
+ pInfo->aConstraintUsage[iLangid].argvIndex = iNext++;
+ pInfo->estimatedCost--;
+ }
return SQLITE_OK;
}
@@ -125006,7 +128102,14 @@ static int fts3auxFilterMethod(
Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor;
Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab;
int rc;
- int isScan;
+ int isScan = 0;
+ int iLangVal = 0; /* Language id to query */
+
+ int iEq = -1; /* Index of term=? value in apVal */
+ int iGe = -1; /* Index of term>=? value in apVal */
+ int iLe = -1; /* Index of term<=? value in apVal */
+ int iLangid = -1; /* Index of languageid=? value in apVal */
+ int iNext = 0;
UNUSED_PARAMETER(nVal);
UNUSED_PARAMETER(idxStr);
@@ -125016,7 +128119,21 @@ static int fts3auxFilterMethod(
|| idxNum==FTS4AUX_LE_CONSTRAINT || idxNum==FTS4AUX_GE_CONSTRAINT
|| idxNum==(FTS4AUX_LE_CONSTRAINT|FTS4AUX_GE_CONSTRAINT)
);
- isScan = (idxNum!=FTS4AUX_EQ_CONSTRAINT);
+
+ if( idxNum==FTS4AUX_EQ_CONSTRAINT ){
+ iEq = iNext++;
+ }else{
+ isScan = 1;
+ if( idxNum & FTS4AUX_GE_CONSTRAINT ){
+ iGe = iNext++;
+ }
+ if( idxNum & FTS4AUX_LE_CONSTRAINT ){
+ iLe = iNext++;
+ }
+ }
+ if( iNext<nVal ){
+ iLangid = iNext++;
+ }
/* In case this cursor is being reused, close and zero it. */
testcase(pCsr->filter.zTerm);
@@ -125028,22 +128145,35 @@ static int fts3auxFilterMethod(
pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY;
if( isScan ) pCsr->filter.flags |= FTS3_SEGMENT_SCAN;
- if( idxNum&(FTS4AUX_EQ_CONSTRAINT|FTS4AUX_GE_CONSTRAINT) ){
+ if( iEq>=0 || iGe>=0 ){
const unsigned char *zStr = sqlite3_value_text(apVal[0]);
+ assert( (iEq==0 && iGe==-1) || (iEq==-1 && iGe==0) );
if( zStr ){
pCsr->filter.zTerm = sqlite3_mprintf("%s", zStr);
pCsr->filter.nTerm = sqlite3_value_bytes(apVal[0]);
if( pCsr->filter.zTerm==0 ) return SQLITE_NOMEM;
}
}
- if( idxNum&FTS4AUX_LE_CONSTRAINT ){
- int iIdx = (idxNum&FTS4AUX_GE_CONSTRAINT) ? 1 : 0;
- pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iIdx]));
- pCsr->nStop = sqlite3_value_bytes(apVal[iIdx]);
+
+ if( iLe>=0 ){
+ pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iLe]));
+ pCsr->nStop = sqlite3_value_bytes(apVal[iLe]);
if( pCsr->zStop==0 ) return SQLITE_NOMEM;
}
+
+ if( iLangid>=0 ){
+ iLangVal = sqlite3_value_int(apVal[iLangid]);
- rc = sqlite3Fts3SegReaderCursor(pFts3, 0, 0, FTS3_SEGCURSOR_ALL,
+ /* If the user specified a negative value for the languageid, use zero
+ ** instead. This works, as the "languageid=?" constraint will also
+ ** be tested by the VDBE layer. The test will always be false (since
+ ** this module will not return a row with a negative languageid), and
+ ** so the overall query will return zero rows. */
+ if( iLangVal<0 ) iLangVal = 0;
+ }
+ pCsr->iLangid = iLangVal;
+
+ rc = sqlite3Fts3SegReaderCursor(pFts3, iLangVal, 0, FTS3_SEGCURSOR_ALL,
pCsr->filter.zTerm, pCsr->filter.nTerm, 0, isScan, &pCsr->csr
);
if( rc==SQLITE_OK ){
@@ -125067,24 +128197,37 @@ static int fts3auxEofMethod(sqlite3_vtab_cursor *pCursor){
*/
static int fts3auxColumnMethod(
sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
- sqlite3_context *pContext, /* Context for sqlite3_result_xxx() calls */
+ sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
int iCol /* Index of column to read value from */
){
Fts3auxCursor *p = (Fts3auxCursor *)pCursor;
assert( p->isEof==0 );
- if( iCol==0 ){ /* Column "term" */
- sqlite3_result_text(pContext, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT);
- }else if( iCol==1 ){ /* Column "col" */
- if( p->iCol ){
- sqlite3_result_int(pContext, p->iCol-1);
- }else{
- sqlite3_result_text(pContext, "*", -1, SQLITE_STATIC);
- }
- }else if( iCol==2 ){ /* Column "documents" */
- sqlite3_result_int64(pContext, p->aStat[p->iCol].nDoc);
- }else{ /* Column "occurrences" */
- sqlite3_result_int64(pContext, p->aStat[p->iCol].nOcc);
+ switch( iCol ){
+ case 0: /* term */
+ sqlite3_result_text(pCtx, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT);
+ break;
+
+ case 1: /* col */
+ if( p->iCol ){
+ sqlite3_result_int(pCtx, p->iCol-1);
+ }else{
+ sqlite3_result_text(pCtx, "*", -1, SQLITE_STATIC);
+ }
+ break;
+
+ case 2: /* documents */
+ sqlite3_result_int64(pCtx, p->aStat[p->iCol].nDoc);
+ break;
+
+ case 3: /* occurrences */
+ sqlite3_result_int64(pCtx, p->aStat[p->iCol].nOcc);
+ break;
+
+ default: /* languageid */
+ assert( iCol==4 );
+ sqlite3_result_int(pCtx, p->iLangid);
+ break;
}
return SQLITE_OK;
@@ -126143,17 +129286,16 @@ SQLITE_PRIVATE int sqlite3Fts3ExprParse(
Fts3Expr **ppExpr, /* OUT: Parsed query structure */
char **pzErr /* OUT: Error message (sqlite3_malloc) */
){
- static const int MAX_EXPR_DEPTH = 12;
int rc = fts3ExprParseUnbalanced(
pTokenizer, iLangid, azCol, bFts4, nCol, iDefaultCol, z, n, ppExpr
);
/* Rebalance the expression. And check that its depth does not exceed
- ** MAX_EXPR_DEPTH. */
+ ** SQLITE_FTS3_MAX_EXPR_DEPTH. */
if( rc==SQLITE_OK && *ppExpr ){
- rc = fts3ExprBalance(ppExpr, MAX_EXPR_DEPTH);
+ rc = fts3ExprBalance(ppExpr, SQLITE_FTS3_MAX_EXPR_DEPTH);
if( rc==SQLITE_OK ){
- rc = fts3ExprCheckDepth(*ppExpr, MAX_EXPR_DEPTH);
+ rc = fts3ExprCheckDepth(*ppExpr, SQLITE_FTS3_MAX_EXPR_DEPTH);
}
}
@@ -126162,7 +129304,8 @@ SQLITE_PRIVATE int sqlite3Fts3ExprParse(
*ppExpr = 0;
if( rc==SQLITE_TOOBIG ){
*pzErr = sqlite3_mprintf(
- "FTS expression tree is too large (maximum depth %d)", MAX_EXPR_DEPTH
+ "FTS expression tree is too large (maximum depth %d)",
+ SQLITE_FTS3_MAX_EXPR_DEPTH
);
rc = SQLITE_ERROR;
}else if( rc==SQLITE_ERROR ){
@@ -127657,7 +130800,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(
#ifdef SQLITE_TEST
-/* #include <tcl.h> */
+#include <tcl.h>
/* #include <string.h> */
/*
@@ -129124,37 +132267,30 @@ static void fts3SqlExec(
/*
-** This function ensures that the caller has obtained a shared-cache
-** table-lock on the %_content table. This is required before reading
-** data from the fts3 table. If this lock is not acquired first, then
-** the caller may end up holding read-locks on the %_segments and %_segdir
-** tables, but no read-lock on the %_content table. If this happens
-** a second connection will be able to write to the fts3 table, but
-** attempting to commit those writes might return SQLITE_LOCKED or
-** SQLITE_LOCKED_SHAREDCACHE (because the commit attempts to obtain
-** write-locks on the %_segments and %_segdir ** tables).
-**
-** We try to avoid this because if FTS3 returns any error when committing
-** a transaction, the whole transaction will be rolled back. And this is
-** not what users expect when they get SQLITE_LOCKED_SHAREDCACHE. It can
-** still happen if the user reads data directly from the %_segments or
-** %_segdir tables instead of going through FTS3 though.
+** This function ensures that the caller has obtained an exclusive
+** shared-cache table-lock on the %_segdir table. This is required before
+** writing data to the fts3 table. If this lock is not acquired first, then
+** the caller may end up attempting to take this lock as part of committing
+** a transaction, causing SQLite to return SQLITE_LOCKED or
+** LOCKED_SHAREDCACHEto a COMMIT command.
**
-** This reasoning does not apply to a content=xxx table.
+** It is best to avoid this because if FTS3 returns any error when
+** committing a transaction, the whole transaction will be rolled back.
+** And this is not what users expect when they get SQLITE_LOCKED_SHAREDCACHE.
+** It can still happen if the user locks the underlying tables directly
+** instead of accessing them via FTS.
*/
-SQLITE_PRIVATE int sqlite3Fts3ReadLock(Fts3Table *p){
- int rc; /* Return code */
- sqlite3_stmt *pStmt; /* Statement used to obtain lock */
-
- if( p->zContentTbl==0 ){
- rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pStmt, 0);
+static int fts3Writelock(Fts3Table *p){
+ int rc = SQLITE_OK;
+
+ if( p->nPendingData==0 ){
+ sqlite3_stmt *pStmt;
+ rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_LEVEL, &pStmt, 0);
if( rc==SQLITE_OK ){
sqlite3_bind_null(pStmt, 1);
sqlite3_step(pStmt);
rc = sqlite3_reset(pStmt);
}
- }else{
- rc = SQLITE_OK;
}
return rc;
@@ -129542,12 +132678,15 @@ static int fts3InsertTerms(
){
int i; /* Iterator variable */
for(i=2; i<p->nColumn+2; i++){
- const char *zText = (const char *)sqlite3_value_text(apVal[i]);
- int rc = fts3PendingTermsAdd(p, iLangid, zText, i-2, &aSz[i-2]);
- if( rc!=SQLITE_OK ){
- return rc;
+ int iCol = i-2;
+ if( p->abNotindexed[iCol]==0 ){
+ const char *zText = (const char *)sqlite3_value_text(apVal[i]);
+ int rc = fts3PendingTermsAdd(p, iLangid, zText, iCol, &aSz[iCol]);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ aSz[p->nColumn] += sqlite3_value_bytes(apVal[i]);
}
- aSz[p->nColumn] += sqlite3_value_bytes(apVal[i]);
}
return SQLITE_OK;
}
@@ -129694,9 +132833,12 @@ static void fts3DeleteTerms(
int iLangid = langidFromSelect(p, pSelect);
rc = fts3PendingTermsDocid(p, iLangid, sqlite3_column_int64(pSelect, 0));
for(i=1; rc==SQLITE_OK && i<=p->nColumn; i++){
- const char *zText = (const char *)sqlite3_column_text(pSelect, i);
- rc = fts3PendingTermsAdd(p, iLangid, zText, -1, &aSz[i-1]);
- aSz[p->nColumn] += sqlite3_column_bytes(pSelect, i);
+ int iCol = i-1;
+ if( p->abNotindexed[iCol]==0 ){
+ const char *zText = (const char *)sqlite3_column_text(pSelect, i);
+ rc = fts3PendingTermsAdd(p, iLangid, zText, -1, &aSz[iCol]);
+ aSz[p->nColumn] += sqlite3_column_bytes(pSelect, i);
+ }
}
if( rc!=SQLITE_OK ){
sqlite3_reset(pSelect);
@@ -130097,7 +133239,7 @@ static int fts3SegReaderNextDocid(
/* The following line of code (and the "p++" below the while() loop) is
** normally all that is required to move pointer p to the desired
** position. The exception is if this node is being loaded from disk
- ** incrementally and pointer "p" now points to the first byte passed
+ ** incrementally and pointer "p" now points to the first byte past
** the populated part of pReader->aNode[].
*/
while( *p | c ) c = *p++ & 0x80;
@@ -131484,8 +134626,8 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(
fts3SegReaderSort(apSegment, nMerge, nMerge, xCmp);
while( apSegment[0]->pOffsetList ){
int j; /* Number of segments that share a docid */
- char *pList;
- int nList;
+ char *pList = 0;
+ int nList = 0;
int nByte;
sqlite3_int64 iDocid = apSegment[0]->iDocid;
fts3SegReaderNextDocid(p, apSegment[0], &pList, &nList);
@@ -131938,9 +135080,11 @@ static int fts3DoRebuild(Fts3Table *p){
rc = fts3PendingTermsDocid(p, iLangid, sqlite3_column_int64(pStmt, 0));
memset(aSz, 0, sizeof(aSz[0]) * (p->nColumn+1));
for(iCol=0; rc==SQLITE_OK && iCol<p->nColumn; iCol++){
- const char *z = (const char *) sqlite3_column_text(pStmt, iCol+1);
- rc = fts3PendingTermsAdd(p, iLangid, z, iCol, &aSz[iCol]);
- aSz[p->nColumn] += sqlite3_column_bytes(pStmt, iCol+1);
+ if( p->abNotindexed[iCol]==0 ){
+ const char *z = (const char *) sqlite3_column_text(pStmt, iCol+1);
+ rc = fts3PendingTermsAdd(p, iLangid, z, iCol, &aSz[iCol]);
+ aSz[p->nColumn] += sqlite3_column_bytes(pStmt, iCol+1);
+ }
}
if( p->bHasDocsize ){
fts3InsertDocsize(&rc, p, aSz);
@@ -133414,7 +136558,7 @@ static int fts3DoAutoincrmerge(
if( rc ) return rc;
}
rc = fts3SqlStmt(p, SQL_REPLACE_STAT, &pStmt, 0);
- if( rc ) return rc;;
+ if( rc ) return rc;
sqlite3_bind_int(pStmt, 1, FTS_STAT_AUTOINCRMERGE);
sqlite3_bind_int(pStmt, 2, p->bAutoincrmerge);
sqlite3_step(pStmt);
@@ -133684,6 +136828,9 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
p->nMaxPendingData = atoi(&zVal[11]);
rc = SQLITE_OK;
+ }else if( nVal>21 && 0==sqlite3_strnicmp(zVal, "test-no-incr-doclist=", 21) ){
+ p->bNoIncrDoclist = atoi(&zVal[21]);
+ rc = SQLITE_OK;
#endif
}else{
rc = SQLITE_ERROR;
@@ -133743,32 +136890,34 @@ SQLITE_PRIVATE int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *pCsr){
iDocid = sqlite3_column_int64(pCsr->pStmt, 0);
for(i=0; i<p->nColumn && rc==SQLITE_OK; i++){
- const char *zText = (const char *)sqlite3_column_text(pCsr->pStmt, i+1);
- sqlite3_tokenizer_cursor *pTC = 0;
-
- rc = sqlite3Fts3OpenTokenizer(pT, pCsr->iLangid, zText, -1, &pTC);
- while( rc==SQLITE_OK ){
- char const *zToken; /* Buffer containing token */
- int nToken = 0; /* Number of bytes in token */
- int iDum1 = 0, iDum2 = 0; /* Dummy variables */
- int iPos = 0; /* Position of token in zText */
-
- rc = pModule->xNext(pTC, &zToken, &nToken, &iDum1, &iDum2, &iPos);
- for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){
- Fts3PhraseToken *pPT = pDef->pToken;
- if( (pDef->iCol>=p->nColumn || pDef->iCol==i)
- && (pPT->bFirst==0 || iPos==0)
- && (pPT->n==nToken || (pPT->isPrefix && pPT->n<nToken))
- && (0==memcmp(zToken, pPT->z, pPT->n))
- ){
- fts3PendingListAppend(&pDef->pList, iDocid, i, iPos, &rc);
+ if( p->abNotindexed[i]==0 ){
+ const char *zText = (const char *)sqlite3_column_text(pCsr->pStmt, i+1);
+ sqlite3_tokenizer_cursor *pTC = 0;
+
+ rc = sqlite3Fts3OpenTokenizer(pT, pCsr->iLangid, zText, -1, &pTC);
+ while( rc==SQLITE_OK ){
+ char const *zToken; /* Buffer containing token */
+ int nToken = 0; /* Number of bytes in token */
+ int iDum1 = 0, iDum2 = 0; /* Dummy variables */
+ int iPos = 0; /* Position of token in zText */
+
+ rc = pModule->xNext(pTC, &zToken, &nToken, &iDum1, &iDum2, &iPos);
+ for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){
+ Fts3PhraseToken *pPT = pDef->pToken;
+ if( (pDef->iCol>=p->nColumn || pDef->iCol==i)
+ && (pPT->bFirst==0 || iPos==0)
+ && (pPT->n==nToken || (pPT->isPrefix && pPT->n<nToken))
+ && (0==memcmp(zToken, pPT->z, pPT->n))
+ ){
+ fts3PendingListAppend(&pDef->pList, iDocid, i, iPos, &rc);
+ }
}
}
+ if( pTC ) pModule->xClose(pTC);
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
}
- if( pTC ) pModule->xClose(pTC);
- if( rc==SQLITE_DONE ) rc = SQLITE_OK;
}
-
+
for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){
if( pDef->pList ){
rc = fts3PendingListAppendVarint(&pDef->pList, 0);
@@ -133932,6 +137081,9 @@ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(
aSzIns = &aSzDel[p->nColumn+1];
memset(aSzDel, 0, sizeof(aSzDel[0])*(p->nColumn+1)*2);
+ rc = fts3Writelock(p);
+ if( rc!=SQLITE_OK ) goto update_out;
+
/* If this is an INSERT operation, or an UPDATE that modifies the rowid
** value, then this operation requires constraint handling.
**
@@ -134551,6 +137703,7 @@ static int fts3StringAppend(
pStr->z = zNew;
pStr->nAlloc = nAlloc;
}
+ assert( pStr->z!=0 && (pStr->nAlloc >= pStr->n+nAppend+1) );
/* Append the data to the string buffer. */
memcpy(&pStr->z[pStr->n], zAppend, nAppend);
@@ -136065,28 +139218,27 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int c){
0x02A97004, 0x02A9DC03, 0x02A9EC01, 0x02AAC001, 0x02AAC803,
0x02AADC02, 0x02AAF802, 0x02AB0401, 0x02AB7802, 0x02ABAC07,
0x02ABD402, 0x02AF8C0B, 0x03600001, 0x036DFC02, 0x036FFC02,
- 0x037FFC02, 0x03E3FC01, 0x03EC7801, 0x03ECA401, 0x03EEC810,
- 0x03F4F802, 0x03F7F002, 0x03F8001A, 0x03F88007, 0x03F8C023,
- 0x03F95013, 0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807,
- 0x03FCEC06, 0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405,
- 0x04040003, 0x0404DC09, 0x0405E411, 0x0406400C, 0x0407402E,
- 0x040E7C01, 0x040F4001, 0x04215C01, 0x04247C01, 0x0424FC01,
- 0x04280403, 0x04281402, 0x04283004, 0x0428E003, 0x0428FC01,
- 0x04294009, 0x0429FC01, 0x042CE407, 0x04400003, 0x0440E016,
- 0x04420003, 0x0442C012, 0x04440003, 0x04449C0E, 0x04450004,
- 0x04460003, 0x0446CC0E, 0x04471404, 0x045AAC0D, 0x0491C004,
- 0x05BD442E, 0x05BE3C04, 0x074000F6, 0x07440027, 0x0744A4B5,
- 0x07480046, 0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01,
- 0x075C5401, 0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401,
- 0x075EA401, 0x075F0C01, 0x07BBC002, 0x07C0002C, 0x07C0C064,
- 0x07C2800F, 0x07C2C40E, 0x07C3040F, 0x07C3440F, 0x07C4401F,
- 0x07C4C03C, 0x07C5C02B, 0x07C7981D, 0x07C8402B, 0x07C90009,
- 0x07C94002, 0x07CC0021, 0x07CCC006, 0x07CCDC46, 0x07CE0014,
- 0x07CE8025, 0x07CF1805, 0x07CF8011, 0x07D0003F, 0x07D10001,
- 0x07D108B6, 0x07D3E404, 0x07D4003E, 0x07D50004, 0x07D54018,
- 0x07D7EC46, 0x07D9140B, 0x07DA0046, 0x07DC0074, 0x38000401,
- 0x38008060, 0x380400F0, 0x3C000001, 0x3FFFF401, 0x40000001,
- 0x43FFF401,
+ 0x037FFC01, 0x03EC7801, 0x03ECA401, 0x03EEC810, 0x03F4F802,
+ 0x03F7F002, 0x03F8001A, 0x03F88007, 0x03F8C023, 0x03F95013,
+ 0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807, 0x03FCEC06,
+ 0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405, 0x04040003,
+ 0x0404DC09, 0x0405E411, 0x0406400C, 0x0407402E, 0x040E7C01,
+ 0x040F4001, 0x04215C01, 0x04247C01, 0x0424FC01, 0x04280403,
+ 0x04281402, 0x04283004, 0x0428E003, 0x0428FC01, 0x04294009,
+ 0x0429FC01, 0x042CE407, 0x04400003, 0x0440E016, 0x04420003,
+ 0x0442C012, 0x04440003, 0x04449C0E, 0x04450004, 0x04460003,
+ 0x0446CC0E, 0x04471404, 0x045AAC0D, 0x0491C004, 0x05BD442E,
+ 0x05BE3C04, 0x074000F6, 0x07440027, 0x0744A4B5, 0x07480046,
+ 0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01, 0x075C5401,
+ 0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401, 0x075EA401,
+ 0x075F0C01, 0x07BBC002, 0x07C0002C, 0x07C0C064, 0x07C2800F,
+ 0x07C2C40E, 0x07C3040F, 0x07C3440F, 0x07C4401F, 0x07C4C03C,
+ 0x07C5C02B, 0x07C7981D, 0x07C8402B, 0x07C90009, 0x07C94002,
+ 0x07CC0021, 0x07CCC006, 0x07CCDC46, 0x07CE0014, 0x07CE8025,
+ 0x07CF1805, 0x07CF8011, 0x07D0003F, 0x07D10001, 0x07D108B6,
+ 0x07D3E404, 0x07D4003E, 0x07D50004, 0x07D54018, 0x07D7EC46,
+ 0x07D9140B, 0x07DA0046, 0x07DC0074, 0x38000401, 0x38008060,
+ 0x380400F0,
};
static const unsigned int aAscii[4] = {
0xFFFFFFFF, 0xFC00FFFF, 0xF8000001, 0xF8000001,
@@ -139681,7 +142833,10 @@ SQLITE_API int sqlite3_rtree_geometry_callback(
}
#if !SQLITE_CORE
-SQLITE_API int sqlite3_extension_init(
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+SQLITE_API int sqlite3_rtree_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
@@ -139719,7 +142874,7 @@ SQLITE_API int sqlite3_extension_init(
** * Implementations of the SQL scalar upper() and lower() functions
** for case mapping.
**
-** * Integration of ICU and SQLite collation seqences.
+** * Integration of ICU and SQLite collation sequences.
**
** * An implementation of the LIKE operator that uses ICU to
** provide case-independent matching.
@@ -140183,7 +143338,10 @@ SQLITE_PRIVATE int sqlite3IcuInit(sqlite3 *db){
}
#if !SQLITE_CORE
-SQLITE_API int sqlite3_extension_init(
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+SQLITE_API int sqlite3_icu_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
diff --git a/src/3rdparty/sqlite/sqlite3.h b/src/3rdparty/sqlite/sqlite3.h
index e398838287..2dd1158d5b 100644
--- a/src/3rdparty/sqlite/sqlite3.h
+++ b/src/3rdparty/sqlite/sqlite3.h
@@ -107,9 +107,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.7.17"
-#define SQLITE_VERSION_NUMBER 3007017
-#define SQLITE_SOURCE_ID "2013-05-20 00:56:22 118a3b35693b134d56ebd780123b7fd6f1497668"
+#define SQLITE_VERSION "3.8.1"
+#define SQLITE_VERSION_NUMBER 3008001
+#define SQLITE_SOURCE_ID "2013-10-17 12:57:35 c78be6d786c19073b3a6730dfe3fb1be54f5657a"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -478,11 +478,15 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8))
#define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23<<8))
#define SQLITE_IOERR_MMAP (SQLITE_IOERR | (24<<8))
+#define SQLITE_IOERR_GETTEMPPATH (SQLITE_IOERR | (25<<8))
+#define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8))
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
+#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8))
#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
+#define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8))
#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
@@ -499,6 +503,7 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8))
#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
+#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
/*
** CAPI3REF: Flags For File Open Operations
@@ -1612,27 +1617,27 @@ struct sqlite3_mem_methods {
** function must be threadsafe. </dd>
**
** [[SQLITE_CONFIG_URI]] <dt>SQLITE_CONFIG_URI
-** <dd> This option takes a single argument of type int. If non-zero, then
+** <dd>^(This option takes a single argument of type int. If non-zero, then
** URI handling is globally enabled. If the parameter is zero, then URI handling
-** is globally disabled. If URI handling is globally enabled, all filenames
+** is globally disabled.)^ ^If URI handling is globally enabled, all filenames
** passed to [sqlite3_open()], [sqlite3_open_v2()], [sqlite3_open16()] or
** specified as part of [ATTACH] commands are interpreted as URIs, regardless
** of whether or not the [SQLITE_OPEN_URI] flag is set when the database
-** connection is opened. If it is globally disabled, filenames are
+** connection is opened. ^If it is globally disabled, filenames are
** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the
-** database connection is opened. By default, URI handling is globally
+** database connection is opened. ^(By default, URI handling is globally
** disabled. The default value may be changed by compiling with the
-** [SQLITE_USE_URI] symbol defined.
+** [SQLITE_USE_URI] symbol defined.)^
**
** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]] <dt>SQLITE_CONFIG_COVERING_INDEX_SCAN
-** <dd> This option takes a single integer argument which is interpreted as
+** <dd>^This option takes a single integer argument which is interpreted as
** a boolean in order to enable or disable the use of covering indices for
-** full table scans in the query optimizer. The default setting is determined
+** full table scans in the query optimizer. ^The default setting is determined
** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on"
** if that compile-time option is omitted.
** The ability to disable the use of covering indices for full table scans
** is because some incorrectly coded legacy applications might malfunction
-** malfunction when the optimization is enabled. Providing the ability to
+** when the optimization is enabled. Providing the ability to
** disable the optimization allows the older, buggy application code to work
** without change even with newer versions of SQLite.
**
@@ -1661,16 +1666,16 @@ struct sqlite3_mem_methods {
**
** [[SQLITE_CONFIG_MMAP_SIZE]]
** <dt>SQLITE_CONFIG_MMAP_SIZE
-** <dd>SQLITE_CONFIG_MMAP_SIZE takes two 64-bit integer (sqlite3_int64) values
+** <dd>^SQLITE_CONFIG_MMAP_SIZE takes two 64-bit integer (sqlite3_int64) values
** that are the default mmap size limit (the default setting for
** [PRAGMA mmap_size]) and the maximum allowed mmap size limit.
-** The default setting can be overridden by each database connection using
+** ^The default setting can be overridden by each database connection using
** either the [PRAGMA mmap_size] command, or by using the
-** [SQLITE_FCNTL_MMAP_SIZE] file control. The maximum allowed mmap size
+** [SQLITE_FCNTL_MMAP_SIZE] file control. ^(The maximum allowed mmap size
** cannot be changed at run-time. Nor may the maximum allowed mmap size
** exceed the compile-time maximum mmap size set by the
-** [SQLITE_MAX_MMAP_SIZE] compile-time option.
-** If either argument to this option is negative, then that argument is
+** [SQLITE_MAX_MMAP_SIZE] compile-time option.)^
+** ^If either argument to this option is negative, then that argument is
** changed to its compile-time default.
** </dl>
*/
@@ -2557,9 +2562,10 @@ SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*,
** interface is to keep a GUI updated during a large query.
**
** ^The parameter P is passed through as the only parameter to the
-** callback function X. ^The parameter N is the number of
+** callback function X. ^The parameter N is the approximate number of
** [virtual machine instructions] that are evaluated between successive
-** invocations of the callback X.
+** invocations of the callback X. ^If N is less than one then the progress
+** handler is disabled.
**
** ^Only a single progress handler may be defined at one time per
** [database connection]; setting a new progress handler cancels the
@@ -4179,41 +4185,49 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
/*
** CAPI3REF: Function Auxiliary Data
**
-** The following two functions may be used by scalar SQL functions to
+** These functions may be used by (non-aggregate) SQL functions to
** associate metadata with argument values. If the same value is passed to
** multiple invocations of the same SQL function during query execution, under
-** some circumstances the associated metadata may be preserved. This may
-** be used, for example, to add a regular-expression matching scalar
-** function. The compiled version of the regular expression is stored as
-** metadata associated with the SQL value passed as the regular expression
-** pattern. The compiled regular expression can be reused on multiple
-** invocations of the same function so that the original pattern string
-** does not need to be recompiled on each invocation.
+** some circumstances the associated metadata may be preserved. An example
+** of where this might be useful is in a regular-expression matching
+** function. The compiled version of the regular expression can be stored as
+** metadata associated with the pattern string.
+** Then as long as the pattern string remains the same,
+** the compiled regular expression can be reused on multiple
+** invocations of the same function.
**
** ^The sqlite3_get_auxdata() interface returns a pointer to the metadata
** associated by the sqlite3_set_auxdata() function with the Nth argument
-** value to the application-defined function. ^If no metadata has been ever
-** been set for the Nth argument of the function, or if the corresponding
-** function parameter has changed since the meta-data was set,
-** then sqlite3_get_auxdata() returns a NULL pointer.
-**
-** ^The sqlite3_set_auxdata() interface saves the metadata
-** pointed to by its 3rd parameter as the metadata for the N-th
-** argument of the application-defined function. Subsequent
-** calls to sqlite3_get_auxdata() might return this data, if it has
-** not been destroyed.
-** ^If it is not NULL, SQLite will invoke the destructor
-** function given by the 4th parameter to sqlite3_set_auxdata() on
-** the metadata when the corresponding function parameter changes
-** or when the SQL statement completes, whichever comes first.
-**
-** SQLite is free to call the destructor and drop metadata on any
-** parameter of any function at any time. ^The only guarantee is that
-** the destructor will be called before the metadata is dropped.
+** value to the application-defined function. ^If there is no metadata
+** associated with the function argument, this sqlite3_get_auxdata() interface
+** returns a NULL pointer.
+**
+** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th
+** argument of the application-defined function. ^Subsequent
+** calls to sqlite3_get_auxdata(C,N) return P from the most recent
+** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or
+** NULL if the metadata has been discarded.
+** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL,
+** SQLite will invoke the destructor function X with parameter P exactly
+** once, when the metadata is discarded.
+** SQLite is free to discard the metadata at any time, including: <ul>
+** <li> when the corresponding function parameter changes, or
+** <li> when [sqlite3_reset()] or [sqlite3_finalize()] is called for the
+** SQL statement, or
+** <li> when sqlite3_set_auxdata() is invoked again on the same parameter, or
+** <li> during the original sqlite3_set_auxdata() call when a memory
+** allocation error occurs. </ul>)^
+**
+** Note the last bullet in particular. The destructor X in
+** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the
+** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata()
+** should be called near the end of the function implementation and the
+** function implementation should not make any use of P after
+** sqlite3_set_auxdata() has been called.
**
** ^(In practice, metadata is preserved between function calls for
-** expressions that are constant at compile time. This includes literal
-** values and [parameters].)^
+** function parameters that are compile-time constants, including literal
+** values and [parameters] and expressions composed from the same.)^
**
** These routines must be called from the same thread in which
** the SQL function is running.
@@ -4518,6 +4532,11 @@ SQLITE_API int sqlite3_key(
sqlite3 *db, /* Database to be rekeyed */
const void *pKey, int nKey /* The key */
);
+SQLITE_API int sqlite3_key_v2(
+ sqlite3 *db, /* Database to be rekeyed */
+ const char *zDbName, /* Name of the database */
+ const void *pKey, int nKey /* The key */
+);
/*
** Change the key on an open database. If the current database is not
@@ -4531,6 +4550,11 @@ SQLITE_API int sqlite3_rekey(
sqlite3 *db, /* Database to be rekeyed */
const void *pKey, int nKey /* The new key */
);
+SQLITE_API int sqlite3_rekey_v2(
+ sqlite3 *db, /* Database to be rekeyed */
+ const char *zDbName, /* Name of the database */
+ const void *pKey, int nKey /* The new key */
+);
/*
** Specify the activation key for a SEE database. Unless
@@ -5116,11 +5140,24 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
** on the list of automatic extensions is a harmless no-op. ^No entry point
** will be called more than once for each database connection that is opened.
**
-** See also: [sqlite3_reset_auto_extension()].
+** See also: [sqlite3_reset_auto_extension()]
+** and [sqlite3_cancel_auto_extension()]
*/
SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void));
/*
+** CAPI3REF: Cancel Automatic Extension Loading
+**
+** ^The [sqlite3_cancel_auto_extension(X)] interface unregisters the
+** initialization routine X that was registered using a prior call to
+** [sqlite3_auto_extension(X)]. ^The [sqlite3_cancel_auto_extension(X)]
+** routine returns 1 if initialization routine X was successfully
+** unregistered and it returns 0 if X was not on the list of initialization
+** routines.
+*/
+SQLITE_API int sqlite3_cancel_auto_extension(void (*xEntryPoint)(void));
+
+/*
** CAPI3REF: Reset Automatic Extension Loading
**
** ^This interface disables all automatic extensions previously
@@ -6232,6 +6269,12 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The
** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0.
** </dd>
+**
+** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt>
+** <dd>This parameter returns zero for the current value if and only if
+** all foreign key constraints (deferred or immediate) have been
+** resolved.)^ ^The highwater mark is always 0.
+** </dd>
** </dl>
*/
#define SQLITE_DBSTATUS_LOOKASIDE_USED 0
@@ -6244,7 +6287,8 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
#define SQLITE_DBSTATUS_CACHE_HIT 7
#define SQLITE_DBSTATUS_CACHE_MISS 8
#define SQLITE_DBSTATUS_CACHE_WRITE 9
-#define SQLITE_DBSTATUS_MAX 9 /* Largest defined DBSTATUS */
+#define SQLITE_DBSTATUS_DEFERRED_FKS 10
+#define SQLITE_DBSTATUS_MAX 10 /* Largest defined DBSTATUS */
/*
@@ -6298,11 +6342,21 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
** A non-zero value in this counter may indicate an opportunity to
** improvement performance by adding permanent indices that do not
** need to be reinitialized each time the statement is run.</dd>
+**
+** [[SQLITE_STMTSTATUS_VM_STEP]] <dt>SQLITE_STMTSTATUS_VM_STEP</dt>
+** <dd>^This is the number of virtual machine operations executed
+** by the prepared statement if that number is less than or equal
+** to 2147483647. The number of virtual machine operations can be
+** used as a proxy for the total work done by the prepared statement.
+** If the number of virtual machine operations exceeds 2147483647
+** then the value returned by this statement status code is undefined.
+** </dd>
** </dl>
*/
#define SQLITE_STMTSTATUS_FULLSCAN_STEP 1
#define SQLITE_STMTSTATUS_SORT 2
#define SQLITE_STMTSTATUS_AUTOINDEX 3
+#define SQLITE_STMTSTATUS_VM_STEP 4
/*
** CAPI3REF: Custom Page Cache Object
@@ -7181,7 +7235,7 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *);
#ifdef __cplusplus
} /* End of the 'extern "C"' block */
#endif
-#endif
+#endif /* _SQLITE3_H_ */
/*
** 2010 August 30
diff --git a/src/angle/patches/0012-ANGLE-Support-WinRT.patch b/src/angle/patches/0012-ANGLE-Support-WinRT.patch
new file mode 100644
index 0000000000..8a5b96c7c0
--- /dev/null
+++ b/src/angle/patches/0012-ANGLE-Support-WinRT.patch
@@ -0,0 +1,1131 @@
+From 67c318c7b9c6d95d3170d11956dbec56494511ca Mon Sep 17 00:00:00 2001
+From: Andrew Knight <andrew.knight@digia.com>
+Date: Tue, 1 Oct 2013 09:43:29 +0300
+Subject: [PATCH] ANGLE: Support WinRT
+
+This enables EGL for WinRT's native types, and adjusts some codepaths
+to accommodate differences in between desktop Windows and WinRT.
+
+- WinRT native handles added to eglplatform.h
+- References to native handles in libEGL/libGLESv2 follow eglplatform.h
+- D3D 11.1 structures and methods used when necessary
+- TLS replaced with thread attribute
+- LocalAlloc/Free replaced with Heap API
+
+Change-Id: Ia90377e700d335a1c569c2145008dd4b0dfd84d3
+Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
+---
+ src/3rdparty/angle/include/EGL/eglplatform.h | 10 ++-
+ src/3rdparty/angle/src/compiler/osinclude.h | 35 ++++------
+ src/3rdparty/angle/src/compiler/ossource_posix.cpp | 8 +++
+ src/3rdparty/angle/src/compiler/ossource_win.cpp | 8 +++
+ src/3rdparty/angle/src/compiler/ossource_winrt.cpp | 75 ++++++++++++++++++++++
+ src/3rdparty/angle/src/libEGL/Display.cpp | 8 ++-
+ src/3rdparty/angle/src/libEGL/Display.h | 4 +-
+ src/3rdparty/angle/src/libEGL/Surface.cpp | 35 +++++++++-
+ src/3rdparty/angle/src/libEGL/Surface.h | 7 +-
+ src/3rdparty/angle/src/libEGL/libEGL.cpp | 4 +-
+ src/3rdparty/angle/src/libEGL/main.cpp | 40 ++++++++++--
+ src/3rdparty/angle/src/libGLESv2/main.cpp | 39 +++++++++--
+ src/3rdparty/angle/src/libGLESv2/precompiled.h | 15 +++++
+ .../angle/src/libGLESv2/renderer/Renderer.cpp | 23 ++++---
+ .../angle/src/libGLESv2/renderer/Renderer.h | 27 +++++++-
+ .../angle/src/libGLESv2/renderer/Renderer11.cpp | 10 ++-
+ .../angle/src/libGLESv2/renderer/Renderer11.h | 2 +-
+ .../angle/src/libGLESv2/renderer/SwapChain.h | 4 +-
+ .../angle/src/libGLESv2/renderer/SwapChain11.cpp | 29 +++++++--
+ .../angle/src/libGLESv2/renderer/SwapChain11.h | 2 +-
+ src/3rdparty/angle/src/libGLESv2/utilities.cpp | 53 +++++++++++++++
+ src/angle/src/common/common.pri | 2 +-
+ src/angle/src/compiler/translator_common.pro | 7 +-
+ src/angle/src/config.pri | 5 +-
+ 24 files changed, 386 insertions(+), 66 deletions(-)
+ create mode 100644 src/3rdparty/angle/src/compiler/ossource_winrt.cpp
+
+diff --git a/src/3rdparty/angle/include/EGL/eglplatform.h b/src/3rdparty/angle/include/EGL/eglplatform.h
+index 34283f2..eb15ae5 100644
+--- a/src/3rdparty/angle/include/EGL/eglplatform.h
++++ b/src/3rdparty/angle/include/EGL/eglplatform.h
+@@ -67,7 +67,15 @@
+ * implementations.
+ */
+
+-#if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */
++#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) /* Windows Runtime */
++
++struct IUnknown;
++
++typedef int EGLNativeDisplayType;
++typedef void *EGLNativePixmapType;
++typedef IUnknown *EGLNativeWindowType;
++
++#elif defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */
+ #ifndef WIN32_LEAN_AND_MEAN
+ #define WIN32_LEAN_AND_MEAN 1
+ #endif
+diff --git a/src/3rdparty/angle/src/compiler/osinclude.h b/src/3rdparty/angle/src/compiler/osinclude.h
+index d8bb1a7..60177d5 100644
+--- a/src/3rdparty/angle/src/compiler/osinclude.h
++++ b/src/3rdparty/angle/src/compiler/osinclude.h
+@@ -13,27 +13,26 @@
+ //
+
+ #if defined(_WIN32) || defined(_WIN64)
++#define STRICT
++#define VC_EXTRALEAN 1
++#include <windows.h>
++#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
++#define ANGLE_OS_WINRT
++#else
+ #define ANGLE_OS_WIN
++#endif
+ #elif defined(__APPLE__) || defined(__linux__) || \
+ defined(__FreeBSD__) || defined(__OpenBSD__) || \
+ defined(__sun) || defined(ANDROID) || \
+ defined(__GLIBC__) || defined(__GNU__) || \
+ defined(__QNX__)
+ #define ANGLE_OS_POSIX
+-#else
+-#error Unsupported platform.
+-#endif
+-
+-#if defined(ANGLE_OS_WIN)
+-#define STRICT
+-#define VC_EXTRALEAN 1
+-#include <windows.h>
+-#elif defined(ANGLE_OS_POSIX)
+ #include <pthread.h>
+ #include <semaphore.h>
+ #include <errno.h>
+-#endif // ANGLE_OS_WIN
+-
++#else
++#error Unsupported platform.
++#endif
+
+ #include "compiler/debug.h"
+
+@@ -43,23 +42,17 @@
+ #if defined(ANGLE_OS_WIN)
+ typedef DWORD OS_TLSIndex;
+ #define OS_INVALID_TLS_INDEX (TLS_OUT_OF_INDEXES)
++#elif defined(ANGLE_OS_WINRT)
++typedef size_t OS_TLSIndex;
++#define OS_INVALID_TLS_INDEX ((DWORD)0xFFFFFF)
+ #elif defined(ANGLE_OS_POSIX)
+ typedef pthread_key_t OS_TLSIndex;
+ #define OS_INVALID_TLS_INDEX (static_cast<OS_TLSIndex>(-1))
+ #endif // ANGLE_OS_WIN
+
+ OS_TLSIndex OS_AllocTLSIndex();
++void *OS_GetTLSValue(OS_TLSIndex nIndex);
+ bool OS_SetTLSValue(OS_TLSIndex nIndex, void *lpvValue);
+ bool OS_FreeTLSIndex(OS_TLSIndex nIndex);
+
+-inline void* OS_GetTLSValue(OS_TLSIndex nIndex)
+-{
+- ASSERT(nIndex != OS_INVALID_TLS_INDEX);
+-#if defined(ANGLE_OS_WIN)
+- return TlsGetValue(nIndex);
+-#elif defined(ANGLE_OS_POSIX)
+- return pthread_getspecific(nIndex);
+-#endif // ANGLE_OS_WIN
+-}
+-
+ #endif // __OSINCLUDE_H
+diff --git a/src/3rdparty/angle/src/compiler/ossource_posix.cpp b/src/3rdparty/angle/src/compiler/ossource_posix.cpp
+index 1e1e699..35510c1 100644
+--- a/src/3rdparty/angle/src/compiler/ossource_posix.cpp
++++ b/src/3rdparty/angle/src/compiler/ossource_posix.cpp
+@@ -33,6 +33,14 @@ OS_TLSIndex OS_AllocTLSIndex()
+ }
+
+
++void *OS_GetTLSValue(OS_TLSIndex nIndex)
++{
++ ASSERT(nIndex != OS_INVALID_TLS_INDEX);
++
++ return pthread_getspecific(nIndex);
++}
++
++
+ bool OS_SetTLSValue(OS_TLSIndex nIndex, void *lpvValue)
+ {
+ if (nIndex == OS_INVALID_TLS_INDEX) {
+diff --git a/src/3rdparty/angle/src/compiler/ossource_win.cpp b/src/3rdparty/angle/src/compiler/ossource_win.cpp
+index 89922fe..708a1ad 100644
+--- a/src/3rdparty/angle/src/compiler/ossource_win.cpp
++++ b/src/3rdparty/angle/src/compiler/ossource_win.cpp
+@@ -29,6 +29,14 @@ OS_TLSIndex OS_AllocTLSIndex()
+ }
+
+
++void *OS_GetTLSValue(OS_TLSIndex nIndex)
++{
++ ASSERT(nIndex != OS_INVALID_TLS_INDEX);
++
++ return TlsGetValue(nIndex);
++}
++
++
+ bool OS_SetTLSValue(OS_TLSIndex nIndex, void *lpvValue)
+ {
+ if (nIndex == OS_INVALID_TLS_INDEX) {
+diff --git a/src/3rdparty/angle/src/compiler/ossource_winrt.cpp b/src/3rdparty/angle/src/compiler/ossource_winrt.cpp
+new file mode 100644
+index 0000000..84443ab
+--- /dev/null
++++ b/src/3rdparty/angle/src/compiler/ossource_winrt.cpp
+@@ -0,0 +1,75 @@
++//
++// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++//
++
++#include "compiler/osinclude.h"
++//
++// This file contains contains Windows Runtime specific functions
++//
++
++#if !defined(ANGLE_OS_WINRT)
++#error Trying to build a WinRT specific file in a non-WinRT build.
++#endif
++
++#include <vector>
++
++
++//
++// Thread Local Storage Operations
++//
++__declspec(thread) std::vector<void *> *tls = nullptr;
++__declspec(thread) std::vector<OS_TLSIndex> *freeIndices = nullptr;
++
++OS_TLSIndex OS_AllocTLSIndex()
++{
++ if (!tls)
++ tls = new std::vector<void*>;
++
++ if (freeIndices && !freeIndices->empty()) {
++ OS_TLSIndex index = freeIndices->back();
++ freeIndices->pop_back();
++ return index;
++ } else {
++ tls->push_back(nullptr);
++ return tls->size() - 1;
++ }
++}
++
++
++void *OS_GetTLSValue(OS_TLSIndex nIndex)
++{
++ ASSERT(nIndex != OS_INVALID_TLS_INDEX);
++ ASSERT(tls);
++
++ return tls->at(nIndex);
++}
++
++
++bool OS_SetTLSValue(OS_TLSIndex nIndex, void *lpvValue)
++{
++ if (!tls || nIndex >= tls->size() || nIndex == OS_INVALID_TLS_INDEX) {
++ ASSERT(0 && "OS_SetTLSValue(): Invalid TLS Index");
++ return false;
++ }
++
++ tls->at(nIndex) = lpvValue;
++ return true;
++}
++
++
++bool OS_FreeTLSIndex(OS_TLSIndex nIndex)
++{
++ if (!tls || nIndex >= tls->size() || nIndex == OS_INVALID_TLS_INDEX) {
++ ASSERT(0 && "OS_SetTLSValue(): Invalid TLS Index");
++ return false;
++ }
++
++ if (!freeIndices)
++ freeIndices = new std::vector<OS_TLSIndex>;
++
++ freeIndices->push_back(nIndex);
++
++ return true;
++}
+diff --git a/src/3rdparty/angle/src/libEGL/Display.cpp b/src/3rdparty/angle/src/libEGL/Display.cpp
+index a382c3b..14973af 100644
+--- a/src/3rdparty/angle/src/libEGL/Display.cpp
++++ b/src/3rdparty/angle/src/libEGL/Display.cpp
+@@ -186,7 +186,7 @@ bool Display::getConfigAttrib(EGLConfig config, EGLint attribute, EGLint *value)
+
+
+
+-EGLSurface Display::createWindowSurface(HWND window, EGLConfig config, const EGLint *attribList)
++EGLSurface Display::createWindowSurface(EGLNativeWindowType window, EGLConfig config, const EGLint *attribList)
+ {
+ const Config *configuration = mConfigSet.get(config);
+ EGLint postSubBufferSupported = EGL_FALSE;
+@@ -456,7 +456,7 @@ bool Display::isValidSurface(egl::Surface *surface)
+ return mSurfaceSet.find(surface) != mSurfaceSet.end();
+ }
+
+-bool Display::hasExistingWindowSurface(HWND window)
++bool Display::hasExistingWindowSurface(EGLNativeWindowType window)
+ {
+ for (SurfaceSet::iterator surface = mSurfaceSet.begin(); surface != mSurfaceSet.end(); surface++)
+ {
+@@ -471,7 +471,6 @@ bool Display::hasExistingWindowSurface(HWND window)
+
+ void Display::initExtensionString()
+ {
+- HMODULE swiftShader = GetModuleHandle(TEXT("swiftshader_d3d9.dll"));
+ bool shareHandleSupported = mRenderer->getShareHandleSupport();
+
+ mExtensionString = "";
+@@ -487,10 +486,13 @@ void Display::initExtensionString()
+
+ mExtensionString += "EGL_ANGLE_query_surface_pointer ";
+
++#if !defined(ANGLE_OS_WINRT)
++ HMODULE swiftShader = GetModuleHandle(TEXT("swiftshader_d3d9.dll"));
+ if (swiftShader)
+ {
+ mExtensionString += "EGL_ANGLE_software_display ";
+ }
++#endif
+
+ if (shareHandleSupported)
+ {
+diff --git a/src/3rdparty/angle/src/libEGL/Display.h b/src/3rdparty/angle/src/libEGL/Display.h
+index 58c3940..5d55410 100644
+--- a/src/3rdparty/angle/src/libEGL/Display.h
++++ b/src/3rdparty/angle/src/libEGL/Display.h
+@@ -40,7 +40,7 @@ class Display
+ bool getConfigs(EGLConfig *configs, const EGLint *attribList, EGLint configSize, EGLint *numConfig);
+ bool getConfigAttrib(EGLConfig config, EGLint attribute, EGLint *value);
+
+- EGLSurface createWindowSurface(HWND window, EGLConfig config, const EGLint *attribList);
++ EGLSurface createWindowSurface(EGLNativeWindowType window, EGLConfig config, const EGLint *attribList);
+ EGLSurface createOffscreenSurface(EGLConfig config, HANDLE shareHandle, const EGLint *attribList);
+ EGLContext createContext(EGLConfig configHandle, const gl::Context *shareContext, bool notifyResets, bool robustAccess);
+
+@@ -51,7 +51,7 @@ class Display
+ bool isValidConfig(EGLConfig config);
+ bool isValidContext(gl::Context *context);
+ bool isValidSurface(egl::Surface *surface);
+- bool hasExistingWindowSurface(HWND window);
++ bool hasExistingWindowSurface(EGLNativeWindowType window);
+
+ rx::Renderer *getRenderer() { return mRenderer; };
+
+diff --git a/src/3rdparty/angle/src/libEGL/Surface.cpp b/src/3rdparty/angle/src/libEGL/Surface.cpp
+index b47a7bc..abc6d7d 100644
+--- a/src/3rdparty/angle/src/libEGL/Surface.cpp
++++ b/src/3rdparty/angle/src/libEGL/Surface.cpp
+@@ -20,10 +20,15 @@
+ #include "libEGL/main.h"
+ #include "libEGL/Display.h"
+
++#if defined(ANGLE_OS_WINRT)
++#include <windows.foundation.h>
++#include <windows.ui.core.h>
++#endif
++
+ namespace egl
+ {
+
+-Surface::Surface(Display *display, const Config *config, HWND window, EGLint postSubBufferSupported)
++Surface::Surface(Display *display, const Config *config, EGLNativeWindowType window, EGLint postSubBufferSupported)
+ : mDisplay(display), mConfig(config), mWindow(window), mPostSubBufferSupported(postSubBufferSupported)
+ {
+ mRenderer = mDisplay->getRenderer();
+@@ -96,6 +101,7 @@ bool Surface::resetSwapChain()
+
+ if (mWindow)
+ {
++#if !defined(ANGLE_OS_WINRT)
+ RECT windowRect;
+ if (!GetClientRect(getWindowHandle(), &windowRect))
+ {
+@@ -107,6 +113,14 @@ bool Surface::resetSwapChain()
+
+ width = windowRect.right - windowRect.left;
+ height = windowRect.bottom - windowRect.top;
++#else
++ ABI::Windows::Foundation::Rect windowRect;
++ ABI::Windows::UI::Core::ICoreWindow *window;
++ ASSERT(SUCCEEDED(mWindow->QueryInterface(IID_PPV_ARGS(&window))));
++ window->get_Bounds(&windowRect);
++ width = windowRect.Width;
++ height = windowRect.Height;
++#endif
+ }
+ else
+ {
+@@ -226,7 +240,7 @@ bool Surface::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
+ return true;
+ }
+
+-HWND Surface::getWindowHandle()
++EGLNativeWindowType Surface::getWindowHandle()
+ {
+ return mWindow;
+ }
+@@ -235,6 +249,7 @@ HWND Surface::getWindowHandle()
+ #define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
+ #define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
+
++#if !defined(ANGLE_OS_WINRT)
+ static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
+ {
+ if (message == WM_SIZE)
+@@ -248,9 +263,13 @@ static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam
+ WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc));
+ return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam);
+ }
++#endif
+
+ void Surface::subclassWindow()
+ {
++#if defined(ANGLE_OS_WINRT)
++ mWindowSubclassed = false;
++#else
+ if (!mWindow)
+ {
+ return;
+@@ -274,10 +293,12 @@ void Surface::subclassWindow()
+ SetProp(mWindow, kSurfaceProperty, reinterpret_cast<HANDLE>(this));
+ SetProp(mWindow, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc));
+ mWindowSubclassed = true;
++#endif
+ }
+
+ void Surface::unsubclassWindow()
+ {
++#if !defined(ANGLE_OS_WINRT)
+ if(!mWindowSubclassed)
+ {
+ return;
+@@ -300,10 +321,12 @@ void Surface::unsubclassWindow()
+ RemoveProp(mWindow, kSurfaceProperty);
+ RemoveProp(mWindow, kParentWndProc);
+ mWindowSubclassed = false;
++#endif
+ }
+
+ bool Surface::checkForOutOfDateSwapChain()
+ {
++#if !defined(ANGLE_OS_WINRT)
+ RECT client;
+ if (!GetClientRect(getWindowHandle(), &client))
+ {
+@@ -314,6 +337,14 @@ bool Surface::checkForOutOfDateSwapChain()
+ // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information.
+ int clientWidth = client.right - client.left;
+ int clientHeight = client.bottom - client.top;
++#else
++ ABI::Windows::Foundation::Rect windowRect;
++ ABI::Windows::UI::Core::ICoreWindow *window;
++ ASSERT(SUCCEEDED(mWindow->QueryInterface(IID_PPV_ARGS(&window))));
++ window->get_Bounds(&windowRect);
++ int clientWidth = windowRect.Width;
++ int clientHeight = windowRect.Height;
++#endif
+ bool sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
+
+ if (mSwapIntervalDirty)
+diff --git a/src/3rdparty/angle/src/libEGL/Surface.h b/src/3rdparty/angle/src/libEGL/Surface.h
+index 938b800..ae9a380 100644
+--- a/src/3rdparty/angle/src/libEGL/Surface.h
++++ b/src/3rdparty/angle/src/libEGL/Surface.h
+@@ -15,6 +15,7 @@
+ #include <EGL/egl.h>
+
+ #include "common/angleutils.h"
++#include "windows.h"
+
+ namespace gl
+ {
+@@ -34,7 +35,7 @@ class Config;
+ class Surface
+ {
+ public:
+- Surface(Display *display, const egl::Config *config, HWND window, EGLint postSubBufferSupported);
++ Surface(Display *display, const egl::Config *config, EGLNativeWindowType window, EGLint postSubBufferSupported);
+ Surface(Display *display, const egl::Config *config, HANDLE shareHandle, EGLint width, EGLint height, EGLenum textureFormat, EGLenum textureTarget);
+
+ ~Surface();
+@@ -43,7 +44,7 @@ class Surface
+ void release();
+ bool resetSwapChain();
+
+- HWND getWindowHandle();
++ EGLNativeWindowType getWindowHandle();
+ bool swap();
+ bool postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height);
+
+@@ -79,7 +80,7 @@ private:
+ bool resetSwapChain(int backbufferWidth, int backbufferHeight);
+ bool swapRect(EGLint x, EGLint y, EGLint width, EGLint height);
+
+- const HWND mWindow; // Window that the surface is created for.
++ const EGLNativeWindowType mWindow; // Window that the surface is created for.
+ bool mWindowSubclassed; // Indicates whether we successfully subclassed mWindow for WM_RESIZE hooking
+ const egl::Config *mConfig; // EGL config surface was created with
+ EGLint mHeight; // Height of surface
+diff --git a/src/3rdparty/angle/src/libEGL/libEGL.cpp b/src/3rdparty/angle/src/libEGL/libEGL.cpp
+index 6e10c39..5bcb5d5 100644
+--- a/src/3rdparty/angle/src/libEGL/libEGL.cpp
++++ b/src/3rdparty/angle/src/libEGL/libEGL.cpp
+@@ -308,14 +308,16 @@ EGLSurface __stdcall eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EG
+ return EGL_NO_SURFACE;
+ }
+
++#if !defined(ANGLE_OS_WINRT)
+ HWND window = (HWND)win;
+
+ if (!IsWindow(window))
+ {
+ return egl::error(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+ }
++#endif
+
+- return display->createWindowSurface(window, config, attrib_list);
++ return display->createWindowSurface(win, config, attrib_list);
+ }
+ catch(std::bad_alloc&)
+ {
+diff --git a/src/3rdparty/angle/src/libEGL/main.cpp b/src/3rdparty/angle/src/libEGL/main.cpp
+index 7dea5fc..964b4b2 100644
+--- a/src/3rdparty/angle/src/libEGL/main.cpp
++++ b/src/3rdparty/angle/src/libEGL/main.cpp
+@@ -1,3 +1,4 @@
++#include "../libGLESv2/precompiled.h"
+ //
+ // Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved.
+ // Use of this source code is governed by a BSD-style license that can be
+@@ -12,7 +13,13 @@
+
+ #ifndef QT_OPENGL_ES_2_ANGLE_STATIC
+
++#if !defined(ANGLE_OS_WINRT)
+ static DWORD currentTLS = TLS_OUT_OF_INDEXES;
++#else
++static __declspec(thread) void *currentTLS = 0;
++#endif
++
++namespace egl { Current *getCurrent(); }
+
+ extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
+ {
+@@ -35,22 +42,25 @@ extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved
+ }
+ #endif
+
++#if !defined(ANGLE_OS_WINRT)
+ currentTLS = TlsAlloc();
+
+ if (currentTLS == TLS_OUT_OF_INDEXES)
+ {
+ return FALSE;
+ }
++#endif
+ }
+ // Fall throught to initialize index
+ case DLL_THREAD_ATTACH:
+ {
+- egl::Current *current = (egl::Current*)LocalAlloc(LPTR, sizeof(egl::Current));
++ egl::Current *current = egl::getCurrent();
+
+ if (current)
+ {
++#if !defined(ANGLE_OS_WINRT)
+ TlsSetValue(currentTLS, current);
+-
++#endif
+ current->error = EGL_SUCCESS;
+ current->API = EGL_OPENGL_ES_API;
+ current->display = EGL_NO_DISPLAY;
+@@ -61,24 +71,35 @@ extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved
+ break;
+ case DLL_THREAD_DETACH:
+ {
+- void *current = TlsGetValue(currentTLS);
++ egl::Current *current = egl::getCurrent();
+
+ if (current)
+ {
++#if !defined(ANGLE_OS_WINRT)
+ LocalFree((HLOCAL)current);
++#else
++ HeapFree(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, current);
++ currentTLS = 0;
++#endif
+ }
+ }
+ break;
+ case DLL_PROCESS_DETACH:
+ {
+- void *current = TlsGetValue(currentTLS);
++ egl::Current *current = egl::getCurrent();
+
+ if (current)
+ {
++#if !defined(ANGLE_OS_WINRT)
+ LocalFree((HLOCAL)current);
+ }
+
+ TlsFree(currentTLS);
++#else
++ HeapFree(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, current);
++ currentTLS = 0;
++ }
++#endif
+ }
+ break;
+ default:
+@@ -95,7 +116,16 @@ namespace egl
+ Current *getCurrent()
+ {
+ #ifndef QT_OPENGL_ES_2_ANGLE_STATIC
+- return (Current*)TlsGetValue(currentTLS);
++#if !defined(ANGLE_OS_WINRT)
++ Current *current = (Current*)TlsGetValue(currentTLS);
++ if (!current)
++ current = (Current*)LocalAlloc(LPTR, sizeof(Current));
++ return current;
++#else
++ if (!currentTLS)
++ currentTLS = HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS|HEAP_ZERO_MEMORY, sizeof(Current));
++ return (Current*)currentTLS;
++#endif
+ #else
+ // No precautions for thread safety taken as ANGLE is used single-threaded in Qt.
+ static Current curr = { EGL_SUCCESS, EGL_OPENGL_ES_API, EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE };
+diff --git a/src/3rdparty/angle/src/libGLESv2/main.cpp b/src/3rdparty/angle/src/libGLESv2/main.cpp
+index 730a6ac..defdf35 100644
+--- a/src/3rdparty/angle/src/libGLESv2/main.cpp
++++ b/src/3rdparty/angle/src/libGLESv2/main.cpp
+@@ -13,7 +13,13 @@
+
+ #ifndef QT_OPENGL_ES_2_ANGLE_STATIC
+
++#if !defined(ANGLE_OS_WINRT)
+ static DWORD currentTLS = TLS_OUT_OF_INDEXES;
++#else
++static __declspec(thread) void *currentTLS = 0;
++#endif
++
++namespace gl { Current *getCurrent(); }
+
+ extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
+ {
+@@ -21,22 +27,25 @@ extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved
+ {
+ case DLL_PROCESS_ATTACH:
+ {
++#if !defined(ANGLE_OS_WINRT)
+ currentTLS = TlsAlloc();
+
+ if (currentTLS == TLS_OUT_OF_INDEXES)
+ {
+ return FALSE;
+ }
++#endif
+ }
+ // Fall throught to initialize index
+ case DLL_THREAD_ATTACH:
+ {
+- gl::Current *current = (gl::Current*)LocalAlloc(LPTR, sizeof(gl::Current));
++ gl::Current *current = gl::getCurrent();
+
+ if (current)
+ {
++#if !defined(ANGLE_OS_WINRT)
+ TlsSetValue(currentTLS, current);
+-
++#endif
+ current->context = NULL;
+ current->display = NULL;
+ }
+@@ -44,24 +53,35 @@ extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved
+ break;
+ case DLL_THREAD_DETACH:
+ {
+- void *current = TlsGetValue(currentTLS);
++ gl::Current *current = gl::getCurrent();
+
+ if (current)
+ {
++#if !defined(ANGLE_OS_WINRT)
+ LocalFree((HLOCAL)current);
++#else
++ HeapFree(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, current);
++ currentTLS = 0;
++#endif
+ }
+ }
+ break;
+ case DLL_PROCESS_DETACH:
+ {
+- void *current = TlsGetValue(currentTLS);
++ gl::Current *current = gl::getCurrent();
+
+ if (current)
+ {
++#if !defined(ANGLE_OS_WINRT)
+ LocalFree((HLOCAL)current);
+ }
+
+ TlsFree(currentTLS);
++#else
++ HeapFree(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, current);
++ currentTLS = 0;
++ }
++#endif
+ }
+ break;
+ default:
+@@ -78,7 +98,16 @@ namespace gl
+ Current *getCurrent()
+ {
+ #ifndef QT_OPENGL_ES_2_ANGLE_STATIC
+- return (Current*)TlsGetValue(currentTLS);
++#if !defined(ANGLE_OS_WINRT)
++ Current *current = (Current*)TlsGetValue(currentTLS);
++ if (!current)
++ current = (Current*)LocalAlloc(LPTR, sizeof(Current));
++ return current;
++#else
++ if (!currentTLS)
++ currentTLS = HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS|HEAP_ZERO_MEMORY, sizeof(Current));
++ return (Current*)currentTLS;
++#endif
+ #else
+ // No precautions for thread safety taken as ANGLE is used single-threaded in Qt.
+ static gl::Current curr = { 0, 0 };
+diff --git a/src/3rdparty/angle/src/libGLESv2/precompiled.h b/src/3rdparty/angle/src/libGLESv2/precompiled.h
+index 50dec6b..823d27b 100644
+--- a/src/3rdparty/angle/src/libGLESv2/precompiled.h
++++ b/src/3rdparty/angle/src/libGLESv2/precompiled.h
+@@ -32,13 +32,28 @@
+ #include <unordered_map>
+ #include <vector>
+
++#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
++#define ANGLE_OS_WINRT
++#if WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP
++#define ANGLE_OS_WINPHONE
++#endif
++#endif
++
+ #ifndef ANGLE_ENABLE_D3D11
+ #include <d3d9.h>
+ #else
++#if !defined(ANGLE_OS_WINRT)
+ #include <D3D11.h>
++#else
++#include <d3d11_1.h>
++#define Sleep(x) WaitForSingleObjectEx(GetCurrentThread(), x, FALSE)
++#define GetVersion() WINVER
++#endif
+ #include <dxgi.h>
+ #endif
++#ifndef ANGLE_OS_WINPHONE
+ #include <D3Dcompiler.h>
++#endif
+
+ #ifdef _MSC_VER
+ #include <hash_map>
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/Renderer.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/Renderer.cpp
+index 21ad223..7ba183d 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/Renderer.cpp
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/Renderer.cpp
+@@ -28,13 +28,18 @@
+ #define D3DERR_OUTOFVIDEOMEMORY MAKE_HRESULT(1, 0x876, 380)
+ #endif
+
+-#ifdef __MINGW32__
+-
+ #ifndef D3DCOMPILER_DLL
++#ifndef ANGLE_OS_WINPHONE
++#define D3DCOMPILER_DLL L"d3dcompiler_43.dll" // Lowest common denominator
++#else
++#define D3DCOMPILER_DLL L"qtd3dcompiler.dll" // Placeholder DLL for phone
++#endif // ANGLE_OS_WINPHONE
++#endif // D3DCOMPILER_DLL
+
+-//Add define + typedefs for older MinGW-w64 headers (pre 5783)
++#if defined(__MINGW32__) || defined(ANGLE_OS_WINPHONE)
+
+-#define D3DCOMPILER_DLL L"d3dcompiler_43.dll"
++//Add define + typedefs for older MinGW-w64 headers (pre 5783)
++//Also define these on Windows Phone, which doesn't have a shader compiler
+
+ HRESULT WINAPI D3DCompile(const void *data, SIZE_T data_size, const char *filename,
+ const D3D_SHADER_MACRO *defines, ID3DInclude *include, const char *entrypoint,
+@@ -43,9 +48,7 @@ typedef HRESULT (WINAPI *pD3DCompile)(const void *data, SIZE_T data_size, const
+ const D3D_SHADER_MACRO *defines, ID3DInclude *include, const char *entrypoint,
+ const char *target, UINT sflags, UINT eflags, ID3DBlob **shader, ID3DBlob **error_messages);
+
+-#endif // D3DCOMPILER_DLL
+-
+-#endif // __MINGW32__
++#endif // __MINGW32__ || ANGLE_OS_WINPHONE
+
+ namespace rx
+ {
+@@ -81,7 +84,11 @@ bool Renderer::initializeCompiler()
+ }
+ #else
+ // Load the version of the D3DCompiler DLL associated with the Direct3D version ANGLE was built with.
++#if !defined(ANGLE_OS_WINRT)
+ mD3dCompilerModule = LoadLibrary(D3DCOMPILER_DLL);
++#else
++ mD3dCompilerModule = LoadPackagedLibrary(D3DCOMPILER_DLL, NULL);
++#endif
+ #endif // ANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES
+
+ if (!mD3dCompilerModule)
+@@ -225,4 +232,4 @@ void glDestroyRenderer(rx::Renderer *renderer)
+ delete renderer;
+ }
+
+-}
+\ No newline at end of file
++}
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/Renderer.h b/src/3rdparty/angle/src/libGLESv2/renderer/Renderer.h
+index 04e877b..ac67c27 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/Renderer.h
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/Renderer.h
+@@ -1,3 +1,4 @@
++#include "../precompiled.h"
+ //
+ // Copyright (c) 2012-2013 The ANGLE Project Authors. All rights reserved.
+ // Use of this source code is governed by a BSD-style license that can be
+@@ -13,6 +14,30 @@
+ #include "libGLESv2/Uniform.h"
+ #include "libGLESv2/angletypes.h"
+
++#ifndef D3DCOMPILE_OPTIMIZATION_LEVEL0
++#define D3DCOMPILE_OPTIMIZATION_LEVEL0 (1 << 14)
++#endif
++#ifndef D3DCOMPILE_OPTIMIZATION_LEVEL1
++#define D3DCOMPILE_OPTIMIZATION_LEVEL1 0
++#endif
++#ifndef D3DCOMPILE_OPTIMIZATION_LEVEL2
++#define D3DCOMPILE_OPTIMIZATION_LEVEL2 ((1 << 14) | (1 << 15))
++#endif
++#ifndef D3DCOMPILE_OPTIMIZATION_LEVEL3
++#define D3DCOMPILE_OPTIMIZATION_LEVEL3 (1 << 15)
++#endif
++#ifndef D3DCOMPILE_DEBUG
++#define D3DCOMPILE_DEBUG (1 << 0)
++#endif
++#ifndef D3DCOMPILE_SKIP_OPTIMIZATION
++#define D3DCOMPILE_SKIP_OPTIMIZATION (1 << 2)
++#endif
++#ifndef D3DCOMPILE_AVOID_FLOW_CONTROL
++#define D3DCOMPILE_AVOID_FLOW_CONTROL (1 << 9)
++#endif
++#ifndef D3DCOMPILE_PREFER_FLOW_CONTROL
++#define D3DCOMPILE_PREFER_FLOW_CONTROL (1 << 10)
++#endif
+ #if !defined(ANGLE_COMPILE_OPTIMIZATION_LEVEL)
+ #define ANGLE_COMPILE_OPTIMIZATION_LEVEL D3DCOMPILE_OPTIMIZATION_LEVEL3
+ #endif
+@@ -107,7 +132,7 @@ class Renderer
+
+ virtual void sync(bool block) = 0;
+
+- virtual SwapChain *createSwapChain(HWND window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat) = 0;
++ virtual SwapChain *createSwapChain(EGLNativeWindowType window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat) = 0;
+
+ virtual void setSamplerState(gl::SamplerType type, int index, const gl::SamplerState &sampler) = 0;
+ virtual void setTexture(gl::SamplerType type, int index, gl::Texture *texture) = 0;
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.cpp
+index a431018..d04467b 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.cpp
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.cpp
+@@ -137,6 +137,7 @@ EGLint Renderer11::initialize()
+ return EGL_NOT_INITIALIZED;
+ }
+
++#if !defined(ANGLE_OS_WINRT)
+ mDxgiModule = LoadLibrary(TEXT("dxgi.dll"));
+ mD3d11Module = LoadLibrary(TEXT("d3d11.dll"));
+
+@@ -155,6 +156,7 @@ EGLint Renderer11::initialize()
+ ERR("Could not retrieve D3D11CreateDevice address - aborting!\n");
+ return EGL_NOT_INITIALIZED;
+ }
++#endif
+
+ D3D_FEATURE_LEVEL featureLevels[] =
+ {
+@@ -203,8 +205,12 @@ EGLint Renderer11::initialize()
+ }
+ }
+
++#if !defined(ANGLE_OS_WINRT)
+ IDXGIDevice *dxgiDevice = NULL;
+- result = mDevice->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice);
++#else
++ IDXGIDevice1 *dxgiDevice = NULL;
++#endif
++ result = mDevice->QueryInterface(IID_PPV_ARGS(&dxgiDevice));
+
+ if (FAILED(result))
+ {
+@@ -524,7 +530,7 @@ void Renderer11::sync(bool block)
+ }
+ }
+
+-SwapChain *Renderer11::createSwapChain(HWND window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat)
++SwapChain *Renderer11::createSwapChain(EGLNativeWindowType window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat)
+ {
+ return new rx::SwapChain11(this, window, shareHandle, backBufferFormat, depthBufferFormat);
+ }
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.h b/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.h
+index f024855..a7f5a39 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.h
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.h
+@@ -52,7 +52,7 @@ class Renderer11 : public Renderer
+
+ virtual void sync(bool block);
+
+- virtual SwapChain *createSwapChain(HWND window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat);
++ virtual SwapChain *createSwapChain(EGLNativeWindowType window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat);
+
+ virtual void setSamplerState(gl::SamplerType type, int index, const gl::SamplerState &sampler);
+ virtual void setTexture(gl::SamplerType type, int index, gl::Texture *texture);
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain.h b/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain.h
+index 14c0515..a6870eb 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain.h
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain.h
+@@ -18,7 +18,7 @@ namespace rx
+ class SwapChain
+ {
+ public:
+- SwapChain(HWND window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat)
++ SwapChain(EGLNativeWindowType window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat)
+ : mWindow(window), mShareHandle(shareHandle), mBackBufferFormat(backBufferFormat), mDepthBufferFormat(depthBufferFormat)
+ {
+ }
+@@ -33,7 +33,7 @@ class SwapChain
+ virtual HANDLE getShareHandle() {return mShareHandle;};
+
+ protected:
+- const HWND mWindow; // Window that the surface is created for.
++ const EGLNativeWindowType mWindow; // Window that the surface is created for.
+ const GLenum mBackBufferFormat;
+ const GLenum mDepthBufferFormat;
+
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.cpp
+index 0da58cb..0797fd7 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.cpp
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.cpp
+@@ -17,7 +17,7 @@
+ namespace rx
+ {
+
+-SwapChain11::SwapChain11(Renderer11 *renderer, HWND window, HANDLE shareHandle,
++SwapChain11::SwapChain11(Renderer11 *renderer, EGLNativeWindowType window, HANDLE shareHandle,
+ GLenum backBufferFormat, GLenum depthBufferFormat)
+ : mRenderer(renderer), SwapChain(window, shareHandle, backBufferFormat, depthBufferFormat)
+ {
+@@ -468,6 +468,7 @@ EGLint SwapChain11::reset(int backbufferWidth, int backbufferHeight, EGLint swap
+
+ if (mWindow)
+ {
++#if !defined(ANGLE_OS_WINRT)
+ // We cannot create a swap chain for an HWND that is owned by a different process
+ DWORD currentProcessId = GetCurrentProcessId();
+ DWORD wndProcessId;
+@@ -491,14 +492,34 @@ EGLint SwapChain11::reset(int backbufferWidth, int backbufferHeight, EGLint swap
+ swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
+ swapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
+ swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
++ swapChainDesc.Windowed = TRUE;
++ swapChainDesc.OutputWindow = mWindow;
++#else
++ IDXGIFactory2 *factory;
++ HRESULT result = mRenderer->getDxgiFactory()->QueryInterface(IID_PPV_ARGS(&factory));
++ ASSERT(SUCCEEDED(result));
++
++ DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
++ swapChainDesc.BufferCount = 2;
++ swapChainDesc.Format = gl_d3d11::ConvertRenderbufferFormat(mBackBufferFormat);
++ swapChainDesc.Width = backbufferWidth;
++ swapChainDesc.Height = backbufferHeight;
++ swapChainDesc.Stereo = FALSE;
++ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
++#endif
++
+ swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ swapChainDesc.Flags = 0;
+- swapChainDesc.OutputWindow = mWindow;
+ swapChainDesc.SampleDesc.Count = 1;
+ swapChainDesc.SampleDesc.Quality = 0;
+- swapChainDesc.Windowed = TRUE;
+
+- HRESULT result = factory->CreateSwapChain(device, &swapChainDesc, &mSwapChain);
++#if !defined(ANGLE_OS_WINRT)
++ result = factory->CreateSwapChain(device, &swapChainDesc, &mSwapChain);
++#else
++ IDXGISwapChain1 *swapChain;
++ result = factory->CreateSwapChainForCoreWindow(device, mWindow, &swapChainDesc, NULL, &swapChain);
++ mSwapChain = swapChain;
++#endif
+
+ if (FAILED(result))
+ {
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.h b/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.h
+index 8001046..2a030c8 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.h
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.h
+@@ -19,7 +19,7 @@ class Renderer11;
+ class SwapChain11 : public SwapChain
+ {
+ public:
+- SwapChain11(Renderer11 *renderer, HWND window, HANDLE shareHandle,
++ SwapChain11(Renderer11 *renderer, EGLNativeWindowType window, HANDLE shareHandle,
+ GLenum backBufferFormat, GLenum depthBufferFormat);
+ virtual ~SwapChain11();
+
+diff --git a/src/3rdparty/angle/src/libGLESv2/utilities.cpp b/src/3rdparty/angle/src/libGLESv2/utilities.cpp
+index 32df49e..8fd193b 100644
+--- a/src/3rdparty/angle/src/libGLESv2/utilities.cpp
++++ b/src/3rdparty/angle/src/libGLESv2/utilities.cpp
+@@ -10,6 +10,14 @@
+ #include "libGLESv2/utilities.h"
+ #include "libGLESv2/mathutil.h"
+
++#if defined(ANGLE_OS_WINRT)
++#include <locale>
++#include <codecvt>
++#include <wrl.h>
++#include <windows.storage.h>
++using namespace ABI::Windows::Storage;
++#endif
++
+ namespace gl
+ {
+
+@@ -737,7 +745,50 @@ bool IsTriangleMode(GLenum drawMode)
+
+ std::string getTempPath()
+ {
++#if defined(ANGLE_OS_WINRT)
++
++ static std::string path;
++
++ while (path.empty()) {
++ IApplicationDataStatics *applicationDataFactory;
++ HRESULT result = RoGetActivationFactory(Microsoft::WRL::Wrappers::HStringReference(RuntimeClass_Windows_Storage_ApplicationData).Get(),
++ IID_PPV_ARGS(&applicationDataFactory));
++ if (FAILED(result))
++ break;
++
++ IApplicationData *applicationData;
++ result = applicationDataFactory->get_Current(&applicationData);
++ if (FAILED(result))
++ break;
++
++ IStorageFolder *storageFolder;
++ result = applicationData->get_LocalFolder(&storageFolder);
++ if (FAILED(result))
++ break;
++
++ IStorageItem *localFolder;
++ result = storageFolder->QueryInterface(IID_PPV_ARGS(&localFolder));
++ if (FAILED(result))
++ break;
++
++ HSTRING localFolderPath;
++ result = localFolder->get_Path(&localFolderPath);
++ if (FAILED(result))
++ break;
++
++ std::wstring_convert< std::codecvt_utf8<wchar_t> > converter;
++ path = converter.to_bytes(WindowsGetStringRawBuffer(localFolderPath, NULL));
++ if (path.empty())
++ {
++ UNREACHABLE();
++ break;
++ }
++ }
++
++#else
++
+ char path[MAX_PATH];
++
+ DWORD pathLen = GetTempPathA(sizeof(path) / sizeof(path[0]), path);
+ if (pathLen == 0)
+ {
+@@ -751,6 +802,8 @@ std::string getTempPath()
+ UNREACHABLE();
+ return std::string();
+ }
++
++#endif
+
+ return path;
+ }
+diff --git a/src/angle/src/common/common.pri b/src/angle/src/common/common.pri
+index a94b9a6..12e26a9 100644
+--- a/src/angle/src/common/common.pri
++++ b/src/angle/src/common/common.pri
+@@ -7,7 +7,7 @@ INCLUDEPATH += \
+ LIBS = $$QMAKE_LIBS_CORE $$QMAKE_LIBS_GUI
+
+ # DirectX is included in the Windows 8 Kit, but everything else requires the DX SDK.
+-win32-msvc2012 {
++win32-msvc2012|winrt {
+ FXC = fxc.exe
+ } else {
+ DX_DIR = $$(DXSDK_DIR)
+diff --git a/src/angle/src/compiler/translator_common.pro b/src/angle/src/compiler/translator_common.pro
+index b281215..5581c9d 100644
+--- a/src/angle/src/compiler/translator_common.pro
++++ b/src/angle/src/compiler/translator_common.pro
+@@ -78,7 +78,6 @@ SOURCES += \
+ $$ANGLE_DIR/src/compiler/intermOut.cpp \
+ $$ANGLE_DIR/src/compiler/IntermTraverse.cpp \
+ $$ANGLE_DIR/src/compiler/MapLongVariableNames.cpp \
+- $$ANGLE_DIR/src/compiler/ossource_win.cpp \
+ $$ANGLE_DIR/src/compiler/parseConst.cpp \
+ $$ANGLE_DIR/src/compiler/ParseHelper.cpp \
+ $$ANGLE_DIR/src/compiler/PoolAlloc.cpp \
+@@ -98,6 +97,12 @@ SOURCES += \
+ $$ANGLE_DIR/src/compiler/timing/RestrictVertexShaderTiming.cpp \
+ $$ANGLE_DIR/src/third_party/compiler/ArrayBoundsClamper.cpp
+
++winrt {
++ SOURCES += $$ANGLE_DIR/src/compiler/ossource_winrt.cpp
++} else {
++ SOURCES += $$ANGLE_DIR/src/compiler/ossource_win.cpp
++}
++
+ # NOTE: 'win_flex' and 'bison' can be found in qt5/gnuwin32/bin
+ flex.commands = $$addGnuPath(win_flex) --noline --nounistd --outfile=${QMAKE_FILE_BASE}_lex.cpp ${QMAKE_FILE_NAME}
+ flex.output = ${QMAKE_FILE_BASE}_lex.cpp
+diff --git a/src/angle/src/config.pri b/src/angle/src/config.pri
+index 1c6d8b0..ed25581 100644
+--- a/src/angle/src/config.pri
++++ b/src/angle/src/config.pri
+@@ -37,8 +37,9 @@ DEFINES += _WINDOWS \
+ NOMINMAX \
+ WIN32_LEAN_AND_MEAN=1
+
+-# Defines specifying the API version (0x0600 = Vista)
+-DEFINES += _WIN32_WINNT=0x0600 WINVER=0x0600
++# Defines specifying the API version (0x0600 = Vista, 0x0602 = Win8))
++winrt: DEFINES += _WIN32_WINNT=0x0602 WINVER=0x0602
++else: DEFINES += _WIN32_WINNT=0x0600 WINVER=0x0600
+
+ # ANGLE specific defines
+ DEFINES += ANGLE_DISABLE_TRACE \
+--
+1.8.4.msysgit.0
+
diff --git a/src/angle/patches/0013-ANGLE-Enable-D3D11-for-feature-level-9-cards.patch b/src/angle/patches/0013-ANGLE-Enable-D3D11-for-feature-level-9-cards.patch
new file mode 100644
index 0000000000..0a8e403e8d
--- /dev/null
+++ b/src/angle/patches/0013-ANGLE-Enable-D3D11-for-feature-level-9-cards.patch
@@ -0,0 +1,990 @@
+From a71ccc033fe2cf1c3c58633d3bd220c52b744478 Mon Sep 17 00:00:00 2001
+From: Andrew Knight <andrew.knight@digia.com>
+Date: Fri, 8 Nov 2013 09:04:59 +0200
+Subject: [PATCH] ANGLE: Enable D3D11 for feature level 9 cards
+
+Enable use of ANGLE on lower-end hardware, such as Surface RT and
+Windows Phone 8.
+
+Based on https://codereview.appspot.com/12917046/
+
+Change-Id: Ice536802e4eedc1d264abd0dd65960638fce59e4
+---
+ src/3rdparty/angle/src/libGLESv2/Buffer.cpp | 8 +-
+ src/3rdparty/angle/src/libGLESv2/Buffer.h | 4 +-
+ src/3rdparty/angle/src/libGLESv2/libGLESv2.cpp | 4 +-
+ .../angle/src/libGLESv2/renderer/BufferStorage.h | 2 +-
+ .../src/libGLESv2/renderer/BufferStorage11.cpp | 9 +-
+ .../angle/src/libGLESv2/renderer/BufferStorage11.h | 2 +-
+ .../src/libGLESv2/renderer/BufferStorage9.cpp | 2 +-
+ .../angle/src/libGLESv2/renderer/BufferStorage9.h | 2 +-
+ .../angle/src/libGLESv2/renderer/Image11.cpp | 7 +-
+ .../angle/src/libGLESv2/renderer/IndexBuffer11.cpp | 4 +-
+ .../src/libGLESv2/renderer/RenderStateCache.cpp | 3 +-
+ .../angle/src/libGLESv2/renderer/Renderer11.cpp | 288 +++++++++++++++------
+ .../angle/src/libGLESv2/renderer/Renderer11.h | 2 +
+ .../angle/src/libGLESv2/renderer/SwapChain11.cpp | 7 +-
+ .../src/libGLESv2/renderer/TextureStorage11.cpp | 8 +-
+ .../src/libGLESv2/renderer/renderer11_utils.cpp | 4 +-
+ .../src/libGLESv2/renderer/renderer11_utils.h | 2 +-
+ .../src/libGLESv2/renderer/shaders/Clear11.hlsl | 4 +
+ src/angle/src/libGLESv2/libGLESv2.pro | 8 +-
+ 19 files changed, 260 insertions(+), 110 deletions(-)
+
+diff --git a/src/3rdparty/angle/src/libGLESv2/Buffer.cpp b/src/3rdparty/angle/src/libGLESv2/Buffer.cpp
+index c007d5d..40baa95 100644
+--- a/src/3rdparty/angle/src/libGLESv2/Buffer.cpp
++++ b/src/3rdparty/angle/src/libGLESv2/Buffer.cpp
+@@ -37,11 +37,11 @@ Buffer::~Buffer()
+ delete mStaticIndexBuffer;
+ }
+
+-void Buffer::bufferData(const void *data, GLsizeiptr size, GLenum usage)
++void Buffer::bufferData(const void *data, GLsizeiptr size, GLenum usage, GLenum target)
+ {
+ mBufferStorage->clear();
+ mIndexRangeCache.clear();
+- mBufferStorage->setData(data, size, 0);
++ mBufferStorage->setData(data, size, 0, target);
+
+ mUsage = usage;
+
+@@ -54,9 +54,9 @@ void Buffer::bufferData(const void *data, GLsizeiptr size, GLenum usage)
+ }
+ }
+
+-void Buffer::bufferSubData(const void *data, GLsizeiptr size, GLintptr offset)
++void Buffer::bufferSubData(const void *data, GLsizeiptr size, GLintptr offset, GLenum target)
+ {
+- mBufferStorage->setData(data, size, offset);
++ mBufferStorage->setData(data, size, offset, target);
+ mIndexRangeCache.invalidateRange(offset, size);
+
+ if ((mStaticVertexBuffer && mStaticVertexBuffer->getBufferSize() != 0) || (mStaticIndexBuffer && mStaticIndexBuffer->getBufferSize() != 0))
+diff --git a/src/3rdparty/angle/src/libGLESv2/Buffer.h b/src/3rdparty/angle/src/libGLESv2/Buffer.h
+index 4048f4b..9b86b97 100644
+--- a/src/3rdparty/angle/src/libGLESv2/Buffer.h
++++ b/src/3rdparty/angle/src/libGLESv2/Buffer.h
+@@ -33,8 +33,8 @@ class Buffer : public RefCountObject
+
+ virtual ~Buffer();
+
+- void bufferData(const void *data, GLsizeiptr size, GLenum usage);
+- void bufferSubData(const void *data, GLsizeiptr size, GLintptr offset);
++ void bufferData(const void *data, GLsizeiptr size, GLenum usage, GLenum target);
++ void bufferSubData(const void *data, GLsizeiptr size, GLintptr offset, GLenum target);
+
+ GLenum usage() const;
+
+diff --git a/src/3rdparty/angle/src/libGLESv2/libGLESv2.cpp b/src/3rdparty/angle/src/libGLESv2/libGLESv2.cpp
+index 320bbcc..91719f8 100644
+--- a/src/3rdparty/angle/src/libGLESv2/libGLESv2.cpp
++++ b/src/3rdparty/angle/src/libGLESv2/libGLESv2.cpp
+@@ -758,7 +758,7 @@ void __stdcall glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data,
+ return gl::error(GL_INVALID_OPERATION);
+ }
+
+- buffer->bufferData(data, size, usage);
++ buffer->bufferData(data, size, usage, target);
+ }
+ }
+ catch(std::bad_alloc&)
+@@ -812,7 +812,7 @@ void __stdcall glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size,
+ return gl::error(GL_INVALID_VALUE);
+ }
+
+- buffer->bufferSubData(data, size, offset);
++ buffer->bufferSubData(data, size, offset, target);
+ }
+ }
+ catch(std::bad_alloc&)
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage.h b/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage.h
+index ace1a11..14a8c27 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage.h
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage.h
+@@ -22,7 +22,7 @@ class BufferStorage
+
+ // The data returned is only guaranteed valid until next non-const method.
+ virtual void *getData() = 0;
+- virtual void setData(const void* data, unsigned int size, unsigned int offset) = 0;
++ virtual void setData(const void* data, unsigned int size, unsigned int offset, unsigned int target) = 0;
+ virtual void clear() = 0;
+ virtual unsigned int getSize() const = 0;
+ virtual bool supportsDirectBinding() const = 0;
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage11.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage11.cpp
+index 3647d8a..2f694db 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage11.cpp
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage11.cpp
+@@ -131,7 +131,7 @@ void *BufferStorage11::getData()
+ return mResolvedData;
+ }
+
+-void BufferStorage11::setData(const void* data, unsigned int size, unsigned int offset)
++void BufferStorage11::setData(const void* data, unsigned int size, unsigned int offset, unsigned int target)
+ {
+ ID3D11Device *device = mRenderer->getDevice();
+ ID3D11DeviceContext *context = mRenderer->getDeviceContext();
+@@ -201,7 +201,10 @@ void BufferStorage11::setData(const void* data, unsigned int size, unsigned int
+ D3D11_BUFFER_DESC bufferDesc;
+ bufferDesc.ByteWidth = requiredBufferSize;
+ bufferDesc.Usage = D3D11_USAGE_DEFAULT;
+- bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_INDEX_BUFFER;
++ if (mRenderer->getFeatureLevel() > D3D_FEATURE_LEVEL_9_3)
++ bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_INDEX_BUFFER;
++ else
++ bufferDesc.BindFlags = target == GL_ARRAY_BUFFER ? D3D11_BIND_VERTEX_BUFFER : D3D11_BIND_INDEX_BUFFER;
+ bufferDesc.CPUAccessFlags = 0;
+ bufferDesc.MiscFlags = 0;
+ bufferDesc.StructureByteStride = 0;
+@@ -324,7 +327,7 @@ unsigned int BufferStorage11::getSize() const
+
+ bool BufferStorage11::supportsDirectBinding() const
+ {
+- return true;
++ return mRenderer->getFeatureLevel() >= D3D_FEATURE_LEVEL_10_0;
+ }
+
+ void BufferStorage11::markBufferUsage()
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage11.h b/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage11.h
+index b62348b..c948962 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage11.h
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage11.h
+@@ -24,7 +24,7 @@ class BufferStorage11 : public BufferStorage
+ static BufferStorage11 *makeBufferStorage11(BufferStorage *bufferStorage);
+
+ virtual void *getData();
+- virtual void setData(const void* data, unsigned int size, unsigned int offset);
++ virtual void setData(const void* data, unsigned int size, unsigned int offset, unsigned int target);
+ virtual void clear();
+ virtual unsigned int getSize() const;
+ virtual bool supportsDirectBinding() const;
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage9.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage9.cpp
+index e69e7a8..57fd29b 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage9.cpp
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage9.cpp
+@@ -36,7 +36,7 @@ void *BufferStorage9::getData()
+ return mMemory;
+ }
+
+-void BufferStorage9::setData(const void* data, unsigned int size, unsigned int offset)
++void BufferStorage9::setData(const void* data, unsigned int size, unsigned int offset, unsigned int)
+ {
+ if (!mMemory || offset + size > mAllocatedSize)
+ {
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage9.h b/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage9.h
+index 3e80396..82ae577 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage9.h
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/BufferStorage9.h
+@@ -23,7 +23,7 @@ class BufferStorage9 : public BufferStorage
+ static BufferStorage9 *makeBufferStorage9(BufferStorage *bufferStorage);
+
+ virtual void *getData();
+- virtual void setData(const void* data, unsigned int size, unsigned int offset);
++ virtual void setData(const void* data, unsigned int size, unsigned int offset, unsigned int target = 0);
+ virtual void clear();
+ virtual unsigned int getSize() const;
+ virtual bool supportsDirectBinding() const;
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/Image11.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/Image11.cpp
+index 09c8922..81e9e9e 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/Image11.cpp
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/Image11.cpp
+@@ -136,7 +136,7 @@ bool Image11::redefine(Renderer *renderer, GLint internalformat, GLsizei width,
+ mHeight = height;
+ mInternalFormat = internalformat;
+ // compute the d3d format that will be used
+- mDXGIFormat = gl_d3d11::ConvertTextureFormat(internalformat);
++ mDXGIFormat = gl_d3d11::ConvertTextureFormat(internalformat, mRenderer->getFeatureLevel());
+ mActualFormat = d3d11_gl::ConvertTextureInternalFormat(mDXGIFormat);
+
+ if (mStagingTexture)
+@@ -185,7 +185,10 @@ void Image11::loadData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei heig
+ switch (mInternalFormat)
+ {
+ case GL_ALPHA8_EXT:
+- loadAlphaDataToNative(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData);
++ if (mRenderer->getFeatureLevel() >= D3D_FEATURE_LEVEL_10_0)
++ loadAlphaDataToNative(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData);
++ else
++ loadAlphaDataToBGRA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData);
+ break;
+ case GL_LUMINANCE8_EXT:
+ loadLuminanceDataToNativeOrBGRA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData, false);
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/IndexBuffer11.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/IndexBuffer11.cpp
+index 66604c4..36a62ad 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/IndexBuffer11.cpp
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/IndexBuffer11.cpp
+@@ -170,7 +170,7 @@ DXGI_FORMAT IndexBuffer11::getIndexFormat() const
+ {
+ case GL_UNSIGNED_BYTE: return DXGI_FORMAT_R16_UINT;
+ case GL_UNSIGNED_SHORT: return DXGI_FORMAT_R16_UINT;
+- case GL_UNSIGNED_INT: return DXGI_FORMAT_R32_UINT;
++ case GL_UNSIGNED_INT: return mRenderer->get32BitIndexSupport() ? DXGI_FORMAT_R32_UINT : DXGI_FORMAT_R16_UINT;
+ default: UNREACHABLE(); return DXGI_FORMAT_UNKNOWN;
+ }
+ }
+@@ -180,4 +180,4 @@ ID3D11Buffer *IndexBuffer11::getBuffer() const
+ return mBuffer;
+ }
+
+-}
+\ No newline at end of file
++}
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/RenderStateCache.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/RenderStateCache.cpp
+index b3111af..fd388df 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/RenderStateCache.cpp
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/RenderStateCache.cpp
+@@ -387,7 +387,8 @@ ID3D11SamplerState *RenderStateCache::getSamplerState(const gl::SamplerState &sa
+ samplerDesc.BorderColor[2] = 0.0f;
+ samplerDesc.BorderColor[3] = 0.0f;
+ samplerDesc.MinLOD = gl_d3d11::ConvertMinLOD(samplerState.minFilter, samplerState.lodOffset);
+- samplerDesc.MaxLOD = gl_d3d11::ConvertMaxLOD(samplerState.minFilter, samplerState.lodOffset);
++ samplerDesc.MaxLOD = mDevice->GetFeatureLevel() >= D3D_FEATURE_LEVEL_10_0
++ ? gl_d3d11::ConvertMaxLOD(samplerState.minFilter, samplerState.lodOffset) : FLT_MAX;
+
+ ID3D11SamplerState *dx11SamplerState = NULL;
+ HRESULT result = mDevice->CreateSamplerState(&samplerDesc, &dx11SamplerState);
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.cpp
+index d04467b..f83e9e9 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.cpp
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.cpp
+@@ -160,9 +160,13 @@ EGLint Renderer11::initialize()
+
+ D3D_FEATURE_LEVEL featureLevels[] =
+ {
++ D3D_FEATURE_LEVEL_11_1,
+ D3D_FEATURE_LEVEL_11_0,
+ D3D_FEATURE_LEVEL_10_1,
+ D3D_FEATURE_LEVEL_10_0,
++ D3D_FEATURE_LEVEL_9_3,
++ D3D_FEATURE_LEVEL_9_2,
++ D3D_FEATURE_LEVEL_9_1,
+ };
+
+ HRESULT result = S_OK;
+@@ -1114,6 +1118,43 @@ void Renderer11::drawElements(GLenum mode, GLsizei count, GLenum type, const GLv
+ }
+ }
+
++template <typename T>
++static void drawLineLoopIndexed(T *data, GLenum type, const GLvoid *indices, GLsizei count)
++{
++ switch (type)
++ {
++ case GL_NONE: // Non-indexed draw
++ for (int i = 0; i < count; i++)
++ {
++ data[i] = i;
++ }
++ data[count] = 0;
++ break;
++ case GL_UNSIGNED_BYTE:
++ for (int i = 0; i < count; i++)
++ {
++ data[i] = static_cast<const GLubyte*>(indices)[i];
++ }
++ data[count] = static_cast<const GLubyte*>(indices)[0];
++ break;
++ case GL_UNSIGNED_SHORT:
++ for (int i = 0; i < count; i++)
++ {
++ data[i] = static_cast<const GLushort*>(indices)[i];
++ }
++ data[count] = static_cast<const GLushort*>(indices)[0];
++ break;
++ case GL_UNSIGNED_INT:
++ for (int i = 0; i < count; i++)
++ {
++ data[i] = static_cast<const GLuint*>(indices)[i];
++ }
++ data[count] = static_cast<const GLuint*>(indices)[0];
++ break;
++ default: UNREACHABLE();
++ }
++}
++
+ void Renderer11::drawLineLoop(GLsizei count, GLenum type, const GLvoid *indices, int minIndex, gl::Buffer *elementArrayBuffer)
+ {
+ // Get the raw indices for an indexed draw
+@@ -1162,59 +1203,71 @@ void Renderer11::drawLineLoop(GLsizei count, GLenum type, const GLvoid *indices,
+ return gl::error(GL_OUT_OF_MEMORY);
+ }
+
+- unsigned int *data = reinterpret_cast<unsigned int*>(mappedMemory);
++ if (get32BitIndexSupport())
++ drawLineLoopIndexed(reinterpret_cast<unsigned int*>(mappedMemory), type, indices, count);
++ else
++ drawLineLoopIndexed(reinterpret_cast<unsigned short*>(mappedMemory), type, indices, count);
++
+ unsigned int indexBufferOffset = offset;
+
++ if (!mLineLoopIB->unmapBuffer())
++ {
++ ERR("Could not unmap index buffer for GL_LINE_LOOP.");
++ return gl::error(GL_OUT_OF_MEMORY);
++ }
++
++ if (mAppliedIBSerial != mLineLoopIB->getSerial() || mAppliedIBOffset != indexBufferOffset)
++ {
++ IndexBuffer11 *indexBuffer = IndexBuffer11::makeIndexBuffer11(mLineLoopIB->getIndexBuffer());
++
++ mDeviceContext->IASetIndexBuffer(indexBuffer->getBuffer(), indexBuffer->getIndexFormat(), indexBufferOffset);
++ mAppliedIBSerial = mLineLoopIB->getSerial();
++ mAppliedStorageIBSerial = 0;
++ mAppliedIBOffset = indexBufferOffset;
++ }
++
++ mDeviceContext->DrawIndexed(count + 1, 0, -minIndex);
++}
++
++template <typename T>
++static void drawTriangleFanIndexed(T *data, GLenum type, const GLvoid *indices, unsigned int numTris)
++{
+ switch (type)
+ {
+ case GL_NONE: // Non-indexed draw
+- for (int i = 0; i < count; i++)
++ for (unsigned int i = 0; i < numTris; i++)
+ {
+- data[i] = i;
++ data[i*3 + 0] = 0;
++ data[i*3 + 1] = i + 1;
++ data[i*3 + 2] = i + 2;
+ }
+- data[count] = 0;
+ break;
+ case GL_UNSIGNED_BYTE:
+- for (int i = 0; i < count; i++)
++ for (unsigned int i = 0; i < numTris; i++)
+ {
+- data[i] = static_cast<const GLubyte*>(indices)[i];
++ data[i*3 + 0] = static_cast<const GLubyte*>(indices)[0];
++ data[i*3 + 1] = static_cast<const GLubyte*>(indices)[i + 1];
++ data[i*3 + 2] = static_cast<const GLubyte*>(indices)[i + 2];
+ }
+- data[count] = static_cast<const GLubyte*>(indices)[0];
+ break;
+ case GL_UNSIGNED_SHORT:
+- for (int i = 0; i < count; i++)
++ for (unsigned int i = 0; i < numTris; i++)
+ {
+- data[i] = static_cast<const GLushort*>(indices)[i];
++ data[i*3 + 0] = static_cast<const GLushort*>(indices)[0];
++ data[i*3 + 1] = static_cast<const GLushort*>(indices)[i + 1];
++ data[i*3 + 2] = static_cast<const GLushort*>(indices)[i + 2];
+ }
+- data[count] = static_cast<const GLushort*>(indices)[0];
+ break;
+ case GL_UNSIGNED_INT:
+- for (int i = 0; i < count; i++)
++ for (unsigned int i = 0; i < numTris; i++)
+ {
+- data[i] = static_cast<const GLuint*>(indices)[i];
++ data[i*3 + 0] = static_cast<const GLuint*>(indices)[0];
++ data[i*3 + 1] = static_cast<const GLuint*>(indices)[i + 1];
++ data[i*3 + 2] = static_cast<const GLuint*>(indices)[i + 2];
+ }
+- data[count] = static_cast<const GLuint*>(indices)[0];
+ break;
+ default: UNREACHABLE();
+ }
+-
+- if (!mLineLoopIB->unmapBuffer())
+- {
+- ERR("Could not unmap index buffer for GL_LINE_LOOP.");
+- return gl::error(GL_OUT_OF_MEMORY);
+- }
+-
+- if (mAppliedIBSerial != mLineLoopIB->getSerial() || mAppliedIBOffset != indexBufferOffset)
+- {
+- IndexBuffer11 *indexBuffer = IndexBuffer11::makeIndexBuffer11(mLineLoopIB->getIndexBuffer());
+-
+- mDeviceContext->IASetIndexBuffer(indexBuffer->getBuffer(), indexBuffer->getIndexFormat(), indexBufferOffset);
+- mAppliedIBSerial = mLineLoopIB->getSerial();
+- mAppliedStorageIBSerial = 0;
+- mAppliedIBOffset = indexBufferOffset;
+- }
+-
+- mDeviceContext->DrawIndexed(count + 1, 0, -minIndex);
+ }
+
+ void Renderer11::drawTriangleFan(GLsizei count, GLenum type, const GLvoid *indices, int minIndex, gl::Buffer *elementArrayBuffer, int instances)
+@@ -1267,45 +1320,12 @@ void Renderer11::drawTriangleFan(GLsizei count, GLenum type, const GLvoid *indic
+ return gl::error(GL_OUT_OF_MEMORY);
+ }
+
+- unsigned int *data = reinterpret_cast<unsigned int*>(mappedMemory);
+- unsigned int indexBufferOffset = offset;
++ if (get32BitIndexSupport())
++ drawTriangleFanIndexed(reinterpret_cast<unsigned int*>(mappedMemory), type, indices, numTris);
++ else
++ drawTriangleFanIndexed(reinterpret_cast<unsigned short*>(mappedMemory), type, indices, numTris);
+
+- switch (type)
+- {
+- case GL_NONE: // Non-indexed draw
+- for (unsigned int i = 0; i < numTris; i++)
+- {
+- data[i*3 + 0] = 0;
+- data[i*3 + 1] = i + 1;
+- data[i*3 + 2] = i + 2;
+- }
+- break;
+- case GL_UNSIGNED_BYTE:
+- for (unsigned int i = 0; i < numTris; i++)
+- {
+- data[i*3 + 0] = static_cast<const GLubyte*>(indices)[0];
+- data[i*3 + 1] = static_cast<const GLubyte*>(indices)[i + 1];
+- data[i*3 + 2] = static_cast<const GLubyte*>(indices)[i + 2];
+- }
+- break;
+- case GL_UNSIGNED_SHORT:
+- for (unsigned int i = 0; i < numTris; i++)
+- {
+- data[i*3 + 0] = static_cast<const GLushort*>(indices)[0];
+- data[i*3 + 1] = static_cast<const GLushort*>(indices)[i + 1];
+- data[i*3 + 2] = static_cast<const GLushort*>(indices)[i + 2];
+- }
+- break;
+- case GL_UNSIGNED_INT:
+- for (unsigned int i = 0; i < numTris; i++)
+- {
+- data[i*3 + 0] = static_cast<const GLuint*>(indices)[0];
+- data[i*3 + 1] = static_cast<const GLuint*>(indices)[i + 1];
+- data[i*3 + 2] = static_cast<const GLuint*>(indices)[i + 2];
+- }
+- break;
+- default: UNREACHABLE();
+- }
++ unsigned int indexBufferOffset = offset;
+
+ if (!mTriangleFanIB->unmapBuffer())
+ {
+@@ -1515,7 +1535,7 @@ void Renderer11::applyUniforms(gl::ProgramBinary *programBinary, gl::UniformArra
+ }
+
+ // needed for the point sprite geometry shader
+- if (mCurrentGeometryConstantBuffer != mDriverConstantBufferPS)
++ if (mFeatureLevel >= D3D_FEATURE_LEVEL_10_0 && mCurrentGeometryConstantBuffer != mDriverConstantBufferPS)
+ {
+ mDeviceContext->GSSetConstantBuffers(0, 1, &mDriverConstantBufferPS);
+ mCurrentGeometryConstantBuffer = mDriverConstantBufferPS;
+@@ -1929,9 +1949,13 @@ bool Renderer11::testDeviceResettable()
+
+ D3D_FEATURE_LEVEL featureLevels[] =
+ {
++ D3D_FEATURE_LEVEL_11_1,
+ D3D_FEATURE_LEVEL_11_0,
+ D3D_FEATURE_LEVEL_10_1,
+ D3D_FEATURE_LEVEL_10_0,
++ D3D_FEATURE_LEVEL_9_3,
++ D3D_FEATURE_LEVEL_9_2,
++ D3D_FEATURE_LEVEL_9_1,
+ };
+
+ ID3D11Device* dummyDevice;
+@@ -2110,11 +2134,17 @@ float Renderer11::getTextureMaxAnisotropy() const
+ {
+ switch (mFeatureLevel)
+ {
++ case D3D_FEATURE_LEVEL_11_1:
+ case D3D_FEATURE_LEVEL_11_0:
+ return D3D11_MAX_MAXANISOTROPY;
+ case D3D_FEATURE_LEVEL_10_1:
+ case D3D_FEATURE_LEVEL_10_0:
+ return D3D10_MAX_MAXANISOTROPY;
++ case D3D_FEATURE_LEVEL_9_3:
++ case D3D_FEATURE_LEVEL_9_2:
++ return 16;
++ case D3D_FEATURE_LEVEL_9_1:
++ return D3D_FL9_1_DEFAULT_MAX_ANISOTROPY;
+ default: UNREACHABLE();
+ return 0;
+ }
+@@ -2129,11 +2159,17 @@ Range Renderer11::getViewportBounds() const
+ {
+ switch (mFeatureLevel)
+ {
++ case D3D_FEATURE_LEVEL_11_1:
+ case D3D_FEATURE_LEVEL_11_0:
+ return Range(D3D11_VIEWPORT_BOUNDS_MIN, D3D11_VIEWPORT_BOUNDS_MAX);
+ case D3D_FEATURE_LEVEL_10_1:
+ case D3D_FEATURE_LEVEL_10_0:
+ return Range(D3D10_VIEWPORT_BOUNDS_MIN, D3D10_VIEWPORT_BOUNDS_MAX);
++ case D3D_FEATURE_LEVEL_9_3:
++ return Range(D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION * -2, D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION * 2);
++ case D3D_FEATURE_LEVEL_9_2:
++ case D3D_FEATURE_LEVEL_9_1:
++ return Range(D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION * -2, D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION * 2);
+ default: UNREACHABLE();
+ return Range(0, 0);
+ }
+@@ -2144,10 +2180,15 @@ unsigned int Renderer11::getMaxVertexTextureImageUnits() const
+ META_ASSERT(MAX_TEXTURE_IMAGE_UNITS_VTF_SM4 <= gl::IMPLEMENTATION_MAX_VERTEX_TEXTURE_IMAGE_UNITS);
+ switch (mFeatureLevel)
+ {
++ case D3D_FEATURE_LEVEL_11_1:
+ case D3D_FEATURE_LEVEL_11_0:
+ case D3D_FEATURE_LEVEL_10_1:
+ case D3D_FEATURE_LEVEL_10_0:
+ return MAX_TEXTURE_IMAGE_UNITS_VTF_SM4;
++ case D3D_FEATURE_LEVEL_9_3:
++ case D3D_FEATURE_LEVEL_9_2:
++ case D3D_FEATURE_LEVEL_9_1:
++ return 0;
+ default: UNREACHABLE();
+ return 0;
+ }
+@@ -2171,15 +2212,41 @@ unsigned int Renderer11::getReservedFragmentUniformVectors() const
+ unsigned int Renderer11::getMaxVertexUniformVectors() const
+ {
+ META_ASSERT(MAX_VERTEX_UNIFORM_VECTORS_D3D11 <= D3D10_REQ_CONSTANT_BUFFER_ELEMENT_COUNT);
+- ASSERT(mFeatureLevel >= D3D_FEATURE_LEVEL_10_0);
+- return MAX_VERTEX_UNIFORM_VECTORS_D3D11;
++ switch (mFeatureLevel)
++ {
++ case D3D_FEATURE_LEVEL_11_1:
++ case D3D_FEATURE_LEVEL_11_0:
++ case D3D_FEATURE_LEVEL_10_1:
++ case D3D_FEATURE_LEVEL_10_0:
++ return MAX_VERTEX_UNIFORM_VECTORS_D3D11;
++ case D3D_FEATURE_LEVEL_9_3:
++ case D3D_FEATURE_LEVEL_9_2:
++ case D3D_FEATURE_LEVEL_9_1:
++ return MAX_VERTEX_UNIFORM_VECTORS_D3D9;
++ default:
++ UNIMPLEMENTED();
++ return 0;
++ }
+ }
+
+ unsigned int Renderer11::getMaxFragmentUniformVectors() const
+ {
+ META_ASSERT(MAX_FRAGMENT_UNIFORM_VECTORS_D3D11 <= D3D10_REQ_CONSTANT_BUFFER_ELEMENT_COUNT);
+- ASSERT(mFeatureLevel >= D3D_FEATURE_LEVEL_10_0);
+- return MAX_FRAGMENT_UNIFORM_VECTORS_D3D11;
++ switch (mFeatureLevel)
++ {
++ case D3D_FEATURE_LEVEL_11_1:
++ case D3D_FEATURE_LEVEL_11_0:
++ case D3D_FEATURE_LEVEL_10_1:
++ case D3D_FEATURE_LEVEL_10_0:
++ return MAX_FRAGMENT_UNIFORM_VECTORS_D3D11;
++ case D3D_FEATURE_LEVEL_9_3:
++ return 221;
++ case D3D_FEATURE_LEVEL_9_2:
++ case D3D_FEATURE_LEVEL_9_1:
++ return 29;
++ default: UNREACHABLE();
++ return 0;
++ }
+ }
+
+ unsigned int Renderer11::getMaxVaryingVectors() const
+@@ -2187,11 +2254,17 @@ unsigned int Renderer11::getMaxVaryingVectors() const
+ META_ASSERT(gl::IMPLEMENTATION_MAX_VARYING_VECTORS == D3D11_VS_OUTPUT_REGISTER_COUNT);
+ switch (mFeatureLevel)
+ {
++ case D3D_FEATURE_LEVEL_11_1:
+ case D3D_FEATURE_LEVEL_11_0:
+ return D3D11_VS_OUTPUT_REGISTER_COUNT;
+ case D3D_FEATURE_LEVEL_10_1:
++ return D3D10_1_VS_OUTPUT_REGISTER_COUNT;
+ case D3D_FEATURE_LEVEL_10_0:
+ return D3D10_VS_OUTPUT_REGISTER_COUNT;
++ case D3D_FEATURE_LEVEL_9_3:
++ case D3D_FEATURE_LEVEL_9_2:
++ case D3D_FEATURE_LEVEL_9_1:
++ return 8;
+ default: UNREACHABLE();
+ return 0;
+ }
+@@ -2201,10 +2274,15 @@ bool Renderer11::getNonPower2TextureSupport() const
+ {
+ switch (mFeatureLevel)
+ {
++ case D3D_FEATURE_LEVEL_11_1:
+ case D3D_FEATURE_LEVEL_11_0:
+ case D3D_FEATURE_LEVEL_10_1:
+ case D3D_FEATURE_LEVEL_10_0:
+ return true;
++ case D3D_FEATURE_LEVEL_9_3:
++ case D3D_FEATURE_LEVEL_9_2:
++ case D3D_FEATURE_LEVEL_9_1:
++ return false;
+ default: UNREACHABLE();
+ return false;
+ }
+@@ -2214,10 +2292,15 @@ bool Renderer11::getOcclusionQuerySupport() const
+ {
+ switch (mFeatureLevel)
+ {
++ case D3D_FEATURE_LEVEL_11_1:
+ case D3D_FEATURE_LEVEL_11_0:
+ case D3D_FEATURE_LEVEL_10_1:
+ case D3D_FEATURE_LEVEL_10_0:
++ case D3D_FEATURE_LEVEL_9_3:
++ case D3D_FEATURE_LEVEL_9_2:
+ return true;
++ case D3D_FEATURE_LEVEL_9_1:
++ return false;
+ default: UNREACHABLE();
+ return false;
+ }
+@@ -2227,10 +2310,15 @@ bool Renderer11::getInstancingSupport() const
+ {
+ switch (mFeatureLevel)
+ {
++ case D3D_FEATURE_LEVEL_11_1:
+ case D3D_FEATURE_LEVEL_11_0:
+ case D3D_FEATURE_LEVEL_10_1:
+ case D3D_FEATURE_LEVEL_10_0:
++ case D3D_FEATURE_LEVEL_9_3:
+ return true;
++ case D3D_FEATURE_LEVEL_9_2:
++ case D3D_FEATURE_LEVEL_9_1:
++ return false;
+ default: UNREACHABLE();
+ return false;
+ }
+@@ -2248,10 +2336,15 @@ bool Renderer11::getDerivativeInstructionSupport() const
+ {
+ switch (mFeatureLevel)
+ {
++ case D3D_FEATURE_LEVEL_11_1:
+ case D3D_FEATURE_LEVEL_11_0:
+ case D3D_FEATURE_LEVEL_10_1:
+ case D3D_FEATURE_LEVEL_10_0:
++ case D3D_FEATURE_LEVEL_9_3:
+ return true;
++ case D3D_FEATURE_LEVEL_9_2:
++ case D3D_FEATURE_LEVEL_9_1:
++ return false;
+ default: UNREACHABLE();
+ return false;
+ }
+@@ -2267,9 +2360,13 @@ int Renderer11::getMajorShaderModel() const
+ {
+ switch (mFeatureLevel)
+ {
++ case D3D_FEATURE_LEVEL_11_1:
+ case D3D_FEATURE_LEVEL_11_0: return D3D11_SHADER_MAJOR_VERSION; // 5
+ case D3D_FEATURE_LEVEL_10_1: return D3D10_1_SHADER_MAJOR_VERSION; // 4
+ case D3D_FEATURE_LEVEL_10_0: return D3D10_SHADER_MAJOR_VERSION; // 4
++ case D3D_FEATURE_LEVEL_9_3:
++ case D3D_FEATURE_LEVEL_9_2:
++ case D3D_FEATURE_LEVEL_9_1: return 4; // SM4 level 9, but treat as 4
+ default: UNREACHABLE(); return 0;
+ }
+ }
+@@ -2278,9 +2375,13 @@ int Renderer11::getMinorShaderModel() const
+ {
+ switch (mFeatureLevel)
+ {
++ case D3D_FEATURE_LEVEL_11_1:
+ case D3D_FEATURE_LEVEL_11_0: return D3D11_SHADER_MINOR_VERSION; // 0
+ case D3D_FEATURE_LEVEL_10_1: return D3D10_1_SHADER_MINOR_VERSION; // 1
+ case D3D_FEATURE_LEVEL_10_0: return D3D10_SHADER_MINOR_VERSION; // 0
++ case D3D_FEATURE_LEVEL_9_3:
++ case D3D_FEATURE_LEVEL_9_2:
++ case D3D_FEATURE_LEVEL_9_1: return 0;
+ default: UNREACHABLE(); return 0;
+ }
+ }
+@@ -2301,11 +2402,17 @@ int Renderer11::getMaxViewportDimension() const
+
+ switch (mFeatureLevel)
+ {
+- case D3D_FEATURE_LEVEL_11_0:
++ case D3D_FEATURE_LEVEL_11_1:
++ case D3D_FEATURE_LEVEL_11_0:
+ return D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 16384
+ case D3D_FEATURE_LEVEL_10_1:
+ case D3D_FEATURE_LEVEL_10_0:
+ return D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 8192
++ case D3D_FEATURE_LEVEL_9_3:
++ return D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 4096
++ case D3D_FEATURE_LEVEL_9_2:
++ case D3D_FEATURE_LEVEL_9_1:
++ return D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 2048
+ default: UNREACHABLE();
+ return 0;
+ }
+@@ -2315,9 +2422,13 @@ int Renderer11::getMaxTextureWidth() const
+ {
+ switch (mFeatureLevel)
+ {
++ case D3D_FEATURE_LEVEL_11_1:
+ case D3D_FEATURE_LEVEL_11_0: return D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 16384
+ case D3D_FEATURE_LEVEL_10_1:
+ case D3D_FEATURE_LEVEL_10_0: return D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 8192
++ case D3D_FEATURE_LEVEL_9_3: return D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 4096
++ case D3D_FEATURE_LEVEL_9_2:
++ case D3D_FEATURE_LEVEL_9_1: return D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 2048
+ default: UNREACHABLE(); return 0;
+ }
+ }
+@@ -2326,9 +2437,13 @@ int Renderer11::getMaxTextureHeight() const
+ {
+ switch (mFeatureLevel)
+ {
++ case D3D_FEATURE_LEVEL_11_1:
+ case D3D_FEATURE_LEVEL_11_0: return D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 16384
+ case D3D_FEATURE_LEVEL_10_1:
+ case D3D_FEATURE_LEVEL_10_0: return D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 8192
++ case D3D_FEATURE_LEVEL_9_3: return D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 4096
++ case D3D_FEATURE_LEVEL_9_2:
++ case D3D_FEATURE_LEVEL_9_1: return D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION; // 2048
+ default: UNREACHABLE(); return 0;
+ }
+ }
+@@ -2337,9 +2452,13 @@ bool Renderer11::get32BitIndexSupport() const
+ {
+ switch (mFeatureLevel)
+ {
+- case D3D_FEATURE_LEVEL_11_0:
++ case D3D_FEATURE_LEVEL_11_1:
++ case D3D_FEATURE_LEVEL_11_0:
+ case D3D_FEATURE_LEVEL_10_1:
+ case D3D_FEATURE_LEVEL_10_0: return D3D10_REQ_DRAWINDEXED_INDEX_COUNT_2_TO_EXP >= 32; // true
++ case D3D_FEATURE_LEVEL_9_3:
++ case D3D_FEATURE_LEVEL_9_2:
++ case D3D_FEATURE_LEVEL_9_1: return false;
+ default: UNREACHABLE(); return false;
+ }
+ }
+@@ -2386,14 +2505,22 @@ unsigned int Renderer11::getMaxRenderTargets() const
+ {
+ META_ASSERT(D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT <= gl::IMPLEMENTATION_MAX_DRAW_BUFFERS);
+ META_ASSERT(D3D10_SIMULTANEOUS_RENDER_TARGET_COUNT <= gl::IMPLEMENTATION_MAX_DRAW_BUFFERS);
++ META_ASSERT(D3D_FL9_3_SIMULTANEOUS_RENDER_TARGET_COUNT <= gl::IMPLEMENTATION_MAX_DRAW_BUFFERS);
++ META_ASSERT(D3D_FL9_1_SIMULTANEOUS_RENDER_TARGET_COUNT <= gl::IMPLEMENTATION_MAX_DRAW_BUFFERS);
+
+ switch (mFeatureLevel)
+ {
++ case D3D_FEATURE_LEVEL_11_1:
+ case D3D_FEATURE_LEVEL_11_0:
+ return D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; // 8
+ case D3D_FEATURE_LEVEL_10_1:
+ case D3D_FEATURE_LEVEL_10_0:
+ return D3D10_SIMULTANEOUS_RENDER_TARGET_COUNT; // 8
++ case D3D_FEATURE_LEVEL_9_3:
++ return D3D_FL9_3_SIMULTANEOUS_RENDER_TARGET_COUNT; // 4
++ case D3D_FEATURE_LEVEL_9_2:
++ case D3D_FEATURE_LEVEL_9_1:
++ return D3D_FL9_1_SIMULTANEOUS_RENDER_TARGET_COUNT; // 1
+ default:
+ UNREACHABLE();
+ return 1;
+@@ -2821,7 +2948,7 @@ ShaderExecutable *Renderer11::loadExecutable(const void *function, size_t length
+
+ ShaderExecutable *Renderer11::compileToExecutable(gl::InfoLog &infoLog, const char *shaderHLSL, rx::ShaderType type)
+ {
+- const char *profile = NULL;
++ std::string profile;
+
+ switch (type)
+ {
+@@ -2839,7 +2966,12 @@ ShaderExecutable *Renderer11::compileToExecutable(gl::InfoLog &infoLog, const ch
+ return NULL;
+ }
+
+- ID3DBlob *binary = (ID3DBlob*)compileToBinary(infoLog, shaderHLSL, profile, D3DCOMPILE_OPTIMIZATION_LEVEL0, false);
++ if (mFeatureLevel == D3D_FEATURE_LEVEL_9_3)
++ profile += "_level_9_3";
++ else if (mFeatureLevel == D3D_FEATURE_LEVEL_9_2 || mFeatureLevel == D3D_FEATURE_LEVEL_9_1)
++ profile += "_level_9_1";
++
++ ID3DBlob *binary = (ID3DBlob*)compileToBinary(infoLog, shaderHLSL, profile.c_str(), D3DCOMPILE_OPTIMIZATION_LEVEL0, false);
+ if (!binary)
+ return NULL;
+
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.h b/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.h
+index a7f5a39..433945d 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.h
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/Renderer11.h
+@@ -32,6 +32,7 @@ class StreamingIndexBufferInterface;
+
+ enum
+ {
++ MAX_VERTEX_UNIFORM_VECTORS_D3D9 = 254,
+ MAX_VERTEX_UNIFORM_VECTORS_D3D11 = 1024,
+ MAX_FRAGMENT_UNIFORM_VECTORS_D3D11 = 1024
+ };
+@@ -177,6 +178,7 @@ class Renderer11 : public Renderer
+ ID3D11Device *getDevice() { return mDevice; }
+ ID3D11DeviceContext *getDeviceContext() { return mDeviceContext; };
+ IDXGIFactory *getDxgiFactory() { return mDxgiFactory; };
++ D3D_FEATURE_LEVEL getFeatureLevel() const { return mFeatureLevel; }
+
+ bool getRenderTargetResource(gl::Renderbuffer *colorbuffer, unsigned int *subresourceIndex, ID3D11Texture2D **resource);
+ void unapplyRenderTargets();
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.cpp
+index 0797fd7..9770772 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.cpp
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/SwapChain11.cpp
+@@ -500,12 +500,17 @@ EGLint SwapChain11::reset(int backbufferWidth, int backbufferHeight, EGLint swap
+ ASSERT(SUCCEEDED(result));
+
+ DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
+- swapChainDesc.BufferCount = 2;
+ swapChainDesc.Format = gl_d3d11::ConvertRenderbufferFormat(mBackBufferFormat);
+ swapChainDesc.Width = backbufferWidth;
+ swapChainDesc.Height = backbufferHeight;
+ swapChainDesc.Stereo = FALSE;
++#if !defined(ANGLE_OS_WINPHONE)
++ swapChainDesc.BufferCount = 2;
+ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
++#else
++ swapChainDesc.BufferCount = 1;
++ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
++#endif
+ #endif
+
+ swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/TextureStorage11.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/TextureStorage11.cpp
+index 408b48e..32a407a 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/TextureStorage11.cpp
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/TextureStorage11.cpp
+@@ -222,14 +222,14 @@ TextureStorage11_2D::TextureStorage11_2D(Renderer *renderer, SwapChain11 *swapch
+ }
+
+ TextureStorage11_2D::TextureStorage11_2D(Renderer *renderer, int levels, GLenum internalformat, GLenum usage, bool forceRenderable, GLsizei width, GLsizei height)
+- : TextureStorage11(renderer, GetTextureBindFlags(gl_d3d11::ConvertTextureFormat(internalformat), usage, forceRenderable))
++ : TextureStorage11(renderer, GetTextureBindFlags(gl_d3d11::ConvertTextureFormat(internalformat, Renderer11::makeRenderer11(renderer)->getFeatureLevel()), usage, forceRenderable))
+ {
+ for (unsigned int i = 0; i < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
+ {
+ mRenderTarget[i] = NULL;
+ }
+
+- DXGI_FORMAT convertedFormat = gl_d3d11::ConvertTextureFormat(internalformat);
++ DXGI_FORMAT convertedFormat = gl_d3d11::ConvertTextureFormat(internalformat, Renderer11::makeRenderer11(renderer)->getFeatureLevel());
+ if (d3d11::IsDepthStencilFormat(convertedFormat))
+ {
+ mTextureFormat = d3d11::GetDepthTextureFormat(convertedFormat);
+@@ -440,7 +440,7 @@ void TextureStorage11_2D::generateMipmap(int level)
+ }
+
+ TextureStorage11_Cube::TextureStorage11_Cube(Renderer *renderer, int levels, GLenum internalformat, GLenum usage, bool forceRenderable, int size)
+- : TextureStorage11(renderer, GetTextureBindFlags(gl_d3d11::ConvertTextureFormat(internalformat), usage, forceRenderable))
++ : TextureStorage11(renderer, GetTextureBindFlags(gl_d3d11::ConvertTextureFormat(internalformat, Renderer11::makeRenderer11(renderer)->getFeatureLevel()), usage, forceRenderable))
+ {
+ for (unsigned int i = 0; i < 6; i++)
+ {
+@@ -450,7 +450,7 @@ TextureStorage11_Cube::TextureStorage11_Cube(Renderer *renderer, int levels, GLe
+ }
+ }
+
+- DXGI_FORMAT convertedFormat = gl_d3d11::ConvertTextureFormat(internalformat);
++ DXGI_FORMAT convertedFormat = gl_d3d11::ConvertTextureFormat(internalformat, Renderer11::makeRenderer11(renderer)->getFeatureLevel());
+ if (d3d11::IsDepthStencilFormat(convertedFormat))
+ {
+ mTextureFormat = d3d11::GetDepthTextureFormat(convertedFormat);
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/renderer11_utils.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/renderer11_utils.cpp
+index 13800da..0624a61 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/renderer11_utils.cpp
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/renderer11_utils.cpp
+@@ -329,7 +329,7 @@ DXGI_FORMAT ConvertRenderbufferFormat(GLenum format)
+ return DXGI_FORMAT_R8G8B8A8_UNORM;
+ }
+
+-DXGI_FORMAT ConvertTextureFormat(GLenum internalformat)
++DXGI_FORMAT ConvertTextureFormat(GLenum internalformat, D3D_FEATURE_LEVEL featureLevel)
+ {
+ switch (internalformat)
+ {
+@@ -342,7 +342,7 @@ DXGI_FORMAT ConvertTextureFormat(GLenum internalformat)
+ case GL_LUMINANCE8_ALPHA8_EXT:
+ return DXGI_FORMAT_R8G8B8A8_UNORM;
+ case GL_ALPHA8_EXT:
+- return DXGI_FORMAT_A8_UNORM;
++ return featureLevel >= D3D_FEATURE_LEVEL_10_0 ? DXGI_FORMAT_A8_UNORM : DXGI_FORMAT_B8G8R8A8_UNORM;
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ return DXGI_FORMAT_BC1_UNORM;
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/renderer11_utils.h b/src/3rdparty/angle/src/libGLESv2/renderer/renderer11_utils.h
+index 1bc48c1..70ad4fe 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/renderer11_utils.h
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/renderer11_utils.h
+@@ -32,7 +32,7 @@ FLOAT ConvertMinLOD(GLenum minFilter, unsigned int lodOffset);
+ FLOAT ConvertMaxLOD(GLenum minFilter, unsigned int lodOffset);
+
+ DXGI_FORMAT ConvertRenderbufferFormat(GLenum format);
+-DXGI_FORMAT ConvertTextureFormat(GLenum format);
++DXGI_FORMAT ConvertTextureFormat(GLenum format, D3D_FEATURE_LEVEL featureLevel);
+ }
+
+ namespace d3d11_gl
+diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/shaders/Clear11.hlsl b/src/3rdparty/angle/src/libGLESv2/renderer/shaders/Clear11.hlsl
+index 042ac69..cb132dc 100644
+--- a/src/3rdparty/angle/src/libGLESv2/renderer/shaders/Clear11.hlsl
++++ b/src/3rdparty/angle/src/libGLESv2/renderer/shaders/Clear11.hlsl
+@@ -12,10 +12,12 @@ struct PS_OutputMultiple
+ float4 color1 : SV_TARGET1;
+ float4 color2 : SV_TARGET2;
+ float4 color3 : SV_TARGET3;
++#ifdef SM4
+ float4 color4 : SV_TARGET4;
+ float4 color5 : SV_TARGET5;
+ float4 color6 : SV_TARGET6;
+ float4 color7 : SV_TARGET7;
++#endif
+ };
+
+ PS_OutputMultiple PS_ClearMultiple(in float4 inPosition : SV_POSITION, in float4 inColor : COLOR)
+@@ -25,10 +27,12 @@ PS_OutputMultiple PS_ClearMultiple(in float4 inPosition : SV_POSITION, in float4
+ outColor.color1 = inColor;
+ outColor.color2 = inColor;
+ outColor.color3 = inColor;
++#ifdef SM4
+ outColor.color4 = inColor;
+ outColor.color5 = inColor;
+ outColor.color6 = inColor;
+ outColor.color7 = inColor;
++#endif
+ return outColor;
+ }
+
+diff --git a/src/angle/src/libGLESv2/libGLESv2.pro b/src/angle/src/libGLESv2/libGLESv2.pro
+index ff2f888..b39ce78 100644
+--- a/src/angle/src/libGLESv2/libGLESv2.pro
++++ b/src/angle/src/libGLESv2/libGLESv2.pro
+@@ -190,7 +190,7 @@ for (ps, PIXEL_SHADERS_BLIT) {
+ QMAKE_EXTRA_COMPILERS += fxc_ps_$${ps}
+ }
+ for (ps, PIXEL_SHADERS_PASSTHROUGH) {
+- fxc_ps_$${ps}.commands = $$FXC /nologo /E PS_$$ps /T ps_4_0 /Fh ${QMAKE_FILE_OUT} ${QMAKE_FILE_NAME}
++ fxc_ps_$${ps}.commands = $$FXC /nologo /E PS_$$ps /T ps_4_0_level_9_1 /Fh ${QMAKE_FILE_OUT} ${QMAKE_FILE_NAME}
+ fxc_ps_$${ps}.output = $$SHADER_DIR/$${ps}11ps.h
+ fxc_ps_$${ps}.input = PASSTHROUGH_INPUT
+ fxc_ps_$${ps}.dependency_type = TYPE_C
+@@ -199,7 +199,7 @@ for (ps, PIXEL_SHADERS_PASSTHROUGH) {
+ QMAKE_EXTRA_COMPILERS += fxc_ps_$${ps}
+ }
+ for (ps, PIXEL_SHADERS_CLEAR) {
+- fxc_ps_$${ps}.commands = $$FXC /nologo /E PS_$$ps /T ps_4_0 /Fh ${QMAKE_FILE_OUT} ${QMAKE_FILE_NAME}
++ fxc_ps_$${ps}.commands = $$FXC /nologo /E PS_$$ps /T ps_4_0_level_9_1 /Fh ${QMAKE_FILE_OUT} ${QMAKE_FILE_NAME}
+ fxc_ps_$${ps}.output = $$SHADER_DIR/$${ps}11ps.h
+ fxc_ps_$${ps}.input = CLEAR_INPUT
+ fxc_ps_$${ps}.dependency_type = TYPE_C
+@@ -217,7 +217,7 @@ for (vs, VERTEX_SHADERS_BLIT) {
+ QMAKE_EXTRA_COMPILERS += fxc_vs_$${vs}
+ }
+ for (vs, VERTEX_SHADERS_PASSTHROUGH) {
+- fxc_vs_$${vs}.commands = $$FXC /nologo /E VS_$$vs /T vs_4_0 /Fh ${QMAKE_FILE_OUT} ${QMAKE_FILE_NAME}
++ fxc_vs_$${vs}.commands = $$FXC /nologo /E VS_$$vs /T vs_4_0_level_9_1 /Fh ${QMAKE_FILE_OUT} ${QMAKE_FILE_NAME}
+ fxc_vs_$${vs}.output = $$SHADER_DIR/$${vs}11vs.h
+ fxc_vs_$${vs}.input = PASSTHROUGH_INPUT
+ fxc_vs_$${vs}.dependency_type = TYPE_C
+@@ -226,7 +226,7 @@ for (vs, VERTEX_SHADERS_PASSTHROUGH) {
+ QMAKE_EXTRA_COMPILERS += fxc_vs_$${vs}
+ }
+ for (vs, VERTEX_SHADERS_CLEAR) {
+- fxc_vs_$${vs}.commands = $$FXC /nologo /E VS_$$vs /T vs_4_0 /Fh ${QMAKE_FILE_OUT} ${QMAKE_FILE_NAME}
++ fxc_vs_$${vs}.commands = $$FXC /nologo /E VS_$$vs /T vs_4_0_level_9_1 /Fh ${QMAKE_FILE_OUT} ${QMAKE_FILE_NAME}
+ fxc_vs_$${vs}.output = $$SHADER_DIR/$${vs}11vs.h
+ fxc_vs_$${vs}.input = CLEAR_INPUT
+ fxc_vs_$${vs}.dependency_type = TYPE_C
+--
+1.8.4.msysgit.0
+
diff --git a/src/angle/src/common/common.pri b/src/angle/src/common/common.pri
index 514c80dbd1..58ad88673a 100644
--- a/src/angle/src/common/common.pri
+++ b/src/angle/src/common/common.pri
@@ -7,7 +7,7 @@ INCLUDEPATH += \
LIBS_PRIVATE = $$QMAKE_LIBS_CORE $$QMAKE_LIBS_GUI
# DirectX is included in the Windows 8 Kit, but everything else requires the DX SDK.
-win32-msvc2012|win32-msvc2013 {
+win32-msvc2012|win32-msvc2013|winrt {
FXC = fxc.exe
} else {
DX_DIR = $$(DXSDK_DIR)
diff --git a/src/angle/src/compiler/translator_common.pro b/src/angle/src/compiler/translator_common.pro
index b2812158e1..5581c9dc58 100644
--- a/src/angle/src/compiler/translator_common.pro
+++ b/src/angle/src/compiler/translator_common.pro
@@ -78,7 +78,6 @@ SOURCES += \
$$ANGLE_DIR/src/compiler/intermOut.cpp \
$$ANGLE_DIR/src/compiler/IntermTraverse.cpp \
$$ANGLE_DIR/src/compiler/MapLongVariableNames.cpp \
- $$ANGLE_DIR/src/compiler/ossource_win.cpp \
$$ANGLE_DIR/src/compiler/parseConst.cpp \
$$ANGLE_DIR/src/compiler/ParseHelper.cpp \
$$ANGLE_DIR/src/compiler/PoolAlloc.cpp \
@@ -98,6 +97,12 @@ SOURCES += \
$$ANGLE_DIR/src/compiler/timing/RestrictVertexShaderTiming.cpp \
$$ANGLE_DIR/src/third_party/compiler/ArrayBoundsClamper.cpp
+winrt {
+ SOURCES += $$ANGLE_DIR/src/compiler/ossource_winrt.cpp
+} else {
+ SOURCES += $$ANGLE_DIR/src/compiler/ossource_win.cpp
+}
+
# NOTE: 'win_flex' and 'bison' can be found in qt5/gnuwin32/bin
flex.commands = $$addGnuPath(win_flex) --noline --nounistd --outfile=${QMAKE_FILE_BASE}_lex.cpp ${QMAKE_FILE_NAME}
flex.output = ${QMAKE_FILE_BASE}_lex.cpp
diff --git a/src/angle/src/config.pri b/src/angle/src/config.pri
index 1c6d8b0167..ed2558117e 100644
--- a/src/angle/src/config.pri
+++ b/src/angle/src/config.pri
@@ -37,8 +37,9 @@ DEFINES += _WINDOWS \
NOMINMAX \
WIN32_LEAN_AND_MEAN=1
-# Defines specifying the API version (0x0600 = Vista)
-DEFINES += _WIN32_WINNT=0x0600 WINVER=0x0600
+# Defines specifying the API version (0x0600 = Vista, 0x0602 = Win8))
+winrt: DEFINES += _WIN32_WINNT=0x0602 WINVER=0x0602
+else: DEFINES += _WIN32_WINNT=0x0600 WINVER=0x0600
# ANGLE specific defines
DEFINES += ANGLE_DISABLE_TRACE \
diff --git a/src/angle/src/libGLESv2/libGLESv2.pro b/src/angle/src/libGLESv2/libGLESv2.pro
index 5d54c0f86a..5b3a49ed56 100644
--- a/src/angle/src/libGLESv2/libGLESv2.pro
+++ b/src/angle/src/libGLESv2/libGLESv2.pro
@@ -190,7 +190,7 @@ for (ps, PIXEL_SHADERS_BLIT) {
QMAKE_EXTRA_COMPILERS += fxc_ps_$${ps}
}
for (ps, PIXEL_SHADERS_PASSTHROUGH) {
- fxc_ps_$${ps}.commands = $$FXC /nologo /E PS_$$ps /T ps_4_0 /Fh ${QMAKE_FILE_OUT} ${QMAKE_FILE_NAME}
+ fxc_ps_$${ps}.commands = $$FXC /nologo /E PS_$$ps /T ps_4_0_level_9_1 /Fh ${QMAKE_FILE_OUT} ${QMAKE_FILE_NAME}
fxc_ps_$${ps}.output = $$SHADER_DIR/$${ps}11ps.h
fxc_ps_$${ps}.input = PASSTHROUGH_INPUT
fxc_ps_$${ps}.dependency_type = TYPE_C
@@ -199,7 +199,7 @@ for (ps, PIXEL_SHADERS_PASSTHROUGH) {
QMAKE_EXTRA_COMPILERS += fxc_ps_$${ps}
}
for (ps, PIXEL_SHADERS_CLEAR) {
- fxc_ps_$${ps}.commands = $$FXC /nologo /E PS_$$ps /T ps_4_0 /Fh ${QMAKE_FILE_OUT} ${QMAKE_FILE_NAME}
+ fxc_ps_$${ps}.commands = $$FXC /nologo /E PS_$$ps /T ps_4_0_level_9_1 /Fh ${QMAKE_FILE_OUT} ${QMAKE_FILE_NAME}
fxc_ps_$${ps}.output = $$SHADER_DIR/$${ps}11ps.h
fxc_ps_$${ps}.input = CLEAR_INPUT
fxc_ps_$${ps}.dependency_type = TYPE_C
@@ -217,7 +217,7 @@ for (vs, VERTEX_SHADERS_BLIT) {
QMAKE_EXTRA_COMPILERS += fxc_vs_$${vs}
}
for (vs, VERTEX_SHADERS_PASSTHROUGH) {
- fxc_vs_$${vs}.commands = $$FXC /nologo /E VS_$$vs /T vs_4_0 /Fh ${QMAKE_FILE_OUT} ${QMAKE_FILE_NAME}
+ fxc_vs_$${vs}.commands = $$FXC /nologo /E VS_$$vs /T vs_4_0_level_9_1 /Fh ${QMAKE_FILE_OUT} ${QMAKE_FILE_NAME}
fxc_vs_$${vs}.output = $$SHADER_DIR/$${vs}11vs.h
fxc_vs_$${vs}.input = PASSTHROUGH_INPUT
fxc_vs_$${vs}.dependency_type = TYPE_C
@@ -226,7 +226,7 @@ for (vs, VERTEX_SHADERS_PASSTHROUGH) {
QMAKE_EXTRA_COMPILERS += fxc_vs_$${vs}
}
for (vs, VERTEX_SHADERS_CLEAR) {
- fxc_vs_$${vs}.commands = $$FXC /nologo /E VS_$$vs /T vs_4_0 /Fh ${QMAKE_FILE_OUT} ${QMAKE_FILE_NAME}
+ fxc_vs_$${vs}.commands = $$FXC /nologo /E VS_$$vs /T vs_4_0_level_9_1 /Fh ${QMAKE_FILE_OUT} ${QMAKE_FILE_NAME}
fxc_vs_$${vs}.output = $$SHADER_DIR/$${vs}11vs.h
fxc_vs_$${vs}.input = CLEAR_INPUT
fxc_vs_$${vs}.dependency_type = TYPE_C
diff --git a/src/corelib/global/qcompilerdetection.h b/src/corelib/global/qcompilerdetection.h
index 44077132d4..2b957782e8 100644
--- a/src/corelib/global/qcompilerdetection.h
+++ b/src/corelib/global/qcompilerdetection.h
@@ -98,6 +98,7 @@
# define Q_UNREACHABLE_IMPL() __assume(0)
# define Q_NORETURN __declspec(noreturn)
# define Q_DECL_DEPRECATED __declspec(deprecated)
+# define Q_DECL_DEPRECATED_X(text) __declspec(deprecated(text))
# define Q_DECL_EXPORT __declspec(dllexport)
# define Q_DECL_IMPORT __declspec(dllimport)
/* Intel C++ disguising as Visual C++: the `using' keyword avoids warnings */
@@ -152,6 +153,9 @@
# define Q_CC_INTEL
# define Q_ASSUME_IMPL(expr) __assume(expr)
# define Q_UNREACHABLE_IMPL() __builtin_unreachable()
+# if __INTEL_COMPILER >= 1300
+# define Q_DECL_DEPRECATED_X(text) __attribute__ ((__deprecated__(text)))
+# endif
# elif defined(__clang__)
/* Clang also masquerades as GCC */
# define Q_CC_CLANG
@@ -173,6 +177,7 @@
# if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
# define Q_ASSUME_IMPL(expr) if (expr){} else __builtin_unreachable()
# define Q_UNREACHABLE_IMPL() __builtin_unreachable()
+# define Q_DECL_DEPRECATED_X(text) __attribute__ ((__deprecated__(text)))
# endif
# endif
@@ -528,6 +533,9 @@
# if !__has_feature(cxx_rtti)
# define QT_NO_RTTI
# endif
+# if __has_feature(attribute_deprecated_with_message)
+# define Q_DECL_DEPRECATED_X(text) __attribute__ ((__deprecated__(text)))
+# endif
/* C++11 features, see http://clang.llvm.org/cxx_status.html */
# if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
@@ -861,6 +869,9 @@
#ifndef Q_DECL_VARIABLE_DEPRECATED
# define Q_DECL_VARIABLE_DEPRECATED Q_DECL_DEPRECATED
#endif
+#ifndef Q_DECL_DEPRECATED_X
+# define Q_DECL_DEPRECATED_X(text) Q_DECL_DEPRECATED
+#endif
#ifndef Q_DECL_EXPORT
# define Q_DECL_EXPORT
#endif
diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp
index 0c72bd7022..f72d27c2e5 100644
--- a/src/corelib/global/qglobal.cpp
+++ b/src/corelib/global/qglobal.cpp
@@ -2188,7 +2188,9 @@ QString qt_error_string(int errorCode)
s = QT_TRANSLATE_NOOP("QIODevice", "No space left on device");
break;
default: {
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN)
+ // Retrieve the system error message for the last-error code.
+# ifndef Q_OS_WINRT
wchar_t *string = 0;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
@@ -2199,6 +2201,17 @@ QString qt_error_string(int errorCode)
NULL);
ret = QString::fromWCharArray(string);
LocalFree((HLOCAL)string);
+# else // !Q_OS_WINRT
+ __declspec(thread) static wchar_t errorString[4096];
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ errorCode,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ errorString,
+ ARRAYSIZE(errorString),
+ NULL);
+ ret = QString::fromWCharArray(errorString);
+# endif // Q_OS_WINRT
if (ret.isEmpty() && errorCode == ERROR_MOD_NOT_FOUND)
ret = QString::fromLatin1("The specified module could not be found.");
diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h
index 3c3d35e373..da5f65fafb 100644
--- a/src/corelib/global/qglobal.h
+++ b/src/corelib/global/qglobal.h
@@ -45,11 +45,11 @@
#include <stddef.h>
-#define QT_VERSION_STR "5.2.1"
+#define QT_VERSION_STR "5.3.0"
/*
QT_VERSION is (major << 16) + (minor << 8) + patch.
*/
-#define QT_VERSION 0x050201
+#define QT_VERSION 0x050300
/*
can be used like #if (QT_VERSION >= QT_VERSION_CHECK(4, 4, 0))
*/
@@ -191,7 +191,7 @@ typedef qint64 qlonglong;
typedef quint64 qulonglong;
#ifndef QT_POINTER_SIZE
-# if defined(Q_OS_WIN64)
+# if defined(Q_OS_WIN64) || (defined(Q_OS_WINRT) && defined(_M_X64))
# define QT_POINTER_SIZE 8
# elif defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_WINRT)
# define QT_POINTER_SIZE 4
@@ -221,11 +221,14 @@ typedef double qreal;
#if defined(QT_NO_DEPRECATED)
# undef QT_DEPRECATED
+# undef QT_DEPRECATED_X
# undef QT_DEPRECATED_VARIABLE
# undef QT_DEPRECATED_CONSTRUCTOR
#elif defined(QT_DEPRECATED_WARNINGS)
# undef QT_DEPRECATED
# define QT_DEPRECATED Q_DECL_DEPRECATED
+# undef QT_DEPRECATED_X
+# define QT_DEPRECATED_X(text) Q_DECL_DEPRECATED_X(text)
# undef QT_DEPRECATED_VARIABLE
# define QT_DEPRECATED_VARIABLE Q_DECL_VARIABLE_DEPRECATED
# undef QT_DEPRECATED_CONSTRUCTOR
@@ -233,6 +236,8 @@ typedef double qreal;
#else
# undef QT_DEPRECATED
# define QT_DEPRECATED
+# undef QT_DEPRECATED_X
+# define QT_DEPRECATED_X(text)
# undef QT_DEPRECATED_VARIABLE
# define QT_DEPRECATED_VARIABLE
# undef QT_DEPRECATED_CONSTRUCTOR
@@ -547,6 +552,7 @@ class QDataStream;
#if defined(Q_OS_WINRT)
# define QT_NO_PROCESS
+# define QT_NO_FILESYSTEMWATCHER
#endif
inline void qt_noop(void) {}
diff --git a/src/corelib/global/qlibraryinfo.cpp b/src/corelib/global/qlibraryinfo.cpp
index 1c3b09f602..22a9980812 100644
--- a/src/corelib/global/qlibraryinfo.cpp
+++ b/src/corelib/global/qlibraryinfo.cpp
@@ -40,6 +40,7 @@
****************************************************************************/
#include "qdir.h"
+#include "qstringlist.h"
#include "qfile.h"
#include "qsettings.h"
#include "qlibraryinfo.h"
@@ -113,6 +114,8 @@ public:
}
};
+static const char platformsSection[] = "Platforms";
+
QLibrarySettings::QLibrarySettings()
: settings(QLibraryInfoPrivate::findConfiguration())
{
@@ -132,7 +135,8 @@ QLibrarySettings::QLibrarySettings()
haveEffectivePaths = children.contains(QLatin1String("EffectivePaths"));
#endif
// Backwards compat: an existing but empty file is claimed to contain the Paths section.
- havePaths = !haveEffectivePaths || children.contains(QLatin1String("Paths"));
+ havePaths = (!haveEffectivePaths && !children.contains(QLatin1String(platformsSection)))
+ || children.contains(QLatin1String("Paths"));
#ifndef QT_BOOTSTRAPPED
if (!havePaths)
settings.reset(0);
@@ -466,6 +470,33 @@ QLibraryInfo::rawLocation(LibraryLocation loc, PathGroup group)
}
/*!
+ Returns additional arguments to the platform plugin matching
+ \a platformName which can be specified as a string list using
+ the key \c Arguments in a group called \c Platforms of the
+ \c qt.conf file.
+
+ sa {Using qt.conf}
+
+ \internal
+
+ \since 5.3
+*/
+
+QStringList QLibraryInfo::platformPluginArguments(const QString &platformName)
+{
+#ifndef QT_BOOTSTRAPPED
+ if (const QSettings *settings = QLibraryInfoPrivate::findConfiguration()) {
+ QString key = QLatin1String(platformsSection);
+ key += QLatin1Char('/');
+ key += platformName;
+ key += QLatin1String("Arguments");
+ return settings->value(key).toStringList();
+ }
+#endif // !QT_BOOTSTRAPPED
+ return QStringList();
+}
+
+/*!
\enum QLibraryInfo::LibraryLocation
\keyword library location
diff --git a/src/corelib/global/qlibraryinfo.h b/src/corelib/global/qlibraryinfo.h
index 17864b555b..54ef794d3e 100644
--- a/src/corelib/global/qlibraryinfo.h
+++ b/src/corelib/global/qlibraryinfo.h
@@ -47,6 +47,8 @@
QT_BEGIN_NAMESPACE
+class QStringList;
+
class Q_CORE_EXPORT QLibraryInfo
{
public:
@@ -96,6 +98,8 @@ public:
static QString rawLocation(LibraryLocation, PathGroup);
#endif
+ static QStringList platformPluginArguments(const QString &platformName);
+
private:
QLibraryInfo();
};
diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h
index 8a46f3a6ab..758f13596e 100644
--- a/src/corelib/global/qnamespace.h
+++ b/src/corelib/global/qnamespace.h
@@ -1596,6 +1596,12 @@ public:
ScrollUpdate,
ScrollEnd
};
+
+ enum MouseEventSource {
+ MouseEventNotSynthesized,
+ MouseEventSynthesizedBySystem,
+ MouseEventSynthesizedByQt
+ };
}
#ifdef Q_MOC_RUN
;
diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc
index 90f38fd51c..e33fd085f2 100644
--- a/src/corelib/global/qnamespace.qdoc
+++ b/src/corelib/global/qnamespace.qdoc
@@ -2897,3 +2897,25 @@
\value ScrollEnd Scrolling has ended, but the scrolling distance
did not change anymore.
*/
+
+/*!
+ \enum Qt::MouseEventSource
+ \since 5.3
+
+ This enum describes the source of a mouse event and can be useful
+ to determine if the event is an artificial mouse event originating
+ from another device such as a touchscreen.
+
+ \value MouseEventNotSynthesized The most common value. On
+ platforms where such information is available this value indicates
+ that the event was generated in response to a genuine mouse event
+ in the system.
+
+ \value MouseEventSynthesizedBySystem Indicates that the mouse
+ event was synthesized from a touch event by the platform.
+
+ \value MouseEventSynthesizedByQt Indicates that the mouse event was
+ synthesized from an unhandled touch event by Qt.
+
+ \sa Qt::AA_SynthesizeMouseForUnhandledTouchEvents
+*/
diff --git a/src/corelib/global/qnumeric.cpp b/src/corelib/global/qnumeric.cpp
index d09357c26c..7d8bd8c3c6 100644
--- a/src/corelib/global/qnumeric.cpp
+++ b/src/corelib/global/qnumeric.cpp
@@ -41,6 +41,7 @@
#include "qnumeric.h"
#include "qnumeric_p.h"
+#include <string.h>
QT_BEGIN_NAMESPACE
@@ -99,4 +100,139 @@ Q_CORE_EXPORT double qQNaN() { return qt_qnan(); }
Q_CORE_EXPORT double qInf() { return qt_inf(); }
+
+/*!
+ \internal
+ */
+static inline quint32 f2i(float f)
+{
+ quint32 i;
+ memcpy(&i, &f, sizeof(f));
+ return i;
+}
+
+/*!
+ Returns the number of representable floating-point numbers between \a a and \a b.
+
+ This function provides an alternative way of doing approximated comparisons of floating-point
+ numbers similar to qFuzzyCompare(). However, it returns the distance between two numbers, which
+ gives the caller a possibility to choose the accepted error. Errors are relative, so for
+ instance the distance between 1.0E-5 and 1.00001E-5 will give 110, while the distance between
+ 1.0E36 and 1.00001E36 will give 127.
+
+ This function is useful if a floating point comparison requires a certain precision.
+ Therefore, if \a a and \a b are equal it will return 0. The maximum value it will return for 32-bit
+ floating point numbers is 4,278,190,078. This is the distance between \c{-FLT_MAX} and
+ \c{+FLT_MAX}.
+
+ The function does not give meaningful results if any of the arguments are \c Infinite or \c NaN.
+ You can check for this by calling qIsFinite().
+
+ The return value can be considered as the "error", so if you for instance want to compare
+ two 32-bit floating point numbers and all you need is an approximated 24-bit precision, you can
+ use this function like this:
+
+ \code
+ if (qFloatDistance(a, b) < (1 << 7)) { // The last 7 bits are not
+ // significant
+ // precise enough
+ }
+ \endcode
+
+ \sa qFuzzyCompare()
+ \relates <QtGlobal>
+*/
+Q_CORE_EXPORT quint32 qFloatDistance(float a, float b)
+{
+ static const quint32 smallestPositiveFloatAsBits = 0x00000001; // denormalized, (SMALLEST), (1.4E-45)
+ /* Assumes:
+ * IEE754 format.
+ * Integers and floats have the same endian
+ */
+ Q_STATIC_ASSERT(sizeof(quint32) == sizeof(float));
+ Q_ASSERT(qIsFinite(a) && qIsFinite(b));
+ if (a == b)
+ return 0;
+ if ((a < 0) != (b < 0)) {
+ // if they have different signs
+ if (a < 0)
+ a = -a;
+ else /*if (b < 0)*/
+ b = -b;
+ return qFloatDistance(0.0F, a) + qFloatDistance(0.0F, b);
+ }
+ if (a < 0) {
+ a = -a;
+ b = -b;
+ }
+ // at this point a and b should not be negative
+
+ // 0 is special
+ if (!a)
+ return f2i(b) - smallestPositiveFloatAsBits + 1;
+ if (!b)
+ return f2i(a) - smallestPositiveFloatAsBits + 1;
+
+ // finally do the common integer subtraction
+ return a > b ? f2i(a) - f2i(b) : f2i(b) - f2i(a);
+}
+
+
+/*!
+ \internal
+ */
+static inline quint64 d2i(double d)
+{
+ quint64 i;
+ memcpy(&i, &d, sizeof(d));
+ return i;
+}
+
+/*!
+ Returns the number of representable floating-point numbers between \a a and \a b.
+
+ This function serves the same purpose as \c{qFloatDistance(float, float)}, but
+ returns the distance between two \c double numbers. Since the range is larger
+ than for two \c float numbers (\c{[-DBL_MAX,DBL_MAX]}), the return type is quint64.
+
+
+ \sa qFuzzyCompare()
+ \relates <QtGlobal>
+*/
+Q_CORE_EXPORT quint64 qFloatDistance(double a, double b)
+{
+ static const quint64 smallestPositiveFloatAsBits = 0x1; // denormalized, (SMALLEST)
+ /* Assumes:
+ * IEE754 format double precision
+ * Integers and floats have the same endian
+ */
+ Q_STATIC_ASSERT(sizeof(quint64) == sizeof(double));
+ Q_ASSERT(qIsFinite(a) && qIsFinite(b));
+ if (a == b)
+ return 0;
+ if ((a < 0) != (b < 0)) {
+ // if they have different signs
+ if (a < 0)
+ a = -a;
+ else /*if (b < 0)*/
+ b = -b;
+ return qFloatDistance(0.0, a) + qFloatDistance(0.0, b);
+ }
+ if (a < 0) {
+ a = -a;
+ b = -b;
+ }
+ // at this point a and b should not be negative
+
+ // 0 is special
+ if (!a)
+ return d2i(b) - smallestPositiveFloatAsBits + 1;
+ if (!b)
+ return d2i(a) - smallestPositiveFloatAsBits + 1;
+
+ // finally do the common integer subtraction
+ return a > b ? d2i(a) - d2i(b) : d2i(b) - d2i(a);
+}
+
+
QT_END_NAMESPACE
diff --git a/src/corelib/global/qnumeric.h b/src/corelib/global/qnumeric.h
index 25db5443eb..633486dff1 100644
--- a/src/corelib/global/qnumeric.h
+++ b/src/corelib/global/qnumeric.h
@@ -57,6 +57,9 @@ Q_CORE_EXPORT double qSNaN();
Q_CORE_EXPORT double qQNaN();
Q_CORE_EXPORT double qInf();
+Q_CORE_EXPORT quint32 qFloatDistance(float a, float b);
+Q_CORE_EXPORT quint64 qFloatDistance(double a, double b);
+
#define Q_INFINITY (QT_PREPEND_NAMESPACE(qInf)())
#define Q_SNAN (QT_PREPEND_NAMESPACE(qSNaN)())
#define Q_QNAN (QT_PREPEND_NAMESPACE(qQNaN)())
diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri
index f06bf61b09..0ec3d949b9 100644
--- a/src/corelib/io/io.pri
+++ b/src/corelib/io/io.pri
@@ -94,7 +94,6 @@ SOURCES += \
io/qloggingregistry.cpp
win32 {
- SOURCES += io/qsettings_win.cpp
SOURCES += io/qfsfileengine_win.cpp
SOURCES += io/qlockfile_win.cpp
@@ -102,11 +101,12 @@ win32 {
HEADERS += io/qfilesystemwatcher_win_p.h
SOURCES += io/qfilesystemengine_win.cpp
SOURCES += io/qfilesystemiterator_win.cpp
- SOURCES += io/qstandardpaths_win.cpp
!winrt {
+ SOURCES += io/qsettings_win.cpp
HEADERS += io/qwindowspipewriter_p.h
SOURCES += io/qwindowspipewriter.cpp
+ SOURCES += io/qstandardpaths_win.cpp
wince* {
SOURCES += io/qprocess_wince.cpp
@@ -119,6 +119,8 @@ win32 {
io/qwinoverlappedionotifier.cpp \
io/qwindowspipereader.cpp
}
+ } else {
+ SOURCES += io/qstandardpaths_winrt.cpp
}
} else:unix|integrity {
SOURCES += \
diff --git a/src/corelib/io/qdatastream.cpp b/src/corelib/io/qdatastream.cpp
index af5605f8c7..a6fbffee7e 100644
--- a/src/corelib/io/qdatastream.cpp
+++ b/src/corelib/io/qdatastream.cpp
@@ -251,7 +251,7 @@ QT_BEGIN_NAMESPACE
return retVal;
enum {
- DefaultStreamVersion = QDataStream::Qt_5_2
+ DefaultStreamVersion = QDataStream::Qt_5_3
};
/*!
@@ -541,6 +541,7 @@ void QDataStream::setByteOrder(ByteOrder bo)
\value Qt_5_0 Version 13 (Qt 5.0)
\value Qt_5_1 Version 14 (Qt 5.1)
\value Qt_5_2 Version 15 (Qt 5.2)
+ \value Qt_5_3 Same as Qt_5_2
\sa setVersion(), version()
*/
diff --git a/src/corelib/io/qdatastream.h b/src/corelib/io/qdatastream.h
index f107e801b6..28f1d51a12 100644
--- a/src/corelib/io/qdatastream.h
+++ b/src/corelib/io/qdatastream.h
@@ -87,8 +87,9 @@ public:
Qt_4_9 = Qt_4_8,
Qt_5_0 = 13,
Qt_5_1 = 14,
- Qt_5_2 = 15
-#if QT_VERSION >= 0x050300
+ Qt_5_2 = 15,
+ Qt_5_3 = Qt_5_2
+#if QT_VERSION >= 0x050400
#error Add the datastream version for this Qt version
#endif
};
diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp
index db27bed9d6..624f45caaf 100644
--- a/src/corelib/io/qdir.cpp
+++ b/src/corelib/io/qdir.cpp
@@ -2144,7 +2144,7 @@ QString QDir::cleanPath(const QString &path)
name.replace(dir_separator, QLatin1Char('/'));
bool allowUncPaths = false;
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) //allow unc paths
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT) //allow unc paths
allowUncPaths = true;
#endif
diff --git a/src/corelib/io/qfilesystemengine_win.cpp b/src/corelib/io/qfilesystemengine_win.cpp
index 257f18a6bb..dbc6d28846 100644
--- a/src/corelib/io/qfilesystemengine_win.cpp
+++ b/src/corelib/io/qfilesystemengine_win.cpp
@@ -63,13 +63,28 @@
# include <types.h>
#endif
#include <objbase.h>
-#include <shlobj.h>
+#ifndef Q_OS_WINRT
+# include <shlobj.h>
+# include <accctrl.h>
+#endif
#include <initguid.h>
-#include <accctrl.h>
#include <ctype.h>
#include <limits.h>
-#define SECURITY_WIN32
-#include <security.h>
+#ifndef Q_OS_WINRT
+# define SECURITY_WIN32
+# include <security.h>
+#else // !Q_OS_WINRT
+# include <wrl.h>
+# include <windows.foundation.h>
+# include <windows.storage.h>
+# include <Windows.ApplicationModel.h>
+
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Storage;
+using namespace ABI::Windows::ApplicationModel;
+#endif // Q_OS_WINRT
#ifndef SPI_GETPLATFORMTYPE
#define SPI_GETPLATFORMTYPE 257
@@ -141,7 +156,7 @@ QT_BEGIN_NAMESPACE
Q_CORE_EXPORT int qt_ntfs_permission_lookup = 0;
-#if defined(Q_OS_WINCE)
+#if defined(Q_OS_WINCE) || defined(Q_OS_WINRT)
static QString qfsPrivateCurrentDir = QLatin1String("");
// As none of the functions we try to resolve do exist on Windows CE
// we use QT_NO_LIBRARY to shorten everything up a little bit.
@@ -289,14 +304,14 @@ static bool resolveUNCLibs()
}
#endif
triedResolve = true;
-#if !defined(Q_OS_WINCE)
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
HINSTANCE hLib = QSystemLibrary::load(L"Netapi32");
if (hLib) {
ptrNetShareEnum = (PtrNetShareEnum)GetProcAddress(hLib, "NetShareEnum");
if (ptrNetShareEnum)
ptrNetApiBufferFree = (PtrNetApiBufferFree)GetProcAddress(hLib, "NetApiBufferFree");
}
-#endif
+#endif // !Q_OS_WINCE && !Q_OS_WINRT
}
return ptrNetShareEnum && ptrNetApiBufferFree;
}
@@ -304,7 +319,7 @@ static bool resolveUNCLibs()
static QString readSymLink(const QFileSystemEntry &link)
{
QString result;
-#if !defined(Q_OS_WINCE)
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
HANDLE handle = CreateFile((wchar_t*)link.nativeFilePath().utf16(),
FILE_READ_EA,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
@@ -347,11 +362,11 @@ static QString readSymLink(const QFileSystemEntry &link)
result.replace(0,matchVolName.matchedLength(), QString::fromWCharArray(buffer));
}
}
-#endif
+#endif // !Q_OS_WINCE && !Q_OS_WINRT
}
#else
Q_UNUSED(link);
-#endif // Q_OS_WINCE
+#endif // Q_OS_WINCE || Q_OS_WINRT
return result;
}
@@ -432,7 +447,11 @@ static inline bool getFindData(QString path, WIN32_FIND_DATA &findData)
// can't handle drives
if (!path.endsWith(QLatin1Char(':'))) {
+#ifndef Q_OS_WINRT
HANDLE hFind = ::FindFirstFile((wchar_t*)path.utf16(), &findData);
+#else
+ HANDLE hFind = ::FindFirstFileEx((const wchar_t*)path.utf16(), FindExInfoStandard, &findData, FindExSearchNameMatch, NULL, 0);
+#endif
if (hFind != INVALID_HANDLE_VALUE) {
::FindClose(hFind);
return true;
@@ -506,7 +525,7 @@ QString QFileSystemEngine::nativeAbsoluteFilePath(const QString &path)
{
// can be //server or //server/share
QString absPath;
-#if !defined(Q_OS_WINCE)
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
QVarLengthArray<wchar_t, MAX_PATH> buf(qMax(MAX_PATH, path.size() + 1));
wchar_t *fileName = 0;
DWORD retLen = GetFullPathName((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName);
@@ -516,12 +535,17 @@ QString QFileSystemEngine::nativeAbsoluteFilePath(const QString &path)
}
if (retLen != 0)
absPath = QString::fromWCharArray(buf.data(), retLen);
-#else
+#elif !defined(Q_OS_WINCE)
+ if (QDir::isRelativePath(path))
+ absPath = QDir::toNativeSeparators(QDir::cleanPath(QDir::currentPath() + QLatin1Char('/') + path));
+ else
+ absPath = QDir::toNativeSeparators(QDir::cleanPath(path));
+#else // Q_OS_WINRT
if (path.startsWith(QLatin1Char('/')) || path.startsWith(QLatin1Char('\\')))
absPath = QDir::toNativeSeparators(path);
else
absPath = QDir::toNativeSeparators(QDir::cleanPath(qfsPrivateCurrentDir + QLatin1Char('/') + path));
-#endif
+#endif // Q_OS_WINCE
// This is really ugly, but GetFullPathName strips off whitespace at the end.
// If you for instance write ". " in the lineedit of QFileDialog,
// (which is an invalid filename) this function will strip the space off and viola,
@@ -548,9 +572,17 @@ QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry)
ret = entry.filePath();
#endif
} else {
+#ifndef Q_OS_WINRT
ret = QDir::cleanPath(QDir::currentPath() + QLatin1Char('/') + entry.filePath());
+#else
+ // Some WinRT APIs do not support absolute paths (due to sandboxing).
+ // Thus the port uses the executable's directory as its root directory
+ // and treats paths relative to that as absolute paths.
+ ret = QDir::cleanPath(QDir::current().relativeFilePath(entry.filePath()));
+#endif
}
+#ifndef Q_OS_WINRT
// The path should be absolute at this point.
// From the docs :
// Absolute paths begin with the directory separator "/"
@@ -563,6 +595,7 @@ QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry)
// Force uppercase drive letters.
ret[0] = ret.at(0).toUpper();
}
+#endif // !Q_OS_WINRT
return QFileSystemEntry(ret, QFileSystemEntry::FromInternalPath());
}
@@ -590,18 +623,24 @@ typedef struct _FILE_ID_INFO {
static inline QByteArray fileId(HANDLE handle)
{
QByteArray result;
+#ifndef Q_OS_WINRT
BY_HANDLE_FILE_INFORMATION info;
if (GetFileInformationByHandle(handle, &info)) {
result = QByteArray::number(uint(info.nFileIndexLow), 16);
result += ':';
result += QByteArray::number(uint(info.nFileIndexHigh), 16);
}
+#else // !Q_OS_WINRT
+ Q_UNUSED(handle);
+ Q_UNIMPLEMENTED();
+#endif // Q_OS_WINRT
return result;
}
// File ID for Windows starting from version 8.
QByteArray fileIdWin8(HANDLE handle)
{
+#ifndef Q_OS_WINRT
typedef BOOL (WINAPI* GetFileInformationByHandleExType)(HANDLE, Q_FILE_INFO_BY_HANDLE_CLASS, void *, DWORD);
// Dynamically resolve GetFileInformationByHandleEx (Vista onwards).
@@ -621,6 +660,16 @@ QByteArray fileIdWin8(HANDLE handle)
result += QByteArray((char *)&infoEx.FileId, sizeof(infoEx.FileId)).toHex();
}
}
+#else // !Q_OS_WINRT
+ QByteArray result;
+ FILE_ID_INFO infoEx;
+ if (GetFileInformationByHandleEx(handle, FileIdInfo,
+ &infoEx, sizeof(FILE_ID_INFO))) {
+ result = QByteArray::number(infoEx.VolumeSerialNumber, 16);
+ result += ':';
+ result += QByteArray((char *)infoEx.FileId.Identifier, sizeof(infoEx.FileId.Identifier)).toHex();
+ }
+#endif // Q_OS_WINRT
return result;
}
#endif // !Q_OS_WINCE
@@ -631,8 +680,13 @@ QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry)
#ifndef Q_OS_WINCE
QByteArray result;
const HANDLE handle =
+#ifndef Q_OS_WINRT
CreateFile((wchar_t*)entry.nativeFilePath().utf16(), GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+#else // !Q_OS_WINRT
+ CreateFile2((const wchar_t*)entry.nativeFilePath().utf16(), GENERIC_READ,
+ FILE_SHARE_READ, OPEN_EXISTING, NULL);
+#endif // Q_OS_WINRT
if (handle) {
result = QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS8 ?
fileIdWin8(handle) : fileId(handle);
@@ -810,7 +864,7 @@ static bool tryDriveUNCFallback(const QFileSystemEntry &fname, QFileSystemMetaDa
{
bool entryExists = false;
DWORD fileAttrib = 0;
-#if !defined(Q_OS_WINCE)
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
if (fname.isDriveRoot()) {
// a valid drive ??
DWORD drivesBitmask = ::GetLogicalDrives();
@@ -851,7 +905,7 @@ static bool tryDriveUNCFallback(const QFileSystemEntry &fname, QFileSystemMetaDa
fileAttrib = FILE_ATTRIBUTE_DIRECTORY;
entryExists = true;
}
-#if !defined(Q_OS_WINCE)
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
}
#endif
if (entryExists)
@@ -894,12 +948,32 @@ bool QFileSystemEngine::fillMetaData(HANDLE fHandle, QFileSystemMetaData &data,
{
data.entryFlags &= ~what;
clearWinStatData(data);
+#ifndef Q_OS_WINRT
BY_HANDLE_FILE_INFORMATION fileInfo;
UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
if (GetFileInformationByHandle(fHandle , &fileInfo)) {
data.fillFromFindInfo(fileInfo);
}
SetErrorMode(oldmode);
+#else // !Q_OS_WINRT
+ FILE_BASIC_INFO fileBasicInfo;
+ if (GetFileInformationByHandleEx(fHandle, FileBasicInfo, &fileBasicInfo, sizeof(fileBasicInfo))) {
+ data.fillFromFileAttribute(fileBasicInfo.FileAttributes);
+ data.creationTime_.dwHighDateTime = fileBasicInfo.CreationTime.HighPart;
+ data.creationTime_.dwLowDateTime = fileBasicInfo.CreationTime.LowPart;
+ data.lastAccessTime_.dwHighDateTime = fileBasicInfo.LastAccessTime.HighPart;
+ data.lastAccessTime_.dwLowDateTime = fileBasicInfo.LastAccessTime.LowPart;
+ data.lastWriteTime_.dwHighDateTime = fileBasicInfo.LastWriteTime.HighPart;
+ data.lastWriteTime_.dwLowDateTime = fileBasicInfo.LastWriteTime.LowPart;
+ if (!(data.fileAttribute_ & FILE_ATTRIBUTE_DIRECTORY)) {
+ FILE_STANDARD_INFO fileStandardInfo;
+ if (GetFileInformationByHandleEx(fHandle, FileStandardInfo, &fileStandardInfo, sizeof(fileStandardInfo)))
+ data.size_ = fileStandardInfo.EndOfFile.QuadPart;
+ } else
+ data.size_ = 0;
+ data.knownFlagsMask |= QFileSystemMetaData::Times | QFileSystemMetaData::SizeAttribute;
+ }
+#endif // Q_OS_WINRT
return data.hasFlags(what);
}
@@ -931,7 +1005,9 @@ bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemM
}
if (what & QFileSystemMetaData::WinStatFlags) {
+#ifndef Q_OS_WINRT
UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
+#endif
clearWinStatData(data);
WIN32_FIND_DATA findData;
// The memory structure for WIN32_FIND_DATA is same as WIN32_FILE_ATTRIBUTE_DATA
@@ -943,11 +1019,15 @@ bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemM
} else {
if (!tryFindFallback(fname, data))
if (!tryDriveUNCFallback(fname, data)) {
+#ifndef Q_OS_WINRT
SetErrorMode(oldmode);
+#endif
return false;
}
}
+#ifndef Q_OS_WINRT
SetErrorMode(oldmode);
+#endif
}
if (what & QFileSystemMetaData::Permissions)
@@ -1006,7 +1086,14 @@ static bool isDirPath(const QString &dirPath, bool *existed)
if (path.length() == 2 && path.at(1) == QLatin1Char(':'))
path += QLatin1Char('\\');
+#ifndef Q_OS_WINRT
DWORD fileAttrib = ::GetFileAttributes((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16());
+#else // Q_OS_WINRT
+ DWORD fileAttrib = INVALID_FILE_ATTRIBUTES;
+ WIN32_FILE_ATTRIBUTE_DATA data;
+ if (::GetFileAttributesEx((const wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16(), GetFileExInfoStandard, &data))
+ fileAttrib = data.dwFileAttributes;
+#endif // Q_OS_WINRT
if (fileAttrib == INVALID_FILE_ATTRIBUTES) {
int errorCode = GetLastError();
if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) {
@@ -1100,6 +1187,30 @@ QString QFileSystemEngine::rootPath()
{
#if defined(Q_OS_WINCE)
QString ret = QLatin1String("/");
+#elif defined(Q_OS_WINRT)
+ // We specify the package root as root directory
+ QString ret = QLatin1String("/");
+ // Get package location
+ ComPtr<IPackageStatics> statics;
+ if (FAILED(GetActivationFactory(HStringReference(RuntimeClass_Windows_ApplicationModel_Package).Get(), &statics)))
+ return ret;
+ ComPtr<IPackage> package;
+ if (FAILED(statics->get_Current(&package)))
+ return ret;
+ ComPtr<IStorageFolder> installedLocation;
+ if (FAILED(package->get_InstalledLocation(&installedLocation)))
+ return ret;
+
+ ComPtr<IStorageItem> item;
+ if (FAILED(installedLocation.As(&item)))
+ return ret;
+
+ HSTRING finalWinPath;
+ if (FAILED(item->get_Path(&finalWinPath)))
+ return ret;
+
+ ret = QDir::fromNativeSeparators(QString::fromWCharArray(WindowsGetStringRawBuffer(finalWinPath, nullptr)));
+
#else
QString ret = QString::fromLatin1(qgetenv("SystemDrive").constData());
if (ret.isEmpty())
@@ -1158,12 +1269,13 @@ QString QFileSystemEngine::homePath()
QString QFileSystemEngine::tempPath()
{
QString ret;
+#ifndef Q_OS_WINRT
wchar_t tempPath[MAX_PATH];
const DWORD len = GetTempPath(MAX_PATH, tempPath);
#ifdef Q_OS_WINCE
if (len)
ret = QString::fromWCharArray(tempPath, len);
-#else
+#else // Q_OS_WINCE
if (len) { // GetTempPath() can return short names, expand.
wchar_t longTempPath[MAX_PATH];
const DWORD longLen = GetLongPathName(tempPath, longTempPath, MAX_PATH);
@@ -1171,12 +1283,33 @@ QString QFileSystemEngine::tempPath()
QString::fromWCharArray(longTempPath, longLen) :
QString::fromWCharArray(tempPath, len);
}
-#endif
+#endif // !Q_OS_WINCE
if (!ret.isEmpty()) {
while (ret.endsWith(QLatin1Char('\\')))
ret.chop(1);
ret = QDir::fromNativeSeparators(ret);
}
+#else // !Q_OS_WINRT
+ // According to http://msdn.microsoft.com/en-us/library/windows/apps/windows.storage.applicationdata.temporaryfolder.aspx
+ // the API is not available on winphone which should cause one of the functions
+ // below to fail
+ ComPtr<IApplicationDataStatics> applicationDataStatics;
+ if (FAILED(GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_ApplicationData).Get(), &applicationDataStatics)))
+ return ret;
+ ComPtr<IApplicationData> applicationData;
+ if (FAILED(applicationDataStatics->get_Current(&applicationData)))
+ return ret;
+ ComPtr<IStorageFolder> tempFolder;
+ if (FAILED(applicationData->get_TemporaryFolder(&tempFolder)))
+ return ret;
+ ComPtr<IStorageItem> tempFolderItem;
+ if (FAILED(tempFolder.As(&tempFolderItem)))
+ return ret;
+ HSTRING path;
+ if (FAILED(tempFolderItem->get_Path(&path)))
+ return ret;
+ ret = QDir::fromNativeSeparators(QString::fromWCharArray(WindowsGetStringRawBuffer(path, nullptr)));
+#endif // Q_OS_WINRT
if (ret.isEmpty()) {
#if !defined(Q_OS_WINCE)
ret = QLatin1String("C:/tmp");
@@ -1195,7 +1328,7 @@ bool QFileSystemEngine::setCurrentPath(const QFileSystemEntry &entry)
if(!(meta.exists() && meta.isDirectory()))
return false;
-#if !defined(Q_OS_WINCE)
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
//TODO: this should really be using nativeFilePath(), but that returns a path in long format \\?\c:\foo
//which causes many problems later on when it's returned through currentPath()
return ::SetCurrentDirectory(reinterpret_cast<const wchar_t*>(QDir::toNativeSeparators(entry.filePath()).utf16())) != 0;
@@ -1208,7 +1341,7 @@ bool QFileSystemEngine::setCurrentPath(const QFileSystemEntry &entry)
QFileSystemEntry QFileSystemEngine::currentPath()
{
QString ret;
-#if !defined(Q_OS_WINCE)
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
DWORD size = 0;
wchar_t currentName[PATH_MAX];
size = ::GetCurrentDirectory(PATH_MAX, currentName);
@@ -1224,13 +1357,17 @@ QFileSystemEntry QFileSystemEngine::currentPath()
}
if (ret.length() >= 2 && ret[1] == QLatin1Char(':'))
ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters.
-#else
+#else // !Q_OS_WINCE && !Q_OS_WINRT
//TODO - a race condition exists when using currentPath / setCurrentPath from multiple threads
if (qfsPrivateCurrentDir.isEmpty())
+#ifndef Q_OS_WINRT
qfsPrivateCurrentDir = QCoreApplication::applicationDirPath();
+#else
+ qfsPrivateCurrentDir = QDir::rootPath();
+#endif
ret = qfsPrivateCurrentDir;
-#endif
+#endif // Q_OS_WINCE || Q_OS_WINRT
return QFileSystemEntry(ret, QFileSystemEntry::FromNativePath());
}
@@ -1248,8 +1385,16 @@ bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSy
//static
bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{
+#ifndef Q_OS_WINRT
bool ret = ::CopyFile((wchar_t*)source.nativeFilePath().utf16(),
(wchar_t*)target.nativeFilePath().utf16(), true) != 0;
+#else // !Q_OS_WINRT
+ COPYFILE2_EXTENDED_PARAMETERS copyParams = {
+ sizeof(copyParams), COPY_FILE_FAIL_IF_EXISTS, NULL, NULL, NULL
+ };
+ bool ret = ::CopyFile2((const wchar_t*)source.nativeFilePath().utf16(),
+ (const wchar_t*)target.nativeFilePath().utf16(), &copyParams) != 0;
+#endif // Q_OS_WINRT
if(!ret)
error = QSystemError(::GetLastError(), QSystemError::NativeError);
return ret;
@@ -1258,8 +1403,13 @@ bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSyst
//static
bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{
+#ifndef Q_OS_WINRT
bool ret = ::MoveFile((wchar_t*)source.nativeFilePath().utf16(),
(wchar_t*)target.nativeFilePath().utf16()) != 0;
+#else // !Q_OS_WINRT
+ bool ret = ::MoveFileEx((const wchar_t*)source.nativeFilePath().utf16(),
+ (const wchar_t*)target.nativeFilePath().utf16(), 0) != 0;
+#endif // Q_OS_WINRT
if(!ret)
error = QSystemError(::GetLastError(), QSystemError::NativeError);
return ret;
diff --git a/src/corelib/io/qfilesystementry.cpp b/src/corelib/io/qfilesystementry.cpp
index 3934c6a673..42a724670e 100644
--- a/src/corelib/io/qfilesystementry.cpp
+++ b/src/corelib/io/qfilesystementry.cpp
@@ -170,6 +170,12 @@ void QFileSystemEntry::resolveNativeFilePath() const
#else
m_nativeFilePath = QFile::encodeName(QDir::toNativeSeparators(m_filePath));
#endif
+#ifdef Q_OS_WINRT
+ while (m_nativeFilePath.startsWith(QLatin1Char('\\')))
+ m_nativeFilePath.remove(0,1);
+ if (m_nativeFilePath.isEmpty())
+ m_nativeFilePath.append(QLatin1Char('.'));
+#endif
}
}
diff --git a/src/corelib/io/qfilesystemiterator_unix.cpp b/src/corelib/io/qfilesystemiterator_unix.cpp
index bfedd3f70c..0b59aa169a 100644
--- a/src/corelib/io/qfilesystemiterator_unix.cpp
+++ b/src/corelib/io/qfilesystemiterator_unix.cpp
@@ -105,8 +105,8 @@ bool QFileSystemIterator::advance(QFileSystemEntry &fileEntry, QFileSystemMetaDa
if (!dir)
return false;
-#if defined(Q_OS_QNX) && defined(__EXT_QNX__READDIR_R)
- lastError = _readdir_r(dir, mt_file.data(), &dirEntry, direntSize);
+#if defined(Q_OS_QNX) && defined(QT_EXT_QNX_READDIR_R)
+ lastError = QT_EXT_QNX_READDIR_R(dir, mt_file.data(), &dirEntry, direntSize);
if (lastError)
return false;
#elif defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN)
diff --git a/src/corelib/io/qfilesystemiterator_win.cpp b/src/corelib/io/qfilesystemiterator_win.cpp
index 90232f7cfc..dda96bd45a 100644
--- a/src/corelib/io/qfilesystemiterator_win.cpp
+++ b/src/corelib/io/qfilesystemiterator_win.cpp
@@ -39,10 +39,12 @@
**
****************************************************************************/
-#if _WIN32_WINNT < 0x0500
-#undef _WIN32_WINNT
-#define _WIN32_WINNT 0x0500
-#endif
+#if !defined(WINAPI_FAMILY)
+# if _WIN32_WINNT < 0x0500
+# undef _WIN32_WINNT
+# define _WIN32_WINNT 0x0500
+# endif // _WIN32_WINNT < 0x500
+#endif // !WINAPI_FAMILY
#include "qfilesystemiterator_p.h"
#include "qfilesystemengine_p.h"
@@ -73,6 +75,10 @@ QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry, QDir::Fi
if (!nativePath.endsWith(QLatin1Char('\\')))
nativePath.append(QLatin1Char('\\'));
nativePath.append(QLatin1Char('*'));
+#ifdef Q_OS_WINRT
+ if (nativePath.startsWith(QLatin1Char('\\')))
+ nativePath.remove(0, 1);
+#endif
if (!dirPath.endsWith(QLatin1Char('/')))
dirPath.append(QLatin1Char('/'));
if ((filters & (QDir::Dirs|QDir::Drives)) && (!(filters & (QDir::Files))))
diff --git a/src/corelib/io/qfilesystemmetadata_p.h b/src/corelib/io/qfilesystemmetadata_p.h
index 1abc9b7ec4..de79ec32d3 100644
--- a/src/corelib/io/qfilesystemmetadata_p.h
+++ b/src/corelib/io/qfilesystemmetadata_p.h
@@ -219,7 +219,9 @@ public:
#if defined(Q_OS_WIN)
inline void fillFromFileAttribute(DWORD fileAttribute, bool isDriveRoot = false);
inline void fillFromFindData(WIN32_FIND_DATA &findData, bool setLinkType = false, bool isDriveRoot = false);
+# ifndef Q_OS_WINRT
inline void fillFromFindInfo(BY_HANDLE_FILE_INFORMATION &fileInfo);
+# endif
#endif
private:
friend class QFileSystemEngine;
@@ -340,6 +342,7 @@ inline void QFileSystemMetaData::fillFromFindData(WIN32_FIND_DATA &findData, boo
}
}
+#ifndef Q_OS_WINRT
inline void QFileSystemMetaData::fillFromFindInfo(BY_HANDLE_FILE_INFORMATION &fileInfo)
{
fillFromFileAttribute(fileInfo.dwFileAttributes);
@@ -355,7 +358,8 @@ inline void QFileSystemMetaData::fillFromFindInfo(BY_HANDLE_FILE_INFORMATION &fi
}
knownFlagsMask |= Times | SizeAttribute;
}
-#endif
+#endif // !Q_OS_WINRT
+#endif // Q_OS_WIN
QT_END_NAMESPACE
diff --git a/src/corelib/io/qfsfileengine_win.cpp b/src/corelib/io/qfsfileengine_win.cpp
index 2b38019674..c974daab06 100644
--- a/src/corelib/io/qfsfileengine_win.cpp
+++ b/src/corelib/io/qfsfileengine_win.cpp
@@ -60,14 +60,18 @@
# include <types.h>
#endif
#include <objbase.h>
-#include <shlobj.h>
+#ifndef Q_OS_WINRT
+# include <shlobj.h>
+# include <accctrl.h>
+#endif
#include <initguid.h>
-#include <accctrl.h>
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
-#define SECURITY_WIN32
-#include <security.h>
+#ifndef Q_OS_WINRT
+# define SECURITY_WIN32
+# include <security.h>
+#endif
#ifndef PATH_MAX
#define PATH_MAX FILENAME_MAX
@@ -93,7 +97,7 @@ QString QFSFileEnginePrivate::longFileName(const QString &path)
return path;
QString absPath = QFileSystemEngine::nativeAbsoluteFilePath(path);
-#if !defined(Q_OS_WINCE)
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
QString prefix = QLatin1String("\\\\?\\");
if (isUncPath(absPath)) {
prefix.append(QLatin1String("UNC\\")); // "\\\\?\\UNC\\"
@@ -121,11 +125,12 @@ bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode)
if (openMode & QIODevice::WriteOnly)
accessRights |= GENERIC_WRITE;
- SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE };
// WriteOnly can create files, ReadOnly cannot.
DWORD creationDisp = (openMode & QIODevice::WriteOnly) ? OPEN_ALWAYS : OPEN_EXISTING;
// Create the file handle.
+#ifndef Q_OS_WINRT
+ SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE };
fileHandle = CreateFile((const wchar_t*)fileEntry.nativeFilePath().utf16(),
accessRights,
shareMode,
@@ -133,6 +138,13 @@ bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode)
creationDisp,
FILE_ATTRIBUTE_NORMAL,
NULL);
+#else // !Q_OS_WINRT
+ fileHandle = CreateFile2((const wchar_t*)fileEntry.nativeFilePath().utf16(),
+ accessRights,
+ shareMode,
+ creationDisp,
+ NULL);
+#endif // Q_OS_WINRT
// Bail out on error.
if (fileHandle == INVALID_HANDLE_VALUE) {
@@ -473,7 +485,7 @@ int QFSFileEnginePrivate::nativeHandle() const
*/
bool QFSFileEnginePrivate::nativeIsSequential() const
{
-#if !defined(Q_OS_WINCE)
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
HANDLE handle = fileHandle;
if (fh || fd != -1)
handle = (HANDLE)_get_osfhandle(fh ? QT_FILENO(fh) : fd);
@@ -577,7 +589,7 @@ bool QFSFileEngine::setCurrentPath(const QString &path)
QString QFSFileEngine::currentPath(const QString &fileName)
{
-#if !defined(Q_OS_WINCE)
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
QString ret;
//if filename is a drive: then get the pwd of that drive
if (fileName.length() >= 2 &&
@@ -596,10 +608,10 @@ QString QFSFileEngine::currentPath(const QString &fileName)
if (ret.length() >= 2 && ret[1] == QLatin1Char(':'))
ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters.
return ret;
-#else
+#else // !Q_OS_WINCE && !Q_OS_WINRT
Q_UNUSED(fileName);
return QFileSystemEngine::currentPath().filePath();
-#endif
+#endif // Q_OS_WINCE || Q_OS_WINRT
}
QString QFSFileEngine::homePath()
@@ -620,7 +632,7 @@ QString QFSFileEngine::tempPath()
QFileInfoList QFSFileEngine::drives()
{
QFileInfoList ret;
-#if !defined(Q_OS_WINCE)
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
#if defined(Q_OS_WIN32)
quint32 driveBits = (quint32) GetLogicalDrives() & 0x3ffffff;
#endif
@@ -633,10 +645,10 @@ QFileInfoList QFSFileEngine::drives()
driveBits = driveBits >> 1;
}
return ret;
-#else
+#else // !Q_OS_WINCE && !Q_OS_WINRT
ret.append(QFileInfo(QLatin1String("/")));
return ret;
-#endif
+#endif // Q_OS_WINCE || Q_OS_WINRT
}
bool QFSFileEnginePrivate::doStat(QFileSystemMetaData::MetaDataFlags flags) const
@@ -661,7 +673,7 @@ bool QFSFileEnginePrivate::doStat(QFileSystemMetaData::MetaDataFlags flags) cons
bool QFSFileEngine::link(const QString &newName)
{
-#if !defined(Q_OS_WINCE)
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
#if !defined(QT_NO_LIBRARY)
bool ret = false;
@@ -707,7 +719,7 @@ bool QFSFileEngine::link(const QString &newName)
Q_UNUSED(newName);
return false;
#endif // QT_NO_LIBRARY
-#else
+#elif defined(Q_OS_WINCE)
QString linkName = newName;
linkName.replace(QLatin1Char('/'), QLatin1Char('\\'));
if (!linkName.endsWith(QLatin1String(".lnk")))
@@ -720,7 +732,11 @@ bool QFSFileEngine::link(const QString &newName)
if (!ret)
setError(QFile::RenameError, qt_error_string());
return ret;
-#endif // Q_OS_WINCE
+#else // Q_OS_WINCE
+ Q_UNUSED(newName);
+ Q_UNIMPLEMENTED();
+ return false;
+#endif // Q_OS_WINRT
}
/*!
@@ -937,6 +953,7 @@ QDateTime QFSFileEngine::fileTime(FileTime time) const
uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size,
QFile::MemoryMapFlags flags)
{
+#ifndef Q_OS_WINPHONE
Q_Q(QFSFileEngine);
Q_UNUSED(flags);
if (openMode == QFile::NotOpen) {
@@ -980,7 +997,11 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size,
// first create the file mapping handle
DWORD protection = (openMode & QIODevice::WriteOnly) ? PAGE_READWRITE : PAGE_READONLY;
+#ifndef Q_OS_WINRT
mapHandle = ::CreateFileMapping(handle, 0, protection, 0, 0, 0);
+#else
+ mapHandle = ::CreateFileMappingFromApp(handle, 0, protection, 0, 0);
+#endif
if (mapHandle == NULL) {
q->setError(QFile::PermissionsError, qt_error_string());
#ifdef Q_USE_DEPRECATED_MAP_API
@@ -998,15 +1019,23 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size,
DWORD offsetHi = offset >> 32;
DWORD offsetLo = offset & Q_UINT64_C(0xffffffff);
SYSTEM_INFO sysinfo;
+#ifndef Q_OS_WINRT
::GetSystemInfo(&sysinfo);
+#else
+ ::GetNativeSystemInfo(&sysinfo);
+#endif
DWORD mask = sysinfo.dwAllocationGranularity - 1;
DWORD extra = offset & mask;
if (extra)
offsetLo &= ~mask;
// attempt to create the map
+#ifndef Q_OS_WINRT
LPVOID mapAddress = ::MapViewOfFile(mapHandle, access,
offsetHi, offsetLo, size + extra);
+#else
+ LPVOID mapAddress = ::MapViewOfFileFromApp(mapHandle, access, offset, size);
+#endif
if (mapAddress) {
uchar *address = extra + static_cast<uchar*>(mapAddress);
maps[address] = extra;
@@ -1025,11 +1054,18 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size,
::CloseHandle(mapHandle);
mapHandle = NULL;
+#else // !Q_OS_WINPHONE
+ Q_UNUSED(offset);
+ Q_UNUSED(size);
+ Q_UNUSED(flags);
+ Q_UNIMPLEMENTED();
+#endif // Q_OS_WINPHONE
return 0;
}
bool QFSFileEnginePrivate::unmap(uchar *ptr)
{
+#ifndef Q_OS_WINPHONE
Q_Q(QFSFileEngine);
if (!maps.contains(ptr)) {
q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
@@ -1048,6 +1084,11 @@ bool QFSFileEnginePrivate::unmap(uchar *ptr)
}
return true;
+#else // !Q_OS_WINPHONE
+ Q_UNUSED(ptr);
+ Q_UNIMPLEMENTED();
+ return false;
+#endif // Q_OS_WINPHONE
}
QT_END_NAMESPACE
diff --git a/src/corelib/io/qlockfile_win.cpp b/src/corelib/io/qlockfile_win.cpp
index b5f6d9f3da..28f6b02a64 100644
--- a/src/corelib/io/qlockfile_win.cpp
+++ b/src/corelib/io/qlockfile_win.cpp
@@ -52,7 +52,6 @@ QT_BEGIN_NAMESPACE
QLockFile::LockError QLockFilePrivate::tryLock_sys()
{
- SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE };
const QFileSystemEntry fileEntry(fileName);
// When writing, allow others to read.
// When reading, QFile will allow others to read and write, all good.
@@ -60,6 +59,8 @@ QLockFile::LockError QLockFilePrivate::tryLock_sys()
// but Windows doesn't allow recreating it while this handle is open anyway,
// so this would only create confusion (can't lock, but no lock file to read from).
const DWORD dwShareMode = FILE_SHARE_READ;
+#ifndef Q_OS_WINRT
+ SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE };
HANDLE fh = CreateFile((const wchar_t*)fileEntry.nativeFilePath().utf16(),
GENERIC_WRITE,
dwShareMode,
@@ -67,6 +68,13 @@ QLockFile::LockError QLockFilePrivate::tryLock_sys()
CREATE_NEW, // error if already exists
FILE_ATTRIBUTE_NORMAL,
NULL);
+#else // !Q_OS_WINRT
+ HANDLE fh = CreateFile2((const wchar_t*)fileEntry.nativeFilePath().utf16(),
+ GENERIC_WRITE,
+ dwShareMode,
+ CREATE_NEW, // error if already exists
+ NULL);
+#endif // Q_OS_WINRT
if (fh == INVALID_HANDLE_VALUE) {
const DWORD lastError = GetLastError();
switch (lastError) {
@@ -112,6 +120,9 @@ bool QLockFilePrivate::isApparentlyStale() const
if (!getLockInfo(&pid, &hostname, &appname))
return false;
+ // On WinRT there seems to be no way of obtaining information about other
+ // processes due to sandboxing
+#ifndef Q_OS_WINRT
HANDLE procHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
if (!procHandle)
return true;
@@ -120,6 +131,7 @@ bool QLockFilePrivate::isApparentlyStale() const
::CloseHandle(procHandle);
if (dwR == WAIT_TIMEOUT)
return true;
+#endif // !Q_OS_WINRT
const qint64 age = QFileInfo(fileName).lastModified().msecsTo(QDateTime::currentDateTime());
return staleLockTime > 0 && age > staleLockTime;
}
diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp
index 35b3ed4e3d..8b67fc1962 100644
--- a/src/corelib/io/qsettings.cpp
+++ b/src/corelib/io/qsettings.cpp
@@ -80,6 +80,16 @@
# include <qt_windows.h>
#endif
+#ifdef Q_OS_WINRT
+#include <wrl.h>
+#include <windows.foundation.h>
+#include <windows.storage.h>
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Storage;
+#endif
+
#ifndef CSIDL_COMMON_APPDATA
#define CSIDL_COMMON_APPDATA 0x0023 // All Users\Application Data
#endif
@@ -365,7 +375,7 @@ after_loop:
// see also qsettings_win.cpp and qsettings_mac.cpp
-#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC)
+#if defined(Q_OS_WINRT) || (!defined(Q_OS_WIN) && !defined(Q_OS_MAC))
QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::Scope scope,
const QString &organization, const QString &application)
{
@@ -373,7 +383,7 @@ QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::
}
#endif
-#if !defined(Q_OS_WIN)
+#if defined(Q_OS_WINRT) || !defined(Q_OS_WIN)
QSettingsPrivate *QSettingsPrivate::create(const QString &fileName, QSettings::Format format)
{
return new QConfFileSettingsPrivate(fileName, format);
@@ -1021,7 +1031,7 @@ void QConfFileSettingsPrivate::initAccess()
sync(); // loads the files the first time
}
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
static QString windowsConfigPath(int type)
{
QString result;
@@ -1063,7 +1073,40 @@ static QString windowsConfigPath(int type)
return result;
}
-#endif // Q_OS_WIN
+#elif defined(Q_OS_WINRT) // Q_OS_WIN && !Q_OS_WINRT
+static QString windowsConfigPath(int type)
+{
+ static QString result;
+ while (result.isEmpty()) {
+ ComPtr<IApplicationDataStatics> applicationDataStatics;
+ if (FAILED(GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_ApplicationData).Get(), &applicationDataStatics)))
+ return result;
+ ComPtr<IApplicationData> applicationData;
+ if (FAILED(applicationDataStatics->get_Current(&applicationData)))
+ return result;
+ ComPtr<IStorageFolder> localFolder;
+ if (FAILED(applicationData->get_LocalFolder(&localFolder)))
+ return result;
+ ComPtr<IStorageItem> localFolderItem;
+ if (FAILED(localFolder.As(&localFolderItem)))
+ return result;
+ HSTRING path;
+ if (FAILED(localFolderItem->get_Path(&path)))
+ return result;
+ result = QString::fromWCharArray(WindowsGetStringRawBuffer(path, nullptr));
+ }
+
+ switch (type) {
+ case CSIDL_COMMON_APPDATA:
+ return result + QLatin1String("\\qt-common");
+ case CSIDL_APPDATA:
+ return result + QLatin1String("\\qt-user");
+ default:
+ break;
+ }
+ return result;
+}
+#endif // Q_OS_WINRT
static inline int pathHashKey(QSettings::Format format, QSettings::Scope scope)
{
@@ -1449,10 +1492,18 @@ void QConfFileSettingsPrivate::syncConfFile(int confFileNo)
QString writeSemName = QLatin1String("QSettingsWriteSem ");
writeSemName.append(file.fileName());
+#ifndef Q_OS_WINRT
writeSemaphore = CreateSemaphore(0, 1, 1, reinterpret_cast<const wchar_t *>(writeSemName.utf16()));
+#else
+ writeSemaphore = CreateSemaphoreEx(0, 1, 1, reinterpret_cast<const wchar_t *>(writeSemName.utf16()), 0, SEMAPHORE_ALL_ACCESS);
+#endif
if (writeSemaphore) {
+#ifndef Q_OS_WINRT
WaitForSingleObject(writeSemaphore, INFINITE);
+#else
+ WaitForSingleObjectEx(writeSemaphore, INFINITE, FALSE);
+#endif
} else {
setStatus(QSettings::AccessError);
return;
@@ -1465,11 +1516,19 @@ void QConfFileSettingsPrivate::syncConfFile(int confFileNo)
QString readSemName(QLatin1String("QSettingsReadSem "));
readSemName.append(file.fileName());
+#ifndef Q_OS_WINRT
readSemaphore = CreateSemaphore(0, FileLockSemMax, FileLockSemMax, reinterpret_cast<const wchar_t *>(readSemName.utf16()));
+#else
+ readSemaphore = CreateSemaphoreEx(0, FileLockSemMax, FileLockSemMax, reinterpret_cast<const wchar_t *>(readSemName.utf16()), 0, SEMAPHORE_ALL_ACCESS);
+#endif
if (readSemaphore) {
for (int i = 0; i < numReadLocks; ++i)
+#ifndef Q_OS_WINRT
WaitForSingleObject(readSemaphore, INFINITE);
+#else
+ WaitForSingleObjectEx(readSemaphore, INFINITE, FALSE);
+#endif
} else {
setStatus(QSettings::AccessError);
if (writeSemaphore != 0) {
diff --git a/src/corelib/io/qstandardpaths.cpp b/src/corelib/io/qstandardpaths.cpp
index 2207b8c43e..1181d1b980 100644
--- a/src/corelib/io/qstandardpaths.cpp
+++ b/src/corelib/io/qstandardpaths.cpp
@@ -72,34 +72,198 @@ QT_BEGIN_NAMESPACE
methods such as QStandardPaths::writableLocation, QStandardPaths::standardLocations,
and QStandardPaths::displayName.
- \value DesktopLocation Returns the user's desktop directory.
- \value DocumentsLocation Returns the user's document.
- \value FontsLocation Returns the user's fonts.
- \value ApplicationsLocation Returns the user's applications.
- \value MusicLocation Returns the user's music.
- \value MoviesLocation Returns the user's movies.
- \value PicturesLocation Returns the user's pictures.
- \value TempLocation Returns the system's temporary directory.
- \value HomeLocation Returns the user's home directory.
+ Some of the values in this enum represent a user configuration. Such enum
+ values will return the same paths in different applications, so they could
+ be used to share data with other applications. Other values are specific to
+ this application. Each enum value in the table below describes whether it's
+ application-specific or generic.
+
+ Application-specific directories should be assumed to be unreachable by
+ other applications. Therefore, files placed there might not be readable by
+ other applications, even if run by the same user. On the other hand, generic
+ directories should be assumed to be accessible by all applications run by
+ this user, but should still be assumed to be unreachable by applications by
+ other users.
+
+ The only exception is QStandardPaths::TempLocation (which is the same as
+ QDir::tempPath()): the path returned may be application-specific, but files
+ stored there may be accessed by other applications run by the same user.
+
+ Data interchange with other users is out of the scope of QStandardPaths.
+
+ \value DesktopLocation Returns the user's desktop directory. This is a generic value.
+ On systems with no concept of a desktop, this is the same as
+ QStandardPaths::HomeLocation.
+ \value DocumentsLocation Returns the directory containing user document files.
+ This is a generic value. The returned path is never empty.
+ \value FontsLocation Returns the directory containing user's fonts. This is a generic value.
+ Note that installing fonts may require additional, platform-specific operations.
+ \value ApplicationsLocation Returns the directory containing the user applications
+ (either executables, application bundles, or shortcuts to them). This is a generic value.
+ Note that installing applications may require additional, platform-specific operations.
+ Files, folders or shortcuts in this directory are platform-specific.
+ \value MusicLocation Returns the directory containing the user's music or other audio files.
+ This is a generic value. If no directory specific for music files exists, a sensible
+ fallback for storing user documents is returned.
+ \value MoviesLocation Returns the directory containing the user's movies and videos.
+ This is a generic value. If no directory specific for movie files exists, a sensible
+ fallback for storing user documents is returned.
+ \value PicturesLocation Returns the directory containing the user's pictures or photos.
+ This is a generic value. If no directory specific for picture files exists, a sensible
+ fallback for storing user documents is returned.
+ \value TempLocation Returns a directory where temporary files can be stored. The returned value
+ might be application-specific, shared among other applications for this user, or even
+ system-wide. The returned path is never empty.
+ \value HomeLocation Returns the user's home directory (the same as QDir::homePath()). On Unix
+ systems, this is equal to the HOME environment variable. This value might be
+ generic or application-specific, but the returned path is never empty.
\value DataLocation Returns a directory location where persistent
- application data can be stored. QCoreApplication::organizationName
- and QCoreApplication::applicationName are appended to the directory location
- returned for GenericDataLocation.
+ application data can be stored. This is an application-specific directory. To obtain a
+ path to store data to be shared with other applications, use
+ QStandardPaths::GenericDataLocation. The returned path is never empty.
\value CacheLocation Returns a directory location where user-specific
- non-essential (cached) data should be written.
- \value GenericCacheLocation Returns a directory location where user-specific
- non-essential (cached) data, shared across applications, should be written.
+ non-essential (cached) data should be written. This is an application-specific directory.
+ The returned path is never empty.
+ \value GenericCacheLocation Returns a directory location where user-specific non-essential
+ (cached) data, shared across applications, should be written. This is a generic value.
+ Note that the returned path may be empty if the system has no concept of shared cache.
\value GenericDataLocation Returns a directory location where persistent
- data shared across applications can be stored.
+ data shared across applications can be stored. This is a generic value. The returned
+ path is never empty.
\value RuntimeLocation Returns a directory location where runtime communication
- files should be written. For instance unix local sockets.
+ files should be written, like Unix local sockets. This is a generic value.
+ The returned path may be empty on some systems.
\value ConfigLocation Returns a directory location where user-specific
- configuration files should be written.
+ configuration files should be written. This may be either a generic value
+ or application-specific, and the returned path is never empty.
+ \value DownloadLocation Returns a directory for user's downloaded files. This is a generic value.
+ If no directory specific for downloads exists, a sensible fallback for storing user
+ documents is returned.
\value GenericConfigLocation Returns a directory location where user-specific
configuration files shared between multiple applications should be written.
This is a generic value and the returned path is never empty.
- \value DownloadLocation Returns a directory for user's downloaded files.
+ The following table gives examples of paths on different operating systems.
+ The first path is the writable path (unless noted). Other, additional
+ paths, if any, represent non-writable locations.
+
+ \table
+ \header \li Path type \li OS X \li Windows
+ \row \li DesktopLocation
+ \li "~/Desktop"
+ \li "C:/Users/<USER>/Desktop"
+ \row \li DocumentsLocation
+ \li "~/Documents"
+ \li "C:/Users/<USER>/Documents"
+ \row \li FontsLocation
+ \li "/System/Library/Fonts" (not writable)
+ \li "C:/Windows/Fonts" (not writable)
+ \row \li ApplicationsLocation
+ \li "/Applications" (not writable)
+ \li "C:/Users/<USER>/AppData/Roaming/Microsoft/Windows/Start Menu/Programs"
+ \row \li MusicLocation
+ \li "~/Music"
+ \li "C:/Users/<USER>/Music"
+ \row \li MoviesLocation
+ \li "~/Movies"
+ \li "C:/Users/<USER>/Videos"
+ \row \li PicturesLocation
+ \li "~/Pictures"
+ \li "C:/Users/<USER>/Pictures"
+ \row \li TempLocation
+ \li randomly generated by the OS
+ \li "C:/Users/<USER>/AppData/Local/Temp"
+ \row \li HomeLocation
+ \li "~"
+ \li "C:/Users/<USER>"
+ \row \li DataLocation
+ \li "~/Library/Application Support/<APPNAME>", "/Library/Application Support/<APPNAME>"
+ \li "C:/Users/<USER>/AppData/Local/<APPNAME>", "C:/ProgramData/<APPNAME>"
+ \row \li CacheLocation
+ \li "~/Library/Caches/<APPNAME>", "/Library/Caches/<APPNAME>"
+ \li "C:/Users/<USER>/AppData/Local/<APPNAME>/cache"
+ \row \li GenericDataLocation
+ \li "~/Library/Application Support", "/Library/Application Support"
+ \li "C:/Users/<USER>/AppData/Local", "C:/ProgramData"
+ \row \li RuntimeLocation
+ \li "~/Library/Application Support"
+ \li "C:/Users/<USER>"
+ \row \li ConfigLocation
+ \li "~/Library/Preferences"
+ \li "C:/Users/<USER>/AppData/Local/<APPNAME>", "C:/ProgramData/<APPNAME>"
+ \row \li GenericConfigLocation
+ \li "~/Library/Preferences"
+ \li "C:/Users/<USER>/AppData/Local", "C:/ProgramData"
+ \row \li DownloadLocation
+ \li "~/Documents"
+ \li "C:/Users/<USER>/Documents"
+ \row \li GenericCacheLocation
+ \li "~/Library/Caches", "/Library/Caches"
+ \li "C:/Users/<USER>/AppData/Local/cache"
+ \endtable
+
+ \table
+ \header \li Path type \li Blackberry \li Linux (including Android)
+ \row \li DesktopLocation
+ \li "<APPROOT>/data"
+ \li "~/Desktop"
+ \row \li DocumentsLocation
+ \li "<APPROOT>/shared/documents"
+ \li "~/Documents"
+ \row \li FontsLocation
+ \li "/base/usr/fonts" (not writable)
+ \li "~/.fonts"
+ \row \li ApplicationsLocation
+ \li not supported (directory not readable)
+ \li "~/.local/share/applications", "/usr/local/share/applications", "/usr/share/applications"
+ \row \li MusicLocation
+ \li "<APPROOT>/shared/music"
+ \li "~/Music"
+ \row \li MoviesLocation
+ \li "<APPROOT>/shared/videos"
+ \li "~/Videos"
+ \row \li PicturesLocation
+ \li "<APPROOT>/shared/photos"
+ \li "~/Pictures"
+ \row \li TempLocation
+ \li "/var/tmp"
+ \li "/tmp"
+ \row \li HomeLocation
+ \li "<APPROOT>/data"
+ \li "~"
+ \row \li DataLocation
+ \li "<APPROOT>/data"
+ \li "~/.local/share/<APPNAME>", "/usr/local/share/<APPNAME>", "/usr/share/<APPNAME>"
+ \row \li CacheLocation
+ \li "<APPROOT>/data/Cache"
+ \li "~/.cache/<APPNAME>"
+ \row \li GenericDataLocation
+ \li "<APPROOT>/shared/misc"
+ \li "~/.local/share", "/usr/local/share", "/usr/share"
+ \row \li RuntimeLocation
+ \li "/var/tmp"
+ \li "/run/user/<USER>"
+ \row \li ConfigLocation
+ \li "<APPROOT>/data/Settings"
+ \li "~/.config", "/etc/xdg"
+ \row \li GenericConfigLocation
+ \li "<APPROOT>/data/Settings"
+ \li "~/.config", "/etc/xdg"
+ \row \li DownloadLocation
+ \li "<APPROOT>/shared/downloads"
+ \li "~/Downloads"
+ \row \li GenericCacheLocation
+ \li "<APPROOT>/data/Cache" (there is no shared cache)
+ \li "~/.cache"
+ \endtable
+
+ In the table above, \c <APPNAME> is usually the organization name, the
+ application name, or both, or a unique name generated at packaging.
+ Similarly, <APPROOT> is the location where this application is installed
+ (often a sandbox).
+
+ The paths above should not be relied upon, as they may change according to
+ OS configuration, locale, or they may change in future Qt versions.
\sa writableLocation(), standardLocations(), displayName(), locate(), locateAll()
*/
diff --git a/src/corelib/io/qstandardpaths_winrt.cpp b/src/corelib/io/qstandardpaths_winrt.cpp
new file mode 100644
index 0000000000..9b6a088a30
--- /dev/null
+++ b/src/corelib/io/qstandardpaths_winrt.cpp
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCore module 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qstandardpaths.h"
+
+#include <qdir.h>
+#include <private/qsystemlibrary_p.h>
+#include <qcoreapplication.h>
+#include <qstringlist.h>
+
+#include <qt_windows.h>
+
+#include <wrl.h>
+#include <windows.foundation.h>
+#include <windows.storage.h>
+#include <Windows.ApplicationModel.h>
+
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Storage;
+using namespace ABI::Windows::ApplicationModel;
+
+#ifndef QT_NO_STANDARDPATHS
+
+QT_BEGIN_NAMESPACE
+
+static QString convertCharArray(const wchar_t *path)
+{
+ return QDir::fromNativeSeparators(QString::fromWCharArray(path));
+}
+
+QString QStandardPaths::writableLocation(StandardLocation type)
+{
+ QString result;
+
+ switch (type) {
+ case ConfigLocation: // same as DataLocation, on Windows
+ case DataLocation:
+ case GenericDataLocation: {
+ ComPtr<IApplicationDataStatics> applicationDataStatics;
+ if (FAILED(GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_ApplicationData).Get(), &applicationDataStatics)))
+ break;
+ ComPtr<IApplicationData> applicationData;
+ if (FAILED(applicationDataStatics->get_Current(&applicationData)))
+ break;
+ ComPtr<IStorageFolder> settingsFolder;
+ if (FAILED(applicationData->get_LocalFolder(&settingsFolder)))
+ break;
+ ComPtr<IStorageItem> settingsFolderItem;
+ if (FAILED(settingsFolder.As(&settingsFolderItem)))
+ break;
+ HSTRING path;
+ if (FAILED(settingsFolderItem->get_Path(&path)))
+ break;
+ result = convertCharArray(WindowsGetStringRawBuffer(path, nullptr));
+ if (isTestModeEnabled())
+ result += QLatin1String("/qttest");
+ break;
+ }
+ case CacheLocation:
+ return writableLocation(DataLocation) + QLatin1String("/cache");
+
+ case GenericCacheLocation:
+ return writableLocation(GenericDataLocation) + QLatin1String("/cache");
+
+ case RuntimeLocation:
+ case HomeLocation:
+ result = QDir::homePath();
+ break;
+
+ case TempLocation:
+ result = QDir::tempPath();
+ break;
+ default:
+ Q_UNIMPLEMENTED();
+ }
+ return result;
+
+}
+
+QStringList QStandardPaths::standardLocations(StandardLocation type)
+{
+ return QStringList(writableLocation(type));
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_STANDARDPATHS
diff --git a/src/corelib/io/qtemporaryfile.cpp b/src/corelib/io/qtemporaryfile.cpp
index b3cb4e43f8..b3a17eae7e 100644
--- a/src/corelib/io/qtemporaryfile.cpp
+++ b/src/corelib/io/qtemporaryfile.cpp
@@ -151,18 +151,27 @@ static bool createFileFromTemplate(NativeFileHandle &file,
for (;;) {
// Atomically create file and obtain handle
#if defined(Q_OS_WIN)
+# ifndef Q_OS_WINRT
file = CreateFile((const wchar_t *)path.constData(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL, NULL);
+# else // !Q_OS_WINRT
+ file = CreateFile2((const wchar_t *)path.constData(),
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, CREATE_NEW,
+ NULL);
+# endif // Q_OS_WINRT
if (file != INVALID_HANDLE_VALUE)
return true;
DWORD err = GetLastError();
if (err == ERROR_ACCESS_DENIED) {
- DWORD attributes = GetFileAttributes((const wchar_t *)path.constData());
- if (attributes == INVALID_FILE_ATTRIBUTES) {
+ WIN32_FILE_ATTRIBUTE_DATA attributes;
+ if (!GetFileAttributesEx((const wchar_t *)path.constData(),
+ GetFileExInfoStandard, &attributes)
+ || attributes.dwFileAttributes == INVALID_FILE_ATTRIBUTES) {
// Potential write error (read-only parent directory, etc.).
error = QSystemError(err, QSystemError::NativeError);
return false;
@@ -336,7 +345,7 @@ bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode)
d->fileEntry = QFileSystemEntry(filename, QFileSystemEntry::FromNativePath());
-#if !defined(Q_OS_WIN)
+#if !defined(Q_OS_WIN) || defined(Q_OS_WINRT)
d->closeFileHandle = true;
#endif
diff --git a/src/corelib/itemmodels/qitemselectionmodel.cpp b/src/corelib/itemmodels/qitemselectionmodel.cpp
index aff9939b87..883aa5b982 100644
--- a/src/corelib/itemmodels/qitemselectionmodel.cpp
+++ b/src/corelib/itemmodels/qitemselectionmodel.cpp
@@ -1271,9 +1271,8 @@ void QItemSelectionModel::clearCurrentIndex()
*/
void QItemSelectionModel::reset()
{
- bool block = blockSignals(true);
+ const QSignalBlocker blocker(this);
clear();
- blockSignals(block);
}
/*!
diff --git a/src/corelib/kernel/kernel.pri b/src/corelib/kernel/kernel.pri
index 7625a74381..4c43f292fb 100644
--- a/src/corelib/kernel/kernel.pri
+++ b/src/corelib/kernel/kernel.pri
@@ -68,16 +68,21 @@ SOURCES += \
win32 {
SOURCES += \
- kernel/qeventdispatcher_win.cpp \
kernel/qcoreapplication_win.cpp \
kernel/qwineventnotifier.cpp \
kernel/qsharedmemory_win.cpp \
kernel/qsystemsemaphore_win.cpp
HEADERS += \
- kernel/qeventdispatcher_win_p.h \
kernel/qwineventnotifier.h
-}
+ winrt {
+ SOURCES += kernel/qeventdispatcher_winrt.cpp
+ HEADERS += kernel/qeventdispatcher_winrt_p.h
+ } else {
+ SOURCES += kernel/qeventdispatcher_win.cpp
+ HEADERS += kernel/qeventdispatcher_win_p.h
+ }
+}
wince*: {
SOURCES += \
@@ -86,6 +91,13 @@ wince*: {
kernel/qfunctions_wince.h
}
+winrt {
+ SOURCES += \
+ kernel/qfunctions_winrt.cpp
+ HEADERS += \
+ kernel/qfunctions_winrt.h
+}
+
mac {
SOURCES += \
kernel/qcoreapplication_mac.cpp
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp
index e77d6894d0..e83a6e2ac4 100644
--- a/src/corelib/kernel/qcoreapplication.cpp
+++ b/src/corelib/kernel/qcoreapplication.cpp
@@ -85,7 +85,11 @@
# endif
#endif
#ifdef Q_OS_WIN
+# ifdef Q_OS_WINRT
+# include "qeventdispatcher_winrt_p.h"
+# else
# include "qeventdispatcher_win_p.h"
+# endif
#endif
#endif // QT_NO_QOBJECT
@@ -471,6 +475,8 @@ void QCoreApplicationPrivate::createEventDispatcher()
# endif
eventDispatcher = new QEventDispatcherUNIX(q);
# endif
+#elif defined(Q_OS_WINRT)
+ eventDispatcher = new QEventDispatcherWinRT(q);
#elif defined(Q_OS_WIN)
eventDispatcher = new QEventDispatcherWin32(q);
#else
@@ -518,6 +524,10 @@ void QCoreApplicationPrivate::appendApplicationPathToLibraryPaths()
coreappdata()->app_libpaths = app_libpaths = new QStringList;
QString app_location = QCoreApplication::applicationFilePath();
app_location.truncate(app_location.lastIndexOf(QLatin1Char('/')));
+#ifdef Q_OS_WINRT
+ if (app_location.isEmpty())
+ app_location.append(QLatin1Char('/'));
+#endif
app_location = QDir(app_location).canonicalPath();
if (QFile::exists(app_location) && !app_libpaths->contains(app_location))
app_libpaths->append(app_location);
diff --git a/src/corelib/kernel/qeventdispatcher_glib_p.h b/src/corelib/kernel/qeventdispatcher_glib_p.h
index 933faff5a5..a2e7b6b33e 100644
--- a/src/corelib/kernel/qeventdispatcher_glib_p.h
+++ b/src/corelib/kernel/qeventdispatcher_glib_p.h
@@ -77,19 +77,19 @@ public:
bool processEvents(QEventLoop::ProcessEventsFlags flags);
bool hasPendingEvents();
- void registerSocketNotifier(QSocketNotifier *socketNotifier);
- void unregisterSocketNotifier(QSocketNotifier *socketNotifier);
+ void registerSocketNotifier(QSocketNotifier *socketNotifier) Q_DECL_FINAL;
+ void unregisterSocketNotifier(QSocketNotifier *socketNotifier) Q_DECL_FINAL;
- void registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object);
- bool unregisterTimer(int timerId);
- bool unregisterTimers(QObject *object);
- QList<TimerInfo> registeredTimers(QObject *object) const;
+ void registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object) Q_DECL_FINAL;
+ bool unregisterTimer(int timerId) Q_DECL_FINAL;
+ bool unregisterTimers(QObject *object) Q_DECL_FINAL;
+ QList<TimerInfo> registeredTimers(QObject *object) const Q_DECL_FINAL;
- int remainingTime(int timerId);
+ int remainingTime(int timerId) Q_DECL_FINAL;
- void wakeUp();
- void interrupt();
- void flush();
+ void wakeUp() Q_DECL_FINAL;
+ void interrupt() Q_DECL_FINAL;
+ void flush() Q_DECL_FINAL;
static bool versionSupported();
diff --git a/src/corelib/kernel/qeventdispatcher_unix_p.h b/src/corelib/kernel/qeventdispatcher_unix_p.h
index 5d69d5e396..242aa9e695 100644
--- a/src/corelib/kernel/qeventdispatcher_unix_p.h
+++ b/src/corelib/kernel/qeventdispatcher_unix_p.h
@@ -94,6 +94,12 @@ public:
class QEventDispatcherUNIXPrivate;
+#ifdef Q_OS_QNX
+# define FINAL_EXCEPT_BLACKBERRY
+#else
+# define FINAL_EXCEPT_BLACKBERRY Q_DECL_FINAL
+#endif
+
class Q_CORE_EXPORT QEventDispatcherUNIX : public QAbstractEventDispatcher
{
Q_OBJECT
@@ -106,18 +112,18 @@ public:
bool processEvents(QEventLoop::ProcessEventsFlags flags);
bool hasPendingEvents();
- void registerSocketNotifier(QSocketNotifier *notifier);
- void unregisterSocketNotifier(QSocketNotifier *notifier);
+ void registerSocketNotifier(QSocketNotifier *notifier) FINAL_EXCEPT_BLACKBERRY;
+ void unregisterSocketNotifier(QSocketNotifier *notifier) FINAL_EXCEPT_BLACKBERRY;
- void registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object);
- bool unregisterTimer(int timerId);
- bool unregisterTimers(QObject *object);
- QList<TimerInfo> registeredTimers(QObject *object) const;
+ void registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object) Q_DECL_FINAL;
+ bool unregisterTimer(int timerId) Q_DECL_FINAL;
+ bool unregisterTimers(QObject *object) Q_DECL_FINAL;
+ QList<TimerInfo> registeredTimers(QObject *object) const Q_DECL_FINAL;
- int remainingTime(int timerId);
+ int remainingTime(int timerId) Q_DECL_FINAL;
- void wakeUp();
- void interrupt();
+ void wakeUp() FINAL_EXCEPT_BLACKBERRY;
+ void interrupt() Q_DECL_FINAL;
void flush();
protected:
@@ -130,7 +136,7 @@ protected:
virtual int select(int nfds,
fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
- timespec *timeout);
+ timespec *timeout) Q_DECL_FINAL;
};
class Q_CORE_EXPORT QEventDispatcherUNIXPrivate : public QAbstractEventDispatcherPrivate
@@ -142,8 +148,8 @@ public:
~QEventDispatcherUNIXPrivate();
int doSelect(QEventLoop::ProcessEventsFlags flags, timespec *timeout);
- virtual int initThreadWakeUp();
- virtual int processThreadWakeUp(int nsel);
+ virtual int initThreadWakeUp() FINAL_EXCEPT_BLACKBERRY;
+ virtual int processThreadWakeUp(int nsel) FINAL_EXCEPT_BLACKBERRY;
bool mainThread;
@@ -165,6 +171,8 @@ public:
QAtomicInt interrupt; // bool
};
+#undef FINAL_EXCEPT_BLACKBERRY
+
QT_END_NAMESPACE
#endif // QEVENTDISPATCHER_UNIX_P_H
diff --git a/src/corelib/kernel/qeventdispatcher_winrt.cpp b/src/corelib/kernel/qeventdispatcher_winrt.cpp
new file mode 100644
index 0000000000..daef8428e1
--- /dev/null
+++ b/src/corelib/kernel/qeventdispatcher_winrt.cpp
@@ -0,0 +1,394 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCore module 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qeventdispatcher_winrt_p.h"
+
+#include "qelapsedtimer.h"
+#include "qcoreapplication.h"
+#include "qthread.h"
+
+#include <private/qcoreapplication_p.h>
+#include <private/qthread_p.h>
+
+#include <windows.foundation.h>
+#include <windows.system.threading.h>
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::System::Threading;
+using namespace ABI::Windows::Foundation;
+
+QT_BEGIN_NAMESPACE
+
+QEventDispatcherWinRT::QEventDispatcherWinRT(QObject *parent)
+ : QAbstractEventDispatcher(*new QEventDispatcherWinRTPrivate, parent)
+{
+}
+
+QEventDispatcherWinRT::QEventDispatcherWinRT(QEventDispatcherWinRTPrivate &dd, QObject *parent)
+ : QAbstractEventDispatcher(dd, parent)
+{ }
+
+QEventDispatcherWinRT::~QEventDispatcherWinRT()
+{
+}
+
+bool QEventDispatcherWinRT::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ Q_UNUSED(flags);
+
+ // we are awake, broadcast it
+ emit awake();
+ QCoreApplicationPrivate::sendPostedEvents(0, 0, QThreadData::current());
+
+ return false;
+}
+
+bool QEventDispatcherWinRT::hasPendingEvents()
+{
+ return qGlobalPostedEventsCount();
+}
+
+void QEventDispatcherWinRT::registerSocketNotifier(QSocketNotifier *notifier)
+{
+ Q_UNUSED(notifier);
+ Q_UNIMPLEMENTED();
+}
+void QEventDispatcherWinRT::unregisterSocketNotifier(QSocketNotifier *notifier)
+{
+ Q_UNUSED(notifier);
+ Q_UNIMPLEMENTED();
+}
+
+void QEventDispatcherWinRT::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object)
+{
+ Q_UNUSED(timerType);
+
+ if (timerId < 1 || interval < 0 || !object) {
+ qWarning("QEventDispatcherWinRT::registerTimer: invalid arguments");
+ return;
+ } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
+ qWarning("QObject::startTimer: timers cannot be started from another thread");
+ return;
+ }
+
+ Q_D(QEventDispatcherWinRT);
+
+ WinRTTimerInfo *t = new WinRTTimerInfo();
+ t->dispatcher = this;
+ t->timerId = timerId;
+ t->interval = interval;
+ t->timeout = interval;
+ t->timerType = timerType;
+ t->obj = object;
+ t->inTimerEvent = false;
+
+ d->registerTimer(t);
+ d->timerVec.append(t); // store in timer vector
+ d->timerDict.insert(t->timerId, t); // store timers in dict
+}
+
+bool QEventDispatcherWinRT::unregisterTimer(int timerId)
+{
+ if (timerId < 1) {
+ qWarning("QEventDispatcherWinRT::unregisterTimer: invalid argument");
+ return false;
+ }
+ if (thread() != QThread::currentThread()) {
+ qWarning("QObject::killTimer: timers cannot be stopped from another thread");
+ return false;
+ }
+
+ Q_D(QEventDispatcherWinRT);
+ if (d->timerVec.isEmpty() || timerId <= 0)
+ return false;
+
+ WinRTTimerInfo *t = d->timerDict.value(timerId);
+ if (!t)
+ return false;
+
+ if (t->timer)
+ d->threadPoolTimerDict.remove(t->timer);
+ d->timerDict.remove(t->timerId);
+ d->timerVec.removeAll(t);
+ d->unregisterTimer(t);
+ return true;
+}
+
+bool QEventDispatcherWinRT::unregisterTimers(QObject *object)
+{
+ if (!object) {
+ qWarning("QEventDispatcherWinRT::unregisterTimers: invalid argument");
+ return false;
+ }
+ QThread *currentThread = QThread::currentThread();
+ if (object->thread() != thread() || thread() != currentThread) {
+ qWarning("QObject::killTimers: timers cannot be stopped from another thread");
+ return false;
+ }
+
+ Q_D(QEventDispatcherWinRT);
+ if (d->timerVec.isEmpty())
+ return false;
+ register WinRTTimerInfo *t;
+ for (int i = 0; i < d->timerVec.size(); i++) {
+ t = d->timerVec.at(i);
+ if (t && t->obj == object) { // object found
+ if (t->timer)
+ d->threadPoolTimerDict.remove(t->timer);
+ d->timerDict.remove(t->timerId);
+ d->timerVec.removeAt(i);
+ d->unregisterTimer(t);
+ --i;
+ }
+ }
+ return true;
+}
+
+QList<QAbstractEventDispatcher::TimerInfo> QEventDispatcherWinRT::registeredTimers(QObject *object) const
+{
+ if (!object) {
+ qWarning("QEventDispatcherWinRT:registeredTimers: invalid argument");
+ return QList<TimerInfo>();
+ }
+
+ Q_D(const QEventDispatcherWinRT);
+ QList<TimerInfo> list;
+ for (int i = 0; i < d->timerVec.size(); ++i) {
+ const WinRTTimerInfo *t = d->timerVec.at(i);
+ if (t && t->obj == object)
+ list << TimerInfo(t->timerId, t->interval, t->timerType);
+ }
+ return list;
+}
+
+bool QEventDispatcherWinRT::registerEventNotifier(QWinEventNotifier *notifier)
+{
+ Q_UNUSED(notifier);
+ Q_UNIMPLEMENTED();
+ return false;
+}
+
+void QEventDispatcherWinRT::unregisterEventNotifier(QWinEventNotifier *notifier)
+{
+ Q_UNUSED(notifier);
+ Q_UNIMPLEMENTED();
+}
+
+int QEventDispatcherWinRT::remainingTime(int timerId)
+{
+#ifndef QT_NO_DEBUG
+ if (timerId < 1) {
+ qWarning("QEventDispatcherWinRT::remainingTime: invalid argument");
+ return -1;
+ }
+#endif
+
+ Q_D(QEventDispatcherWinRT);
+
+ if (d->timerVec.isEmpty())
+ return -1;
+
+ quint64 currentTime = qt_msectime();
+
+ register WinRTTimerInfo *t;
+ for (int i = 0; i < d->timerVec.size(); i++) {
+ t = d->timerVec.at(i);
+ if (t && t->timerId == timerId) { // timer found
+ if (currentTime < t->timeout) {
+ // time to wait
+ return t->timeout - currentTime;
+ } else {
+ return 0;
+ }
+ }
+ }
+
+#ifndef QT_NO_DEBUG
+ qWarning("QEventDispatcherWinRT::remainingTime: timer id %d not found", timerId);
+#endif
+
+ return -1;
+}
+
+void QEventDispatcherWinRT::wakeUp()
+{
+ Q_D(QEventDispatcherWinRT);
+ if (d->wakeUps.testAndSetAcquire(0, 1)) {
+ // ###TODO: is there any thing to wake up?
+ }
+}
+
+void QEventDispatcherWinRT::interrupt()
+{
+ Q_D(QEventDispatcherWinRT);
+ d->interrupt = true;
+ wakeUp();
+}
+
+void QEventDispatcherWinRT::flush()
+{
+}
+
+void QEventDispatcherWinRT::startingUp()
+{
+}
+
+void QEventDispatcherWinRT::closingDown()
+{
+ Q_D(QEventDispatcherWinRT);
+
+ // clean up any timers
+ for (int i = 0; i < d->timerVec.count(); ++i)
+ d->unregisterTimer(d->timerVec.at(i));
+ d->timerVec.clear();
+ d->timerDict.clear();
+ d->threadPoolTimerDict.clear();
+}
+
+bool QEventDispatcherWinRT::event(QEvent *e)
+{
+ Q_D(QEventDispatcherWinRT);
+ if (e->type() == QEvent::ZeroTimerEvent) {
+ QZeroTimerEvent *zte = static_cast<QZeroTimerEvent*>(e);
+ WinRTTimerInfo *t = d->timerDict.value(zte->timerId());
+ if (t) {
+ t->inTimerEvent = true;
+
+ QTimerEvent te(zte->timerId());
+ QCoreApplication::sendEvent(t->obj, &te);
+
+ t = d->timerDict.value(zte->timerId());
+ if (t) {
+ if (t->interval == 0 && t->inTimerEvent) {
+ // post the next zero timer event as long as the timer was not restarted
+ QCoreApplication::postEvent(this, new QZeroTimerEvent(zte->timerId()));
+ }
+
+ t->inTimerEvent = false;
+ }
+ }
+ return true;
+ } else if (e->type() == QEvent::Timer) {
+ QTimerEvent *te = static_cast<QTimerEvent*>(e);
+ d->sendTimerEvent(te->timerId());
+ }
+ return QAbstractEventDispatcher::event(e);
+}
+
+QEventDispatcherWinRTPrivate::QEventDispatcherWinRTPrivate()
+ : interrupt(false)
+ , timerFactory(0)
+{
+ CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+ HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_System_Threading_ThreadPoolTimer).Get(), &timerFactory);
+ if (FAILED(hr))
+ qWarning("QEventDispatcherWinRTPrivate::QEventDispatcherWinRTPrivate: Could not obtain timer factory: %lx", hr);
+}
+
+QEventDispatcherWinRTPrivate::~QEventDispatcherWinRTPrivate()
+{
+ if (timerFactory)
+ timerFactory->Release();
+ CoUninitialize();
+}
+
+void QEventDispatcherWinRTPrivate::registerTimer(WinRTTimerInfo *t)
+{
+ Q_Q(QEventDispatcherWinRT);
+
+ int ok = 0;
+ uint interval = t->interval;
+ if (interval == 0u) {
+ // optimization for single-shot-zero-timer
+ QCoreApplication::postEvent(q, new QZeroTimerEvent(t->timerId));
+ ok = 1;
+ } else {
+ TimeSpan period;
+ period.Duration = interval * 10000; // TimeSpan is based on 100-nanosecond units
+ ok = SUCCEEDED(timerFactory->CreatePeriodicTimer(
+ Callback<ITimerElapsedHandler>(this, &QEventDispatcherWinRTPrivate::timerExpiredCallback).Get(), period, &t->timer));
+ if (ok)
+ threadPoolTimerDict.insert(t->timer, t);
+ }
+ t->timeout = qt_msectime() + interval;
+ if (ok == 0)
+ qErrnoWarning("QEventDispatcherWinRT::registerTimer: Failed to create a timer");
+}
+
+void QEventDispatcherWinRTPrivate::unregisterTimer(WinRTTimerInfo *t)
+{
+ if (t->timer) {
+ t->timer->Cancel();
+ t->timer->Release();
+ }
+ delete t;
+ t = 0;
+}
+
+void QEventDispatcherWinRTPrivate::sendTimerEvent(int timerId)
+{
+ WinRTTimerInfo *t = timerDict.value(timerId);
+ if (t && !t->inTimerEvent) {
+ // send event, but don't allow it to recurse
+ t->inTimerEvent = true;
+
+ QTimerEvent e(t->timerId);
+ QCoreApplication::sendEvent(t->obj, &e);
+
+ // timer could have been removed
+ t = timerDict.value(timerId);
+ if (t) {
+ t->inTimerEvent = false;
+ }
+ }
+}
+
+HRESULT QEventDispatcherWinRTPrivate::timerExpiredCallback(IThreadPoolTimer *source)
+{
+ register WinRTTimerInfo *t = threadPoolTimerDict.value(source);
+ if (t)
+ QCoreApplication::postEvent(t->dispatcher, new QTimerEvent(t->timerId));
+ else
+ qWarning("QEventDispatcherWinRT::timerExpiredCallback: Could not find timer %d in timer list", source);
+ return S_OK;
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qeventdispatcher_winrt_p.h b/src/corelib/kernel/qeventdispatcher_winrt_p.h
new file mode 100644
index 0000000000..35b3faa771
--- /dev/null
+++ b/src/corelib/kernel/qeventdispatcher_winrt_p.h
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCore module 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QEVENTDISPATCHER_WINRT_P_H
+#define QEVENTDISPATCHER_WINRT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qabstracteventdispatcher.h"
+#include "private/qabstracteventdispatcher_p.h"
+
+#include <qt_windows.h>
+#include <wrl.h>
+
+namespace ABI {
+ namespace Windows {
+ namespace System {
+ namespace Threading {
+ struct IThreadPoolTimer;
+ struct IThreadPoolTimerStatics;
+ }
+ }
+ }
+}
+
+QT_BEGIN_NAMESPACE
+
+int qt_msectime();
+
+class QEventDispatcherWinRTPrivate;
+
+class Q_CORE_EXPORT QEventDispatcherWinRT : public QAbstractEventDispatcher
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QEventDispatcherWinRT)
+
+public:
+ explicit QEventDispatcherWinRT(QObject *parent = 0);
+ ~QEventDispatcherWinRT();
+
+ bool processEvents(QEventLoop::ProcessEventsFlags flags);
+ bool hasPendingEvents();
+
+ void registerSocketNotifier(QSocketNotifier *notifier);
+ void unregisterSocketNotifier(QSocketNotifier *notifier);
+
+ void registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object);
+ bool unregisterTimer(int timerId);
+ bool unregisterTimers(QObject *object);
+ QList<TimerInfo> registeredTimers(QObject *object) const;
+
+ int remainingTime(int timerId);
+
+ bool registerEventNotifier(QWinEventNotifier *notifier);
+ void unregisterEventNotifier(QWinEventNotifier *notifier);
+
+ void wakeUp();
+ void interrupt();
+ void flush();
+
+ void startingUp();
+ void closingDown();
+
+protected:
+ QEventDispatcherWinRT(QEventDispatcherWinRTPrivate &dd, QObject *parent = 0);
+
+
+ bool event(QEvent *);
+ int activateTimers();
+ int activateSocketNotifiers();
+};
+
+struct WinRTTimerInfo // internal timer info
+{
+ WinRTTimerInfo() : timer(0) {}
+
+ QObject *dispatcher;
+ int timerId;
+ int interval;
+ Qt::TimerType timerType;
+ quint64 timeout; // - when to actually fire
+ QObject *obj; // - object to receive events
+ bool inTimerEvent;
+ ABI::Windows::System::Threading::IThreadPoolTimer *timer;
+};
+
+class QZeroTimerEvent : public QTimerEvent
+{
+public:
+ explicit inline QZeroTimerEvent(int timerId)
+ : QTimerEvent(timerId)
+ { t = QEvent::ZeroTimerEvent; }
+};
+
+class Q_CORE_EXPORT QEventDispatcherWinRTPrivate : public QAbstractEventDispatcherPrivate
+{
+ Q_DECLARE_PUBLIC(QEventDispatcherWinRT)
+
+public:
+ QEventDispatcherWinRTPrivate();
+ ~QEventDispatcherWinRTPrivate();
+
+ QList<WinRTTimerInfo*> timerVec;
+ QHash<int, WinRTTimerInfo*> timerDict;
+ QHash<ABI::Windows::System::Threading::IThreadPoolTimer*, WinRTTimerInfo*> threadPoolTimerDict;
+
+ void registerTimer(WinRTTimerInfo *t);
+ void unregisterTimer(WinRTTimerInfo *t);
+ void sendTimerEvent(int timerId);
+ HRESULT timerExpiredCallback(ABI::Windows::System::Threading::IThreadPoolTimer *source);
+
+ QAtomicInt wakeUps;
+ bool interrupt;
+
+ ABI::Windows::System::Threading::IThreadPoolTimerStatics *timerFactory;
+};
+
+QT_END_NAMESPACE
+
+#endif // QEVENTDISPATCHER_WINRT_P_H
diff --git a/src/corelib/kernel/qfunctions_p.h b/src/corelib/kernel/qfunctions_p.h
index 6e094f1ed3..e3014a0dcf 100644
--- a/src/corelib/kernel/qfunctions_p.h
+++ b/src/corelib/kernel/qfunctions_p.h
@@ -61,6 +61,8 @@
# include "QtCore/qfunctions_vxworks.h"
#elif defined(Q_OS_NACL)
# include "QtCore/qfunctions_nacl.h"
+#elif defined(Q_OS_WINRT)
+# include "QtCore/qfunctions_winrt.h"
#endif
#ifdef Q_CC_RVCT
diff --git a/src/corelib/kernel/qfunctions_winrt.cpp b/src/corelib/kernel/qfunctions_winrt.cpp
new file mode 100644
index 0000000000..1348af2acb
--- /dev/null
+++ b/src/corelib/kernel/qfunctions_winrt.cpp
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCore module 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifdef Q_OS_WINRT
+
+#include "qfunctions_winrt.h"
+#include "qstring.h"
+#include "qbytearray.h"
+#include "qhash.h"
+
+QT_USE_NAMESPACE
+
+// Environment ------------------------------------------------------
+inline QHash<QByteArray, QByteArray> &qt_app_environment()
+{
+ static QHash<QByteArray, QByteArray> internalEnvironment;
+ return internalEnvironment;
+}
+
+errno_t qt_winrt_getenv_s(size_t* sizeNeeded, char* buffer, size_t bufferSize, const char* varName)
+{
+ if (!sizeNeeded)
+ return EINVAL;
+
+ if (!qt_app_environment().contains(varName)) {
+ if (buffer)
+ buffer[0] = '\0';
+ return ENOENT;
+ }
+
+ QByteArray value = qt_app_environment().value(varName);
+ if (!value.endsWith('\0')) // win32 guarantees terminated string
+ value.append('\0');
+
+ if (bufferSize < (size_t)value.size()) {
+ *sizeNeeded = value.size();
+ return ERANGE;
+ }
+
+ strcpy(buffer, value.constData());
+ return 0;
+}
+
+errno_t qt_winrt__putenv_s(const char* varName, const char* value)
+{
+ QByteArray input = value;
+ if (input.isEmpty()) {
+ if (qt_app_environment().contains(varName))
+ qt_app_environment().remove(varName);
+ } else {
+ // win32 on winrt guarantees terminated string
+ if (!input.endsWith('\0'))
+ input.append('\0');
+ qt_app_environment()[varName] = input;
+ }
+
+ return 0;
+}
+
+void qt_winrt_tzset()
+{
+}
+
+void qt_winrt__tzset()
+{
+}
+
+#endif // Q_OS_WINRT
diff --git a/src/corelib/kernel/qfunctions_winrt.h b/src/corelib/kernel/qfunctions_winrt.h
new file mode 100644
index 0000000000..fa2b2e12ff
--- /dev/null
+++ b/src/corelib/kernel/qfunctions_winrt.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCore module 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFUNCTIONS_WINRT_H
+#define QFUNCTIONS_WINRT_H
+
+#include <QtCore/qglobal.h>
+
+#ifdef Q_OS_WINRT
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_BUILD_CORE_LIB
+#endif
+
+QT_END_NAMESPACE
+
+// Environment ------------------------------------------------------
+errno_t qt_winrt_getenv_s(size_t*, char*, size_t, const char*);
+errno_t qt_winrt__putenv_s(const char*, const char*);
+void qt_winrt_tzset();
+void qt_winrt__tzset();
+
+// As Windows Runtime lacks some standard functions used in Qt, these got
+// reimplemented. Other projects do this as well. Inline functions are used
+// that there is a central place to disable functions for newer versions if
+// they get available. There are no defines used anymore, because this
+// will break member functions of classes which are called like these
+// functions.
+// The other declarations available in this file are being used per
+// define inside qplatformdefs.h of the corresponding WinRT mkspec.
+
+#define generate_inline_return_func0(funcname, returntype) \
+ inline returntype funcname() \
+ { \
+ return qt_winrt_##funcname(); \
+ }
+#define generate_inline_return_func1(funcname, returntype, param1) \
+ inline returntype funcname(param1 p1) \
+ { \
+ return qt_winrt_##funcname(p1); \
+ }
+#define generate_inline_return_func2(funcname, returntype, param1, param2) \
+ inline returntype funcname(param1 p1, param2 p2) \
+ { \
+ return qt_winrt_##funcname(p1, p2); \
+ }
+#define generate_inline_return_func3(funcname, returntype, param1, param2, param3) \
+ inline returntype funcname(param1 p1, param2 p2, param3 p3) \
+ { \
+ return qt_winrt_##funcname(p1, p2, p3); \
+ }
+#define generate_inline_return_func4(funcname, returntype, param1, param2, param3, param4) \
+ inline returntype funcname(param1 p1, param2 p2, param3 p3, param4 p4) \
+ { \
+ return qt_winrt_##funcname(p1, p2, p3, p4); \
+ }
+#define generate_inline_return_func5(funcname, returntype, param1, param2, param3, param4, param5) \
+ inline returntype funcname(param1 p1, param2 p2, param3 p3, param4 p4, param5 p5) \
+ { \
+ return qt_winrt_##funcname(p1, p2, p3, p4, p5); \
+ }
+#define generate_inline_return_func6(funcname, returntype, param1, param2, param3, param4, param5, param6) \
+ inline returntype funcname(param1 p1, param2 p2, param3 p3, param4 p4, param5 p5, param6 p6) \
+ { \
+ return qt_winrt_##funcname(p1, p2, p3, p4, p5, p6); \
+ }
+#define generate_inline_return_func7(funcname, returntype, param1, param2, param3, param4, param5, param6, param7) \
+ inline returntype funcname(param1 p1, param2 p2, param3 p3, param4 p4, param5 p5, param6 p6, param7 p7) \
+ { \
+ return qt_winrt_##funcname(p1, p2, p3, p4, p5, p6, p7); \
+ }
+
+typedef unsigned (__stdcall *StartAdressExFunc)(void *);
+typedef void(*StartAdressFunc)(void *);
+typedef int ( __cdecl *CompareFunc ) (const void *, const void *) ;
+
+generate_inline_return_func4(getenv_s, errno_t, size_t *, char *, size_t, const char *)
+generate_inline_return_func2(_putenv_s, errno_t, const char *, const char *)
+generate_inline_return_func0(tzset, void)
+generate_inline_return_func0(_tzset, void)
+
+#endif // Q_OS_WINRT
+#endif // QFUNCTIONS_WINRT_H
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp
index e02026ca4c..29c5a3dd24 100644
--- a/src/corelib/kernel/qobject.cpp
+++ b/src/corelib/kernel/qobject.cpp
@@ -485,6 +485,79 @@ void QMetaCallEvent::placeMetaCall(QObject *object)
}
/*!
+ \class QSignalBlocker
+ \brief Exception-safe wrapper around QObject::blockSignals()
+ \since 5.3
+ \ingroup objectmodel
+
+ \reentrant
+
+ QSignalBlocker can be used whereever you would otherwise use a
+ pair of calls to blockSignals(). It blocks signals in its
+ constructor and in the destructor it resets the state to what
+ it was before the constructor ran.
+
+ \code
+ {
+ const QSignalBlocker blocker(someQObject);
+ // no signals here
+ }
+ \endcode
+ is thus equivalent to
+ \code
+ const bool wasBlocked = someQObject->blockSignals(true);
+ // no signals here
+ someQObject->blockSignals(false);
+ \endcode
+
+ except the code using QSignalBlocker is safe in the face of
+ exceptions.
+
+ \sa QMutexLocker, QEventLoopLocker
+*/
+
+/*!
+ \fn QSignalBlocker::QSignalBlocker(QObject *object)
+
+ Constructor. Calls \a{object}->blockSignals(true).
+*/
+
+/*!
+ \fn QSignalBlocker::QSignalBlocker(QObject &object)
+ \overload
+
+ Calls \a{object}.blockSignals(true).
+*/
+
+/*!
+ \fn QSignalBlocker::~QSignalBlocker()
+
+ Destructor. Restores the QObject::signalsBlocked() state to what it
+ was before the constructor ran, unless unblock() has been called
+ without a following reblock(), in which case it does nothing.
+*/
+
+/*!
+ \fn void QSignalBlocker::reblock()
+
+ Re-blocks signals after a previous unblock().
+
+ The numbers of reblock() and unblock() calls are not counted, so
+ every reblock() undoes any number of unblock() calls.
+*/
+
+/*!
+ \fn void QSignalBlocker::unblock()
+
+ Temporarily restores the QObject::signalsBlocked() state to what
+ it was before this QSignaBlocker's constructor ran. To undo, use
+ reblock().
+
+ The numbers of reblock() and unblock() calls are not counted, so
+ every unblock() undoes any number of reblock() calls.
+*/
+
+/*!
\class QObject
\inmodule QtCore
\brief The QObject class is the base class of all Qt objects.
diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h
index e2000afc82..f9239bf575 100644
--- a/src/corelib/kernel/qobject.h
+++ b/src/corelib/kernel/qobject.h
@@ -549,6 +549,52 @@ template <class T> inline const char * qobject_interface_iid()
Q_CORE_EXPORT QDebug operator<<(QDebug, const QObject *);
#endif
+class Q_CORE_EXPORT QSignalBlocker
+{
+public:
+ inline explicit QSignalBlocker(QObject *o);
+ inline explicit QSignalBlocker(QObject &o);
+ inline ~QSignalBlocker();
+
+ inline void reblock();
+ inline void unblock();
+private:
+ Q_DISABLE_COPY(QSignalBlocker)
+ QObject * const m_o;
+ bool m_blocked;
+ bool m_inhibited;
+};
+
+QSignalBlocker::QSignalBlocker(QObject *o)
+ : m_o(o),
+ m_blocked(o && o->blockSignals(true)),
+ m_inhibited(false)
+{}
+
+QSignalBlocker::QSignalBlocker(QObject &o)
+ : m_o(&o),
+ m_blocked(o.blockSignals(true)),
+ m_inhibited(false)
+{}
+
+QSignalBlocker::~QSignalBlocker()
+{
+ if (m_o && !m_inhibited)
+ m_o->blockSignals(m_blocked);
+}
+
+void QSignalBlocker::reblock()
+{
+ if (m_o) m_o->blockSignals(true);
+ m_inhibited = false;
+}
+
+void QSignalBlocker::unblock()
+{
+ if (m_o) m_o->blockSignals(m_blocked);
+ m_inhibited = true;
+}
+
namespace QtPrivate {
inline QObject & deref_for_methodcall(QObject &o) { return o; }
inline QObject & deref_for_methodcall(QObject *o) { return *o; }
diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp
index 69cfa7888f..91ccf3996e 100644
--- a/src/corelib/kernel/qvariant.cpp
+++ b/src/corelib/kernel/qvariant.cpp
@@ -868,7 +868,7 @@ static void customConstruct(QVariant::Private *d, const void *copy)
// this logic should match with QVariantIntegrator::CanUseInternalSpace
if (size <= sizeof(QVariant::Private::Data)
- && (type.flags() & QMetaType::MovableType)) {
+ && (type.flags() & (QMetaType::MovableType | QMetaType::IsEnumeration))) {
type.construct(&d->data.ptr, copy);
d->is_shared = false;
} else {
diff --git a/src/corelib/kernel/qvariant_p.h b/src/corelib/kernel/qvariant_p.h
index 4ec049e20d..b523d19187 100644
--- a/src/corelib/kernel/qvariant_p.h
+++ b/src/corelib/kernel/qvariant_p.h
@@ -67,7 +67,7 @@ template<typename T>
struct QVariantIntegrator
{
static const bool CanUseInternalSpace = sizeof(T) <= sizeof(QVariant::Private::Data)
- && (!QTypeInfo<T>::isStatic);
+ && ((!QTypeInfo<T>::isStatic) || Q_IS_ENUM(T));
};
Q_STATIC_ASSERT(QVariantIntegrator<double>::CanUseInternalSpace);
Q_STATIC_ASSERT(QVariantIntegrator<long int>::CanUseInternalSpace);
diff --git a/src/corelib/kernel/qwineventnotifier.cpp b/src/corelib/kernel/qwineventnotifier.cpp
index eab5a37645..c694237dc3 100644
--- a/src/corelib/kernel/qwineventnotifier.cpp
+++ b/src/corelib/kernel/qwineventnotifier.cpp
@@ -41,7 +41,11 @@
#include "qwineventnotifier.h"
+#ifdef Q_OS_WINRT
+#include "qeventdispatcher_winrt_p.h"
+#else
#include "qeventdispatcher_win_p.h"
+#endif
#include "qcoreapplication.h"
#include <private/qthread_p.h>
diff --git a/src/corelib/plugin/qlibrary_win.cpp b/src/corelib/plugin/qlibrary_win.cpp
index 928f2c5eb1..b9494a3041 100644
--- a/src/corelib/plugin/qlibrary_win.cpp
+++ b/src/corelib/plugin/qlibrary_win.cpp
@@ -107,12 +107,16 @@ bool QLibraryPrivate::load_sys()
attempts.append(QFileInfo(fileName).absoluteFilePath());
#endif
}
+#ifdef Q_OS_WINRT
+ if (fileName.startsWith(QLatin1Char('/')))
+ attempts.prepend(QDir::rootPath() + fileName);
+#endif
Q_FOREACH (const QString &attempt, attempts) {
#ifndef Q_OS_WINRT
pHnd = LoadLibrary((wchar_t*)QDir::toNativeSeparators(attempt).utf16());
#else // Q_OS_WINRT
- QString path = QDir::toNativeSeparators(QDir::current().relativeFilePath(fileName));
+ QString path = QDir::toNativeSeparators(QDir::current().relativeFilePath(attempt));
pHnd = LoadPackagedLibrary((LPCWSTR)path.utf16(), 0);
if (pHnd)
qualifiedFileName = attempt;
diff --git a/src/corelib/thread/qmutex_win.cpp b/src/corelib/thread/qmutex_win.cpp
index 14b7f34008..a8cdf85fb6 100644
--- a/src/corelib/thread/qmutex_win.cpp
+++ b/src/corelib/thread/qmutex_win.cpp
@@ -48,7 +48,12 @@ QT_BEGIN_NAMESPACE
QMutexPrivate::QMutexPrivate()
{
+#ifndef Q_OS_WINRT
event = CreateEvent(0, FALSE, FALSE, 0);
+#else
+ event = CreateEventEx(0, NULL, 0, EVENT_ALL_ACCESS);
+#endif
+
if (!event)
qWarning("QMutexData::QMutexData: Cannot create event");
}
@@ -58,7 +63,11 @@ QMutexPrivate::~QMutexPrivate()
bool QMutexPrivate::wait(int timeout)
{
+#ifndef Q_OS_WINRT
return (WaitForSingleObject(event, timeout < 0 ? INFINITE : timeout) == WAIT_OBJECT_0);
+#else
+ return (WaitForSingleObjectEx(event, timeout < 0 ? INFINITE : timeout, FALSE) == WAIT_OBJECT_0);
+#endif
}
void QMutexPrivate::wakeUp() Q_DECL_NOTHROW
diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp
index 50ccca9eda..adad4b81a1 100644
--- a/src/corelib/thread/qthread.cpp
+++ b/src/corelib/thread/qthread.cpp
@@ -154,7 +154,9 @@ QThreadPrivate::QThreadPrivate(QThreadData *d)
thread_id = 0;
#elif defined (Q_OS_WIN)
handle = 0;
+# ifndef Q_OS_WINRT
id = 0;
+# endif
waiters = 0;
#endif
#if defined (Q_OS_WIN)
diff --git a/src/corelib/thread/qthread_p.h b/src/corelib/thread/qthread_p.h
index 8429e41433..c9b98ac5c0 100644
--- a/src/corelib/thread/qthread_p.h
+++ b/src/corelib/thread/qthread_p.h
@@ -66,6 +66,10 @@
#include <algorithm>
+#ifdef Q_OS_WINRT
+#include <thread>
+#endif
+
QT_BEGIN_NAMESPACE
class QAbstractEventDispatcher;
@@ -173,8 +177,13 @@ public:
static unsigned int __stdcall start(void *);
static void finish(void *, bool lockAnyway=true);
+# ifndef Q_OS_WINRT
Qt::HANDLE handle;
unsigned int id;
+# else
+ std::thread *handle;
+ std::thread::id id;
+# endif
int waiters;
bool terminationEnabled, terminatePending;
# endif
diff --git a/src/corelib/thread/qthread_win.cpp b/src/corelib/thread/qthread_win.cpp
index 9d90583a28..903e72c151 100644
--- a/src/corelib/thread/qthread_win.cpp
+++ b/src/corelib/thread/qthread_win.cpp
@@ -40,7 +40,7 @@
****************************************************************************/
//#define WINVER 0x0500
-#if _WIN32_WINNT < 0x0400
+#if (_WIN32_WINNT < 0x0400) && !defined(Q_OS_WINRT)
#define _WIN32_WINNT 0x0400
#endif
@@ -54,10 +54,17 @@
#include <qpointer.h>
#include <private/qcoreapplication_p.h>
+#ifdef Q_OS_WINRT
+#include "private/qeventdispatcher_winrt_p.h"
+#else
#include <private/qeventdispatcher_win_p.h>
+#endif
#include <qt_windows.h>
+#ifdef Q_OS_WINRT
+#include <thread>
+#endif
#ifndef Q_OS_WINCE
#ifndef _MT
@@ -71,6 +78,7 @@
#ifndef QT_NO_THREAD
QT_BEGIN_NAMESPACE
+#ifndef Q_OS_WINRT
void qt_watch_adopted_thread(const HANDLE adoptedThreadHandle, QThread *qthread);
DWORD WINAPI qt_adopted_thread_watcher_function(LPVOID);
@@ -92,6 +100,38 @@ static void qt_free_tls()
}
}
Q_DESTRUCTOR_FUNCTION(qt_free_tls)
+#else // !Q_OS_WINRT
+
+__declspec(thread) static QThreadData* qt_current_thread_data_tls_index = 0;
+void qt_create_tls()
+{
+}
+
+static void qt_free_tls()
+{
+ if (qt_current_thread_data_tls_index) {
+ qt_current_thread_data_tls_index->deref();
+ qt_current_thread_data_tls_index = 0;
+ }
+}
+
+QThreadData* TlsGetValue(QThreadData*& tls)
+{
+ Q_ASSERT(tls == qt_current_thread_data_tls_index);
+ return tls;
+}
+
+void TlsSetValue(QThreadData*& tls, QThreadData* data)
+{
+ Q_ASSERT(tls == qt_current_thread_data_tls_index);
+ if (tls)
+ tls->deref();
+ tls = data;
+ if (tls)
+ tls->ref();
+}
+Q_DESTRUCTOR_FUNCTION(qt_free_tls)
+#endif // Q_OS_WINRT
/*
QThreadData
@@ -124,6 +164,9 @@ QThreadData *QThreadData::current()
if (!QCoreApplicationPrivate::theMainThread) {
QCoreApplicationPrivate::theMainThread = threadData->thread;
+#ifndef Q_OS_WINRT
+ // TODO: is there a way to reflect the branch's behavior using
+ // WinRT API?
} else {
HANDLE realHandle = INVALID_HANDLE_VALUE;
#if !defined(Q_OS_WINCE) || (defined(_WIN32_WCE) && (_WIN32_WCE>=0x600))
@@ -138,6 +181,7 @@ QThreadData *QThreadData::current()
realHandle = reinterpret_cast<HANDLE>(GetCurrentThreadId());
#endif
qt_watch_adopted_thread(realHandle, threadData->thread);
+#endif // !Q_OS_WINRT
}
}
return threadData;
@@ -145,10 +189,16 @@ QThreadData *QThreadData::current()
void QAdoptedThread::init()
{
+#ifndef Q_OS_WINRT
d_func()->handle = GetCurrentThread();
d_func()->id = GetCurrentThreadId();
+#else
+ d_func()->handle = nullptr;
+ d_func()->id = std::this_thread::get_id();
+#endif
}
+#ifndef Q_OS_WINRT
static QVector<HANDLE> qt_adopted_thread_handles;
static QVector<QThread *> qt_adopted_qthreads;
static QMutex qt_adopted_thread_watcher_mutex;
@@ -297,6 +347,7 @@ void qt_set_thread_name(HANDLE threadId, LPCSTR threadName)
}
}
#endif // !QT_NO_DEBUG && Q_CC_MSVC && !Q_OS_WINCE
+#endif // !Q_OS_WINRT
/**************************************************************************
** QThreadPrivate
@@ -306,7 +357,11 @@ void qt_set_thread_name(HANDLE threadId, LPCSTR threadName)
void QThreadPrivate::createEventDispatcher(QThreadData *data)
{
+#ifdef Q_OS_WINRT
+ QEventDispatcherWinRT *theEventDispatcher = new QEventDispatcherWinRT;
+#else
QEventDispatcherWin32 *theEventDispatcher = new QEventDispatcherWin32;
+#endif
data->eventDispatcher.storeRelease(theEventDispatcher);
theEventDispatcher->startingUp();
}
@@ -334,7 +389,7 @@ unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(voi
else
createEventDispatcher(data);
-#if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC) && !defined(Q_OS_WINCE)
+#if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
// sets the name of the current thread.
QByteArray objectName = thr->objectName().toLocal8Bit();
qt_set_thread_name((HANDLE)-1,
@@ -380,11 +435,20 @@ void QThreadPrivate::finish(void *arg, bool lockAnyway)
d->interruptionRequested = false;
if (!d->waiters) {
+#ifndef Q_OS_WINRT
CloseHandle(d->handle);
+#else
+ CloseHandle(d->handle->native_handle());
+ delete d->handle;
+#endif
d->handle = 0;
}
+#ifndef Q_OS_WINRT
d->id = 0;
+#else
+ d->id = std::thread::id();
+#endif
}
@@ -400,10 +464,15 @@ Qt::HANDLE QThread::currentThreadId() Q_DECL_NOTHROW
int QThread::idealThreadCount() Q_DECL_NOTHROW
{
SYSTEM_INFO sysinfo;
+#ifndef Q_OS_WINRT
GetSystemInfo(&sysinfo);
+#else
+ GetNativeSystemInfo(&sysinfo);
+#endif
return sysinfo.dwNumberOfProcessors;
}
+#ifndef Q_OS_WINRT
void QThread::yieldCurrentThread()
{
#ifndef Q_OS_WINCE
@@ -427,7 +496,28 @@ void QThread::usleep(unsigned long usecs)
{
::Sleep((usecs / 1000) + 1);
}
+#else // !Q_OS_WINRT
+
+void QThread::yieldCurrentThread()
+{
+ std::this_thread::yield();
+}
+void QThread::sleep(unsigned long secs)
+{
+ std::this_thread::sleep_for(std::chrono::seconds(secs));
+}
+
+void QThread::msleep(unsigned long msecs)
+{
+ std::this_thread::sleep_for(std::chrono::milliseconds(msecs));
+}
+
+void QThread::usleep(unsigned long usecs)
+{
+ std::this_thread::sleep_for(std::chrono::microseconds(usecs));
+}
+#endif // Q_OS_WINRT
void QThread::start(Priority priority)
{
@@ -449,6 +539,7 @@ void QThread::start(Priority priority)
d->returnCode = 0;
d->interruptionRequested = false;
+#ifndef Q_OS_WINRT
/*
NOTE: we create the thread in the suspended state, set the
priority and then resume the thread.
@@ -513,6 +604,23 @@ void QThread::start(Priority priority)
if (ResumeThread(d->handle) == (DWORD) -1) {
qErrnoWarning("QThread::start: Failed to resume new thread");
}
+#else // !Q_OS_WINRT
+ d->handle = new std::thread(QThreadPrivate::start, this);
+
+ if (!d->handle) {
+ qErrnoWarning(errno, "QThread::start: Failed to create thread");
+ d->running = false;
+ d->finished = true;
+ return;
+ }
+
+ d->id = d->handle->get_id();
+
+ if (priority != NormalPriority || priority != InheritPriority) {
+ qWarning("QThread::start: Failed to set thread priority (not implemented)");
+ d->priority = NormalPriority;
+ }
+#endif // Q_OS_WINRT
}
void QThread::terminate()
@@ -525,7 +633,14 @@ void QThread::terminate()
d->terminatePending = true;
return;
}
+
+#ifndef Q_OS_WINRT
TerminateThread(d->handle, 0);
+#else // !Q_OS_WINRT
+ qWarning("QThread::terminate: Terminate is not supported on WinRT");
+ CloseHandle(d->handle->native_handle());
+ d->handle = 0;
+#endif // Q_OS_WINRT
QThreadPrivate::finish(this, false);
}
@@ -534,7 +649,11 @@ bool QThread::wait(unsigned long time)
Q_D(QThread);
QMutexLocker locker(&d->mutex);
+#ifndef Q_OS_WINRT
if (d->id == GetCurrentThreadId()) {
+#else
+ if (d->id == std::this_thread::get_id()) {
+#endif
qWarning("QThread::wait: Thread tried to wait on itself");
return false;
}
@@ -545,6 +664,7 @@ bool QThread::wait(unsigned long time)
locker.mutex()->unlock();
bool ret = false;
+#ifndef Q_OS_WINRT
switch (WaitForSingleObject(d->handle, time)) {
case WAIT_OBJECT_0:
ret = true;
@@ -557,6 +677,23 @@ bool QThread::wait(unsigned long time)
default:
break;
}
+#else // !Q_OS_WINRT
+ if (d->handle->joinable()) {
+ switch (WaitForSingleObjectEx(d->handle->native_handle(), time, FALSE)) {
+ case WAIT_OBJECT_0:
+ ret = true;
+ d->handle->join();
+ break;
+ case WAIT_FAILED:
+ qErrnoWarning("QThread::wait: WaitForSingleObjectEx() failed");
+ break;
+ case WAIT_ABANDONED:
+ case WAIT_TIMEOUT:
+ default:
+ break;
+ }
+ }
+#endif // Q_OS_WINRT
locker.mutex()->lock();
--d->waiters;
@@ -568,7 +705,11 @@ bool QThread::wait(unsigned long time)
}
if (d->finished && !d->waiters) {
+#ifndef Q_OS_WINRT
CloseHandle(d->handle);
+#else
+ delete d->handle;
+#endif
d->handle = 0;
}
@@ -586,13 +727,16 @@ void QThread::setTerminationEnabled(bool enabled)
if (enabled && d->terminatePending) {
QThreadPrivate::finish(thr, false);
locker.unlock(); // don't leave the mutex locked!
+#ifndef Q_OS_WINRT
_endthreadex(0);
+#endif
}
}
// Caller must hold the mutex
void QThreadPrivate::setPriority(QThread::Priority threadPriority)
{
+#ifndef Q_OS_WINRT
// copied from start() with a few modifications:
int prio;
@@ -635,6 +779,12 @@ void QThreadPrivate::setPriority(QThread::Priority threadPriority)
if (!SetThreadPriority(handle, prio)) {
qErrnoWarning("QThread::setPriority: Failed to set thread priority");
}
+#else // !Q_OS_WINRT
+ if (priority != threadPriority) {
+ qWarning("QThread::setPriority: Failed to set thread priority (not implemented)");
+ return;
+ }
+#endif // Q_OS_WINRT
}
QT_END_NAMESPACE
diff --git a/src/corelib/thread/qthreadpool.cpp b/src/corelib/thread/qthreadpool.cpp
index fb1d1ee7cc..269f561a91 100644
--- a/src/corelib/thread/qthreadpool.cpp
+++ b/src/corelib/thread/qthreadpool.cpp
@@ -61,6 +61,7 @@ public:
void run();
void registerThreadInactive();
+ QWaitCondition runnableReady;
QThreadPoolPrivate *manager;
QRunnable *runnable;
};
@@ -128,14 +129,13 @@ void QThreadPoolThread::run()
// if too many threads are active, expire this thread
bool expired = manager->tooManyThreadsActive();
if (!expired) {
- ++manager->waitingThreads;
+ manager->waitingThreads.enqueue(this);
registerThreadInactive();
// wait for work, exiting after the expiry timeout is reached
- expired = !manager->runnableReady.wait(locker.mutex(), manager->expiryTimeout);
+ runnableReady.wait(locker.mutex(), manager->expiryTimeout);
++manager->activeThreads;
-
- if (expired)
- --manager->waitingThreads;
+ if (manager->waitingThreads.removeOne(this))
+ expired = true;
}
if (expired) {
manager->expiredThreads.enqueue(this);
@@ -160,7 +160,6 @@ QThreadPoolPrivate:: QThreadPoolPrivate()
expiryTimeout(30000),
maxThreadCount(qAbs(QThread::idealThreadCount())),
reservedThreads(0),
- waitingThreads(0),
activeThreads(0)
{ }
@@ -176,11 +175,10 @@ bool QThreadPoolPrivate::tryStart(QRunnable *task)
if (activeThreadCount() >= maxThreadCount)
return false;
- if (waitingThreads > 0) {
+ if (waitingThreads.count() > 0) {
// recycle an available thread
- --waitingThreads;
enqueueTask(task);
- runnableReady.wakeOne();
+ waitingThreads.takeFirst()->runnableReady.wakeOne();
return true;
}
@@ -225,7 +223,7 @@ int QThreadPoolPrivate::activeThreadCount() const
{
return (allThreads.count()
- expiredThreads.count()
- - waitingThreads
+ - waitingThreads.count()
+ reservedThreads);
}
@@ -266,7 +264,6 @@ void QThreadPoolPrivate::reset()
{
QMutexLocker locker(&mutex);
isExiting = true;
- runnableReady.wakeAll();
while (!allThreads.empty()) {
// move the contents of the set out so that we can iterate without the lock
@@ -275,6 +272,7 @@ void QThreadPoolPrivate::reset()
locker.unlock();
foreach (QThreadPoolThread *thread, allThreadsCopy) {
+ thread->runnableReady.wakeAll();
thread->wait();
delete thread;
}
@@ -283,7 +281,7 @@ void QThreadPoolPrivate::reset()
// repeat until all newly arrived threads have also completed
}
- waitingThreads = 0;
+ waitingThreads.clear();
expiredThreads.clear();
isExiting = false;
@@ -459,10 +457,8 @@ void QThreadPool::start(QRunnable *runnable, int priority)
if (!d->tryStart(runnable)) {
d->enqueueTask(runnable, priority);
- if (d->waitingThreads > 0) {
- --d->waitingThreads;
- d->runnableReady.wakeOne();
- }
+ if (!d->waitingThreads.isEmpty())
+ d->waitingThreads.takeFirst()->runnableReady.wakeOne();
}
}
diff --git a/src/corelib/thread/qthreadpool_p.h b/src/corelib/thread/qthreadpool_p.h
index ba77f7e57c..bd5f546fdb 100644
--- a/src/corelib/thread/qthreadpool_p.h
+++ b/src/corelib/thread/qthreadpool_p.h
@@ -87,8 +87,8 @@ public:
void stealRunnable(QRunnable *);
mutable QMutex mutex;
- QWaitCondition runnableReady;
QSet<QThreadPoolThread *> allThreads;
+ QQueue<QThreadPoolThread *> waitingThreads;
QQueue<QThreadPoolThread *> expiredThreads;
QList<QPair<QRunnable *, int> > queue;
QWaitCondition noActiveThreads;
@@ -97,7 +97,6 @@ public:
int expiryTimeout;
int maxThreadCount;
int reservedThreads;
- int waitingThreads;
int activeThreads;
};
diff --git a/src/corelib/thread/qwaitcondition_win.cpp b/src/corelib/thread/qwaitcondition_win.cpp
index 1cb1f82b03..f09667a364 100644
--- a/src/corelib/thread/qwaitcondition_win.cpp
+++ b/src/corelib/thread/qwaitcondition_win.cpp
@@ -64,7 +64,11 @@ class QWaitConditionEvent
public:
inline QWaitConditionEvent() : priority(0), wokenUp(false)
{
+#ifndef Q_OS_WINRT
event = CreateEvent(NULL, TRUE, FALSE, NULL);
+#else
+ event = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
+#endif
}
inline ~QWaitConditionEvent() { CloseHandle(event); }
int priority;
@@ -91,7 +95,9 @@ QWaitConditionEvent *QWaitConditionPrivate::pre()
mtx.lock();
QWaitConditionEvent *wce =
freeQueue.isEmpty() ? new QWaitConditionEvent : freeQueue.takeFirst();
+#ifndef Q_OS_WINRT
wce->priority = GetThreadPriority(GetCurrentThread());
+#endif
wce->wokenUp = false;
// insert 'wce' into the queue (sorted by priority)
@@ -111,7 +117,12 @@ bool QWaitConditionPrivate::wait(QWaitConditionEvent *wce, unsigned long time)
{
// wait for the event
bool ret = false;
+#ifndef Q_OS_WINRT
switch (WaitForSingleObject(wce->event, time)) {
+#else
+ switch (WaitForSingleObjectEx(wce->event, time, FALSE)) {
+#endif
+
default: break;
case WAIT_OBJECT_0:
diff --git a/src/corelib/tools/qalgorithms.h b/src/corelib/tools/qalgorithms.h
index c6eede05cb..f75f33f25c 100644
--- a/src/corelib/tools/qalgorithms.h
+++ b/src/corelib/tools/qalgorithms.h
@@ -55,28 +55,28 @@ namespace QAlgorithmsPrivate {
#if QT_DEPRECATED_SINCE(5, 2)
template <typename RandomAccessIterator, typename T, typename LessThan>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE void qSortHelper(RandomAccessIterator start, RandomAccessIterator end, const T &t, LessThan lessThan);
+QT_DEPRECATED_X("Use std::sort") Q_OUTOFLINE_TEMPLATE void qSortHelper(RandomAccessIterator start, RandomAccessIterator end, const T &t, LessThan lessThan);
template <typename RandomAccessIterator, typename T>
-QT_DEPRECATED inline void qSortHelper(RandomAccessIterator begin, RandomAccessIterator end, const T &dummy);
+QT_DEPRECATED_X("Use std::sort") inline void qSortHelper(RandomAccessIterator begin, RandomAccessIterator end, const T &dummy);
template <typename RandomAccessIterator, typename T, typename LessThan>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE void qStableSortHelper(RandomAccessIterator start, RandomAccessIterator end, const T &t, LessThan lessThan);
+QT_DEPRECATED_X("Use std::stable_sort") Q_OUTOFLINE_TEMPLATE void qStableSortHelper(RandomAccessIterator start, RandomAccessIterator end, const T &t, LessThan lessThan);
template <typename RandomAccessIterator, typename T>
-QT_DEPRECATED inline void qStableSortHelper(RandomAccessIterator, RandomAccessIterator, const T &);
+QT_DEPRECATED_X("Use std::stable_sort") inline void qStableSortHelper(RandomAccessIterator, RandomAccessIterator, const T &);
template <typename RandomAccessIterator, typename T, typename LessThan>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE RandomAccessIterator qLowerBoundHelper(RandomAccessIterator begin, RandomAccessIterator end, const T &value, LessThan lessThan);
+QT_DEPRECATED_X("Use std::lower_bound") Q_OUTOFLINE_TEMPLATE RandomAccessIterator qLowerBoundHelper(RandomAccessIterator begin, RandomAccessIterator end, const T &value, LessThan lessThan);
template <typename RandomAccessIterator, typename T, typename LessThan>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE RandomAccessIterator qUpperBoundHelper(RandomAccessIterator begin, RandomAccessIterator end, const T &value, LessThan lessThan);
+QT_DEPRECATED_X("Use std::upper_bound") Q_OUTOFLINE_TEMPLATE RandomAccessIterator qUpperBoundHelper(RandomAccessIterator begin, RandomAccessIterator end, const T &value, LessThan lessThan);
template <typename RandomAccessIterator, typename T, typename LessThan>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE RandomAccessIterator qBinaryFindHelper(RandomAccessIterator begin, RandomAccessIterator end, const T &value, LessThan lessThan);
+QT_DEPRECATED_X("Use std::binary_search") Q_OUTOFLINE_TEMPLATE RandomAccessIterator qBinaryFindHelper(RandomAccessIterator begin, RandomAccessIterator end, const T &value, LessThan lessThan);
#endif // QT_DEPRECATED_SINCE(5, 2)
}
#if QT_DEPRECATED_SINCE(5, 2)
template <typename InputIterator, typename OutputIterator>
-QT_DEPRECATED inline OutputIterator qCopy(InputIterator begin, InputIterator end, OutputIterator dest)
+QT_DEPRECATED_X("Use std::copy") inline OutputIterator qCopy(InputIterator begin, InputIterator end, OutputIterator dest)
{
while (begin != end)
*dest++ = *begin++;
@@ -84,7 +84,7 @@ QT_DEPRECATED inline OutputIterator qCopy(InputIterator begin, InputIterator end
}
template <typename BiIterator1, typename BiIterator2>
-QT_DEPRECATED inline BiIterator2 qCopyBackward(BiIterator1 begin, BiIterator1 end, BiIterator2 dest)
+QT_DEPRECATED_X("Use std::copy_backward") inline BiIterator2 qCopyBackward(BiIterator1 begin, BiIterator1 end, BiIterator2 dest)
{
while (begin != end)
*--dest = *--end;
@@ -92,7 +92,7 @@ QT_DEPRECATED inline BiIterator2 qCopyBackward(BiIterator1 begin, BiIterator1 en
}
template <typename InputIterator1, typename InputIterator2>
-QT_DEPRECATED inline bool qEqual(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2)
+QT_DEPRECATED_X("Use std::equal") inline bool qEqual(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2)
{
for (; first1 != last1; ++first1, ++first2)
if (!(*first1 == *first2))
@@ -101,20 +101,20 @@ QT_DEPRECATED inline bool qEqual(InputIterator1 first1, InputIterator1 last1, In
}
template <typename ForwardIterator, typename T>
-QT_DEPRECATED inline void qFill(ForwardIterator first, ForwardIterator last, const T &val)
+QT_DEPRECATED_X("Use std::fill") inline void qFill(ForwardIterator first, ForwardIterator last, const T &val)
{
for (; first != last; ++first)
*first = val;
}
template <typename Container, typename T>
-QT_DEPRECATED inline void qFill(Container &container, const T &val)
+QT_DEPRECATED_X("Use std::fill") inline void qFill(Container &container, const T &val)
{
qFill(container.begin(), container.end(), val);
}
template <typename InputIterator, typename T>
-QT_DEPRECATED inline InputIterator qFind(InputIterator first, InputIterator last, const T &val)
+QT_DEPRECATED_X("Use std::find") inline InputIterator qFind(InputIterator first, InputIterator last, const T &val)
{
while (first != last && !(*first == val))
++first;
@@ -122,13 +122,13 @@ QT_DEPRECATED inline InputIterator qFind(InputIterator first, InputIterator last
}
template <typename Container, typename T>
-QT_DEPRECATED inline typename Container::const_iterator qFind(const Container &container, const T &val)
+QT_DEPRECATED_X("Use std::find") inline typename Container::const_iterator qFind(const Container &container, const T &val)
{
return qFind(container.constBegin(), container.constEnd(), val);
}
template <typename InputIterator, typename T, typename Size>
-QT_DEPRECATED inline void qCount(InputIterator first, InputIterator last, const T &value, Size &n)
+QT_DEPRECATED_X("Use std::count") inline void qCount(InputIterator first, InputIterator last, const T &value, Size &n)
{
for (; first != last; ++first)
if (*first == value)
@@ -136,7 +136,7 @@ QT_DEPRECATED inline void qCount(InputIterator first, InputIterator last, const
}
template <typename Container, typename T, typename Size>
-QT_DEPRECATED inline void qCount(const Container &container, const T &value, Size &n)
+QT_DEPRECATED_X("Use std::count") inline void qCount(const Container &container, const T &value, Size &n)
{
qCount(container.constBegin(), container.constEnd(), value, n);
}
@@ -153,7 +153,7 @@ LessThan qGreater()
}
#else
template <typename T>
-class QT_DEPRECATED qLess
+class QT_DEPRECATED_X("Use std::less") qLess
{
public:
inline bool operator()(const T &t1, const T &t2) const
@@ -163,7 +163,7 @@ public:
};
template <typename T>
-class QT_DEPRECATED qGreater
+class QT_DEPRECATED_X("Use std::greater") qGreater
{
public:
inline bool operator()(const T &t1, const T &t2) const
@@ -174,21 +174,21 @@ public:
#endif
template <typename RandomAccessIterator>
-QT_DEPRECATED inline void qSort(RandomAccessIterator start, RandomAccessIterator end)
+QT_DEPRECATED_X("Use std::sort") inline void qSort(RandomAccessIterator start, RandomAccessIterator end)
{
if (start != end)
QAlgorithmsPrivate::qSortHelper(start, end, *start);
}
template <typename RandomAccessIterator, typename LessThan>
-QT_DEPRECATED inline void qSort(RandomAccessIterator start, RandomAccessIterator end, LessThan lessThan)
+QT_DEPRECATED_X("Use std::sort") inline void qSort(RandomAccessIterator start, RandomAccessIterator end, LessThan lessThan)
{
if (start != end)
QAlgorithmsPrivate::qSortHelper(start, end, *start, lessThan);
}
template<typename Container>
-QT_DEPRECATED inline void qSort(Container &c)
+QT_DEPRECATED_X("Use std::sort") inline void qSort(Container &c)
{
#ifdef Q_CC_BOR
// Work around Borland 5.5 optimizer bug
@@ -199,21 +199,21 @@ QT_DEPRECATED inline void qSort(Container &c)
}
template <typename RandomAccessIterator>
-QT_DEPRECATED inline void qStableSort(RandomAccessIterator start, RandomAccessIterator end)
+QT_DEPRECATED_X("Use std::stable_sort") inline void qStableSort(RandomAccessIterator start, RandomAccessIterator end)
{
if (start != end)
QAlgorithmsPrivate::qStableSortHelper(start, end, *start);
}
template <typename RandomAccessIterator, typename LessThan>
-QT_DEPRECATED inline void qStableSort(RandomAccessIterator start, RandomAccessIterator end, LessThan lessThan)
+QT_DEPRECATED_X("Use std::stable_sort") inline void qStableSort(RandomAccessIterator start, RandomAccessIterator end, LessThan lessThan)
{
if (start != end)
QAlgorithmsPrivate::qStableSortHelper(start, end, *start, lessThan);
}
template<typename Container>
-QT_DEPRECATED inline void qStableSort(Container &c)
+QT_DEPRECATED_X("Use std::stable_sort") inline void qStableSort(Container &c)
{
#ifdef Q_CC_BOR
// Work around Borland 5.5 optimizer bug
@@ -224,7 +224,7 @@ QT_DEPRECATED inline void qStableSort(Container &c)
}
template <typename RandomAccessIterator, typename T>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE RandomAccessIterator qLowerBound(RandomAccessIterator begin, RandomAccessIterator end, const T &value)
+QT_DEPRECATED_X("Use std::lower_bound") Q_OUTOFLINE_TEMPLATE RandomAccessIterator qLowerBound(RandomAccessIterator begin, RandomAccessIterator end, const T &value)
{
// Implementation is duplicated from QAlgorithmsPrivate to keep existing code
// compiling. We have to allow using *begin and value with different types,
@@ -247,19 +247,19 @@ QT_DEPRECATED Q_OUTOFLINE_TEMPLATE RandomAccessIterator qLowerBound(RandomAccess
}
template <typename RandomAccessIterator, typename T, typename LessThan>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE RandomAccessIterator qLowerBound(RandomAccessIterator begin, RandomAccessIterator end, const T &value, LessThan lessThan)
+QT_DEPRECATED_X("Use std::lower_bound") Q_OUTOFLINE_TEMPLATE RandomAccessIterator qLowerBound(RandomAccessIterator begin, RandomAccessIterator end, const T &value, LessThan lessThan)
{
return QAlgorithmsPrivate::qLowerBoundHelper(begin, end, value, lessThan);
}
template <typename Container, typename T>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE typename Container::const_iterator qLowerBound(const Container &container, const T &value)
+QT_DEPRECATED_X("Use std::lower_bound") Q_OUTOFLINE_TEMPLATE typename Container::const_iterator qLowerBound(const Container &container, const T &value)
{
return QAlgorithmsPrivate::qLowerBoundHelper(container.constBegin(), container.constEnd(), value, qLess<T>());
}
template <typename RandomAccessIterator, typename T>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE RandomAccessIterator qUpperBound(RandomAccessIterator begin, RandomAccessIterator end, const T &value)
+QT_DEPRECATED_X("Use std::upper_bound") Q_OUTOFLINE_TEMPLATE RandomAccessIterator qUpperBound(RandomAccessIterator begin, RandomAccessIterator end, const T &value)
{
// Implementation is duplicated from QAlgorithmsPrivate.
RandomAccessIterator middle;
@@ -280,19 +280,19 @@ QT_DEPRECATED Q_OUTOFLINE_TEMPLATE RandomAccessIterator qUpperBound(RandomAccess
}
template <typename RandomAccessIterator, typename T, typename LessThan>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE RandomAccessIterator qUpperBound(RandomAccessIterator begin, RandomAccessIterator end, const T &value, LessThan lessThan)
+QT_DEPRECATED_X("Use std::upper_bound") Q_OUTOFLINE_TEMPLATE RandomAccessIterator qUpperBound(RandomAccessIterator begin, RandomAccessIterator end, const T &value, LessThan lessThan)
{
return QAlgorithmsPrivate::qUpperBoundHelper(begin, end, value, lessThan);
}
template <typename Container, typename T>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE typename Container::const_iterator qUpperBound(const Container &container, const T &value)
+QT_DEPRECATED_X("Use std::upper_bound") Q_OUTOFLINE_TEMPLATE typename Container::const_iterator qUpperBound(const Container &container, const T &value)
{
return QAlgorithmsPrivate::qUpperBoundHelper(container.constBegin(), container.constEnd(), value, qLess<T>());
}
template <typename RandomAccessIterator, typename T>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE RandomAccessIterator qBinaryFind(RandomAccessIterator begin, RandomAccessIterator end, const T &value)
+QT_DEPRECATED_X("Use std::binary_search") Q_OUTOFLINE_TEMPLATE RandomAccessIterator qBinaryFind(RandomAccessIterator begin, RandomAccessIterator end, const T &value)
{
// Implementation is duplicated from QAlgorithmsPrivate.
RandomAccessIterator it = qLowerBound(begin, end, value);
@@ -304,13 +304,13 @@ QT_DEPRECATED Q_OUTOFLINE_TEMPLATE RandomAccessIterator qBinaryFind(RandomAccess
}
template <typename RandomAccessIterator, typename T, typename LessThan>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE RandomAccessIterator qBinaryFind(RandomAccessIterator begin, RandomAccessIterator end, const T &value, LessThan lessThan)
+QT_DEPRECATED_X("Use std::binary_search") Q_OUTOFLINE_TEMPLATE RandomAccessIterator qBinaryFind(RandomAccessIterator begin, RandomAccessIterator end, const T &value, LessThan lessThan)
{
return QAlgorithmsPrivate::qBinaryFindHelper(begin, end, value, lessThan);
}
template <typename Container, typename T>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE typename Container::const_iterator qBinaryFind(const Container &container, const T &value)
+QT_DEPRECATED_X("Use std::binary_search") Q_OUTOFLINE_TEMPLATE typename Container::const_iterator qBinaryFind(const Container &container, const T &value)
{
return QAlgorithmsPrivate::qBinaryFindHelper(container.constBegin(), container.constEnd(), value, qLess<T>());
}
@@ -340,7 +340,7 @@ namespace QAlgorithmsPrivate {
#if QT_DEPRECATED_SINCE(5, 2)
template <typename RandomAccessIterator, typename T, typename LessThan>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE void qSortHelper(RandomAccessIterator start, RandomAccessIterator end, const T &t, LessThan lessThan)
+QT_DEPRECATED_X("Use std::sort") Q_OUTOFLINE_TEMPLATE void qSortHelper(RandomAccessIterator start, RandomAccessIterator end, const T &t, LessThan lessThan)
{
top:
int span = int(end - start);
@@ -393,13 +393,13 @@ top:
}
template <typename RandomAccessIterator, typename T>
-QT_DEPRECATED inline void qSortHelper(RandomAccessIterator begin, RandomAccessIterator end, const T &dummy)
+QT_DEPRECATED_X("Use std::sort") inline void qSortHelper(RandomAccessIterator begin, RandomAccessIterator end, const T &dummy)
{
qSortHelper(begin, end, dummy, qLess<T>());
}
template <typename RandomAccessIterator>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE void qReverse(RandomAccessIterator begin, RandomAccessIterator end)
+QT_DEPRECATED_X("Use std::reverse") Q_OUTOFLINE_TEMPLATE void qReverse(RandomAccessIterator begin, RandomAccessIterator end)
{
--end;
while (begin < end)
@@ -407,7 +407,7 @@ QT_DEPRECATED Q_OUTOFLINE_TEMPLATE void qReverse(RandomAccessIterator begin, Ran
}
template <typename RandomAccessIterator>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE void qRotate(RandomAccessIterator begin, RandomAccessIterator middle, RandomAccessIterator end)
+QT_DEPRECATED_X("Use std::rotate") Q_OUTOFLINE_TEMPLATE void qRotate(RandomAccessIterator begin, RandomAccessIterator middle, RandomAccessIterator end)
{
qReverse(begin, middle);
qReverse(middle, end);
@@ -415,7 +415,7 @@ QT_DEPRECATED Q_OUTOFLINE_TEMPLATE void qRotate(RandomAccessIterator begin, Rand
}
template <typename RandomAccessIterator, typename T, typename LessThan>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE void qMerge(RandomAccessIterator begin, RandomAccessIterator pivot, RandomAccessIterator end, T &t, LessThan lessThan)
+QT_DEPRECATED_X("Use std::merge") Q_OUTOFLINE_TEMPLATE void qMerge(RandomAccessIterator begin, RandomAccessIterator pivot, RandomAccessIterator end, T &t, LessThan lessThan)
{
const int len1 = pivot - begin;
const int len2 = end - pivot;
@@ -450,7 +450,7 @@ QT_DEPRECATED Q_OUTOFLINE_TEMPLATE void qMerge(RandomAccessIterator begin, Rando
}
template <typename RandomAccessIterator, typename T, typename LessThan>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE void qStableSortHelper(RandomAccessIterator begin, RandomAccessIterator end, const T &t, LessThan lessThan)
+QT_DEPRECATED_X("Use std::stable_sort") Q_OUTOFLINE_TEMPLATE void qStableSortHelper(RandomAccessIterator begin, RandomAccessIterator end, const T &t, LessThan lessThan)
{
const int span = end - begin;
if (span < 2)
@@ -463,13 +463,13 @@ QT_DEPRECATED Q_OUTOFLINE_TEMPLATE void qStableSortHelper(RandomAccessIterator b
}
template <typename RandomAccessIterator, typename T>
-QT_DEPRECATED inline void qStableSortHelper(RandomAccessIterator begin, RandomAccessIterator end, const T &dummy)
+QT_DEPRECATED_X("Use std::stable_sort") inline void qStableSortHelper(RandomAccessIterator begin, RandomAccessIterator end, const T &dummy)
{
qStableSortHelper(begin, end, dummy, qLess<T>());
}
template <typename RandomAccessIterator, typename T, typename LessThan>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE RandomAccessIterator qLowerBoundHelper(RandomAccessIterator begin, RandomAccessIterator end, const T &value, LessThan lessThan)
+QT_DEPRECATED_X("Use std::lower_bound") Q_OUTOFLINE_TEMPLATE RandomAccessIterator qLowerBoundHelper(RandomAccessIterator begin, RandomAccessIterator end, const T &value, LessThan lessThan)
{
RandomAccessIterator middle;
int n = int(end - begin);
@@ -490,7 +490,7 @@ QT_DEPRECATED Q_OUTOFLINE_TEMPLATE RandomAccessIterator qLowerBoundHelper(Random
template <typename RandomAccessIterator, typename T, typename LessThan>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE RandomAccessIterator qUpperBoundHelper(RandomAccessIterator begin, RandomAccessIterator end, const T &value, LessThan lessThan)
+QT_DEPRECATED_X("Use std::upper_bound") Q_OUTOFLINE_TEMPLATE RandomAccessIterator qUpperBoundHelper(RandomAccessIterator begin, RandomAccessIterator end, const T &value, LessThan lessThan)
{
RandomAccessIterator middle;
int n = end - begin;
@@ -510,7 +510,7 @@ QT_DEPRECATED Q_OUTOFLINE_TEMPLATE RandomAccessIterator qUpperBoundHelper(Random
}
template <typename RandomAccessIterator, typename T, typename LessThan>
-QT_DEPRECATED Q_OUTOFLINE_TEMPLATE RandomAccessIterator qBinaryFindHelper(RandomAccessIterator begin, RandomAccessIterator end, const T &value, LessThan lessThan)
+QT_DEPRECATED_X("Use std::binary_search") Q_OUTOFLINE_TEMPLATE RandomAccessIterator qBinaryFindHelper(RandomAccessIterator begin, RandomAccessIterator end, const T &value, LessThan lessThan)
{
RandomAccessIterator it = qLowerBoundHelper(begin, end, value, lessThan);
diff --git a/src/corelib/tools/qcollator_win.cpp b/src/corelib/tools/qcollator_win.cpp
index 8e59000946..9a672a0505 100644
--- a/src/corelib/tools/qcollator_win.cpp
+++ b/src/corelib/tools/qcollator_win.cpp
@@ -125,9 +125,15 @@ int QCollator::compare(const QChar *s1, int len1, const QChar *s2, int len2) con
// comparing strings, the value 2 can be subtracted from a nonzero return value. Then, the
// meaning of <0, ==0, and >0 is consistent with the C runtime.
+#ifndef Q_OS_WINRT
return CompareString(LOCALE_USER_DEFAULT, d->collator,
reinterpret_cast<const wchar_t*>(s1), len1,
reinterpret_cast<const wchar_t*>(s2), len2) - 2;
+#else // !Q_OS_WINRT
+ return CompareStringEx(LOCALE_NAME_USER_DEFAULT, d->collator,
+ reinterpret_cast<LPCWSTR>(s1), len1,
+ reinterpret_cast<LPCWSTR>(s2), len2, NULL, NULL, 0) - 2;
+#endif // Q_OS_WINRT
}
int QCollator::compare(const QString &str1, const QString &str2) const
@@ -142,13 +148,31 @@ int QCollator::compare(const QStringRef &s1, const QStringRef &s2) const
QCollatorSortKey QCollator::sortKey(const QString &string) const
{
+#ifndef Q_OS_WINRT
int size = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | d->collator,
reinterpret_cast<const wchar_t*>(string.constData()), string.size(),
0, 0);
+#elif defined(Q_OS_WINPHONE)
+ int size = 0;
+ Q_UNIMPLEMENTED();
+#else // Q_OS_WINPHONE
+ int size = LCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_SORTKEY | d->collator,
+ reinterpret_cast<LPCWSTR>(string.constData()), string.size(),
+ 0, 0, NULL, NULL, 0);
+#endif // !Q_OS_WINPHONE
QString ret(size, Qt::Uninitialized);
+#ifndef Q_OS_WINRT
int finalSize = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | d->collator,
reinterpret_cast<const wchar_t*>(string.constData()), string.size(),
reinterpret_cast<wchar_t*>(ret.data()), ret.size());
+#elif defined(Q_OS_WINPHONE)
+ int finalSize = 0;
+#else // Q_OS_WINPHONE
+ int finalSize = LCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_SORTKEY | d->collator,
+ reinterpret_cast<LPCWSTR>(string.constData()), string.size(),
+ reinterpret_cast<LPWSTR>(ret.data()), ret.size(),
+ NULL, NULL, 0);
+#endif // !Q_OS_WINPHONE
if (finalSize == 0) {
qWarning() << "there were problems when generating the ::sortKey by LCMapStringW with error:" << GetLastError();
}
diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp
index 42721c018f..a95c7f53f7 100644
--- a/src/corelib/tools/qdatetime.cpp
+++ b/src/corelib/tools/qdatetime.cpp
@@ -61,6 +61,9 @@
# ifdef Q_OS_WINCE
# include "qfunctions_wince.h"
# endif
+# ifdef Q_OS_WINRT
+# include "qfunctions_winrt.h"
+# endif
#endif
#if defined(Q_OS_MAC)
diff --git a/src/corelib/tools/qlocale_win.cpp b/src/corelib/tools/qlocale_win.cpp
index 0730002ca3..bbc208e673 100644
--- a/src/corelib/tools/qlocale_win.cpp
+++ b/src/corelib/tools/qlocale_win.cpp
@@ -55,12 +55,32 @@
# include <time.h>
#endif
+#ifdef Q_OS_WINRT
+#include <wrl.h>
+#include <windows.foundation.h>
+#include <windows.foundation.collections.h>
+#ifndef Q_OS_WINPHONE
+#include <windows.globalization.h>
+#endif
+#endif // Q_OS_WINRT
+
QT_BEGIN_NAMESPACE
+#ifndef Q_OS_WINRT
static QByteArray getWinLocaleName(LCID id = LOCALE_USER_DEFAULT);
static const char *winLangCodeToIsoName(int code);
static QString winIso639LangName(LCID id = LOCALE_USER_DEFAULT);
static QString winIso3116CtryName(LCID id = LOCALE_USER_DEFAULT);
+#else // !Q_OS_WINRT
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::Foundation;
+
+static QByteArray getWinLocaleName(LPWSTR id = LOCALE_NAME_USER_DEFAULT);
+static const char *winLangCodeToIsoName(int code);
+static QString winIso639LangName(LPWSTR id = LOCALE_NAME_USER_DEFAULT);
+static QString winIso3116CtryName(LPWSTR id = LOCALE_NAME_USER_DEFAULT);
+#endif // Q_OS_WINRT
#ifndef QT_NO_SYSTEMLOCALE
@@ -121,14 +141,23 @@ private:
};
// cached values:
+#ifndef Q_OS_WINRT
LCID lcid;
+#else
+ WCHAR lcName[LOCALE_NAME_MAX_LENGTH];
+#endif
SubstitutionType substitutionType;
QChar zero;
+ int getLocaleInfo(LCTYPE type, LPWSTR data, int size);
QString getLocaleInfo(LCTYPE type, int maxlen = 0);
int getLocaleInfo_int(LCTYPE type, int maxlen = 0);
QChar getLocaleInfo_qchar(LCTYPE type);
+ int getCurrencyFormat(DWORD flags, LPCWSTR value, const CURRENCYFMTW *format, LPWSTR data, int size);
+ int getDateFormat(DWORD flags, const SYSTEMTIME * date, LPCWSTR format, LPWSTR data, int size);
+ int getTimeFormat(DWORD flags, const SYSTEMTIME *date, LPCWSTR format, LPWSTR data, int size);
+
SubstitutionType substitution();
QString &substituteDigits(QString &string);
@@ -140,20 +169,60 @@ Q_GLOBAL_STATIC(QSystemLocalePrivate, systemLocalePrivate)
QSystemLocalePrivate::QSystemLocalePrivate()
: substitutionType(SUnknown)
{
+#ifndef Q_OS_WINRT
lcid = GetUserDefaultLCID();
+#else
+ GetUserDefaultLocaleName(lcName, LOCALE_NAME_MAX_LENGTH);
+#endif
+}
+
+inline int QSystemLocalePrivate::getCurrencyFormat(DWORD flags, LPCWSTR value, const CURRENCYFMTW *format, LPWSTR data, int size)
+{
+#ifndef Q_OS_WINRT
+ return GetCurrencyFormat(lcid, flags, value, format, data, size);
+#else
+ return GetCurrencyFormatEx(lcName, flags, value, format, data, size);
+#endif
+}
+
+inline int QSystemLocalePrivate::getDateFormat(DWORD flags, const SYSTEMTIME * date, LPCWSTR format, LPWSTR data, int size)
+{
+#ifndef Q_OS_WINRT
+ return GetDateFormat(lcid, flags, date, format, data, size);
+#else
+ return GetDateFormatEx(lcName, flags, date, format, data, size, NULL);
+#endif
+}
+
+inline int QSystemLocalePrivate::getTimeFormat(DWORD flags, const SYSTEMTIME *date, LPCWSTR format, LPWSTR data, int size)
+{
+#ifndef Q_OS_WINRT
+ return GetTimeFormat(lcid, flags, date, format, data, size);
+#else
+ return GetTimeFormatEx(lcName, flags, date, format, data, size);
+#endif
+}
+
+inline int QSystemLocalePrivate::getLocaleInfo(LCTYPE type, LPWSTR data, int size)
+{
+#ifndef Q_OS_WINRT
+ return GetLocaleInfo(lcid, type, data, size);
+#else
+ return GetLocaleInfoEx(lcName, type, data, size);
+#endif
}
QString QSystemLocalePrivate::getLocaleInfo(LCTYPE type, int maxlen)
{
QVarLengthArray<wchar_t, 64> buf(maxlen ? maxlen : 64);
- if (!GetLocaleInfo(lcid, type, buf.data(), buf.size()))
+ if (!getLocaleInfo(type, buf.data(), buf.size()))
return QString();
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
- int cnt = GetLocaleInfo(lcid, type, 0, 0);
+ int cnt = getLocaleInfo(type, 0, 0);
if (cnt == 0)
return QString();
buf.resize(cnt);
- if (!GetLocaleInfo(lcid, type, buf.data(), buf.size()))
+ if (!getLocaleInfo(type, buf.data(), buf.size()))
return QString();
}
return QString::fromWCharArray(buf.data());
@@ -177,7 +246,7 @@ QSystemLocalePrivate::SubstitutionType QSystemLocalePrivate::substitution()
{
if (substitutionType == SUnknown) {
wchar_t buf[8];
- if (!GetLocaleInfo(lcid, LOCALE_IDIGITSUBSTITUTION, buf, 8)) {
+ if (!getLocaleInfo(LOCALE_IDIGITSUBSTITUTION, buf, 8)) {
substitutionType = QSystemLocalePrivate::SNever;
return substitutionType;
}
@@ -189,7 +258,7 @@ QSystemLocalePrivate::SubstitutionType QSystemLocalePrivate::substitution()
substitutionType = QSystemLocalePrivate::SAlways;
else {
wchar_t digits[11];
- if (!GetLocaleInfo(lcid, LOCALE_SNATIVEDIGITS, digits, 11)) {
+ if (!getLocaleInfo(LOCALE_SNATIVEDIGITS, digits, 11)) {
substitutionType = QSystemLocalePrivate::SNever;
return substitutionType;
}
@@ -332,7 +401,7 @@ QVariant QSystemLocalePrivate::toString(const QDate &date, QLocale::FormatType t
DWORD flags = (type == QLocale::LongFormat ? DATE_LONGDATE : DATE_SHORTDATE);
wchar_t buf[255];
- if (GetDateFormat(lcid, flags, &st, NULL, buf, 255)) {
+ if (getDateFormat(flags, &st, NULL, buf, 255)) {
QString format = QString::fromWCharArray(buf);
if (substitution() == SAlways)
substituteDigits(format);
@@ -353,7 +422,7 @@ QVariant QSystemLocalePrivate::toString(const QTime &time, QLocale::FormatType)
DWORD flags = 0;
wchar_t buf[255];
- if (GetTimeFormat(lcid, flags, &st, NULL, buf, 255)) {
+ if (getTimeFormat(flags, &st, NULL, buf, 255)) {
QString format = QString::fromWCharArray(buf);
if (substitution() == SAlways)
substituteDigits(format);
@@ -371,7 +440,7 @@ QVariant QSystemLocalePrivate::measurementSystem()
{
wchar_t output[2];
- if (GetLocaleInfo(lcid, LOCALE_IMEASURE, output, 2)) {
+ if (getLocaleInfo(LOCALE_IMEASURE, output, 2)) {
QString iMeasure = QString::fromWCharArray(output);
if (iMeasure == QLatin1String("1")) {
return QLocale::ImperialSystem;
@@ -385,7 +454,7 @@ QVariant QSystemLocalePrivate::amText()
{
wchar_t output[15]; // maximum length including terminating zero character for Win2003+
- if (GetLocaleInfo(lcid, LOCALE_S1159, output, 15)) {
+ if (getLocaleInfo(LOCALE_S1159, output, 15)) {
return QString::fromWCharArray(output);
}
@@ -396,7 +465,7 @@ QVariant QSystemLocalePrivate::pmText()
{
wchar_t output[15]; // maximum length including terminating zero character for Win2003+
- if (GetLocaleInfo(lcid, LOCALE_S2359, output, 15)) {
+ if (getLocaleInfo(LOCALE_S2359, output, 15)) {
return QString::fromWCharArray(output);
}
@@ -407,7 +476,7 @@ QVariant QSystemLocalePrivate::firstDayOfWeek()
{
wchar_t output[4]; // maximum length including terminating zero character for Win2003+
- if (GetLocaleInfo(lcid, LOCALE_IFIRSTDAYOFWEEK, output, 4))
+ if (getLocaleInfo(LOCALE_IFIRSTDAYOFWEEK, output, 4))
return QString::fromWCharArray(output).toUInt()+1;
return 1;
@@ -418,20 +487,20 @@ QVariant QSystemLocalePrivate::currencySymbol(QLocale::CurrencySymbolFormat form
wchar_t buf[13];
switch (format) {
case QLocale::CurrencySymbol:
- if (GetLocaleInfo(lcid, LOCALE_SCURRENCY, buf, 13))
+ if (getLocaleInfo(LOCALE_SCURRENCY, buf, 13))
return QString::fromWCharArray(buf);
break;
case QLocale::CurrencyIsoCode:
- if (GetLocaleInfo(lcid, LOCALE_SINTLSYMBOL, buf, 9))
+ if (getLocaleInfo(LOCALE_SINTLSYMBOL, buf, 9))
return QString::fromWCharArray(buf);
break;
case QLocale::CurrencyDisplayName: {
QVarLengthArray<wchar_t, 64> buf(64);
- if (!GetLocaleInfo(lcid, LOCALE_SNATIVECURRNAME, buf.data(), buf.size())) {
+ if (!getLocaleInfo(LOCALE_SNATIVECURRNAME, buf.data(), buf.size())) {
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
break;
buf.resize(255); // should be large enough, right?
- if (!GetLocaleInfo(lcid, LOCALE_SNATIVECURRNAME, buf.data(), buf.size()))
+ if (!getLocaleInfo(LOCALE_SNATIVECURRNAME, buf.data(), buf.size()))
break;
}
return QString::fromWCharArray(buf.data());
@@ -478,14 +547,14 @@ QVariant QSystemLocalePrivate::toCurrencyString(const QSystemLocale::CurrencyToS
CURRENCYFMT format;
CURRENCYFMT *pformat = NULL;
if (!arg.symbol.isEmpty()) {
- format.NumDigits = getLocaleInfo_int(lcid, LOCALE_ICURRDIGITS);
- format.LeadingZero = getLocaleInfo_int(lcid, LOCALE_ILZERO);
- decimalSep = getLocaleInfo(lcid, LOCALE_SMONDECIMALSEP);
+ format.NumDigits = getLocaleInfo_int(LOCALE_ICURRDIGITS);
+ format.LeadingZero = getLocaleInfo_int(LOCALE_ILZERO);
+ decimalSep = getLocaleInfo(LOCALE_SMONDECIMALSEP);
format.lpDecimalSep = (wchar_t *)decimalSep.utf16();
- thousandSep = getLocaleInfo(lcid, LOCALE_SMONTHOUSANDSEP);
+ thousandSep = getLocaleInfo(LOCALE_SMONTHOUSANDSEP);
format.lpThousandSep = (wchar_t *)thousandSep.utf16();
- format.NegativeOrder = getLocaleInfo_int(lcid, LOCALE_INEGCURR);
- format.PositiveOrder = getLocaleInfo_int(lcid, LOCALE_ICURRENCY);
+ format.NegativeOrder = getLocaleInfo_int(LOCALE_INEGCURR);
+ format.PositiveOrder = getLocaleInfo_int(LOCALE_ICURRENCY);
format.lpCurrencySymbol = (wchar_t *)arg.symbol.utf16();
// grouping is complicated and ugly:
@@ -494,7 +563,7 @@ QVariant QSystemLocalePrivate::toCurrencyString(const QSystemLocale::CurrencyToS
// int(30) == "123456,789.00" == string("3;0;0")
// int(32) == "12,34,56,789.00" == string("3;2;0")
// int(320)== "1234,56,789.00" == string("3;2")
- QString groupingStr = getLocaleInfo(lcid, LOCALE_SMONGROUPING);
+ QString groupingStr = getLocaleInfo(LOCALE_SMONGROUPING);
format.Grouping = groupingStr.remove(QLatin1Char(';')).toInt();
if (format.Grouping % 10 == 0) // magic
format.Grouping /= 10;
@@ -503,13 +572,13 @@ QVariant QSystemLocalePrivate::toCurrencyString(const QSystemLocale::CurrencyToS
pformat = &format;
}
- int ret = ::GetCurrencyFormat(lcid, 0, reinterpret_cast<const wchar_t *>(value.utf16()),
+ int ret = getCurrencyFormat(0, reinterpret_cast<const wchar_t *>(value.utf16()),
pformat, out.data(), out.size());
if (ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
- ret = ::GetCurrencyFormat(lcid, 0, reinterpret_cast<const wchar_t *>(value.utf16()),
+ ret = getCurrencyFormat(0, reinterpret_cast<const wchar_t *>(value.utf16()),
pformat, out.data(), 0);
out.resize(ret);
- ::GetCurrencyFormat(lcid, 0, reinterpret_cast<const wchar_t *>(value.utf16()),
+ getCurrencyFormat(0, reinterpret_cast<const wchar_t *>(value.utf16()),
pformat, out.data(), out.size());
}
@@ -528,11 +597,13 @@ QVariant QSystemLocalePrivate::uiLanguages()
PWSTR pwszLanguagesBuffer,
PULONG pcchLanguagesBuffer);
static GetUserPreferredUILanguagesFunc GetUserPreferredUILanguages_ptr = 0;
+#ifndef Q_OS_WINRT
if (!GetUserPreferredUILanguages_ptr) {
QSystemLibrary lib(QLatin1String("kernel32"));
if (lib.load())
GetUserPreferredUILanguages_ptr = (GetUserPreferredUILanguagesFunc)lib.resolve("GetUserPreferredUILanguages");
}
+#endif // !Q_OS_WINRT
if (GetUserPreferredUILanguages_ptr) {
unsigned long cnt = 0;
QVarLengthArray<wchar_t, 64> buf(64);
@@ -560,8 +631,39 @@ QVariant QSystemLocalePrivate::uiLanguages()
}
}
+#ifndef Q_OS_WINRT
// old Windows before Vista
return QStringList(QString::fromLatin1(winLangCodeToIsoName(GetUserDefaultUILanguage())));
+#else // !Q_OS_WINRT
+ QStringList result;
+#ifndef Q_OS_WINPHONE
+ ComPtr<ABI::Windows::Globalization::IApplicationLanguagesStatics> appLanguagesStatics;
+ if (FAILED(GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Globalization_ApplicationLanguages).Get(), &appLanguagesStatics))) {
+ qWarning("Could not obtain ApplicationLanguagesStatic");
+ return QStringList();
+ }
+
+ ComPtr<ABI::Windows::Foundation::Collections::IVectorView<HSTRING> > languageList;
+ appLanguagesStatics->get_ManifestLanguages(&languageList);
+
+ if (!languageList)
+ return QStringList();
+
+ unsigned int size;
+ languageList->get_Size(&size);
+ for (unsigned int i = 0; i < size; ++i) {
+ HSTRING language;
+ languageList->GetAt(i, &language);
+ UINT32 length;
+ PCWSTR rawString = WindowsGetStringRawBuffer(language, &length);
+ result << QString::fromWCharArray(rawString, length);
+ }
+#else // !Q_OS_WINPHONE
+ result << QString::fromWCharArray(lcName);
+#endif // Q_OS_WINPHONE
+
+ return result;
+#endif // Q_OS_WINRT
}
QVariant QSystemLocalePrivate::nativeLanguageName()
@@ -581,7 +683,11 @@ QVariant QSystemLocalePrivate::nativeCountryName()
void QSystemLocalePrivate::update()
{
+#ifndef Q_OS_WINRT
lcid = GetUserDefaultLCID();
+#else
+ GetUserDefaultLocaleName(lcName, LOCALE_NAME_MAX_LENGTH);
+#endif
substitutionType = SUnknown;
zero = QChar();
}
@@ -895,7 +1001,11 @@ static const char *winLangCodeToIsoName(int code)
}
+#ifndef Q_OS_WINRT
static QString winIso639LangName(LCID id)
+#else
+static QString winIso639LangName(LPWSTR id)
+#endif
{
QString result;
@@ -903,7 +1013,11 @@ static QString winIso639LangName(LCID id)
// the language code
QString lang_code;
wchar_t out[256];
- if (GetLocaleInfo(id, LOCALE_ILANGUAGE, out, 255)) // ### shouldn't use them according to msdn
+#ifndef Q_OS_WINRT
+ if (GetLocaleInfo(id, LOCALE_ILANGUAGE, out, 255))
+#else
+ if (GetLocaleInfoEx(id, LOCALE_ILANGUAGE, out, 255))
+#endif
lang_code = QString::fromWCharArray(out);
if (!lang_code.isEmpty()) {
@@ -926,27 +1040,47 @@ static QString winIso639LangName(LCID id)
return result;
// not one of the problematic languages - do the usual lookup
- if (GetLocaleInfo(id, LOCALE_SISO639LANGNAME , out, 255))
+#ifndef Q_OS_WINRT
+ if (GetLocaleInfo(id, LOCALE_SISO639LANGNAME, out, 255))
+#else
+ if (GetLocaleInfoEx(id, LOCALE_SISO639LANGNAME, out, 255))
+#endif
result = QString::fromWCharArray(out);
return result;
}
+#ifndef Q_OS_WINRT
static QString winIso3116CtryName(LCID id)
+#else
+static QString winIso3116CtryName(LPWSTR id)
+#endif
{
QString result;
wchar_t out[256];
+#ifndef Q_OS_WINRT
if (GetLocaleInfo(id, LOCALE_SISO3166CTRYNAME, out, 255))
+#else
+ if (GetLocaleInfoEx(id, LOCALE_SISO3166CTRYNAME, out, 255))
+#endif
result = QString::fromWCharArray(out);
return result;
}
+#ifndef Q_OS_WINRT
static QByteArray getWinLocaleName(LCID id)
+#else
+static QByteArray getWinLocaleName(LPWSTR id)
+#endif
{
QByteArray result;
+#ifndef Q_OS_WINRT
if (id == LOCALE_USER_DEFAULT) {
+#else
+ if (QString::fromWCharArray(id) == QString::fromWCharArray(LOCALE_NAME_USER_DEFAULT)) {
+#endif
static QByteArray langEnvVar = qgetenv("LANG");
result = langEnvVar;
QString lang, script, cntry;
@@ -964,9 +1098,17 @@ static QByteArray getWinLocaleName(LCID id)
#if defined(Q_OS_WINCE)
result = winLangCodeToIsoName(id != LOCALE_USER_DEFAULT ? id : GetUserDefaultLCID());
-#else
+#else // !Q_OS_WINCE
+# ifndef Q_OS_WINRT
if (id == LOCALE_USER_DEFAULT)
id = GetUserDefaultLCID();
+# else // !Q_OS_WINRT
+ WCHAR lcName[LOCALE_NAME_MAX_LENGTH];
+ if (QString::fromWCharArray(id) == QString::fromWCharArray(LOCALE_NAME_USER_DEFAULT)) {
+ GetUserDefaultLocaleName(lcName, LOCALE_NAME_MAX_LENGTH);
+ id = lcName;
+ }
+# endif // Q_OS_WINRT
QString resultuage = winIso639LangName(id);
QString country = winIso3116CtryName(id);
result = resultuage.toLatin1();
@@ -974,14 +1116,20 @@ static QByteArray getWinLocaleName(LCID id)
result += '_';
result += country.toLatin1();
}
-#endif
+#endif // !Q_OS_WINCE
return result;
}
Q_CORE_EXPORT QLocale qt_localeFromLCID(LCID id)
{
+#ifndef Q_OS_WINRT
return QLocale(QString::fromLatin1(getWinLocaleName(id)));
+#else // !Q_OS_WINRT
+ WCHAR name[LOCALE_NAME_MAX_LENGTH];
+ LCIDToLocaleName(id, name, LOCALE_NAME_MAX_LENGTH, 0);
+ return QLocale(QString::fromLatin1(getWinLocaleName(name)));
+#endif // Q_OS_WINRT
}
QT_END_NAMESPACE
diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp
index 3976f2cb6f..77d002e4d5 100644
--- a/src/corelib/tools/qstring.cpp
+++ b/src/corelib/tools/qstring.cpp
@@ -3158,6 +3158,15 @@ int QString::count(const QStringRef &str, Qt::CaseSensitivity cs) const
\sa indexOf(), count()
*/
+/*! \fn bool QString::contains(QLatin1String str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const
+ \since 5.3
+
+ \overload contains()
+
+ Returns \c true if this string contains an occurrence of the latin-1 string
+ \a str; otherwise returns \c false.
+*/
+
/*! \fn bool QString::contains(QChar ch, Qt::CaseSensitivity cs = Qt::CaseSensitive) const
\overload contains()
@@ -5109,7 +5118,11 @@ int QString::localeAwareCompare_helper(const QChar *data1, int length1,
return ucstrcmp(data1, length1, data2, length2);
#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
+#ifndef Q_OS_WINRT
int res = CompareString(GetUserDefaultLCID(), 0, (wchar_t*)data1, length1, (wchar_t*)data2, length2);
+#else
+ int res = CompareStringEx(LOCALE_NAME_USER_DEFAULT, 0, (LPCWSTR)data1, length1, (LPCWSTR)data2, length2, NULL, NULL, 0);
+#endif
switch (res) {
case CSTR_LESS_THAN:
diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h
index 3140af084b..798ff2f3e0 100644
--- a/src/corelib/tools/qstring.h
+++ b/src/corelib/tools/qstring.h
@@ -332,6 +332,7 @@ public:
inline bool contains(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
inline bool contains(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
+ inline bool contains(QLatin1String s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
inline bool contains(const QStringRef &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
int count(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
int count(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
@@ -985,6 +986,8 @@ inline bool QString::contains(const QString &s, Qt::CaseSensitivity cs) const
{ return indexOf(s, 0, cs) != -1; }
inline bool QString::contains(const QStringRef &s, Qt::CaseSensitivity cs) const
{ return indexOf(s, 0, cs) != -1; }
+inline bool QString::contains(QLatin1String s, Qt::CaseSensitivity cs) const
+{ return indexOf(s, 0, cs) != -1; }
inline bool QString::contains(QChar c, Qt::CaseSensitivity cs) const
{ return indexOf(c, 0, cs) != -1; }
diff --git a/src/corelib/tools/qtimezone.cpp b/src/corelib/tools/qtimezone.cpp
index 5fec06b30a..24c20b46c7 100644
--- a/src/corelib/tools/qtimezone.cpp
+++ b/src/corelib/tools/qtimezone.cpp
@@ -65,7 +65,8 @@ static QTimeZonePrivate *newBackendTimeZone()
return new QMacTimeZonePrivate();
#elif defined Q_OS_UNIX
return new QTzTimeZonePrivate();
-#elif defined Q_OS_WIN
+ // Registry based timezone backend not available on WinRT
+#elif defined Q_OS_WIN && !defined Q_OS_WINRT
return new QWinTimeZonePrivate();
#elif defined QT_USE_ICU
return new QIcuTimeZonePrivate();
@@ -89,7 +90,8 @@ static QTimeZonePrivate *newBackendTimeZone(const QByteArray &olsenId)
return new QMacTimeZonePrivate(olsenId);
#elif defined Q_OS_UNIX
return new QTzTimeZonePrivate(olsenId);
-#elif defined Q_OS_WIN
+ // Registry based timezone backend not available on WinRT
+#elif defined Q_OS_WIN && !defined Q_OS_WINRT
return new QWinTimeZonePrivate(olsenId);
#elif defined QT_USE_ICU
return new QIcuTimeZonePrivate(olsenId);
diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri
index e4a7b02aee..5de2364fe2 100644
--- a/src/corelib/tools/tools.pri
+++ b/src/corelib/tools/tools.pri
@@ -125,8 +125,11 @@ else:blackberry {
HEADERS += tools/qlocale_blackberry.h
}
else:unix:SOURCES += tools/qelapsedtimer_unix.cpp tools/qlocale_unix.cpp tools/qtimezoneprivate_tz.cpp
-else:win32:SOURCES += tools/qelapsedtimer_win.cpp tools/qlocale_win.cpp tools/qtimezoneprivate_win.cpp
-else:integrity:SOURCES += tools/qelapsedtimer_unix.cpp tools/qlocale_unix.cpp
+else:win32 {
+ SOURCES += tools/qelapsedtimer_win.cpp tools/qlocale_win.cpp
+ !winrt: SOURCES += tools/qtimezoneprivate_win.cpp
+ winphone: LIBS_PRIVATE += -lWindowsPhoneGlobalizationUtil
+} else:integrity:SOURCES += tools/qelapsedtimer_unix.cpp tools/qlocale_unix.cpp
else:SOURCES += tools/qelapsedtimer_generic.cpp
contains(QT_CONFIG, zlib) {
diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp
index 28666085a7..ae63b8bc32 100644
--- a/src/gui/kernel/qevent.cpp
+++ b/src/gui/kernel/qevent.cpp
@@ -274,6 +274,24 @@ QMouseEvent::~QMouseEvent()
{
}
+/*!
+ \since 5.3
+
+ Returns information about the mouse event source.
+
+ The mouse event source can be used to distinguish between genuine
+ and artificial mouse events. The latter are events that are
+ synthesized from touch events by the operating system or Qt itself.
+
+ \note Many platforms provide no such information. On such platforms
+ \l Qt::MouseEventNotSynthesized is returned always.
+
+ \sa Qt::MouseEventSource
+ */
+Qt::MouseEventSource QMouseEvent::source() const
+{
+ return QGuiApplicationPrivate::mouseEventSource(this);
+}
/*!
\fn QPointF QMouseEvent::localPos() const
diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h
index d22e423248..b6b1e0c76b 100644
--- a/src/gui/kernel/qevent.h
+++ b/src/gui/kernel/qevent.h
@@ -135,6 +135,8 @@ public:
QT_DEPRECATED inline QPointF posF() const { return l; }
#endif
+ Qt::MouseEventSource source() const;
+
protected:
QPointF l, w, s;
Qt::MouseButton b;
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp
index bc4a135e03..9f95f2c234 100644
--- a/src/gui/kernel/qguiapplication.cpp
+++ b/src/gui/kernel/qguiapplication.cpp
@@ -59,6 +59,7 @@
#include <QtCore/qmutex.h>
#include <QtCore/private/qthread_p.h>
#include <QtCore/qdir.h>
+#include <QtCore/qlibraryinfo.h>
#include <QtDebug>
#ifndef QT_NO_ACCESSIBILITY
#include "qaccessible.h"
@@ -892,6 +893,9 @@ static void init_platform(const QString &pluginArgument, const QString &platform
// Split into platform name and arguments
QStringList arguments = pluginArgument.split(QLatin1Char(':'));
const QString name = arguments.takeFirst().toLower();
+ QString argumentsKey = name;
+ argumentsKey[0] = argumentsKey.at(0).toUpper();
+ arguments.append(QLibraryInfo::platformPluginArguments(argumentsKey));
// Create the platform integration.
QGuiApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name, arguments, argc, argv, platformPluginPath);
@@ -1569,6 +1573,7 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo
QMouseEvent ev(type, localPoint, localPoint, globalPoint, button, buttons, e->modifiers);
ev.setTimestamp(e->timestamp);
+ setMouseEventSource(&ev, e->source);
#ifndef QT_NO_CURSOR
if (!e->synthetic) {
if (const QScreen *screen = window->screen())
@@ -1623,6 +1628,7 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo
QMouseEvent dblClickEvent(doubleClickType, localPoint, localPoint, globalPoint,
button, buttons, e->modifiers);
dblClickEvent.setTimestamp(e->timestamp);
+ setMouseEventSource(&dblClickEvent, e->source);
QGuiApplication::sendSpontaneousEvent(window, &dblClickEvent);
}
}
@@ -2039,7 +2045,8 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To
synthIt->pos,
synthIt->screenPos,
Qt::NoButton,
- e->modifiers);
+ e->modifiers,
+ Qt::MouseEventSynthesizedByQt);
fake.synthetic = true;
processMouseEvent(&fake);
}
@@ -3139,9 +3146,16 @@ void QGuiApplicationPrivate::_q_updateFocusObject(QObject *object)
emit q->focusObjectChanged(object);
}
+enum {
+ MouseCapsMask = 0xFF,
+ MouseSourceMaskDst = 0xFF00,
+ MouseSourceMaskSrc = MouseCapsMask,
+ MouseSourceShift = 8,
+};
+
int QGuiApplicationPrivate::mouseEventCaps(QMouseEvent *event)
{
- return event->caps;
+ return event->caps & MouseCapsMask;
}
QVector2D QGuiApplicationPrivate::mouseEventVelocity(QMouseEvent *event)
@@ -3151,16 +3165,26 @@ QVector2D QGuiApplicationPrivate::mouseEventVelocity(QMouseEvent *event)
void QGuiApplicationPrivate::setMouseEventCapsAndVelocity(QMouseEvent *event, int caps, const QVector2D &velocity)
{
- event->caps = caps;
+ Q_ASSERT(caps <= MouseCapsMask);
+ event->caps &= ~MouseCapsMask;
+ event->caps |= caps & MouseCapsMask;
event->velocity = velocity;
}
-void QGuiApplicationPrivate::setMouseEventCapsAndVelocity(QMouseEvent *event, QMouseEvent *other)
+Qt::MouseEventSource QGuiApplicationPrivate::mouseEventSource(const QMouseEvent *event)
{
- event->caps = other->caps;
- event->velocity = other->velocity;
+ return Qt::MouseEventSource((event->caps & MouseSourceMaskDst) >> MouseSourceShift);
}
+void QGuiApplicationPrivate::setMouseEventSource(QMouseEvent *event, Qt::MouseEventSource source)
+{
+ // Mouse event synthesization status is encoded in the caps field because
+ // QTouchDevice::CapabilityFlag uses only 6 bits from it.
+ int value = source;
+ Q_ASSERT(value <= MouseSourceMaskSrc);
+ event->caps &= ~MouseSourceMaskDst;
+ event->caps |= (value & MouseSourceMaskSrc) << MouseSourceShift;
+}
#include "moc_qguiapplication.cpp"
diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h
index 65c6d814e6..b158138bb7 100644
--- a/src/gui/kernel/qguiapplication_p.h
+++ b/src/gui/kernel/qguiapplication_p.h
@@ -265,7 +265,9 @@ public:
static int mouseEventCaps(QMouseEvent *event);
static QVector2D mouseEventVelocity(QMouseEvent *event);
static void setMouseEventCapsAndVelocity(QMouseEvent *event, int caps, const QVector2D &velocity);
- static void setMouseEventCapsAndVelocity(QMouseEvent *event, QMouseEvent *other);
+
+ static Qt::MouseEventSource mouseEventSource(const QMouseEvent *event);
+ static void setMouseEventSource(QMouseEvent *event, Qt::MouseEventSource source);
const QDrawHelperGammaTables *gammaTables();
diff --git a/src/gui/kernel/qguivariant.cpp b/src/gui/kernel/qguivariant.cpp
index b4e936f818..1739e8c6fd 100644
--- a/src/gui/kernel/qguivariant.cpp
+++ b/src/gui/kernel/qguivariant.cpp
@@ -165,7 +165,7 @@ public:
#ifndef QT_NO_ICON
bool delegate(const QIcon *)
{
- return false;
+ return v_cast<QIcon>(Base::m_a)->cacheKey() == v_cast<QIcon>(Base::m_b)->cacheKey();
}
#endif
bool delegate(const void *p) { return Base::delegate(p); }
diff --git a/src/gui/kernel/qstylehints.cpp b/src/gui/kernel/qstylehints.cpp
index 68eb724454..8d751d8615 100644
--- a/src/gui/kernel/qstylehints.cpp
+++ b/src/gui/kernel/qstylehints.cpp
@@ -62,6 +62,25 @@ static inline QVariant themeableHint(QPlatformTheme::ThemeHint th,
return QGuiApplicationPrivate::platformIntegration()->styleHint(ih);
}
+class QStyleHintsPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QStyleHints)
+public:
+ inline QStyleHintsPrivate()
+ : m_mouseDoubleClickInterval(-1)
+ , m_startDragDistance(-1)
+ , m_startDragTime(-1)
+ , m_keyboardInputInterval(-1)
+ , m_cursorFlashTime(-1)
+ {}
+
+ int m_mouseDoubleClickInterval;
+ int m_startDragDistance;
+ int m_startDragTime;
+ int m_keyboardInputInterval;
+ int m_cursorFlashTime;
+};
+
/*!
\class QStyleHints
\since 5.0
@@ -80,8 +99,20 @@ static inline QVariant themeableHint(QPlatformTheme::ThemeHint th,
\sa QGuiApplication::styleHints(), QPlatformTheme
*/
QStyleHints::QStyleHints()
- : QObject()
+ : QObject(*new QStyleHintsPrivate(), 0)
+{
+}
+
+/*!
+ Sets the \a mouseDoubleClickInterval.
+ \internal
+ \sa mouseDoubleClickInterval()
+ \since 5.3
+*/
+void QStyleHints::setMouseDoubleClickInterval(int mouseDoubleClickInterval)
{
+ Q_D(QStyleHints);
+ d->m_mouseDoubleClickInterval = mouseDoubleClickInterval;
}
/*!
@@ -90,7 +121,22 @@ QStyleHints::QStyleHints()
*/
int QStyleHints::mouseDoubleClickInterval() const
{
- return themeableHint(QPlatformTheme::MouseDoubleClickInterval, QPlatformIntegration::MouseDoubleClickInterval).toInt();
+ Q_D(const QStyleHints);
+ return d->m_mouseDoubleClickInterval >= 0 ?
+ d->m_mouseDoubleClickInterval :
+ themeableHint(QPlatformTheme::MouseDoubleClickInterval, QPlatformIntegration::MouseDoubleClickInterval).toInt();
+}
+
+/*!
+ Sets the \a startDragDistance.
+ \internal
+ \sa startDragDistance()
+ \since 5.3
+*/
+void QStyleHints::setStartDragDistance(int startDragDistance)
+{
+ Q_D(QStyleHints);
+ d->m_startDragDistance = startDragDistance;
}
/*!
@@ -112,7 +158,22 @@ int QStyleHints::mouseDoubleClickInterval() const
*/
int QStyleHints::startDragDistance() const
{
- return themeableHint(QPlatformTheme::StartDragDistance, QPlatformIntegration::StartDragDistance).toInt();
+ Q_D(const QStyleHints);
+ return d->m_startDragDistance >= 0 ?
+ d->m_startDragDistance :
+ themeableHint(QPlatformTheme::StartDragDistance, QPlatformIntegration::StartDragDistance).toInt();
+}
+
+/*!
+ Sets the \a startDragDragTime.
+ \internal
+ \sa startDragTime()
+ \since 5.3
+*/
+void QStyleHints::setStartDragTime(int startDragTime)
+{
+ Q_D(QStyleHints);
+ d->m_startDragTime = startDragTime;
}
/*!
@@ -127,7 +188,10 @@ int QStyleHints::startDragDistance() const
*/
int QStyleHints::startDragTime() const
{
- return themeableHint(QPlatformTheme::StartDragTime, QPlatformIntegration::StartDragTime).toInt();
+ Q_D(const QStyleHints);
+ return d->m_startDragTime >= 0 ?
+ d->m_startDragTime :
+ themeableHint(QPlatformTheme::StartDragTime, QPlatformIntegration::StartDragTime).toInt();
}
/*!
@@ -143,12 +207,27 @@ int QStyleHints::startDragVelocity() const
}
/*!
+ Sets the \a keyboardInputInterval.
+ \internal
+ \sa keyboardInputInterval()
+ \since 5.3
+*/
+void QStyleHints::setKeyboardInputInterval(int keyboardInputInterval)
+{
+ Q_D(QStyleHints);
+ d->m_keyboardInputInterval = keyboardInputInterval;
+}
+
+/*!
Returns the time limit, in milliseconds, that distinguishes a key press
from two consecutive key presses.
*/
int QStyleHints::keyboardInputInterval() const
{
- return themeableHint(QPlatformTheme::KeyboardInputInterval, QPlatformIntegration::KeyboardInputInterval).toInt();
+ Q_D(const QStyleHints);
+ return d->m_keyboardInputInterval >= 0 ?
+ d->m_keyboardInputInterval :
+ themeableHint(QPlatformTheme::KeyboardInputInterval, QPlatformIntegration::KeyboardInputInterval).toInt();
}
/*!
@@ -161,6 +240,18 @@ int QStyleHints::keyboardAutoRepeatRate() const
}
/*!
+ Sets the \a cursorFlashTime.
+ \internal
+ \sa cursorFlashTime()
+ \since 5.3
+*/
+void QStyleHints::setCursorFlashTime(int cursorFlashTime)
+{
+ Q_D(QStyleHints);
+ d->m_cursorFlashTime = cursorFlashTime;
+}
+
+/*!
Returns the text cursor's flash (blink) time in milliseconds.
The flash time is the time used to display, invert and restore the
@@ -169,7 +260,10 @@ int QStyleHints::keyboardAutoRepeatRate() const
*/
int QStyleHints::cursorFlashTime() const
{
- return themeableHint(QPlatformTheme::CursorFlashTime, QPlatformIntegration::CursorFlashTime).toInt();
+ Q_D(const QStyleHints);
+ return d->m_cursorFlashTime >= 0 ?
+ d->m_cursorFlashTime :
+ themeableHint(QPlatformTheme::CursorFlashTime, QPlatformIntegration::CursorFlashTime).toInt();
}
/*!
diff --git a/src/gui/kernel/qstylehints.h b/src/gui/kernel/qstylehints.h
index a0facd5f94..cc8d71108a 100644
--- a/src/gui/kernel/qstylehints.h
+++ b/src/gui/kernel/qstylehints.h
@@ -48,17 +48,24 @@ QT_BEGIN_NAMESPACE
class QPlatformIntegration;
+class QStyleHintsPrivate;
class Q_GUI_EXPORT QStyleHints : public QObject
{
Q_OBJECT
+ Q_DECLARE_PRIVATE(QStyleHints)
public:
+ void setMouseDoubleClickInterval(int mouseDoubleClickInterval);
int mouseDoubleClickInterval() const;
+ void setStartDragDistance(int startDragDistance);
int startDragDistance() const;
+ void setStartDragTime(int startDragTime);
int startDragTime() const;
int startDragVelocity() const;
+ void setKeyboardInputInterval(int keyboardInputInterval);
int keyboardInputInterval() const;
int keyboardAutoRepeatRate() const;
+ void setCursorFlashTime(int cursorFlashTime);
int cursorFlashTime() const;
bool showIsFullScreen() const;
int passwordMaskDelay() const;
diff --git a/src/gui/kernel/qsurfaceformat.cpp b/src/gui/kernel/qsurfaceformat.cpp
index fe5615d394..2b6cb2d949 100644
--- a/src/gui/kernel/qsurfaceformat.cpp
+++ b/src/gui/kernel/qsurfaceformat.cpp
@@ -72,6 +72,7 @@ public:
, profile(QSurfaceFormat::NoProfile)
, major(2)
, minor(0)
+ , swapInterval(1) // default to vsync
{
}
@@ -89,7 +90,8 @@ public:
renderableType(other->renderableType),
profile(other->profile),
major(other->major),
- minor(other->minor)
+ minor(other->minor),
+ swapInterval(other->swapInterval)
{
}
@@ -107,6 +109,7 @@ public:
QSurfaceFormat::OpenGLContextProfile profile;
int major;
int minor;
+ int swapInterval;
};
/*!
@@ -311,9 +314,15 @@ void QSurfaceFormat::setSamples(int numSamples)
}
/*!
- Sets the format option to \a opt.
+ \obsolete
+ \overload
- \sa testOption()
+ Use setOption(QSurfaceFormat::FormatOption, bool) or setOptions() instead.
+
+ Sets the format options to the OR combination of \a opt and the
+ current format options.
+
+ \sa options(), testOption()
*/
void QSurfaceFormat::setOption(QSurfaceFormat::FormatOptions opt)
{
@@ -325,7 +334,13 @@ void QSurfaceFormat::setOption(QSurfaceFormat::FormatOptions opt)
}
/*!
- Returns \c true if format option \a opt is set; otherwise returns \c false.
+ \obsolete
+ \overload
+
+ Use testOption(QSurfaceFormat::FormatOption) instead.
+
+ Returns \c true if any of the options in \a opt is currently set
+ on this object; otherwise returns false.
\sa setOption()
*/
@@ -335,6 +350,63 @@ bool QSurfaceFormat::testOption(QSurfaceFormat::FormatOptions opt) const
}
/*!
+ \since 5.3
+
+ Sets the format options to \a options.
+
+ \sa options(), testOption()
+*/
+void QSurfaceFormat::setOptions(QSurfaceFormat::FormatOptions options)
+{
+ if (int(d->opts) != int(options)) {
+ detach();
+ d->opts = options;
+ }
+}
+
+/*!
+ \since 5.3
+
+ Sets the format option \a option if \a on is true; otherwise, clears the option.
+
+ \sa setOptions(), options(), testOption()
+*/
+void QSurfaceFormat::setOption(QSurfaceFormat::FormatOption option, bool on)
+{
+ if (testOption(option) == on)
+ return;
+ detach();
+ if (on)
+ d->opts |= option;
+ else
+ d->opts &= ~option;
+}
+
+/*!
+ \since 5.3
+
+ Returns true if the format option \a option is set; otherwise returns false.
+
+ \sa options(), testOption()
+*/
+bool QSurfaceFormat::testOption(QSurfaceFormat::FormatOption option) const
+{
+ return d->opts & option;
+}
+
+/*!
+ \since 5.3
+
+ Returns the currently set format options.
+
+ \sa setOption(), setOptions(), testOption()
+*/
+QSurfaceFormat::FormatOptions QSurfaceFormat::options() const
+{
+ return d->opts;
+}
+
+/*!
Set the minimum depth buffer size to \a size.
\sa depthBufferSize()
@@ -607,6 +679,46 @@ void QSurfaceFormat::setVersion(int major, int minor)
}
/*!
+ Sets the preferred swap interval. The swap interval specifies the
+ minimum number of video frames that are displayed before a buffer
+ swap occurs. This can be used to sync the GL drawing into a window
+ to the vertical refresh of the screen.
+
+ Setting an \a interval value of 0 will turn the vertical refresh
+ syncing off, any value higher than 0 will turn the vertical
+ syncing on. Setting \a interval to a higher value, for example 10,
+ results in having 10 vertical retraces between every buffer swap.
+
+ The default interval is 1.
+
+ Changing the swap interval may not be supported by the underlying
+ platform. In this case, the request will be silently ignored.
+
+ \since 5.3
+
+ \sa swapInterval()
+ */
+void QSurfaceFormat::setSwapInterval(int interval)
+{
+ if (d->swapInterval != interval) {
+ detach();
+ d->swapInterval = interval;
+ }
+}
+
+/*!
+ Returns the swap interval.
+
+ \since 5.3
+
+ \sa setSwapInterval()
+*/
+int QSurfaceFormat::swapInterval() const
+{
+ return d->swapInterval;
+}
+
+/*!
Returns \c true if all the options of the two QSurfaceFormat objects
\a a and \a b are equal.
@@ -625,7 +737,8 @@ bool operator==(const QSurfaceFormat& a, const QSurfaceFormat& b)
&& a.d->swapBehavior == b.d->swapBehavior
&& a.d->profile == b.d->profile
&& a.d->major == b.d->major
- && a.d->minor == b.d->minor);
+ && a.d->minor == b.d->minor
+ && a.d->swapInterval == b.d->swapInterval);
}
/*!
@@ -655,6 +768,7 @@ QDebug operator<<(QDebug dbg, const QSurfaceFormat &f)
<< ", stencilBufferSize " << d->stencilSize
<< ", samples " << d->numSamples
<< ", swapBehavior " << d->swapBehavior
+ << ", swapInterval " << d->swapInterval
<< ", profile " << d->profile
<< ')';
diff --git a/src/gui/kernel/qsurfaceformat.h b/src/gui/kernel/qsurfaceformat.h
index 7c3c846df3..453beac5cd 100644
--- a/src/gui/kernel/qsurfaceformat.h
+++ b/src/gui/kernel/qsurfaceformat.h
@@ -127,8 +127,16 @@ public:
bool stereo() const;
void setStereo(bool enable);
- void setOption(QSurfaceFormat::FormatOptions opt);
- bool testOption(QSurfaceFormat::FormatOptions opt) const;
+ QT_DEPRECATED void setOption(QSurfaceFormat::FormatOptions opt);
+ QT_DEPRECATED bool testOption(QSurfaceFormat::FormatOptions opt) const;
+
+ void setOptions(QSurfaceFormat::FormatOptions options);
+ void setOption(FormatOption option, bool on = true);
+ bool testOption(FormatOption option) const;
+ QSurfaceFormat::FormatOptions options() const;
+
+ int swapInterval() const;
+ void setSwapInterval(int interval);
private:
QSurfaceFormatPrivate *d;
diff --git a/src/gui/kernel/qwindowsysteminterface.cpp b/src/gui/kernel/qwindowsysteminterface.cpp
index 8ef275a27c..49ff8bcb0d 100644
--- a/src/gui/kernel/qwindowsysteminterface.cpp
+++ b/src/gui/kernel/qwindowsysteminterface.cpp
@@ -164,31 +164,35 @@ void QWindowSystemInterface::handleCloseEvent(QWindow *tlw, bool *accepted)
\a w == 0 means that the event is in global coords only, \a local will be ignored in this case
*/
-void QWindowSystemInterface::handleMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
+void QWindowSystemInterface::handleMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b,
+ Qt::KeyboardModifiers mods, Qt::MouseEventSource source)
{
unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed();
- handleMouseEvent(w, time, local, global, b, mods);
+ handleMouseEvent(w, time, local, global, b, mods, source);
}
-void QWindowSystemInterface::handleMouseEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
+void QWindowSystemInterface::handleMouseEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, Qt::MouseButtons b,
+ Qt::KeyboardModifiers mods, Qt::MouseEventSource source)
{
QWindowSystemInterfacePrivate::MouseEvent * e =
- new QWindowSystemInterfacePrivate::MouseEvent(w, timestamp, local, global, b, mods);
+ new QWindowSystemInterfacePrivate::MouseEvent(w, timestamp, local, global, b, mods, source);
QWindowSystemInterfacePrivate::handleWindowSystemEvent(e);
}
-void QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
+void QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b,
+ Qt::KeyboardModifiers mods, Qt::MouseEventSource source)
{
const unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed();
- handleFrameStrutMouseEvent(w, time, local, global, b, mods);
+ handleFrameStrutMouseEvent(w, time, local, global, b, mods, source);
}
-void QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
+void QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, Qt::MouseButtons b,
+ Qt::KeyboardModifiers mods, Qt::MouseEventSource source)
{
QWindowSystemInterfacePrivate::MouseEvent * e =
new QWindowSystemInterfacePrivate::MouseEvent(w, timestamp,
QWindowSystemInterfacePrivate::FrameStrutMouse,
- local, global, b, mods);
+ local, global, b, mods, source);
QWindowSystemInterfacePrivate::handleWindowSystemEvent(e);
}
diff --git a/src/gui/kernel/qwindowsysteminterface.h b/src/gui/kernel/qwindowsysteminterface.h
index d1c3c8e249..71feb1bcb7 100644
--- a/src/gui/kernel/qwindowsysteminterface.h
+++ b/src/gui/kernel/qwindowsysteminterface.h
@@ -73,10 +73,18 @@ class QPlatformDropQtResponse;
class Q_GUI_EXPORT QWindowSystemInterface
{
public:
- static void handleMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b, Qt::KeyboardModifiers mods = Qt::NoModifier);
- static void handleMouseEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, Qt::MouseButtons b, Qt::KeyboardModifiers mods = Qt::NoModifier);
- static void handleFrameStrutMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b, Qt::KeyboardModifiers mods = Qt::NoModifier);
- static void handleFrameStrutMouseEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, Qt::MouseButtons b, Qt::KeyboardModifiers mods = Qt::NoModifier);
+ static void handleMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b,
+ Qt::KeyboardModifiers mods = Qt::NoModifier,
+ Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
+ static void handleMouseEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, Qt::MouseButtons b,
+ Qt::KeyboardModifiers mods = Qt::NoModifier,
+ Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
+ static void handleFrameStrutMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b,
+ Qt::KeyboardModifiers mods = Qt::NoModifier,
+ Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
+ static void handleFrameStrutMouseEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, Qt::MouseButtons b,
+ Qt::KeyboardModifiers mods = Qt::NoModifier,
+ Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
static bool tryHandleShortcutEvent(QWindow *w, int k, Qt::KeyboardModifiers mods,
const QString & text = QString(), bool autorep = false, ushort count = 1);
diff --git a/src/gui/kernel/qwindowsysteminterface_p.h b/src/gui/kernel/qwindowsysteminterface_p.h
index 8cafb8da5c..367b0ac11d 100644
--- a/src/gui/kernel/qwindowsysteminterface_p.h
+++ b/src/gui/kernel/qwindowsysteminterface_p.h
@@ -207,14 +207,17 @@ public:
class MouseEvent : public InputEvent {
public:
MouseEvent(QWindow * w, ulong time, const QPointF & local, const QPointF & global,
- Qt::MouseButtons b, Qt::KeyboardModifiers mods)
- : InputEvent(w, time, Mouse, mods), localPos(local), globalPos(global), buttons(b) { }
+ Qt::MouseButtons b, Qt::KeyboardModifiers mods,
+ Qt::MouseEventSource src = Qt::MouseEventNotSynthesized)
+ : InputEvent(w, time, Mouse, mods), localPos(local), globalPos(global), buttons(b), source(src) { }
MouseEvent(QWindow * w, ulong time, EventType t, const QPointF & local, const QPointF & global,
- Qt::MouseButtons b, Qt::KeyboardModifiers mods)
- : InputEvent(w, time, t, mods), localPos(local), globalPos(global), buttons(b) { }
+ Qt::MouseButtons b, Qt::KeyboardModifiers mods,
+ Qt::MouseEventSource src = Qt::MouseEventNotSynthesized)
+ : InputEvent(w, time, t, mods), localPos(local), globalPos(global), buttons(b), source(src) { }
QPointF localPos;
QPointF globalPos;
Qt::MouseButtons buttons;
+ Qt::MouseEventSource source;
};
class WheelEvent : public InputEvent {
diff --git a/src/gui/math3d/qvector2d.h b/src/gui/math3d/qvector2d.h
index 55e606ec35..649d45d477 100644
--- a/src/gui/math3d/qvector2d.h
+++ b/src/gui/math3d/qvector2d.h
@@ -57,10 +57,10 @@ class QVariant;
class Q_GUI_EXPORT QVector2D
{
public:
- QVector2D();
- QVector2D(float xpos, float ypos);
- explicit QVector2D(const QPoint& point);
- explicit QVector2D(const QPointF& point);
+ Q_DECL_CONSTEXPR QVector2D();
+ Q_DECL_CONSTEXPR QVector2D(float xpos, float ypos);
+ Q_DECL_CONSTEXPR explicit QVector2D(const QPoint& point);
+ Q_DECL_CONSTEXPR explicit QVector2D(const QPointF& point);
#ifndef QT_NO_VECTOR3D
explicit QVector2D(const QVector3D& vector);
#endif
@@ -70,8 +70,8 @@ public:
bool isNull() const;
- float x() const;
- float y() const;
+ Q_DECL_CONSTEXPR float x() const;
+ Q_DECL_CONSTEXPR float y() const;
void setX(float x);
void setY(float y);
@@ -80,7 +80,12 @@ public:
float operator[](int i) const;
float length() const;
+#ifdef QT_BUILD_GUI_LIB
float lengthSquared() const;
+#else
+ Q_DECL_CONSTEXPR inline float lengthSquared() const
+ { return xp * xp + yp * yp; }
+#endif
QVector2D normalized() const;
void normalize();
@@ -94,19 +99,24 @@ public:
QVector2D &operator*=(const QVector2D &vector);
QVector2D &operator/=(float divisor);
+#ifdef QT_BUILD_GUI_LIB
static float dotProduct(const QVector2D& v1, const QVector2D& v2);
+#else
+ Q_DECL_CONSTEXPR inline static float dotProduct(const QVector2D& v1, const QVector2D& v2)
+ { return v1.xp * v2.xp + v1.yp * v2.yp; }
+#endif
- friend inline bool operator==(const QVector2D &v1, const QVector2D &v2);
- friend inline bool operator!=(const QVector2D &v1, const QVector2D &v2);
- friend inline const QVector2D operator+(const QVector2D &v1, const QVector2D &v2);
- friend inline const QVector2D operator-(const QVector2D &v1, const QVector2D &v2);
- friend inline const QVector2D operator*(float factor, const QVector2D &vector);
- friend inline const QVector2D operator*(const QVector2D &vector, float factor);
- friend inline const QVector2D operator*(const QVector2D &v1, const QVector2D &v2);
- friend inline const QVector2D operator-(const QVector2D &vector);
- friend inline const QVector2D operator/(const QVector2D &vector, float divisor);
+ Q_DECL_CONSTEXPR friend inline bool operator==(const QVector2D &v1, const QVector2D &v2);
+ Q_DECL_CONSTEXPR friend inline bool operator!=(const QVector2D &v1, const QVector2D &v2);
+ Q_DECL_CONSTEXPR friend inline const QVector2D operator+(const QVector2D &v1, const QVector2D &v2);
+ Q_DECL_CONSTEXPR friend inline const QVector2D operator-(const QVector2D &v1, const QVector2D &v2);
+ Q_DECL_CONSTEXPR friend inline const QVector2D operator*(float factor, const QVector2D &vector);
+ Q_DECL_CONSTEXPR friend inline const QVector2D operator*(const QVector2D &vector, float factor);
+ Q_DECL_CONSTEXPR friend inline const QVector2D operator*(const QVector2D &v1, const QVector2D &v2);
+ Q_DECL_CONSTEXPR friend inline const QVector2D operator-(const QVector2D &vector);
+ Q_DECL_CONSTEXPR friend inline const QVector2D operator/(const QVector2D &vector, float divisor);
- friend inline bool qFuzzyCompare(const QVector2D& v1, const QVector2D& v2);
+ Q_DECL_CONSTEXPR friend inline bool qFuzzyCompare(const QVector2D& v1, const QVector2D& v2);
#ifndef QT_NO_VECTOR3D
QVector3D toVector3D() const;
@@ -115,8 +125,8 @@ public:
QVector4D toVector4D() const;
#endif
- QPoint toPoint() const;
- QPointF toPointF() const;
+ Q_DECL_CONSTEXPR QPoint toPoint() const;
+ Q_DECL_CONSTEXPR QPointF toPointF() const;
operator QVariant() const;
@@ -129,21 +139,21 @@ private:
Q_DECLARE_TYPEINFO(QVector2D, Q_MOVABLE_TYPE);
-inline QVector2D::QVector2D() : xp(0.0f), yp(0.0f) {}
+Q_DECL_CONSTEXPR inline QVector2D::QVector2D() : xp(0.0f), yp(0.0f) {}
-inline QVector2D::QVector2D(float xpos, float ypos) : xp(xpos), yp(ypos) {}
+Q_DECL_CONSTEXPR inline QVector2D::QVector2D(float xpos, float ypos) : xp(xpos), yp(ypos) {}
-inline QVector2D::QVector2D(const QPoint& point) : xp(point.x()), yp(point.y()) {}
+Q_DECL_CONSTEXPR inline QVector2D::QVector2D(const QPoint& point) : xp(point.x()), yp(point.y()) {}
-inline QVector2D::QVector2D(const QPointF& point) : xp(point.x()), yp(point.y()) {}
+Q_DECL_CONSTEXPR inline QVector2D::QVector2D(const QPointF& point) : xp(point.x()), yp(point.y()) {}
inline bool QVector2D::isNull() const
{
return qIsNull(xp) && qIsNull(yp);
}
-inline float QVector2D::x() const { return xp; }
-inline float QVector2D::y() const { return yp; }
+Q_DECL_CONSTEXPR inline float QVector2D::x() const { return xp; }
+Q_DECL_CONSTEXPR inline float QVector2D::y() const { return yp; }
inline void QVector2D::setX(float aX) { xp = aX; }
inline void QVector2D::setY(float aY) { yp = aY; }
@@ -195,62 +205,62 @@ inline QVector2D &QVector2D::operator/=(float divisor)
return *this;
}
-inline bool operator==(const QVector2D &v1, const QVector2D &v2)
+Q_DECL_CONSTEXPR inline bool operator==(const QVector2D &v1, const QVector2D &v2)
{
return v1.xp == v2.xp && v1.yp == v2.yp;
}
-inline bool operator!=(const QVector2D &v1, const QVector2D &v2)
+Q_DECL_CONSTEXPR inline bool operator!=(const QVector2D &v1, const QVector2D &v2)
{
return v1.xp != v2.xp || v1.yp != v2.yp;
}
-inline const QVector2D operator+(const QVector2D &v1, const QVector2D &v2)
+Q_DECL_CONSTEXPR inline const QVector2D operator+(const QVector2D &v1, const QVector2D &v2)
{
return QVector2D(v1.xp + v2.xp, v1.yp + v2.yp);
}
-inline const QVector2D operator-(const QVector2D &v1, const QVector2D &v2)
+Q_DECL_CONSTEXPR inline const QVector2D operator-(const QVector2D &v1, const QVector2D &v2)
{
return QVector2D(v1.xp - v2.xp, v1.yp - v2.yp);
}
-inline const QVector2D operator*(float factor, const QVector2D &vector)
+Q_DECL_CONSTEXPR inline const QVector2D operator*(float factor, const QVector2D &vector)
{
return QVector2D(vector.xp * factor, vector.yp * factor);
}
-inline const QVector2D operator*(const QVector2D &vector, float factor)
+Q_DECL_CONSTEXPR inline const QVector2D operator*(const QVector2D &vector, float factor)
{
return QVector2D(vector.xp * factor, vector.yp * factor);
}
-inline const QVector2D operator*(const QVector2D &v1, const QVector2D &v2)
+Q_DECL_CONSTEXPR inline const QVector2D operator*(const QVector2D &v1, const QVector2D &v2)
{
return QVector2D(v1.xp * v2.xp, v1.yp * v2.yp);
}
-inline const QVector2D operator-(const QVector2D &vector)
+Q_DECL_CONSTEXPR inline const QVector2D operator-(const QVector2D &vector)
{
return QVector2D(-vector.xp, -vector.yp);
}
-inline const QVector2D operator/(const QVector2D &vector, float divisor)
+Q_DECL_CONSTEXPR inline const QVector2D operator/(const QVector2D &vector, float divisor)
{
return QVector2D(vector.xp / divisor, vector.yp / divisor);
}
-inline bool qFuzzyCompare(const QVector2D& v1, const QVector2D& v2)
+Q_DECL_CONSTEXPR inline bool qFuzzyCompare(const QVector2D& v1, const QVector2D& v2)
{
return qFuzzyCompare(v1.xp, v2.xp) && qFuzzyCompare(v1.yp, v2.yp);
}
-inline QPoint QVector2D::toPoint() const
+Q_DECL_CONSTEXPR inline QPoint QVector2D::toPoint() const
{
return QPoint(qRound(xp), qRound(yp));
}
-inline QPointF QVector2D::toPointF() const
+Q_DECL_CONSTEXPR inline QPointF QVector2D::toPointF() const
{
return QPointF(qreal(xp), qreal(yp));
}
diff --git a/src/gui/math3d/qvector3d.h b/src/gui/math3d/qvector3d.h
index c880930935..c5506bf1ac 100644
--- a/src/gui/math3d/qvector3d.h
+++ b/src/gui/math3d/qvector3d.h
@@ -57,10 +57,11 @@ class QVector4D;
class Q_GUI_EXPORT QVector3D
{
public:
- QVector3D();
- QVector3D(float xpos, float ypos, float zpos);
- explicit QVector3D(const QPoint& point);
- explicit QVector3D(const QPointF& point);
+ Q_DECL_CONSTEXPR QVector3D();
+ Q_DECL_CONSTEXPR QVector3D(float xpos, float ypos, float zpos) : xp(xpos), yp(ypos), zp(zpos) {}
+
+ Q_DECL_CONSTEXPR explicit QVector3D(const QPoint& point);
+ Q_DECL_CONSTEXPR explicit QVector3D(const QPointF& point);
#ifndef QT_NO_VECTOR2D
QVector3D(const QVector2D& vector);
QVector3D(const QVector2D& vector, float zpos);
@@ -71,9 +72,9 @@ public:
bool isNull() const;
- float x() const;
- float y() const;
- float z() const;
+ Q_DECL_CONSTEXPR float x() const;
+ Q_DECL_CONSTEXPR float y() const;
+ Q_DECL_CONSTEXPR float z() const;
void setX(float x);
void setY(float y);
@@ -94,8 +95,17 @@ public:
QVector3D &operator*=(const QVector3D& vector);
QVector3D &operator/=(float divisor);
+#ifdef QT_BUILD_GUI_LIB
static float dotProduct(const QVector3D& v1, const QVector3D& v2);
static QVector3D crossProduct(const QVector3D& v1, const QVector3D& v2);
+#else
+ Q_DECL_CONSTEXPR inline static float dotProduct(const QVector3D& v1, const QVector3D& v2)
+ { return v1.xp * v2.xp + v1.yp * v2.yp + v1.zp * v2.zp; }
+ Q_DECL_CONSTEXPR inline static QVector3D crossProduct(const QVector3D& v1, const QVector3D& v2)
+ { return QVector3D(v1.yp * v2.zp - v1.zp * v2.yp,
+ v1.zp * v2.xp - v1.xp * v2.zp,
+ v1.xp * v2.yp - v1.yp * v2.xp); }
+#endif
static QVector3D normal(const QVector3D& v1, const QVector3D& v2);
static QVector3D normal
(const QVector3D& v1, const QVector3D& v2, const QVector3D& v3);
@@ -105,17 +115,17 @@ public:
float distanceToPlane(const QVector3D& plane1, const QVector3D& plane2, const QVector3D& plane3) const;
float distanceToLine(const QVector3D& point, const QVector3D& direction) const;
- friend inline bool operator==(const QVector3D &v1, const QVector3D &v2);
- friend inline bool operator!=(const QVector3D &v1, const QVector3D &v2);
- friend inline const QVector3D operator+(const QVector3D &v1, const QVector3D &v2);
- friend inline const QVector3D operator-(const QVector3D &v1, const QVector3D &v2);
- friend inline const QVector3D operator*(float factor, const QVector3D &vector);
- friend inline const QVector3D operator*(const QVector3D &vector, float factor);
- friend const QVector3D operator*(const QVector3D &v1, const QVector3D& v2);
- friend inline const QVector3D operator-(const QVector3D &vector);
- friend inline const QVector3D operator/(const QVector3D &vector, float divisor);
+ Q_DECL_CONSTEXPR friend inline bool operator==(const QVector3D &v1, const QVector3D &v2);
+ Q_DECL_CONSTEXPR friend inline bool operator!=(const QVector3D &v1, const QVector3D &v2);
+ Q_DECL_CONSTEXPR friend inline const QVector3D operator+(const QVector3D &v1, const QVector3D &v2);
+ Q_DECL_CONSTEXPR friend inline const QVector3D operator-(const QVector3D &v1, const QVector3D &v2);
+ Q_DECL_CONSTEXPR friend inline const QVector3D operator*(float factor, const QVector3D &vector);
+ Q_DECL_CONSTEXPR friend inline const QVector3D operator*(const QVector3D &vector, float factor);
+ Q_DECL_CONSTEXPR friend const QVector3D operator*(const QVector3D &v1, const QVector3D& v2);
+ Q_DECL_CONSTEXPR friend inline const QVector3D operator-(const QVector3D &vector);
+ Q_DECL_CONSTEXPR friend inline const QVector3D operator/(const QVector3D &vector, float divisor);
- friend inline bool qFuzzyCompare(const QVector3D& v1, const QVector3D& v2);
+ Q_DECL_CONSTEXPR friend inline bool qFuzzyCompare(const QVector3D& v1, const QVector3D& v2);
#ifndef QT_NO_VECTOR2D
QVector2D toVector2D() const;
@@ -124,8 +134,8 @@ public:
QVector4D toVector4D() const;
#endif
- QPoint toPoint() const;
- QPointF toPointF() const;
+ Q_DECL_CONSTEXPR QPoint toPoint() const;
+ Q_DECL_CONSTEXPR QPointF toPointF() const;
operator QVariant() const;
@@ -142,22 +152,20 @@ private:
Q_DECLARE_TYPEINFO(QVector3D, Q_MOVABLE_TYPE);
-inline QVector3D::QVector3D() : xp(0.0f), yp(0.0f), zp(0.0f) {}
-
-inline QVector3D::QVector3D(float xpos, float ypos, float zpos) : xp(xpos), yp(ypos), zp(zpos) {}
+Q_DECL_CONSTEXPR inline QVector3D::QVector3D() : xp(0.0f), yp(0.0f), zp(0.0f) {}
-inline QVector3D::QVector3D(const QPoint& point) : xp(point.x()), yp(point.y()), zp(0.0f) {}
+Q_DECL_CONSTEXPR inline QVector3D::QVector3D(const QPoint& point) : xp(point.x()), yp(point.y()), zp(0.0f) {}
-inline QVector3D::QVector3D(const QPointF& point) : xp(point.x()), yp(point.y()), zp(0.0f) {}
+Q_DECL_CONSTEXPR inline QVector3D::QVector3D(const QPointF& point) : xp(point.x()), yp(point.y()), zp(0.0f) {}
inline bool QVector3D::isNull() const
{
return qIsNull(xp) && qIsNull(yp) && qIsNull(zp);
}
-inline float QVector3D::x() const { return xp; }
-inline float QVector3D::y() const { return yp; }
-inline float QVector3D::z() const { return zp; }
+Q_DECL_CONSTEXPR inline float QVector3D::x() const { return xp; }
+Q_DECL_CONSTEXPR inline float QVector3D::y() const { return yp; }
+Q_DECL_CONSTEXPR inline float QVector3D::z() const { return zp; }
inline void QVector3D::setX(float aX) { xp = aX; }
inline void QVector3D::setY(float aY) { yp = aY; }
@@ -215,64 +223,64 @@ inline QVector3D &QVector3D::operator/=(float divisor)
return *this;
}
-inline bool operator==(const QVector3D &v1, const QVector3D &v2)
+Q_DECL_CONSTEXPR inline bool operator==(const QVector3D &v1, const QVector3D &v2)
{
return v1.xp == v2.xp && v1.yp == v2.yp && v1.zp == v2.zp;
}
-inline bool operator!=(const QVector3D &v1, const QVector3D &v2)
+Q_DECL_CONSTEXPR inline bool operator!=(const QVector3D &v1, const QVector3D &v2)
{
return v1.xp != v2.xp || v1.yp != v2.yp || v1.zp != v2.zp;
}
-inline const QVector3D operator+(const QVector3D &v1, const QVector3D &v2)
+Q_DECL_CONSTEXPR inline const QVector3D operator+(const QVector3D &v1, const QVector3D &v2)
{
return QVector3D(v1.xp + v2.xp, v1.yp + v2.yp, v1.zp + v2.zp);
}
-inline const QVector3D operator-(const QVector3D &v1, const QVector3D &v2)
+Q_DECL_CONSTEXPR inline const QVector3D operator-(const QVector3D &v1, const QVector3D &v2)
{
return QVector3D(v1.xp - v2.xp, v1.yp - v2.yp, v1.zp - v2.zp);
}
-inline const QVector3D operator*(float factor, const QVector3D &vector)
+Q_DECL_CONSTEXPR inline const QVector3D operator*(float factor, const QVector3D &vector)
{
return QVector3D(vector.xp * factor, vector.yp * factor, vector.zp * factor);
}
-inline const QVector3D operator*(const QVector3D &vector, float factor)
+Q_DECL_CONSTEXPR inline const QVector3D operator*(const QVector3D &vector, float factor)
{
return QVector3D(vector.xp * factor, vector.yp * factor, vector.zp * factor);
}
-inline const QVector3D operator*(const QVector3D &v1, const QVector3D& v2)
+Q_DECL_CONSTEXPR inline const QVector3D operator*(const QVector3D &v1, const QVector3D& v2)
{
return QVector3D(v1.xp * v2.xp, v1.yp * v2.yp, v1.zp * v2.zp);
}
-inline const QVector3D operator-(const QVector3D &vector)
+Q_DECL_CONSTEXPR inline const QVector3D operator-(const QVector3D &vector)
{
return QVector3D(-vector.xp, -vector.yp, -vector.zp);
}
-inline const QVector3D operator/(const QVector3D &vector, float divisor)
+Q_DECL_CONSTEXPR inline const QVector3D operator/(const QVector3D &vector, float divisor)
{
return QVector3D(vector.xp / divisor, vector.yp / divisor, vector.zp / divisor);
}
-inline bool qFuzzyCompare(const QVector3D& v1, const QVector3D& v2)
+Q_DECL_CONSTEXPR inline bool qFuzzyCompare(const QVector3D& v1, const QVector3D& v2)
{
return qFuzzyCompare(v1.xp, v2.xp) &&
qFuzzyCompare(v1.yp, v2.yp) &&
qFuzzyCompare(v1.zp, v2.zp);
}
-inline QPoint QVector3D::toPoint() const
+Q_DECL_CONSTEXPR inline QPoint QVector3D::toPoint() const
{
return QPoint(qRound(xp), qRound(yp));
}
-inline QPointF QVector3D::toPointF() const
+Q_DECL_CONSTEXPR inline QPointF QVector3D::toPointF() const
{
return QPointF(qreal(xp), qreal(yp));
}
diff --git a/src/gui/math3d/qvector4d.h b/src/gui/math3d/qvector4d.h
index 810380b805..1256f384a0 100644
--- a/src/gui/math3d/qvector4d.h
+++ b/src/gui/math3d/qvector4d.h
@@ -57,10 +57,10 @@ class QVector3D;
class Q_GUI_EXPORT QVector4D
{
public:
- QVector4D();
- QVector4D(float xpos, float ypos, float zpos, float wpos);
- explicit QVector4D(const QPoint& point);
- explicit QVector4D(const QPointF& point);
+ Q_DECL_CONSTEXPR QVector4D();
+ Q_DECL_CONSTEXPR QVector4D(float xpos, float ypos, float zpos, float wpos);
+ Q_DECL_CONSTEXPR explicit QVector4D(const QPoint& point);
+ Q_DECL_CONSTEXPR explicit QVector4D(const QPointF& point);
#ifndef QT_NO_VECTOR2D
QVector4D(const QVector2D& vector);
QVector4D(const QVector2D& vector, float zpos, float wpos);
@@ -72,10 +72,10 @@ public:
bool isNull() const;
- float x() const;
- float y() const;
- float z() const;
- float w() const;
+ Q_DECL_CONSTEXPR float x() const;
+ Q_DECL_CONSTEXPR float y() const;
+ Q_DECL_CONSTEXPR float z() const;
+ Q_DECL_CONSTEXPR float w() const;
void setX(float x);
void setY(float y);
@@ -86,7 +86,12 @@ public:
float operator[](int i) const;
float length() const;
+#ifdef QT_BUILD_GUI_LIB
float lengthSquared() const;
+#else
+ Q_DECL_CONSTEXPR inline float lengthSquared() const
+ { return xp * xp + yp * yp + zp * zp + wp * wp; }
+#endif
QVector4D normalized() const;
void normalize();
@@ -97,19 +102,24 @@ public:
QVector4D &operator*=(const QVector4D &vector);
QVector4D &operator/=(float divisor);
+#ifdef QT_BUILD_GUI_LIB
static float dotProduct(const QVector4D& v1, const QVector4D& v2);
+#else
+ static float dotProduct(const QVector4D& v1, const QVector4D& v2)
+ { return v1.xp * v2.xp + v1.yp * v2.yp + v1.zp * v2.zp + v1.wp * v2.wp; }
+#endif
- friend inline bool operator==(const QVector4D &v1, const QVector4D &v2);
- friend inline bool operator!=(const QVector4D &v1, const QVector4D &v2);
- friend inline const QVector4D operator+(const QVector4D &v1, const QVector4D &v2);
- friend inline const QVector4D operator-(const QVector4D &v1, const QVector4D &v2);
- friend inline const QVector4D operator*(float factor, const QVector4D &vector);
- friend inline const QVector4D operator*(const QVector4D &vector, float factor);
- friend inline const QVector4D operator*(const QVector4D &v1, const QVector4D& v2);
- friend inline const QVector4D operator-(const QVector4D &vector);
- friend inline const QVector4D operator/(const QVector4D &vector, float divisor);
+ Q_DECL_CONSTEXPR friend inline bool operator==(const QVector4D &v1, const QVector4D &v2);
+ Q_DECL_CONSTEXPR friend inline bool operator!=(const QVector4D &v1, const QVector4D &v2);
+ Q_DECL_CONSTEXPR friend inline const QVector4D operator+(const QVector4D &v1, const QVector4D &v2);
+ Q_DECL_CONSTEXPR friend inline const QVector4D operator-(const QVector4D &v1, const QVector4D &v2);
+ Q_DECL_CONSTEXPR friend inline const QVector4D operator*(float factor, const QVector4D &vector);
+ Q_DECL_CONSTEXPR friend inline const QVector4D operator*(const QVector4D &vector, float factor);
+ Q_DECL_CONSTEXPR friend inline const QVector4D operator*(const QVector4D &v1, const QVector4D& v2);
+ Q_DECL_CONSTEXPR friend inline const QVector4D operator-(const QVector4D &vector);
+ Q_DECL_CONSTEXPR friend inline const QVector4D operator/(const QVector4D &vector, float divisor);
- friend inline bool qFuzzyCompare(const QVector4D& v1, const QVector4D& v2);
+ Q_DECL_CONSTEXPR friend inline bool qFuzzyCompare(const QVector4D& v1, const QVector4D& v2);
#ifndef QT_NO_VECTOR2D
QVector2D toVector2D() const;
@@ -120,8 +130,8 @@ public:
QVector3D toVector3DAffine() const;
#endif
- QPoint toPoint() const;
- QPointF toPointF() const;
+ Q_DECL_CONSTEXPR QPoint toPoint() const;
+ Q_DECL_CONSTEXPR QPointF toPointF() const;
operator QVariant() const;
@@ -138,23 +148,23 @@ private:
Q_DECLARE_TYPEINFO(QVector4D, Q_MOVABLE_TYPE);
-inline QVector4D::QVector4D() : xp(0.0f), yp(0.0f), zp(0.0f), wp(0.0f) {}
+Q_DECL_CONSTEXPR inline QVector4D::QVector4D() : xp(0.0f), yp(0.0f), zp(0.0f), wp(0.0f) {}
-inline QVector4D::QVector4D(float xpos, float ypos, float zpos, float wpos) : xp(xpos), yp(ypos), zp(zpos), wp(wpos) {}
+Q_DECL_CONSTEXPR inline QVector4D::QVector4D(float xpos, float ypos, float zpos, float wpos) : xp(xpos), yp(ypos), zp(zpos), wp(wpos) {}
-inline QVector4D::QVector4D(const QPoint& point) : xp(point.x()), yp(point.y()), zp(0.0f), wp(0.0f) {}
+Q_DECL_CONSTEXPR inline QVector4D::QVector4D(const QPoint& point) : xp(point.x()), yp(point.y()), zp(0.0f), wp(0.0f) {}
-inline QVector4D::QVector4D(const QPointF& point) : xp(point.x()), yp(point.y()), zp(0.0f), wp(0.0f) {}
+Q_DECL_CONSTEXPR inline QVector4D::QVector4D(const QPointF& point) : xp(point.x()), yp(point.y()), zp(0.0f), wp(0.0f) {}
inline bool QVector4D::isNull() const
{
return qIsNull(xp) && qIsNull(yp) && qIsNull(zp) && qIsNull(wp);
}
-inline float QVector4D::x() const { return xp; }
-inline float QVector4D::y() const { return yp; }
-inline float QVector4D::z() const { return zp; }
-inline float QVector4D::w() const { return wp; }
+Q_DECL_CONSTEXPR inline float QVector4D::x() const { return xp; }
+Q_DECL_CONSTEXPR inline float QVector4D::y() const { return yp; }
+Q_DECL_CONSTEXPR inline float QVector4D::z() const { return zp; }
+Q_DECL_CONSTEXPR inline float QVector4D::w() const { return wp; }
inline void QVector4D::setX(float aX) { xp = aX; }
inline void QVector4D::setY(float aY) { yp = aY; }
@@ -218,52 +228,52 @@ inline QVector4D &QVector4D::operator/=(float divisor)
return *this;
}
-inline bool operator==(const QVector4D &v1, const QVector4D &v2)
+Q_DECL_CONSTEXPR inline bool operator==(const QVector4D &v1, const QVector4D &v2)
{
return v1.xp == v2.xp && v1.yp == v2.yp && v1.zp == v2.zp && v1.wp == v2.wp;
}
-inline bool operator!=(const QVector4D &v1, const QVector4D &v2)
+Q_DECL_CONSTEXPR inline bool operator!=(const QVector4D &v1, const QVector4D &v2)
{
return v1.xp != v2.xp || v1.yp != v2.yp || v1.zp != v2.zp || v1.wp != v2.wp;
}
-inline const QVector4D operator+(const QVector4D &v1, const QVector4D &v2)
+Q_DECL_CONSTEXPR inline const QVector4D operator+(const QVector4D &v1, const QVector4D &v2)
{
return QVector4D(v1.xp + v2.xp, v1.yp + v2.yp, v1.zp + v2.zp, v1.wp + v2.wp);
}
-inline const QVector4D operator-(const QVector4D &v1, const QVector4D &v2)
+Q_DECL_CONSTEXPR inline const QVector4D operator-(const QVector4D &v1, const QVector4D &v2)
{
return QVector4D(v1.xp - v2.xp, v1.yp - v2.yp, v1.zp - v2.zp, v1.wp - v2.wp);
}
-inline const QVector4D operator*(float factor, const QVector4D &vector)
+Q_DECL_CONSTEXPR inline const QVector4D operator*(float factor, const QVector4D &vector)
{
return QVector4D(vector.xp * factor, vector.yp * factor, vector.zp * factor, vector.wp * factor);
}
-inline const QVector4D operator*(const QVector4D &vector, float factor)
+Q_DECL_CONSTEXPR inline const QVector4D operator*(const QVector4D &vector, float factor)
{
return QVector4D(vector.xp * factor, vector.yp * factor, vector.zp * factor, vector.wp * factor);
}
-inline const QVector4D operator*(const QVector4D &v1, const QVector4D& v2)
+Q_DECL_CONSTEXPR inline const QVector4D operator*(const QVector4D &v1, const QVector4D& v2)
{
return QVector4D(v1.xp * v2.xp, v1.yp * v2.yp, v1.zp * v2.zp, v1.wp * v2.wp);
}
-inline const QVector4D operator-(const QVector4D &vector)
+Q_DECL_CONSTEXPR inline const QVector4D operator-(const QVector4D &vector)
{
return QVector4D(-vector.xp, -vector.yp, -vector.zp, -vector.wp);
}
-inline const QVector4D operator/(const QVector4D &vector, float divisor)
+Q_DECL_CONSTEXPR inline const QVector4D operator/(const QVector4D &vector, float divisor)
{
return QVector4D(vector.xp / divisor, vector.yp / divisor, vector.zp / divisor, vector.wp / divisor);
}
-inline bool qFuzzyCompare(const QVector4D& v1, const QVector4D& v2)
+Q_DECL_CONSTEXPR inline bool qFuzzyCompare(const QVector4D& v1, const QVector4D& v2)
{
return qFuzzyCompare(v1.xp, v2.xp) &&
qFuzzyCompare(v1.yp, v2.yp) &&
@@ -271,12 +281,12 @@ inline bool qFuzzyCompare(const QVector4D& v1, const QVector4D& v2)
qFuzzyCompare(v1.wp, v2.wp);
}
-inline QPoint QVector4D::toPoint() const
+Q_DECL_CONSTEXPR inline QPoint QVector4D::toPoint() const
{
return QPoint(qRound(xp), qRound(yp));
}
-inline QPointF QVector4D::toPointF() const
+Q_DECL_CONSTEXPR inline QPointF QVector4D::toPointF() const
{
return QPointF(qreal(xp), qreal(yp));
}
diff --git a/src/gui/painting/qbrush.cpp b/src/gui/painting/qbrush.cpp
index 8bbe6b6f42..b35fa38ce0 100644
--- a/src/gui/painting/qbrush.cpp
+++ b/src/gui/painting/qbrush.cpp
@@ -56,44 +56,49 @@ QT_BEGIN_NAMESPACE
const uchar *qt_patternForBrush(int brushStyle, bool invert)
{
Q_ASSERT(brushStyle > Qt::SolidPattern && brushStyle < Qt::LinearGradientPattern);
- if(invert) {
- static const uchar dense1_pat[] = { 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff };
- static const uchar dense2_pat[] = { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff };
- static const uchar dense3_pat[] = { 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55, 0xee };
- static const uchar dense4_pat[] = { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 };
- static const uchar dense5_pat[] = { 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa, 0x11 };
- static const uchar dense6_pat[] = { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 };
- static const uchar dense7_pat[] = { 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00 };
- static const uchar hor_pat[] = { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 };
- static const uchar ver_pat[] = { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 };
- static const uchar cross_pat[] = { 0x10, 0x10, 0x10, 0xff, 0x10, 0x10, 0x10, 0x10 };
- static const uchar bdiag_pat[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
- static const uchar fdiag_pat[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
- static const uchar dcross_pat[] = { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 };
- static const uchar *const pat_tbl[] = {
- dense1_pat, dense2_pat, dense3_pat, dense4_pat, dense5_pat,
- dense6_pat, dense7_pat,
- hor_pat, ver_pat, cross_pat, bdiag_pat, fdiag_pat, dcross_pat };
- return pat_tbl[brushStyle - Qt::Dense1Pattern];
- }
- static const uchar dense1_pat[] = { 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00 };
- static const uchar dense2_pat[] = { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 };
- static const uchar dense3_pat[] = { 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa, 0x11 };
- static const uchar dense4_pat[] = { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa };
- static const uchar dense5_pat[] = { 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55, 0xee };
- static const uchar dense6_pat[] = { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff };
- static const uchar dense7_pat[] = { 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff };
- static const uchar hor_pat[] = { 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff };
- static const uchar ver_pat[] = { 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef };
- static const uchar cross_pat[] = { 0xef, 0xef, 0xef, 0x00, 0xef, 0xef, 0xef, 0xef };
- static const uchar bdiag_pat[] = { 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe };
- static const uchar fdiag_pat[] = { 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f };
- static const uchar dcross_pat[] = { 0x7e, 0xbd, 0xdb, 0xe7, 0xe7, 0xdb, 0xbd, 0x7e };
- static const uchar *const pat_tbl[] = {
- dense1_pat, dense2_pat, dense3_pat, dense4_pat, dense5_pat,
- dense6_pat, dense7_pat,
- hor_pat, ver_pat, cross_pat, bdiag_pat, fdiag_pat, dcross_pat };
- return pat_tbl[brushStyle - Qt::Dense1Pattern];
+ static const uchar pat_tbl[][2][8] = {
+ {
+ /* dense1 */ { 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00 },
+ /*~dense1 */ { 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff },
+ }, {
+ /* dense2 */ { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 },
+ /*~dense2 */ { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff },
+ }, {
+ /* dense3 */ { 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa, 0x11 },
+ /*~dense3 */ { 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55, 0xee },
+ }, {
+ /* dense4 */ { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa },
+ /*~dense4 */ { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 },
+ }, {
+ /* dense5 */ { 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55, 0xee },
+ /*~dense5 */ { 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa, 0x11 },
+ }, {
+ /* dense6 */ { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff },
+ /*~dense6 */ { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 },
+ }, {
+ /* dense7 */ { 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff },
+ /*~dense7 */ { 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00 },
+ }, {
+ /* hor */ { 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff },
+ /*~hor */ { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 },
+ }, {
+ /* ver */ { 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef },
+ /*~ver */ { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 },
+ }, {
+ /* cross */ { 0xef, 0xef, 0xef, 0x00, 0xef, 0xef, 0xef, 0xef },
+ /*~cross */ { 0x10, 0x10, 0x10, 0xff, 0x10, 0x10, 0x10, 0x10 },
+ }, {
+ /* bdiag */ { 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe },
+ /*~bdiag */ { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 },
+ }, {
+ /* fdiag */ { 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f },
+ /*~fdiag */ { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 },
+ }, {
+ /* dcross */ { 0x7e, 0xbd, 0xdb, 0xe7, 0xe7, 0xdb, 0xbd, 0x7e },
+ /*~dcross */ { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 },
+ },
+ };
+ return pat_tbl[brushStyle - Qt::Dense1Pattern][invert];
}
QPixmap qt_pixmapForBrush(int brushStyle, bool invert)
diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp
index a037545dc2..8b36a0e28f 100644
--- a/src/gui/painting/qdrawhelper.cpp
+++ b/src/gui/painting/qdrawhelper.cpp
@@ -118,6 +118,22 @@ static const uint *QT_FASTCALL convertARGB32ToARGB32PM(uint *buffer, const uint
return buffer;
}
+static const uint *QT_FASTCALL convertRGBA8888PMToARGB32PM(uint *buffer, const uint *src, int count,
+ const QPixelLayout *, const QRgb *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = RGBA2ARGB(src[i]);
+ return buffer;
+}
+
+static const uint *QT_FASTCALL convertRGBA8888ToARGB32PM(uint *buffer, const uint *src, int count,
+ const QPixelLayout *, const QRgb *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = PREMUL(RGBA2ARGB(src[i]));
+ return buffer;
+}
+
static const uint *QT_FASTCALL convertToRGB32(uint *buffer, const uint *src, int count,
const QPixelLayout *layout, const QRgb *)
{
@@ -222,6 +238,22 @@ static const uint *QT_FASTCALL convertARGB32FromARGB32PM(uint *buffer, const uin
return buffer;
}
+static const uint *QT_FASTCALL convertRGBA8888PMFromARGB32PM(uint *buffer, const uint *src, int count,
+ const QPixelLayout *, const QRgb *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = ARGB2RGBA(src[i]);
+ return buffer;
+}
+
+static const uint *QT_FASTCALL convertRGBA8888FromARGB32PM(uint *buffer, const uint *src, int count,
+ const QPixelLayout *, const QRgb *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = ARGB2RGBA(INV_PREMUL(src[i]));
+ return buffer;
+}
+
static const uint *QT_FASTCALL convertFromARGB32PM(uint *buffer, const uint *src, int count,
const QPixelLayout *layout, const QRgb *)
{
@@ -415,13 +447,13 @@ QPixelLayout qPixelLayouts[QImage::NImageFormats] = {
{ 4, 8, 4, 4, 4, 0, 0, 0, false, QPixelLayout::BPP16, convertToRGB32, convertFromARGB32PM }, // Format_RGB444
{ 4, 8, 4, 4, 4, 0, 4, 12, true, QPixelLayout::BPP16, convertToARGB32PM, convertFromARGB32PM }, // Format_ARGB4444_Premultiplied
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
- { 8, 24, 8, 16, 8, 8, 0, 0, false, QPixelLayout::BPP32, convertToRGB32, convertRGBFromARGB32PM }, // Format_RGBX8888
- { 8, 24, 8, 16, 8, 8, 8, 0, false, QPixelLayout::BPP32, convertToARGB32PM, convertFromARGB32PM }, // Format_RGBA8888
- { 8, 24, 8, 16, 8, 8, 8, 0, true, QPixelLayout::BPP32, convertToARGB32PM, convertFromARGB32PM }, // Format_RGBA8888_Premultiplied
+ { 8, 24, 8, 16, 8, 8, 0, 0, false, QPixelLayout::BPP32, convertRGBA8888PMToARGB32PM, convertRGBFromARGB32PM }, // Format_RGBX8888
+ { 8, 24, 8, 16, 8, 8, 8, 0, false, QPixelLayout::BPP32, convertRGBA8888ToARGB32PM, convertRGBA8888FromARGB32PM }, // Format_RGBA8888
+ { 8, 24, 8, 16, 8, 8, 8, 0, true, QPixelLayout::BPP32, convertRGBA8888PMToARGB32PM, convertRGBA8888PMFromARGB32PM }, // Format_RGBA8888_Premultiplied
#else
- { 8, 0, 8, 8, 8, 16, 0, 24, false, QPixelLayout::BPP32, convertToRGB32, convertRGBFromARGB32PM }, // Format_RGBX8888
- { 8, 0, 8, 8, 8, 16, 8, 24, false, QPixelLayout::BPP32, convertToARGB32PM, convertFromARGB32PM }, // Format_RGBA8888 (ABGR32)
- { 8, 0, 8, 8, 8, 16, 8, 24, true, QPixelLayout::BPP32, convertToARGB32PM, convertFromARGB32PM } // Format_RGBA8888_Premultiplied
+ { 8, 0, 8, 8, 8, 16, 0, 24, false, QPixelLayout::BPP32, convertRGBA8888PMToARGB32PM, convertRGBFromARGB32PM }, // Format_RGBX8888
+ { 8, 0, 8, 8, 8, 16, 8, 24, false, QPixelLayout::BPP32, convertRGBA8888ToARGB32PM, convertRGBA8888FromARGB32PM }, // Format_RGBA8888 (ABGR32)
+ { 8, 0, 8, 8, 8, 16, 8, 24, true, QPixelLayout::BPP32, convertRGBA8888PMToARGB32PM, convertRGBA8888PMFromARGB32PM } // Format_RGBA8888_Premultiplied
#endif
};
diff --git a/src/gui/painting/qpen.cpp b/src/gui/painting/qpen.cpp
index 6a3eacd67a..c0b3769c2d 100644
--- a/src/gui/painting/qpen.cpp
+++ b/src/gui/painting/qpen.cpp
@@ -455,15 +455,19 @@ QVector<qreal> QPen::dashPattern() const
switch (d->style) {
case Qt::DashLine:
+ dd->dashPattern.reserve(2);
dd->dashPattern << dash << space;
break;
case Qt::DotLine:
+ dd->dashPattern.reserve(2);
dd->dashPattern << dot << space;
break;
case Qt::DashDotLine:
+ dd->dashPattern.reserve(4);
dd->dashPattern << dash << space << dot << space;
break;
case Qt::DashDotDotLine:
+ dd->dashPattern.reserve(6);
dd->dashPattern << dash << space << dot << space << dot << space;
break;
default:
diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp
index 42cf15ee3b..22d2585898 100644
--- a/src/gui/text/qfontengine_ft.cpp
+++ b/src/gui/text/qfontengine_ft.cpp
@@ -716,7 +716,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
FT_Set_Transform(face, &matrix, 0);
freetype->matrix = matrix;
// fake bold
- if ((fontDef.weight == QFont::Bold) && !(face->style_flags & FT_STYLE_FLAG_BOLD) && !FT_IS_FIXED_WIDTH(face))
+ if ((fontDef.weight >= QFont::Bold) && !(face->style_flags & FT_STYLE_FLAG_BOLD) && !FT_IS_FIXED_WIDTH(face))
embolden = true;
// underline metrics
line_thickness = QFixed::fromFixed(FT_MulFix(face->underline_thickness, face->size->metrics.y_scale));
@@ -1187,7 +1187,7 @@ int QFontEngineFT::synthesized() const
int s = 0;
if ((fontDef.style != QFont::StyleNormal) && !(freetype->face->style_flags & FT_STYLE_FLAG_ITALIC))
s = SynthesizedItalic;
- if ((fontDef.weight == QFont::Bold) && !(freetype->face->style_flags & FT_STYLE_FLAG_BOLD))
+ if ((fontDef.weight >= QFont::Bold) && !(freetype->face->style_flags & FT_STYLE_FLAG_BOLD))
s |= SynthesizedBold;
if (fontDef.stretch != 100 && FT_IS_SCALABLE(freetype->face))
s |= SynthesizedStretch;
diff --git a/src/gui/text/qtextcursor.cpp b/src/gui/text/qtextcursor.cpp
index d12f3cccd8..ac9762b183 100644
--- a/src/gui/text/qtextcursor.cpp
+++ b/src/gui/text/qtextcursor.cpp
@@ -174,7 +174,6 @@ void QTextCursorPrivate::remove()
} else {
priv->remove(pos1, pos2-pos1, op);
adjusted_anchor = anchor = position;
- priv->finishEdit();
}
}
diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp
index 4a34f0d3c3..82d63237e7 100644
--- a/src/gui/text/qtextdocument.cpp
+++ b/src/gui/text/qtextdocument.cpp
@@ -551,6 +551,39 @@ void QTextDocument::setDefaultTextOption(const QTextOption &option)
}
/*!
+ \property QTextDocument::baseUrl
+ \since 5.3
+ \brief the base URL used to resolve relative resource URLs within the document.
+
+ Resource URLs are resolved to be within the same directory as the target of the base
+ URL meaning any portion of the path after the last '/' will be ignored.
+
+ \table
+ \header \li Base URL \li Relative URL \li Resolved URL
+ \row \li file:///path/to/content \li images/logo.png \li file:///path/to/images/logo.png
+ \row \li file:///path/to/content/ \li images/logo.png \li file:///path/to/content/images/logo.png
+ \row \li file:///path/to/content/index.html \li images/logo.png \li file:///path/to/content/images/logo.png
+ \row \li file:///path/to/content/images/ \li ../images/logo.png \li file:///path/to/content/images/logo.png
+ \endtable
+*/
+QUrl QTextDocument::baseUrl() const
+{
+ Q_D(const QTextDocument);
+ return d->baseUrl;
+}
+
+void QTextDocument::setBaseUrl(const QUrl &url)
+{
+ Q_D(QTextDocument);
+ if (d->baseUrl != url) {
+ d->baseUrl = url;
+ if (d->lout)
+ d->lout->documentChanged(0, 0, d->length());
+ emit baseUrlChanged(url);
+ }
+}
+
+/*!
\since 4.8
The default cursor movement style is used by all QTextCursor objects
@@ -1849,11 +1882,12 @@ void QTextDocument::print(QPagedPaintDevice *printer) const
QVariant QTextDocument::resource(int type, const QUrl &name) const
{
Q_D(const QTextDocument);
- QVariant r = d->resources.value(name);
+ const QUrl url = d->baseUrl.resolved(name);
+ QVariant r = d->resources.value(url);
if (!r.isValid()) {
- r = d->cachedResources.value(name);
+ r = d->cachedResources.value(url);
if (!r.isValid())
- r = const_cast<QTextDocument *>(this)->loadResource(type, name);
+ r = const_cast<QTextDocument *>(this)->loadResource(type, url);
}
return r;
}
@@ -1924,27 +1958,29 @@ QVariant QTextDocument::loadResource(int type, const QUrl &name)
}
// if resource was not loaded try to load it here
- if (!qobject_cast<QTextDocument *>(p) && r.isNull() && name.isRelative()) {
- QUrl currentURL = d->url;
+ if (!qobject_cast<QTextDocument *>(p) && r.isNull()) {
QUrl resourceUrl = name;
- // For the second case QUrl can merge "#someanchor" with "foo.html"
- // correctly to "foo.html#someanchor"
- if (!(currentURL.isRelative()
- || (currentURL.scheme() == QLatin1String("file")
- && !QFileInfo(currentURL.toLocalFile()).isAbsolute()))
- || (name.hasFragment() && name.path().isEmpty())) {
- resourceUrl = currentURL.resolved(name);
- } else {
- // this is our last resort when current url and new url are both relative
- // we try to resolve against the current working directory in the local
- // file system.
- QFileInfo fi(currentURL.toLocalFile());
- if (fi.exists()) {
- resourceUrl =
- QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(name);
- } else if (currentURL.isEmpty()) {
- resourceUrl.setScheme(QLatin1String("file"));
+ if (name.isRelative()) {
+ QUrl currentURL = d->url;
+ // For the second case QUrl can merge "#someanchor" with "foo.html"
+ // correctly to "foo.html#someanchor"
+ if (!(currentURL.isRelative()
+ || (currentURL.scheme() == QLatin1String("file")
+ && !QFileInfo(currentURL.toLocalFile()).isAbsolute()))
+ || (name.hasFragment() && name.path().isEmpty())) {
+ resourceUrl = currentURL.resolved(name);
+ } else {
+ // this is our last resort when current url and new url are both relative
+ // we try to resolve against the current working directory in the local
+ // file system.
+ QFileInfo fi(currentURL.toLocalFile());
+ if (fi.exists()) {
+ resourceUrl =
+ QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(name);
+ } else if (currentURL.isEmpty()) {
+ resourceUrl.setScheme(QLatin1String("file"));
+ }
}
}
diff --git a/src/gui/text/qtextdocument.h b/src/gui/text/qtextdocument.h
index a85e9c86c9..24e93b7e63 100644
--- a/src/gui/text/qtextdocument.h
+++ b/src/gui/text/qtextdocument.h
@@ -47,6 +47,7 @@
#include <QtCore/qrect.h>
#include <QtCore/qvariant.h>
#include <QtGui/qfont.h>
+#include <QtCore/qurl.h>
QT_BEGIN_NAMESPACE
@@ -63,7 +64,6 @@ class QTextFormat;
class QTextFrame;
class QTextBlock;
class QTextCodec;
-class QUrl;
class QVariant;
class QRectF;
class QTextOption;
@@ -114,6 +114,7 @@ class Q_GUI_EXPORT QTextDocument : public QObject
Q_PROPERTY(int maximumBlockCount READ maximumBlockCount WRITE setMaximumBlockCount)
Q_PROPERTY(qreal documentMargin READ documentMargin WRITE setDocumentMargin)
QDOC_PROPERTY(QTextOption defaultTextOption READ defaultTextOption WRITE setDefaultTextOption)
+ Q_PROPERTY(QUrl baseUrl READ baseUrl WRITE setBaseUrl NOTIFY baseUrlChanged)
public:
explicit QTextDocument(QObject *parent = 0);
@@ -256,6 +257,9 @@ public:
QTextOption defaultTextOption() const;
void setDefaultTextOption(const QTextOption &option);
+ QUrl baseUrl() const;
+ void setBaseUrl(const QUrl &url);
+
Qt::CursorMoveStyle defaultCursorMoveStyle() const;
void setDefaultCursorMoveStyle(Qt::CursorMoveStyle style);
@@ -268,7 +272,7 @@ Q_SIGNALS:
void modificationChanged(bool m);
void cursorPositionChanged(const QTextCursor &cursor);
void blockCountChanged(int newBlockCount);
-
+ void baseUrlChanged(const QUrl &url);
void documentLayoutChanged();
public Q_SLOTS:
diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h
index 8d4cab30ae..fa22131c9e 100644
--- a/src/gui/text/qtextdocument_p.h
+++ b/src/gui/text/qtextdocument_p.h
@@ -355,6 +355,7 @@ public:
QString url;
qreal indentWidth;
qreal documentMargin;
+ QUrl baseUrl;
void mergeCachedResources(const QTextDocumentPrivate *priv);
diff --git a/src/network/kernel/qdnslookup.cpp b/src/network/kernel/qdnslookup.cpp
index 53675d083b..e776a8eb76 100644
--- a/src/network/kernel/qdnslookup.cpp
+++ b/src/network/kernel/qdnslookup.cpp
@@ -363,6 +363,25 @@ void QDnsLookup::setType(Type type)
}
/*!
+ \property QDnsLookup::nameserver
+ \brief the nameserver to use for DNS lookup.
+*/
+
+QHostAddress QDnsLookup::nameserver() const
+{
+ return d_func()->nameserver;
+}
+
+void QDnsLookup::setNameserver(const QHostAddress &nameserver)
+{
+ Q_D(QDnsLookup);
+ if (nameserver != d->nameserver) {
+ d->nameserver = nameserver;
+ emit nameserverChanged(nameserver);
+ }
+}
+
+/*!
Returns the list of canonical name records associated with this lookup.
*/
diff --git a/src/network/kernel/qdnslookup.h b/src/network/kernel/qdnslookup.h
index 1df21d866e..ffbef61f92 100644
--- a/src/network/kernel/qdnslookup.h
+++ b/src/network/kernel/qdnslookup.h
@@ -180,6 +180,7 @@ class Q_NETWORK_EXPORT QDnsLookup : public QObject
Q_PROPERTY(QString errorString READ errorString NOTIFY finished)
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(Type type READ type WRITE setType NOTIFY typeChanged)
+ Q_PROPERTY(QHostAddress nameserver READ nameserver WRITE setNameserver NOTIFY nameserverChanged)
public:
enum Error
@@ -209,6 +210,7 @@ public:
explicit QDnsLookup(QObject *parent = 0);
QDnsLookup(Type type, const QString &name, QObject *parent = 0);
+ QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent = 0);
~QDnsLookup();
Error error() const;
@@ -221,6 +223,9 @@ public:
Type type() const;
void setType(QDnsLookup::Type);
+ QHostAddress nameserver() const;
+ void setNameserver(const QHostAddress &nameserver);
+
QList<QDnsDomainNameRecord> canonicalNameRecords() const;
QList<QDnsHostAddressRecord> hostAddressRecords() const;
QList<QDnsMailExchangeRecord> mailExchangeRecords() const;
@@ -238,6 +243,7 @@ Q_SIGNALS:
void finished();
void nameChanged(const QString &name);
void typeChanged(Type type);
+ void nameserverChanged(const QHostAddress &nameserver);
private:
Q_DECLARE_PRIVATE(QDnsLookup)
diff --git a/src/network/kernel/qdnslookup_p.h b/src/network/kernel/qdnslookup_p.h
index 692b9088fe..4bd3bd7603 100644
--- a/src/network/kernel/qdnslookup_p.h
+++ b/src/network/kernel/qdnslookup_p.h
@@ -100,6 +100,7 @@ public:
bool isFinished;
QString name;
QDnsLookup::Type type;
+ QHostAddress nameserver;
QDnsLookupReply reply;
QDnsLookupRunnable *runnable;
diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp
index 6818ff6354..b62c4a6bef 100644
--- a/src/network/socket/qsocks5socketengine.cpp
+++ b/src/network/socket/qsocks5socketengine.cpp
@@ -735,9 +735,10 @@ void QSocks5SocketEnginePrivate::reauthenticate()
proxyInfo.setPassword(auth.password());
data->authenticator = new QSocks5PasswordAuthenticator(proxyInfo.user(), proxyInfo.password());
- data->controlSocket->blockSignals(true);
- data->controlSocket->abort();
- data->controlSocket->blockSignals(false);
+ {
+ const QSignalBlocker blocker(data->controlSocket);
+ data->controlSocket->abort();
+ }
data->controlSocket->connectToHost(proxyInfo.hostName(), proxyInfo.port());
} else {
// authentication failure
diff --git a/src/platformsupport/eglconvenience/qeglconvenience.cpp b/src/platformsupport/eglconvenience/qeglconvenience.cpp
index 32f553a3f5..fe6ba2b215 100644
--- a/src/platformsupport/eglconvenience/qeglconvenience.cpp
+++ b/src/platformsupport/eglconvenience/qeglconvenience.cpp
@@ -365,6 +365,7 @@ QSurfaceFormat q_glFormatFromConfig(EGLDisplay display, const EGLConfig config,
format.setStencilBufferSize(stencilSize);
format.setSamples(sampleCount);
format.setStereo(false); // EGL doesn't support stereo buffers
+ format.setSwapInterval(referenceFormat.swapInterval());
// Clear the EGL error state because some of the above may
// have errored out because the attribute is not applicable
diff --git a/src/platformsupport/eglconvenience/qeglplatformcontext.cpp b/src/platformsupport/eglconvenience/qeglplatformcontext.cpp
index 714ad8a17e..ec740b87db 100644
--- a/src/platformsupport/eglconvenience/qeglplatformcontext.cpp
+++ b/src/platformsupport/eglconvenience/qeglplatformcontext.cpp
@@ -73,6 +73,9 @@ QEGLPlatformContext::QEGLPlatformContext(const QSurfaceFormat &format, QPlatform
: m_eglDisplay(display)
, m_eglApi(eglApi)
, m_eglConfig(q_configFromGLFormat(display, format))
+ , m_swapInterval(-1)
+ , m_swapIntervalEnvChecked(false)
+ , m_swapIntervalFromEnv(-1)
{
init(format, share);
}
@@ -145,6 +148,27 @@ bool QEGLPlatformContext::makeCurrent(QPlatformSurface *surface)
}
#endif
+
+ if (ok) {
+ if (!m_swapIntervalEnvChecked) {
+ m_swapIntervalEnvChecked = true;
+ if (qEnvironmentVariableIsSet("QT_QPA_EGL_SWAPINTERVAL")) {
+ QByteArray swapIntervalString = qgetenv("QT_QPA_EGL_SWAPINTERVAL");
+ bool ok;
+ const int swapInterval = swapIntervalString.toInt(&ok);
+ if (ok)
+ m_swapIntervalFromEnv = swapInterval;
+ }
+ }
+ const int requestedSwapInterval = m_swapIntervalFromEnv >= 0
+ ? m_swapIntervalFromEnv
+ : surface->format().swapInterval();
+ if (requestedSwapInterval >= 0 && m_swapInterval != requestedSwapInterval) {
+ m_swapInterval = requestedSwapInterval;
+ eglSwapInterval(eglDisplay(), m_swapInterval);
+ }
+ }
+
return ok;
}
diff --git a/src/platformsupport/eglconvenience/qeglplatformcontext_p.h b/src/platformsupport/eglconvenience/qeglplatformcontext_p.h
index 952f5a856a..3d81a1c56e 100644
--- a/src/platformsupport/eglconvenience/qeglplatformcontext_p.h
+++ b/src/platformsupport/eglconvenience/qeglplatformcontext_p.h
@@ -80,6 +80,9 @@ private:
EGLenum m_eglApi;
EGLConfig m_eglConfig;
QSurfaceFormat m_format;
+ int m_swapInterval;
+ bool m_swapIntervalEnvChecked;
+ int m_swapIntervalFromEnv;
};
#endif //QEGLPLATFORMCONTEXT_H
diff --git a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp
index 8c3ca2d229..da4007ff21 100644
--- a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp
+++ b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp
@@ -69,21 +69,52 @@ static inline bool requiresOpenType(int writingSystem)
|| writingSystem == QFontDatabase::Khmer || writingSystem == QFontDatabase::Nko);
}
-static int getFCWeight(int fc_weight)
+static inline int weightFromFcWeight(int fcweight)
{
- int qtweight = QFont::Black;
- if (fc_weight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_REGULAR) / 2)
- qtweight = QFont::Light;
- else if (fc_weight <= (FC_WEIGHT_REGULAR + FC_WEIGHT_MEDIUM) / 2)
- qtweight = QFont::Normal;
- else if (fc_weight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_BOLD) / 2)
- qtweight = QFont::DemiBold;
- else if (fc_weight <= (FC_WEIGHT_BOLD + FC_WEIGHT_BLACK) / 2)
- qtweight = QFont::Bold;
+ // Font Config uses weights from 0 to 215 (the highest enum value) while QFont ranges from
+ // 0 to 99. The spacing between the values for the enums are uneven so a linear mapping from
+ // Font Config values to Qt would give surprising results. So, we do a piecewise linear
+ // mapping. This ensures that where there is a corresponding enum on both sides (for example
+ // FC_WEIGHT_DEMIBOLD and QFont::DemiBold) we map one to the other but other values map
+ // to intermediate Qt weights.
+ const int maxWeight = 99;
+ int qtweight;
+ if (fcweight < 0)
+ qtweight = 0;
+ else if (fcweight <= FC_WEIGHT_LIGHT)
+ qtweight = (fcweight * QFont::Light) / FC_WEIGHT_LIGHT;
+ else if (fcweight <= FC_WEIGHT_NORMAL)
+ qtweight = QFont::Light + ((fcweight - FC_WEIGHT_LIGHT) * (QFont::Normal - QFont::Light)) / (FC_WEIGHT_NORMAL - FC_WEIGHT_LIGHT);
+ else if (fcweight <= FC_WEIGHT_DEMIBOLD)
+ qtweight = QFont::Normal + ((fcweight - FC_WEIGHT_NORMAL) * (QFont::DemiBold - QFont::Normal)) / (FC_WEIGHT_DEMIBOLD - FC_WEIGHT_NORMAL);
+ else if (fcweight <= FC_WEIGHT_BOLD)
+ qtweight = QFont::DemiBold + ((fcweight - FC_WEIGHT_DEMIBOLD) * (QFont::Bold - QFont::DemiBold)) / (FC_WEIGHT_BOLD - FC_WEIGHT_DEMIBOLD);
+ else if (fcweight <= FC_WEIGHT_BLACK)
+ qtweight = QFont::Bold + ((fcweight - FC_WEIGHT_BOLD) * (QFont::Black - QFont::Bold)) / (FC_WEIGHT_BLACK - FC_WEIGHT_BOLD);
+ else if (fcweight <= FC_WEIGHT_ULTRABLACK)
+ qtweight = QFont::Black + ((fcweight - FC_WEIGHT_BLACK) * (maxWeight - QFont::Black)) / (FC_WEIGHT_ULTRABLACK - FC_WEIGHT_BLACK);
+ else
+ qtweight = maxWeight;
return qtweight;
}
+static inline int stretchFromFcWidth(int fcwidth)
+{
+ // Font Config enums for width match pretty closely with those used by Qt so just use
+ // Font Config values directly while enforcing the same limits imposed by QFont.
+ const int maxStretch = 4000;
+ int qtstretch;
+ if (fcwidth < 1)
+ qtstretch = 1;
+ else if (fcwidth > maxStretch)
+ qtstretch = maxStretch;
+ else
+ qtstretch = fcwidth;
+
+ return qtstretch;
+}
+
static const char *specialLanguages[] = {
"", // Unknown
"", // Inherited
@@ -309,6 +340,7 @@ void QFontconfigDatabase::populateFontDatabase()
int weight_value;
int slant_value;
int spacing_value;
+ int width_value;
FcChar8 *file_value;
int indexValue;
FcChar8 *foundry_value;
@@ -322,7 +354,7 @@ void QFontconfigDatabase::populateFontDatabase()
const char *properties [] = {
FC_FAMILY, FC_STYLE, FC_WEIGHT, FC_SLANT,
FC_SPACING, FC_FILE, FC_INDEX,
- FC_LANG, FC_CHARSET, FC_FOUNDRY, FC_SCALABLE, FC_PIXEL_SIZE, FC_WEIGHT,
+ FC_LANG, FC_CHARSET, FC_FOUNDRY, FC_SCALABLE, FC_PIXEL_SIZE,
FC_WIDTH,
#if FC_VERSION >= 20297
FC_CAPABILITY,
@@ -356,6 +388,8 @@ void QFontconfigDatabase::populateFontDatabase()
slant_value = FC_SLANT_ROMAN;
if (FcPatternGetInteger (fonts->fonts[i], FC_WEIGHT, 0, &weight_value) != FcResultMatch)
weight_value = FC_WEIGHT_REGULAR;
+ if (FcPatternGetInteger (fonts->fonts[i], FC_WIDTH, 0, &width_value) != FcResultMatch)
+ width_value = FC_WIDTH_NORMAL;
if (FcPatternGetInteger (fonts->fonts[i], FC_SPACING, 0, &spacing_value) != FcResultMatch)
spacing_value = FC_PROPORTIONAL;
if (FcPatternGetString (fonts->fonts[i], FC_FILE, 0, &file_value) != FcResultMatch)
@@ -417,17 +451,16 @@ void QFontconfigDatabase::populateFontDatabase()
: ((slant_value == FC_SLANT_OBLIQUE)
? QFont::StyleOblique
: QFont::StyleNormal);
- QFont::Weight weight = QFont::Weight(getFCWeight(weight_value));
+ // Note: weight should really be an int but registerFont incorrectly uses an enum
+ QFont::Weight weight = QFont::Weight(weightFromFcWeight(weight_value));
double pixel_size = 0;
- if (!scalable) {
- int width = 100;
- FcPatternGetInteger (fonts->fonts[i], FC_WIDTH, 0, &width);
+ if (!scalable)
FcPatternGetDouble (fonts->fonts[i], FC_PIXEL_SIZE, 0, &pixel_size);
- }
bool fixedPitch = spacing_value >= FC_MONO;
- QFont::Stretch stretch = QFont::Unstretched;
+ // Note: stretch should really be an int but registerFont incorrectly uses an enum
+ QFont::Stretch stretch = QFont::Stretch(stretchFromFcWidth(width_value));
QString styleName = style_value ? QString::fromUtf8((const char *) style_value) : QString();
QPlatformFontDatabase::registerFont(familyName,styleName,QLatin1String((const char *)foundry_value),weight,style,stretch,antialias,scalable,pixel_size,fixedPitch,writingSystems,fontFile);
// qDebug() << familyName << (const char *)foundry_value << weight << style << &writingSystems << scalable << true << pixel_size;
diff --git a/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp b/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp
index dad34e121b..3d1b281620 100644
--- a/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp
+++ b/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp
@@ -58,6 +58,7 @@
#include <locale.h> // LC_CTYPE
#include <string.h> // strchr, strncmp, etc.
#include <strings.h> // strncasecmp
+#include <clocale> // LC_CTYPE
TableGenerator::TableGenerator() : m_state(NoErrors),
m_systemComposeDir(QString())
diff --git a/src/plugins/platforms/eglfs/qeglfscontext.cpp b/src/plugins/platforms/eglfs/qeglfscontext.cpp
index 2c6846132d..dd3f272d8b 100644
--- a/src/plugins/platforms/eglfs/qeglfscontext.cpp
+++ b/src/plugins/platforms/eglfs/qeglfscontext.cpp
@@ -54,29 +54,13 @@ QT_BEGIN_NAMESPACE
QEglFSContext::QEglFSContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share,
EGLDisplay display, EGLenum eglApi)
: QEGLPlatformContext(QEglFSHooks::hooks()->surfaceFormatFor(format), share, display,
- QEglFSIntegration::chooseConfig(display, QEglFSHooks::hooks()->surfaceFormatFor(format)), eglApi),
- m_swapIntervalSet(false)
+ QEglFSIntegration::chooseConfig(display, QEglFSHooks::hooks()->surfaceFormatFor(format)), eglApi)
{
}
bool QEglFSContext::makeCurrent(QPlatformSurface *surface)
{
- bool success = QEGLPlatformContext::makeCurrent(surface);
-
- if (success && !m_swapIntervalSet) {
- m_swapIntervalSet = true;
- int swapInterval = 1;
- QByteArray swapIntervalString = qgetenv("QT_QPA_EGLFS_SWAPINTERVAL");
- if (!swapIntervalString.isEmpty()) {
- bool ok;
- swapInterval = swapIntervalString.toInt(&ok);
- if (!ok)
- swapInterval = 1;
- }
- eglSwapInterval(eglDisplay(), swapInterval);
- }
-
- return success;
+ return QEGLPlatformContext::makeCurrent(surface);
}
EGLSurface QEglFSContext::eglSurfaceForPlatformSurface(QPlatformSurface *surface)
diff --git a/src/plugins/platforms/eglfs/qeglfscontext.h b/src/plugins/platforms/eglfs/qeglfscontext.h
index 8db340252c..6caa49ab4f 100644
--- a/src/plugins/platforms/eglfs/qeglfscontext.h
+++ b/src/plugins/platforms/eglfs/qeglfscontext.h
@@ -55,9 +55,6 @@ public:
bool makeCurrent(QPlatformSurface *surface);
EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface);
void swapBuffers(QPlatformSurface *surface);
-
-private:
- bool m_swapIntervalSet;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/qeglfswindow.cpp b/src/plugins/platforms/eglfs/qeglfswindow.cpp
index bba00da128..cc0e64a83b 100644
--- a/src/plugins/platforms/eglfs/qeglfswindow.cpp
+++ b/src/plugins/platforms/eglfs/qeglfswindow.cpp
@@ -126,7 +126,7 @@ void QEglFSWindow::create()
EGLDisplay display = static_cast<QEglFSScreen *>(screen)->display();
QSurfaceFormat platformFormat = QEglFSHooks::hooks()->surfaceFormatFor(window()->requestedFormat());
m_config = QEglFSIntegration::chooseConfig(display, platformFormat);
- m_format = q_glFormatFromConfig(display, m_config);
+ m_format = q_glFormatFromConfig(display, m_config, platformFormat);
resetSurface();
diff --git a/src/plugins/platforms/minimal/qminimalintegration.cpp b/src/plugins/platforms/minimal/qminimalintegration.cpp
index a08cede76a..bb2c5f53c3 100644
--- a/src/plugins/platforms/minimal/qminimalintegration.cpp
+++ b/src/plugins/platforms/minimal/qminimalintegration.cpp
@@ -41,16 +41,19 @@
#include "qminimalintegration.h"
#include "qminimalbackingstore.h"
-#ifndef Q_OS_WIN
-#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h>
-#else
-#include <QtCore/private/qeventdispatcher_win_p.h>
-#endif
#include <QtGui/private/qpixmap_raster_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformwindow.h>
+#if !defined(Q_OS_WIN)
+#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h>
+#elif defined(Q_OS_WINRT)
+#include <QtCore/private/qeventdispatcher_winrt_p.h>
+#else
+#include <QtCore/private/qeventdispatcher_win_p.h>
+#endif
+
QT_BEGIN_NAMESPACE
QMinimalIntegration::QMinimalIntegration()
@@ -89,7 +92,11 @@ QPlatformBackingStore *QMinimalIntegration::createPlatformBackingStore(QWindow *
QAbstractEventDispatcher *QMinimalIntegration::createEventDispatcher() const
{
#ifdef Q_OS_WIN
+#ifndef Q_OS_WINRT
return new QEventDispatcherWin32;
+#else // !Q_OS_WINRT
+ return new QEventDispatcherWinRT;
+#endif // Q_OS_WINRT
#else
return createUnixEventDispatcher();
#endif
diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration.cpp b/src/plugins/platforms/offscreen/qoffscreenintegration.cpp
index a1da8e3a16..76881db6fc 100644
--- a/src/plugins/platforms/offscreen/qoffscreenintegration.cpp
+++ b/src/plugins/platforms/offscreen/qoffscreenintegration.cpp
@@ -52,7 +52,11 @@
#endif
#elif defined(Q_OS_WIN)
#include <QtPlatformSupport/private/qbasicfontdatabase_p.h>
+#ifndef Q_OS_WINRT
#include <QtCore/private/qeventdispatcher_win_p.h>
+#else
+#include <QtCore/private/qeventdispatcher_winrt_p.h>
+#endif
#endif
#include <QtGui/private/qpixmap_raster_p.h>
@@ -143,7 +147,11 @@ QAbstractEventDispatcher *QOffscreenIntegration::createEventDispatcher() const
#if defined(Q_OS_UNIX)
return createUnixEventDispatcher();
#elif defined(Q_OS_WIN)
+#ifndef Q_OS_WINRT
return new QOffscreenEventDispatcher<QEventDispatcherWin32>();
+#else // !Q_OS_WINRT
+ return new QOffscreenEventDispatcher<QEventDispatcherWinRT>();
+#endif // Q_OS_WINRT
#else
return 0;
#endif
diff --git a/src/plugins/platforms/platforms.pro b/src/plugins/platforms/platforms.pro
index 377ca32e64..4a7c3b279f 100644
--- a/src/plugins/platforms/platforms.pro
+++ b/src/plugins/platforms/platforms.pro
@@ -15,7 +15,8 @@ mac {
else: SUBDIRS += cocoa
}
-win32: SUBDIRS += windows
+win32:!winrt: SUBDIRS += windows
+winrt: SUBDIRS += winrt
qnx {
SUBDIRS += qnx
diff --git a/src/plugins/platforms/qnx/qnx.pro b/src/plugins/platforms/qnx/qnx.pro
index bc7219de5c..100db54a5c 100644
--- a/src/plugins/platforms/qnx/qnx.pro
+++ b/src/plugins/platforms/qnx/qnx.pro
@@ -62,7 +62,8 @@ HEADERS = main.h \
qqnxabstractcover.h \
qqnxservices.h \
qqnxcursor.h \
- qqnxrasterwindow.h
+ qqnxrasterwindow.h \
+ qqnxscreeneventfilter.h
CONFIG(qqnx_screeneventthread) {
DEFINES += QQNX_SCREENEVENTTHREAD
diff --git a/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.cpp b/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.cpp
index a42f73415e..1d8591cfa1 100644
--- a/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.cpp
+++ b/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.cpp
@@ -1,6 +1,6 @@
/***************************************************************************
**
-** Copyright (C) 2011 - 2012 Research In Motion
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
@@ -62,23 +62,19 @@ void QQnxAbstractVirtualKeyboard::setKeyboardMode(KeyboardMode mode)
applyKeyboardMode(mode);
}
-void QQnxAbstractVirtualKeyboard::setInputHintsFromObject(QObject *focusObject)
+void QQnxAbstractVirtualKeyboard::setInputHints(int inputHints)
{
- if (focusObject) {
- const Qt::InputMethodHints hints = static_cast<Qt::InputMethodHints>(
- focusObject->property("inputMethodHints").toInt());
- if (hints & Qt::ImhEmailCharactersOnly) {
- setKeyboardMode(QQnxAbstractVirtualKeyboard::Email);
- } else if (hints & Qt::ImhDialableCharactersOnly) {
- setKeyboardMode(QQnxAbstractVirtualKeyboard::Phone);
- } else if (hints & Qt::ImhUrlCharactersOnly) {
- setKeyboardMode(QQnxAbstractVirtualKeyboard::Web);
- } else if (hints & Qt::ImhFormattedNumbersOnly || hints & Qt::ImhDigitsOnly ||
- hints & Qt::ImhDate || hints & Qt::ImhTime) {
- setKeyboardMode(QQnxAbstractVirtualKeyboard::NumPunc);
- } else {
- setKeyboardMode(QQnxAbstractVirtualKeyboard::Default);
- }
+ if (inputHints & Qt::ImhEmailCharactersOnly) {
+ setKeyboardMode(QQnxAbstractVirtualKeyboard::Email);
+ } else if (inputHints & Qt::ImhDialableCharactersOnly) {
+ setKeyboardMode(QQnxAbstractVirtualKeyboard::Phone);
+ } else if (inputHints & Qt::ImhUrlCharactersOnly) {
+ setKeyboardMode(QQnxAbstractVirtualKeyboard::Web);
+ } else if (inputHints & Qt::ImhFormattedNumbersOnly || inputHints & Qt::ImhDigitsOnly ||
+ inputHints & Qt::ImhDate || inputHints & Qt::ImhTime) {
+ setKeyboardMode(QQnxAbstractVirtualKeyboard::NumPunc);
+ } else if (inputHints & Qt::ImhHiddenText) {
+ setKeyboardMode(QQnxAbstractVirtualKeyboard::Password);
} else {
setKeyboardMode(QQnxAbstractVirtualKeyboard::Default);
}
diff --git a/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.h b/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.h
index 9b911e1dec..033d8ebb74 100644
--- a/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.h
+++ b/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.h
@@ -1,6 +1,6 @@
/***************************************************************************
**
-** Copyright (C) 2011 - 2012 Research In Motion
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
@@ -59,10 +59,11 @@ public:
// Symbol - All symbols, alternate to NumPunc, currently unused.
// Phone - Phone enhanced keyboard - currently unused as no alternate keyboard available to access a-zA-Z
// Pin - Keyboard for entering Pins (Hex values) currently unused.
+ // Password - Keyboard for entering passwords.
//
// SPECIAL NOTE: Usage of NumPunc may have to be removed, ABC button is non-functional.
//
- enum KeyboardMode { Default, Url, Email, Web, NumPunc, Symbol, Phone, Pin };
+ enum KeyboardMode { Default, Url, Email, Web, NumPunc, Symbol, Phone, Pin, Password };
explicit QQnxAbstractVirtualKeyboard(QObject *parent = 0);
@@ -74,7 +75,7 @@ public:
QLocale locale() const { return m_locale; }
void setKeyboardMode(KeyboardMode mode);
- void setInputHintsFromObject(QObject *focusObject);
+ void setInputHints(int inputHints);
KeyboardMode keyboardMode() const { return m_keyboardMode; }
Q_SIGNALS:
diff --git a/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp b/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp
index 580553f6e2..dd47d37af3 100644
--- a/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp
+++ b/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp
@@ -1,6 +1,6 @@
/***************************************************************************
**
-** Copyright (C) 2011 - 2012 Research In Motion
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
@@ -40,10 +40,10 @@
****************************************************************************/
#include "qqnxinputcontext_imf.h"
-#include "qqnxeventthread.h"
#include "qqnxabstractvirtualkeyboard.h"
#include "qqnxintegration.h"
#include "qqnxscreen.h"
+#include "qqnxscreeneventhandler.h"
#include <QtGui/QGuiApplication>
#include <QtGui/QInputMethodEvent>
@@ -62,9 +62,9 @@
#include <sys/keycodes.h>
#if defined(QQNXINPUTCONTEXT_IMF_EVENT_DEBUG)
-#define qInputContextIMFEventDebug qDebug
+#define qInputContextIMFRequestDebug qDebug
#else
-#define qInputContextIMFEventDebug QT_NO_QDEBUG_MACRO
+#define qInputContextIMFRequestDebug QT_NO_QDEBUG_MACRO
#endif
#if defined(QQNXINPUTCONTEXT_DEBUG)
@@ -73,492 +73,419 @@
#define qInputContextDebug QT_NO_QDEBUG_MACRO
#endif
-/** TODO:
- Support inputMethodHints to restrict input (needs additional features in IMF).
-*/
+#include <QtGui/QAccessible>
+#include <QtGui/private/qaccessible2_p.h>
-#define STRX(x) #x
-#define STR(x) STRX(x)
+static QQnxInputContext *sInputContextInstance;
+static QColor sSelectedColor(0,0xb8,0,85);
+static QColor sAutoCorrectedColor(0,0xb8,0,85);
+static QColor sRevertedColor(0,0x8d,0xaf,51);
-// Someone tell me why input_control methods are in this namespace, but the rest is not.
-using namespace InputMethodSystem;
-
-#define qs(x) QString::fromLatin1(x)
-#define iarg(name) event->mArgs[qs(#name)] = QVariant::fromValue(name)
-#define parg(name) event->mArgs[qs(#name)] = QVariant::fromValue((void*)name)
-namespace
-{
-
-spannable_string_t *toSpannableString(const QString &text);
static const input_session_t *sInputSession = 0;
-bool isSessionOkay(input_session_t *ic)
+static bool isSessionOkay(input_session_t *ic)
{
return ic !=0 && sInputSession != 0 && ic->component_id == sInputSession->component_id;
}
enum ImfEventType
{
- ImfBeginBatchEdit,
- ImfClearMetaKeyStates,
ImfCommitText,
ImfDeleteSurroundingText,
- ImfEndBatchEdit,
ImfFinishComposingText,
- ImfGetCursorCapsMode,
ImfGetCursorPosition,
- ImfGetExtractedText,
- ImfGetSelectedText,
ImfGetTextAfterCursor,
ImfGetTextBeforeCursor,
- ImfPerformEditorAction,
- ImfReportFullscreenMode,
ImfSendEvent,
ImfSendAsyncEvent,
ImfSetComposingRegion,
ImfSetComposingText,
- ImfSetSelection
+ ImfIsTextSelected,
+ ImfIsAllTextSelected,
};
-// We use this class as a round about way to support a posting synchronous event into
-// Qt's main thread from the IMF thread.
-class ImfEventResult
+// IMF requests all arrive on IMF's own thread and have to be posted to the main thread to be processed.
+class QQnxImfRequest
{
public:
- ImfEventResult()
- {
- m_mutex.lock();
- }
-
- ~ImfEventResult()
- {
- m_mutex.unlock();
- }
-
- void wait()
- {
- m_wait.wait(&m_mutex);
- }
-
- void signal()
- {
- m_wait.wakeAll();
- }
-
- void setResult(const QVariant& result)
- {
- m_mutex.lock();
- m_retVal = result;
- signal();
- m_mutex.unlock();
- }
-
- QVariant result()
- {
- return m_retVal;
- }
-
-private:
- QVariant m_retVal;
- QMutex m_mutex;
- QWaitCondition m_wait;
+ QQnxImfRequest(input_session_t *_session, ImfEventType _type)
+ : session(_session), type(_type)
+ { }
+ ~QQnxImfRequest() { }
+
+ input_session_t *session;
+ ImfEventType type;
+ union {
+ struct {
+ int32_t n;
+ int32_t flags;
+ bool before;
+ spannable_string_t *result;
+ } gtac; // ic_get_text_before_cursor/ic_get_text_after_cursor
+ struct {
+ int32_t result;
+ } gcp; // ic_get_cursor_position
+ struct {
+ int32_t start;
+ int32_t end;
+ int32_t result;
+ } scr; // ic_set_composing_region
+ struct {
+ spannable_string_t* text;
+ int32_t new_cursor_position;
+ int32_t result;
+ } sct; // ic_set_composing_text
+ struct {
+ spannable_string_t* text;
+ int32_t new_cursor_position;
+ int32_t result;
+ } ct; // ic_commit_text
+ struct {
+ int32_t result;
+ } fct; // ic_finish_composing_text
+ struct {
+ int32_t left_length;
+ int32_t right_length;
+ int32_t result;
+ } dst; // ic_delete_surrounding_text
+ struct {
+ event_t *event;
+ int32_t result;
+ } sae; // ic_send_async_event/ic_send_event
+ struct {
+ int32_t *pIsSelected;
+ int32_t result;
+ } its; // ic_is_text_selected/ic_is_all_text_selected
+ };
};
-class ImfEvent : public QEvent
+// Invoke an IMF initiated request synchronously on Qt's main thread. As describe below all
+// IMF requests are made from another thread but need to be executed on the main thread.
+static void executeIMFRequest(QQnxImfRequest *event)
+{
+ QMetaObject::invokeMethod(sInputContextInstance,
+ "processImfEvent",
+ Qt::BlockingQueuedConnection,
+ Q_ARG(QQnxImfRequest*, event));
+}
+
+// The following functions (ic_*) are callback functions called by the input system to query information
+// about the text object that currently has focus or to make changes to it. All calls are made from the
+// input system's own thread. The pattern for each callback function is to copy its parameters into
+// a QQnxImfRequest structure and call executeIMFRequest to have it passed synchronously to Qt's main thread.
+// Any return values should be pre-initialised with suitable default values as in some cases
+// (e.g. a stale session) the call will return without having executed any request specific code.
+//
+// To make the correspondence more obvious, the names of these functions match those defined in the headers.
+// They're in an anonymous namespace to avoid compiler conflicts with external functions defined with the
+// same names.
+namespace
{
- public:
- ImfEvent(input_session_t *session, ImfEventType type, ImfEventResult *result) :
- QEvent((QEvent::Type)sUserEventType),
- m_session(session),
- m_imfType(type),
- m_result(result)
- {
- }
- ~ImfEvent() { }
-
- input_session_t *m_session;
- ImfEventType m_imfType;
- QVariantHash m_args;
- ImfEventResult *m_result;
- static int sUserEventType;
-};
-int ImfEvent::sUserEventType = QEvent::registerEventType();
-
-static int32_t imfBeginBatchEdit(input_session_t *ic)
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_begin_batch_edit(input_session_t *ic)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
+ Q_UNUSED(ic);
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfBeginBatchEdit, &result);
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
-
- result.wait();
- int32_t ret = result.result().value<int32_t>();
-
- return ret;
+ // Ignore silently.
+ return 0;
}
-static int32_t imfClearMetaKeyStates(input_session_t *ic, int32_t states)
+// End composition, committing the supplied text.
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_commit_text(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return 0;
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfClearMetaKeyStates, &result);
- iarg(states);
-
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
-
- result.wait();
- int32_t ret = result.result().value<int32_t>();
+ QQnxImfRequest event(ic, ImfCommitText);
+ event.ct.text = text;
+ event.ct.new_cursor_position = new_cursor_position;
+ event.ct.result = -1;
+ executeIMFRequest(&event);
- return ret;
+ return event.ct.result;
}
-static int32_t imfCommitText(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position)
+// Delete left_length characters before and right_length characters after the cursor.
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_delete_surrounding_text(input_session_t *ic, int32_t left_length, int32_t right_length)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfCommitText, &result);
- parg(text);
- iarg(new_cursor_position);
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
+ QQnxImfRequest event(ic, ImfDeleteSurroundingText);
+ event.dst.left_length = left_length;
+ event.dst.right_length = right_length;
+ event.dst.result = -1;
+ executeIMFRequest(&event);
- result.wait();
- int32_t ret = result.result().value<int32_t>();
-
- return ret;
+ return event.dst.result;
}
-static int32_t imfDeleteSurroundingText(input_session_t *ic, int32_t left_length, int32_t right_length)
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_end_batch_edit(input_session_t *ic)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfDeleteSurroundingText, &result);
- iarg(left_length);
- iarg(right_length);
-
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
-
- result.wait();
- int32_t ret = result.result().value<int32_t>();
+ Q_UNUSED(ic);
- return ret;
+ // Ignore silently.
+ return 0;
}
-static int32_t imfEndBatchEdit(input_session_t *ic)
+// End composition, committing what's there.
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_finish_composing_text(input_session_t *ic)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfEndBatchEdit, &result);
+ QQnxImfRequest event(ic, ImfFinishComposingText);
+ event.fct.result = -1;
+ executeIMFRequest(&event);
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
-
- result.wait();
- int32_t ret = result.result().value<int32_t>();
-
- return ret;
+ return event.fct.result;
}
-static int32_t imfFinishComposingText(input_session_t *ic)
+// Return the position of the cursor.
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_get_cursor_position(input_session_t *ic)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfFinishComposingText, &result);
-
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- result.wait();
- int32_t ret = result.result().value<int32_t>();
+ QQnxImfRequest event(ic, ImfGetCursorPosition);
+ event.gcp.result = -1;
+ executeIMFRequest(&event);
- return ret;
+ return event.gcp.result;
}
-static int32_t imfGetCursorCapsMode(input_session_t *ic, int32_t req_modes)
+// Return the n characters after the cursor.
+// See comment at beginning of namespace declaration for general information
+static spannable_string_t *ic_get_text_after_cursor(input_session_t *ic, int32_t n, int32_t flags)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfGetCursorCapsMode, &result);
- iarg(req_modes);
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
+ QQnxImfRequest event(ic, ImfGetTextAfterCursor);
+ event.gtac.n = n;
+ event.gtac.flags = flags;
+ event.gtac.result = 0;
+ executeIMFRequest(&event);
- int32_t ret = result.result().value<int32_t>();
- return ret;
+ return event.gtac.result;
}
-static int32_t imfGetCursorPosition(input_session_t *ic)
+// Return the n characters before the cursor.
+// See comment at beginning of namespace declaration for general information
+static spannable_string_t *ic_get_text_before_cursor(input_session_t *ic, int32_t n, int32_t flags)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return 0;
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfGetCursorPosition, &result);
-
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
-
- result.wait();
- int32_t ret = result.result().value<int32_t>();
+ QQnxImfRequest event(ic, ImfGetTextBeforeCursor);
+ event.gtac.n = n;
+ event.gtac.flags = flags;
+ event.gtac.result = 0;
+ executeIMFRequest(&event);
- return ret;
+ return event.gtac.result;
}
-static extracted_text_t *imfGetExtractedText(input_session_t *ic, extracted_text_request_t *request, int32_t flags)
+// Process an event from IMF. Primarily used for reflecting back keyboard events.
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_send_event(input_session_t *ic, event_t *event)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic)) {
- extracted_text_t *et = (extracted_text_t *)calloc(sizeof(extracted_text_t),1);
- et->text = (spannable_string_t *)calloc(sizeof(spannable_string_t),1);
- return et;
- }
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfGetExtractedText, &result);
- parg(request);
- iarg(flags);
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
+ QQnxImfRequest imfEvent(ic, ImfSendEvent);
+ imfEvent.sae.event = event;
+ imfEvent.sae.result = -1;
+ executeIMFRequest(&imfEvent);
- result.wait();
- return result.result().value<extracted_text_t *>();
+ return imfEvent.sae.result;
}
-static spannable_string_t *imfGetSelectedText(input_session_t *ic, int32_t flags)
+// Same as ic_send_event.
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_send_async_event(input_session_t *ic, event_t *event)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return toSpannableString("");
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfGetSelectedText, &result);
- iarg(flags);
-
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
+ QQnxImfRequest imfEvent(ic, ImfSendAsyncEvent);
+ imfEvent.sae.event = event;
+ imfEvent.sae.result = -1;
+ executeIMFRequest(&imfEvent);
- result.wait();
- return result.result().value<extracted_text_t *>();
+ return imfEvent.sae.result;
}
-static spannable_string_t *imfGetTextAfterCursor(input_session_t *ic, int32_t n, int32_t flags)
+// Set the range of text between start and end as the composition range.
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_set_composing_region(input_session_t *ic, int32_t start, int32_t end)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return toSpannableString("");
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfGetTextAfterCursor, &result);
- iarg(n);
- iarg(flags);
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
+ QQnxImfRequest event(ic, ImfSetComposingRegion);
+ event.scr.start = start;
+ event.scr.end = end;
+ event.scr.result = -1;
+ executeIMFRequest(&event);
- result.wait();
- return result.result().value<extracted_text_t *>();
+ return event.scr.result;
}
-static spannable_string_t *imfGetTextBeforeCursor(input_session_t *ic, int32_t n, int32_t flags)
+// Update the composition range with the supplied text. This can be called when no composition
+// range is in effect in which case one is started at the current cursor position.
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_set_composing_text(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return toSpannableString("");
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfGetTextBeforeCursor, &result);
- iarg(n);
- iarg(flags);
+ QQnxImfRequest event(ic, ImfSetComposingText);
+ event.sct.text = text;
+ event.sct.new_cursor_position = new_cursor_position;
+ event.sct.result = -1;
+ executeIMFRequest(&event);
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
-
- result.wait();
- return result.result().value<extracted_text_t *>();
+ return event.sct.result;
}
-static int32_t imfPerformEditorAction(input_session_t *ic, int32_t editor_action)
+// Indicate if any text is selected
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_is_text_selected(input_session_t* ic, int32_t* pIsSelected)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return 0;
+ QQnxImfRequest event(ic, ImfIsTextSelected);
+ event.its.pIsSelected = pIsSelected;
+ event.its.result = -1;
+ executeIMFRequest(&event);
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfPerformEditorAction, &result);
- iarg(editor_action);
-
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
-
- result.wait();
- int32_t ret = result.result().value<int32_t>();
- return ret;
+ return event.its.result;
}
-static int32_t imfReportFullscreenMode(input_session_t *ic, int32_t enabled)
+// Indicate if all text is selected
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_is_all_text_selected(input_session_t* ic, int32_t* pIsSelected)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return 0;
+ QQnxImfRequest event(ic, ImfIsAllTextSelected);
+ event.its.pIsSelected = pIsSelected;
+ event.its.result = -1;
+ executeIMFRequest(&event);
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfReportFullscreenMode, &result);
- iarg(enabled);
-
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
-
- result.wait();
- int32_t ret = result.result().value<int32_t>();
- return ret;
+ return event.its.result;
}
-static int32_t imfSendEvent(input_session_t *ic, event_t *event)
-{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- ImfEvent *imfEvent = new ImfEvent(ic, ImfSendEvent, 0);
- imfEvent->m_args[qs("event")] = QVariant::fromValue(static_cast<void *>(event));
+// The following functions are defined in the IMF headers but are not currently called.
- QCoreApplication::postEvent(QCoreApplication::instance(), imfEvent);
+// Not currently used
+static int32_t ic_perform_editor_action(input_session_t *ic, int32_t editor_action)
+{
+ Q_UNUSED(ic);
+ Q_UNUSED(editor_action);
+ qCritical() << "ic_perform_editor_action not implemented";
return 0;
}
-static int32_t imfSendAsyncEvent(input_session_t *ic, event_t *event)
+// Not currently used
+static int32_t ic_report_fullscreen_mode(input_session_t *ic, int32_t enabled)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- ImfEvent *imfEvent = new ImfEvent(ic, ImfSendAsyncEvent, 0);
- imfEvent->m_args[qs("event")] = QVariant::fromValue(static_cast<void *>(event));
-
- QCoreApplication::postEvent(QCoreApplication::instance(), imfEvent);
+ Q_UNUSED(ic);
+ Q_UNUSED(enabled);
+ qCritical() << "ic_report_fullscreen_mode not implemented";
return 0;
}
-static int32_t imfSetComposingRegion(input_session_t *ic, int32_t start, int32_t end)
+// Not currently used
+static extracted_text_t *ic_get_extracted_text(input_session_t *ic, extracted_text_request_t *request, int32_t flags)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
+ Q_UNUSED(ic);
+ Q_UNUSED(request);
+ Q_UNUSED(flags);
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfSetComposingRegion, &result);
- iarg(start);
- iarg(end);
+ qCritical() << "ic_get_extracted_text not implemented";
+ return 0;
+}
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
+// Not currently used
+static spannable_string_t *ic_get_selected_text(input_session_t *ic, int32_t flags)
+{
+ Q_UNUSED(ic);
+ Q_UNUSED(flags);
- result.wait();
- int32_t ret = result.result().value<int32_t>();
- return ret;
+ qCritical() << "ic_get_selected_text not implemented";
+ return 0;
}
-static int32_t imfSetComposingText(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position)
+// Not currently used
+static int32_t ic_get_cursor_caps_mode(input_session_t *ic, int32_t req_modes)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
+ Q_UNUSED(ic);
+ Q_UNUSED(req_modes);
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfSetComposingText, &result);
- parg(text);
- iarg(new_cursor_position);
+ qCritical() << "ic_get_cursor_caps_mode not implemented";
+ return 0;
+}
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
+// Not currently used
+static int32_t ic_clear_meta_key_states(input_session_t *ic, int32_t states)
+{
+ Q_UNUSED(ic);
+ Q_UNUSED(states);
- result.wait();
- int32_t ret = result.result().value<int32_t>();
- return ret;
+ qCritical() << "ic_clear_meta_key_states not implemented";
+ return 0;
}
-static int32_t imfSetSelection(input_session_t *ic, int32_t start, int32_t end)
+// Not currently used
+static int32_t ic_set_selection(input_session_t *ic, int32_t start, int32_t end)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
+ Q_UNUSED(ic);
+ Q_UNUSED(start);
+ Q_UNUSED(end);
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfSetSelection, &result);
- iarg(start);
- iarg(end);
+ qCritical() << "ic_set_selection not implemented";
+ return 0;
+}
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
- result.wait();
- int32_t ret = result.result().value<int32_t>();
- return ret;
-}
static connection_interface_t ic_funcs = {
- imfBeginBatchEdit,
- imfClearMetaKeyStates,
- imfCommitText,
- imfDeleteSurroundingText,
- imfEndBatchEdit,
- imfFinishComposingText,
- imfGetCursorCapsMode,
- imfGetCursorPosition,
- imfGetExtractedText,
- imfGetSelectedText,
- imfGetTextAfterCursor,
- imfGetTextBeforeCursor,
- imfPerformEditorAction,
- imfReportFullscreenMode,
- NULL, //ic_send_key_event
- imfSendEvent,
- imfSendAsyncEvent,
- imfSetComposingRegion,
- imfSetComposingText,
- imfSetSelection,
- NULL, //ic_set_candidates,
+ ic_begin_batch_edit,
+ ic_clear_meta_key_states,
+ ic_commit_text,
+ ic_delete_surrounding_text,
+ ic_end_batch_edit,
+ ic_finish_composing_text,
+ ic_get_cursor_caps_mode,
+ ic_get_cursor_position,
+ ic_get_extracted_text,
+ ic_get_selected_text,
+ ic_get_text_after_cursor,
+ ic_get_text_before_cursor,
+ ic_perform_editor_action,
+ ic_report_fullscreen_mode,
+ 0, //ic_send_key_event
+ ic_send_event,
+ ic_send_async_event,
+ ic_set_composing_region,
+ ic_set_composing_text,
+ ic_set_selection,
+ 0, //ic_set_candidates,
+ 0, //ic_get_cursor_offset,
+ 0, //ic_get_selection,
+ ic_is_text_selected,
+ ic_is_all_text_selected,
+ 0, //ic_get_max_cursor_offset_t
};
+} // namespace
+
static void
-initEvent(event_t *pEvent, const input_session_t *pSession, EventType eventType, int eventId)
+initEvent(event_t *pEvent, const input_session_t *pSession, EventType eventType, int eventId, int eventSize)
{
static int s_transactionId;
// Make sure structure is squeaky clean since it's not clear just what is significant.
- memset(pEvent, 0, sizeof(event_t));
+ memset(pEvent, 0, eventSize);
pEvent->event_type = eventType;
pEvent->event_id = eventId;
pEvent->pid = getpid();
@@ -566,7 +493,7 @@ initEvent(event_t *pEvent, const input_session_t *pSession, EventType eventType,
pEvent->transaction_id = ++s_transactionId;
}
-spannable_string_t *toSpannableString(const QString &text)
+static spannable_string_t *toSpannableString(const QString &text)
{
qInputContextDebug() << Q_FUNC_INFO << text;
@@ -579,8 +506,7 @@ spannable_string_t *toSpannableString(const QString &text)
const QChar *pData = text.constData();
wchar_t *pDst = pString->str;
- while (!pData->isNull())
- {
+ while (!pData->isNull()) {
*pDst = pData->unicode();
pDst++;
pData++;
@@ -590,7 +516,6 @@ spannable_string_t *toSpannableString(const QString &text)
return pString;
}
-} // namespace
static const input_session_t *(*p_ictrl_open_session)(connection_interface_t *) = 0;
static void (*p_ictrl_close_session)(input_session_t *) = 0;
@@ -645,27 +570,29 @@ QT_BEGIN_NAMESPACE
QQnxInputContext::QQnxInputContext(QQnxIntegration *integration, QQnxAbstractVirtualKeyboard &keyboard) :
QPlatformInputContext(),
- m_lastCaretPos(0),
+ m_caretPosition(0),
m_isComposing(false),
+ m_isUpdatingText(false),
m_inputPanelVisible(false),
m_inputPanelLocale(QLocale::c()),
m_integration(integration),
- m_virtualKeyboad(keyboard)
+ m_virtualKeyboard(keyboard)
{
qInputContextDebug() << Q_FUNC_INFO;
if (!imfAvailable())
return;
- if ( p_imf_client_init() != 0 ) {
+ // Save a pointer to ourselves so we can execute calls from IMF through executeIMFRequest
+ // In practice there will only ever be a single instance.
+ Q_ASSERT(sInputContextInstance == 0);
+ sInputContextInstance = this;
+
+ if (p_imf_client_init() != 0) {
s_imfInitFailed = true;
qCritical("imf_client_init failed - IMF services will be unavailable");
}
- QCoreApplication::instance()->installEventFilter(this);
-
- // p_vkb_init_selection_service();
-
connect(&keyboard, SIGNAL(visibilityChanged(bool)), this, SLOT(keyboardVisibilityChanged(bool)));
connect(&keyboard, SIGNAL(localeChanged(QLocale)), this, SLOT(keyboardLocaleChanged(QLocale)));
keyboardVisibilityChanged(keyboard.isVisible());
@@ -676,168 +603,74 @@ QQnxInputContext::~QQnxInputContext()
{
qInputContextDebug() << Q_FUNC_INFO;
+ Q_ASSERT(sInputContextInstance == this);
+ sInputContextInstance = 0;
+
if (!imfAvailable())
return;
- QCoreApplication::instance()->removeEventFilter(this);
p_imf_client_disconnect();
}
-#define getarg(type, name) type name = imfEvent->mArgs[qs(#name)].value<type>()
-#define getparg(type, name) type name = (type)(imfEvent->mArgs[qs(#name)].value<void*>())
-
bool QQnxInputContext::isValid() const
{
return imfAvailable();
}
-bool QQnxInputContext::eventFilter(QObject *obj, QEvent *event)
+void QQnxInputContext::processImfEvent(QQnxImfRequest *imfEvent)
{
- if (event->type() == ImfEvent::sUserEventType) {
- // Forward the event to our real handler.
- ImfEvent *imfEvent = static_cast<ImfEvent *>(event);
- switch (imfEvent->m_imfType) {
- case ImfBeginBatchEdit: {
- int32_t ret = onBeginBatchEdit(imfEvent->m_session);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
-
- case ImfClearMetaKeyStates: {
- getarg(int32_t, states);
- int32_t ret = onClearMetaKeyStates(imfEvent->m_session, states);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
-
- case ImfCommitText: {
- getparg(spannable_string_t*, text);
- getarg(int32_t, new_cursor_position);
- int32_t ret = onCommitText(imfEvent->m_session, text, new_cursor_position);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
-
- case ImfDeleteSurroundingText: {
- getarg(int32_t, left_length);
- getarg(int32_t, right_length);
- int32_t ret = onDeleteSurroundingText(imfEvent->m_session, left_length, right_length);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
-
- case ImfEndBatchEdit: {
- int32_t ret = onEndBatchEdit(imfEvent->m_session);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
-
- case ImfFinishComposingText: {
- int32_t ret = onFinishComposingText(imfEvent->m_session);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
-
- case ImfGetCursorCapsMode: {
- getarg(int32_t, req_modes);
- int32_t ret = onGetCursorCapsMode(imfEvent->m_session, req_modes);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
-
- case ImfGetCursorPosition: {
- int32_t ret = onGetCursorPosition(imfEvent->m_session);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
-
- case ImfGetExtractedText: {
- getparg(extracted_text_request_t*, request);
- getarg(int32_t, flags);
- extracted_text_t *ret = onGetExtractedText(imfEvent->m_session, request, flags);
- imfEvent->m_result->setResult(QVariant::fromValue(static_cast<void *>(ret)));
- break;
- }
+ // If input session is no longer current, just bail, imfEvent should already be set with the appropriate
+ // return value.
+ if (!isSessionOkay(imfEvent->session))
+ return;
- case ImfGetSelectedText: {
- getarg(int32_t, flags);
- spannable_string_t *ret = onGetSelectedText(imfEvent->m_session, flags);
- imfEvent->m_result->setResult(QVariant::fromValue(static_cast<void *>(ret)));
- break;
- }
+ switch (imfEvent->type) {
+ case ImfCommitText:
+ imfEvent->ct.result = onCommitText(imfEvent->ct.text, imfEvent->ct.new_cursor_position);
+ break;
- case ImfGetTextAfterCursor: {
- getarg(int32_t, n);
- getarg(int32_t, flags);
- spannable_string_t *ret = onGetTextAfterCursor(imfEvent->m_session, n, flags);
- imfEvent->m_result->setResult(QVariant::fromValue(static_cast<void *>(ret)));
- break;
- }
+ case ImfDeleteSurroundingText:
+ imfEvent->dst.result = onDeleteSurroundingText(imfEvent->dst.left_length, imfEvent->dst.right_length);
+ break;
- case ImfGetTextBeforeCursor: {
- getarg(int32_t, n);
- getarg(int32_t, flags);
- spannable_string_t *ret = onGetTextBeforeCursor(imfEvent->m_session, n, flags);
- imfEvent->m_result->setResult(QVariant::fromValue((void*)ret));
- break;
- }
+ case ImfFinishComposingText:
+ imfEvent->fct.result = onFinishComposingText();
+ break;
- case ImfPerformEditorAction: {
- getarg(int32_t, editor_action);
- int32_t ret = onPerformEditorAction(imfEvent->m_session, editor_action);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
+ case ImfGetCursorPosition:
+ imfEvent->gcp.result = onGetCursorPosition();
+ break;
- case ImfReportFullscreenMode: {
- getarg(int32_t, enabled);
- int32_t ret = onReportFullscreenMode(imfEvent->m_session, enabled);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
+ case ImfGetTextAfterCursor:
+ imfEvent->gtac.result = onGetTextAfterCursor(imfEvent->gtac.n, imfEvent->gtac.flags);
+ break;
- case ImfSendEvent: {
- getparg(event_t*, event);
- onSendEvent(imfEvent->m_session, event);
- break;
- }
+ case ImfGetTextBeforeCursor:
+ imfEvent->gtac.result = onGetTextBeforeCursor(imfEvent->gtac.n, imfEvent->gtac.flags);
+ break;
- case ImfSendAsyncEvent: {
- getparg(event_t*, event);
- onSendAsyncEvent(imfEvent->m_session, event);
- break;
- }
+ // Don't need to distinguish these two cases
+ case ImfSendEvent:
+ case ImfSendAsyncEvent:
+ imfEvent->sae.result = onSendEvent(imfEvent->sae.event);
+ break;
- case ImfSetComposingRegion: {
- getarg(int32_t, start);
- getarg(int32_t, end);
- int32_t ret = onSetComposingRegion(imfEvent->m_session, start, end);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
+ case ImfSetComposingRegion:
+ imfEvent->scr.result = onSetComposingRegion(imfEvent->scr.start, imfEvent->scr.end);
+ break;
- case ImfSetComposingText: {
- getparg(spannable_string_t*, text);
- getarg(int32_t, new_cursor_position);
- int32_t ret = onSetComposingText(imfEvent->m_session, text, new_cursor_position);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
+ case ImfSetComposingText:
+ imfEvent->sct.result = onSetComposingText(imfEvent->sct.text, imfEvent->sct.new_cursor_position);
+ break;
- case ImfSetSelection: {
- getarg(int32_t, start);
- getarg(int32_t, end);
- int32_t ret = onSetSelection(imfEvent->m_session, start, end);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
- }; //switch
+ case ImfIsTextSelected:
+ imfEvent->its.result = onIsTextSelected(imfEvent->its.pIsSelected);
+ break;
- return true;
- } else {
- // standard event processing
- return QObject::eventFilter(obj, event);
- }
+ case ImfIsAllTextSelected:
+ imfEvent->its.result = onIsAllTextSelected(imfEvent->its.pIsSelected);
+ break;
+ }; //switch
}
bool QQnxInputContext::filterEvent( const QEvent *event )
@@ -845,12 +678,12 @@ bool QQnxInputContext::filterEvent( const QEvent *event )
qInputContextDebug() << Q_FUNC_INFO << event;
switch (event->type()) {
- case QEvent::CloseSoftwareInputPanel: {
+ case QEvent::CloseSoftwareInputPanel:
return dispatchCloseSoftwareInputPanel();
- }
- case QEvent::RequestSoftwareInputPanel: {
+
+ case QEvent::RequestSoftwareInputPanel:
return dispatchRequestSoftwareInputPanel();
- }
+
default:
return false;
}
@@ -869,12 +702,30 @@ void QQnxInputContext::reset()
endComposition();
}
-void QQnxInputContext::update(Qt::InputMethodQueries queries)
+void QQnxInputContext::commit()
{
qInputContextDebug() << Q_FUNC_INFO;
- reset();
+ endComposition();
+}
- QPlatformInputContext::update(queries);
+void QQnxInputContext::update(Qt::InputMethodQueries queries)
+{
+ qInputContextDebug() << Q_FUNC_INFO << queries;
+
+ if (queries & Qt::ImCursorPosition) {
+ int lastCaret = m_caretPosition;
+ updateCursorPosition();
+ // If caret position has changed we need to inform IMF unless this is just due to our own action
+ // such as committing text.
+ if (hasSession() && !m_isUpdatingText && lastCaret != m_caretPosition) {
+ caret_event_t caretEvent;
+ initEvent(&caretEvent.event, sInputSession, EVENT_CARET, CARET_POS_CHANGED, sizeof(caretEvent));
+ caretEvent.old_pos = lastCaret;
+ caretEvent.new_pos = m_caretPosition;
+ qInputContextDebug() << Q_FUNC_INFO << "ictrl_dispatch_event caret changed" << lastCaret << m_caretPosition;
+ p_ictrl_dispatch_event(&caretEvent.event);
+ }
+ }
}
void QQnxInputContext::closeSession()
@@ -887,16 +738,23 @@ void QQnxInputContext::closeSession()
p_ictrl_close_session((input_session_t *)sInputSession);
sInputSession = 0;
}
+ // These are likely already in the right state but this depends on the text control
+ // having called reset or commit. So, just in case, set them to proper values.
+ m_isComposing = false;
+ m_composingText.clear();
}
-void QQnxInputContext::openSession()
+bool QQnxInputContext::openSession()
{
- qInputContextDebug() << Q_FUNC_INFO;
if (!imfAvailable())
- return;
+ return false;
closeSession();
sInputSession = p_ictrl_open_session(&ic_funcs);
+
+ qInputContextDebug() << Q_FUNC_INFO;
+
+ return sInputSession != 0;
}
bool QQnxInputContext::hasSession()
@@ -918,79 +776,89 @@ bool QQnxInputContext::hasSelectedText()
bool QQnxInputContext::dispatchRequestSoftwareInputPanel()
{
+ qInputContextDebug() << Q_FUNC_INFO << "requesting keyboard" << m_inputPanelVisible;
m_virtualKeyboard.showKeyboard();
- qInputContextDebug() << Q_FUNC_INFO << "requesting virtual keyboard";
- QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input || !inputMethodAccepted())
- return true;
-
- if (!hasSession())
- openSession();
- // This also means that the caret position has moved
- QInputMethodQueryEvent query(Qt::ImCursorPosition);
- QCoreApplication::sendEvent(input, &query);
- int caretPos = query.value(Qt::ImCursorPosition).toInt();
- caret_event_t caretEvent;
- memset(&caretEvent, 0, sizeof(caret_event_t));
- initEvent(&caretEvent.event, sInputSession, EVENT_CARET, CARET_POS_CHANGED);
- caretEvent.old_pos = m_lastCaretPos;
- m_lastCaretPos = caretEvent.new_pos = caretPos;
- p_ictrl_dispatch_event((event_t *)&caretEvent);
return true;
}
bool QQnxInputContext::dispatchCloseSoftwareInputPanel()
{
+ qInputContextDebug() << Q_FUNC_INFO << "hiding keyboard" << m_inputPanelVisible;
m_virtualKeyboard.hideKeyboard();
- qInputContextDebug() << Q_FUNC_INFO << "hiding virtual keyboard";
- // This also means we are stopping composition, but we should already have done that.
return true;
}
/**
* IMF Event Dispatchers.
*/
-bool QQnxInputContext::dispatchFocusEvent(FocusEventId id, int hints)
+bool QQnxInputContext::dispatchFocusGainEvent(int inputHints)
{
- qInputContextDebug() << Q_FUNC_INFO;
+ if (hasSession())
+ dispatchFocusLossEvent();
- if (!sInputSession) {
- qWarning() << Q_FUNC_INFO << "Attempt to dispatch a focus event with no input session.";
- return false;
- }
+ QObject *input = qGuiApp->focusObject();
- if (!imfAvailable())
+ if (!input || !openSession())
return false;
// Set the last caret position to 0 since we don't really have one and we don't
// want to have the old one.
- m_lastCaretPos = 0;
+ m_caretPosition = 0;
+
+ QInputMethodQueryEvent query(Qt::ImHints);
+ QCoreApplication::sendEvent(input, &query);
focus_event_t focusEvent;
- memset(&focusEvent, 0, sizeof(focusEvent));
- initEvent(&focusEvent.event, sInputSession, EVENT_FOCUS, id);
+ initEvent(&focusEvent.event, sInputSession, EVENT_FOCUS, FOCUS_GAINED, sizeof(focusEvent));
focusEvent.style = DEFAULT_STYLE;
- if (hints && Qt::ImhNoPredictiveText)
+ if (inputHints & Qt::ImhNoPredictiveText)
focusEvent.style |= NO_PREDICTION | NO_AUTO_CORRECTION;
- if (hints && Qt::ImhNoAutoUppercase)
+ if (inputHints & Qt::ImhNoAutoUppercase)
focusEvent.style |= NO_AUTO_TEXT;
+ // Following styles are mutually exclusive
+ if (inputHints & Qt::ImhHiddenText) {
+ focusEvent.style |= IMF_PASSWORD_TYPE;
+ } else if (inputHints & Qt::ImhDialableCharactersOnly) {
+ focusEvent.style |= IMF_PHONE_TYPE;
+ } else if (inputHints & Qt::ImhUrlCharactersOnly) {
+ focusEvent.style |= IMF_URL_TYPE;
+ } else if (inputHints & Qt::ImhEmailCharactersOnly) {
+ focusEvent.style |= IMF_EMAIL_TYPE;
+ }
+
+ qInputContextDebug() << Q_FUNC_INFO << "ictrl_dispatch_event focus gain style:" << focusEvent.style;
+
p_ictrl_dispatch_event((event_t *)&focusEvent);
return true;
}
-bool QQnxInputContext::handleKeyboardEvent(int flags, int sym, int mod, int scan, int cap)
+void QQnxInputContext::dispatchFocusLossEvent()
{
- if (!imfAvailable())
+ if (hasSession()) {
+ qInputContextDebug() << Q_FUNC_INFO << "ictrl_dispatch_event focus lost";
+
+ focus_event_t focusEvent;
+ initEvent(&focusEvent.event, sInputSession, EVENT_FOCUS, FOCUS_LOST, sizeof(focusEvent));
+ p_ictrl_dispatch_event((event_t *)&focusEvent);
+ closeSession();
+ }
+}
+
+bool QQnxInputContext::handleKeyboardEvent(int flags, int sym, int mod, int scan, int cap, int sequenceId)
+{
+ Q_UNUSED(scan);
+
+ if (!hasSession())
return false;
int key = (flags & KEY_SYM_VALID) ? sym : cap;
- bool navKey = false;
- switch ( key ) {
+ bool navigationKey = false;
+ switch (key) {
case KEYCODE_RETURN:
/* In a single line edit we should end composition because enter might be used by something.
endComposition();
@@ -1007,27 +875,22 @@ bool QQnxInputContext::handleKeyboardEvent(int flags, int sym, int mod, int scan
break;
case KEYCODE_LEFT:
key = NAVIGATE_LEFT;
- navKey = true;
+ navigationKey = true;
break;
case KEYCODE_RIGHT:
key = NAVIGATE_RIGHT;
- navKey = true;
+ navigationKey = true;
break;
case KEYCODE_UP:
key = NAVIGATE_UP;
- navKey = true;
+ navigationKey = true;
break;
case KEYCODE_DOWN:
key = NAVIGATE_DOWN;
- navKey = true;
+ navigationKey = true;
break;
- case KEYCODE_CAPS_LOCK:
- case KEYCODE_LEFT_SHIFT:
- case KEYCODE_RIGHT_SHIFT:
case KEYCODE_LEFT_CTRL:
case KEYCODE_RIGHT_CTRL:
- case KEYCODE_LEFT_ALT:
- case KEYCODE_RIGHT_ALT:
case KEYCODE_MENU:
case KEYCODE_LEFT_HYPER:
case KEYCODE_RIGHT_HYPER:
@@ -1041,85 +904,135 @@ bool QQnxInputContext::handleKeyboardEvent(int flags, int sym, int mod, int scan
break;
}
- if ( mod & KEYMOD_CTRL ) {
- // If CTRL is pressed, just let AIR handle it. But terminate any composition first
- //endComposition();
- return false;
- }
-
// Pass the keys we don't know about on through
if ( key == 0 )
return false;
- // IMF doesn't need key releases so just swallow them.
- if (!(flags & KEY_DOWN))
- return true;
-
- if ( navKey ) {
+ if (navigationKey) {
// Even if we're forwarding up events, we can't do this for
// navigation keys.
if ( flags & KEY_DOWN ) {
navigation_event_t navEvent;
- initEvent(&navEvent.event, sInputSession, EVENT_NAVIGATION, key);
+ initEvent(&navEvent.event, sInputSession, EVENT_NAVIGATION, key, sizeof(navEvent));
navEvent.magnitude = 1;
- qInputContextDebug() << Q_FUNC_INFO << "dispatch navigation event " << key;
+ qInputContextDebug() << Q_FUNC_INFO << "ictrl_dispatch_even navigation" << key;
p_ictrl_dispatch_event(&navEvent.event);
}
- }
- else {
+ } else {
key_event_t keyEvent;
- initEvent(&keyEvent.event, sInputSession, EVENT_KEY, flags & KEY_DOWN ? IMF_KEY_DOWN : IMF_KEY_UP);
- keyEvent.key_code = key;
- keyEvent.character = 0;
- keyEvent.meta_key_state = 0;
+ initEvent(&keyEvent.event, sInputSession, EVENT_KEY, flags & KEY_DOWN ? IMF_KEY_DOWN : IMF_KEY_UP,
+ sizeof(keyEvent));
+ keyEvent.key_code = cap;
+ keyEvent.character = sym;
+ keyEvent.meta_key_state = mod;
+ keyEvent.sequence_id = sequenceId;
p_ictrl_dispatch_event(&keyEvent.event);
- qInputContextDebug() << Q_FUNC_INFO << "dispatch key event " << key;
+ qInputContextDebug() << Q_FUNC_INFO << "ictrl_dispatch_even key" << key;
}
- scan = 0;
return true;
}
+void QQnxInputContext::updateCursorPosition()
+{
+ QObject *input = qGuiApp->focusObject();
+ if (!input)
+ return;
+
+ QInputMethodQueryEvent query(Qt::ImCursorPosition);
+ QCoreApplication::sendEvent(input, &query);
+ m_caretPosition = query.value(Qt::ImCursorPosition).toInt();
+
+ qInputContextDebug() << Q_FUNC_INFO << m_caretPosition;
+}
+
void QQnxInputContext::endComposition()
{
if (!m_isComposing)
return;
- QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
- return;
+ finishComposingText();
+
+ if (hasSession()) {
+ action_event_t actionEvent;
+ initEvent(&actionEvent.event, sInputSession, EVENT_ACTION, ACTION_END_COMPOSITION, sizeof(actionEvent));
+ qInputContextDebug() << Q_FUNC_INFO << "ictrl_dispatch_even end composition";
+ p_ictrl_dispatch_event(&actionEvent.event);
+ }
+}
+
+void QQnxInputContext::updateComposition(spannable_string_t *text, int32_t new_cursor_position)
+{
+ QObject *input = qGuiApp->focusObject();
+ if (!input)
+ return;
+
+ if (new_cursor_position > 0)
+ new_cursor_position += text->length - 1;
+
+ m_composingText = QString::fromWCharArray(text->str, text->length);
+ m_isComposing = true;
+
+ qInputContextDebug() << Q_FUNC_INFO << m_composingText << new_cursor_position;
QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent event(QLatin1String(""), attributes);
- event.setCommitString(m_composingText);
- m_composingText = QString();
- m_isComposing = false;
+ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
+ new_cursor_position,
+ 1,
+ QVariant()));
+
+ for (unsigned int i = 0; i < text->spans_count; ++i) {
+ const uint64_t knownMask = ACTIVE_REGION_ATTRIB | COMPOSED_TEXT_ATTRIB |
+ AUTO_CORRECTION_ATTRIB | REVERT_CORRECTION_ATTRIB;
+ bool selected = (text->spans[i].attributes_mask & ACTIVE_REGION_ATTRIB) != 0;
+ bool converted = (text->spans[i].attributes_mask & COMPOSED_TEXT_ATTRIB) != 0;
+ bool autoCorrected = (text->spans[i].attributes_mask & AUTO_CORRECTION_ATTRIB) != 0;
+ bool reverted = (text->spans[i].attributes_mask & REVERT_CORRECTION_ATTRIB) != 0;
+
+ if (text->spans[i].attributes_mask & knownMask) {
+ QTextCharFormat format;
+
+ if (converted || selected) {
+ format.setFontUnderline(true);
+ }
+ if (selected) {
+ format.setBackground(QBrush(sSelectedColor));
+ } else if (autoCorrected) {
+ format.setBackground(QBrush(sAutoCorrectedColor));
+ } else if (reverted) {
+ format.setBackground(QBrush(sRevertedColor));
+ }
+ qInputContextDebug() << "attrib:" << selected << converted << text->spans[i].start << text->spans[i].end;
+ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, text->spans[i].start,
+ text->spans[i].end - text->spans[i].start + 1, QVariant(format)));
+ }
+ }
+ QInputMethodEvent event(m_composingText, attributes);
+ m_isUpdatingText = true;
QCoreApplication::sendEvent(input, &event);
+ m_isUpdatingText = false;
- action_event_t actionEvent;
- memset(&actionEvent, 0, sizeof(actionEvent));
- initEvent(&actionEvent.event, sInputSession, EVENT_ACTION, ACTION_END_COMPOSITION);
- p_ictrl_dispatch_event(&actionEvent.event);
+ updateCursorPosition();
}
-void QQnxInputContext::setComposingText(QString const& composingText)
+void QQnxInputContext::finishComposingText()
{
- m_composingText = composingText;
- m_isComposing = true;
-
QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
- return;
- QList<QInputMethodEvent::Attribute> attributes;
- QTextCharFormat format;
- format.setFontUnderline(true);
- attributes.push_back(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, composingText.length(), format));
+ if (input) {
+ qInputContextDebug() << Q_FUNC_INFO << m_composingText;
- QInputMethodEvent event(composingText, attributes);
+ QInputMethodEvent event;
+ event.setCommitString(m_composingText);
+ m_isUpdatingText = true;
+ QCoreApplication::sendEvent(input, &event);
+ m_isUpdatingText = false;
+ }
+ m_composingText = QString();
+ m_isComposing = false;
- QCoreApplication::sendEvent(input, &event);
+ updateCursorPosition();
}
int32_t QQnxInputContext::processEvent(event_t *event)
@@ -1140,19 +1053,24 @@ int32_t QQnxInputContext::processEvent(event_t *event)
event->event_id == NAVIGATE_LEFT ? KEYCODE_LEFT :
event->event_id == NAVIGATE_RIGHT ? KEYCODE_RIGHT : 0;
- QQnxEventThread::injectKeyboardEvent(KEY_DOWN | KEY_CAP_VALID, key, 0, 0, 0);
- QQnxEventThread::injectKeyboardEvent(KEY_CAP_VALID, key, 0, 0, 0);
+ QQnxScreenEventHandler::injectKeyboardEvent(KEY_DOWN | KEY_CAP_VALID, key, 0, 0, 0);
+ QQnxScreenEventHandler::injectKeyboardEvent(KEY_CAP_VALID, key, 0, 0, 0);
result = 0;
break;
}
case EVENT_KEY: {
- qInputContextDebug() << Q_FUNC_INFO << "EVENT_KEY";
- key_event_t *kevent = static_cast<key_event_t *>(event);
-
- QQnxEventThread::injectKeyboardEvent(KEY_DOWN | KEY_SYM_VALID | KEY_CAP_VALID, kevent->key_code, 0, 0, kevent->key_code);
- QQnxEventThread::injectKeyboardEvent(KEY_SYM_VALID | KEY_CAP_VALID, kevent->key_code, 0, 0, kevent->key_code);
-
+ key_event_t *kevent = reinterpret_cast<key_event_t *>(event);
+ int keySym = kevent->character != 0 ? kevent->character : kevent->key_code;
+ int keyCap = kevent->key_code;
+ int modifiers = 0;
+ if (kevent->meta_key_state & META_SHIFT_ON)
+ modifiers |= KEYMOD_SHIFT;
+ int flags = KEY_SYM_VALID | KEY_CAP_VALID;
+ if (event->event_id == IMF_KEY_DOWN)
+ flags |= KEY_DOWN;
+ qInputContextDebug() << Q_FUNC_INFO << "EVENT_KEY" << flags << keySym;
+ QQnxScreenEventHandler::injectKeyboardEvent(flags, keySym, modifiers, 0, keyCap);
result = 0;
break;
}
@@ -1179,339 +1097,182 @@ int32_t QQnxInputContext::processEvent(event_t *event)
* IMF Event Handlers
*/
-int32_t QQnxInputContext::onBeginBatchEdit(input_session_t *ic)
-{
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- // We don't care.
- return 0;
-}
-
-int32_t QQnxInputContext::onClearMetaKeyStates(input_session_t *ic, int32_t states)
+int32_t QQnxInputContext::onCommitText(spannable_string_t *text, int32_t new_cursor_position)
{
- Q_UNUSED(states);
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- // Should never get called.
- qCritical() << Q_FUNC_INFO << "onClearMetaKeyStates is unsupported.";
- return 0;
-}
-
-int32_t QQnxInputContext::onCommitText(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position)
-{
- Q_UNUSED(new_cursor_position); // TODO: How can we set the cursor position it's not part of the API.
- if (!isSessionOkay(ic))
- return 0;
-
- QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
- return 0;
-
- QString commitString = QString::fromWCharArray(text->str, text->length);
-
- qInputContextDebug() << Q_FUNC_INFO << "Committing [" << commitString << "]";
-
- QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent event(QLatin1String(""), attributes);
- event.setCommitString(commitString, 0, 0);
+ Q_UNUSED(new_cursor_position);
- QCoreApplication::sendEvent(input, &event);
- m_composingText = QString();
+ m_composingText = QString::fromWCharArray(text->str, text->length);
+ finishComposingText();
return 0;
}
-int32_t QQnxInputContext::onDeleteSurroundingText(input_session_t *ic, int32_t left_length, int32_t right_length)
+int32_t QQnxInputContext::onDeleteSurroundingText(int32_t left_length, int32_t right_length)
{
qInputContextDebug() << Q_FUNC_INFO << "L:" << left_length << " R:" << right_length;
- if (!isSessionOkay(ic))
- return 0;
-
QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
- return 0;
-
- if (hasSelectedText()) {
- QQnxEventThread::injectKeyboardEvent(KEY_DOWN | KEY_CAP_VALID, KEYCODE_DELETE, 0, 0, 0);
- QQnxEventThread::injectKeyboardEvent(KEY_CAP_VALID, KEYCODE_DELETE, 0, 0, 0);
- reset();
+ if (!input)
return 0;
- }
int replacementLength = left_length + right_length;
int replacementStart = -left_length;
- QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent event(QLatin1String(""), attributes);
- event.setCommitString(QLatin1String(""), replacementStart, replacementLength);
- QCoreApplication::sendEvent(input, &event);
-
- return 0;
-}
-
-int32_t QQnxInputContext::onEndBatchEdit(input_session_t *ic)
-{
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- return 0;
-}
+ finishComposingText();
-int32_t QQnxInputContext::onFinishComposingText(input_session_t *ic)
-{
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
- return 0;
-
- // Only update the control, no need to send a message back to imf (don't call
- // end composition)
- QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent event(QLatin1String(""), attributes);
- event.setCommitString(m_composingText);
- m_composingText = QString();
- m_isComposing = false;
+ QInputMethodEvent event;
+ event.setCommitString(QString(), replacementStart, replacementLength);
+ m_isUpdatingText = true;
QCoreApplication::sendEvent(input, &event);
+ m_isUpdatingText = false;
- return 0;
-}
-
-int32_t QQnxInputContext::onGetCursorCapsMode(input_session_t *ic, int32_t req_modes)
-{
- Q_UNUSED(req_modes);
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- // Should never get called.
- qCritical() << Q_FUNC_INFO << "onGetCursorCapsMode is unsupported.";
+ updateCursorPosition();
return 0;
}
-int32_t QQnxInputContext::onGetCursorPosition(input_session_t *ic)
-{
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
- return 0;
-
- QInputMethodQueryEvent query(Qt::ImCursorPosition);
- QCoreApplication::sendEvent(input, &query);
- m_lastCaretPos = query.value(Qt::ImCursorPosition).toInt();
-
- return m_lastCaretPos;
-}
-
-extracted_text_t *QQnxInputContext::onGetExtractedText(input_session_t *ic, extracted_text_request_t *request, int32_t flags)
+int32_t QQnxInputContext::onFinishComposingText()
{
- Q_UNUSED(flags);
- Q_UNUSED(request);
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic)) {
- extracted_text_t *et = (extracted_text_t *)calloc(sizeof(extracted_text_t),1);
- et->text = reinterpret_cast<spannable_string_t *>(calloc(sizeof(spannable_string_t),1));
- return et;
- }
+ finishComposingText();
- // Used to update dictionaries, but not supported right now.
- extracted_text_t *et = (extracted_text_t *)calloc(sizeof(extracted_text_t),1);
- et->text = reinterpret_cast<spannable_string_t *>(calloc(sizeof(spannable_string_t),1));
-
- return et;
+ return 0;
}
-spannable_string_t *QQnxInputContext::onGetSelectedText(input_session_t *ic, int32_t flags)
+int32_t QQnxInputContext::onGetCursorPosition()
{
- Q_UNUSED(flags);
qInputContextDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return toSpannableString("");
-
QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
+ if (!input)
return 0;
- QInputMethodQueryEvent query(Qt::ImCurrentSelection);
- QCoreApplication::sendEvent(input, &query);
- QString text = query.value(Qt::ImCurrentSelection).toString();
+ updateCursorPosition();
- return toSpannableString(text);
+ return m_caretPosition;
}
-spannable_string_t *QQnxInputContext::onGetTextAfterCursor(input_session_t *ic, int32_t n, int32_t flags)
+spannable_string_t *QQnxInputContext::onGetTextAfterCursor(int32_t n, int32_t flags)
{
Q_UNUSED(flags);
qInputContextDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return toSpannableString("");
-
QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
+ if (!input)
return toSpannableString("");
QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImSurroundingText);
QCoreApplication::sendEvent(input, &query);
QString text = query.value(Qt::ImSurroundingText).toString();
- m_lastCaretPos = query.value(Qt::ImCursorPosition).toInt();
+ m_caretPosition = query.value(Qt::ImCursorPosition).toInt();
- return toSpannableString(text.mid(m_lastCaretPos+1, n));
+ return toSpannableString(text.mid(m_caretPosition, n));
}
-spannable_string_t *QQnxInputContext::onGetTextBeforeCursor(input_session_t *ic, int32_t n, int32_t flags)
+spannable_string_t *QQnxInputContext::onGetTextBeforeCursor(int32_t n, int32_t flags)
{
Q_UNUSED(flags);
qInputContextDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return toSpannableString("");
-
QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
+ if (!input)
return toSpannableString("");
QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImSurroundingText);
QCoreApplication::sendEvent(input, &query);
QString text = query.value(Qt::ImSurroundingText).toString();
- m_lastCaretPos = query.value(Qt::ImCursorPosition).toInt();
+ m_caretPosition = query.value(Qt::ImCursorPosition).toInt();
- if (n < m_lastCaretPos)
- return toSpannableString(text.mid(m_lastCaretPos - n, n));
+ if (n < m_caretPosition)
+ return toSpannableString(text.mid(m_caretPosition - n, n));
else
- return toSpannableString(text.mid(0, m_lastCaretPos));
-}
-
-int32_t QQnxInputContext::onPerformEditorAction(input_session_t *ic, int32_t editor_action)
-{
- Q_UNUSED(editor_action);
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- // Should never get called.
- qCritical() << Q_FUNC_INFO << "onPerformEditorAction is unsupported.";
-
- return 0;
+ return toSpannableString(text.mid(0, m_caretPosition));
}
-int32_t QQnxInputContext::onReportFullscreenMode(input_session_t *ic, int32_t enabled)
+int32_t QQnxInputContext::onSendEvent(event_t *event)
{
- Q_UNUSED(enabled);
qInputContextDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return 0;
-
- // Should never get called.
- qCritical() << Q_FUNC_INFO << "onReportFullscreenMode is unsupported.";
-
- return 0;
-}
-
-int32_t QQnxInputContext::onSendEvent(input_session_t *ic, event_t *event)
-{
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
return processEvent(event);
}
-int32_t QQnxInputContext::onSendAsyncEvent(input_session_t *ic, event_t *event)
+int32_t QQnxInputContext::onSendAsyncEvent(event_t *event)
{
qInputContextDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return 0;
-
return processEvent(event);
}
-int32_t QQnxInputContext::onSetComposingRegion(input_session_t *ic, int32_t start, int32_t end)
+int32_t QQnxInputContext::onSetComposingRegion(int32_t start, int32_t end)
{
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
+ if (!input)
return 0;
QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImSurroundingText);
QCoreApplication::sendEvent(input, &query);
QString text = query.value(Qt::ImSurroundingText).toString();
- m_lastCaretPos = query.value(Qt::ImCursorPosition).toInt();
+ m_caretPosition = query.value(Qt::ImCursorPosition).toInt();
+
+ qInputContextDebug() << Q_FUNC_INFO << text;
- QString empty = QString::fromLatin1("");
- text = text.mid(start, end - start);
+ m_isUpdatingText = true;
// Delete the current text.
+ QInputMethodEvent deleteEvent;
+ deleteEvent.setCommitString(QString(), start - m_caretPosition, end - start);
+ QCoreApplication::sendEvent(input, &deleteEvent);
+
+ m_composingText = text.mid(start, end - start);
+ m_isComposing = true;
+
QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent event(empty, attributes);
- event.setCommitString(empty, start - m_lastCaretPos, end - start);
- QCoreApplication::sendEvent(input, &event);
+ QTextCharFormat format;
+ format.setFontUnderline(true);
+ attributes.push_back(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, m_composingText.length(), format));
+
+ QInputMethodEvent setTextEvent(m_composingText, attributes);
+ QCoreApplication::sendEvent(input, &setTextEvent);
- // Move the specified text into a preedit string.
- setComposingText(text);
+ m_isUpdatingText = false;
return 0;
}
-int32_t QQnxInputContext::onSetComposingText(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position)
+int32_t QQnxInputContext::onSetComposingText(spannable_string_t *text, int32_t new_cursor_position)
{
- Q_UNUSED(new_cursor_position);
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
- return 0;
+ if (text->length > 0) {
+ updateComposition(text, new_cursor_position);
+ } else {
+ // If the composing text is empty we can simply end composition, the visual effect is the same.
+ // However, sometimes one wants to display hint text in an empty text field and for this to work
+ // QQuickTextEdit.inputMethodComposing has to be false if the composition string is empty.
+ m_composingText.clear();
+ finishComposingText();
+ }
+ return 0;
+}
- m_isComposing = true;
+int32_t QQnxInputContext::onIsTextSelected(int32_t* pIsSelected)
+{
+ *pIsSelected = hasSelectedText();
- QString preeditString = QString::fromWCharArray(text->str, text->length);
- setComposingText(preeditString);
+ qInputContextDebug() << Q_FUNC_INFO << *pIsSelected;
return 0;
}
-int32_t QQnxInputContext::onSetSelection(input_session_t *ic, int32_t start, int32_t end)
+int32_t QQnxInputContext::onIsAllTextSelected(int32_t* pIsSelected)
{
- Q_UNUSED(start);
- Q_UNUSED(end);
- qInputContextDebug() << Q_FUNC_INFO;
+ QObject *input = qGuiApp->focusObject();
+ if (!input)
+ return -1;
- if (!isSessionOkay(ic))
- return 0;
+ QInputMethodQueryEvent query(Qt::ImCurrentSelection | Qt::ImSurroundingText);
+ QCoreApplication::sendEvent(input, &query);
- // Should never get called.
- qCritical() << Q_FUNC_INFO << "onSetSelection is unsupported.";
+ *pIsSelected = query.value(Qt::ImSurroundingText).toString().length() == query.value(Qt::ImCurrentSelection).toString().length();
+
+ qInputContextDebug() << Q_FUNC_INFO << *pIsSelected;
return 0;
}
@@ -1563,8 +1324,16 @@ void QQnxInputContext::setFocusObject(QObject *object)
if (!inputMethodAccepted()) {
if (m_inputPanelVisible)
hideInputPanel();
+ if (hasSession())
+ dispatchFocusLossEvent();
} else {
- m_virtualKeyboard.setInputHintsFromObject(object);
+ QInputMethodQueryEvent query(Qt::ImHints);
+ QCoreApplication::sendEvent(object, &query);
+ int inputHints = query.value(Qt::ImHints).toInt();
+
+ dispatchFocusGainEvent(inputHints);
+
+ m_virtualKeyboard.setInputHints(inputHints);
if (!m_inputPanelVisible)
showInputPanel();
diff --git a/src/plugins/platforms/qnx/qqnxinputcontext_imf.h b/src/plugins/platforms/qnx/qqnxinputcontext_imf.h
index 1980a99ed9..cda4f2e88e 100644
--- a/src/plugins/platforms/qnx/qqnxinputcontext_imf.h
+++ b/src/plugins/platforms/qnx/qqnxinputcontext_imf.h
@@ -1,6 +1,6 @@
/***************************************************************************
**
-** Copyright (C) 2011 - 2012 Research In Motion
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
@@ -43,6 +43,7 @@
#define QQNXINPUTCONTEXT_H
#include <qpa/qplatforminputcontext.h>
+#include "qqnxscreeneventfilter.h"
#include <QtCore/QLocale>
#include <QtCore/QMetaType>
@@ -55,8 +56,9 @@ QT_BEGIN_NAMESPACE
class QQnxAbstractVirtualKeyboard;
class QQnxIntegration;
+class QQnxImfRequest;
-class QQnxInputContext : public QPlatformInputContext
+class QQnxInputContext : public QPlatformInputContext, public QQnxScreenEventFilter
{
Q_OBJECT
public:
@@ -68,8 +70,9 @@ public:
bool filterEvent(const QEvent *event);
QRectF keyboardRect() const;
void reset();
+ void commit();
void update(Qt::InputMethodQueries);
- bool handleKeyboardEvent(int flags, int sym, int mod, int scan, int cap);
+ bool handleKeyboardEvent(int flags, int sym, int mod, int scan, int cap, int sequenceId);
void showInputPanel();
@@ -79,61 +82,54 @@ public:
QLocale locale() const;
void setFocusObject(QObject *object);
-protected:
- // Filters only for IMF events.
- bool eventFilter(QObject *obj, QEvent *event);
-
private Q_SLOTS:
void keyboardVisibilityChanged(bool visible);
void keyboardLocaleChanged(const QLocale &locale);
+ void processImfEvent(QQnxImfRequest *event);
private:
// IMF Event dispatchers
- bool dispatchFocusEvent(FocusEventId id, int hints = Qt::ImhNone);
+ bool dispatchFocusGainEvent(int inputHints);
+ void dispatchFocusLossEvent();
bool dispatchRequestSoftwareInputPanel();
bool dispatchCloseSoftwareInputPanel();
int32_t processEvent(event_t *event);
void closeSession();
- void openSession();
+ bool openSession();
bool hasSession();
+ void updateCursorPosition();
void endComposition();
- void setComposingText(QString const &composingText);
+ void finishComposingText();
bool hasSelectedText();
+ void updateComposition(spannable_string_t *text, int32_t new_cursor_position);
// IMF Event handlers - these events will come in from QCoreApplication.
- int32_t onBeginBatchEdit(input_session_t *ic);
- int32_t onClearMetaKeyStates(input_session_t *ic, int32_t states);
- int32_t onCommitText(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position);
- int32_t onDeleteSurroundingText(input_session_t *ic, int32_t left_length, int32_t right_length);
- int32_t onEndBatchEdit(input_session_t *ic);
- int32_t onFinishComposingText(input_session_t *ic);
- int32_t onGetCursorCapsMode(input_session_t *ic, int32_t req_modes);
- int32_t onGetCursorPosition(input_session_t *ic);
- extracted_text_t *onGetExtractedText(input_session_t *ic, extracted_text_request_t *request, int32_t flags);
- spannable_string_t *onGetSelectedText(input_session_t *ic, int32_t flags);
- spannable_string_t *onGetTextAfterCursor(input_session_t *ic, int32_t n, int32_t flags);
- spannable_string_t *onGetTextBeforeCursor(input_session_t *ic, int32_t n, int32_t flags);
- int32_t onPerformEditorAction(input_session_t *ic, int32_t editor_action);
- int32_t onReportFullscreenMode(input_session_t *ic, int32_t enabled);
- int32_t onSendEvent(input_session_t *ic, event_t *event);
- int32_t onSendAsyncEvent(input_session_t *ic, event_t *event);
- int32_t onSetComposingRegion(input_session_t *ic, int32_t start, int32_t end);
- int32_t onSetComposingText(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position);
- int32_t onSetSelection(input_session_t *ic, int32_t start, int32_t end);
+ int32_t onCommitText(spannable_string_t *text, int32_t new_cursor_position);
+ int32_t onDeleteSurroundingText(int32_t left_length, int32_t right_length);
+ int32_t onGetCursorCapsMode(int32_t req_modes);
+ int32_t onFinishComposingText();
+ int32_t onGetCursorPosition();
+ spannable_string_t *onGetTextAfterCursor(int32_t n, int32_t flags);
+ spannable_string_t *onGetTextBeforeCursor(int32_t n, int32_t flags);
+ int32_t onSendEvent(event_t *event);
+ int32_t onSendAsyncEvent(event_t *event);
+ int32_t onSetComposingRegion(int32_t start, int32_t end);
+ int32_t onSetComposingText(spannable_string_t *text, int32_t new_cursor_position);
+ int32_t onIsTextSelected(int32_t* pIsSelected);
+ int32_t onIsAllTextSelected(int32_t* pIsSelected);
int32_t onForceUpdate();
- int m_lastCaretPos;
+ int m_caretPosition;
bool m_isComposing;
QString m_composingText;
+ bool m_isUpdatingText;
bool m_inputPanelVisible;
QLocale m_inputPanelLocale;
QQnxIntegration *m_integration;
- QQnxAbstractVirtualKeyboard &m_virtualKeyboad;
+ QQnxAbstractVirtualKeyboard &m_virtualKeyboard;
};
-Q_DECLARE_METATYPE(extracted_text_t*)
-
QT_END_NAMESPACE
#endif // QQNXINPUTCONTEXT_H
diff --git a/src/plugins/platforms/qnx/qqnxinputcontext_noimf.cpp b/src/plugins/platforms/qnx/qqnxinputcontext_noimf.cpp
index f444d34b5e..9270f1ed6b 100644
--- a/src/plugins/platforms/qnx/qqnxinputcontext_noimf.cpp
+++ b/src/plugins/platforms/qnx/qqnxinputcontext_noimf.cpp
@@ -1,6 +1,6 @@
/***************************************************************************
**
-** Copyright (C) 2011 - 2012 Research In Motion
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
@@ -46,6 +46,7 @@
#include <QtCore/QDebug>
#include <QtGui/QGuiApplication>
+#include <QtGui/QInputMethodEvent>
#if defined(QQNXINPUTCONTEXT_DEBUG)
#define qInputContextDebug qDebug
@@ -179,7 +180,11 @@ void QQnxInputContext::setFocusObject(QObject *object)
if (m_inputPanelVisible)
hideInputPanel();
} else {
- m_virtualKeyboard.setInputHintsFromObject(object);
+ QInputMethodQueryEvent query(Qt::ImHints);
+ QCoreApplication::sendEvent(object, &query);
+ int inputHints = query.value(Qt::ImHints).toInt();
+
+ m_virtualKeyboard.setInputHints(inputHints);
if (!m_inputPanelVisible)
showInputPanel();
diff --git a/src/plugins/platforms/qnx/qqnxintegration.cpp b/src/plugins/platforms/qnx/qqnxintegration.cpp
index 52f836abbe..5695ef433a 100644
--- a/src/plugins/platforms/qnx/qqnxintegration.cpp
+++ b/src/plugins/platforms/qnx/qqnxintegration.cpp
@@ -1,6 +1,6 @@
/***************************************************************************
**
-** Copyright (C) 2011 - 2013 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
@@ -251,6 +251,9 @@ QQnxIntegration::QQnxIntegration(const QStringList &paramList)
#if defined(QQNX_PPS)
// Set up the input context
m_inputContext = new QQnxInputContext(this, *m_virtualKeyboard);
+#if defined(QQNX_IMF)
+ m_screenEventHandler->addScreenEventFilter(m_inputContext);
+#endif
#endif
}
@@ -271,17 +274,6 @@ QQnxIntegration::~QQnxIntegration()
delete m_drag;
#endif
-#if defined(QQNX_PPS)
- // Destroy the hardware button notifier
- delete m_buttonsNotifier;
-
- // Destroy input context
- delete m_inputContext;
-#endif
-
- // Destroy the keyboard class.
- delete m_virtualKeyboard;
-
#if !defined(QT_NO_CLIPBOARD)
// Delete the clipboard
delete m_clipboard;
@@ -321,6 +313,17 @@ QQnxIntegration::~QQnxIntegration()
QQnxGLContext::shutdown();
#endif
+#if defined(QQNX_PPS)
+ // Destroy the hardware button notifier
+ delete m_buttonsNotifier;
+
+ // Destroy input context
+ delete m_inputContext;
+#endif
+
+ // Destroy the keyboard class.
+ delete m_virtualKeyboard;
+
// Destroy services class
delete m_services;
diff --git a/src/plugins/platforms/qnx/qqnxscreeneventfilter.h b/src/plugins/platforms/qnx/qqnxscreeneventfilter.h
new file mode 100644
index 0000000000..f9ecadd2a9
--- /dev/null
+++ b/src/plugins/platforms/qnx/qqnxscreeneventfilter.h
@@ -0,0 +1,58 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQNXSCREENEVENTFILTER_H
+#define QQNXSCREENEVENTFILTER_H
+
+QT_BEGIN_NAMESPACE
+
+class QQnxScreenEventFilter
+{
+protected:
+ ~QQnxScreenEventFilter() {}
+
+public:
+ virtual bool handleKeyboardEvent(int flags, int sym, int mod, int scan, int cap, int sequenceId) = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp
index 6f06797393..37071d825c 100644
--- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp
+++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp
@@ -1,6 +1,6 @@
/***************************************************************************
**
-** Copyright (C) 2011 - 2012 Research In Motion
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
@@ -46,6 +46,7 @@
#include "qqnxintegration.h"
#include "qqnxkeytranslator.h"
#include "qqnxscreen.h"
+#include "qqnxscreeneventfilter.h"
#include <QDebug>
#include <QGuiApplication>
@@ -90,6 +91,16 @@ QQnxScreenEventHandler::QQnxScreenEventHandler(QQnxIntegration *integration)
}
}
+void QQnxScreenEventHandler::addScreenEventFilter(QQnxScreenEventFilter *filter)
+{
+ m_eventFilters.append(filter);
+}
+
+void QQnxScreenEventHandler::removeScreenEventFilter(QQnxScreenEventFilter *filter)
+{
+ m_eventFilters.removeOne(filter);
+}
+
bool QQnxScreenEventHandler::handleEvent(screen_event_t event)
{
// get the event type
@@ -256,7 +267,23 @@ void QQnxScreenEventHandler::handleKeyboardEvent(screen_event_t event)
if (result)
qFatal("QQNX: failed to query event cap, errno=%d", errno);
- injectKeyboardEvent(flags, sym, modifiers, scan, cap);
+ int sequenceId = 0;
+#if defined(Q_OS_BLACKBERRY)
+ result = screen_get_event_property_iv(event, SCREEN_PROPERTY_SEQUENCE_ID, &sequenceId);
+ if (result)
+ qFatal("QQNX: failed to query event seqId, errno=%d", errno);
+#endif
+
+ bool inject = true;
+ Q_FOREACH (QQnxScreenEventFilter *filter, m_eventFilters) {
+ if (filter->handleKeyboardEvent(flags, sym, modifiers, scan, cap, sequenceId)) {
+ inject = false;
+ break;
+ }
+ }
+
+ if (inject)
+ injectKeyboardEvent(flags, sym, modifiers, scan, cap);
}
void QQnxScreenEventHandler::handlePointerEvent(screen_event_t event)
diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.h b/src/plugins/platforms/qnx/qqnxscreeneventhandler.h
index 1fdb2c83cd..a7bcd449ee 100644
--- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.h
+++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.h
@@ -1,6 +1,6 @@
/***************************************************************************
**
-** Copyright (C) 2011 - 2012 Research In Motion
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
@@ -49,6 +49,7 @@
QT_BEGIN_NAMESPACE
class QQnxIntegration;
+class QQnxScreenEventFilter;
#if defined(QQNX_SCREENEVENTTHREAD)
class QQnxScreenEventThread;
#endif
@@ -59,6 +60,9 @@ class QQnxScreenEventHandler : public QObject
public:
explicit QQnxScreenEventHandler(QQnxIntegration *integration);
+ void addScreenEventFilter(QQnxScreenEventFilter *filter);
+ void removeScreenEventFilter(QQnxScreenEventFilter *filter);
+
bool handleEvent(screen_event_t event);
bool handleEvent(screen_event_t event, int qnxType);
@@ -99,6 +103,7 @@ private:
screen_window_t m_lastMouseWindow;
QTouchDevice *m_touchDevice;
QWindowSystemInterface::TouchPoint m_touchPoints[MaximumTouchPoints];
+ QList<QQnxScreenEventFilter*> m_eventFilters;
#if defined(QQNX_SCREENEVENTTHREAD)
QQnxScreenEventThread *m_eventThread;
#endif
diff --git a/src/plugins/platforms/qnx/qqnxvirtualkeyboardbps.cpp b/src/plugins/platforms/qnx/qqnxvirtualkeyboardbps.cpp
index 11eb4a5082..f3a6887613 100644
--- a/src/plugins/platforms/qnx/qqnxvirtualkeyboardbps.cpp
+++ b/src/plugins/platforms/qnx/qqnxvirtualkeyboardbps.cpp
@@ -1,6 +1,6 @@
/***************************************************************************
**
-** Copyright (C) 2012 Research In Motion
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
@@ -135,6 +135,10 @@ void QQnxVirtualKeyboardBps::applyKeyboardMode(KeyboardMode mode)
layout = VIRTUALKEYBOARD_LAYOUT_PIN;
break;
+ case Password:
+ layout = VIRTUALKEYBOARD_LAYOUT_PASSWORD;
+ break;
+
case Default: // fall through
default:
layout = VIRTUALKEYBOARD_LAYOUT_DEFAULT;
diff --git a/src/plugins/platforms/windows/qwindowsglcontext.cpp b/src/plugins/platforms/windows/qwindowsglcontext.cpp
index d1ede39549..93f2b53672 100644
--- a/src/plugins/platforms/windows/qwindowsglcontext.cpp
+++ b/src/plugins/platforms/windows/qwindowsglcontext.cpp
@@ -880,7 +880,9 @@ QWindowsGLContext::QWindowsGLContext(const QOpenGLStaticContextPtr &staticContex
m_staticContext(staticContext),
m_context(context),
m_renderingContext(0),
- m_pixelFormat(0), m_extensionsUsed(false)
+ m_pixelFormat(0),
+ m_extensionsUsed(false),
+ m_swapInterval(-1)
{
QSurfaceFormat format = context->format();
if (format.renderableType() == QSurfaceFormat::DefaultRenderableType)
@@ -977,11 +979,9 @@ QWindowsGLContext::QWindowsGLContext(const QOpenGLStaticContextPtr &staticContex
QWindowsOpenGLContextFormat::current().apply(&m_obtainedFormat);
- if (requestedAdditional.swapInterval != -1 && m_staticContext->wglSwapInternalExt) {
- m_staticContext->wglSwapInternalExt(requestedAdditional.swapInterval);
- if (m_staticContext->wglGetSwapInternalExt)
- obtainedSwapInternal = m_staticContext->wglGetSwapInternalExt();
- }
+ if (m_staticContext->wglGetSwapInternalExt)
+ obtainedSwapInternal = m_staticContext->wglGetSwapInternalExt();
+
wglMakeCurrent(0, 0);
} while (false);
if (hdc)
@@ -1085,7 +1085,19 @@ bool QWindowsGLContext::makeCurrent(QPlatformSurface *surface)
window->setFlag(QWindowsWindow::OpenGLDoubleBuffered);
}
m_windowContexts.append(newContext);
- return wglMakeCurrent(newContext.hdc, newContext.renderingContext);
+
+ bool success = wglMakeCurrent(newContext.hdc, newContext.renderingContext);
+
+ // Set the swap interval
+ if (m_staticContext->wglSwapInternalExt) {
+ const int interval = surface->format().swapInterval();
+ if (interval >= 0 && m_swapInterval != interval) {
+ m_swapInterval = interval;
+ m_staticContext->wglSwapInternalExt(interval);
+ }
+ }
+
+ return success;
}
void QWindowsGLContext::doneCurrent()
diff --git a/src/plugins/platforms/windows/qwindowsglcontext.h b/src/plugins/platforms/windows/qwindowsglcontext.h
index 730e90bf70..c6b477128a 100644
--- a/src/plugins/platforms/windows/qwindowsglcontext.h
+++ b/src/plugins/platforms/windows/qwindowsglcontext.h
@@ -64,11 +64,10 @@ enum QWindowsGLFormatFlags
// Additional format information for Windows.
struct QWindowsOpenGLAdditionalFormat
{
- QWindowsOpenGLAdditionalFormat(unsigned formatFlagsIn = 0, unsigned pixmapDepthIn = 0, unsigned swapIntervalIn = -1) :
- formatFlags(formatFlagsIn), pixmapDepth(pixmapDepthIn), swapInterval(swapIntervalIn) {}
+ QWindowsOpenGLAdditionalFormat(unsigned formatFlagsIn = 0, unsigned pixmapDepthIn = 0) :
+ formatFlags(formatFlagsIn), pixmapDepth(pixmapDepthIn) { }
unsigned formatFlags; // QWindowsGLFormatFlags.
unsigned pixmapDepth; // for QWindowsGLRenderToPixmap
- int swapInterval;
};
// Per-window data for active OpenGL contexts.
@@ -178,6 +177,7 @@ private:
PIXELFORMATDESCRIPTOR m_obtainedPixelFormatDescriptor;
int m_pixelFormat;
bool m_extensionsUsed;
+ int m_swapInterval;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp
index b6e75929f8..e72b1d60c8 100644
--- a/src/plugins/platforms/windows/qwindowsintegration.cpp
+++ b/src/plugins/platforms/windows/qwindowsintegration.cpp
@@ -345,8 +345,8 @@ static inline unsigned parseOptions(const QStringList &paramList)
}
} else if (param == QLatin1String("gl=gdi")) {
options |= QWindowsIntegration::DisableArb;
- } else if (param == QLatin1String("mousefromtouch")) {
- options |= QWindowsIntegration::PassOsMouseEventsSynthesizedFromTouch;
+ } else if (param == QLatin1String("nomousefromtouch")) {
+ options |= QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch;
}
}
return options;
@@ -563,11 +563,9 @@ QVariant QWindowsIntegration::styleHint(QPlatformIntegration::StyleHint hint) co
case QPlatformIntegration::SynthesizeMouseFromTouchEvents:
#ifdef Q_OS_WINCE
// We do not want Qt to synthesize mouse events as Windows also does that.
- // Alternatively, Windows-generated touch mouse events can be identified and
- // ignored by checking GetMessageExtraInfo() for MI_WP_SIGNATURE (0xFF515700).
return false;
#else // Q_OS_WINCE
- return QVariant(!(d->m_options & PassOsMouseEventsSynthesizedFromTouch));
+ return QVariant(bool(d->m_options & DontPassOsMouseEventsSynthesizedFromTouch));
#endif // !Q_OS_WINCE
default:
break;
diff --git a/src/plugins/platforms/windows/qwindowsintegration.h b/src/plugins/platforms/windows/qwindowsintegration.h
index 97916a479b..270ff7ef68 100644
--- a/src/plugins/platforms/windows/qwindowsintegration.h
+++ b/src/plugins/platforms/windows/qwindowsintegration.h
@@ -60,7 +60,7 @@ public:
DisableArb = 0x4,
NoNativeDialogs = 0x8,
XpNativeDialogs = 0x10,
- PassOsMouseEventsSynthesizedFromTouch = 0x20 // Pass OS-generated mouse events from touch.
+ DontPassOsMouseEventsSynthesizedFromTouch = 0x20 // Do not pass OS-generated mouse events from touch.
};
explicit QWindowsIntegration(const QStringList &paramList);
diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp
index 3dd8c5a0cd..b43aafeba0 100644
--- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp
+++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp
@@ -167,8 +167,12 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
if (et == QtWindows::MouseWheelEvent)
return translateMouseWheelEvent(window, hwnd, msg, result);
+ Qt::MouseEventSource source = Qt::MouseEventNotSynthesized;
+
#ifndef Q_OS_WINCE
- static const bool passSynthesizedMouseEvents = QWindowsIntegration::instance()->options() & QWindowsIntegration::PassOsMouseEventsSynthesizedFromTouch;
+ // Check for events synthesized from touch. Lower byte is touch index, 0 means pen.
+ static const bool passSynthesizedMouseEvents =
+ !(QWindowsIntegration::instance()->options() & QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch);
if (!passSynthesizedMouseEvents) {
// Check for events synthesized from touch. Lower 7 bits are touch/pen index, bit 8 indicates touch.
// However, when tablet support is active, extraInfo is a packet serial number. This is not a problem
@@ -177,6 +181,7 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
const bool fromTouch = (extraInfo & signatureMask) == miWpSignature && (extraInfo & 0x80);
if (fromTouch)
return false;
+ source = Qt::MouseEventSynthesizedBySystem;
}
#endif // !Q_OS_WINCE
@@ -187,7 +192,8 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
const Qt::MouseButtons buttons = QWindowsMouseHandler::queryMouseButtons();
QWindowSystemInterface::handleFrameStrutMouseEvent(window, clientPosition,
globalPosition, buttons,
- QWindowsKeyMapper::queryKeyboardModifiers());
+ QWindowsKeyMapper::queryKeyboardModifiers(),
+ source);
return false; // Allow further event processing (dragging of windows).
}
@@ -335,7 +341,8 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
}
QWindowSystemInterface::handleMouseEvent(window, winEventPosition, globalPosition, buttons,
- QWindowsKeyMapper::queryKeyboardModifiers());
+ QWindowsKeyMapper::queryKeyboardModifiers(),
+ source);
m_previousCaptureWindow = hasCapture ? window : 0;
return true;
}
diff --git a/src/plugins/platforms/winrt/blit.hlsl b/src/plugins/platforms/winrt/blit.hlsl
new file mode 100644
index 0000000000..170e7f40ca
--- /dev/null
+++ b/src/plugins/platforms/winrt/blit.hlsl
@@ -0,0 +1,14 @@
+uniform SamplerState Sampler : register(s0);
+uniform Texture2D Texture : register(t0);
+
+void blitvs(in float4 pos0 : TEXCOORD0, in float2 tex0 : TEXCOORD1,
+ out float4 gl_Position : SV_POSITION, out float2 coord : TEXCOORD0)
+{
+ coord = tex0;
+ gl_Position = pos0 * float4(1.0, -1.0, 1.0, 1.0);
+}
+
+float4 blitps(in float4 gl_Position : SV_POSITION, in float2 coord : TEXCOORD0) : SV_TARGET0
+{
+ return Texture.Sample(Sampler, coord);
+}
diff --git a/src/plugins/platforms/winrt/main.cpp b/src/plugins/platforms/winrt/main.cpp
new file mode 100644
index 0000000000..89d560dbe3
--- /dev/null
+++ b/src/plugins/platforms/winrt/main.cpp
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwinrtintegration.h"
+
+#include <qpa/qplatformintegrationplugin.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWinRTIntegrationPlugin : public QPlatformIntegrationPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.2" FILE "winrt.json")
+
+public:
+ QStringList keys() const;
+ QPlatformIntegration *create(const QString&, const QStringList&);
+};
+
+QStringList QWinRTIntegrationPlugin::keys() const
+{
+ return QStringList(QStringLiteral("WinRT"));
+}
+
+QPlatformIntegration *QWinRTIntegrationPlugin::create(const QString& system, const QStringList& paramList)
+{
+ Q_UNUSED(paramList);
+ if (!system.compare(QLatin1String("winrt"), Qt::CaseInsensitive))
+ return QWinRTIntegration::create();
+
+ return 0;
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/platforms/winrt/qwinrtbackingstore.cpp b/src/plugins/platforms/winrt/qwinrtbackingstore.cpp
new file mode 100644
index 0000000000..b219548788
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrtbackingstore.cpp
@@ -0,0 +1,393 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwinrtbackingstore.h"
+
+#include "qwinrtscreen.h"
+#include "qwinrtwindow.h"
+#include "qwinrteglcontext.h"
+#include <QtGui/QOpenGLContext>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <dxgi.h>
+
+// Generated shader headers
+#include "blitps.h"
+#include "blitvs.h"
+
+namespace { // Utility namespace for writing out an ANGLE-compatible binary blob
+
+// Must match packaged ANGLE
+enum : quint32 {
+ AngleMajorVersion = 1,
+ AngleMinorVersion = 2,
+ AngleBuildRevision = 2446,
+ AngleVersion = ((AngleMajorVersion << 24) | (AngleMinorVersion << 16) | AngleBuildRevision),
+ AngleOptimizationLevel = (1 << 14)
+};
+
+struct ShaderString
+{
+ ShaderString(const char *data = 0) : data(data) { }
+ const char *data;
+};
+
+// ANGLE stream compatibility - when size_t is 32-bit, QDataStream::writeBytes() also works
+QDataStream &operator<<(QDataStream &stream, const ShaderString &shaderString)
+{
+ if (!shaderString.data)
+ return stream << size_t(0);
+
+ size_t len = strlen(shaderString.data);
+ stream << len;
+ stream.writeRawData(shaderString.data, int(len));
+ return stream;
+}
+
+struct Attribute
+{
+ Attribute(GLenum type = 0, const char *name = 0, quint32 index = 0)
+ : type(type), name(name), index(index) { }
+ GLenum type;
+ ShaderString name;
+ quint32 index;
+};
+
+struct Sampler
+{
+ enum TextureType { Texture2D, TextureCube };
+ Sampler(bool active = false, GLint unit = 0, TextureType type = Texture2D)
+ : active(active), unit(unit), type(type) { }
+ bool active;
+ GLint unit;
+ TextureType type;
+};
+
+struct Uniform
+{
+ Uniform() { }
+ Uniform(GLenum type, quint32 precision, const char *name, quint32 arraySize,
+ quint32 psRegisterIndex, quint32 vsRegisterIndex, quint32 registerCount)
+ : type(type), precision(precision), name(name), arraySize(arraySize)
+ , psRegisterIndex(psRegisterIndex), vsRegisterIndex(vsRegisterIndex), registerCount(registerCount) { }
+ GLenum type;
+ quint32 precision;
+ ShaderString name;
+ quint32 arraySize;
+ quint32 psRegisterIndex;
+ quint32 vsRegisterIndex;
+ quint32 registerCount;
+};
+
+struct UniformIndex
+{
+ UniformIndex(const char *name = 0, quint32 element = 0, quint32 index = 0)
+ : name(name), element(element), index(index) { }
+ ShaderString name;
+ quint32 element;
+ quint32 index;
+};
+
+static const QByteArray createAngleBinary(
+ const QVector<Attribute> &attributes,
+ const QVector<Sampler> &textureSamplers,
+ const QVector<Sampler> &vertexSamplers,
+ const QVector<Uniform> &uniforms,
+ const QVector<UniformIndex> &uniformIndex,
+ const QByteArray &pixelShader,
+ const QByteArray &vertexShader,
+ const QByteArray &geometryShader = QByteArray(),
+ bool usesPointSize = false)
+{
+ QByteArray binary;
+
+ QDataStream stream(&binary, QIODevice::WriteOnly);
+ stream.setByteOrder(QDataStream::LittleEndian);
+
+ stream << quint32(GL_PROGRAM_BINARY_ANGLE)
+ << quint32(AngleVersion)
+ << quint32(AngleOptimizationLevel);
+
+ // Vertex attributes
+ for (int i = 0; i < 16; ++i) {
+ if (i < attributes.size())
+ stream << quint32(attributes[i].type) << attributes[i].name << attributes[i].index;
+ else
+ stream << quint32(GL_NONE) << ShaderString() << qint32(-1);
+ }
+
+ // Texture units
+ for (int i = 0; i < 16; ++i) {
+ if (i < textureSamplers.size())
+ stream << textureSamplers[i].active << textureSamplers[i].unit << qint32(textureSamplers[i].type);
+ else
+ stream << false << qint32(0) << qint32(Sampler::Texture2D);
+ }
+
+ // Vertex texture units
+ for (int i = 0; i < 16; ++i) {
+ if (i < vertexSamplers.size())
+ stream << vertexSamplers[i].active << vertexSamplers[i].unit << qint32(vertexSamplers[i].type);
+ else
+ stream << false << qint32(0) << qint32(Sampler::Texture2D);
+ }
+
+ stream << vertexSamplers.size()
+ << textureSamplers.size()
+ << usesPointSize;
+
+ stream << size_t(uniforms.size());
+ foreach (const Uniform &uniform, uniforms) {
+ stream << uniform.type << uniform.precision << uniform.name << uniform.arraySize
+ << uniform.psRegisterIndex << uniform.vsRegisterIndex << uniform.registerCount;
+ }
+
+ stream << size_t(uniformIndex.size());
+ foreach (const UniformIndex &index, uniformIndex)
+ stream << index.name << index.element << index.index;
+
+ stream << quint32(pixelShader.size())
+ << quint32(vertexShader.size())
+ << quint32(geometryShader.size());
+
+ // ANGLE requires that we query the adapter for its LUID. Later on, it may be useful
+ // for checking feature level support, picking the best adapter on the system, etc.
+ IDXGIFactory1 *dxgiFactory;
+ if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)))) {
+ qCritical("QWinRTBackingStore: failed to create DXGI factory.");
+ return QByteArray();
+ }
+ IDXGIAdapter *dxgiAdapter;
+ if (FAILED(dxgiFactory->EnumAdapters(0, &dxgiAdapter))) {
+ qCritical("QWinRTBackingStore:: failed to enumerate adapter.");
+ dxgiFactory->Release();
+ return QByteArray();
+ }
+ DXGI_ADAPTER_DESC desc;
+ dxgiAdapter->GetDesc(&desc);
+ dxgiAdapter->Release();
+ QByteArray guid(sizeof(GUID), '\0');
+ memcpy(guid.data(), &desc.AdapterLuid, sizeof(LUID));
+ stream.writeRawData(guid.constData(), guid.size());
+ stream.writeRawData(pixelShader.constData(), pixelShader.size());
+ stream.writeRawData(vertexShader.constData(), vertexShader.size());
+ if (!geometryShader.isEmpty())
+ stream.writeRawData(geometryShader.constData(), geometryShader.size());
+
+ return binary;
+}
+
+} // namespace
+
+QT_BEGIN_NAMESPACE
+
+static const GLfloat normCoords[] = { -1, 1, 1, 1, 1, -1, -1, -1 };
+static const GLfloat quadCoords[] = { 0, 0, 1, 0, 1, 1, 0, 1 };
+
+QWinRTBackingStore::QWinRTBackingStore(QWindow *window)
+ : QPlatformBackingStore(window)
+ , m_context(new QOpenGLContext)
+ , m_shaderProgram(0)
+ , m_fbo(0)
+ , m_texture(0)
+ , m_screen(static_cast<QWinRTScreen*>(window->screen()->handle()))
+{
+ window->setSurfaceType(QSurface::OpenGLSurface); // Required for flipping, but could be done in the swap
+
+ m_context->setFormat(window->requestedFormat());
+ m_context->setScreen(window->screen());
+ m_context->create();
+
+ m_context->makeCurrent(window);
+ glGenFramebuffers(1, &m_fbo);
+ glGenRenderbuffers(1, &m_rbo);
+ glGenTextures(1, &m_texture);
+ m_shaderProgram = glCreateProgram();
+
+#if 0 // Standard GLES passthrough shader program
+ static const char *vertexShaderSource =
+ "attribute vec4 pos0;\n"
+ "attribute vec2 tex0;\n"
+ "varying vec2 coord;\n"
+ "void main() {\n"
+ " coord = tex0;\n"
+ " gl_Position = pos0;\n"
+ "}\n";
+ static const char *fragmentShaderSource =
+ "uniform sampler2D texture;\n"
+ "varying highp vec2 coord;\n"
+ "void main() {\n"
+ " gl_FragColor = texture2D(texture, coord);\n"
+ "}\n";
+ GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
+ glCompileShader(vertexShader);
+ GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
+ glCompileShader(fragmentShader);
+ glAttachShader(m_shaderProgram, vertexShader);
+ glAttachShader(m_shaderProgram, fragmentShader);
+ glLinkProgram(m_shaderProgram);
+#else // Precompiled passthrough shader
+ QVector<Attribute> attributes = QVector<Attribute>() << Attribute(GL_FLOAT_VEC4, "pos0", 0)
+ << Attribute(GL_FLOAT_VEC2, "tex0", 1);
+ QVector<Sampler> textureSamplers = QVector<Sampler>() << Sampler(true, 0, Sampler::Texture2D);
+ QVector<Sampler> vertexSamplers;
+ QVector<Uniform> uniforms = QVector<Uniform>() << Uniform(GL_SAMPLER_2D, 0, "texture", 0, 0, -1, 1);
+ QVector<UniformIndex> uniformsIndex = QVector<UniformIndex>() << UniformIndex("texture", 0, 0);
+ QByteArray pixelShader(reinterpret_cast<const char *>(q_blitps), sizeof(q_blitps));
+ QByteArray vertexShader(reinterpret_cast<const char *>(q_blitvs), sizeof(q_blitvs));
+ QByteArray binary = createAngleBinary(attributes, textureSamplers, vertexSamplers,
+ uniforms, uniformsIndex, pixelShader, vertexShader);
+ glProgramBinaryOES(m_shaderProgram, GL_PROGRAM_BINARY_ANGLE, binary.constData(), binary.size());
+#endif
+ m_context->doneCurrent();
+ resize(window->size(), QRegion());
+}
+
+QWinRTBackingStore::~QWinRTBackingStore()
+{
+ glDeleteBuffers(1, &m_fbo);
+ glDeleteRenderbuffers(1, &m_rbo);
+ glDeleteTextures(1, &m_texture);
+ glDeleteProgram(m_shaderProgram);
+}
+
+QPaintDevice *QWinRTBackingStore::paintDevice()
+{
+ return m_paintDevice.data();
+}
+
+void QWinRTBackingStore::flush(QWindow *window, const QRegion &region, const QPoint &offset)
+{
+ Q_UNUSED(offset)
+
+ const QImage *image = static_cast<QImage *>(m_paintDevice.data());
+
+ m_context->makeCurrent(window);
+
+ // Blitting the entire image width trades zero image copy/relayout for a larger texture upload.
+ // Since we're blitting the whole width anyway, the boundingRect() is used in the assumption that
+ // we don't repeat upload. This is of course dependent on the distance between update regions.
+ // Ideally, we would use the GL_EXT_unpack_subimage extension, which should be possible to implement
+ // since D3D11_MAPPED_SUBRESOURCE supports RowPitch (see below).
+ // Note that single-line blits in a loop are *very* slow, so reducing calls to glTexSubImage2D
+ // is probably a good idea anyway.
+ glBindTexture(GL_TEXTURE_2D, m_texture);
+ QRect bounds = region.boundingRect();
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, bounds.y(), m_size.width(), bounds.height(),
+ GL_BGRA_EXT, GL_UNSIGNED_BYTE, image->scanLine(bounds.y()));
+ // TODO: Implement GL_EXT_unpack_subimage in ANGLE for more minimal uploads
+ //glPixelStorei(GL_UNPACK_ROW_LENGTH, image->bytesPerLine());
+ //glTexSubImage2D(GL_TEXTURE_2D, 0, bounds.x(), bounds.y(), bounds.width(), bounds.height(),
+ // GL_BGRA_EXT, GL_UNSIGNED_BYTE, image->scanLine(bounds.y()) + bounds.x() * 4);
+
+ // Bind render buffer
+ glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rbo);
+
+ // Bind position
+ glUseProgram(m_shaderProgram);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, normCoords);
+ glEnableVertexAttribArray(1);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, quadCoords);
+
+ // Render
+ glViewport(0, 0, m_size.width(), m_size.height());
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ // Unbind
+ glDisableVertexAttribArray(0);
+ glDisableVertexAttribArray(1);
+ glUseProgram(0);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ // fast blit - TODO: perform the blit inside swap buffers instead
+ glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, m_fbo);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, 0);
+ glBlitFramebufferANGLE(0, 0, m_size.width(), m_size.height(), // TODO: blit only the changed rectangle
+ 0, 0, m_size.width(), m_size.height(),
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+ m_context->swapBuffers(window);
+ m_context->doneCurrent();
+}
+
+void QWinRTBackingStore::resize(const QSize &size, const QRegion &staticContents)
+{
+ Q_UNUSED(staticContents)
+ if (m_size == size)
+ return;
+
+ m_size = size;
+ m_paintDevice.reset(new QImage(m_size, QImage::Format_ARGB32_Premultiplied));
+
+ m_context->makeCurrent(window());
+ // Input texture
+ glBindTexture(GL_TEXTURE_2D, m_texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, m_size.width(), m_size.height(),
+ 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, NULL);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ // Render buffer
+ glBindRenderbuffer(GL_RENDERBUFFER, m_rbo);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_BGRA8_EXT, m_size.width(), m_size.height());
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ m_context->doneCurrent();
+}
+
+void QWinRTBackingStore::beginPaint(const QRegion &region)
+{
+ Q_UNUSED(region)
+}
+
+void QWinRTBackingStore::endPaint()
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/winrt/qwinrtbackingstore.h b/src/plugins/platforms/winrt/qwinrtbackingstore.h
new file mode 100644
index 0000000000..8be549b441
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrtbackingstore.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINRTBACKINGSTORE_H
+#define QWINRTBACKINGSTORE_H
+
+#include <qpa/qplatformbackingstore.h>
+#include <QtCore/QScopedPointer>
+
+QT_BEGIN_NAMESPACE
+
+class QWinRTScreen;
+class QOpenGLContext;
+
+class QWinRTBackingStore : public QPlatformBackingStore
+{
+public:
+ explicit QWinRTBackingStore(QWindow *window);
+ ~QWinRTBackingStore();
+ QPaintDevice *paintDevice();
+ void beginPaint(const QRegion &);
+ void endPaint();
+ void flush(QWindow *window, const QRegion &region, const QPoint &offset);
+ void resize(const QSize &size, const QRegion &staticContents);
+
+private:
+ QSize m_size;
+ QScopedPointer<QPaintDevice> m_paintDevice;
+ QScopedPointer<QOpenGLContext> m_context;
+ quint32 m_shaderProgram;
+ quint32 m_fbo;
+ quint32 m_rbo;
+ quint32 m_texture;
+ QWinRTScreen *m_screen;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINRTBACKINGSTORE_H
diff --git a/src/plugins/platforms/winrt/qwinrtcursor.cpp b/src/plugins/platforms/winrt/qwinrtcursor.cpp
new file mode 100644
index 0000000000..8241560cef
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrtcursor.cpp
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwinrtcursor.h"
+
+#include <wrl.h>
+#include <windows.ui.core.h>
+#include <windows.foundation.h>
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::UI::Core;
+using namespace ABI::Windows::Foundation;
+
+QT_BEGIN_NAMESPACE
+
+QWinRTCursor::QWinRTCursor(ICoreWindow *window) : m_window(window), m_cursorFactory(nullptr)
+{
+#ifndef Q_OS_WINPHONE
+ GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_Core_CoreCursor).Get(), &m_cursorFactory);
+#endif
+}
+
+QWinRTCursor::~QWinRTCursor()
+{
+ if (m_cursorFactory)
+ m_cursorFactory->Release();
+}
+
+#ifndef QT_NO_CURSOR
+void QWinRTCursor::changeCursor(QCursor *windowCursor, QWindow *)
+{
+#ifndef Q_OS_WINPHONE
+ if (!m_cursorFactory)
+ return;
+
+ CoreCursorType type;
+ switch (windowCursor ? windowCursor->shape() : Qt::ArrowCursor) {
+ case Qt::BlankCursor:
+ m_window->put_PointerCursor(nullptr);
+ return;
+ default:
+ case Qt::OpenHandCursor:
+ case Qt::ClosedHandCursor:
+ case Qt::DragCopyCursor:
+ case Qt::DragMoveCursor:
+ case Qt::DragLinkCursor:
+ // (unavailable)
+ case Qt::ArrowCursor:
+ type = CoreCursorType_Arrow;
+ break;
+ case Qt::UpArrowCursor:
+ type = CoreCursorType_UpArrow;
+ break;
+ case Qt::CrossCursor:
+ type = CoreCursorType_Cross;
+ break;
+ case Qt::WaitCursor:
+ case Qt::BusyCursor:
+ type = CoreCursorType_Wait;
+ break;
+ case Qt::IBeamCursor:
+ type = CoreCursorType_IBeam;
+ break;
+ case Qt::SizeVerCursor:
+ case Qt::SplitVCursor:
+ type = CoreCursorType_SizeNorthSouth;
+ break;
+ case Qt::SizeHorCursor:
+ case Qt::SplitHCursor:
+ type = CoreCursorType_SizeWestEast;
+ break;
+ case Qt::SizeBDiagCursor:
+ type = CoreCursorType_SizeNortheastSouthwest;
+ break;
+ case Qt::SizeFDiagCursor:
+ type = CoreCursorType_SizeNorthwestSoutheast;
+ break;
+ case Qt::SizeAllCursor:
+ type = CoreCursorType_SizeAll;
+ break;
+ case Qt::PointingHandCursor:
+ type = CoreCursorType_Hand;
+ break;
+ case Qt::ForbiddenCursor:
+ type = CoreCursorType_UniversalNo;
+ break;
+ case Qt::WhatsThisCursor:
+ type = CoreCursorType_Help;
+ break;
+ case Qt::BitmapCursor:
+ case Qt::CustomCursor:
+ // TODO: figure out if arbitrary bitmaps can be made into resource IDs
+ // For now, we don't get enough info from QCursor to set a custom cursor
+ type = CoreCursorType_Custom;
+ break;
+ }
+
+ ICoreCursor *cursor;
+ if (SUCCEEDED(m_cursorFactory->CreateCursor(type, 0, &cursor)))
+ m_window->put_PointerCursor(cursor);
+#endif // Q_OS_WINPHONE
+}
+#endif // QT_NO_CURSOR
+
+QPoint QWinRTCursor::pos() const
+{
+#ifdef Q_OS_WINPHONE
+ return QPlatformCursor::pos();
+#else
+ Point point;
+ m_window->get_PointerPosition(&point);
+ return QPoint(point.X, point.Y);
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/winrt/qwinrtcursor.h b/src/plugins/platforms/winrt/qwinrtcursor.h
new file mode 100644
index 0000000000..f7b301a98b
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrtcursor.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINRTCURSOR_H
+#define QWINRTCURSOR_H
+
+#include <qpa/qplatformcursor.h>
+
+namespace ABI {
+ namespace Windows {
+ namespace UI {
+ namespace Core {
+ struct ICoreWindow;
+ struct ICoreCursorFactory;
+ }
+ }
+ }
+}
+
+QT_BEGIN_NAMESPACE
+
+class QWinRTCursor : public QPlatformCursor
+{
+public:
+ explicit QWinRTCursor(ABI::Windows::UI::Core::ICoreWindow *window);
+ ~QWinRTCursor();
+#ifndef QT_NO_CURSOR
+ void changeCursor(QCursor * windowCursor, QWindow *);
+#endif
+ QPoint pos() const;
+
+private:
+ ABI::Windows::UI::Core::ICoreWindow *m_window;
+ ABI::Windows::UI::Core::ICoreCursorFactory *m_cursorFactory;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINRTCURSOR_H
diff --git a/src/plugins/platforms/winrt/qwinrteglcontext.cpp b/src/plugins/platforms/winrt/qwinrteglcontext.cpp
new file mode 100644
index 0000000000..014378f896
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrteglcontext.cpp
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwinrteglcontext.h"
+
+QT_BEGIN_NAMESPACE
+
+QWinRTEGLContext::QWinRTEGLContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share, EGLDisplay display, EGLSurface surface)
+ : QEGLPlatformContext(format, share, display, EGL_OPENGL_ES_API), m_eglSurface(surface)
+{
+}
+
+EGLSurface QWinRTEGLContext::eglSurfaceForPlatformSurface(QPlatformSurface *surface)
+{
+ if (surface->surface()->surfaceClass() == QSurface::Window) {
+ // All windows use the same surface
+ return m_eglSurface;
+ } else {
+ // TODO: return EGL surfaces for offscreen surfaces
+ qWarning("This plugin does not support offscreen surfaces.");
+ return EGL_NO_SURFACE;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/winrt/qwinrteglcontext.h b/src/plugins/platforms/winrt/qwinrteglcontext.h
new file mode 100644
index 0000000000..c065847374
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrteglcontext.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSEGLCONTEXT_H
+#define QWINDOWSEGLCONTEXT_H
+
+#include <QtPlatformSupport/private/qeglplatformcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWinRTEGLContext : public QEGLPlatformContext
+{
+public:
+ explicit QWinRTEGLContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share, EGLDisplay display, EGLSurface surface);
+
+protected:
+ EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface);
+
+private:
+ EGLSurface m_eglSurface;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSEGLCONTEXT_H
diff --git a/src/plugins/platforms/winrt/qwinrteventdispatcher.cpp b/src/plugins/platforms/winrt/qwinrteventdispatcher.cpp
new file mode 100644
index 0000000000..3fada75b25
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrteventdispatcher.cpp
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwinrteventdispatcher.h"
+#include <qpa/qwindowsysteminterface.h>
+#include <qpa/qplatformscreen.h>
+#include <qpa/qplatformscreenpageflipper.h>
+#include <QtCore/QThread>
+#include <QtGui/QGuiApplication>
+
+#include <Windows.ui.core.h>
+#include <Windows.ApplicationModel.core.h>
+
+using namespace ABI::Windows::ApplicationModel::Core;
+using namespace ABI::Windows::UI::Core;
+using namespace ABI::Windows::Foundation;
+
+QT_BEGIN_NAMESPACE
+
+QWinRTEventDispatcher::QWinRTEventDispatcher(ICoreDispatcher *dispatcher, QObject *parent)
+ : QEventDispatcherWinRT(parent)
+ , m_dispatcher(dispatcher)
+{
+}
+
+bool QWinRTEventDispatcher::hasPendingEvents()
+{
+ return QEventDispatcherWinRT::hasPendingEvents() || QWindowSystemInterface::windowSystemEventsQueued();
+}
+
+bool QWinRTEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ if (m_dispatcher)
+ m_dispatcher->ProcessEvents(CoreProcessEventsOption_ProcessAllIfPresent);
+
+ const bool didProcess = QWindowSystemInterface::sendWindowSystemEvents(flags);
+
+ return QEventDispatcherWinRT::processEvents(flags & ~QEventLoop::WaitForMoreEvents) || didProcess;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/winrt/qwinrteventdispatcher.h b/src/plugins/platforms/winrt/qwinrteventdispatcher.h
new file mode 100644
index 0000000000..275a508b3c
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrteventdispatcher.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINRTEVENTDISPATCHER_H
+#define QWINRTEVENTDISPATCHER_H
+
+#include <QtCore/QAbstractEventDispatcher>
+#include <QtCore/QEvent>
+
+#include <QtCore/private/qeventdispatcher_winrt_p.h>
+
+namespace ABI {
+ namespace Windows {
+ namespace UI {
+ namespace Core {
+ struct ICoreDispatcher;
+ }
+ }
+ }
+}
+
+QT_BEGIN_NAMESPACE
+
+class QWinRTEventDispatcher : public QEventDispatcherWinRT
+{
+ Q_OBJECT
+public:
+ explicit QWinRTEventDispatcher(ABI::Windows::UI::Core::ICoreDispatcher *dispatcher, QObject *parent = 0);
+
+protected:
+ bool hasPendingEvents();
+ bool processEvents(QEventLoop::ProcessEventsFlags flags);
+
+private:
+ ABI::Windows::UI::Core::ICoreDispatcher *m_dispatcher;
+
+ friend class QWinRTIntegration;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINRTEVENTDISPATCHER_H
diff --git a/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp b/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp
new file mode 100644
index 0000000000..c7fa339fad
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwinrtfontdatabase.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QFile>
+
+QT_BEGIN_NAMESPACE
+
+QString QWinRTFontDatabase::fontDir() const
+{
+ QString fontDirectory = QBasicFontDatabase::fontDir();
+ if (!QFile::exists(fontDirectory)) {
+ // Fall back to app directory + fonts, and just app directory after that
+ const QString applicationDirPath = QCoreApplication::applicationDirPath();
+ fontDirectory = applicationDirPath + QLatin1String("/fonts");
+ if (!QFile::exists(fontDirectory)) {
+ qWarning("No fonts directory found in application package.");
+ fontDirectory = applicationDirPath;
+ }
+ }
+ return fontDirectory;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/winrt/qwinrtfontdatabase.h b/src/plugins/platforms/winrt/qwinrtfontdatabase.h
new file mode 100644
index 0000000000..49e32470c2
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrtfontdatabase.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINRTFONTDATABASE_H
+#define QWINRTFONTDATABASE_H
+
+#include <QtPlatformSupport/private/qbasicfontdatabase_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWinRTFontDatabase : public QBasicFontDatabase
+{
+public:
+ QString fontDir() const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINRTFONTDATABASE_H
diff --git a/src/plugins/platforms/winrt/qwinrtinputcontext.cpp b/src/plugins/platforms/winrt/qwinrtinputcontext.cpp
new file mode 100644
index 0000000000..bc15f1e448
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrtinputcontext.cpp
@@ -0,0 +1,300 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwinrtinputcontext.h"
+#include <QtGui/QWindow>
+
+#include <wrl.h>
+#include <roapi.h>
+#include <windows.ui.viewmanagement.h>
+#include <windows.ui.core.h>
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::UI::ViewManagement;
+using namespace ABI::Windows::UI::Core;
+
+#ifdef Q_OS_WINPHONE
+#include <windows.phone.ui.core.h>
+using namespace ABI::Windows::Phone::UI::Core;
+#endif
+
+typedef ITypedEventHandler<InputPane*, InputPaneVisibilityEventArgs*> InputPaneVisibilityHandler;
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QWinRTInputContext
+ \brief Manages Input Method visibility
+ \internal
+ \ingroup qt-qpa-winrt
+
+ Listens to the native virtual keyboard for hide/show events and provides
+ hints to the OS for showing/hiding. On WinRT, showInputPanel()/hideInputPanel()
+ have no effect because WinRT dictates that keyboard presence is user-driven:
+ (http://msdn.microsoft.com/en-us/library/windows/apps/hh465404.aspx)
+ Windows Phone, however, supports direct hiding/showing of the keyboard.
+*/
+
+QWinRTInputContext::QWinRTInputContext(ICoreWindow *window)
+ : m_window(window)
+{
+ IInputPaneStatics *statics;
+ if (FAILED(GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_ViewManagement_InputPane).Get(),
+ &statics))) {
+ qWarning(Q_FUNC_INFO ": failed to retrieve input pane statics.");
+ return;
+ }
+
+ IInputPane *inputPane;
+ statics->GetForCurrentView(&inputPane);
+ statics->Release();
+ if (inputPane) {
+ EventRegistrationToken showToken, hideToken;
+ inputPane->add_Showing(Callback<InputPaneVisibilityHandler>(
+ this, &QWinRTInputContext::onShowing).Get(), &showToken);
+ inputPane->add_Hiding(Callback<InputPaneVisibilityHandler>(
+ this, &QWinRTInputContext::onHiding).Get(), &hideToken);
+
+ Rect rect;
+ inputPane->get_OccludedRect(&rect);
+ m_keyboardRect = QRectF(rect.X, rect.Y, rect.Width, rect.Height);
+ m_isInputPanelVisible = !m_keyboardRect.isEmpty();
+ } else {
+ qWarning(Q_FUNC_INFO ": failed to retrieve InputPane.");
+ }
+}
+
+QRectF QWinRTInputContext::keyboardRect() const
+{
+ return m_keyboardRect;
+}
+
+bool QWinRTInputContext::isInputPanelVisible() const
+{
+ return m_isInputPanelVisible;
+}
+
+HRESULT QWinRTInputContext::onShowing(IInputPane *pane, IInputPaneVisibilityEventArgs *)
+{
+ m_isInputPanelVisible = true;
+ emitInputPanelVisibleChanged();
+
+ Rect rect;
+ pane->get_OccludedRect(&rect);
+ setKeyboardRect(QRectF(rect.X, rect.Y, rect.Width, rect.Height));
+
+ return S_OK;
+}
+
+HRESULT QWinRTInputContext::onHiding(IInputPane *pane, IInputPaneVisibilityEventArgs *)
+{
+ m_isInputPanelVisible = false;
+ emitInputPanelVisibleChanged();
+
+ Rect rect;
+ pane->get_OccludedRect(&rect);
+ setKeyboardRect(QRectF(rect.X, rect.Y, rect.Width, rect.Height));
+
+ return S_OK;
+}
+
+void QWinRTInputContext::setKeyboardRect(const QRectF rect)
+{
+ if (m_keyboardRect == rect)
+ return;
+
+ m_keyboardRect = rect;
+ emitKeyboardRectChanged();
+}
+
+#ifdef Q_OS_WINPHONE
+
+void QWinRTInputContext::showInputPanel()
+{
+ ICoreWindowKeyboardInput *input;
+ if (SUCCEEDED(m_window->QueryInterface(IID_PPV_ARGS(&input)))) {
+ input->put_IsKeyboardInputEnabled(true);
+ input->Release();
+ }
+}
+
+void QWinRTInputContext::hideInputPanel()
+{
+ ICoreWindowKeyboardInput *input;
+ if (SUCCEEDED(m_window->QueryInterface(IID_PPV_ARGS(&input)))) {
+ input->put_IsKeyboardInputEnabled(false);
+ input->Release();
+ }
+}
+
+#else // Q_OS_WINPHONE
+
+// IRawElementProviderSimple
+HRESULT QWinRTInputContext::get_ProviderOptions(ProviderOptions *retVal)
+{
+ *retVal = ProviderOptions_ServerSideProvider|ProviderOptions_UseComThreading;
+ return S_OK;
+}
+
+HRESULT QWinRTInputContext::GetPatternProvider(PATTERNID id, IUnknown **retVal)
+{
+ switch (id) {
+ case 10002: //UIA_ValuePatternId
+ return QueryInterface(__uuidof(IValueProvider), (void**)retVal);
+ break;
+ case 10014: //UIA_TextPatternId:
+ return QueryInterface(__uuidof(ITextProvider), (void**)retVal);
+ case 10029: //UIA_TextChildPatternId:
+ *retVal = nullptr;
+ break;
+ default:
+ qWarning("Unhandled pattern ID: %d", id);
+ break;
+ }
+ return S_OK;
+}
+
+HRESULT QWinRTInputContext::GetPropertyValue(PROPERTYID idProp, VARIANT *retVal)
+{
+ switch (idProp) {
+ case 30003: //UIA_ControlTypePropertyId
+ retVal->vt = VT_I4;
+ retVal->lVal = 50025; //UIA_CustomControlTypeId
+ break;
+ case 30008: //UIA_IsKeyboardFocusablePropertyId
+ case 30009: //UIA_HasKeyboardFocusPropertyId
+ // These are probably never actually called
+ case 30016: //UIA_IsControlElementPropertyId
+ case 30017: //UIA_IsContentElementPropertyId
+ retVal->vt = VT_BOOL;
+ retVal->boolVal = VARIANT_TRUE;
+ break;
+ case 30019: //UIA_IsPasswordPropertyId
+ retVal->vt = VT_BOOL;
+ retVal->boolVal = VARIANT_FALSE;
+ break;
+ case 30020: //UIA_NativeWindowHandlePropertyId
+ retVal->vt = VT_PTR;
+ retVal->punkVal = m_window;
+ break;
+ }
+ return S_OK;
+}
+
+HRESULT QWinRTInputContext::get_HostRawElementProvider(IRawElementProviderSimple **retVal)
+{
+ // Return the window's element provider
+ IInspectable *hostProvider;
+ HRESULT hr = m_window->get_AutomationHostProvider(&hostProvider);
+ if (SUCCEEDED(hr)) {
+ hr = hostProvider->QueryInterface(IID_PPV_ARGS(retVal));
+ hostProvider->Release();
+ }
+ return hr;
+}
+
+// ITextProvider
+HRESULT QWinRTInputContext::GetSelection(SAFEARRAY **)
+{
+ // To be useful, requires listening to the focus object for a selection change and raising an event
+ return S_OK;
+}
+
+HRESULT QWinRTInputContext::GetVisibleRanges(SAFEARRAY **)
+{
+ // To be useful, requires listening to the focus object for a selection change and raising an event
+ return S_OK;
+}
+
+HRESULT QWinRTInputContext::RangeFromChild(IRawElementProviderSimple *,ITextRangeProvider **)
+{
+ // To be useful, requires listening to the focus object for a selection change and raising an event
+ return S_OK;
+}
+
+HRESULT QWinRTInputContext::RangeFromPoint(UiaPoint, ITextRangeProvider **)
+{
+ // To be useful, requires listening to the focus object for a selection change and raising an event
+ return S_OK;
+}
+
+HRESULT QWinRTInputContext::get_DocumentRange(ITextRangeProvider **)
+{
+ // To be useful, requires listening to the focus object for a selection change and raising an event
+ return S_OK;
+}
+
+HRESULT QWinRTInputContext::get_SupportedTextSelection(SupportedTextSelection *)
+{
+ // To be useful, requires listening to the focus object for a selection change and raising an event
+ return S_OK;
+}
+
+// IValueProvider
+HRESULT QWinRTInputContext::SetValue(LPCWSTR)
+{
+ // To be useful, requires listening to the focus object for a value change and raising an event
+ // May be useful for inputPanel autocomplete, etc.
+ return S_OK;
+}
+
+HRESULT QWinRTInputContext::get_Value(BSTR *)
+{
+ // To be useful, requires listening to the focus object for a value change and raising an event
+ // May be useful for inputPanel autocomplete, etc.
+ return S_OK;
+}
+
+HRESULT QWinRTInputContext::get_IsReadOnly(BOOL *isReadOnly)
+{
+ // isReadOnly dictates keyboard opening behavior when view is tapped.
+ // We need to decide if the user tapped within a control which is about to receive focus...
+ // Since this isn't possible (this function gets called before we receive the touch event),
+ // the most platform-aligned option is to show the keyboard if an editable item has focus,
+ // and close the keyboard if it is already open.
+ *isReadOnly = m_isInputPanelVisible || !inputMethodAccepted();
+ return S_OK;
+}
+
+#endif // !Q_OS_WINPHONE
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/winrt/qwinrtinputcontext.h b/src/plugins/platforms/winrt/qwinrtinputcontext.h
new file mode 100644
index 0000000000..0a35f9b6e1
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrtinputcontext.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINRTINPUTCONTEXT_H
+#define QWINRTINPUTCONTEXT_H
+
+#include <qpa/qplatforminputcontext.h>
+#include <QtCore/QRectF>
+
+#include <wrl.h>
+#ifndef Q_OS_WINPHONE
+# include <UIAutomationCore.h>
+#endif
+
+namespace ABI {
+ namespace Windows {
+ namespace UI {
+ namespace Core {
+ struct ICoreWindow;
+ }
+ namespace ViewManagement {
+ struct IInputPane;
+ struct IInputPaneVisibilityEventArgs;
+ }
+ }
+ }
+}
+
+QT_BEGIN_NAMESPACE
+
+class QWinRTInputContext : public QPlatformInputContext
+#ifndef Q_OS_WINPHONE
+ , public Microsoft::WRL::RuntimeClass<
+ Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::WinRtClassicComMix>,
+ IRawElementProviderSimple, ITextProvider, IValueProvider>
+#endif // !Q_OS_WINPHONE
+{
+public:
+ explicit QWinRTInputContext(ABI::Windows::UI::Core::ICoreWindow *window);
+
+ QRectF keyboardRect() const;
+
+ bool isInputPanelVisible() const;
+
+#ifdef Q_OS_WINPHONE
+ void showInputPanel();
+ void hideInputPanel();
+#else // Q_OS_WINPHONE
+ // IRawElementProviderSimple
+ HRESULT __stdcall get_ProviderOptions(ProviderOptions *retVal);
+ HRESULT __stdcall GetPatternProvider(PATTERNID, IUnknown **);
+ HRESULT __stdcall GetPropertyValue(PROPERTYID idProp, VARIANT *retVal);
+ HRESULT __stdcall get_HostRawElementProvider(IRawElementProviderSimple **retVal);
+
+ // ITextProvider
+ HRESULT __stdcall GetSelection(SAFEARRAY **);
+ HRESULT __stdcall GetVisibleRanges(SAFEARRAY **);
+ HRESULT __stdcall RangeFromChild(IRawElementProviderSimple *,ITextRangeProvider **);
+ HRESULT __stdcall RangeFromPoint(UiaPoint, ITextRangeProvider **);
+ HRESULT __stdcall get_DocumentRange(ITextRangeProvider **);
+ HRESULT __stdcall get_SupportedTextSelection(SupportedTextSelection *);
+
+ // IValueProvider
+ HRESULT __stdcall SetValue(LPCWSTR);
+ HRESULT __stdcall get_Value(BSTR *);
+ HRESULT __stdcall get_IsReadOnly(BOOL *);
+#endif // !Q_OS_WINPHONE
+
+private:
+ HRESULT onShowing(ABI::Windows::UI::ViewManagement::IInputPane *,
+ ABI::Windows::UI::ViewManagement::IInputPaneVisibilityEventArgs *);
+ HRESULT onHiding(ABI::Windows::UI::ViewManagement::IInputPane *,
+ ABI::Windows::UI::ViewManagement::IInputPaneVisibilityEventArgs *);
+ void setKeyboardRect(const QRectF rect);
+
+ ABI::Windows::UI::Core::ICoreWindow *m_window;
+ QRectF m_keyboardRect;
+ bool m_isInputPanelVisible;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINRTINPUTCONTEXT_H
diff --git a/src/plugins/platforms/winrt/qwinrtintegration.cpp b/src/plugins/platforms/winrt/qwinrtintegration.cpp
new file mode 100644
index 0000000000..22c50e67f3
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrtintegration.cpp
@@ -0,0 +1,194 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwinrtintegration.h"
+#include "qwinrtwindow.h"
+#include "qwinrteventdispatcher.h"
+#include "qwinrtbackingstore.h"
+#include "qwinrtscreen.h"
+#include "qwinrtinputcontext.h"
+#include "qwinrtservices.h"
+#include "qwinrteglcontext.h"
+#include "qwinrtfontdatabase.h"
+
+#include <QtGui/QOpenGLContext>
+
+#include <wrl.h>
+#include <windows.ui.core.h>
+#include <windows.ui.viewmanagement.h>
+#include <Windows.ApplicationModel.core.h>
+
+using namespace Microsoft::WRL;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::UI::Core;
+using namespace ABI::Windows::UI::ViewManagement;
+using namespace ABI::Windows::ApplicationModel::Core;
+
+static IUISettings *getSettings()
+{
+ static IUISettings *settings = 0;
+ if (!settings) {
+ if (FAILED(RoActivateInstance(Wrappers::HString::MakeReference(RuntimeClass_Windows_UI_ViewManagement_UISettings).Get(),
+ reinterpret_cast<IInspectable **>(&settings)))) {
+ qWarning("Could not activate UISettings.");
+ }
+ }
+ return settings;
+}
+
+QT_BEGIN_NAMESPACE
+
+QWinRTIntegration::QWinRTIntegration()
+ : m_success(false)
+ , m_fontDatabase(new QWinRTFontDatabase)
+ , m_services(new QWinRTServices)
+{
+ // Obtain the WinRT Application, view, and window
+ ICoreApplication *application;
+ if (FAILED(RoGetActivationFactory(Wrappers::HString::MakeReference(RuntimeClass_Windows_ApplicationModel_Core_CoreApplication).Get(),
+ IID_PPV_ARGS(&application))))
+ qCritical("Could not attach to the application factory.");
+
+ ICoreApplicationView *view;
+ if (FAILED(application->GetCurrentView(&view))) {
+ qCritical("Could not obtain the application view - have you started outside of WinRT?");
+ return;
+ }
+
+ // Get core window (will act as our screen)
+ ICoreWindow *window;
+ if (FAILED(view->get_CoreWindow(&window))) {
+ qCritical("Could not obtain the application window - have you started outside of WinRT?");
+ return;
+ }
+ window->Activate();
+ m_screen = new QWinRTScreen(window);
+ screenAdded(m_screen);
+
+ m_success = true;
+}
+
+QWinRTIntegration::~QWinRTIntegration()
+{
+ Windows::Foundation::Uninitialize();
+}
+
+QAbstractEventDispatcher *QWinRTIntegration::createEventDispatcher() const
+{
+ ICoreDispatcher *dispatcher;
+ if (FAILED(m_screen->coreWindow()->get_Dispatcher(&dispatcher)))
+ qCritical("Could not capture UI Dispatcher");
+ return new QWinRTEventDispatcher(dispatcher);
+}
+
+bool QWinRTIntegration::hasCapability(QPlatformIntegration::Capability cap) const
+{
+ switch (cap) {
+ case ThreadedPixmaps:
+ case OpenGL:
+ case ApplicationState:
+ return true;
+ default:
+ return QPlatformIntegration::hasCapability(cap);
+ }
+}
+
+QVariant QWinRTIntegration::styleHint(StyleHint hint) const
+{
+ switch (hint) {
+ case CursorFlashTime:
+ if (IUISettings *settings = getSettings()) {
+ quint32 blinkRate;
+ settings->get_CaretBlinkRate(&blinkRate);
+ return blinkRate;
+ }
+ break;
+ case MouseDoubleClickInterval:
+ if (IUISettings *settings = getSettings()) {
+ quint32 doubleClickTime;
+ settings->get_DoubleClickTime(&doubleClickTime);
+ return doubleClickTime;
+ }
+ case ShowIsFullScreen:
+ return true;
+ default:
+ break;
+ }
+ return QPlatformIntegration::styleHint(hint);
+}
+
+QPlatformWindow *QWinRTIntegration::createPlatformWindow(QWindow *window) const
+{
+ return new QWinRTWindow(window);
+}
+
+QPlatformBackingStore *QWinRTIntegration::createPlatformBackingStore(QWindow *window) const
+{
+ return new QWinRTBackingStore(window);
+}
+
+QPlatformOpenGLContext *QWinRTIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
+{
+ QWinRTScreen *screen = static_cast<QWinRTScreen *>(context->screen()->handle());
+ return new QWinRTEGLContext(context->format(), context->handle(), screen->eglDisplay(), screen->eglSurface());
+}
+
+QPlatformFontDatabase *QWinRTIntegration::fontDatabase() const
+{
+ return m_fontDatabase;
+}
+
+QPlatformInputContext *QWinRTIntegration::inputContext() const
+{
+ return m_screen->inputContext();
+}
+
+QPlatformServices *QWinRTIntegration::services() const
+{
+ return m_services;
+}
+
+Qt::KeyboardModifiers QWinRTIntegration::queryKeyboardModifiers() const
+{
+ return m_screen->keyboardModifiers();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/winrt/qwinrtintegration.h b/src/plugins/platforms/winrt/qwinrtintegration.h
new file mode 100644
index 0000000000..d9438bcb3a
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrtintegration.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINRTINTEGRATION_H
+#define QWINRTINTEGRATION_H
+
+#include <qpa/qplatformintegration.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAbstractEventDispatcher;
+class QWinRTScreen;
+
+class QWinRTIntegration : public QPlatformIntegration
+{
+private:
+ explicit QWinRTIntegration();
+public:
+ ~QWinRTIntegration();
+
+ static QWinRTIntegration *create()
+ {
+ QWinRTIntegration *integration = new QWinRTIntegration;
+ return integration->m_success ? integration : 0;
+ }
+
+ bool hasCapability(QPlatformIntegration::Capability cap) const;
+ QVariant styleHint(StyleHint hint) const;
+
+ QPlatformWindow *createPlatformWindow(QWindow *window) const;
+ QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const;
+ QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const;
+ QAbstractEventDispatcher *createEventDispatcher() const;
+ QPlatformFontDatabase *fontDatabase() const;
+ QPlatformInputContext *inputContext() const;
+ QPlatformServices *services() const;
+ Qt::KeyboardModifiers queryKeyboardModifiers() const;
+
+private:
+ bool m_success;
+ QWinRTScreen *m_screen;
+ QPlatformFontDatabase *m_fontDatabase;
+ QPlatformServices *m_services;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINRTINTEGRATION_H
diff --git a/src/plugins/platforms/winrt/qwinrtscreen.cpp b/src/plugins/platforms/winrt/qwinrtscreen.cpp
new file mode 100644
index 0000000000..911d3619fe
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrtscreen.cpp
@@ -0,0 +1,975 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwinrtscreen.h"
+
+#include "qwinrtbackingstore.h"
+#include "qwinrtinputcontext.h"
+#include "qwinrtcursor.h"
+#include "qwinrteglcontext.h"
+
+#include <QtGui/QSurfaceFormat>
+#include <QtGui/QGuiApplication>
+#include <QtPlatformSupport/private/qeglconvenience_p.h>
+#include <qpa/qwindowsysteminterface.h>
+#include <QtCore/qt_windows.h>
+
+#include <wrl.h>
+#include <windows.system.h>
+#include <windows.devices.input.h>
+#include <windows.ui.h>
+#include <windows.ui.core.h>
+#include <windows.ui.input.h>
+#include <windows.ui.viewmanagement.h>
+#include <windows.graphics.display.h>
+#include <windows.foundation.h>
+
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::System;
+using namespace ABI::Windows::UI::Core;
+using namespace ABI::Windows::UI::Input;
+using namespace ABI::Windows::UI::ViewManagement;
+using namespace ABI::Windows::Devices::Input;
+using namespace ABI::Windows::Graphics::Display;
+
+typedef ITypedEventHandler<CoreWindow*, WindowActivatedEventArgs*> ActivatedHandler;
+typedef ITypedEventHandler<CoreWindow*, CoreWindowEventArgs*> ClosedHandler;
+typedef ITypedEventHandler<CoreWindow*, CharacterReceivedEventArgs*> CharacterReceivedHandler;
+typedef ITypedEventHandler<CoreWindow*, InputEnabledEventArgs*> InputEnabledHandler;
+typedef ITypedEventHandler<CoreWindow*, KeyEventArgs*> KeyHandler;
+typedef ITypedEventHandler<CoreWindow*, PointerEventArgs*> PointerHandler;
+typedef ITypedEventHandler<CoreWindow*, WindowSizeChangedEventArgs*> SizeChangedHandler;
+typedef ITypedEventHandler<CoreWindow*, VisibilityChangedEventArgs*> VisibilityChangedHandler;
+typedef ITypedEventHandler<CoreWindow*, AutomationProviderRequestedEventArgs*> AutomationProviderRequestedHandler;
+
+QT_BEGIN_NAMESPACE
+
+static inline Qt::ScreenOrientations qtOrientationsFromNative(DisplayOrientations native)
+{
+ Qt::ScreenOrientations orientations = Qt::PrimaryOrientation;
+ if (native & DisplayOrientations_Portrait)
+ orientations |= Qt::PortraitOrientation;
+ if (native & DisplayOrientations_PortraitFlipped)
+ orientations |= Qt::InvertedPortraitOrientation;
+ if (native & DisplayOrientations_Landscape)
+ orientations |= Qt::LandscapeOrientation;
+ if (native & DisplayOrientations_LandscapeFlipped)
+ orientations |= Qt::InvertedLandscapeOrientation;
+ return orientations;
+}
+
+static inline DisplayOrientations nativeOrientationsFromQt(Qt::ScreenOrientations orientation)
+{
+ DisplayOrientations native = DisplayOrientations_None;
+ if (orientation & Qt::PortraitOrientation)
+ native |= DisplayOrientations_Portrait;
+ if (orientation & Qt::InvertedPortraitOrientation)
+ native |= DisplayOrientations_PortraitFlipped;
+ if (orientation & Qt::LandscapeOrientation)
+ native |= DisplayOrientations_Landscape;
+ if (orientation & Qt::InvertedLandscapeOrientation)
+ native |= DisplayOrientations_LandscapeFlipped;
+ return native;
+}
+
+static inline bool qIsNonPrintable(quint32 keyCode)
+{
+ switch (keyCode) {
+ case '\b':
+ case '\n':
+ case '\t':
+ case '\r':
+ case '\v':
+ case '\f':
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Return Qt meta key from VirtualKey
+static inline Qt::Key qKeyFromVirtual(VirtualKey key)
+{
+ switch (key) {
+
+ default:
+ return Qt::Key_unknown;
+
+ // Non-printable characters
+ case VirtualKey_Enter:
+ return Qt::Key_Enter;
+ case VirtualKey_Tab:
+ return Qt::Key_Tab;
+ case VirtualKey_Back:
+ return Qt::Key_Backspace;
+
+ // Modifiers
+ case VirtualKey_Shift:
+ case VirtualKey_LeftShift:
+ case VirtualKey_RightShift:
+ return Qt::Key_Shift;
+ case VirtualKey_Control:
+ case VirtualKey_LeftControl:
+ case VirtualKey_RightControl:
+ return Qt::Key_Control;
+ case VirtualKey_Menu:
+ case VirtualKey_LeftMenu:
+ case VirtualKey_RightMenu:
+ return Qt::Key_Alt;
+ case VirtualKey_LeftWindows:
+ case VirtualKey_RightWindows:
+ return Qt::Key_Meta;
+
+ // Toggle keys
+ case VirtualKey_CapitalLock:
+ return Qt::Key_CapsLock;
+ case VirtualKey_NumberKeyLock:
+ return Qt::Key_NumLock;
+ case VirtualKey_Scroll:
+ return Qt::Key_ScrollLock;
+
+ // East-Asian language keys
+ case VirtualKey_Kana:
+ //case VirtualKey_Hangul: // Same enum as Kana
+ return Qt::Key_Kana_Shift;
+ case VirtualKey_Junja:
+ return Qt::Key_Hangul_Jeonja;
+ case VirtualKey_Kanji:
+ //case VirtualKey_Hanja: // Same enum as Kanji
+ return Qt::Key_Kanji;
+ case VirtualKey_ModeChange:
+ return Qt::Key_Mode_switch;
+ case VirtualKey_Convert:
+ return Qt::Key_Henkan;
+ case VirtualKey_NonConvert:
+ return Qt::Key_Muhenkan;
+
+ // Misc. keys
+ case VirtualKey_Cancel:
+ return Qt::Key_Cancel;
+ case VirtualKey_Clear:
+ return Qt::Key_Clear;
+ case VirtualKey_Application:
+ return Qt::Key_ApplicationLeft;
+ case VirtualKey_Sleep:
+ return Qt::Key_Sleep;
+ case VirtualKey_Pause:
+ return Qt::Key_Pause;
+ case VirtualKey_PageUp:
+ return Qt::Key_PageUp;
+ case VirtualKey_PageDown:
+ return Qt::Key_PageDown;
+ case VirtualKey_End:
+ return Qt::Key_End;
+ case VirtualKey_Home:
+ return Qt::Key_Home;
+ case VirtualKey_Left:
+ return Qt::Key_Left;
+ case VirtualKey_Up:
+ return Qt::Key_Up;
+ case VirtualKey_Right:
+ return Qt::Key_Right;
+ case VirtualKey_Down:
+ return Qt::Key_Down;
+ case VirtualKey_Select:
+ return Qt::Key_Select;
+ case VirtualKey_Print:
+ return Qt::Key_Print;
+ case VirtualKey_Execute:
+ return Qt::Key_Execute;
+ case VirtualKey_Insert:
+ return Qt::Key_Insert;
+ case VirtualKey_Delete:
+ return Qt::Key_Delete;
+ case VirtualKey_Help:
+ return Qt::Key_Help;
+ case VirtualKey_Snapshot:
+ return Qt::Key_Camera;
+ case VirtualKey_Escape:
+ return Qt::Key_Escape;
+
+ // Function Keys
+ case VirtualKey_F1:
+ return Qt::Key_F1;
+ case VirtualKey_F2:
+ return Qt::Key_F2;
+ case VirtualKey_F3:
+ return Qt::Key_F3;
+ case VirtualKey_F4:
+ return Qt::Key_F4;
+ case VirtualKey_F5:
+ return Qt::Key_F5;
+ case VirtualKey_F6:
+ return Qt::Key_F6;
+ case VirtualKey_F7:
+ return Qt::Key_F7;
+ case VirtualKey_F8:
+ return Qt::Key_F8;
+ case VirtualKey_F9:
+ return Qt::Key_F9;
+ case VirtualKey_F10:
+ return Qt::Key_F10;
+ case VirtualKey_F11:
+ return Qt::Key_F11;
+ case VirtualKey_F12:
+ return Qt::Key_F12;
+ case VirtualKey_F13:
+ return Qt::Key_F13;
+ case VirtualKey_F14:
+ return Qt::Key_F14;
+ case VirtualKey_F15:
+ return Qt::Key_F15;
+ case VirtualKey_F16:
+ return Qt::Key_F16;
+ case VirtualKey_F17:
+ return Qt::Key_F17;
+ case VirtualKey_F18:
+ return Qt::Key_F18;
+ case VirtualKey_F19:
+ return Qt::Key_F19;
+ case VirtualKey_F20:
+ return Qt::Key_F20;
+ case VirtualKey_F21:
+ return Qt::Key_F21;
+ case VirtualKey_F22:
+ return Qt::Key_F22;
+ case VirtualKey_F23:
+ return Qt::Key_F23;
+ case VirtualKey_F24:
+ return Qt::Key_F24;
+
+ // Character keys
+ case VirtualKey_Space:
+ return Qt::Key_Space;
+ case VirtualKey_Number0:
+ case VirtualKey_NumberPad0:
+ return Qt::Key_0;
+ case VirtualKey_Number1:
+ case VirtualKey_NumberPad1:
+ return Qt::Key_1;
+ case VirtualKey_Number2:
+ case VirtualKey_NumberPad2:
+ return Qt::Key_2;
+ case VirtualKey_Number3:
+ case VirtualKey_NumberPad3:
+ return Qt::Key_3;
+ case VirtualKey_Number4:
+ case VirtualKey_NumberPad4:
+ return Qt::Key_4;
+ case VirtualKey_Number5:
+ case VirtualKey_NumberPad5:
+ return Qt::Key_5;
+ case VirtualKey_Number6:
+ case VirtualKey_NumberPad6:
+ return Qt::Key_6;
+ case VirtualKey_Number7:
+ case VirtualKey_NumberPad7:
+ return Qt::Key_7;
+ case VirtualKey_Number8:
+ case VirtualKey_NumberPad8:
+ return Qt::Key_8;
+ case VirtualKey_Number9:
+ case VirtualKey_NumberPad9:
+ return Qt::Key_9;
+ case VirtualKey_A:
+ return Qt::Key_A;
+ case VirtualKey_B:
+ return Qt::Key_B;
+ case VirtualKey_C:
+ return Qt::Key_C;
+ case VirtualKey_D:
+ return Qt::Key_D;
+ case VirtualKey_E:
+ return Qt::Key_E;
+ case VirtualKey_F:
+ return Qt::Key_F;
+ case VirtualKey_G:
+ return Qt::Key_G;
+ case VirtualKey_H:
+ return Qt::Key_H;
+ case VirtualKey_I:
+ return Qt::Key_I;
+ case VirtualKey_J:
+ return Qt::Key_J;
+ case VirtualKey_K:
+ return Qt::Key_K;
+ case VirtualKey_L:
+ return Qt::Key_L;
+ case VirtualKey_M:
+ return Qt::Key_M;
+ case VirtualKey_N:
+ return Qt::Key_N;
+ case VirtualKey_O:
+ return Qt::Key_O;
+ case VirtualKey_P:
+ return Qt::Key_P;
+ case VirtualKey_Q:
+ return Qt::Key_Q;
+ case VirtualKey_R:
+ return Qt::Key_R;
+ case VirtualKey_S:
+ return Qt::Key_S;
+ case VirtualKey_T:
+ return Qt::Key_T;
+ case VirtualKey_U:
+ return Qt::Key_U;
+ case VirtualKey_V:
+ return Qt::Key_V;
+ case VirtualKey_W:
+ return Qt::Key_W;
+ case VirtualKey_X:
+ return Qt::Key_X;
+ case VirtualKey_Y:
+ return Qt::Key_Y;
+ case VirtualKey_Z:
+ return Qt::Key_Z;
+ case VirtualKey_Multiply:
+ return Qt::Key_9;
+ case VirtualKey_Add:
+ return Qt::Key_9;
+ case VirtualKey_Separator:
+ return Qt::Key_9;
+ case VirtualKey_Subtract:
+ return Qt::Key_9;
+ case VirtualKey_Decimal:
+ return Qt::Key_9;
+ case VirtualKey_Divide:
+ return Qt::Key_9;
+
+ /* Keys with no matching Qt enum (?)
+ case VirtualKey_None:
+ case VirtualKey_LeftButton:
+ case VirtualKey_RightButton:
+ case VirtualKey_MiddleButton:
+ case VirtualKey_XButton1:
+ case VirtualKey_XButton2:
+ case VirtualKey_Final:
+ case VirtualKey_Accept:*/
+ }
+}
+
+static inline Qt::Key qKeyFromCode(quint32 code, int mods)
+{
+ if (code >= 'a' && code <= 'z')
+ code = toupper(code);
+ if ((mods & Qt::ControlModifier) != 0) {
+ if (code >= 0 && code <= 31) // Ctrl+@..Ctrl+A..CTRL+Z..Ctrl+_
+ code += '@'; // to @..A..Z.._
+ }
+ return static_cast<Qt::Key>(code & 0xff);
+}
+
+QWinRTScreen::QWinRTScreen(ICoreWindow *window)
+ : m_coreWindow(window)
+ , m_depth(32)
+ , m_format(QImage::Format_ARGB32_Premultiplied)
+#ifdef Q_OS_WINPHONE
+ , m_inputContext(new QWinRTInputContext(m_coreWindow))
+#else
+ , m_inputContext(Make<QWinRTInputContext>(m_coreWindow).Detach())
+#endif
+ , m_cursor(new QWinRTCursor(window))
+ , m_orientation(Qt::PrimaryOrientation)
+{
+#ifdef Q_OS_WINPHONE // On phone, there can be only one touch device
+ QTouchDevice *touchDevice = new QTouchDevice;
+ touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::Pressure);
+ touchDevice->setType(QTouchDevice::TouchScreen);
+ touchDevice->setName(QStringLiteral("WinPhoneTouchScreen"));
+ Pointer pointer = { Pointer::TouchScreen, touchDevice };
+ m_pointers.insert(0, pointer);
+ QWindowSystemInterface::registerTouchDevice(touchDevice);
+#endif
+
+ Rect rect;
+ window->get_Bounds(&rect);
+ m_geometry = QRect(0, 0, rect.Width, rect.Height);
+
+ m_surfaceFormat.setAlphaBufferSize(0);
+ m_surfaceFormat.setRedBufferSize(8);
+ m_surfaceFormat.setGreenBufferSize(8);
+ m_surfaceFormat.setBlueBufferSize(8);
+
+ m_surfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES);
+ m_surfaceFormat.setSamples(1);
+ m_surfaceFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
+ m_surfaceFormat.setDepthBufferSize(24);
+ m_surfaceFormat.setStencilBufferSize(8);
+
+ m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (m_eglDisplay == EGL_NO_DISPLAY)
+ qFatal("Qt WinRT platform plugin: failed to initialize EGL display.");
+
+ if (!eglInitialize(m_eglDisplay, NULL, NULL))
+ qFatal("Qt WinRT platform plugin: failed to initialize EGL. This can happen if you haven't included the D3D compiler DLL in your application package.");
+
+ // TODO: move this to Window
+ m_eglSurface = eglCreateWindowSurface(m_eglDisplay, q_configFromGLFormat(m_eglDisplay, m_surfaceFormat), window, NULL);
+ if (m_eglSurface == EGL_NO_SURFACE)
+ qFatal("Could not create EGL surface, error 0x%X", eglGetError());
+
+ // Event handlers mapped to QEvents
+ m_coreWindow->add_KeyDown(Callback<KeyHandler>(this, &QWinRTScreen::onKeyDown).Get(), &m_tokens[QEvent::KeyPress]);
+ m_coreWindow->add_KeyUp(Callback<KeyHandler>(this, &QWinRTScreen::onKeyUp).Get(), &m_tokens[QEvent::KeyRelease]);
+ m_coreWindow->add_CharacterReceived(Callback<CharacterReceivedHandler>(this, &QWinRTScreen::onCharacterReceived).Get(), &m_tokens[QEvent::User]);
+ m_coreWindow->add_PointerEntered(Callback<PointerHandler>(this, &QWinRTScreen::onPointerEntered).Get(), &m_tokens[QEvent::Enter]);
+ m_coreWindow->add_PointerExited(Callback<PointerHandler>(this, &QWinRTScreen::onPointerExited).Get(), &m_tokens[QEvent::Leave]);
+ m_coreWindow->add_PointerMoved(Callback<PointerHandler>(this, &QWinRTScreen::onPointerUpdated).Get(), &m_tokens[QEvent::MouseMove]);
+ m_coreWindow->add_PointerPressed(Callback<PointerHandler>(this, &QWinRTScreen::onPointerUpdated).Get(), &m_tokens[QEvent::MouseButtonPress]);
+ m_coreWindow->add_PointerReleased(Callback<PointerHandler>(this, &QWinRTScreen::onPointerUpdated).Get(), &m_tokens[QEvent::MouseButtonRelease]);
+ m_coreWindow->add_PointerWheelChanged(Callback<PointerHandler>(this, &QWinRTScreen::onPointerUpdated).Get(), &m_tokens[QEvent::Wheel]);
+ m_coreWindow->add_SizeChanged(Callback<SizeChangedHandler>(this, &QWinRTScreen::onSizeChanged).Get(), &m_tokens[QEvent::Resize]);
+
+ // Window event handlers
+ m_coreWindow->add_Activated(Callback<ActivatedHandler>(this, &QWinRTScreen::onActivated).Get(), &m_tokens[QEvent::WindowActivate]);
+ m_coreWindow->add_Closed(Callback<ClosedHandler>(this, &QWinRTScreen::onClosed).Get(), &m_tokens[QEvent::WindowDeactivate]);
+ m_coreWindow->add_VisibilityChanged(Callback<VisibilityChangedHandler>(this, &QWinRTScreen::onVisibilityChanged).Get(), &m_tokens[QEvent::Show]);
+ m_coreWindow->add_AutomationProviderRequested(Callback<AutomationProviderRequestedHandler>(this, &QWinRTScreen::onAutomationProviderRequested).Get(), &m_tokens[QEvent::InputMethodQuery]);
+
+ // Orientation handling
+ if (SUCCEEDED(GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Graphics_Display_DisplayProperties).Get(),
+ &m_displayProperties))) {
+ // Set native orientation
+ DisplayOrientations displayOrientation;
+ m_displayProperties->get_NativeOrientation(&displayOrientation);
+ m_nativeOrientation = static_cast<Qt::ScreenOrientation>(static_cast<int>(qtOrientationsFromNative(displayOrientation)));
+
+ // Set initial orientation
+ onOrientationChanged(0);
+
+ m_displayProperties->add_OrientationChanged(Callback<IDisplayPropertiesEventHandler>(this, &QWinRTScreen::onOrientationChanged).Get(),
+ &m_tokens[QEvent::OrientationChange]);
+ }
+
+#ifndef Q_OS_WINPHONE
+ GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_ViewManagement_ApplicationView).Get(),
+ &m_applicationView);
+#endif
+}
+
+QRect QWinRTScreen::geometry() const
+{
+ return m_geometry;
+}
+
+int QWinRTScreen::depth() const
+{
+ return m_depth;
+}
+
+QImage::Format QWinRTScreen::format() const
+{
+ return m_format;
+}
+
+QSurfaceFormat QWinRTScreen::surfaceFormat() const
+{
+ return m_surfaceFormat;
+}
+
+QWinRTInputContext *QWinRTScreen::inputContext() const
+{
+ return m_inputContext;
+}
+
+QPlatformCursor *QWinRTScreen::cursor() const
+{
+ return m_cursor;
+}
+
+Qt::KeyboardModifiers QWinRTScreen::keyboardModifiers() const
+{
+ Qt::KeyboardModifiers mods;
+ CoreVirtualKeyStates mod;
+ m_coreWindow->GetAsyncKeyState(VirtualKey_Shift, &mod);
+ if (mod == CoreVirtualKeyStates_Down)
+ mods |= Qt::ShiftModifier;
+ m_coreWindow->GetAsyncKeyState(VirtualKey_Menu, &mod);
+ if (mod == CoreVirtualKeyStates_Down)
+ mods |= Qt::AltModifier;
+ m_coreWindow->GetAsyncKeyState(VirtualKey_Control, &mod);
+ if (mod == CoreVirtualKeyStates_Down)
+ mods |= Qt::ControlModifier;
+ m_coreWindow->GetAsyncKeyState(VirtualKey_LeftWindows, &mod);
+ if (mod == CoreVirtualKeyStates_Down) {
+ mods |= Qt::MetaModifier;
+ } else {
+ m_coreWindow->GetAsyncKeyState(VirtualKey_RightWindows, &mod);
+ if (mod == CoreVirtualKeyStates_Down)
+ mods |= Qt::MetaModifier;
+ }
+ return mods;
+}
+
+Qt::ScreenOrientation QWinRTScreen::nativeOrientation() const
+{
+ return m_nativeOrientation;
+}
+
+Qt::ScreenOrientation QWinRTScreen::orientation() const
+{
+ return m_orientation;
+}
+
+void QWinRTScreen::setOrientationUpdateMask(Qt::ScreenOrientations mask)
+{
+ m_displayProperties->put_AutoRotationPreferences(nativeOrientationsFromQt(mask));
+}
+
+ICoreWindow *QWinRTScreen::coreWindow() const
+{
+ return m_coreWindow;
+}
+
+EGLDisplay QWinRTScreen::eglDisplay() const
+{
+ return m_eglDisplay;
+}
+
+EGLSurface QWinRTScreen::eglSurface() const
+{
+ return m_eglSurface;
+}
+
+QWindow *QWinRTScreen::topWindow() const
+{
+ return m_visibleWindows.isEmpty() ? 0 : m_visibleWindows.first();
+}
+
+void QWinRTScreen::addWindow(QWindow *window)
+{
+ if (window == topWindow())
+ return;
+ m_visibleWindows.prepend(window);
+ QWindowSystemInterface::handleWindowActivated(window, Qt::OtherFocusReason);
+ handleExpose();
+}
+
+void QWinRTScreen::removeWindow(QWindow *window)
+{
+ const bool wasTopWindow = window == topWindow();
+ if (!m_visibleWindows.removeAll(window))
+ return;
+ if (wasTopWindow)
+ QWindowSystemInterface::handleWindowActivated(window, Qt::OtherFocusReason);
+ handleExpose();
+}
+
+void QWinRTScreen::raise(QWindow *window)
+{
+ m_visibleWindows.removeAll(window);
+ addWindow(window);
+}
+
+void QWinRTScreen::lower(QWindow *window)
+{
+ const bool wasTopWindow = window == topWindow();
+ if (wasTopWindow && m_visibleWindows.size() == 1)
+ return;
+ m_visibleWindows.removeAll(window);
+ m_visibleWindows.append(window);
+ if (wasTopWindow)
+ QWindowSystemInterface::handleWindowActivated(window, Qt::OtherFocusReason);
+ handleExpose();
+}
+
+void QWinRTScreen::handleExpose()
+{
+ if (m_visibleWindows.isEmpty())
+ return;
+ QList<QWindow *>::const_iterator it = m_visibleWindows.constBegin();
+ QWindowSystemInterface::handleExposeEvent(*it, m_geometry);
+ while (++it != m_visibleWindows.constEnd())
+ QWindowSystemInterface::handleExposeEvent(*it, QRegion());
+ QWindowSystemInterface::flushWindowSystemEvents();
+}
+
+HRESULT QWinRTScreen::onKeyDown(ABI::Windows::UI::Core::ICoreWindow *window, ABI::Windows::UI::Core::IKeyEventArgs *args)
+{
+ Q_UNUSED(window);
+ VirtualKey virtualKey;
+ args->get_VirtualKey(&virtualKey);
+ Qt::Key key = qKeyFromVirtual(virtualKey);
+ // Defer character key presses to onCharacterReceived
+ if (key == Qt::Key_unknown || (key >= Qt::Key_Space && key <= Qt::Key_ydiaeresis))
+ return S_OK;
+ QWindowSystemInterface::handleKeyEvent(topWindow(), QEvent::KeyPress, key, keyboardModifiers());
+ return S_OK;
+}
+
+HRESULT QWinRTScreen::onKeyUp(ABI::Windows::UI::Core::ICoreWindow *window, ABI::Windows::UI::Core::IKeyEventArgs *args)
+{
+ Q_UNUSED(window);
+ Qt::KeyboardModifiers mods = keyboardModifiers();
+#ifndef Q_OS_WINPHONE
+ CorePhysicalKeyStatus status; // Look for a pressed character key
+ if (SUCCEEDED(args->get_KeyStatus(&status)) && m_activeKeys.contains(status.ScanCode)) {
+ QPair<Qt::Key, QString> keyStatus = m_activeKeys.take(status.ScanCode);
+ QWindowSystemInterface::handleKeyEvent(topWindow(), QEvent::KeyRelease,
+ keyStatus.first, mods, keyStatus.second);
+ return S_OK;
+ }
+#endif // !Q_OS_WINPHONE
+ VirtualKey virtualKey;
+ args->get_VirtualKey(&virtualKey);
+ QWindowSystemInterface::handleKeyEvent(topWindow(), QEvent::KeyRelease,
+ qKeyFromVirtual(virtualKey), mods);
+ return S_OK;
+}
+
+HRESULT QWinRTScreen::onCharacterReceived(ICoreWindow *window, ICharacterReceivedEventArgs *args)
+{
+ Q_UNUSED(window);
+
+ quint32 keyCode;
+ args->get_KeyCode(&keyCode);
+ // Don't generate character events for non-printables; the meta key stage is enough
+ if (qIsNonPrintable(keyCode))
+ return S_OK;
+
+ Qt::KeyboardModifiers mods = keyboardModifiers();
+ Qt::Key key = qKeyFromCode(keyCode, mods);
+ QString text = QChar(keyCode);
+ QWindowSystemInterface::handleKeyEvent(topWindow(), QEvent::KeyPress, key, mods, text);
+#ifndef Q_OS_WINPHONE
+ CorePhysicalKeyStatus status; // Defer release to onKeyUp for physical keys
+ if (SUCCEEDED(args->get_KeyStatus(&status)) && !status.IsKeyReleased) {
+ m_activeKeys.insert(status.ScanCode, qMakePair(key, text));
+ return S_OK;
+ }
+#endif // !Q_OS_WINPHONE
+ QWindowSystemInterface::handleKeyEvent(topWindow(), QEvent::KeyRelease, key, mods, text);
+ return S_OK;
+}
+
+HRESULT QWinRTScreen::onPointerEntered(ICoreWindow *window, IPointerEventArgs *args)
+{
+ Q_UNUSED(window);
+ IPointerPoint *pointerPoint;
+ if (SUCCEEDED(args->get_CurrentPoint(&pointerPoint))) {
+ // Assumes full-screen window
+ Point point;
+ pointerPoint->get_Position(&point);
+ QPoint pos(point.X, point.Y);
+
+ QWindowSystemInterface::handleEnterEvent(topWindow(), pos, pos);
+ pointerPoint->Release();
+ }
+ return S_OK;
+}
+
+HRESULT QWinRTScreen::onPointerExited(ICoreWindow *window, IPointerEventArgs *args)
+{
+ Q_UNUSED(window);
+ Q_UNUSED(args);
+ QWindowSystemInterface::handleLeaveEvent(0);
+ return S_OK;
+}
+
+HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *window, IPointerEventArgs *args)
+{
+ Q_UNUSED(window);
+
+ IPointerPoint *pointerPoint;
+ if (FAILED(args->get_CurrentPoint(&pointerPoint)))
+ return E_INVALIDARG;
+
+ // Common traits - point, modifiers, properties
+ Point point;
+ pointerPoint->get_Position(&point);
+ QPointF pos(point.X, point.Y);
+
+ VirtualKeyModifiers modifiers;
+ args->get_KeyModifiers(&modifiers);
+ Qt::KeyboardModifiers mods;
+ if (modifiers & VirtualKeyModifiers_Control)
+ mods |= Qt::ControlModifier;
+ if (modifiers & VirtualKeyModifiers_Menu)
+ mods |= Qt::AltModifier;
+ if (modifiers & VirtualKeyModifiers_Shift)
+ mods |= Qt::ShiftModifier;
+ if (modifiers & VirtualKeyModifiers_Windows)
+ mods |= Qt::MetaModifier;
+
+ IPointerPointProperties *properties;
+ if (FAILED(pointerPoint->get_Properties(&properties)))
+ return E_INVALIDARG;
+
+#ifdef Q_OS_WINPHONE
+ quint32 pointerId = 0;
+ Pointer pointer = m_pointers.value(pointerId);
+#else
+ Pointer pointer = { Pointer::Unknown, 0 };
+ quint32 pointerId;
+ pointerPoint->get_PointerId(&pointerId);
+ if (m_pointers.contains(pointerId)) {
+ pointer = m_pointers.value(pointerId);
+ } else { // We have not yet enumerated this device. Do so now...
+ IPointerDevice *device;
+ if (SUCCEEDED(pointerPoint->get_PointerDevice(&device))) {
+ PointerDeviceType type;
+ device->get_PointerDeviceType(&type);
+ switch (type) {
+ case PointerDeviceType_Touch:
+ pointer.type = Pointer::TouchScreen;
+ pointer.device = new QTouchDevice;
+ pointer.device->setName(QStringLiteral("WinRT TouchScreen ") + QString::number(pointerId));
+ // TODO: We may want to probe the device usage flags for more accurate values for these next two
+ pointer.device->setType(QTouchDevice::TouchScreen);
+ pointer.device->setCapabilities(QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::Pressure);
+ QWindowSystemInterface::registerTouchDevice(pointer.device);
+ break;
+
+ case PointerDeviceType_Pen:
+ pointer.type = Pointer::Tablet;
+ break;
+
+ case PointerDeviceType_Mouse:
+ pointer.type = Pointer::Mouse;
+ break;
+ }
+
+ m_pointers.insert(pointerId, pointer);
+ device->Release();
+ }
+ }
+#endif
+ switch (pointer.type) {
+ case Pointer::Mouse: {
+ qint32 delta;
+ properties->get_MouseWheelDelta(&delta);
+ if (delta) {
+ boolean isHorizontal;
+ properties->get_IsHorizontalMouseWheel(&isHorizontal);
+ QPoint angleDelta(isHorizontal ? delta : 0, isHorizontal ? 0 : delta);
+ QWindowSystemInterface::handleWheelEvent(topWindow(), pos, pos, QPoint(), angleDelta, mods);
+ break;
+ }
+
+ boolean isPressed;
+ Qt::MouseButtons buttons = Qt::NoButton;
+ properties->get_IsLeftButtonPressed(&isPressed);
+ if (isPressed)
+ buttons |= Qt::LeftButton;
+
+ properties->get_IsMiddleButtonPressed(&isPressed);
+ if (isPressed)
+ buttons |= Qt::MiddleButton;
+
+ properties->get_IsRightButtonPressed(&isPressed);
+ if (isPressed)
+ buttons |= Qt::RightButton;
+
+ properties->get_IsXButton1Pressed(&isPressed);
+ if (isPressed)
+ buttons |= Qt::XButton1;
+
+ properties->get_IsXButton2Pressed(&isPressed);
+ if (isPressed)
+ buttons |= Qt::XButton2;
+
+ QWindowSystemInterface::handleMouseEvent(topWindow(), pos, pos, buttons, mods);
+
+ break;
+ }
+ case Pointer::TouchScreen: {
+ quint32 id;
+ pointerPoint->get_PointerId(&id);
+
+ Rect area;
+ properties->get_ContactRect(&area);
+
+ float pressure;
+ properties->get_Pressure(&pressure);
+
+ QHash<quint32, QWindowSystemInterface::TouchPoint>::iterator it = m_touchPoints.find(id);
+ if (it != m_touchPoints.end()) {
+ boolean isPressed;
+#ifndef Q_OS_WINPHONE
+ pointerPoint->get_IsInContact(&isPressed);
+#else
+ properties->get_IsLeftButtonPressed(&isPressed); // IsInContact not reliable on phone
+#endif
+ it.value().state = isPressed ? Qt::TouchPointMoved : Qt::TouchPointReleased;
+ } else {
+ it = m_touchPoints.insert(id, QWindowSystemInterface::TouchPoint());
+ it.value().state = Qt::TouchPointPressed;
+ it.value().id = id;
+ }
+ it.value().area = QRectF(area.X, area.Y, area.Width, area.Height);
+ it.value().normalPosition = QPointF(pos.x()/m_geometry.width(), pos.y()/m_geometry.height());
+ it.value().pressure = pressure;
+
+ QWindowSystemInterface::handleTouchEvent(topWindow(), pointer.device, m_touchPoints.values(), mods);
+
+ // Remove released points, station others
+ for (QHash<quint32, QWindowSystemInterface::TouchPoint>::iterator i = m_touchPoints.begin(); i != m_touchPoints.end();) {
+ if (i.value().state == Qt::TouchPointReleased)
+ i = m_touchPoints.erase(i);
+ else
+ (i++).value().state = Qt::TouchPointStationary;
+ }
+
+ break;
+ }
+ case Pointer::Tablet: {
+ quint32 id;
+ pointerPoint->get_PointerId(&id);
+
+ boolean isPressed;
+ pointerPoint->get_IsInContact(&isPressed);
+
+ boolean isEraser;
+ properties->get_IsEraser(&isEraser);
+ int pointerType = isEraser ? 3 : 1;
+
+ float pressure;
+ properties->get_Pressure(&pressure);
+
+ float xTilt;
+ properties->get_XTilt(&xTilt);
+
+ float yTilt;
+ properties->get_YTilt(&yTilt);
+
+ float rotation;
+ properties->get_Twist(&rotation);
+
+ QWindowSystemInterface::handleTabletEvent(topWindow(), isPressed, pos, pos, pointerId,
+ pointerType, pressure, xTilt, yTilt,
+ 0, rotation, 0, id, mods);
+
+ break;
+ }
+ }
+
+ properties->Release();
+ pointerPoint->Release();
+
+ return S_OK;
+}
+
+HRESULT QWinRTScreen::onAutomationProviderRequested(ICoreWindow *, IAutomationProviderRequestedEventArgs *args)
+{
+#ifndef Q_OS_WINPHONE
+ args->put_AutomationProvider(m_inputContext);
+#endif
+ return S_OK;
+}
+
+HRESULT QWinRTScreen::onSizeChanged(ICoreWindow *window, IWindowSizeChangedEventArgs *args)
+{
+ Q_UNUSED(window);
+
+ Size size;
+ if (FAILED(args->get_Size(&size))) {
+ qWarning(Q_FUNC_INFO ": failed to get size");
+ return S_OK;
+ }
+
+ // Regardless of state, all top-level windows are viewport-sized - this might change if
+ // a more advanced compositor is written.
+ m_geometry.setSize(QSize(size.Width, size.Height));
+ QWindowSystemInterface::handleScreenGeometryChange(screen(), m_geometry);
+ QWindowSystemInterface::handleScreenAvailableGeometryChange(screen(), m_geometry);
+ QPlatformScreen::resizeMaximizedWindows();
+ handleExpose();
+
+ return S_OK;
+}
+
+HRESULT QWinRTScreen::onActivated(ICoreWindow *window, IWindowActivatedEventArgs *args)
+{
+ Q_UNUSED(window);
+
+ CoreWindowActivationState activationState;
+ args->get_WindowActivationState(&activationState);
+ if (activationState == CoreWindowActivationState_Deactivated) {
+ QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive);
+ return S_OK;
+ }
+
+ // Activate topWindow
+ if (!m_visibleWindows.isEmpty()) {
+ Qt::FocusReason focusReason = activationState == CoreWindowActivationState_PointerActivated
+ ? Qt::MouseFocusReason : Qt::ActiveWindowFocusReason;
+ QWindowSystemInterface::handleWindowActivated(topWindow(), focusReason);
+ }
+ return S_OK;
+}
+
+HRESULT QWinRTScreen::onClosed(ICoreWindow *window, ICoreWindowEventArgs *args)
+{
+ Q_UNUSED(window);
+ Q_UNUSED(args);
+
+ foreach (QWindow *w, QGuiApplication::topLevelWindows())
+ QWindowSystemInterface::handleCloseEvent(w);
+ return S_OK;
+}
+
+HRESULT QWinRTScreen::onVisibilityChanged(ICoreWindow *window, IVisibilityChangedEventArgs *args)
+{
+ Q_UNUSED(window);
+ Q_UNUSED(args);
+
+ boolean visible;
+ args->get_Visible(&visible);
+ QWindowSystemInterface::handleApplicationStateChanged(visible ? Qt::ApplicationActive : Qt::ApplicationHidden);
+ return S_OK;
+}
+
+HRESULT QWinRTScreen::onOrientationChanged(IInspectable *)
+{
+ DisplayOrientations displayOrientation;
+ m_displayProperties->get_CurrentOrientation(&displayOrientation);
+ Qt::ScreenOrientation newOrientation = static_cast<Qt::ScreenOrientation>(static_cast<int>(qtOrientationsFromNative(displayOrientation)));
+ if (m_orientation != newOrientation) {
+ m_orientation = newOrientation;
+ QWindowSystemInterface::handleScreenOrientationChange(screen(), m_orientation);
+ }
+
+ return S_OK;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/winrt/qwinrtscreen.h b/src/plugins/platforms/winrt/qwinrtscreen.h
new file mode 100644
index 0000000000..21e50fa10a
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrtscreen.h
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINRTSCREEN_H
+#define QWINRTSCREEN_H
+
+#include <qpa/qplatformscreen.h>
+#include <qpa/qwindowsysteminterface.h>
+
+#include <QtCore/QHash>
+#include <QtGui/QSurfaceFormat>
+#include <EGL/egl.h>
+
+#include <EventToken.h>
+
+namespace ABI {
+ namespace Windows {
+ namespace UI {
+ namespace Core {
+ struct IAutomationProviderRequestedEventArgs;
+ struct ICharacterReceivedEventArgs;
+ struct ICoreWindow;
+ struct ICoreWindowEventArgs;
+ struct IKeyEventArgs;
+ struct IPointerEventArgs;
+ struct IVisibilityChangedEventArgs;
+ struct IWindowActivatedEventArgs;
+ struct IWindowSizeChangedEventArgs;
+ }
+ namespace ViewManagement {
+ struct IApplicationViewStatics;
+ }
+ }
+ namespace Graphics {
+ namespace Display {
+ struct IDisplayPropertiesStatics;
+ }
+ }
+ }
+}
+struct IInspectable;
+
+QT_BEGIN_NAMESPACE
+
+class QTouchDevice;
+class QWinRTEGLContext;
+class QWinRTPageFlipper;
+class QWinRTCursor;
+class QWinRTInputContext;
+
+struct Pointer {
+ enum Type { Unknown, Mouse, TouchScreen, Tablet };
+ Type type;
+ QTouchDevice *device;
+};
+
+class QWinRTScreen : public QPlatformScreen
+{
+public:
+ explicit QWinRTScreen(ABI::Windows::UI::Core::ICoreWindow *window);
+ QRect geometry() const;
+ int depth() const;
+ QImage::Format format() const;
+ QSurfaceFormat surfaceFormat() const;
+ QWinRTInputContext *inputContext() const;
+ QPlatformCursor *cursor() const;
+ Qt::KeyboardModifiers keyboardModifiers() const;
+
+ Qt::ScreenOrientation nativeOrientation() const;
+ Qt::ScreenOrientation orientation() const;
+ void setOrientationUpdateMask(Qt::ScreenOrientations mask);
+
+ QWindow *topWindow() const;
+ void addWindow(QWindow *window);
+ void removeWindow(QWindow *window);
+ void raise(QWindow *window);
+ void lower(QWindow *window);
+
+ ABI::Windows::UI::Core::ICoreWindow *coreWindow() const;
+ EGLDisplay eglDisplay() const; // To opengl context
+ EGLSurface eglSurface() const; // To window
+
+private:
+ void handleExpose();
+
+ // Event handlers
+ QHash<QEvent::Type, EventRegistrationToken> m_tokens;
+
+ HRESULT onKeyDown(ABI::Windows::UI::Core::ICoreWindow *window, ABI::Windows::UI::Core::IKeyEventArgs *args);
+ HRESULT onKeyUp(ABI::Windows::UI::Core::ICoreWindow *window, ABI::Windows::UI::Core::IKeyEventArgs *args);
+ HRESULT onCharacterReceived(ABI::Windows::UI::Core::ICoreWindow *window, ABI::Windows::UI::Core::ICharacterReceivedEventArgs *args);
+ HRESULT onPointerEntered(ABI::Windows::UI::Core::ICoreWindow *window, ABI::Windows::UI::Core::IPointerEventArgs *args);
+ HRESULT onPointerExited(ABI::Windows::UI::Core::ICoreWindow *window, ABI::Windows::UI::Core::IPointerEventArgs *args);
+ HRESULT onPointerUpdated(ABI::Windows::UI::Core::ICoreWindow *window, ABI::Windows::UI::Core::IPointerEventArgs *args);
+ HRESULT onSizeChanged(ABI::Windows::UI::Core::ICoreWindow *window, ABI::Windows::UI::Core::IWindowSizeChangedEventArgs *args);
+
+ HRESULT onActivated(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::IWindowActivatedEventArgs *args);
+ HRESULT onClosed(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::ICoreWindowEventArgs *args);
+ HRESULT onVisibilityChanged(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::IVisibilityChangedEventArgs *args);
+ HRESULT onAutomationProviderRequested(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::IAutomationProviderRequestedEventArgs *args);
+
+ HRESULT onOrientationChanged(IInspectable *);
+
+ ABI::Windows::UI::Core::ICoreWindow *m_coreWindow;
+ ABI::Windows::UI::ViewManagement::IApplicationViewStatics *m_applicationView;
+ QRect m_geometry;
+ QImage::Format m_format;
+ QSurfaceFormat m_surfaceFormat;
+ int m_depth;
+ QWinRTInputContext *m_inputContext;
+ QWinRTCursor *m_cursor;
+ QList<QWindow *> m_visibleWindows;
+
+ EGLDisplay m_eglDisplay;
+ EGLSurface m_eglSurface;
+
+ ABI::Windows::Graphics::Display::IDisplayPropertiesStatics *m_displayProperties;
+ Qt::ScreenOrientation m_nativeOrientation;
+ Qt::ScreenOrientation m_orientation;
+
+#ifndef Q_OS_WINPHONE
+ QHash<quint32, QPair<Qt::Key, QString> > m_activeKeys;
+#endif
+ QHash<quint32, Pointer> m_pointers;
+ QHash<quint32, QWindowSystemInterface::TouchPoint> m_touchPoints;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINRTSCREEN_H
diff --git a/src/plugins/platforms/winrt/qwinrtservices.cpp b/src/plugins/platforms/winrt/qwinrtservices.cpp
new file mode 100644
index 0000000000..8f0a1d55bb
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrtservices.cpp
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwinrtservices.h"
+#include <QtCore/QUrl>
+#include <QtCore/QDir>
+#include <QtCore/QCoreApplication>
+
+#include <wrl.h>
+#include <windows.foundation.h>
+#include <windows.storage.h>
+#include <windows.system.h>
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Storage;
+using namespace ABI::Windows::System;
+
+QT_BEGIN_NAMESPACE
+
+QWinRTServices::QWinRTServices()
+{
+ GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Foundation_Uri).Get(), &m_uriFactory);
+ GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_StorageFile).Get(), &m_fileFactory);
+ GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_System_Launcher).Get(), &m_launcher);
+}
+
+QWinRTServices::~QWinRTServices()
+{
+ if (m_uriFactory)
+ m_uriFactory->Release();
+
+ if (m_fileFactory)
+ m_fileFactory->Release();
+
+ if (m_launcher)
+ m_launcher->Release();
+}
+
+bool QWinRTServices::openUrl(const QUrl &url)
+{
+ if (!(m_uriFactory && m_launcher))
+ return QPlatformServices::openUrl(url);
+
+ IUriRuntimeClass *uri;
+ QString urlString = url.toString(); HSTRING uriString; HSTRING_HEADER header;
+ WindowsCreateStringReference((const wchar_t*)urlString.utf16(), urlString.length(), &header, &uriString);
+ m_uriFactory->CreateUri(uriString, &uri);
+ if (!uri)
+ return false;
+
+ IAsyncOperation<bool> *launchOp;
+ m_launcher->LaunchUriAsync(uri, &launchOp);
+ uri->Release();
+ if (!launchOp)
+ return false;
+
+ boolean result = false;
+ while (launchOp->GetResults(&result) == E_ILLEGAL_METHOD_CALL)
+ QCoreApplication::processEvents();
+ launchOp->Release();
+
+ return result;
+}
+
+bool QWinRTServices::openDocument(const QUrl &url)
+{
+ if (!(m_fileFactory && m_launcher))
+ return QPlatformServices::openDocument(url);
+
+ QString pathString = QDir::toNativeSeparators(
+ QDir::cleanPath(qApp->applicationDirPath().append(url.toString(QUrl::RemoveScheme))));
+ HSTRING_HEADER header; HSTRING path;
+ WindowsCreateStringReference((const wchar_t*)pathString.utf16(), pathString.length(), &header, &path);
+ IAsyncOperation<StorageFile*> *fileOp;
+ m_fileFactory->GetFileFromPathAsync(path, &fileOp);
+ if (!fileOp)
+ return false;
+
+ IStorageFile *file = nullptr;
+ while (fileOp->GetResults(&file) == E_ILLEGAL_METHOD_CALL)
+ QCoreApplication::processEvents();
+ fileOp->Release();
+ if (!file)
+ return false;
+
+ IAsyncOperation<bool> *launchOp;
+ m_launcher->LaunchFileAsync(file, &launchOp);
+ if (!launchOp)
+ return false;
+
+ boolean result = false;
+ while (launchOp->GetResults(&result) == E_ILLEGAL_METHOD_CALL)
+ QCoreApplication::processEvents();
+ launchOp->Release();
+
+ return result;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/winrt/qwinrtservices.h b/src/plugins/platforms/winrt/qwinrtservices.h
new file mode 100644
index 0000000000..9cc917030a
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrtservices.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINRTSERVICES_H
+#define QWINRTSERVICES_H
+
+#include <qpa/qplatformservices.h>
+
+namespace ABI {
+ namespace Windows {
+ namespace Foundation {
+ struct IUriRuntimeClassFactory;
+ }
+ namespace Storage {
+ struct IStorageFileStatics;
+ }
+ namespace System {
+ struct ILauncherStatics;
+ }
+ }
+}
+
+QT_BEGIN_NAMESPACE
+
+class QWinRTServices : public QPlatformServices
+{
+public:
+ explicit QWinRTServices();
+ ~QWinRTServices();
+
+ bool openUrl(const QUrl &url);
+ bool openDocument(const QUrl &url);
+
+private:
+ ABI::Windows::Foundation::IUriRuntimeClassFactory *m_uriFactory;
+ ABI::Windows::Storage::IStorageFileStatics *m_fileFactory;
+ ABI::Windows::System::ILauncherStatics *m_launcher;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINRTSERVICES_H
diff --git a/src/plugins/platforms/winrt/qwinrtwindow.cpp b/src/plugins/platforms/winrt/qwinrtwindow.cpp
new file mode 100644
index 0000000000..88b753b463
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrtwindow.cpp
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwinrtwindow.h"
+#include "qwinrtscreen.h"
+
+#include <qpa/qwindowsysteminterface.h>
+#include <qpa/qplatformscreen.h>
+#include <QtGui/QGuiApplication>
+#include <QtGui/QWindow>
+#include <QtGui/QOpenGLContext>
+
+QT_BEGIN_NAMESPACE
+
+QWinRTWindow::QWinRTWindow(QWindow *window)
+ : QPlatformWindow(window)
+ , m_screen(static_cast<QWinRTScreen*>(screen()))
+{
+ setWindowFlags(window->flags());
+ setWindowState(window->windowState());
+ handleContentOrientationChange(window->contentOrientation());
+ setGeometry(window->geometry());
+}
+
+QWinRTWindow::~QWinRTWindow()
+{
+ m_screen->removeWindow(window());
+}
+
+QSurfaceFormat QWinRTWindow::format() const
+{
+ return m_screen->surfaceFormat();
+}
+
+bool QWinRTWindow::isActive() const
+{
+ return m_screen->topWindow() == window();
+}
+
+bool QWinRTWindow::isExposed() const
+{
+ const bool exposed = isActive();
+ return exposed;
+}
+
+void QWinRTWindow::setGeometry(const QRect &rect)
+{
+ if (window()->isTopLevel()) {
+ QPlatformWindow::setGeometry(m_screen->geometry());
+ QWindowSystemInterface::handleGeometryChange(window(), geometry());
+ } else {
+ QPlatformWindow::setGeometry(rect);
+ QWindowSystemInterface::handleGeometryChange(window(), rect);
+ }
+}
+
+void QWinRTWindow::setVisible(bool visible)
+{
+ if (!window()->isTopLevel())
+ return;
+ if (visible)
+ m_screen->addWindow(window());
+ else
+ m_screen->removeWindow(window());
+}
+
+void QWinRTWindow::raise()
+{
+ if (!window()->isTopLevel())
+ return;
+ m_screen->raise(window());
+}
+
+void QWinRTWindow::lower()
+{
+ if (!window()->isTopLevel())
+ return;
+ m_screen->lower(window());
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/winrt/qwinrtwindow.h b/src/plugins/platforms/winrt/qwinrtwindow.h
new file mode 100644
index 0000000000..1f19b4f2d5
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrtwindow.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINRTWINDOW_H
+#define QWINRTWINDOW_H
+
+#include <qpa/qplatformwindow.h>
+#include <qpa/qwindowsysteminterface.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWinRTScreen;
+
+class QWinRTWindow : public QPlatformWindow
+{
+public:
+ QWinRTWindow(QWindow *window);
+ ~QWinRTWindow();
+
+ QSurfaceFormat format() const;
+ bool isActive() const;
+ bool isExposed() const;
+ void setGeometry(const QRect &rect);
+ void setVisible(bool visible);
+ void raise();
+ void lower();
+
+private:
+ QWinRTScreen *m_screen;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINRTWINDOW_H
diff --git a/src/plugins/platforms/winrt/winrt.json b/src/plugins/platforms/winrt/winrt.json
new file mode 100644
index 0000000000..962747b697
--- /dev/null
+++ b/src/plugins/platforms/winrt/winrt.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "winrt" ]
+}
diff --git a/src/plugins/platforms/winrt/winrt.pro b/src/plugins/platforms/winrt/winrt.pro
new file mode 100644
index 0000000000..ea5ff93d00
--- /dev/null
+++ b/src/plugins/platforms/winrt/winrt.pro
@@ -0,0 +1,55 @@
+TARGET = qwinrt
+CONFIG -= precompile_header
+
+PLUGIN_TYPE = platforms
+PLUGIN_CLASS_NAME = QWinRTIntegrationPlugin
+load(qt_plugin)
+
+QT += core-private gui-private platformsupport-private
+
+DEFINES *= QT_NO_CAST_FROM_ASCII __WRL_NO_DEFAULT_LIB__ GL_GLEXT_PROTOTYPES
+
+LIBS += $$QMAKE_LIBS_CORE -ldxgi
+
+SOURCES = \
+ main.cpp \
+ qwinrtbackingstore.cpp \
+ qwinrtcursor.cpp \
+ qwinrteglcontext.cpp \
+ qwinrteventdispatcher.cpp \
+ qwinrtfontdatabase.cpp \
+ qwinrtinputcontext.cpp \
+ qwinrtintegration.cpp \
+ qwinrtscreen.cpp \
+ qwinrtservices.cpp \
+ qwinrtwindow.cpp
+
+HEADERS = \
+ qwinrtbackingstore.h \
+ qwinrtcursor.h \
+ qwinrteglcontext.h \
+ qwinrteventdispatcher.h \
+ qwinrtfontdatabase.h \
+ qwinrtinputcontext.h \
+ qwinrtintegration.h \
+ qwinrtscreen.h \
+ qwinrtservices.h \
+ qwinrtwindow.h
+
+BLIT_INPUT = $$PWD/blit.hlsl
+fxc_blitps.commands = fxc.exe /nologo /T ps_4_0_level_9_1 /E blitps /Vn q_blitps /Fh ${QMAKE_FILE_OUT} ${QMAKE_FILE_NAME}
+fxc_blitps.output = $$OUT_PWD/blitps.h
+fxc_blitps.input = BLIT_INPUT
+fxc_blitps.dependency_type = TYPE_C
+fxc_blitps.variable_out = HEADERS
+fxc_blitps.CONFIG += target_predeps
+fxc_blitvs.commands = fxc.exe /nologo /T vs_4_0_level_9_1 /E blitvs /Vn q_blitvs /Fh ${QMAKE_FILE_OUT} ${QMAKE_FILE_NAME}
+fxc_blitvs.output = $$OUT_PWD/blitvs.h
+fxc_blitvs.input = BLIT_INPUT
+fxc_blitvs.dependency_type = TYPE_C
+fxc_blitvs.variable_out = HEADERS
+fxc_blitvs.CONFIG += target_predeps
+QMAKE_EXTRA_COMPILERS += fxc_blitps fxc_blitvs
+
+OTHER_FILES += winrt.json \
+ blit.hlsl
diff --git a/src/plugins/platforms/xcb/qglxintegration.cpp b/src/plugins/platforms/xcb/qglxintegration.cpp
index e504d93fba..3f1c53b122 100644
--- a/src/plugins/platforms/xcb/qglxintegration.cpp
+++ b/src/plugins/platforms/xcb/qglxintegration.cpp
@@ -167,6 +167,7 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat
, m_shareContext(0)
, m_format(format)
, m_isPBufferCurrent(false)
+ , m_swapInterval(-1)
{
if (m_format.renderableType() == QSurfaceFormat::DefaultRenderableType)
m_format.setRenderableType(QSurfaceFormat::OpenGL);
@@ -326,19 +327,50 @@ QGLXContext::~QGLXContext()
bool QGLXContext::makeCurrent(QPlatformSurface *surface)
{
+ bool success = false;
Q_ASSERT(surface->surface()->surfaceType() == QSurface::OpenGLSurface);
+ Display *dpy = DISPLAY_FROM_XCB(m_screen);
+ GLXDrawable glxDrawable = 0;
QSurface::SurfaceClass surfaceClass = surface->surface()->surfaceClass();
if (surfaceClass == QSurface::Window) {
m_isPBufferCurrent = false;
QXcbWindow *window = static_cast<QXcbWindow *>(surface);
- return glXMakeCurrent(DISPLAY_FROM_XCB(m_screen), window->xcb_window(), m_context);
+ glxDrawable = window->xcb_window();
+ success = glXMakeCurrent(dpy, glxDrawable, m_context);
} else if (surfaceClass == QSurface::Offscreen) {
m_isPBufferCurrent = true;
QGLXPbuffer *pbuffer = static_cast<QGLXPbuffer *>(surface);
- return glXMakeContextCurrent(DISPLAY_FROM_XCB(m_screen), pbuffer->pbuffer(), pbuffer->pbuffer(), m_context);
+ glxDrawable = pbuffer->pbuffer();
+ success = glXMakeContextCurrent(dpy, glxDrawable, glxDrawable, m_context);
+ }
+
+ if (success) {
+ int interval = surface->format().swapInterval();
+ if (interval >= 0 && m_swapInterval != interval) {
+ m_swapInterval = interval;
+ typedef void (*qt_glXSwapIntervalEXT)(Display *, GLXDrawable, int);
+ typedef void (*qt_glXSwapIntervalMESA)(unsigned int);
+ static qt_glXSwapIntervalEXT glXSwapIntervalEXT = 0;
+ static qt_glXSwapIntervalMESA glXSwapIntervalMESA = 0;
+ static bool resolved = false;
+ if (!resolved) {
+ resolved = true;
+ QList<QByteArray> glxExt = QByteArray(glXQueryExtensionsString(dpy,
+ m_screen->screenNumber())).split(' ');
+ if (glxExt.contains("GLX_EXT_swap_control"))
+ glXSwapIntervalEXT = (qt_glXSwapIntervalEXT) getProcAddress("glXSwapIntervalEXT");
+ if (glxExt.contains("GLX_MESA_swap_control"))
+ glXSwapIntervalMESA = (qt_glXSwapIntervalMESA) getProcAddress("glXSwapIntervalMESA");
+ }
+ if (glXSwapIntervalEXT)
+ glXSwapIntervalEXT(dpy, glxDrawable, interval);
+ else if (glXSwapIntervalMESA)
+ glXSwapIntervalMESA(interval);
+ }
}
- return false;
+
+ return success;
}
void QGLXContext::doneCurrent()
diff --git a/src/plugins/platforms/xcb/qglxintegration.h b/src/plugins/platforms/xcb/qglxintegration.h
index dcc7fe8855..00bba94ab3 100644
--- a/src/plugins/platforms/xcb/qglxintegration.h
+++ b/src/plugins/platforms/xcb/qglxintegration.h
@@ -81,7 +81,7 @@ private:
GLXContext m_shareContext;
QSurfaceFormat m_format;
bool m_isPBufferCurrent;
-
+ int m_swapInterval;
static bool m_queriedDummyContext;
static bool m_supportsThreading;
};
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index 4d2735ca85..96e6534b74 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -319,23 +319,6 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
initializeXRandr();
updateScreens();
- m_connectionEventListener = xcb_generate_id(m_connection);
- xcb_create_window(m_connection, XCB_COPY_FROM_PARENT,
- m_connectionEventListener, m_screens.at(0)->root(),
- 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY,
- m_screens.at(0)->screen()->root_visual, 0, 0);
-#ifndef QT_NO_DEBUG
- QByteArray ba("Qt xcb connection listener window");
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_connectionEventListener,
- atom(QXcbAtom::_NET_WM_NAME),
- atom(QXcbAtom::UTF8_STRING),
- 8,
- ba.length(),
- ba.constData()));
-#endif
-
initializeGLX();
initializeXFixes();
initializeXRender();
@@ -372,9 +355,6 @@ QXcbConnection::~QXcbConnection()
#ifndef QT_NO_DRAGANDDROP
delete m_drag;
#endif
- // Delete screens in reverse order to avoid crash in case of multiple screens
- while (!m_screens.isEmpty())
- delete m_screens.takeLast();
#ifdef XCB_USE_XINPUT2_MAEMO
finalizeXInput2Maemo();
@@ -389,6 +369,10 @@ QXcbConnection::~QXcbConnection()
delete m_reader;
+ // Delete screens in reverse order to avoid crash in case of multiple screens
+ while (!m_screens.isEmpty())
+ delete m_screens.takeLast();
+
#ifdef XCB_USE_EGL
if (m_has_egl)
eglTerminate(m_egl_display);
@@ -1066,14 +1050,21 @@ void QXcbConnection::sendConnectionEvent(QXcbAtom::Atom a, uint id)
xcb_client_message_event_t event;
memset(&event, 0, sizeof(event));
+ const xcb_window_t eventListener = xcb_generate_id(m_connection);
+ Q_XCB_CALL(xcb_create_window(m_connection, XCB_COPY_FROM_PARENT,
+ eventListener, m_screens.at(0)->root(),
+ 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY,
+ m_screens.at(0)->screen()->root_visual, 0, 0));
+
event.response_type = XCB_CLIENT_MESSAGE;
event.format = 32;
event.sequence = 0;
- event.window = m_connectionEventListener;
+ event.window = eventListener;
event.type = atom(a);
event.data.data32[0] = id;
- Q_XCB_CALL(xcb_send_event(xcb_connection(), false, m_connectionEventListener, XCB_EVENT_MASK_NO_EVENT, (const char *)&event));
+ Q_XCB_CALL(xcb_send_event(xcb_connection(), false, eventListener, XCB_EVENT_MASK_NO_EVENT, (const char *)&event));
+ Q_XCB_CALL(xcb_destroy_window(m_connection, eventListener));
xcb_flush(xcb_connection());
}
diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h
index ff7a6dd606..3a71eb2574 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -521,8 +521,6 @@ private:
QByteArray m_displayName;
- xcb_window_t m_connectionEventListener;
-
QXcbKeyboard *m_keyboard;
#ifndef QT_NO_CLIPBOARD
QXcbClipboard *m_clipboard;
diff --git a/src/plugins/platforms/xcb/qxcbsessionmanager.cpp b/src/plugins/platforms/xcb/qxcbsessionmanager.cpp
index 9d81c8e224..8c10b134bd 100644
--- a/src/plugins/platforms/xcb/qxcbsessionmanager.cpp
+++ b/src/plugins/platforms/xcb/qxcbsessionmanager.cpp
@@ -50,6 +50,7 @@
#include <X11/SM/SMlib.h>
#include <errno.h> // ERANGE
+#include <cerrno> // ERANGE
class QSmSocketReceiver : public QObject
{
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index e2c6932992..806f948cc2 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -293,7 +293,7 @@ void QXcbWindow::create()
#elif defined(XCB_USE_EGL)
EGLDisplay eglDisplay = connection()->egl_display();
EGLConfig eglConfig = q_configFromGLFormat(eglDisplay, m_format, true);
- m_format = q_glFormatFromConfig(eglDisplay, eglConfig);
+ m_format = q_glFormatFromConfig(eglDisplay, eglConfig, m_format);
VisualID id = QXlibEglIntegration::getCompatibleVisualId(DISPLAY_FROM_XCB(this), eglDisplay, eglConfig);
diff --git a/src/printsupport/widgets/qprintpreviewwidget.cpp b/src/printsupport/widgets/qprintpreviewwidget.cpp
index f788663041..3ef1e882fe 100644
--- a/src/printsupport/widgets/qprintpreviewwidget.cpp
+++ b/src/printsupport/widgets/qprintpreviewwidget.cpp
@@ -162,9 +162,10 @@ signals:
protected:
void resizeEvent(QResizeEvent* e)
{
- const bool blocked = verticalScrollBar()->blockSignals(true); // Don't change page, QTBUG-14517
- QGraphicsView::resizeEvent(e);
- verticalScrollBar()->blockSignals(blocked);
+ {
+ const QSignalBlocker blocker(verticalScrollBar()); // Don't change page, QTBUG-14517
+ QGraphicsView::resizeEvent(e);
+ }
emit resized();
}
diff --git a/src/sql/kernel/qsqlquery.cpp b/src/sql/kernel/qsqlquery.cpp
index 661a582afe..e5f4c5fc56 100644
--- a/src/sql/kernel/qsqlquery.cpp
+++ b/src/sql/kernel/qsqlquery.cpp
@@ -309,19 +309,20 @@ QSqlQuery& QSqlQuery::operator=(const QSqlQuery& other)
}
/*!
- Returns \c true if the query is \l{isActive()}{active} and positioned
- on a valid record and the \a field is NULL; otherwise returns
- false. Note that for some drivers, isNull() will not return accurate
- information until after an attempt is made to retrieve data.
+ Returns \c true if the query is not \l{isActive()}{active},
+ the query is not positioned on a valid record,
+ there is no such field, or the field is null; otherwise \c false.
+ Note that for some drivers, isNull() will not return accurate
+ information until after an attempt is made to retrieve data.
- \sa isActive(), isValid(), value()
+ \sa isActive(), isValid(), value()
*/
bool QSqlQuery::isNull(int field) const
{
- if (d->sqlResult->isActive() && d->sqlResult->isValid())
- return d->sqlResult->isNull(field);
- return true;
+ return !d->sqlResult->isActive()
+ || !d->sqlResult->isValid()
+ || d->sqlResult->isNull(field);
}
/*!
diff --git a/src/src.pro b/src/src.pro
index b7887a6108..74ca7fbf0b 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -149,6 +149,12 @@ SUBDIRS += src_plugins src_tools_qdoc
nacl: SUBDIRS -= src_network src_testlib
+winrt {
+ src_platformsupport.depends -= src_network
+ src_plugins.depends -= src_network
+ SUBDIRS -= src_network
+}
+
android:!android-no-sdk: SUBDIRS += src_android
TR_EXCLUDE = \
@@ -156,4 +162,4 @@ TR_EXCLUDE = \
src_tools_bootstrap_dbus src_tools_qdbusxml2cpp src_tools_qdbuscpp2xml
sub-tools.depends = $$TOOLS
-QMAKE_EXTRA_TARGETS = sub-tools \ No newline at end of file
+QMAKE_EXTRA_TARGETS = sub-tools
diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp
index e170d2a044..224357dd85 100644
--- a/src/testlib/qtestcase.cpp
+++ b/src/testlib/qtestcase.cpp
@@ -165,6 +165,25 @@ QT_BEGIN_NAMESPACE
\sa QVERIFY(), QTRY_COMPARE(), QTest::toString()
*/
+/*! \macro QVERIFY_EXCEPTION_THROWN(expression, exceptiontype)
+ \since 5.3
+
+ \relates QTest
+
+ The QVERIFY_EXCEPTION_THROWN macro executes an \a expression and tries
+ to catch an exception thrown from the \a expression. If the \a expression
+ throws an exception and its type is the same as \a exceptiontype
+ or \a exceptiontype is substitutable with the type of thrown exception
+ (i.e. usually the type of thrown exception is publically derived
+ from \a exceptiontype) then execution will be continued. If not-substitutable
+ type of exception is thrown or the \a expression doesn't throw an exception
+ at all, then a failure will be recorded in the test log and
+ the test won't be executed further.
+
+ \note This macro can only be used in a test function that is invoked
+ by the test framework.
+*/
+
/*! \macro QTRY_VERIFY_WITH_TIMEOUT(condition, timeout)
\since 5.0
@@ -1540,6 +1559,10 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml)
} else if (strcmp(argv[i], "-vb") == 0) {
QBenchmarkGlobalData::current->verboseOutput = true;
+#ifdef Q_OS_WINRT
+ } else if (strncmp(argv[i], "-ServerName:", 12) == 0) {
+ continue;
+#endif
} else if (argv[i][0] == '-') {
fprintf(stderr, "Unknown option: '%s'\n\n%s", argv[i], testOptions);
if (qml) {
@@ -2170,13 +2193,15 @@ int QTest::qExec(QObject *testObject, int argc, char **argv)
qtest_qParseArgs(argc, argv, false);
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
if (!noCrashHandler) {
# ifndef Q_CC_MINGW
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
# endif
+# ifndef Q_OS_WINRT
SetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX);
SetUnhandledExceptionFilter(windowsFaultHandler);
+# endif
} // !noCrashHandler
#endif // Q_OS_WIN) && !Q_OS_WINCE && !Q_OS_WINRT
@@ -2325,6 +2350,27 @@ void QTest::ignoreMessage(QtMsgType type, const char *message)
QTestLog::ignoreMessage(type, message);
}
+/*!
+ \overload
+
+ Ignores messages created by qDebug() or qWarning(). If the \a message
+ matching \a messagePattern
+ with the corresponding \a type is outputted, it will be removed from the
+ test log. If the test finished and the \a message was not outputted,
+ a test failure is appended to the test log.
+
+ \b {Note:} Invoking this function will only ignore one message.
+ If the message you want to ignore is outputted twice, you have to
+ call ignoreMessage() twice, too.
+
+ \since 5.3
+*/
+
+void QTest::ignoreMessage(QtMsgType type, const QRegularExpression &messagePattern)
+{
+ QTestLog::ignoreMessage(type, messagePattern);
+}
+
/*! \internal
*/
diff --git a/src/testlib/qtestcase.h b/src/testlib/qtestcase.h
index ba727b5afe..f95a155ed9 100644
--- a/src/testlib/qtestcase.h
+++ b/src/testlib/qtestcase.h
@@ -51,8 +51,14 @@
#include <string.h>
+#ifndef QT_NO_EXCEPTIONS
+# include <exception>
+#endif // QT_NO_EXCEPTIONS
+
+
QT_BEGIN_NAMESPACE
+class QRegularExpression;
#define QVERIFY(statement) \
do {\
@@ -83,6 +89,46 @@ do {\
return;\
} while (0)
+
+#ifndef QT_NO_EXCEPTIONS
+
+# define QVERIFY_EXCEPTION_THROWN(expression, exceptiontype) \
+ do {\
+ QT_TRY {\
+ QT_TRY {\
+ expression;\
+ QTest::qFail("Expected exception of type " #exceptiontype " to be thrown" \
+ " but no exception caught", __FILE__, __LINE__);\
+ return;\
+ } QT_CATCH (const exceptiontype &) {\
+ }\
+ } QT_CATCH (const std::exception &e) {\
+ QByteArray msg = QByteArray() + "Expected exception of type " #exceptiontype \
+ " to be thrown but std::exception caught with message: " + e.what(); \
+ QTest::qFail(msg.constData(), __FILE__, __LINE__);\
+ return;\
+ } QT_CATCH (...) {\
+ QTest::qFail("Expected exception of type " #exceptiontype " to be thrown" \
+ " but unknown exception caught", __FILE__, __LINE__);\
+ return;\
+ }\
+ } while (0)
+
+#else // QT_NO_EXCEPTIONS
+
+/*
+ * The expression passed to the macro should throw an exception and we can't
+ * catch it because Qt has been compiled without exception support. We can't
+ * skip the expression because it may have side effects and must be executed.
+ * So, users must use Qt with exception support enabled if they use exceptions
+ * in their code.
+ */
+# define QVERIFY_EXCEPTION_THROWN(expression, exceptiontype) \
+ Q_STATIC_ASSERT_X(false, "Support of exceptions is disabled")
+
+#endif // !QT_NO_EXCEPTIONS
+
+
// Will try to wait for the expression to become true while allowing event processing
#define QTRY_VERIFY_WITH_TIMEOUT(__expr, __timeout) \
do { \
@@ -191,6 +237,7 @@ namespace QTest
const char *file, int line);
Q_TESTLIB_EXPORT void qWarn(const char *message, const char *file = 0, int line = 0);
Q_TESTLIB_EXPORT void ignoreMessage(QtMsgType type, const char *message);
+ Q_TESTLIB_EXPORT void ignoreMessage(QtMsgType type, const QRegularExpression &messagePattern);
Q_TESTLIB_EXPORT QString qFindTestData(const char* basepath, const char* file = 0, int line = 0, const char* builddir = 0);
Q_TESTLIB_EXPORT QString qFindTestData(const QString& basepath, const char* file = 0, int line = 0, const char* builddir = 0);
diff --git a/src/testlib/qtestlog.cpp b/src/testlib/qtestlog.cpp
index 60f3630709..0e67335222 100644
--- a/src/testlib/qtestlog.cpp
+++ b/src/testlib/qtestlog.cpp
@@ -49,6 +49,8 @@
#include <QtTest/private/qxmltestlogger_p.h>
#include <QtCore/qatomic.h>
#include <QtCore/qbytearray.h>
+#include <QtCore/QVariant>
+#include <QtCore/QRegularExpression>
#include <stdlib.h>
#include <string.h>
@@ -84,11 +86,8 @@ namespace QTest {
struct IgnoreResultList
{
- inline IgnoreResultList(QtMsgType tp, const char *message)
- : type(tp), next(0)
- { msg = qstrdup(message); }
- inline ~IgnoreResultList()
- { delete [] msg; }
+ inline IgnoreResultList(QtMsgType tp, const QVariant &patternIn)
+ : type(tp), pattern(patternIn), next(0) {}
static inline void clearList(IgnoreResultList *&list)
{
@@ -99,8 +98,29 @@ namespace QTest {
}
}
+ static void append(IgnoreResultList *&list, QtMsgType type, const QVariant &patternIn)
+ {
+ QTest::IgnoreResultList *item = new QTest::IgnoreResultList(type, patternIn);
+
+ if (!list) {
+ list = item;
+ return;
+ }
+ IgnoreResultList *last = list;
+ for ( ; last->next; last = last->next) ;
+ last->next = item;
+ }
+
+ inline bool matches(QtMsgType tp, const QString &message) const
+ {
+ return tp == type
+ && (pattern.type() == QVariant::String ?
+ pattern.toString() == message :
+ pattern.toRegularExpression().match(message).hasMatch());
+ }
+
QtMsgType type;
- char *msg;
+ QVariant pattern;
IgnoreResultList *next;
};
@@ -208,10 +228,13 @@ namespace QTest {
static bool handleIgnoredMessage(QtMsgType type, const char *msg)
{
+ if (!ignoreResultList)
+ return false;
+ const QString message = QString::fromLocal8Bit(msg);
IgnoreResultList *last = 0;
IgnoreResultList *list = ignoreResultList;
while (list) {
- if (list->type == type && strcmp(msg, list->msg) == 0) {
+ if (list->matches(type, message)) {
// remove the item from the list
if (last)
last->next = list->next;
@@ -315,7 +338,11 @@ void QTestLog::printUnhandledIgnoreMessages()
char msg[1024];
QTest::IgnoreResultList *list = QTest::ignoreResultList;
while (list) {
- qsnprintf(msg, 1024, "Did not receive message: \"%s\"", list->msg);
+ if (list->pattern.type() == QVariant::String) {
+ qsnprintf(msg, 1024, "Did not receive message: \"%s\"", qPrintable(list->pattern.toString()));
+ } else {
+ qsnprintf(msg, 1024, "Did not receive any message matching: \"%s\"", qPrintable(list->pattern.toRegularExpression().pattern()));
+ }
QTest::TestLoggers::addMessage(QAbstractTestLogger::Info, msg);
list = list->next;
@@ -461,16 +488,14 @@ void QTestLog::ignoreMessage(QtMsgType type, const char *msg)
{
QTEST_ASSERT(msg);
- QTest::IgnoreResultList *item = new QTest::IgnoreResultList(type, msg);
+ QTest::IgnoreResultList::append(QTest::ignoreResultList, type, QString::fromLocal8Bit(msg));
+}
- QTest::IgnoreResultList *list = QTest::ignoreResultList;
- if (!list) {
- QTest::ignoreResultList = item;
- return;
- }
- while (list->next)
- list = list->next;
- list->next = item;
+void QTestLog::ignoreMessage(QtMsgType type, const QRegularExpression &expression)
+{
+ QTEST_ASSERT(expression.isValid());
+
+ QTest::IgnoreResultList::append(QTest::ignoreResultList, type, QVariant(expression));
}
void QTestLog::setMaxWarnings(int m)
diff --git a/src/testlib/qtestlog_p.h b/src/testlib/qtestlog_p.h
index df3e2ab5d4..bd83870934 100644
--- a/src/testlib/qtestlog_p.h
+++ b/src/testlib/qtestlog_p.h
@@ -58,6 +58,7 @@
QT_BEGIN_NAMESPACE
class QBenchmarkResult;
+class QRegularExpression;
class Q_TESTLIB_EXPORT QTestLog
{
@@ -75,6 +76,7 @@ public:
static void addBenchmarkResult(const QBenchmarkResult &result);
static void ignoreMessage(QtMsgType type, const char *msg);
+ static void ignoreMessage(QtMsgType type, const QRegularExpression &expression);
static int unhandledIgnoreMessages();
static void printUnhandledIgnoreMessages();
static void clearIgnoreMessages();
diff --git a/src/tools/uic/cpp/cppwriteincludes.cpp b/src/tools/uic/cpp/cppwriteincludes.cpp
index c473566e3a..5b7403bf06 100644
--- a/src/tools/uic/cpp/cppwriteincludes.cpp
+++ b/src/tools/uic/cpp/cppwriteincludes.cpp
@@ -127,6 +127,9 @@ void WriteIncludes::acceptUI(DomUI *node)
TreeWalker::acceptUI(node);
+ if (!m_uic->option().includeFile.isEmpty())
+ m_globalIncludes.insert(m_uic->option().includeFile, true);
+
writeHeaders(m_globalIncludes, true);
writeHeaders(m_localIncludes, false);
diff --git a/src/tools/uic/main.cpp b/src/tools/uic/main.cpp
index c29292a99b..cb2bd430ff 100644
--- a/src/tools/uic/main.cpp
+++ b/src/tools/uic/main.cpp
@@ -95,6 +95,11 @@ int runUic(int argc, char *argv[])
translateOption.setValueName(QStringLiteral("function"));
parser.addOption(translateOption);
+ QCommandLineOption includeOption(QStringList() << QStringLiteral("include"));
+ includeOption.setDescription(QStringLiteral("Add #include <include-file> to <file>."));
+ includeOption.setValueName(QStringLiteral("include-file"));
+ parser.addOption(includeOption);
+
QCommandLineOption generatorOption(QStringList() << QStringLiteral("g") << QStringLiteral("generator"));
generatorOption.setDescription(QStringLiteral("Select generator."));
generatorOption.setValueName(QStringLiteral("java|cpp"));
@@ -110,6 +115,7 @@ int runUic(int argc, char *argv[])
driver.option().implicitIncludes = !parser.isSet(noImplicitIncludesOption);
driver.option().postfix = parser.value(postfixOption);
driver.option().translateFunction = parser.value(translateOption);
+ driver.option().includeFile = parser.value(includeOption);
driver.option().generator = (parser.value(generatorOption).toLower() == QLatin1String("java")) ? Option::JavaGenerator : Option::CppGenerator;
QString inputFile;
diff --git a/src/tools/uic/option.h b/src/tools/uic/option.h
index 14ed422d63..52dc731057 100644
--- a/src/tools/uic/option.h
+++ b/src/tools/uic/option.h
@@ -73,6 +73,7 @@ struct Option
QString prefix;
QString postfix;
QString translateFunction;
+ QString includeFile;
#ifdef QT_UIC_JAVA_GENERATOR
QString javaPackage;
QString javaOutputDirectory;
diff --git a/src/widgets/dialogs/qcolordialog.cpp b/src/widgets/dialogs/qcolordialog.cpp
index 2e6518dd17..4c5fb18766 100644
--- a/src/widgets/dialogs/qcolordialog.cpp
+++ b/src/widgets/dialogs/qcolordialog.cpp
@@ -907,10 +907,8 @@ public:
QColSpinBox(QWidget *parent)
: QSpinBox(parent) { setRange(0, 255); }
void setValue(int i) {
- bool block = signalsBlocked();
- blockSignals(true);
+ const QSignalBlocker blocker(this);
QSpinBox::setValue(i);
- blockSignals(block);
}
};
diff --git a/src/widgets/dialogs/qfiledialog.cpp b/src/widgets/dialogs/qfiledialog.cpp
index da026d23a6..1c3a793234 100644
--- a/src/widgets/dialogs/qfiledialog.cpp
+++ b/src/widgets/dialogs/qfiledialog.cpp
@@ -1723,7 +1723,7 @@ int QFileDialogPrivate::maxNameLength(const QString &path)
{
#if defined(Q_OS_UNIX)
return ::pathconf(QFile::encodeName(path).data(), _PC_NAME_MAX);
-#elif defined(Q_OS_WINCE)
+#elif defined(Q_OS_WINCE) || defined(Q_OS_WINRT)
Q_UNUSED(path);
return MAX_PATH;
#elif defined(Q_OS_WIN)
@@ -2621,9 +2621,8 @@ void QFileDialog::accept()
// special case for ".."
if (lineEditText == QLatin1String("..")) {
d->_q_navigateToParent();
- bool block = d->qFileDialogUi->fileNameEdit->blockSignals(true);
+ const QSignalBlocker blocker(d->qFileDialogUi->fileNameEdit);
d->lineEdit()->selectAll();
- d->qFileDialogUi->fileNameEdit->blockSignals(block);
return;
}
diff --git a/src/widgets/dialogs/qfilesystemmodel.cpp b/src/widgets/dialogs/qfilesystemmodel.cpp
index fa6306005b..bda448bde3 100644
--- a/src/widgets/dialogs/qfilesystemmodel.cpp
+++ b/src/widgets/dialogs/qfilesystemmodel.cpp
@@ -1715,7 +1715,7 @@ QFileSystemModelPrivate::QFileSystemNode* QFileSystemModelPrivate::addNode(QFile
#ifndef QT_NO_FILESYSTEMWATCHER
node->populate(info);
#endif
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
//The parentNode is "" so we are listing the drives
if (parentNode->fileName.isEmpty()) {
wchar_t name[MAX_PATH + 1];
diff --git a/src/widgets/dialogs/qfontdialog.cpp b/src/widgets/dialogs/qfontdialog.cpp
index d908a683a9..94e96a52c5 100644
--- a/src/widgets/dialogs/qfontdialog.cpp
+++ b/src/widgets/dialogs/qfontdialog.cpp
@@ -632,12 +632,11 @@ void QFontDialogPrivate::updateSizes()
}
sizeList->setCurrentItem(current);
- sizeEdit->blockSignals(true);
+ const QSignalBlocker blocker(sizeEdit);
sizeEdit->setText((smoothScalable ? QString::number(size) : sizeList->currentText()));
if (q->style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, 0, q)
&& sizeList->hasFocus())
sizeEdit->selectAll();
- sizeEdit->blockSignals(false);
} else {
sizeEdit->clear();
}
@@ -750,9 +749,8 @@ void QFontDialogPrivate::_q_sizeChanged(const QString &s)
if (sizeList->text(i).toInt() >= this->size)
break;
}
- sizeList->blockSignals(true);
+ const QSignalBlocker blocker(sizeList);
sizeList->setCurrentItem(i);
- sizeList->blockSignals(false);
}
_q_updateSample();
}
diff --git a/src/widgets/dialogs/qinputdialog.cpp b/src/widgets/dialogs/qinputdialog.cpp
index 4eec2eb3e2..10d693b4a3 100644
--- a/src/widgets/dialogs/qinputdialog.cpp
+++ b/src/widgets/dialogs/qinputdialog.cpp
@@ -771,10 +771,11 @@ void QInputDialog::setComboBoxItems(const QStringList &items)
Q_D(QInputDialog);
d->ensureComboBox();
- d->comboBox->blockSignals(true);
- d->comboBox->clear();
- d->comboBox->addItems(items);
- d->comboBox->blockSignals(false);
+ {
+ const QSignalBlocker blocker(d->comboBox);
+ d->comboBox->clear();
+ d->comboBox->addItems(items);
+ }
if (inputMode() == TextInput)
d->chooseRightTextInputWidget();
diff --git a/src/widgets/dialogs/qmessagebox.cpp b/src/widgets/dialogs/qmessagebox.cpp
index ee2ef409d8..1b64b0ba67 100644
--- a/src/widgets/dialogs/qmessagebox.cpp
+++ b/src/widgets/dialogs/qmessagebox.cpp
@@ -73,7 +73,7 @@
QT_BEGIN_NAMESPACE
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
HMENU qt_getWindowsSystemMenu(const QWidget *w)
{
if (QWindow *window = QApplicationPrivate::windowForWidget(w))
@@ -1620,7 +1620,7 @@ void QMessageBox::showEvent(QShowEvent *e)
QAccessibleEvent event(this, QAccessible::Alert);
QAccessible::updateAccessibility(&event);
#endif
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
if (const HMENU systemMenu = qt_getWindowsSystemMenu(this)) {
EnableMenuItem(systemMenu, SC_CLOSE, d->detectedEscapeButton ?
MF_BYCOMMAND|MF_ENABLED : MF_BYCOMMAND|MF_GRAYED);
diff --git a/src/widgets/itemviews/qfileiconprovider.cpp b/src/widgets/itemviews/qfileiconprovider.cpp
index 71697ddc40..b12ab736f4 100644
--- a/src/widgets/itemviews/qfileiconprovider.cpp
+++ b/src/widgets/itemviews/qfileiconprovider.cpp
@@ -53,8 +53,10 @@
#if defined(Q_OS_WIN)
# include <qt_windows.h>
-# include <commctrl.h>
-# include <objbase.h>
+# ifndef Q_OS_WINRT
+# include <commctrl.h>
+# include <objbase.h>
+# endif
#endif
#if defined(Q_OS_UNIX) && !defined(QT_NO_STYLE_GTK)
@@ -313,7 +315,7 @@ QIcon QFileIconProvider::icon(const QFileInfo &info) const
return retIcon;
if (info.isRoot())
-#if defined (Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined (Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
{
UINT type = GetDriveType((wchar_t *)info.absoluteFilePath().utf16());
diff --git a/src/widgets/itemviews/qtreewidget.cpp b/src/widgets/itemviews/qtreewidget.cpp
index e75f602e90..3ce757847c 100644
--- a/src/widgets/itemviews/qtreewidget.cpp
+++ b/src/widgets/itemviews/qtreewidget.cpp
@@ -493,8 +493,7 @@ bool QTreeModel::removeRows(int row, int count, const QModelIndex &parent) {
beginRemoveRows(parent, row, row + count - 1);
- bool blockSignal = signalsBlocked();
- blockSignals(true);
+ QSignalBlocker blocker(this);
QTreeWidgetItem *itm = item(parent);
for (int i = row + count - 1; i >= row; --i) {
@@ -504,7 +503,7 @@ bool QTreeModel::removeRows(int row, int count, const QModelIndex &parent) {
delete child;
child = 0;
}
- blockSignals(blockSignal);
+ blocker.unblock();
endRemoveRows();
return true;
diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp
index 9056ffe461..57a2063b78 100644
--- a/src/widgets/kernel/qapplication.cpp
+++ b/src/widgets/kernel/qapplication.cpp
@@ -2596,7 +2596,7 @@ QDesktopWidget *QApplication::desktop()
void QApplication::setStartDragTime(int ms)
{
- Q_UNUSED(ms)
+ QGuiApplication::styleHints()->setStartDragTime(ms);
}
/*!
@@ -2629,7 +2629,7 @@ int QApplication::startDragTime()
void QApplication::setStartDragDistance(int l)
{
- Q_UNUSED(l);
+ QGuiApplication::styleHints()->setStartDragDistance(l);
}
/*!
@@ -3596,7 +3596,7 @@ bool QApplication::keypadNavigationEnabled()
*/
void QApplication::setCursorFlashTime(int msecs)
{
- Q_UNUSED(msecs);
+ QGuiApplication::styleHints()->setCursorFlashTime(msecs);
}
int QApplication::cursorFlashTime()
@@ -3611,12 +3611,10 @@ int QApplication::cursorFlashTime()
The default value on X11 is 400 milliseconds. On Windows and Mac OS, the
operating system's value is used.
-
- Setting the interval is not supported anymore in Qt 5.
*/
void QApplication::setDoubleClickInterval(int ms)
{
- Q_UNUSED(ms);
+ QGuiApplication::styleHints()->setMouseDoubleClickInterval(ms);
}
int QApplication::doubleClickInterval()
@@ -3644,7 +3642,7 @@ int QApplication::doubleClickInterval()
*/
void QApplication::setKeyboardInputInterval(int ms)
{
- Q_UNUSED(ms);
+ QGuiApplication::styleHints()->setKeyboardInputInterval(ms);
}
int QApplication::keyboardInputInterval()
diff --git a/src/widgets/kernel/qapplication_qpa.cpp b/src/widgets/kernel/qapplication_qpa.cpp
index 1f8e950d00..7977ae3528 100644
--- a/src/widgets/kernel/qapplication_qpa.cpp
+++ b/src/widgets/kernel/qapplication_qpa.cpp
@@ -451,7 +451,7 @@ void qt_init(QApplicationPrivate *priv, int type)
QApplicationPrivate::initializeWidgetFontHash();
}
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
// #fixme: Remove.
static HDC displayDC = 0; // display device context
@@ -470,7 +470,7 @@ void qt_cleanup()
QColormap::cleanup();
QApplicationPrivate::active_window = 0; //### this should not be necessary
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
if (displayDC) {
ReleaseDC(0, displayDC);
displayDC = 0;
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp
index 1da0be9781..0d8e20ca0c 100644
--- a/src/widgets/kernel/qwidget.cpp
+++ b/src/widgets/kernel/qwidget.cpp
@@ -4175,7 +4175,7 @@ const QPalette &QWidget::palette() const
if (!isEnabled()) {
data->pal.setCurrentColorGroup(QPalette::Disabled);
} else if ((!isVisible() || isActiveWindow())
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
&& !QApplicationPrivate::isBlockedByModal(const_cast<QWidget *>(this))
#endif
) {
diff --git a/src/widgets/kernel/qwidgetbackingstore.cpp b/src/widgets/kernel/qwidgetbackingstore.cpp
index 02fa80bef6..9fcd14e813 100644
--- a/src/widgets/kernel/qwidgetbackingstore.cpp
+++ b/src/widgets/kernel/qwidgetbackingstore.cpp
@@ -120,7 +120,7 @@ static inline void qt_flush(QWidget *widget, const QRegion &region, QBackingStor
}
#ifndef QT_NO_PAINT_DEBUG
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
static void showYellowThing_win(QWidget *widget, const QRegion &region, int msec)
{
@@ -160,7 +160,7 @@ static void showYellowThing_win(QWidget *widget, const QRegion &region, int msec
QGuiApplication::platformNativeInterface()->nativeResourceForWindow(QByteArrayLiteral("releaseDC"), nativeWindow);
::Sleep(msec);
}
-#endif // Q_OS_WIN
+#endif // defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
void QWidgetBackingStore::showYellowThing(QWidget *widget, const QRegion &toBePainted, int msec, bool unclipped)
{
@@ -175,7 +175,7 @@ void QWidgetBackingStore::showYellowThing(QWidget *widget, const QRegion &toBePa
widget = nativeParent;
}
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
Q_UNUSED(unclipped);
showYellowThing_win(widget, paintRegion, msec);
#else
diff --git a/src/widgets/kernel/win.pri b/src/widgets/kernel/win.pri
index d5cba740d1..76bb709e2b 100644
--- a/src/widgets/kernel/win.pri
+++ b/src/widgets/kernel/win.pri
@@ -2,6 +2,6 @@
# --------------------------------------------------------------------
INCLUDEPATH += ../3rdparty/wintab
-!wince* {
+!wince*:!winrt {
LIBS_PRIVATE *= -lshell32
}
diff --git a/src/widgets/styles/qwindowsstyle.cpp b/src/widgets/styles/qwindowsstyle.cpp
index dde705f9b9..9ab8f8f08a 100644
--- a/src/widgets/styles/qwindowsstyle.cpp
+++ b/src/widgets/styles/qwindowsstyle.cpp
@@ -252,7 +252,7 @@ void QWindowsStyle::polish(QApplication *app)
d->inactiveGradientCaptionColor = app->palette().dark().color();
d->inactiveCaptionText = app->palette().background().color();
-#if defined(Q_OS_WIN) //fetch native title bar colors
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) //fetch native title bar colors
if(app->desktopSettingsAware()){
DWORD activeCaption = GetSysColor(COLOR_ACTIVECAPTION);
DWORD gradientActiveCaption = GetSysColor(COLOR_GRADIENTACTIVECAPTION);
@@ -413,6 +413,7 @@ int QWindowsStyle::pixelMetric(PixelMetric pm, const QStyleOption *opt, const QW
#if defined(Q_OS_WIN)
+#ifndef Q_OS_WINRT // There is no title bar in Windows Runtime applications
case PM_TitleBarHeight:
if (widget && (widget->windowType() == Qt::Tool)) {
// MS always use one less than they say
@@ -426,16 +427,17 @@ int QWindowsStyle::pixelMetric(PixelMetric pm, const QStyleOption *opt, const QW
}
break;
+#endif // !Q_OS_WINRT
case PM_ScrollBarExtent:
{
-#ifndef Q_OS_WINCE
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
NONCLIENTMETRICS ncm;
ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICS, lfMessageFont) + sizeof(LOGFONT);
if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0))
ret = qMax(ncm.iScrollHeight, ncm.iScrollWidth);
else
-#endif
+#endif // !Q_OS_WINCE && !Q_OS_WINRT
ret = QCommonStyle::pixelMetric(pm, opt, widget);
}
break;
@@ -446,6 +448,7 @@ int QWindowsStyle::pixelMetric(PixelMetric pm, const QStyleOption *opt, const QW
break;
#if defined(Q_OS_WIN)
+#ifndef Q_OS_WINRT // Mdi concept not available for WinRT applications
case PM_MdiSubWindowFrameWidth:
#if defined(Q_OS_WINCE)
ret = GetSystemMetrics(SM_CYDLGFRAME);
@@ -453,7 +456,8 @@ int QWindowsStyle::pixelMetric(PixelMetric pm, const QStyleOption *opt, const QW
ret = GetSystemMetrics(SM_CYFRAME);
#endif
break;
-#endif
+#endif // !Q_OS_WINRT
+#endif // Q_OS_WIN
case PM_ToolBarItemMargin:
ret = 1;
break;
@@ -477,7 +481,7 @@ int QWindowsStyle::pixelMetric(PixelMetric pm, const QStyleOption *opt, const QW
QPixmap QWindowsStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
const QWidget *widget) const
{
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
QPixmap desktopIcon;
switch(standardPixmap) {
case SP_DriveCDIcon:
@@ -516,7 +520,7 @@ QPixmap QWindowsStyle::standardPixmap(StandardPixmap standardPixmap, const QStyl
if (!desktopIcon.isNull()) {
return desktopIcon;
}
-#endif
+#endif // Q_OS_WIN && !Q_OS_WINCE && !Q_OS_WINRT
return QCommonStyle::standardPixmap(standardPixmap, opt, widget);
}
@@ -554,7 +558,7 @@ int QWindowsStyle::styleHint(StyleHint hint, const QStyleOption *opt, const QWid
ret = 0;
break;
-#if defined(Q_OS_WIN)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) // Option not used on WinRT -> common style
case SH_UnderlineShortcut:
{
ret = 1;
@@ -590,7 +594,7 @@ int QWindowsStyle::styleHint(StyleHint hint, const QStyleOption *opt, const QWid
#endif // QT_NO_ACCESSIBILITY
break;
}
-#endif
+#endif // Q_OS_WIN && !Q_OS_WINRT
#ifndef QT_NO_RUBBERBAND
case SH_RubberBand_Mask:
if (const QStyleOptionRubberBand *rbOpt = qstyleoption_cast<const QStyleOptionRubberBand *>(opt)) {
diff --git a/src/widgets/util/util.pri b/src/widgets/util/util.pri
index 072c736f71..b4bbc5fc30 100644
--- a/src/widgets/util/util.pri
+++ b/src/widgets/util/util.pri
@@ -27,7 +27,7 @@ SOURCES += \
util/qundostack.cpp \
util/qundoview.cpp
-win32:!wince* {
+win32:!wince*:!winrt {
SOURCES += util/qsystemtrayicon_win.cpp
} else:contains(QT_CONFIG, xcb) {
SOURCES += util/qsystemtrayicon_x11.cpp
diff --git a/src/widgets/widgets/qabstractspinbox.cpp b/src/widgets/widgets/qabstractspinbox.cpp
index 25369a01b6..92af91b66e 100644
--- a/src/widgets/widgets/qabstractspinbox.cpp
+++ b/src/widgets/widgets/qabstractspinbox.cpp
@@ -396,6 +396,30 @@ bool QAbstractSpinBox::isAccelerated() const
}
/*!
+ \property QAbstractSpinBox::showGroupSeparator
+ \since 5.3
+
+
+ This property holds whether a thousands separator is enabled. By default this
+ property is false.
+*/
+bool QAbstractSpinBox::isGroupSeparatorShown() const
+{
+ Q_D(const QAbstractSpinBox);
+ return d->showGroupSeparator;
+}
+
+void QAbstractSpinBox::setGroupSeparatorShown(bool shown)
+{
+ Q_D(QAbstractSpinBox);
+ if (d->showGroupSeparator == shown)
+ return;
+ d->showGroupSeparator = shown;
+ d->setValue(d->value, EmitIfChanged);
+ updateGeometry();
+}
+
+/*!
\enum QAbstractSpinBox::CorrectionMode
This enum type describes the mode the spinbox will use to correct
@@ -1316,7 +1340,8 @@ QAbstractSpinBoxPrivate::QAbstractSpinBoxPrivate()
cachedState(QValidator::Invalid), pendingEmit(false), readOnly(false), wrapping(false),
ignoreCursorPositionChanged(false), frame(true), accelerate(false), keyboardTracking(true),
cleared(false), ignoreUpdateEdit(false), correctionMode(QAbstractSpinBox::CorrectToPreviousValue),
- acceleration(0), hoverControl(QStyle::SC_None), buttonSymbols(QAbstractSpinBox::UpDownArrows), validator(0)
+ acceleration(0), hoverControl(QStyle::SC_None), buttonSymbols(QAbstractSpinBox::UpDownArrows), validator(0),
+ showGroupSeparator(0)
{
}
@@ -1491,13 +1516,12 @@ void QAbstractSpinBoxPrivate::_q_editorCursorPositionChanged(int oldpos, int new
* (newpos < pos ? -1 : 1)) - newpos + pos
: 0;
- const bool wasBlocked = edit->blockSignals(true);
+ const QSignalBlocker blocker(edit);
if (selSize != 0) {
edit->setSelection(pos - selSize, selSize);
} else {
edit->setCursorPosition(pos);
}
- edit->blockSignals(wasBlocked);
}
ignoreCursorPositionChanged = false;
}
@@ -1697,7 +1721,7 @@ void QAbstractSpinBoxPrivate::updateEdit()
const bool empty = edit->text().isEmpty();
int cursor = edit->cursorPosition();
int selsize = edit->selectedText().size();
- const bool sb = edit->blockSignals(true);
+ const QSignalBlocker blocker(edit);
edit->setText(newText);
if (!specialValue()) {
@@ -1709,7 +1733,6 @@ void QAbstractSpinBoxPrivate::updateEdit()
edit->setCursorPosition(empty ? prefix.size() : cursor);
}
}
- edit->blockSignals(sb);
q->update();
}
diff --git a/src/widgets/widgets/qabstractspinbox.h b/src/widgets/widgets/qabstractspinbox.h
index 4f6aad0cde..7989000cc8 100644
--- a/src/widgets/widgets/qabstractspinbox.h
+++ b/src/widgets/widgets/qabstractspinbox.h
@@ -72,6 +72,7 @@ class Q_WIDGETS_EXPORT QAbstractSpinBox : public QWidget
Q_PROPERTY(CorrectionMode correctionMode READ correctionMode WRITE setCorrectionMode)
Q_PROPERTY(bool acceptableInput READ hasAcceptableInput)
Q_PROPERTY(bool keyboardTracking READ keyboardTracking WRITE setKeyboardTracking)
+ Q_PROPERTY(bool showGroupSeparator READ isGroupSeparatorShown WRITE setGroupSeparatorShown)
public:
explicit QAbstractSpinBox(QWidget *parent = 0);
~QAbstractSpinBox();
@@ -114,6 +115,9 @@ public:
void setAccelerated(bool on);
bool isAccelerated() const;
+ void setGroupSeparatorShown(bool shown);
+ bool isGroupSeparatorShown() const;
+
QSize sizeHint() const;
QSize minimumSizeHint() const;
void interpretText();
diff --git a/src/widgets/widgets/qabstractspinbox_p.h b/src/widgets/widgets/qabstractspinbox_p.h
index 0eeec02abc..da9f1d9757 100644
--- a/src/widgets/widgets/qabstractspinbox_p.h
+++ b/src/widgets/widgets/qabstractspinbox_p.h
@@ -150,6 +150,7 @@ public:
QRect hoverRect;
QAbstractSpinBox::ButtonSymbols buttonSymbols;
QSpinBoxValidator *validator;
+ uint showGroupSeparator : 1;
};
class QSpinBoxValidator : public QValidator
diff --git a/src/widgets/widgets/qcombobox.cpp b/src/widgets/widgets/qcombobox.cpp
index 37a14d3eb3..fd74ab7595 100644
--- a/src/widgets/widgets/qcombobox.cpp
+++ b/src/widgets/widgets/qcombobox.cpp
@@ -2608,9 +2608,9 @@ void QComboBox::hidePopup()
Q_D(QComboBox);
if (d->container && d->container->isVisible()) {
#if !defined(QT_NO_EFFECTS)
- d->model->blockSignals(true);
- d->container->itemView()->blockSignals(true);
- d->container->blockSignals(true);
+ QSignalBlocker modelBlocker(d->model);
+ QSignalBlocker viewBlocker(d->container->itemView());
+ QSignalBlocker containerBlocker(d->container);
// Flash selected/triggered item (if any).
if (style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem)) {
QItemSelectionModel *selectionModel = view() ? view()->selectionModel() : 0;
@@ -2646,9 +2646,9 @@ void QComboBox::hidePopup()
#endif // Q_OS_MAC
// Other platform implementations welcome :-)
}
- d->model->blockSignals(false);
- d->container->itemView()->blockSignals(false);
- d->container->blockSignals(false);
+ containerBlocker.unblock();
+ viewBlocker.unblock();
+ modelBlocker.unblock();
if (!didFade)
#endif // QT_NO_EFFECTS
diff --git a/src/widgets/widgets/qdatetimeedit.cpp b/src/widgets/widgets/qdatetimeedit.cpp
index a0bbbbf7c7..6bfc251a2a 100644
--- a/src/widgets/widgets/qdatetimeedit.cpp
+++ b/src/widgets/widgets/qdatetimeedit.cpp
@@ -1718,7 +1718,7 @@ void QDateTimeEditPrivate::updateEdit()
if (newText == displayText())
return;
int selsize = edit->selectedText().size();
- const bool sb = edit->blockSignals(true);
+ const QSignalBlocker blocker(edit);
edit->setText(newText);
@@ -1740,7 +1740,6 @@ void QDateTimeEditPrivate::updateEdit()
}
}
- edit->blockSignals(sb);
}
@@ -1871,7 +1870,7 @@ void QDateTimeEditPrivate::clearSection(int index)
{
const QLatin1Char space(' ');
int cursorPos = edit->cursorPosition();
- bool blocked = edit->blockSignals(true);
+ const QSignalBlocker blocker(edit);
QString t = edit->text();
const int pos = sectionPos(index);
if (pos == -1) {
@@ -1883,8 +1882,6 @@ void QDateTimeEditPrivate::clearSection(int index)
edit->setText(t);
edit->setCursorPosition(cursorPos);
QDTEDEBUG << cursorPos;
-
- edit->blockSignals(blocked);
}
@@ -2574,10 +2571,9 @@ void QDateTimeEditPrivate::syncCalendarWidget()
{
Q_Q(QDateTimeEdit);
if (monthCalendar) {
- const bool sb = monthCalendar->blockSignals(true);
+ const QSignalBlocker blocker(monthCalendar);
monthCalendar->setDateRange(q->minimumDate(), q->maximumDate());
monthCalendar->setDate(q->date());
- monthCalendar->blockSignals(sb);
}
}
diff --git a/src/widgets/widgets/qdockarealayout.cpp b/src/widgets/widgets/qdockarealayout.cpp
index 72a463b30b..8dab6afb81 100644
--- a/src/widgets/widgets/qdockarealayout.cpp
+++ b/src/widgets/widgets/qdockarealayout.cpp
@@ -2096,7 +2096,7 @@ bool QDockAreaLayoutInfo::updateTabBar() const
that->tabBar->setDrawBase(true);
}
- bool blocked = tabBar->blockSignals(true);
+ const QSignalBlocker blocker(tabBar);
bool gap = false;
int tab_idx = 0;
@@ -2147,8 +2147,6 @@ bool QDockAreaLayoutInfo::updateTabBar() const
tabBar->removeTab(tab_idx);
}
- tabBar->blockSignals(blocked);
-
//returns if the tabbar is visible or not
return ( (gap ? 1 : 0) + tabBar->count()) > 1;
}
diff --git a/src/widgets/widgets/qfontcombobox.cpp b/src/widgets/widgets/qfontcombobox.cpp
index 0b0efa2bdf..2bbf3730db 100644
--- a/src/widgets/widgets/qfontcombobox.cpp
+++ b/src/widgets/widgets/qfontcombobox.cpp
@@ -346,9 +346,10 @@ void QFontComboBoxPrivate::_q_updateModel()
//this prevents the current index from changing
//it will be updated just after this
///TODO: we should finda way to avoid blocking signals and have a real update of the model
- const bool old = m->blockSignals(true);
- m->setStringList(list);
- m->blockSignals(old);
+ {
+ const QSignalBlocker blocker(m);
+ m->setStringList(list);
+ }
if (list.isEmpty()) {
if (currentFont != QFont()) {
diff --git a/src/widgets/widgets/qlcdnumber.h b/src/widgets/widgets/qlcdnumber.h
index 3dde1527f2..ba7b2d8494 100644
--- a/src/widgets/widgets/qlcdnumber.h
+++ b/src/widgets/widgets/qlcdnumber.h
@@ -43,7 +43,6 @@
#define QLCDNUMBER_H
#include <QtWidgets/qframe.h>
-#include <QtCore/qbitarray.h>
QT_BEGIN_NAMESPACE
diff --git a/src/widgets/widgets/qmdiarea.cpp b/src/widgets/widgets/qmdiarea.cpp
index 349d8c3423..600d9b536f 100644
--- a/src/widgets/widgets/qmdiarea.cpp
+++ b/src/widgets/widgets/qmdiarea.cpp
@@ -1115,10 +1115,9 @@ void QMdiAreaPrivate::updateActiveWindow(int removedIndex, bool activeRemoved)
#ifndef QT_NO_TABBAR
if (tabBar && removedIndex >= 0) {
- tabBar->blockSignals(true);
+ const QSignalBlocker blocker(tabBar);
tabBar->removeTab(removedIndex);
updateTabBarGeometry();
- tabBar->blockSignals(false);
}
#endif
diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp
index 3a4fd449c8..d1b0da1a55 100644
--- a/src/widgets/widgets/qmenu.cpp
+++ b/src/widgets/widgets/qmenu.cpp
@@ -435,7 +435,7 @@ void QMenuPrivate::hideMenu(QMenu *menu)
if (!menu)
return;
#if !defined(QT_NO_EFFECTS)
- menu->blockSignals(true);
+ QSignalBlocker blocker(menu);
aboutToHide = true;
// Flash item which is about to trigger (if any).
if (menu->style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem)
@@ -455,7 +455,7 @@ void QMenuPrivate::hideMenu(QMenu *menu)
}
aboutToHide = false;
- menu->blockSignals(false);
+ blocker.unblock();
#endif // QT_NO_EFFECTS
menu->close();
}
diff --git a/src/widgets/widgets/qplaintextedit.cpp b/src/widgets/widgets/qplaintextedit.cpp
index 90f2e47cd3..df86d141b4 100644
--- a/src/widgets/widgets/qplaintextedit.cpp
+++ b/src/widgets/widgets/qplaintextedit.cpp
@@ -639,9 +639,10 @@ void QPlainTextEditPrivate::setTopBlock(int blockNumber, int lineNumber, int dx)
lineNumber = maxTopLine - block.firstLineNumber();
}
- bool vbarSignalsBlocked = vbar->blockSignals(true);
- vbar->setValue(newTopLine);
- vbar->blockSignals(vbarSignalsBlocked);
+ {
+ const QSignalBlocker blocker(vbar);
+ vbar->setValue(newTopLine);
+ }
if (!dx && blockNumber == control->topBlock && lineNumber == topLine)
return;
@@ -657,9 +658,10 @@ void QPlainTextEditPrivate::setTopBlock(int blockNumber, int lineNumber, int dx)
control->topBlock = blockNumber;
topLine = lineNumber;
- bool vbarSignalsBlocked = vbar->blockSignals(true);
- vbar->setValue(block.firstLineNumber() + lineNumber);
- vbar->blockSignals(vbarSignalsBlocked);
+ {
+ const QSignalBlocker blocker(vbar);
+ vbar->setValue(block.firstLineNumber() + lineNumber);
+ }
if (dx || dy) {
viewport->scroll(q->isRightToLeft() ? -dx : dx, dy);
@@ -1006,9 +1008,11 @@ void QPlainTextEditPrivate::_q_adjustScrollbars()
QTextBlock firstVisibleBlock = q->firstVisibleBlock();
if (firstVisibleBlock.isValid())
visualTopLine = firstVisibleBlock.firstLineNumber() + topLine;
- bool vbarSignalsBlocked = vbar->blockSignals(true);
- vbar->setValue(visualTopLine);
- vbar->blockSignals(vbarSignalsBlocked);
+
+ {
+ const QSignalBlocker blocker(vbar);
+ vbar->setValue(visualTopLine);
+ }
hbar->setRange(0, (int)documentSize.width() - viewport->width());
hbar->setPageStep(viewport->width());
diff --git a/src/widgets/widgets/qspinbox.cpp b/src/widgets/widgets/qspinbox.cpp
index bf4e130d4e..a43b937951 100644
--- a/src/widgets/widgets/qspinbox.cpp
+++ b/src/widgets/widgets/qspinbox.cpp
@@ -463,8 +463,8 @@ void QSpinBox::setDisplayIntegerBase(int base)
display the given \a value. The default implementation returns a
string containing \a value printed in the standard way using
QWidget::locale().toString(), but with the thousand separator
- removed. Reimplementations may return anything. (See the example
- in the detailed description.)
+ removed unless setGroupSeparatorShown() is set. Reimplementations may
+ return anything. (See the example in the detailed description.)
Note: QSpinBox does not call this function for specialValueText()
and that neither prefix() nor suffix() should be included in the
@@ -487,7 +487,7 @@ QString QSpinBox::textFromValue(int value) const
str.prepend('-');
} else {
str = locale().toString(value);
- if (qAbs(value) >= 1000 || value == INT_MIN) {
+ if (!d->showGroupSeparator && (qAbs(value) >= 1000 || value == INT_MIN)) {
str.remove(locale().groupSeparator());
}
}
@@ -538,7 +538,8 @@ QValidator::State QSpinBox::validate(QString &text, int &pos) const
*/
void QSpinBox::fixup(QString &input) const
{
- input.remove(locale().groupSeparator());
+ if (!isGroupSeparatorShown())
+ input.remove(locale().groupSeparator());
}
@@ -891,7 +892,8 @@ void QDoubleSpinBox::setDecimals(int decimals)
display the given \a value. The default implementation returns a string
containing \a value printed using QWidget::locale().toString(\a value,
QLatin1Char('f'), decimals()) and will remove the thousand
- separator. Reimplementations may return anything.
+ separator unless setGroupSeparatorShown() is set. Reimplementations may
+ return anything.
Note: QDoubleSpinBox does not call this function for
specialValueText() and that neither prefix() nor suffix() should
@@ -908,9 +910,9 @@ QString QDoubleSpinBox::textFromValue(double value) const
{
Q_D(const QDoubleSpinBox);
QString str = locale().toString(value, 'f', d->decimals);
- if (qAbs(value) >= 1000.0) {
+ if (!d->showGroupSeparator && qAbs(value) >= 1000.0)
str.remove(locale().groupSeparator());
- }
+
return str;
}
diff --git a/src/widgets/widgets/qsplashscreen.cpp b/src/widgets/widgets/qsplashscreen.cpp
index b2a0d3f8b8..db9db68039 100644
--- a/src/widgets/widgets/qsplashscreen.cpp
+++ b/src/widgets/widgets/qsplashscreen.cpp
@@ -251,7 +251,9 @@ inline static bool waitForWindowExposed(QWindow *window, int timeout = 1000)
break;
QCoreApplication::processEvents(QEventLoop::AllEvents, remaining);
QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WINRT)
+ WaitForSingleObjectEx(GetCurrentThread(), TimeOutMs, false);
+#elif defined(Q_OS_WIN)
Sleep(uint(TimeOutMs));
#else
struct timespec ts = { TimeOutMs / 1000, (TimeOutMs % 1000) * 1000 * 1000 };
diff --git a/src/widgets/widgets/qtabwidget.cpp b/src/widgets/widgets/qtabwidget.cpp
index 8c15b3e58e..fced1c01ec 100644
--- a/src/widgets/widgets/qtabwidget.cpp
+++ b/src/widgets/widgets/qtabwidget.cpp
@@ -759,11 +759,10 @@ void QTabWidgetPrivate::_q_removeTab(int index)
void QTabWidgetPrivate::_q_tabMoved(int from, int to)
{
- stack->blockSignals(true);
+ const QSignalBlocker blocker(stack);
QWidget *w = stack->widget(from);
stack->removeWidget(w);
stack->insertWidget(to, w);
- stack->blockSignals(false);
}
/*
diff --git a/src/winmain/qtmain_winrt.cpp b/src/winmain/qtmain_winrt.cpp
index d0f138269f..8ad5f56bee 100644
--- a/src/winmain/qtmain_winrt.cpp
+++ b/src/winmain/qtmain_winrt.cpp
@@ -69,7 +69,7 @@ typedef ITypedEventHandler<Core::CoreApplicationView *, Activation::IActivatedEv
class AppContainer : public Microsoft::WRL::RuntimeClass<Core::IFrameworkView>
{
public:
- AppContainer(int argc, wchar_t **argv) : m_argc(argc)
+ AppContainer(int argc, wchar_t **argv) : m_argc(argc), m_debugWait(false)
{
m_argv.reserve(argc);
for (int i = 0; i < argc; ++i) {
@@ -95,6 +95,11 @@ public:
HRESULT __stdcall Load(HSTRING) { return S_OK; }
HRESULT __stdcall Run()
{
+ // Wait for debugger before continuing
+ if (m_debugWait) {
+ while (!IsDebuggerPresent())
+ WaitForSingleObjectEx(GetCurrentThread(), 1, true);
+ }
return main(m_argv.count(), m_argv.data());
}
HRESULT __stdcall Uninitialize() { return S_OK; }
@@ -110,9 +115,13 @@ private:
m_argv.resize(m_argc);
HSTRING arguments;
launchArgs->get_Arguments(&arguments);
- foreach (const QByteArray &arg, QString::fromWCharArray(
- WindowsGetStringRawBuffer(arguments, nullptr)).toLocal8Bit().split(' ')) {
- m_argv.append(qstrdup(arg.constData()));
+ if (arguments) {
+ foreach (const QByteArray &arg, QString::fromWCharArray(
+ WindowsGetStringRawBuffer(arguments, nullptr)).toLocal8Bit().split(' ')) {
+ m_argv.append(qstrdup(arg.constData()));
+ if (arg == "-qdebug")
+ m_debugWait = true;
+ }
}
}
return S_OK;
@@ -120,6 +129,7 @@ private:
int m_argc;
QVector<char *> m_argv;
+ bool m_debugWait;
EventRegistrationToken m_activationToken;
};
diff --git a/tests/auto/corelib/codecs/qtextcodec/echo/main.cpp b/tests/auto/corelib/codecs/qtextcodec/echo/main.cpp
index 55c831bd35..09c3ac3c09 100644
--- a/tests/auto/corelib/codecs/qtextcodec/echo/main.cpp
+++ b/tests/auto/corelib/codecs/qtextcodec/echo/main.cpp
@@ -47,8 +47,7 @@
int main(int argc, char **argv)
{
- static char lc_all[] = "LC_ALL=C";
- putenv(lc_all);
+ qputenv("LC_ALL", "C");
QCoreApplication app(argc, argv);
QString string(QChar(0x410));
diff --git a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp
index 20f99e9191..36e01a0ccd 100644
--- a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp
+++ b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp
@@ -44,6 +44,7 @@
#include <QtGlobal>
#include <math.h>
+#include <float.h>
class tst_QNumeric: public QObject
{
@@ -53,6 +54,10 @@ private slots:
void fuzzyCompare_data();
void fuzzyCompare();
void qNan();
+ void floatDistance_data();
+ void floatDistance();
+ void floatDistance_double_data();
+ void floatDistance_double();
};
void tst_QNumeric::fuzzyCompare_data()
@@ -121,5 +126,93 @@ void tst_QNumeric::qNan()
QVERIFY(qFuzzyCompare(1/inf, 0.0));
}
+void tst_QNumeric::floatDistance_data()
+{
+ QTest::addColumn<float>("val1");
+ QTest::addColumn<float>("val2");
+ QTest::addColumn<quint32>("expectedDistance");
+
+ // exponent: 8 bits
+ // mantissa: 23 bits
+ const quint32 number_of_denormals = (1 << 23) - 1; // Set to 0 if denormals are not included
+
+ quint32 _0_to_1 = quint32((1 << 23) * 126 + 1 + number_of_denormals); // We need +1 to include the 0
+ quint32 _1_to_2 = quint32(1 << 23);
+
+ // We don't need +1 because FLT_MAX has all bits set in the mantissa. (Thus mantissa
+ // have not wrapped back to 0, which would be the case for 1 in _0_to_1
+ quint32 _0_to_FLT_MAX = quint32((1 << 23) * 254) + number_of_denormals;
+
+ quint32 _0_to_FLT_MIN = 1 + number_of_denormals;
+ QTest::newRow("[0,FLT_MIN]") << 0.F << FLT_MIN << _0_to_FLT_MIN;
+ QTest::newRow("[0,FLT_MAX]") << 0.F << FLT_MAX << _0_to_FLT_MAX;
+ QTest::newRow("[1,1.5]") << 1.0F << 1.5F << quint32(1 << 22);
+ QTest::newRow("[0,1]") << 0.F << 1.0F << _0_to_1;
+ QTest::newRow("[0.5,1]") << 0.5F << 1.0F << quint32(1 << 23);
+ QTest::newRow("[1,2]") << 1.F << 2.0F << _1_to_2;
+ QTest::newRow("[-1,+1]") << -1.F << +1.0F << 2 * _0_to_1;
+ QTest::newRow("[-1,0]") << -1.F << 0.0F << _0_to_1;
+ QTest::newRow("[-1,FLT_MAX]") << -1.F << FLT_MAX << _0_to_1 + _0_to_FLT_MAX;
+ QTest::newRow("[-2,-1") << -2.F << -1.F << _1_to_2;
+ QTest::newRow("[-1,-2") << -1.F << -2.F << _1_to_2;
+ QTest::newRow("[FLT_MIN,FLT_MAX]") << FLT_MIN << FLT_MAX << _0_to_FLT_MAX - _0_to_FLT_MIN;
+ QTest::newRow("[-FLT_MAX,FLT_MAX]") << -FLT_MAX << FLT_MAX << (2*_0_to_FLT_MAX);
+ float denormal = FLT_MIN;
+ denormal/=2.0F;
+ QTest::newRow("denormal") << 0.F << denormal << _0_to_FLT_MIN/2;
+}
+
+void tst_QNumeric::floatDistance()
+{
+ QFETCH(float, val1);
+ QFETCH(float, val2);
+ QFETCH(quint32, expectedDistance);
+ QCOMPARE(qFloatDistance(val1, val2), expectedDistance);
+}
+
+void tst_QNumeric::floatDistance_double_data()
+{
+ QTest::addColumn<double>("val1");
+ QTest::addColumn<double>("val2");
+ QTest::addColumn<quint64>("expectedDistance");
+
+ // exponent: 11 bits
+ // mantissa: 52 bits
+ const quint64 number_of_denormals = (Q_UINT64_C(1) << 52) - 1; // Set to 0 if denormals are not included
+
+ quint64 _0_to_1 = (Q_UINT64_C(1) << 52) * ((1 << (11-1)) - 2) + 1 + number_of_denormals; // We need +1 to include the 0
+ quint64 _1_to_2 = Q_UINT64_C(1) << 52;
+
+ // We don't need +1 because DBL_MAX has all bits set in the mantissa. (Thus mantissa
+ // have not wrapped back to 0, which would be the case for 1 in _0_to_1
+ quint64 _0_to_DBL_MAX = quint64((Q_UINT64_C(1) << 52) * ((1 << 11) - 2)) + number_of_denormals;
+
+ quint64 _0_to_DBL_MIN = 1 + number_of_denormals;
+ QTest::newRow("[0,DBL_MIN]") << 0.0 << DBL_MIN << _0_to_DBL_MIN;
+ QTest::newRow("[0,DBL_MAX]") << 0.0 << DBL_MAX << _0_to_DBL_MAX;
+ QTest::newRow("[1,1.5]") << 1.0 << 1.5 << (Q_UINT64_C(1) << 51);
+ QTest::newRow("[0,1]") << 0.0 << 1.0 << _0_to_1;
+ QTest::newRow("[0.5,1]") << 0.5 << 1.0 << (Q_UINT64_C(1) << 52);
+ QTest::newRow("[1,2]") << 1.0 << 2.0 << _1_to_2;
+ QTest::newRow("[-1,+1]") << -1.0 << +1.0 << 2 * _0_to_1;
+ QTest::newRow("[-1,0]") << -1.0 << 0.0 << _0_to_1;
+ QTest::newRow("[-1,DBL_MAX]") << -1.0 << DBL_MAX << _0_to_1 + _0_to_DBL_MAX;
+ QTest::newRow("[-2,-1") << -2.0 << -1.0 << _1_to_2;
+ QTest::newRow("[-1,-2") << -1.0 << -2.0 << _1_to_2;
+ QTest::newRow("[DBL_MIN,DBL_MAX]") << DBL_MIN << DBL_MAX << _0_to_DBL_MAX - _0_to_DBL_MIN;
+ QTest::newRow("[-DBL_MAX,DBL_MAX]") << -DBL_MAX << DBL_MAX << (2*_0_to_DBL_MAX);
+ double denormal = DBL_MIN;
+ denormal/=2.0;
+ QTest::newRow("denormal") << 0.0 << denormal << _0_to_DBL_MIN/2;
+}
+
+void tst_QNumeric::floatDistance_double()
+{
+ QFETCH(double, val1);
+ QFETCH(double, val2);
+ QFETCH(quint64, expectedDistance);
+ QCOMPARE(qFloatDistance(val1, val2), expectedDistance);
+}
+
QTEST_APPLESS_MAIN(tst_QNumeric)
#include "tst_qnumeric.moc"
diff --git a/tests/auto/corelib/io/io.pro b/tests/auto/corelib/io/io.pro
index ab66323d3b..259463f976 100644
--- a/tests/auto/corelib/io/io.pro
+++ b/tests/auto/corelib/io/io.pro
@@ -43,7 +43,6 @@ SUBDIRS=\
qsettings
!qtHaveModule(network): SUBDIRS -= \
- qfile \
qiodevice \
qprocess \
qtextstream
@@ -56,3 +55,9 @@ SUBDIRS=\
win32:!contains(QT_CONFIG, private_tests): SUBDIRS -= \
qfilesystementry
+
+winrt: SUBDIRS -= \
+ qprocess \
+ qprocess-noapplication \
+ qprocessenvironment \
+ qwinoverlappedionotifier
diff --git a/tests/auto/corelib/io/qfile/test/test.pro b/tests/auto/corelib/io/qfile/test/test.pro
index bc6922b4e9..03863e9943 100644
--- a/tests/auto/corelib/io/qfile/test/test.pro
+++ b/tests/auto/corelib/io/qfile/test/test.pro
@@ -1,7 +1,10 @@
CONFIG += testcase
CONFIG += parallel_test
CONFIG -= app_bundle debug_and_release_target
-QT = core-private core network testlib
+QT = core-private core testlib
+qtHaveModule(network): QT += network
+else: DEFINES += QT_NO_NETWORK
+
TARGET = ../tst_qfile
SOURCES = ../tst_qfile.cpp
wince*: SOURCES += $$QT_SOURCE_TREE/src/corelib/kernel/qfunctions_wince.cpp
@@ -13,5 +16,5 @@ TESTDATA += ../dosfile.txt ../noendofline.txt ../testfile.txt \
../Makefile ../forCopying.txt ../forRenaming.txt \
../resources/file1.ext1
-win32: LIBS+=-lole32 -luuid
+win32:!winrt: LIBS+=-lole32 -luuid
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
diff --git a/tests/auto/corelib/io/qfile/tst_qfile.cpp b/tests/auto/corelib/io/qfile/tst_qfile.cpp
index 2b029203e9..77ac4bcd86 100644
--- a/tests/auto/corelib/io/qfile/tst_qfile.cpp
+++ b/tests/auto/corelib/io/qfile/tst_qfile.cpp
@@ -59,7 +59,7 @@ extern Q_CORE_EXPORT int qt_ntfs_permission_lookup;
QT_END_NAMESPACE
#endif
-#if !defined(Q_OS_WINCE)
+#if !defined(Q_OS_WINCE) && !defined(QT_NO_NETWORK)
#include <QHostInfo>
#endif
#include <QProcess>
@@ -527,7 +527,7 @@ void tst_QFile::open_data()
<< false << QFile::OpenError;
QTest::newRow("noreadfile") << QString::fromLatin1(noReadFile) << int(QIODevice::ReadOnly)
<< false << QFile::OpenError;
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
//opening devices requires administrative privileges (and elevation).
HANDLE hTest = CreateFile(_T("\\\\.\\PhysicalDrive0"), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hTest != INVALID_HANDLE_VALUE) {
@@ -1057,7 +1057,7 @@ void tst_QFile::ungetChar()
QCOMPARE(buf[2], '4');
}
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
QString driveLetters()
{
wchar_t volumeName[MAX_PATH];
@@ -1094,7 +1094,7 @@ void tst_QFile::invalidFile_data()
#if !defined(Q_OS_WIN)
QTest::newRow( "x11" ) << QString( "qwe//" );
#else
-#if !defined(Q_OS_WINCE)
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
QTest::newRow( "colon2" ) << invalidDriveLetter() + QString::fromLatin1(":ail:invalid");
#endif
QTest::newRow( "colon3" ) << QString( ":failinvalid" );
@@ -1338,10 +1338,12 @@ void tst_QFile::copyFallback()
#ifdef Q_OS_WIN
#include <objbase.h>
+#ifndef Q_OS_WINPHONE
#include <shlobj.h>
#endif
+#endif
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
static QString getWorkingDirectoryForLink(const QString &linkFileName)
{
bool neededCoInit = false;
@@ -1400,7 +1402,7 @@ void tst_QFile::link()
QCOMPARE(QFile::symLinkTarget("myLink.lnk"), referenceTarget);
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
QString wd = getWorkingDirectoryForLink(info2.absoluteFilePath());
QCOMPARE(QDir::fromNativeSeparators(wd), QDir::cleanPath(info1.absolutePath()));
#endif
@@ -2243,7 +2245,7 @@ void tst_QFile::writeLargeDataBlock_data()
QTest::newRow("localfile-Fd") << "./largeblockfile.txt" << (int)OpenFd;
QTest::newRow("localfile-Stream") << "./largeblockfile.txt" << (int)OpenStream;
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(QT_NO_NETWORK)
// Some semi-randomness to avoid collisions.
QTest::newRow("unc file")
<< QString("//" + QtNetworkSettings::winServerName() + "/TESTSHAREWRITABLE/largefile-%1-%2.txt")
@@ -2690,8 +2692,12 @@ void tst_QFile::nativeHandleLeaks()
}
#ifdef Q_OS_WIN
+# ifndef Q_OS_WINRT
handle1 = ::CreateFileA("qt_file.tmp", GENERIC_READ, 0, NULL,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+# else
+ handle1 = ::CreateFile2(L"qt_file.tmp", GENERIC_READ, 0, OPEN_ALWAYS, NULL);
+# endif
QVERIFY( INVALID_HANDLE_VALUE != handle1 );
QVERIFY( ::CloseHandle(handle1) );
#endif
@@ -2705,8 +2711,12 @@ void tst_QFile::nativeHandleLeaks()
}
#ifdef Q_OS_WIN
+# ifndef Q_OS_WINRT
handle2 = ::CreateFileA("qt_file.tmp", GENERIC_READ, 0, NULL,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+# else
+ handle2 = ::CreateFile2(L"qt_file.tmp", GENERIC_READ, 0, OPEN_ALWAYS, NULL);
+# endif
QVERIFY( INVALID_HANDLE_VALUE != handle2 );
QVERIFY( ::CloseHandle(handle2) );
#endif
diff --git a/tests/auto/corelib/io/qfileinfo/qfileinfo.pro b/tests/auto/corelib/io/qfileinfo/qfileinfo.pro
index c4b37a8847..64d289bc3c 100644
--- a/tests/auto/corelib/io/qfileinfo/qfileinfo.pro
+++ b/tests/auto/corelib/io/qfileinfo/qfileinfo.pro
@@ -6,5 +6,5 @@ RESOURCES += qfileinfo.qrc
TESTDATA += qfileinfo.qrc qfileinfo.pro tst_qfileinfo.cpp resources/file1 resources/file1.ext1 resources/file1.ext1.ext2
-win32*:!wince*:LIBS += -ladvapi32 -lnetapi32
+win32*:!wince*:!winrt:LIBS += -ladvapi32 -lnetapi32
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
diff --git a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp
index d2171cc64a..74667a951f 100644
--- a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp
+++ b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp
@@ -62,7 +62,7 @@
#ifdef Q_OS_WIN
#include <qt_windows.h>
#include <qlibrary.h>
-#if !defined(Q_OS_WINCE)
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
#include <lm.h>
#endif
#endif
@@ -176,7 +176,7 @@ private slots:
void refresh();
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
void ntfsJunctionPointsAndSymlinks_data();
void ntfsJunctionPointsAndSymlinks();
void brokenShortcut();
@@ -193,7 +193,7 @@ private slots:
void detachingOperations();
-#if !defined(Q_OS_WINCE)
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
void owner();
#endif
void group();
@@ -1058,7 +1058,7 @@ void tst_QFileInfo::fileTimes()
//In Vista the last-access timestamp is not updated when the file is accessed/touched (by default).
//To enable this the HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\NtfsDisableLastAccessUpdate
//is set to 0, in the test machine.
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
HKEY key;
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\FileSystem",
0, KEY_READ, &key)) {
@@ -1085,8 +1085,8 @@ void tst_QFileInfo::fileTimes()
void tst_QFileInfo::fileTimes_oldFile()
{
- // This is not supported on WinCE
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ // This is not supported on WinCE or WinRT
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
// All files are opened in share mode (both read and write).
DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
@@ -1329,7 +1329,7 @@ void tst_QFileInfo::refresh()
QCOMPARE(info2.size(), info.size());
}
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
void tst_QFileInfo::ntfsJunctionPointsAndSymlinks_data()
{
QTest::addColumn<QString>("path");
@@ -1680,7 +1680,7 @@ void tst_QFileInfo::detachingOperations()
QVERIFY(!info1.caching());
}
-#if !defined(Q_OS_WINCE)
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
#if defined (Q_OS_WIN)
BOOL IsUserAdmin()
{
diff --git a/tests/auto/corelib/io/qfilesystemwatcher/tst_qfilesystemwatcher.cpp b/tests/auto/corelib/io/qfilesystemwatcher/tst_qfilesystemwatcher.cpp
index fda6519d82..b681cec802 100644
--- a/tests/auto/corelib/io/qfilesystemwatcher/tst_qfilesystemwatcher.cpp
+++ b/tests/auto/corelib/io/qfilesystemwatcher/tst_qfilesystemwatcher.cpp
@@ -55,6 +55,7 @@ class tst_QFileSystemWatcher : public QObject
public:
tst_QFileSystemWatcher();
+#ifndef QT_NO_FILESYSTEMWATCHER
private slots:
void basicTest_data();
void basicTest();
@@ -83,16 +84,20 @@ private slots:
private:
QString m_tempDirPattern;
+#endif // QT_NO_FILESYSTEMWATCHER
};
tst_QFileSystemWatcher::tst_QFileSystemWatcher()
{
+#ifndef QT_NO_FILESYSTEMWATCHER
m_tempDirPattern = QDir::tempPath();
if (!m_tempDirPattern.endsWith(QLatin1Char('/')))
m_tempDirPattern += QLatin1Char('/');
m_tempDirPattern += QStringLiteral("tst_qfilesystemwatcherXXXXXX");
+#endif // QT_NO_FILESYSTEMWATCHER
}
+#ifndef QT_NO_FILESYSTEMWATCHER
void tst_QFileSystemWatcher::basicTest_data()
{
QTest::addColumn<QString>("backend");
@@ -676,6 +681,7 @@ void tst_QFileSystemWatcher::signalsEmittedAfterFileMoved()
QTRY_COMPARE(changedSpy.count(), 10);
}
+#endif // QT_NO_FILESYSTEMWATCHER
QTEST_MAIN(tst_QFileSystemWatcher)
#include "tst_qfilesystemwatcher.moc"
diff --git a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp
index dc83cf2fc4..b020a50346 100644
--- a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp
+++ b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp
@@ -119,7 +119,7 @@ private slots:
#if !defined(Q_OS_WIN) && !defined(QT_QSETTINGS_ALWAYS_CASE_SENSITIVE_AND_FORGET_ORIGINAL_KEY_ORDER)
void dontReorderIniKeysNeedlessly();
#endif
-#if defined(Q_OS_WIN)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
void consistentRegistryStorage();
#endif
@@ -274,7 +274,7 @@ void tst_QSettings::init()
QSettings::setSystemIniPath(settingsPath("__system__"));
QSettings::setUserIniPath(settingsPath("__user__"));
-#if defined(Q_OS_WIN)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
QSettings("HKEY_CURRENT_USER\\Software\\software.org", QSettings::NativeFormat).clear();
QSettings("HKEY_LOCAL_MACHINE\\Software\\software.org", QSettings::NativeFormat).clear();
QSettings("HKEY_CURRENT_USER\\Software\\other.software.org", QSettings::NativeFormat).clear();
@@ -1502,7 +1502,7 @@ void tst_QSettings::sync()
// Now "some other app" will change other.software.org.ini
QString userConfDir = settingsPath("__user__") + QDir::separator();
-#if !defined(Q_OS_WINCE)
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
unlink((userConfDir + "other.software.org.ini").toLatin1());
rename((userConfDir + "software.org.ini").toLatin1(),
(userConfDir + "other.software.org.ini").toLatin1());
@@ -3202,7 +3202,7 @@ void tst_QSettings::recursionBug()
}
}
-#if defined(Q_OS_WIN)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
static DWORD readKeyType(HKEY handle, const QString &rSubKey)
{
diff --git a/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp b/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp
index 35a59401b7..d19f4379c7 100644
--- a/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp
+++ b/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp
@@ -289,9 +289,9 @@ void tst_qstandardpaths::testDataLocation()
{
// On all platforms, DataLocation should be GenericDataLocation / organization name / app name
// This allows one app to access the data of another app.
- // Blackberry OS is an exception to this case, owing to the fact that
+ // Blackberry OS and WinRT are an exception to this case, owing to the fact that
// applications are sandboxed.
-#ifndef Q_OS_BLACKBERRY
+#if !defined(Q_OS_BLACKBERRY) && !defined(Q_OS_WINRT)
const QString base = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::DataLocation), base + "/tst_qstandardpaths");
QCoreApplication::instance()->setOrganizationName("Qt");
@@ -335,6 +335,7 @@ void tst_qstandardpaths::testFindExecutable_data()
QTest::addColumn<QString>("needle");
QTest::addColumn<QString>("expected");
#ifdef Q_OS_WIN
+# ifndef Q_OS_WINRT
const QFileInfo cmdFi = QFileInfo(QDir::cleanPath(QString::fromLocal8Bit(qgetenv("COMSPEC"))));
const QString cmdPath = cmdFi.absoluteFilePath();
@@ -358,6 +359,7 @@ void tst_qstandardpaths::testFindExecutable_data()
QTest::newRow("win8-logo-nosuffix")
<< QString() << logo << logoPath;
}
+# endif // Q_OS_WINRT
#else
const QFileInfo shFi = findSh();
Q_ASSERT(shFi.exists());
diff --git a/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp b/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp
index 713d0c5c17..a6cc083d9c 100644
--- a/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp
+++ b/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp
@@ -257,7 +257,7 @@ void tst_QTemporaryDir::nonWritableCurrentDir()
void tst_QTemporaryDir::openOnRootDrives()
{
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
unsigned int lastErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
#endif
// If it's possible to create a file in the root directory, it
@@ -271,7 +271,7 @@ void tst_QTemporaryDir::openOnRootDrives()
QVERIFY(dir.isValid());
}
}
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
SetErrorMode(lastErrorMode);
#endif
}
diff --git a/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp b/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp
index 6eb6f83d2a..5ad798ae1f 100644
--- a/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp
+++ b/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp
@@ -371,7 +371,7 @@ void tst_QTemporaryFile::resize()
void tst_QTemporaryFile::openOnRootDrives()
{
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
unsigned int lastErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
#endif
// If it's possible to create a file in the root directory, it
@@ -385,7 +385,7 @@ void tst_QTemporaryFile::openOnRootDrives()
QVERIFY(file.open());
}
}
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
SetErrorMode(lastErrorMode);
#endif
}
diff --git a/tests/auto/corelib/kernel/kernel.pro b/tests/auto/corelib/kernel/kernel.pro
index fe3e4b7e0a..c1b867e6eb 100644
--- a/tests/auto/corelib/kernel/kernel.pro
+++ b/tests/auto/corelib/kernel/kernel.pro
@@ -34,6 +34,6 @@ SUBDIRS=\
qsharedmemory
# This test is only applicable on Windows
-!win32*:SUBDIRS -= qwineventnotifier
+!win32*|winrt: SUBDIRS -= qwineventnotifier
android|qnx: SUBDIRS -= qsharedmemory qsystemsemaphore
diff --git a/tests/auto/corelib/kernel/qeventloop/qeventloop.pro b/tests/auto/corelib/kernel/qeventloop/qeventloop.pro
index e5bcc31e6a..5593aa2430 100644
--- a/tests/auto/corelib/kernel/qeventloop/qeventloop.pro
+++ b/tests/auto/corelib/kernel/qeventloop/qeventloop.pro
@@ -4,7 +4,7 @@ TARGET = tst_qeventloop
QT = core network testlib core-private
SOURCES = $$PWD/tst_qeventloop.cpp
-win32:!wince*:LIBS += -luser32
+win32:!wince*:!winrt:LIBS += -luser32
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
contains(QT_CONFIG, glib): DEFINES += HAVE_GLIB
diff --git a/tests/auto/corelib/kernel/qmetatype/qmetatype.pro b/tests/auto/corelib/kernel/qmetatype/qmetatype.pro
index 23a8e6d23a..d19ec23760 100644
--- a/tests/auto/corelib/kernel/qmetatype/qmetatype.pro
+++ b/tests/auto/corelib/kernel/qmetatype/qmetatype.pro
@@ -6,11 +6,11 @@ SOURCES = tst_qmetatype.cpp
TESTDATA=./typeFlags.bin
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
-win32-msvc*|wince {
+win32-msvc*|wince|winrt {
# Prevents "fatal error C1128: number of sections exceeded object file format limit".
QMAKE_CXXFLAGS += /bigobj
# Reduce compile time
- win32-msvc2012|wince {
+ win32-msvc2012|wince|winrt {
QMAKE_CXXFLAGS_RELEASE -= -O2
QMAKE_CFLAGS_RELEASE -= -O2
}
diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
index ece88d47e3..e4804e6079 100644
--- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
+++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
@@ -102,6 +102,7 @@ private slots:
#ifndef QT_NO_PROCESS
void recursiveSignalEmission();
#endif
+ void signalBlocking();
void blockingQueuedConnection();
void childEvents();
void installEventFilter();
@@ -2980,6 +2981,54 @@ void tst_QObject::recursiveSignalEmission()
}
#endif
+void tst_QObject::signalBlocking()
+{
+ SenderObject sender;
+ ReceiverObject receiver;
+
+ receiver.connect(&sender, SIGNAL(signal1()), SLOT(slot1()));
+
+ sender.emitSignal1();
+ QVERIFY(receiver.called(1));
+ receiver.reset();
+
+ {
+ QSignalBlocker blocker(&sender);
+
+ sender.emitSignal1();
+ QVERIFY(!receiver.called(1));
+ receiver.reset();
+
+ sender.blockSignals(false);
+
+ sender.emitSignal1();
+ QVERIFY(receiver.called(1));
+ receiver.reset();
+
+ sender.blockSignals(true);
+
+ sender.emitSignal1();
+ QVERIFY(!receiver.called(1));
+ receiver.reset();
+
+ blocker.unblock();
+
+ sender.emitSignal1();
+ QVERIFY(receiver.called(1));
+ receiver.reset();
+
+ blocker.reblock();
+
+ sender.emitSignal1();
+ QVERIFY(!receiver.called(1));
+ receiver.reset();
+ }
+
+ sender.emitSignal1();
+ QVERIFY(receiver.called(1));
+ receiver.reset();
+}
+
void tst_QObject::blockingQueuedConnection()
{
{
diff --git a/tests/auto/corelib/thread/qthread/tst_qthread.cpp b/tests/auto/corelib/thread/qthread/tst_qthread.cpp
index 5cc0e5bdb4..0e53139414 100644
--- a/tests/auto/corelib/thread/qthread/tst_qthread.cpp
+++ b/tests/auto/corelib/thread/qthread/tst_qthread.cpp
@@ -55,6 +55,8 @@
#endif
#if defined(Q_OS_WINCE)
#include <windows.h>
+#elif defined(Q_OS_WINRT)
+#include <thread>
#elif defined(Q_OS_WIN)
#include <process.h>
#include <windows.h>
@@ -326,6 +328,9 @@ void tst_QThread::isRunning()
void tst_QThread::setPriority()
{
+#if defined(Q_OS_WINRT)
+ QSKIP("Thread priority is not supported on WinRT");
+#endif
Simple_Thread thread;
// cannot change the priority, since the thread is not running
@@ -460,6 +465,10 @@ void tst_QThread::start()
QVERIFY(!thread.isFinished());
QVERIFY(!thread.isRunning());
QMutexLocker locker(&thread.mutex);
+#ifdef Q_OS_WINRT
+ if (priorities[i] != QThread::NormalPriority && priorities[i] != QThread::InheritPriority)
+ QTest::ignoreMessage(QtWarningMsg, "QThread::start: Failed to set thread priority (not implemented)");
+#endif
thread.start(priorities[i]);
QVERIFY(thread.isRunning());
QVERIFY(!thread.isFinished());
@@ -472,6 +481,9 @@ void tst_QThread::start()
void tst_QThread::terminate()
{
+#if defined(Q_OS_WINRT)
+ QSKIP("Terminate is not supported on WinRT");
+#endif
Terminate_Thread thread;
{
QMutexLocker locker(&thread.mutex);
@@ -535,6 +547,9 @@ void tst_QThread::finished()
void tst_QThread::terminated()
{
+#if defined(Q_OS_WINRT)
+ QSKIP("Terminate is not supported on WinRT");
+#endif
SignalRecorder recorder;
Terminate_Thread thread;
connect(&thread, SIGNAL(finished()), &recorder, SLOT(slot()), Qt::DirectConnection);
@@ -630,6 +645,8 @@ void noop(void*) { }
#if defined Q_OS_UNIX
typedef pthread_t ThreadHandle;
+#elif defined Q_OS_WINRT
+ typedef std::thread ThreadHandle;
#elif defined Q_OS_WIN
typedef HANDLE ThreadHandle;
#endif
@@ -671,6 +688,8 @@ void NativeThreadWrapper::start(FunctionPointer functionPointer, void *data)
#if defined Q_OS_UNIX
const int state = pthread_create(&nativeThreadHandle, 0, NativeThreadWrapper::runUnix, this);
Q_UNUSED(state);
+#elif defined(Q_OS_WINRT)
+ nativeThreadHandle = std::thread(NativeThreadWrapper::runWin, this);
#elif defined(Q_OS_WINCE)
nativeThreadHandle = CreateThread(NULL, 0 , (LPTHREAD_START_ROUTINE)NativeThreadWrapper::runWin , this, 0, NULL);
#elif defined Q_OS_WIN
@@ -690,6 +709,8 @@ void NativeThreadWrapper::join()
{
#if defined Q_OS_UNIX
pthread_join(nativeThreadHandle, 0);
+#elif defined Q_OS_WINRT
+ nativeThreadHandle.join();
#elif defined Q_OS_WIN
WaitForSingleObject(nativeThreadHandle, INFINITE);
CloseHandle(nativeThreadHandle);
@@ -780,6 +801,9 @@ void tst_QThread::adoptedThreadAffinity()
void tst_QThread::adoptedThreadSetPriority()
{
+#if defined(Q_OS_WINRT)
+ QSKIP("Thread priority is not supported on WinRT");
+#endif
NativeThreadWrapper nativeThread;
nativeThread.setWaitForStop();
@@ -857,6 +881,9 @@ void tst_QThread::adoptedThreadFinished()
nativeThread.join();
QTestEventLoop::instance().enterLoop(5);
+#if defined(Q_OS_WINRT)
+ QEXPECT_FAIL("", "QTBUG-31397: Known not to work on WinRT", Abort);
+#endif
QVERIFY(!QTestEventLoop::instance().timeout());
}
@@ -872,6 +899,9 @@ void tst_QThread::adoptedThreadExecFinished()
nativeThread.join();
QTestEventLoop::instance().enterLoop(5);
+#if defined(Q_OS_WINRT)
+ QEXPECT_FAIL("", "QTBUG-31397: Known not to work on WinRT", Abort);
+#endif
QVERIFY(!QTestEventLoop::instance().timeout());
}
@@ -908,6 +938,9 @@ void tst_QThread::adoptMultipleThreads()
}
QTestEventLoop::instance().enterLoop(5);
+#if defined(Q_OS_WINRT)
+ QEXPECT_FAIL("", "QTBUG-31397: Known not to work on WinRT", Abort);
+#endif
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(recorder.activationCount.load(), numThreads);
}
@@ -950,6 +983,9 @@ void tst_QThread::adoptMultipleThreadsOverlap()
}
QTestEventLoop::instance().enterLoop(5);
+#if defined(Q_OS_WINRT)
+ QEXPECT_FAIL("", "QTBUG-31397: Known not to work on WinRT", Abort);
+#endif
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(recorder.activationCount.load(), numThreads);
}
diff --git a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp
index 4a9932798c..f43149c3eb 100644
--- a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp
+++ b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp
@@ -80,6 +80,7 @@ private slots:
void destruction();
void threadRecycling();
void expiryTimeout();
+ void expiryTimeoutRace();
#ifndef QT_NO_EXCEPTIONS
void exceptions();
#endif
@@ -315,7 +316,7 @@ class ExpiryTimeoutTask : public QRunnable
{
public:
QThread *thread;
- int runCount;
+ QAtomicInt runCount;
QSemaphore semaphore;
ExpiryTimeoutTask()
@@ -327,7 +328,7 @@ public:
void run()
{
thread = QThread::currentThread();
- ++runCount;
+ runCount.ref();
semaphore.release();
}
};
@@ -346,7 +347,7 @@ void tst_QThreadPool::expiryTimeout()
// run the task
threadPool.start(&task);
QVERIFY(task.semaphore.tryAcquire(1, 10000));
- QCOMPARE(task.runCount, 1);
+ QCOMPARE(task.runCount.load(), 1);
QVERIFY(!task.thread->wait(100));
// thread should expire
QThread *firstThread = task.thread;
@@ -355,7 +356,7 @@ void tst_QThreadPool::expiryTimeout()
// run task again, thread should be restarted
threadPool.start(&task);
QVERIFY(task.semaphore.tryAcquire(1, 10000));
- QCOMPARE(task.runCount, 2);
+ QCOMPARE(task.runCount.load(), 2);
QVERIFY(!task.thread->wait(100));
// thread should expire again
QVERIFY(task.thread->wait(10000));
@@ -368,6 +369,22 @@ void tst_QThreadPool::expiryTimeout()
QCOMPARE(threadPool.expiryTimeout(), expiryTimeout);
}
+void tst_QThreadPool::expiryTimeoutRace() // QTBUG-3786
+{
+ ExpiryTimeoutTask task;
+
+ QThreadPool threadPool;
+ threadPool.setMaxThreadCount(1);
+ threadPool.setExpiryTimeout(50);
+ const int numTasks = 20;
+ for (int i = 0; i < numTasks; ++i) {
+ threadPool.start(&task);
+ QThread::msleep(50); // exactly the same as the expiry timeout
+ }
+ QCOMPARE(task.runCount.load(), numTasks);
+ QVERIFY(threadPool.waitForDone(2000));
+}
+
#ifndef QT_NO_EXCEPTIONS
class ExceptionTask : public QRunnable
{
diff --git a/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp b/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp
index 2072034f5f..c96de29b98 100644
--- a/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp
+++ b/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp
@@ -232,7 +232,7 @@ void tst_QThreadStorage::adoptedThreads()
const int state = pthread_create(&thread, 0, testAdoptedThreadStorageUnix, &pointers);
QCOMPARE(state, 0);
pthread_join(thread, 0);
-#elif defined Q_OS_WIN
+#elif defined Q_OS_WIN && !defined(Q_OS_WINRT)
HANDLE thread;
#if defined(Q_OS_WINCE)
thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)testAdoptedThreadStorageWin, &pointers, 0, NULL);
diff --git a/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp b/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp
index f37e192ad3..d5b3323506 100644
--- a/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp
+++ b/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp
@@ -553,6 +553,9 @@ void tst_QCommandLineParser::testHelpOption()
void tst_QCommandLineParser::testQuoteEscaping()
{
+#ifdef QT_NO_PROCESS
+ QSKIP("This test requires QProcess support");
+#else
QCoreApplication app(empty_argc, empty_argv);
QProcess process;
process.start("testhelper/qcommandlineparser_test_helper", QStringList() <<
@@ -568,6 +571,7 @@ void tst_QCommandLineParser::testQuoteEscaping()
QVERIFY2(output.contains("KEY2=\\\"VALUE2\\\""), qPrintable(output));
QVERIFY2(output.contains("QTBUG-15379=C:\\path\\'file.ext"), qPrintable(output));
QVERIFY2(output.contains("QTBUG-30628=C:\\temp\\'file'.ext"), qPrintable(output));
+#endif // !QT_NO_PROCESS
}
QTEST_APPLESS_MAIN(tst_QCommandLineParser)
diff --git a/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp
index 6c80c5ff47..7d51696fe3 100644
--- a/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp
+++ b/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp
@@ -49,6 +49,9 @@
#ifdef Q_OS_WIN
# include <qt_windows.h>
+# if defined(Q_OS_WINRT)
+# define tzset()
+# endif
#endif
class tst_QDateTime : public QObject
@@ -175,7 +178,7 @@ void tst_QDateTime::init()
{
#if defined(Q_OS_WINCE)
SetUserDefaultLCID(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT));
-#elif defined(Q_OS_WIN)
+#elif defined(Q_OS_WIN32)
SetThreadLocale(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT));
#endif
}
diff --git a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp
index 028cea3d62..1bac5f39e7 100644
--- a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp
+++ b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp
@@ -133,7 +133,7 @@ public:
private slots:
void initTestCase();
void cleanupTestCase();
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
void windowsDefaultLocale();
#endif
#ifdef Q_OS_MAC
@@ -1472,7 +1472,7 @@ void tst_QLocale::macDefaultLocale()
}
#endif // Q_OS_MAC
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
#include <qt_windows.h>
static QString getWinLocaleInfo(LCTYPE type)
@@ -1526,7 +1526,7 @@ public:
#endif // Q_OS_WIN
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
void tst_QLocale::windowsDefaultLocale()
{
RestoreLocaleHelper systemLocale;
diff --git a/tests/auto/corelib/tools/qstring/tst_qstring.cpp b/tests/auto/corelib/tools/qstring/tst_qstring.cpp
index 48874781c0..9a3aaebaaf 100644
--- a/tests/auto/corelib/tools/qstring/tst_qstring.cpp
+++ b/tests/auto/corelib/tools/qstring/tst_qstring.cpp
@@ -1440,6 +1440,8 @@ void tst_QString::contains()
QVERIFY(a.contains('F',Qt::CaseInsensitive));
QVERIFY(a.contains("FG"));
QVERIFY(a.contains("FG",Qt::CaseInsensitive));
+ QVERIFY(a.contains(QLatin1String("FG")));
+ QVERIFY(a.contains(QLatin1String("fg"),Qt::CaseInsensitive));
QVERIFY(a.contains( QString(), Qt::CaseInsensitive));
QVERIFY(a.contains( "", Qt::CaseInsensitive));
QVERIFY(a.contains(QRegExp("[FG][HI]")));
diff --git a/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp b/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp
index d7643ff55d..0a63ffeeaf 100644
--- a/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp
+++ b/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp
@@ -796,7 +796,7 @@ void tst_QTimeZone::macTest()
void tst_QTimeZone::winTest()
{
-#if defined(QT_BUILD_INTERNAL) && defined(Q_OS_WIN)
+#if defined(QT_BUILD_INTERNAL) && defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
// Known datetimes
qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0, 0), Qt::UTC).toMSecsSinceEpoch();
qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0, 0), Qt::UTC).toMSecsSinceEpoch();
diff --git a/tests/auto/gui/image/qpixmap/qpixmap.pro b/tests/auto/gui/image/qpixmap/qpixmap.pro
index bdd0c15788..33c301a500 100644
--- a/tests/auto/gui/image/qpixmap/qpixmap.pro
+++ b/tests/auto/gui/image/qpixmap/qpixmap.pro
@@ -5,7 +5,7 @@ QT += core-private gui-private testlib
qtHaveModule(widgets): QT += widgets widgets-private
SOURCES += tst_qpixmap.cpp
-!wince* {
+!wince*:!winrt {
win32:LIBS += -lgdi32 -luser32
}
diff --git a/tests/auto/gui/image/qpixmap/tst_qpixmap.cpp b/tests/auto/gui/image/qpixmap/tst_qpixmap.cpp
index cb5d836291..79dc3f311a 100644
--- a/tests/auto/gui/image/qpixmap/tst_qpixmap.cpp
+++ b/tests/auto/gui/image/qpixmap/tst_qpixmap.cpp
@@ -116,7 +116,7 @@ private slots:
void convertFromImageDetach();
void convertFromImageCacheKey();
-#if defined(Q_OS_WIN)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
void toWinHBITMAP_data();
void toWinHBITMAP();
void fromWinHBITMAP_data();
@@ -805,7 +805,7 @@ void tst_QPixmap::convertFromImageCacheKey()
QCOMPARE(copy.cacheKey(), pix.cacheKey());
}
-#if defined(Q_OS_WIN)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
QT_BEGIN_NAMESPACE
Q_GUI_EXPORT HBITMAP qt_createIconMask(const QBitmap &bitmap);
@@ -1024,7 +1024,7 @@ void tst_QPixmap::fromWinHICON()
#endif // Q_OS_WINCE
}
-#endif // Q_OS_WIN
+#endif // Q_OS_WIN && !Q_OS_WINRT
void tst_QPixmap::onlyNullPixmapsOutsideGuiThread()
{
diff --git a/tests/auto/gui/kernel/qguivariant/test/black.png b/tests/auto/gui/kernel/qguivariant/test/black.png
new file mode 100644
index 0000000000..6c94085ed5
--- /dev/null
+++ b/tests/auto/gui/kernel/qguivariant/test/black.png
Binary files differ
diff --git a/tests/auto/gui/kernel/qguivariant/test/black2.png b/tests/auto/gui/kernel/qguivariant/test/black2.png
new file mode 100644
index 0000000000..6c94085ed5
--- /dev/null
+++ b/tests/auto/gui/kernel/qguivariant/test/black2.png
Binary files differ
diff --git a/tests/auto/gui/kernel/qguivariant/test/test.pro b/tests/auto/gui/kernel/qguivariant/test/test.pro
index e3b4a350ca..c4123797d2 100644
--- a/tests/auto/gui/kernel/qguivariant/test/test.pro
+++ b/tests/auto/gui/kernel/qguivariant/test/test.pro
@@ -2,6 +2,7 @@ CONFIG += testcase
CONFIG += parallel_test
TARGET = tst_qguivariant
SOURCES += tst_qguivariant.cpp
+RESOURCES = tst_qguivariant.qrc
INCLUDEPATH += $$PWD/../../../../other/qvariant_common
QT += testlib
RESOURCES += qguivariant.qrc
diff --git a/tests/auto/gui/kernel/qguivariant/test/tst_qguivariant.cpp b/tests/auto/gui/kernel/qguivariant/test/tst_qguivariant.cpp
index 42570751fc..efc688f0b1 100644
--- a/tests/auto/gui/kernel/qguivariant/test/tst_qguivariant.cpp
+++ b/tests/auto/gui/kernel/qguivariant/test/tst_qguivariant.cpp
@@ -126,6 +126,8 @@ private slots:
void implicitConstruction();
void guiVariantAtExit();
+
+ void iconEquality();
};
void tst_QGuiVariant::constructor_invalid_data()
@@ -761,5 +763,32 @@ void tst_QGuiVariant::guiVariantAtExit()
QVERIFY(true);
}
+void tst_QGuiVariant::iconEquality()
+{
+ QIcon i;
+ QVariant a = i;
+ QVariant b = i;
+ QCOMPARE(a, b);
+
+ i = QIcon(":/black.png");
+ a = i;
+ QVERIFY(a != b);
+
+ b = a;
+ QCOMPARE(a, b);
+
+ i = QIcon(":/black2.png");
+ a = i;
+ QVERIFY(a != b);
+
+ b = i;
+ QCOMPARE(a, b);
+
+ // This is a "different" QIcon
+ // even if the contents are the same
+ b = QIcon(":/black2.png");
+ QVERIFY(a != b);
+}
+
QTEST_MAIN(tst_QGuiVariant)
#include "tst_qguivariant.moc"
diff --git a/tests/auto/gui/kernel/qguivariant/test/tst_qguivariant.qrc b/tests/auto/gui/kernel/qguivariant/test/tst_qguivariant.qrc
new file mode 100644
index 0000000000..15cfde5788
--- /dev/null
+++ b/tests/auto/gui/kernel/qguivariant/test/tst_qguivariant.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/">
+<file>black.png</file>
+<file>black2.png</file>
+</qresource>
+</RCC>
diff --git a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp
index d5ddf8fbec..53aef40df0 100644
--- a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp
+++ b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp
@@ -187,6 +187,9 @@ private slots:
void htmlExportImportBlockCount();
void QTBUG27354_spaceAndSoftSpace();
+ void baseUrl_data();
+ void baseUrl();
+
void QTBUG28998_linkColor();
private:
@@ -2947,6 +2950,53 @@ void tst_QTextDocument::QTBUG27354_spaceAndSoftSpace()
}
}
+class BaseDocument : public QTextDocument
+{
+public:
+ QUrl loadedResource() const { return resourceUrl; }
+
+ QVariant loadResource(int type, const QUrl &name)
+ {
+ resourceUrl = name;
+ return QTextDocument::loadResource(type, name);
+ }
+
+private:
+ QUrl resourceUrl;
+};
+
+void tst_QTextDocument::baseUrl_data()
+{
+ QTest::addColumn<QUrl>("base");
+ QTest::addColumn<QUrl>("resource");
+ QTest::addColumn<QUrl>("loaded");
+
+ QTest::newRow("1") << QUrl() << QUrl("images/logo.png") << QUrl("images/logo.png");
+ QTest::newRow("2") << QUrl("file:///path/to/content") << QUrl("images/logo.png") << QUrl("file:///path/to/images/logo.png");
+ QTest::newRow("3") << QUrl("file:///path/to/content/") << QUrl("images/logo.png") << QUrl("file:///path/to/content/images/logo.png");
+ QTest::newRow("4") << QUrl("file:///path/to/content/images") << QUrl("images/logo.png") << QUrl("file:///path/to/content/images/logo.png");
+ QTest::newRow("5") << QUrl("file:///path/to/content/images/") << QUrl("images/logo.png") << QUrl("file:///path/to/content/images/images/logo.png");
+ QTest::newRow("6") << QUrl("file:///path/to/content/images") << QUrl("../images/logo.png") << QUrl("file:///path/to/images/logo.png");
+ QTest::newRow("7") << QUrl("file:///path/to/content/images/") << QUrl("../images/logo.png") << QUrl("file:///path/to/content/images/logo.png");
+ QTest::newRow("8") << QUrl("file:///path/to/content/index.html") << QUrl("images/logo.png") << QUrl("file:///path/to/content/images/logo.png");
+}
+
+void tst_QTextDocument::baseUrl()
+{
+ QFETCH(QUrl, base);
+ QFETCH(QUrl, resource);
+ QFETCH(QUrl, loaded);
+
+ BaseDocument document;
+ QVERIFY(!document.baseUrl().isValid());
+ document.setBaseUrl(base);
+ QCOMPARE(document.baseUrl(), base);
+
+ document.setHtml(QString("<img src='%1'/>").arg(resource.toString()));
+ document.resource(QTextDocument::ImageResource, resource);
+ QCOMPARE(document.loadedResource(), loaded);
+}
+
void tst_QTextDocument::QTBUG28998_linkColor()
{
QPalette pal;
diff --git a/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp b/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp
index dd9202c748..acb921b34a 100644
--- a/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp
+++ b/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp
@@ -50,6 +50,9 @@
#include <qdatastream.h>
#ifdef Q_OS_WIN
# include <qt_windows.h>
+# if defined(Q_OS_WINRT)
+# include <winsock2.h>
+# endif
#endif
class tst_QHostAddress : public QObject
diff --git a/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp b/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp
index e81c7f71db..e3156ceb3f 100644
--- a/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp
+++ b/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp
@@ -67,7 +67,7 @@
#include <time.h>
#include <qlibrary.h>
-#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN)
#include <windows.h>
#else
#include <unistd.h>
diff --git a/tests/auto/printsupport/dialogs/dialogs.pro b/tests/auto/printsupport/dialogs/dialogs.pro
new file mode 100644
index 0000000000..419bd13ccf
--- /dev/null
+++ b/tests/auto/printsupport/dialogs/dialogs.pro
@@ -0,0 +1,3 @@
+TEMPLATE=subdirs
+SUBDIRS=\
+ qabstractprintdialog \
diff --git a/tests/auto/widgets/dialogs/qabstractprintdialog/.gitignore b/tests/auto/printsupport/dialogs/qabstractprintdialog/.gitignore
index a768494da5..a768494da5 100644
--- a/tests/auto/widgets/dialogs/qabstractprintdialog/.gitignore
+++ b/tests/auto/printsupport/dialogs/qabstractprintdialog/.gitignore
diff --git a/tests/auto/widgets/dialogs/qabstractprintdialog/qabstractprintdialog.pro b/tests/auto/printsupport/dialogs/qabstractprintdialog/qabstractprintdialog.pro
index 2e9ae33592..2e9ae33592 100644
--- a/tests/auto/widgets/dialogs/qabstractprintdialog/qabstractprintdialog.pro
+++ b/tests/auto/printsupport/dialogs/qabstractprintdialog/qabstractprintdialog.pro
diff --git a/tests/auto/widgets/dialogs/qabstractprintdialog/tst_qabstractprintdialog.cpp b/tests/auto/printsupport/dialogs/qabstractprintdialog/tst_qabstractprintdialog.cpp
index c79a4a97ef..c79a4a97ef 100644
--- a/tests/auto/widgets/dialogs/qabstractprintdialog/tst_qabstractprintdialog.cpp
+++ b/tests/auto/printsupport/dialogs/qabstractprintdialog/tst_qabstractprintdialog.cpp
diff --git a/tests/auto/printsupport/printsupport.pro b/tests/auto/printsupport/printsupport.pro
index 69ba296738..062f46980a 100644
--- a/tests/auto/printsupport/printsupport.pro
+++ b/tests/auto/printsupport/printsupport.pro
@@ -1,3 +1,4 @@
TEMPLATE=subdirs
SUBDIRS=\
+ dialogs \
kernel \
diff --git a/tests/auto/sql/kernel/qsqldatabase/tst_databases.h b/tests/auto/sql/kernel/qsqldatabase/tst_databases.h
index f87493205f..69ecbcb019 100644
--- a/tests/auto/sql/kernel/qsqldatabase/tst_databases.h
+++ b/tests/auto/sql/kernel/qsqldatabase/tst_databases.h
@@ -55,11 +55,14 @@
#include <QtSql/private/qsqldriver_p.h>
#include <QtTest/QtTest>
-#if defined (Q_OS_WIN) || defined (Q_OS_WIN32)
+#if defined(Q_OS_WIN)
# include <qt_windows.h>
-# if defined (Q_OS_WINCE)
+# if defined(Q_OS_WINCE) || defined(Q_OS_WINRT)
# include <winsock2.h>
# endif
+# if defined(Q_OS_WINRT) && !defined(Q_OS_WINPHONE)
+static inline int gethostname(char *name, int len) { qstrcpy(name, "localhost"); return 9; }
+# endif
#else
#include <unistd.h>
#endif
diff --git a/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp b/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp
index 1a100ce706..9098d5b101 100644
--- a/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp
+++ b/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp
@@ -1393,6 +1393,9 @@ void tst_QSqlQuery::isNull()
QVERIFY( q.next() );
QVERIFY( !q.isNull( 0 ) );
QVERIFY( !q.isNull( 1 ) );
+
+ // For a non existent field, it should be returning true.
+ QVERIFY(q.isNull(2));
}
/*! TDS specific BIT field test */
diff --git a/tests/auto/testlib/selftests/crashes/tst_crashes.cpp b/tests/auto/testlib/selftests/crashes/tst_crashes.cpp
index 067c2a9f4f..3b73d87876 100644
--- a/tests/auto/testlib/selftests/crashes/tst_crashes.cpp
+++ b/tests/auto/testlib/selftests/crashes/tst_crashes.cpp
@@ -57,7 +57,7 @@ private slots:
void tst_Crashes::crash()
{
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
//we avoid the error dialogbox to appear on windows
SetErrorMode( SEM_NOGPFAULTERRORBOX | SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
#endif
diff --git a/tests/auto/testlib/selftests/expected_verifyexceptionthrown.lightxml b/tests/auto/testlib/selftests/expected_verifyexceptionthrown.lightxml
new file mode 100644
index 0000000000..6ef857c6d0
--- /dev/null
+++ b/tests/auto/testlib/selftests/expected_verifyexceptionthrown.lightxml
@@ -0,0 +1,49 @@
+<Environment>
+ <QtVersion>@INSERT_QT_VERSION_HERE@</QtVersion>
+ <QTestVersion>@INSERT_QT_VERSION_HERE@</QTestVersion>
+</Environment>
+<TestFunction name="initTestCase">
+<Incident type="pass" file="" line="0" />
+</TestFunction>
+<TestFunction name="testCorrectStdTypes">
+<Incident type="pass" file="" line="0" />
+</TestFunction>
+<TestFunction name="testCorrectStdExceptions">
+<Incident type="pass" file="" line="0" />
+</TestFunction>
+<TestFunction name="testCorrectMyExceptions">
+<Incident type="pass" file="" line="0" />
+</TestFunction>
+<TestFunction name="testFailInt">
+<Incident type="fail" file="/home/user/dev/qt5/qtbase/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp" line="128">
+ <Description><![CDATA[Expected exception of type double to be thrown but unknown exception caught]]></Description>
+</Incident>
+</TestFunction>
+<TestFunction name="testFailStdString">
+<Incident type="fail" file="/home/user/dev/qt5/qtbase/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp" line="133">
+ <Description><![CDATA[Expected exception of type char* to be thrown but unknown exception caught]]></Description>
+</Incident>
+</TestFunction>
+<TestFunction name="testFailStdRuntimeError">
+<Incident type="fail" file="/home/user/dev/qt5/qtbase/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp" line="138">
+ <Description><![CDATA[Expected exception of type std::runtime_error to be thrown but std::exception caught with message: logic error]]></Description>
+</Incident>
+</TestFunction>
+<TestFunction name="testFailMyException">
+<Incident type="fail" file="/home/user/dev/qt5/qtbase/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp" line="143">
+ <Description><![CDATA[Expected exception of type MyBaseException to be thrown but std::exception caught with message: logic error]]></Description>
+</Incident>
+</TestFunction>
+<TestFunction name="testFailMyDerivedException">
+<Incident type="fail" file="/home/user/dev/qt5/qtbase/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp" line="148">
+ <Description><![CDATA[Expected exception of type std::runtime_error to be thrown but std::exception caught with message: MyDerivedException]]></Description>
+</Incident>
+</TestFunction>
+<TestFunction name="testFailNoException">
+<Incident type="fail" file="/home/user/dev/qt5/qtbase/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp" line="153">
+ <Description><![CDATA[Expected exception of type std::exception to be thrown but no exception caught]]></Description>
+</Incident>
+</TestFunction>
+<TestFunction name="cleanupTestCase">
+<Incident type="pass" file="" line="0" />
+</TestFunction>
diff --git a/tests/auto/testlib/selftests/expected_verifyexceptionthrown.txt b/tests/auto/testlib/selftests/expected_verifyexceptionthrown.txt
new file mode 100644
index 0000000000..5c40f62dd2
--- /dev/null
+++ b/tests/auto/testlib/selftests/expected_verifyexceptionthrown.txt
@@ -0,0 +1,21 @@
+********* Start testing of tst_VerifyExceptionThrown *********
+Config: Using QtTest library @INSERT_QT_VERSION_HERE@, Qt @INSERT_QT_VERSION_HERE@
+PASS : tst_VerifyExceptionThrown::initTestCase()
+PASS : tst_VerifyExceptionThrown::testCorrectStdTypes()
+PASS : tst_VerifyExceptionThrown::testCorrectStdExceptions()
+PASS : tst_VerifyExceptionThrown::testCorrectMyExceptions()
+FAIL! : tst_VerifyExceptionThrown::testFailInt() Expected exception of type double to be thrown but unknown exception caught
+ Loc: [/home/user/dev/qt5/qtbase/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp(128)]
+FAIL! : tst_VerifyExceptionThrown::testFailStdString() Expected exception of type char* to be thrown but unknown exception caught
+ Loc: [/home/user/dev/qt5/qtbase/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp(133)]
+FAIL! : tst_VerifyExceptionThrown::testFailStdRuntimeError() Expected exception of type std::runtime_error to be thrown but std::exception caught with message: logic error
+ Loc: [/home/user/dev/qt5/qtbase/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp(138)]
+FAIL! : tst_VerifyExceptionThrown::testFailMyException() Expected exception of type MyBaseException to be thrown but std::exception caught with message: logic error
+ Loc: [/home/user/dev/qt5/qtbase/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp(143)]
+FAIL! : tst_VerifyExceptionThrown::testFailMyDerivedException() Expected exception of type std::runtime_error to be thrown but std::exception caught with message: MyDerivedException
+ Loc: [/home/user/dev/qt5/qtbase/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp(148)]
+FAIL! : tst_VerifyExceptionThrown::testFailNoException() Expected exception of type std::exception to be thrown but no exception caught
+ Loc: [/home/user/dev/qt5/qtbase/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp(153)]
+PASS : tst_VerifyExceptionThrown::cleanupTestCase()
+Totals: 5 passed, 6 failed, 0 skipped
+********* Finished testing of tst_VerifyExceptionThrown *********
diff --git a/tests/auto/testlib/selftests/expected_verifyexceptionthrown.xml b/tests/auto/testlib/selftests/expected_verifyexceptionthrown.xml
new file mode 100644
index 0000000000..df261b72c4
--- /dev/null
+++ b/tests/auto/testlib/selftests/expected_verifyexceptionthrown.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<TestCase name="tst_VerifyExceptionThrown">
+<Environment>
+ <QtVersion>@INSERT_QT_VERSION_HERE@</QtVersion>
+ <QTestVersion>@INSERT_QT_VERSION_HERE@</QTestVersion>
+</Environment>
+<TestFunction name="initTestCase">
+<Incident type="pass" file="" line="0" />
+</TestFunction>
+<TestFunction name="testCorrectStdTypes">
+<Incident type="pass" file="" line="0" />
+</TestFunction>
+<TestFunction name="testCorrectStdExceptions">
+<Incident type="pass" file="" line="0" />
+</TestFunction>
+<TestFunction name="testCorrectMyExceptions">
+<Incident type="pass" file="" line="0" />
+</TestFunction>
+<TestFunction name="testFailInt">
+<Incident type="fail" file="/home/user/dev/qt5/qtbase/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp" line="128">
+ <Description><![CDATA[Expected exception of type double to be thrown but unknown exception caught]]></Description>
+</Incident>
+</TestFunction>
+<TestFunction name="testFailStdString">
+<Incident type="fail" file="/home/user/dev/qt5/qtbase/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp" line="133">
+ <Description><![CDATA[Expected exception of type char* to be thrown but unknown exception caught]]></Description>
+</Incident>
+</TestFunction>
+<TestFunction name="testFailStdRuntimeError">
+<Incident type="fail" file="/home/user/dev/qt5/qtbase/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp" line="138">
+ <Description><![CDATA[Expected exception of type std::runtime_error to be thrown but std::exception caught with message: logic error]]></Description>
+</Incident>
+</TestFunction>
+<TestFunction name="testFailMyException">
+<Incident type="fail" file="/home/user/dev/qt5/qtbase/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp" line="143">
+ <Description><![CDATA[Expected exception of type MyBaseException to be thrown but std::exception caught with message: logic error]]></Description>
+</Incident>
+</TestFunction>
+<TestFunction name="testFailMyDerivedException">
+<Incident type="fail" file="/home/user/dev/qt5/qtbase/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp" line="148">
+ <Description><![CDATA[Expected exception of type std::runtime_error to be thrown but std::exception caught with message: MyDerivedException]]></Description>
+</Incident>
+</TestFunction>
+<TestFunction name="testFailNoException">
+<Incident type="fail" file="/home/user/dev/qt5/qtbase/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp" line="153">
+ <Description><![CDATA[Expected exception of type std::exception to be thrown but no exception caught]]></Description>
+</Incident>
+</TestFunction>
+<TestFunction name="cleanupTestCase">
+<Incident type="pass" file="" line="0" />
+</TestFunction>
+</TestCase>
diff --git a/tests/auto/testlib/selftests/expected_verifyexceptionthrown.xunitxml b/tests/auto/testlib/selftests/expected_verifyexceptionthrown.xunitxml
new file mode 100644
index 0000000000..f49746a9af
--- /dev/null
+++ b/tests/auto/testlib/selftests/expected_verifyexceptionthrown.xunitxml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<testsuite errors="0" failures="6" tests="11" name="tst_VerifyExceptionThrown">
+ <properties>
+ <property value="@INSERT_QT_VERSION_HERE@" name="QTestVersion"/>
+ <property value="@INSERT_QT_VERSION_HERE@" name="QtVersion"/>
+ </properties>
+ <testcase result="pass" name="initTestCase"/>
+ <testcase result="pass" name="testCorrectStdTypes"/>
+ <testcase result="pass" name="testCorrectStdExceptions"/>
+ <testcase result="pass" name="testCorrectMyExceptions"/>
+ <testcase result="fail" name="testFailInt">
+ <failure message="Expected exception of type double to be thrown but unknown exception caught" result="fail"/>
+ </testcase>
+ <testcase result="fail" name="testFailStdString">
+ <failure message="Expected exception of type char* to be thrown but unknown exception caught" result="fail"/>
+ </testcase>
+ <testcase result="fail" name="testFailStdRuntimeError">
+ <failure message="Expected exception of type std::runtime_error to be thrown but std::exception caught with message: logic error" result="fail"/>
+ </testcase>
+ <testcase result="fail" name="testFailMyException">
+ <failure message="Expected exception of type MyBaseException to be thrown but std::exception caught with message: logic error" result="fail"/>
+ </testcase>
+ <testcase result="fail" name="testFailMyDerivedException">
+ <failure message="Expected exception of type std::runtime_error to be thrown but std::exception caught with message: MyDerivedException" result="fail"/>
+ </testcase>
+ <testcase result="fail" name="testFailNoException">
+ <failure message="Expected exception of type std::exception to be thrown but no exception caught" result="fail"/>
+ </testcase>
+ <testcase result="pass" name="cleanupTestCase"/>
+ <system-err/>
+</testsuite>
diff --git a/tests/auto/testlib/selftests/expected_warnings.lightxml b/tests/auto/testlib/selftests/expected_warnings.lightxml
index ad786832ca..2ad9e97449 100644
--- a/tests/auto/testlib/selftests/expected_warnings.lightxml
+++ b/tests/auto/testlib/selftests/expected_warnings.lightxml
@@ -24,6 +24,12 @@
<Message type="qdebug" file="" line="0">
<Description><![CDATA[Baba]]></Description>
</Message>
+<Message type="qdebug" file="" line="0">
+ <Description><![CDATA[Bubublabla]]></Description>
+</Message>
+<Message type="qwarn" file="" line="0">
+ <Description><![CDATA[Babablabla]]></Description>
+</Message>
<Incident type="pass" file="" line="0" />
</TestFunction>
<TestFunction name="testMissingWarnings">
@@ -37,6 +43,14 @@
<Description><![CDATA[Not all expected messages were received]]></Description>
</Incident>
</TestFunction>
+<TestFunction name="testMissingWarningsRegularExpression">
+<Message type="info" file="" line="0">
+ <Description><![CDATA[Did not receive any message matching: "Warning\s\d"]]></Description>
+</Message>
+<Incident type="fail" file="" line="0">
+ <Description><![CDATA[Not all expected messages were received]]></Description>
+</Incident>
+</TestFunction>
<TestFunction name="testMissingWarningsWithData">
<Message type="info" file="" line="0">
<DataTag><![CDATA[first row]]></DataTag>
diff --git a/tests/auto/testlib/selftests/expected_warnings.txt b/tests/auto/testlib/selftests/expected_warnings.txt
index d8064651b0..e73de980cb 100644
--- a/tests/auto/testlib/selftests/expected_warnings.txt
+++ b/tests/auto/testlib/selftests/expected_warnings.txt
@@ -7,10 +7,14 @@ QDEBUG : tst_Warnings::testWarnings() Debug
QDEBUG : tst_Warnings::testWarnings() Debug
QDEBUG : tst_Warnings::testWarnings() Baba
QDEBUG : tst_Warnings::testWarnings() Baba
+QDEBUG : tst_Warnings::testWarnings() Bubublabla
+QWARN : tst_Warnings::testWarnings() Babablabla
PASS : tst_Warnings::testWarnings()
INFO : tst_Warnings::testMissingWarnings() Did not receive message: "Warning0"
INFO : tst_Warnings::testMissingWarnings() Did not receive message: "Warning1"
FAIL! : tst_Warnings::testMissingWarnings() Not all expected messages were received
+INFO : tst_Warnings::testMissingWarningsRegularExpression() Did not receive any message matching: "Warning\s\d"
+FAIL! : tst_Warnings::testMissingWarningsRegularExpression() Not all expected messages were received
INFO : tst_Warnings::testMissingWarningsWithData(first row) Did not receive message: "Warning0"
INFO : tst_Warnings::testMissingWarningsWithData(first row) Did not receive message: "Warning1"
FAIL! : tst_Warnings::testMissingWarningsWithData(first row) Not all expected messages were received
@@ -18,5 +22,5 @@ INFO : tst_Warnings::testMissingWarningsWithData(second row) Did not receive m
INFO : tst_Warnings::testMissingWarningsWithData(second row) Did not receive message: "Warning1"
FAIL! : tst_Warnings::testMissingWarningsWithData(second row) Not all expected messages were received
PASS : tst_Warnings::cleanupTestCase()
-Totals: 3 passed, 3 failed, 0 skipped
+Totals: 3 passed, 4 failed, 0 skipped
********* Finished testing of tst_Warnings *********
diff --git a/tests/auto/testlib/selftests/expected_warnings.xml b/tests/auto/testlib/selftests/expected_warnings.xml
index 8ad236b52a..14a45ca4fd 100644
--- a/tests/auto/testlib/selftests/expected_warnings.xml
+++ b/tests/auto/testlib/selftests/expected_warnings.xml
@@ -26,6 +26,12 @@
<Message type="qdebug" file="" line="0">
<Description><![CDATA[Baba]]></Description>
</Message>
+<Message type="qdebug" file="" line="0">
+ <Description><![CDATA[Bubublabla]]></Description>
+</Message>
+<Message type="qwarn" file="" line="0">
+ <Description><![CDATA[Babablabla]]></Description>
+</Message>
<Incident type="pass" file="" line="0" />
</TestFunction>
<TestFunction name="testMissingWarnings">
@@ -39,6 +45,14 @@
<Description><![CDATA[Not all expected messages were received]]></Description>
</Incident>
</TestFunction>
+<TestFunction name="testMissingWarningsRegularExpression">
+<Message type="info" file="" line="0">
+ <Description><![CDATA[Did not receive any message matching: "Warning\s\d"]]></Description>
+</Message>
+<Incident type="fail" file="" line="0">
+ <Description><![CDATA[Not all expected messages were received]]></Description>
+</Incident>
+</TestFunction>
<TestFunction name="testMissingWarningsWithData">
<Message type="info" file="" line="0">
<DataTag><![CDATA[first row]]></DataTag>
diff --git a/tests/auto/testlib/selftests/expected_warnings.xunitxml b/tests/auto/testlib/selftests/expected_warnings.xunitxml
index 3e3b9ce18e..7be47174c6 100644
--- a/tests/auto/testlib/selftests/expected_warnings.xunitxml
+++ b/tests/auto/testlib/selftests/expected_warnings.xunitxml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<testsuite errors="12" failures="3" tests="5" name="tst_Warnings">
+<testsuite errors="15" failures="4" tests="6" name="tst_Warnings">
<properties>
<property value="@INSERT_QT_VERSION_HERE@" name="QTestVersion"/>
<property value="@INSERT_QT_VERSION_HERE@" name="QtVersion"/>
@@ -12,12 +12,18 @@
<!-- message="Debug" type="qdebug" -->
<!-- message="Baba" type="qdebug" -->
<!-- message="Baba" type="qdebug" -->
+ <!-- message="Bubublabla" type="qdebug" -->
+ <!-- message="Babablabla" type="qwarn" -->
</testcase>
<testcase result="fail" name="testMissingWarnings">
<!-- message="Did not receive message: &quot;Warning0&quot;" type="info" -->
<!-- message="Did not receive message: &quot;Warning1&quot;" type="info" -->
<failure message="Not all expected messages were received" result="fail"/>
</testcase>
+ <testcase result="fail" name="testMissingWarningsRegularExpression">
+ <!-- message="Did not receive any message matching: &quot;Warning\s\d&quot;" type="info" -->
+ <failure message="Not all expected messages were received" result="fail"/>
+ </testcase>
<testcase result="fail" name="testMissingWarningsWithData">
<!-- tag="first row" message="Did not receive message: &quot;Warning0&quot;" type="info" -->
<!-- tag="first row" message="Did not receive message: &quot;Warning1&quot;" type="info" -->
@@ -34,8 +40,11 @@
<![CDATA[Debug]]>
<![CDATA[Baba]]>
<![CDATA[Baba]]>
+<![CDATA[Bubublabla]]>
+<![CDATA[Babablabla]]>
<![CDATA[Did not receive message: "Warning0"]]>
<![CDATA[Did not receive message: "Warning1"]]>
+<![CDATA[Did not receive any message matching: "Warning\s\d"]]>
<![CDATA[Did not receive message: "Warning0"]]>
<![CDATA[Did not receive message: "Warning1"]]>
<![CDATA[Did not receive message: "Warning0"]]>
diff --git a/tests/auto/testlib/selftests/selftests.pri b/tests/auto/testlib/selftests/selftests.pri
index c9474419eb..7b706735a9 100644
--- a/tests/auto/testlib/selftests/selftests.pri
+++ b/tests/auto/testlib/selftests/selftests.pri
@@ -41,5 +41,6 @@ SUBPROGRAMS = \
subtest \
verbose1 \
verbose2 \
+ verifyexceptionthrown \
warnings \
xunit
diff --git a/tests/auto/testlib/selftests/selftests.qrc b/tests/auto/testlib/selftests/selftests.qrc
index 03de05fb89..e7ca7138b2 100644
--- a/tests/auto/testlib/selftests/selftests.qrc
+++ b/tests/auto/testlib/selftests/selftests.qrc
@@ -134,6 +134,10 @@
<file>expected_verbose2.txt</file>
<file>expected_verbose2.xml</file>
<file>expected_verbose2.xunitxml</file>
+ <file>expected_verifyexceptionthrown.lightxml</file>
+ <file>expected_verifyexceptionthrown.txt</file>
+ <file>expected_verifyexceptionthrown.xml</file>
+ <file>expected_verifyexceptionthrown.xunitxml</file>
<file>expected_warnings.lightxml</file>
<file>expected_warnings.txt</file>
<file>expected_warnings.xml</file>
diff --git a/tests/auto/testlib/selftests/tst_selftests.cpp b/tests/auto/testlib/selftests/tst_selftests.cpp
index e332f01861..1a8e336b55 100644
--- a/tests/auto/testlib/selftests/tst_selftests.cpp
+++ b/tests/auto/testlib/selftests/tst_selftests.cpp
@@ -381,6 +381,10 @@ void tst_Selftests::runSubTest_data()
<< "subtest"
<< "verbose1"
<< "verbose2"
+#ifndef QT_NO_EXCEPTIONS
+ // this test will test nothing if the exceptions are disabled
+ << "verifyexceptionthrown"
+#endif //!QT_NO_EXCEPTIONS
<< "warnings"
<< "xunit"
;
diff --git a/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp b/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp
new file mode 100644
index 0000000000..fc57f19c4f
--- /dev/null
+++ b/tests/auto/testlib/selftests/verifyexceptionthrown/tst_verifyexceptionthrown.cpp
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the test suite 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+
+#ifndef QT_NO_EXCEPTIONS
+# include <stdexcept>
+#endif
+
+
+#ifndef QT_NO_EXCEPTIONS
+
+class MyBaseException
+{
+
+};
+
+class MyDerivedException: public MyBaseException, public std::domain_error
+{
+public:
+ MyDerivedException(): std::domain_error("MyDerivedException") {}
+};
+
+
+#endif // !QT_NO_EXCEPTIONS
+
+
+class tst_VerifyExceptionThrown: public QObject
+{
+ Q_OBJECT
+private:
+ void doSomething() const {}
+
+private slots:
+// Remove all test cases if exceptions are not available
+#ifndef QT_NO_EXCEPTIONS
+ void testCorrectStdTypes() const;
+ void testCorrectStdExceptions() const;
+ void testCorrectMyExceptions() const;
+
+ void testFailInt() const;
+ void testFailStdString() const;
+ void testFailStdRuntimeError() const;
+ void testFailMyException() const;
+ void testFailMyDerivedException() const;
+
+ void testFailNoException() const;
+#endif // !QT_NO_EXCEPTIONS
+};
+
+
+
+#ifndef QT_NO_EXCEPTIONS
+
+void tst_VerifyExceptionThrown::testCorrectStdTypes() const
+{
+ QVERIFY_EXCEPTION_THROWN(throw int(5), int);
+ QVERIFY_EXCEPTION_THROWN(throw float(9.8), float);
+ QVERIFY_EXCEPTION_THROWN(throw bool(true), bool);
+ QVERIFY_EXCEPTION_THROWN(throw std::string("some string"), std::string);
+}
+
+void tst_VerifyExceptionThrown::testCorrectStdExceptions() const
+{
+ // same type
+ QVERIFY_EXCEPTION_THROWN(throw std::exception(), std::exception);
+ QVERIFY_EXCEPTION_THROWN(throw std::runtime_error("runtime error"), std::runtime_error);
+ QVERIFY_EXCEPTION_THROWN(throw std::overflow_error("overflow error"), std::overflow_error);
+
+ // inheritance
+ QVERIFY_EXCEPTION_THROWN(throw std::overflow_error("overflow error"), std::runtime_error);
+ QVERIFY_EXCEPTION_THROWN(throw std::overflow_error("overflow error"), std::exception);
+}
+
+void tst_VerifyExceptionThrown::testCorrectMyExceptions() const
+{
+ // same type
+ QVERIFY_EXCEPTION_THROWN(throw MyBaseException(), MyBaseException);
+ QVERIFY_EXCEPTION_THROWN(throw MyDerivedException(), MyDerivedException);
+
+ // inheritance
+ QVERIFY_EXCEPTION_THROWN(throw MyDerivedException(), MyBaseException);
+ QVERIFY_EXCEPTION_THROWN(throw MyDerivedException(), std::domain_error);
+}
+
+void tst_VerifyExceptionThrown::testFailInt() const
+{
+ QVERIFY_EXCEPTION_THROWN(throw int(5), double);
+}
+
+void tst_VerifyExceptionThrown::testFailStdString() const
+{
+ QVERIFY_EXCEPTION_THROWN(throw std::string("some string"), char*);
+}
+
+void tst_VerifyExceptionThrown::testFailStdRuntimeError() const
+{
+ QVERIFY_EXCEPTION_THROWN(throw std::logic_error("logic error"), std::runtime_error);
+}
+
+void tst_VerifyExceptionThrown::testFailMyException() const
+{
+ QVERIFY_EXCEPTION_THROWN(throw std::logic_error("logic error"), MyBaseException);
+}
+
+void tst_VerifyExceptionThrown::testFailMyDerivedException() const
+{
+ QVERIFY_EXCEPTION_THROWN(throw MyDerivedException(), std::runtime_error);
+}
+
+void tst_VerifyExceptionThrown::testFailNoException() const
+{
+ QVERIFY_EXCEPTION_THROWN(doSomething(), std::exception);
+}
+
+#endif // !QT_NO_EXCEPTIONS
+
+
+
+QTEST_MAIN(tst_VerifyExceptionThrown)
+
+#include "tst_verifyexceptionthrown.moc"
diff --git a/tests/auto/testlib/selftests/verifyexceptionthrown/verifyexceptionthrown.pro b/tests/auto/testlib/selftests/verifyexceptionthrown/verifyexceptionthrown.pro
new file mode 100644
index 0000000000..51f108c9d7
--- /dev/null
+++ b/tests/auto/testlib/selftests/verifyexceptionthrown/verifyexceptionthrown.pro
@@ -0,0 +1,8 @@
+SOURCES += tst_verifyexceptionthrown.cpp
+QT = core testlib
+
+mac:CONFIG -= app_bundle
+CONFIG -= debug_and_release_target
+CONFIG += exceptions
+
+TARGET = verifyexceptionthrown
diff --git a/tests/auto/testlib/selftests/warnings/tst_warnings.cpp b/tests/auto/testlib/selftests/warnings/tst_warnings.cpp
index 20f53fdc91..ff4357f11f 100644
--- a/tests/auto/testlib/selftests/warnings/tst_warnings.cpp
+++ b/tests/auto/testlib/selftests/warnings/tst_warnings.cpp
@@ -41,6 +41,7 @@
#include <QtCore/QCoreApplication>
+#include <QtCore/QRegularExpression>
#include <QtTest/QtTest>
class tst_Warnings: public QObject
@@ -49,6 +50,7 @@ class tst_Warnings: public QObject
private slots:
void testWarnings();
void testMissingWarnings();
+ void testMissingWarningsRegularExpression();
void testMissingWarningsWithData_data();
void testMissingWarningsWithData();
};
@@ -73,6 +75,13 @@ void tst_Warnings::testWarnings()
qDebug("Baba");
qDebug("Bubu");
qDebug("Baba");
+
+ QTest::ignoreMessage(QtDebugMsg, QRegularExpression("^Bubu.*"));
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression("^Baba.*"));
+ qDebug("Bubublabla");
+ qWarning("Babablabla");
+ qDebug("Bubublabla");
+ qWarning("Babablabla");
}
void tst_Warnings::testMissingWarnings()
@@ -84,6 +93,14 @@ void tst_Warnings::testMissingWarnings()
qWarning("Warning2");
}
+void tst_Warnings::testMissingWarningsRegularExpression()
+{
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression("Warning\\d\\d"));
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression("Warning\\s\\d"));
+
+ qWarning("Warning11");
+}
+
void tst_Warnings::testMissingWarningsWithData_data()
{
QTest::addColumn<int>("dummy");
diff --git a/tests/auto/tools/uic/baseline/translation/Dialog_without_Buttons_tr.h b/tests/auto/tools/uic/baseline/translation/Dialog_without_Buttons_tr.h
new file mode 100644
index 0000000000..597728e207
--- /dev/null
+++ b/tests/auto/tools/uic/baseline/translation/Dialog_without_Buttons_tr.h
@@ -0,0 +1,50 @@
+/********************************************************************************
+** Form generated from reading UI file 'Dialog_without_Buttons.ui'
+**
+** Created by: Qt User Interface Compiler version 5.3.0
+**
+** WARNING! All changes made in this file will be lost when recompiling UI file!
+********************************************************************************/
+
+#ifndef DIALOG_WITHOUT_BUTTONS_TR_H
+#define DIALOG_WITHOUT_BUTTONS_TR_H
+
+#include <QtCore/QVariant>
+#include <QtWidgets/QAction>
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QButtonGroup>
+#include <QtWidgets/QDialog>
+#include <QtWidgets/QHeaderView>
+#include <ki18n.h>
+
+QT_BEGIN_NAMESPACE
+
+class Ui_Dialog
+{
+public:
+
+ void setupUi(QDialog *Dialog)
+ {
+ if (Dialog->objectName().isEmpty())
+ Dialog->setObjectName(QStringLiteral("Dialog"));
+ Dialog->resize(400, 300);
+
+ retranslateUi(Dialog);
+
+ QMetaObject::connectSlotsByName(Dialog);
+ } // setupUi
+
+ void retranslateUi(QDialog *Dialog)
+ {
+ Dialog->setWindowTitle(i18n("Dialog", 0));
+ } // retranslateUi
+
+};
+
+namespace Ui {
+ class Dialog: public Ui_Dialog {};
+} // namespace Ui
+
+QT_END_NAMESPACE
+
+#endif // DIALOG_WITHOUT_BUTTONS_TR_H
diff --git a/tests/auto/tools/uic/tst_uic.cpp b/tests/auto/tools/uic/tst_uic.cpp
index c6e8466d89..02a19c0c33 100644
--- a/tests/auto/tools/uic/tst_uic.cpp
+++ b/tests/auto/tools/uic/tst_uic.cpp
@@ -64,9 +64,13 @@ private Q_SLOTS:
void run();
void run_data() const;
+ void runTranslation();
+
void compare();
void compare_data() const;
+ void runCompare();
+
private:
const QString m_command;
QString m_baseline;
@@ -220,5 +224,53 @@ void tst_uic::compare_data() const
}
}
+void tst_uic::runTranslation()
+{
+ QProcess process;
+
+ QDir baseline(m_baseline);
+
+ QDir generated(m_generated.path());
+ generated.mkdir(QLatin1String("translation"));
+ QString generatedFile = generated.absolutePath() + QLatin1String("/translation/Dialog_without_Buttons_tr.h");
+
+ process.start(m_command, QStringList(baseline.filePath("Dialog_without_Buttons.ui"))
+ << QString(QLatin1String("-tr")) << "i18n"
+ << QString(QLatin1String("-include")) << "ki18n.h"
+ << QString(QLatin1String("-o")) << generatedFile);
+ QVERIFY2(process.waitForStarted(), msgProcessStartFailed(m_command, process.errorString()));
+ QVERIFY(process.waitForFinished());
+ QCOMPARE(process.exitStatus(), QProcess::NormalExit);
+ QCOMPARE(process.exitCode(), 0);
+ QCOMPARE(QFileInfo(generatedFile).exists(), true);
+}
+
+
+void tst_uic::runCompare()
+{
+ QFile orgFile(m_baseline + QLatin1String("/translation/Dialog_without_Buttons_tr.h"));
+
+ QDir generated(m_generated.path());
+ QFile genFile(generated.absolutePath() + QLatin1String("/translation/Dialog_without_Buttons_tr.h"));
+
+ if (!orgFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ QString err(QLatin1String("Could not read file: %1..."));
+ QFAIL(err.arg(orgFile.fileName()).toUtf8());
+ }
+
+ if (!genFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ QString err(QLatin1String("Could not read file: %1..."));
+ QFAIL(err.arg(genFile.fileName()).toUtf8());
+ }
+
+ QString originalFile = orgFile.readAll();
+ originalFile.replace(QRegExp(QLatin1String("Created by: Qt User Interface Compiler version [.\\d]{5,5}")), "");
+
+ QString generatedFile = genFile.readAll();
+ generatedFile.replace(QRegExp(QLatin1String("Created by: Qt User Interface Compiler version [.\\d]{5,5}")), "");
+
+ QCOMPARE(generatedFile, originalFile);
+}
+
QTEST_MAIN(tst_uic)
#include "tst_uic.moc"
diff --git a/tests/auto/widgets/dialogs/dialogs.pro b/tests/auto/widgets/dialogs/dialogs.pro
index acff1df5ba..c5f2c409dd 100644
--- a/tests/auto/widgets/dialogs/dialogs.pro
+++ b/tests/auto/widgets/dialogs/dialogs.pro
@@ -1,6 +1,5 @@
TEMPLATE=subdirs
SUBDIRS=\
- qabstractprintdialog \
qcolordialog \
qdialog \
qerrormessage \
@@ -14,8 +13,6 @@ SUBDIRS=\
qsidebar \
qwizard \
-wince*|!qtHaveModule(printsupport):SUBDIRS -= qabstractprintdialog
-
!contains(QT_CONFIG, private_tests): SUBDIRS -= \
qsidebar \
diff --git a/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp b/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp
index 9cb391d5f4..3a1f650351 100644
--- a/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp
+++ b/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp
@@ -424,7 +424,14 @@ bool tst_QFileSystemModel::createFiles(const QString &test_path, const QStringLi
wchar_t nativeHiddenFile[MAX_PATH];
memset(nativeHiddenFile, 0, sizeof(nativeHiddenFile));
hiddenFile.toWCharArray(nativeHiddenFile);
+#ifndef Q_OS_WINRT
DWORD currentAttributes = ::GetFileAttributes(nativeHiddenFile);
+#else // !Q_OS_WINRT
+ WIN32_FILE_ATTRIBUTE_DATA attributeData;
+ if (!::GetFileAttributesEx(nativeHiddenFile, GetFileExInfoStandard, &attributeData))
+ attributeData.dwFileAttributes = 0xFFFFFFFF;
+ DWORD currentAttributes = attributeData.dwFileAttributes;
+#endif // Q_OS_WINRT
if (currentAttributes == 0xFFFFFFFF) {
qErrnoWarning("failed to get file attributes: %s", qPrintable(hiddenFile));
return false;
diff --git a/tests/auto/widgets/graphicsview/qgraphicsitem/qgraphicsitem.pro b/tests/auto/widgets/graphicsview/qgraphicsitem/qgraphicsitem.pro
index 0c3b46c5d5..527f62b22d 100644
--- a/tests/auto/widgets/graphicsview/qgraphicsitem/qgraphicsitem.pro
+++ b/tests/auto/widgets/graphicsview/qgraphicsitem/qgraphicsitem.pro
@@ -5,5 +5,5 @@ QT += core-private gui-private
SOURCES += tst_qgraphicsitem.cpp
DEFINES += QT_NO_CAST_TO_ASCII
-win32:!wince*: LIBS += -luser32
+win32:!wince*:!winrt: LIBS += -luser32
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
diff --git a/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp
index 6c26ddb293..2798d1e22d 100644
--- a/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp
+++ b/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp
@@ -71,7 +71,7 @@ Q_DECLARE_METATYPE(QPainterPath)
#include "../../../qtest-config.h"
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
#include <windows.h>
#define Q_CHECK_PAINTEVENTS \
if (::SwitchDesktop(::GetThreadDesktop(::GetCurrentThreadId())) == 0) \
diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/qgraphicsscene.pro b/tests/auto/widgets/graphicsview/qgraphicsscene/qgraphicsscene.pro
index 8d00931ef1..a6022e0d7d 100644
--- a/tests/auto/widgets/graphicsview/qgraphicsscene/qgraphicsscene.pro
+++ b/tests/auto/widgets/graphicsview/qgraphicsscene/qgraphicsscene.pro
@@ -4,7 +4,7 @@ QT += widgets widgets-private testlib
QT += core-private gui-private
SOURCES += tst_qgraphicsscene.cpp
RESOURCES += images.qrc
-win32:!wince*: LIBS += -luser32
+win32:!wince*:!winrt: LIBS += -luser32
!wince*:DEFINES += SRCDIR=\\\"$$PWD\\\"
DEFINES += QT_NO_CAST_TO_ASCII
diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp b/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp
index 15c92663ec..dfc8465210 100644
--- a/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp
+++ b/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp
@@ -54,7 +54,7 @@
#include "../../../shared/platforminputcontext.h"
#include <private/qinputmethod_p.h>
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
#include <windows.h>
#define Q_CHECK_PAINTEVENTS \
if (::SwitchDesktop(::GetThreadDesktop(::GetCurrentThreadId())) == 0) \
diff --git a/tests/auto/widgets/itemviews/qitemdelegate/qitemdelegate.pro b/tests/auto/widgets/itemviews/qitemdelegate/qitemdelegate.pro
index cb935fd2fd..313cadd6a1 100644
--- a/tests/auto/widgets/itemviews/qitemdelegate/qitemdelegate.pro
+++ b/tests/auto/widgets/itemviews/qitemdelegate/qitemdelegate.pro
@@ -3,4 +3,4 @@ TARGET = tst_qitemdelegate
QT += widgets testlib
SOURCES += tst_qitemdelegate.cpp
-win32:!wince*: LIBS += -luser32
+win32:!wince*:!winrt: LIBS += -luser32
diff --git a/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp b/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp
index 439725b257..addb226101 100644
--- a/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp
+++ b/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp
@@ -66,7 +66,7 @@
Q_DECLARE_METATYPE(QAbstractItemDelegate::EndEditHint)
-#if defined (Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined (Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
#include <windows.h>
#define Q_CHECK_PAINTEVENTS \
if (::SwitchDesktop(::GetThreadDesktop(::GetCurrentThreadId())) == 0) \
diff --git a/tests/auto/widgets/itemviews/qlistview/qlistview.pro b/tests/auto/widgets/itemviews/qlistview/qlistview.pro
index 413304bdcf..1ea8beb8df 100644
--- a/tests/auto/widgets/itemviews/qlistview/qlistview.pro
+++ b/tests/auto/widgets/itemviews/qlistview/qlistview.pro
@@ -2,4 +2,4 @@ CONFIG += testcase
TARGET = tst_qlistview
QT += widgets gui-private widgets-private core-private testlib
SOURCES += tst_qlistview.cpp
-win32:!wince*: LIBS += -luser32
+win32:!wince*:!winrt: LIBS += -luser32
diff --git a/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp b/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp
index 268276bd4a..9f5484983d 100644
--- a/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp
+++ b/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp
@@ -1484,7 +1484,7 @@ void tst_QListView::wordWrap()
QTRY_COMPARE(lv.verticalScrollBar()->isVisible(), true);
}
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
class SetCurrentIndexAfterAppendRowCrashDialog : public QDialog
{
Q_OBJECT
@@ -1525,7 +1525,7 @@ private:
};
#endif
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && WINVER >= 0x0500
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT) && WINVER >= 0x0500
// This test only makes sense on windows 2000 and higher.
void tst_QListView::setCurrentIndexAfterAppendRowCrash()
{
diff --git a/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp b/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp
index 83ba1ddcda..d2625c400a 100644
--- a/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp
+++ b/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp
@@ -1758,9 +1758,7 @@ void tst_QTreeWidget::setData()
QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item);
QCOMPARE(qvariant_cast<int>(args.at(1)), j);
item->setIcon(j, icon);
- // #### shouldn't cause dataChanged()
- QCOMPARE(itemChangedSpy.count(), 1);
- itemChangedSpy.clear();
+ QCOMPARE(itemChangedSpy.count(), 0);
QString toolTip = QString("toolTip %0").arg(i);
item->setToolTip(j, toolTip);
diff --git a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
index 091927abe4..e13dfe836f 100644
--- a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
+++ b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
@@ -179,6 +179,8 @@ private slots:
void globalStaticObjectDestruction(); // run this last
void abortQuitOnShow();
+
+ void settableStyleHints(); // Needs to run last as it changes style hints.
};
class EventSpy : public QObject
@@ -1949,7 +1951,7 @@ void tst_QApplication::windowsCommandLine_data()
void tst_QApplication::windowsCommandLine()
{
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
QFETCH(QString, args);
QFETCH(QString, expected);
@@ -2305,6 +2307,22 @@ void tst_QApplication::abortQuitOnShow()
QCOMPARE(app.exec(), 1);
}
+void tst_QApplication::settableStyleHints()
+{
+ int argc = 0;
+ QApplication app(argc, 0);
+ QApplication::setCursorFlashTime(437);
+ QCOMPARE(QApplication::cursorFlashTime(), 437);
+ QApplication::setDoubleClickInterval(128);
+ QCOMPARE(QApplication::doubleClickInterval(), 128);
+ QApplication::setStartDragDistance(122000);
+ QCOMPARE(QApplication::startDragDistance(), 122000);
+ QApplication::setStartDragTime(834);
+ QCOMPARE(QApplication::startDragTime(), 834);
+ QApplication::setKeyboardInputInterval(309);
+ QCOMPARE(QApplication::keyboardInputInterval(), 309);
+}
+
/*
This test is meant to ensure that certain objects (public & commonly used)
can safely be used in a Q_GLOBAL_STATIC such that their destructors are
@@ -2314,7 +2332,9 @@ Q_GLOBAL_STATIC(QLocale, tst_qapp_locale);
#ifndef QT_NO_PROCESS
Q_GLOBAL_STATIC(QProcess, tst_qapp_process);
#endif
+#ifndef QT_NO_FILESYSTEMWATCHER
Q_GLOBAL_STATIC(QFileSystemWatcher, tst_qapp_fileSystemWatcher);
+#endif
#ifndef QT_NO_SHAREDMEMORY
Q_GLOBAL_STATIC(QSharedMemory, tst_qapp_sharedMemory);
#endif
@@ -2337,7 +2357,9 @@ void tst_QApplication::globalStaticObjectDestruction()
#ifndef QT_NO_PROCESS
QVERIFY(tst_qapp_process());
#endif
+#ifndef QT_NO_FILESYSTEMWATCHER
QVERIFY(tst_qapp_fileSystemWatcher());
+#endif
#ifndef QT_NO_SHAREDMEMORY
QVERIFY(tst_qapp_sharedMemory());
#endif
diff --git a/tests/auto/widgets/kernel/qwidget/qwidget.pro b/tests/auto/widgets/kernel/qwidget/qwidget.pro
index a4fcde8a34..3eedfa1d3a 100644
--- a/tests/auto/widgets/kernel/qwidget/qwidget.pro
+++ b/tests/auto/widgets/kernel/qwidget/qwidget.pro
@@ -20,7 +20,7 @@ x11 {
LIBS += $$QMAKE_LIBS_X11
}
-!wince*:win32: LIBS += -luser32 -lgdi32
+!wince*:win32:!winrt: LIBS += -luser32 -lgdi32
mac:CONFIG+=insignificant_test # QTBUG-25300, QTBUG-23695
linux-*:system(". /etc/lsb-release && [ $DISTRIB_CODENAME = oneiric ]"):DEFINES+=UBUNTU_ONEIRIC # QTBUG-30566 \ No newline at end of file
diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
index 36a3a8bad5..de028aa462 100644
--- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
+++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
@@ -117,11 +117,13 @@ static bool qt_wince_is_platform(const QString &platformString) {
}
static inline bool qt_wince_is_smartphone() { return qt_wince_is_platform(QString::fromLatin1("Smartphone")); }
# endif // Q_OS_WINCE_WM
-# else // Q_OS_WINCE
+# elif !defined(Q_OS_WINRT) // Q_OS_WINCE
# define Q_CHECK_PAINTEVENTS \
if (::SwitchDesktop(::GetThreadDesktop(::GetCurrentThreadId())) == 0) \
QSKIP("desktop is not visible, this test would fail");
-# endif // !Q_OS_WINCE
+# else // !Q_OS_WINCE && !Q_OS_WINRT
+# define Q_CHECK_PAINTEVENTS
+# endif // Q_OS_WINRT
#else // Q_OS_WIN
# define Q_CHECK_PAINTEVENTS
#endif // else Q_OS_WIN
@@ -304,7 +306,7 @@ private slots:
void subtractOpaqueSiblings();
#endif
-#if defined (Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined (Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
void setGeometry_win();
#endif
@@ -374,7 +376,7 @@ private slots:
void quitOnCloseAttribute();
void moveRect();
-#if defined (Q_OS_WIN)
+#if defined (Q_OS_WIN) && !defined(Q_OS_WINRT)
void gdiPainting();
void paintOnScreenPossible();
#endif
@@ -592,7 +594,7 @@ void tst_QWidget::getSetCheck()
QCOMPARE(true, obj1.autoFillBackground());
var1.reset();
-#if defined (Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined (Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
obj1.setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint);
const HWND handle = reinterpret_cast<HWND>(obj1.winId()); // explicitly create window handle
QVERIFY(GetWindowLong(handle, GWL_STYLE) & WS_POPUP);
@@ -1283,7 +1285,7 @@ void tst_QWidget::visible_setWindowOpacity()
testWidget->hide();
QVERIFY( !testWidget->isVisible() );
testWidget->setWindowOpacity(0.5);
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
QVERIFY(!::IsWindowVisible(winHandleOf(testWidget)));
#endif
testWidget->setWindowOpacity(1.0);
@@ -3623,7 +3625,7 @@ void tst_QWidget::optimizedResize_topLevel()
topLevel.partial = false;
topLevel.paintedRegion = QRegion();
-#ifndef Q_OS_WIN
+#if !defined(Q_OS_WIN32) && !defined(Q_OS_WINCE)
topLevel.resize(topLevel.size() + QSize(10, 10));
#else
// Static contents does not work when programmatically resizing
@@ -4555,7 +4557,7 @@ void tst_QWidget::setWindowGeometry()
}
}
-#if defined (Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined (Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
void tst_QWidget::setGeometry_win()
{
QWidget widget;
@@ -4574,7 +4576,7 @@ void tst_QWidget::setGeometry_win()
QEXPECT_FAIL("", "QTBUG-26424", Continue);
QVERIFY(rt.top <= 0);
}
-#endif // defined (Q_OS_WIN) && !defined(Q_OS_WINCE)
+#endif // defined (Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
// Since X11 WindowManager operation are all async, and we have no way to know if the window
// manager has finished playing with the window geometry, this test can't be reliable on X11.
@@ -8119,7 +8121,7 @@ void tst_QWidget::moveRect()
child.move(10, 10); // Don't crash.
}
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
class GDIWidget : public QDialog
{
Q_OBJECT
@@ -8186,7 +8188,7 @@ void tst_QWidget::paintOnScreenPossible()
w2.setAttribute(Qt::WA_PaintOnScreen);
QVERIFY(w2.testAttribute(Qt::WA_PaintOnScreen));
}
-#endif // Q_OS_WIN
+#endif // Q_OS_WIN && !Q_OS_WINRT
void tst_QWidget::reparentStaticWidget()
{
diff --git a/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp b/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp
index bfffa357a8..ded3e55283 100644
--- a/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp
+++ b/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp
@@ -346,7 +346,7 @@ void tst_QDateTimeEdit::cleanupTestCase()
void tst_QDateTimeEdit::init()
{
QLocale::setDefault(QLocale(QLocale::C));
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
SetThreadLocale(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT));
#endif
testWidget->setDisplayFormat("dd/MM/yyyy"); // Nice default to have
diff --git a/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp b/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp
index b880ebe078..49f058862d 100644
--- a/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp
+++ b/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp
@@ -145,6 +145,9 @@ private slots:
void taskQTBUG_6670_selectAllWithPrefix();
void taskQTBUG_6496_fiddlingWithPrecision();
+ void setGroupSeparatorShown_data();
+ void setGroupSeparatorShown();
+
public slots:
void valueChangedHelper(const QString &);
void valueChangedHelper(double);
@@ -156,6 +159,9 @@ private:
typedef QList<double> DoubleList;
+Q_DECLARE_METATYPE(QLocale::Language)
+Q_DECLARE_METATYPE(QLocale::Country)
+
tst_QDoubleSpinBox::tst_QDoubleSpinBox()
{
@@ -1099,5 +1105,45 @@ void tst_QDoubleSpinBox::taskQTBUG_6496_fiddlingWithPrecision()
QCOMPARE(dsb.maximum(), 0.991);
}
+void tst_QDoubleSpinBox::setGroupSeparatorShown_data()
+{
+ QTest::addColumn<QLocale::Language>("lang");
+ QTest::addColumn<QLocale::Country>("country");
+
+ QTest::newRow("data0") << QLocale::English << QLocale::UnitedStates;
+ QTest::newRow("data1") << QLocale::Swedish << QLocale::Sweden;
+ QTest::newRow("data2") << QLocale::German << QLocale::Germany;
+ QTest::newRow("data3") << QLocale::Georgian << QLocale::Georgia;
+ QTest::newRow("data3") << QLocale::Macedonian << QLocale::Macedonia;
+}
+
+void tst_QDoubleSpinBox::setGroupSeparatorShown()
+{
+ QFETCH(QLocale::Language, lang);
+ QFETCH(QLocale::Country, country);
+
+ QLocale loc(lang, country);
+ QLocale::setDefault(loc);
+ DoubleSpinBox spinBox;
+ spinBox.setMaximum(99999999);
+ spinBox.setValue(1300000.00);
+ spinBox.setGroupSeparatorShown(true);
+ QCOMPARE(spinBox.lineEdit()->text(), spinBox.locale().toString(1300000.00, 'f', 2));
+ QCOMPARE(spinBox.isGroupSeparatorShown(), true);
+ QCOMPARE(spinBox.textFromValue(23421),spinBox.locale().toString(23421.00, 'f', 2));
+
+ spinBox.setGroupSeparatorShown(false);
+ QCOMPARE(spinBox.lineEdit()->text(), spinBox.locale().toString(1300000.00, 'f', 2).remove(
+ spinBox.locale().groupSeparator()));
+ QCOMPARE(spinBox.isGroupSeparatorShown(), false);
+
+ spinBox.setMaximum(72000);
+ spinBox.lineEdit()->setText(spinBox.locale().toString(32000.64, 'f', 2));
+ QCOMPARE(spinBox.value()+1000, 33000.64);
+
+ spinBox.lineEdit()->setText(spinBox.locale().toString(32000.44, 'f', 2));
+ QCOMPARE(spinBox.value()+1000, 33000.44);
+}
+
QTEST_MAIN(tst_QDoubleSpinBox)
#include "tst_qdoublespinbox.moc"
diff --git a/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp b/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp
index 004fdda5ef..1c97686668 100644
--- a/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp
+++ b/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp
@@ -142,6 +142,10 @@ private slots:
void taskQTBUG_5008_textFromValueAndValidate();
void lineEditReturnPressed();
+
+ void setGroupSeparatorShown_data();
+ void setGroupSeparatorShown();
+
public slots:
void valueChangedHelper(const QString &);
void valueChangedHelper(int);
@@ -152,6 +156,9 @@ private:
typedef QList<int> IntList;
+Q_DECLARE_METATYPE(QLocale::Language)
+Q_DECLARE_METATYPE(QLocale::Country)
+
// Testing get/set functions
void tst_QSpinBox::getSetCheck()
{
@@ -1111,5 +1118,47 @@ void tst_QSpinBox::lineEditReturnPressed()
QCOMPARE(spyCurrentChanged.count(), 1);
}
+void tst_QSpinBox::setGroupSeparatorShown_data()
+{
+ QTest::addColumn<QLocale::Language>("lang");
+ QTest::addColumn<QLocale::Country>("country");
+
+ QTest::newRow("data0") << QLocale::English << QLocale::UnitedStates;
+ QTest::newRow("data1") << QLocale::Swedish << QLocale::Sweden;
+ QTest::newRow("data2") << QLocale::German << QLocale::Germany;
+ QTest::newRow("data3") << QLocale::Georgian << QLocale::Georgia;
+ QTest::newRow("data3") << QLocale::Macedonian << QLocale::Macedonia;
+}
+
+void tst_QSpinBox::setGroupSeparatorShown()
+{
+ QFETCH(QLocale::Language, lang);
+ QFETCH(QLocale::Country, country);
+
+ QLocale loc(lang, country);
+ QLocale::setDefault(loc);
+ SpinBox spinBox;
+ spinBox.setMaximum(99999);
+ spinBox.setValue(13000);
+ spinBox.setGroupSeparatorShown(true);
+ QCOMPARE(spinBox.lineEdit()->text(), spinBox.locale().toString(13000));
+ QCOMPARE(spinBox.isGroupSeparatorShown(), true);
+ QCOMPARE(spinBox.textFromValue(23421),spinBox.locale().toString(23421));
+
+ spinBox.setGroupSeparatorShown(false);
+ QCOMPARE(spinBox.lineEdit()->text(), QStringLiteral("13000"));
+ QCOMPARE(spinBox.isGroupSeparatorShown(), false);
+
+ spinBox.setMaximum(72000);
+ spinBox.lineEdit()->setText(spinBox.locale().toString(32000));
+ QCOMPARE(spinBox.value()+1000, 33000);
+
+ spinBox.lineEdit()->setText(QStringLiteral("32000"));
+ QCOMPARE(spinBox.value()+1000, 33000);
+
+ spinBox.lineEdit()->setText(QStringLiteral("32,000"));
+ QCOMPARE(spinBox.value()+1000, 33000);
+}
+
QTEST_MAIN(tst_QSpinBox)
#include "tst_qspinbox.moc"
diff --git a/tests/auto/widgets/widgets/qtabwidget/qtabwidget.pro b/tests/auto/widgets/widgets/qtabwidget/qtabwidget.pro
index 72956a6867..c367959cbc 100644
--- a/tests/auto/widgets/widgets/qtabwidget/qtabwidget.pro
+++ b/tests/auto/widgets/widgets/qtabwidget/qtabwidget.pro
@@ -8,4 +8,4 @@ INCLUDEPATH += ../
HEADERS +=
SOURCES += tst_qtabwidget.cpp
-win32:!wince*:LIBS += -luser32
+win32:!wince*:!winrt:LIBS += -luser32
diff --git a/tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp b/tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp
index fa518e6afd..e55438f3d3 100644
--- a/tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp
+++ b/tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp
@@ -48,7 +48,7 @@
#include <qlabel.h>
#include <QtWidgets/qboxlayout.h>
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
# include <qt_windows.h>
#define Q_CHECK_PAINTEVENTS \
if (::SwitchDesktop(::GetThreadDesktop(::GetCurrentThreadId())) == 0) \
diff --git a/tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp b/tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp
index 11bb5c88a1..d06807eedb 100644
--- a/tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp
+++ b/tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp
@@ -203,6 +203,8 @@ private slots:
void highlightLongLine();
+ void countTextChangedOnRemove();
+
private:
void createSelection();
int blockCount() const;
@@ -2499,6 +2501,19 @@ void tst_QTextEdit::highlightLongLine()
QVERIFY(true);
}
+//check for bug 15003, are there multiple textChanged() signals on remove?
+void tst_QTextEdit::countTextChangedOnRemove()
+{
+ QTextEdit edit;
+ edit.insertPlainText("Hello");
+
+ QSignalSpy spy(&edit, SIGNAL(textChanged()));
+
+ QKeyEvent event(QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier);
+ QCoreApplication::instance()->notify(&edit, &event);
+
+ QCOMPARE(spy.count(), 1);
+}
QTEST_MAIN(tst_QTextEdit)
#include "tst_qtextedit.moc"
diff --git a/tests/baselineserver/shared/baselineprotocol.cpp b/tests/baselineserver/shared/baselineprotocol.cpp
index cbe3ec8798..f6190f20c4 100644
--- a/tests/baselineserver/shared/baselineprotocol.cpp
+++ b/tests/baselineserver/shared/baselineprotocol.cpp
@@ -76,7 +76,11 @@ const QString PI_PulseTestrBranch(QLS("PulseTestrBranch"));
void BaselineProtocol::sysSleep(int ms)
{
#if defined(Q_OS_WIN)
+# ifndef Q_OS_WINRT
Sleep(DWORD(ms));
+# else
+ WaitForSingleObjectEx(GetCurrentThread(), ms, false);
+# endif
#else
struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
nanosleep(&ts, NULL);
@@ -116,6 +120,7 @@ PlatformInfo PlatformInfo::localHostInfo()
pi.insert(PI_OSName, QLS("Other"));
#endif
+#ifndef QT_NO_PROCESS
QProcess git;
QString cmd;
QStringList args;
@@ -151,6 +156,7 @@ PlatformInfo PlatformInfo::localHostInfo()
pi.insert(PI_PulseGitBranch, QString::fromLatin1(gb));
}
}
+#endif // !QT_NO_PROCESS
return pi;
}
diff --git a/tests/benchmarks/corelib/io/qdir/10000/bench_qdir_10000.cpp b/tests/benchmarks/corelib/io/qdir/10000/bench_qdir_10000.cpp
index 49672a90bf..b2380b0e58 100644
--- a/tests/benchmarks/corelib/io/qdir/10000/bench_qdir_10000.cpp
+++ b/tests/benchmarks/corelib/io/qdir/10000/bench_qdir_10000.cpp
@@ -176,7 +176,12 @@ private slots:
wcscat(appendedPath, L"\\*");
WIN32_FIND_DATA fd;
+#ifndef Q_OS_WINRT
HANDLE hSearch = FindFirstFileW(appendedPath, &fd);
+#else
+ HANDLE hSearch = FindFirstFileEx(appendedPath, FindExInfoStandard, &fd,
+ FindExSearchNameMatch, NULL, FIND_FIRST_EX_LARGE_FETCH);
+#endif
QVERIFY(hSearch != INVALID_HANDLE_VALUE);
QBENCHMARK {
diff --git a/tests/benchmarks/corelib/io/qdiriterator/main.cpp b/tests/benchmarks/corelib/io/qdiriterator/main.cpp
index 5188658bdb..691e822379 100644
--- a/tests/benchmarks/corelib/io/qdiriterator/main.cpp
+++ b/tests/benchmarks/corelib/io/qdiriterator/main.cpp
@@ -106,7 +106,12 @@ static int posix_helper(const wchar_t *dirpath)
wchar_t appendedPath[MAX_PATH];
wcscpy(appendedPath, dirpath);
wcscat(appendedPath, L"\\*");
+#ifndef Q_OS_WINRT
hSearch = FindFirstFile(appendedPath, &fd);
+#else
+ hSearch = FindFirstFileEx(appendedPath, FindExInfoStandard, &fd,
+ FindExSearchNameMatch, NULL, FIND_FIRST_EX_LARGE_FETCH);
+#endif
appendedPath[origDirPathLength] = 0;
if (hSearch == INVALID_HANDLE_VALUE) {
diff --git a/tests/benchmarks/corelib/io/qdiriterator/qfilesystemiterator.cpp b/tests/benchmarks/corelib/io/qdiriterator/qfilesystemiterator.cpp
index bb4a921fc7..82bca1541d 100644
--- a/tests/benchmarks/corelib/io/qdiriterator/qfilesystemiterator.cpp
+++ b/tests/benchmarks/corelib/io/qdiriterator/qfilesystemiterator.cpp
@@ -230,7 +230,12 @@ void QFileSystemIteratorPrivate::pushSubDirectory(const QByteArray &path)
wchar_t szSearchPath[MAX_PATH];
QString::fromLatin1(path).toWCharArray(szSearchPath);
wcscat(szSearchPath, L"\\*");
+#ifndef Q_OS_WINRT
HANDLE dir = FindFirstFile(szSearchPath, &m_fileSearchResult);
+#else
+ HANDLE dir = FindFirstFileEx(szSearchPath, FindExInfoStandard, &m_fileSearchResult,
+ FindExSearchLimitToDirectories, NULL, FIND_FIRST_EX_LARGE_FETCH);
+#endif
m_bFirstSearchResult = true;
#else
DIR *dir = ::opendir(path.constData());
diff --git a/tests/benchmarks/corelib/io/qfile/main.cpp b/tests/benchmarks/corelib/io/qfile/main.cpp
index 0beffebfb7..e0ae29fbee 100644
--- a/tests/benchmarks/corelib/io/qfile/main.cpp
+++ b/tests/benchmarks/corelib/io/qfile/main.cpp
@@ -310,7 +310,11 @@ void tst_qfile::readBigFile()
// ensure we don't account string conversion
wchar_t* cfilename = (wchar_t*)filename.utf16();
+#ifndef Q_OS_WINRT
hndl = CreateFile(cfilename, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
+#else
+ hndl = CreateFile2(cfilename, GENERIC_READ, 0, OPEN_EXISTING, 0);
+#endif
Q_ASSERT(hndl);
wchar_t* nativeBuffer = new wchar_t[BUFSIZE];
DWORD numberOfBytesRead;
@@ -319,7 +323,12 @@ void tst_qfile::readBigFile()
do {
ReadFile(hndl, nativeBuffer, blockSize, &numberOfBytesRead, NULL);
} while(numberOfBytesRead != 0);
+#ifndef Q_OS_WINRT
SetFilePointer(hndl, 0, NULL, FILE_BEGIN);
+#else
+ LARGE_INTEGER offset = { 0 };
+ SetFilePointerEx(hndl, offset, NULL, FILE_BEGIN);
+#endif
}
delete[] nativeBuffer;
CloseHandle(hndl);
@@ -400,11 +409,20 @@ void tst_qfile::seek()
// ensure we don't account string conversion
wchar_t* cfilename = (wchar_t*)filename.utf16();
+#ifndef Q_OS_WINRT
hndl = CreateFile(cfilename, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
+#else
+ hndl = CreateFile2(cfilename, GENERIC_READ, 0, OPEN_EXISTING, 0);
+#endif
Q_ASSERT(hndl);
QBENCHMARK {
i=(i+1)%sp_size;
+#ifndef Q_OS_WINRT
SetFilePointer(hndl, seekpos[i], NULL, 0);
+#else
+ LARGE_INTEGER offset = { seekpos[i] };
+ SetFilePointerEx(hndl, offset, NULL, FILE_BEGIN);
+#endif
}
CloseHandle(hndl);
#else
@@ -489,7 +507,11 @@ void tst_qfile::open()
wchar_t* cfilename = (wchar_t*)filename.utf16();
QBENCHMARK {
+#ifndef Q_OS_WINRT
hndl = CreateFile(cfilename, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
+#else
+ hndl = CreateFile2(cfilename, GENERIC_READ, 0, OPEN_EXISTING, 0);
+#endif
Q_ASSERT(hndl);
CloseHandle(hndl);
}
@@ -698,7 +720,11 @@ void tst_qfile::readSmallFiles()
// ensure we don't account string conversion
wchar_t* cfilename = (wchar_t*)filename.utf16();
+#ifndef Q_OS_WINRT
hndl = CreateFile(cfilename, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
+#else
+ hndl = CreateFile2(cfilename, GENERIC_READ, 0, OPEN_EXISTING, 0);
+#endif
Q_ASSERT(hndl);
wchar_t* nativeBuffer = new wchar_t[BUFSIZE];
DWORD numberOfBytesRead;
diff --git a/tests/benchmarks/corelib/io/qfileinfo/main.cpp b/tests/benchmarks/corelib/io/qfileinfo/main.cpp
index 594e5b7478..de1aaea177 100644
--- a/tests/benchmarks/corelib/io/qfileinfo/main.cpp
+++ b/tests/benchmarks/corelib/io/qfileinfo/main.cpp
@@ -54,7 +54,7 @@ class qfileinfo : public QObject
private slots:
void existsTemporary();
void existsStatic();
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
void symLinkTargetPerformanceLNK();
void symLinkTargetPerformanceMounpoint();
#endif
@@ -84,7 +84,7 @@ void qfileinfo::existsStatic()
QBENCHMARK { QFileInfo::exists(appPath); }
}
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
void qfileinfo::symLinkTargetPerformanceLNK()
{
QVERIFY(QFile::link("file","link.lnk"));
diff --git a/tests/benchmarks/corelib/thread/qmutex/tst_qmutex.cpp b/tests/benchmarks/corelib/thread/qmutex/tst_qmutex.cpp
index 47227ef630..52c60f9484 100644
--- a/tests/benchmarks/corelib/thread/qmutex/tst_qmutex.cpp
+++ b/tests/benchmarks/corelib/thread/qmutex/tst_qmutex.cpp
@@ -95,7 +95,11 @@ void NativeMutexUnlock(NativeMutexType *mutex)
typedef CRITICAL_SECTION NativeMutexType;
void NativeMutexInitialize(NativeMutexType *mutex)
{
+#ifndef Q_OS_WINRT
InitializeCriticalSection(mutex);
+#else
+ InitializeCriticalSectionEx(mutex, 0, 0);
+#endif
}
void NativeMutexDestroy(NativeMutexType *mutex)
{
diff --git a/tests/benchmarks/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp b/tests/benchmarks/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp
index 55967b9215..d5919ebe5a 100644
--- a/tests/benchmarks/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp
+++ b/tests/benchmarks/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp
@@ -55,156 +55,88 @@ public:
}
private slots:
- void oscillate_data();
- void oscillate();
-
- void thrash_data();
- void thrash();
-
-public:
- static QWaitCondition local, remote;
- enum Turn {LocalTurn, RemoteTurn};
- static Turn turn;
+ void oscillate_mutex_data();
+ void oscillate_mutex();
+ void oscillate_writelock_data();
+ void oscillate_writelock();
};
-QWaitCondition tst_QWaitCondition::local;
-QWaitCondition tst_QWaitCondition::remote;
-tst_QWaitCondition::Turn tst_QWaitCondition::turn = tst_QWaitCondition::LocalTurn;
+int turn;
+const int threadCount = 10;
+QWaitCondition cond;
+
+template <class Mutex, class Locker>
class OscillateThread : public QThread
{
public:
- bool m_done;
- bool m_useMutex;
- unsigned long m_timeout;
- bool m_wakeOne;
- int count;
-
- OscillateThread(bool useMutex, unsigned long timeout, bool wakeOne)
- : m_done(false), m_useMutex(useMutex), m_timeout(timeout), m_wakeOne(wakeOne)
- {}
+ Mutex *mutex;
+ int m_threadid;
+ int timeout;
+
void run()
{
- QMutex mtx;
- QReadWriteLock rwl;
- count = 0;
-
- forever {
- if (m_done)
- break;
- if (m_useMutex) {
- mtx.lock();
- while (tst_QWaitCondition::turn == tst_QWaitCondition::LocalTurn)
- tst_QWaitCondition::remote.wait(&mtx, m_timeout);
- mtx.unlock();
- } else {
- rwl.lockForWrite();
- while (tst_QWaitCondition::turn == tst_QWaitCondition::LocalTurn)
- tst_QWaitCondition::remote.wait(&rwl, m_timeout);
- rwl.unlock();
+ for (int count = 0; count < 5000; ++count) {
+
+ Locker lock(mutex);
+ while (m_threadid != turn) {
+ cond.wait(mutex, timeout);
}
- tst_QWaitCondition::turn = tst_QWaitCondition::LocalTurn;
- if (m_wakeOne)
- tst_QWaitCondition::local.wakeOne();
- else
- tst_QWaitCondition::local.wakeAll();
- count++;
+ turn = (turn+1) % threadCount;
+ cond.wakeAll();
}
}
};
-void tst_QWaitCondition::oscillate_data()
-{
- QTest::addColumn<bool>("useMutex");
- QTest::addColumn<unsigned long>("timeout");
- QTest::addColumn<bool>("wakeOne");
-
- QTest::newRow("mutex, timeout, one") << true << 1000ul << true;
- QTest::newRow("readWriteLock, timeout, one") << false << 1000ul << true;
- QTest::newRow("mutex, timeout, all") << true << 1000ul << false;
- QTest::newRow("readWriteLock, timeout, all") << false << 1000ul << false;
- QTest::newRow("mutex, forever, one") << true << ULONG_MAX << true;
- QTest::newRow("readWriteLock, forever, one") << false << ULONG_MAX << true;
- QTest::newRow("mutex, forever, all") << true << ULONG_MAX << false;
- QTest::newRow("readWriteLock, forever, all") << false << ULONG_MAX << false;
-}
-
-void tst_QWaitCondition::oscillate()
-{
- QMutex mtx;
- QReadWriteLock rwl;
+template <class Mutex, class Locker>
+void oscillate(unsigned long timeout) {
- QFETCH(bool, useMutex);
- QFETCH(unsigned long, timeout);
- QFETCH(bool, wakeOne);
-
- turn = LocalTurn;
- OscillateThread thrd(useMutex, timeout, wakeOne);
- thrd.start();
+ OscillateThread<Mutex, Locker> thrd[threadCount];
+ Mutex m;
+ for (int i = 0; i < threadCount; ++i) {
+ thrd[i].mutex = &m;
+ thrd[i].m_threadid = i;
+ thrd[i].timeout = timeout;
+ }
QBENCHMARK {
- if (useMutex)
- mtx.lock();
- else
- rwl.lockForWrite();
- turn = RemoteTurn;
- if (wakeOne)
- remote.wakeOne();
- else
- remote.wakeAll();
- if (useMutex) {
- while (turn == RemoteTurn)
- local.wait(&mtx, timeout);
- mtx.unlock();
- } else {
- while (turn == RemoteTurn)
- local.wait(&rwl, timeout);
- rwl.unlock();
+ for (int i = 0; i < threadCount; ++i) {
+ thrd[i].start();
+ }
+ for (int i = 0; i < threadCount; ++i) {
+ thrd[i].wait();
}
}
- thrd.m_done = true;
- remote.wakeAll();
- thrd.wait();
-
- QCOMPARE(0, 0);
}
-void tst_QWaitCondition::thrash_data()
+void tst_QWaitCondition::oscillate_mutex_data()
{
- oscillate_data();
+ QTest::addColumn<unsigned long>("timeout");
+
+ QTest::newRow("0") << 0ul;
+ QTest::newRow("1") << 1ul;
+ QTest::newRow("1000") << 1000ul;
+ QTest::newRow("forever") << ULONG_MAX;
}
-void tst_QWaitCondition::thrash()
+void tst_QWaitCondition::oscillate_mutex()
{
- QMutex mtx;
- mtx.lock();
-
- QFETCH(bool, useMutex);
QFETCH(unsigned long, timeout);
- QFETCH(bool, wakeOne);
-
- turn = LocalTurn;
- OscillateThread thrd(useMutex, timeout, wakeOne);
- thrd.start();
- local.wait(&mtx, 1000ul);
- mtx.unlock();
-
- QBENCHMARK {
- turn = RemoteTurn;
- if (wakeOne)
- remote.wakeOne();
- else
- remote.wakeAll();
- }
+ oscillate<QMutex, QMutexLocker>(timeout);
+}
- thrd.m_done = true;
- turn = RemoteTurn;
- remote.wakeAll();
- thrd.wait();
+void tst_QWaitCondition::oscillate_writelock_data()
+{
+ oscillate_mutex_data();
+}
- QCOMPARE(0, 0);
+void tst_QWaitCondition::oscillate_writelock()
+{
+ QFETCH(unsigned long, timeout);
+ oscillate<QReadWriteLock, QWriteLocker>(timeout);
}
+
QTEST_MAIN(tst_QWaitCondition)
#include "tst_qwaitcondition.moc"
diff --git a/tests/benchmarks/corelib/thread/thread.pro b/tests/benchmarks/corelib/thread/thread.pro
index d7f65a911d..4e602ceb4e 100644
--- a/tests/benchmarks/corelib/thread/thread.pro
+++ b/tests/benchmarks/corelib/thread/thread.pro
@@ -3,3 +3,4 @@ SUBDIRS = \
qmutex \
qthreadstorage \
qthreadpool \
+ qwaitcondition \
diff --git a/tests/shared/filesystem.h b/tests/shared/filesystem.h
index 7aa72cc28e..a68130a324 100644
--- a/tests/shared/filesystem.h
+++ b/tests/shared/filesystem.h
@@ -92,7 +92,7 @@ public:
return file.isNull() ? qint64(-1) : file->write(relativeFileName.toUtf8());
}
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
static void createNtfsJunction(QString target, QString linkName)
{
typedef struct {
diff --git a/tools/configure/configureapp.cpp b/tools/configure/configureapp.cpp
index 5ab413a971..d237228812 100644
--- a/tools/configure/configureapp.cpp
+++ b/tools/configure/configureapp.cpp
@@ -194,6 +194,7 @@ Configure::Configure(int& argc, char** argv)
dictionary[ "QT_CUPS" ] = "auto";
dictionary[ "CFG_GCC_SYSROOT" ] = "yes";
dictionary[ "SLOG2" ] = "no";
+ dictionary[ "QNX_IMF" ] = "no";
dictionary[ "PPS" ] = "no";
dictionary[ "SYSTEM_PROXIES" ] = "no";
dictionary[ "WERROR" ] = "auto";
@@ -880,6 +881,10 @@ void Configure::parseCmdLine()
dictionary[ "SLOG2" ] = "no";
} else if (configCmdLine.at(i) == "-slog2") {
dictionary[ "SLOG2" ] = "yes";
+ } else if (configCmdLine.at(i) == "-no-imf") {
+ dictionary[ "QNX_IMF" ] = "no";
+ } else if (configCmdLine.at(i) == "-imf") {
+ dictionary[ "QNX_IMF" ] = "yes";
} else if (configCmdLine.at(i) == "-no-pps") {
dictionary[ "PPS" ] = "no";
} else if (configCmdLine.at(i) == "-pps") {
@@ -1596,8 +1601,8 @@ void Configure::applySpecSpecifics()
dictionary[ "LIBPNG" ] = "qt";
dictionary[ "FREETYPE" ] = "yes";
dictionary[ "ACCESSIBILITY" ] = "no";
- dictionary[ "OPENGL" ] = "no";
- dictionary[ "OPENGL_ES_2" ] = "no";
+ dictionary[ "OPENGL" ] = "yes";
+ dictionary[ "OPENGL_ES_2" ] = "yes";
dictionary[ "OPENVG" ] = "no";
dictionary[ "OPENSSL" ] = "auto";
dictionary[ "DBUS" ] = "no";
@@ -1606,7 +1611,7 @@ void Configure::applySpecSpecifics()
dictionary[ "ICU" ] = "qt";
dictionary[ "CE_CRT" ] = "yes";
dictionary[ "LARGE_FILE" ] = "no";
- dictionary[ "ANGLE" ] = "no";
+ dictionary[ "ANGLE" ] = "d3d11";
if (dictionary.value("XQMAKESPEC").startsWith("winphone"))
dictionary[ "SQL_SQLITE" ] = "no";
} else if (dictionary.value("XQMAKESPEC").startsWith("wince")) {
@@ -1653,6 +1658,7 @@ void Configure::applySpecSpecifics()
} else if ((platform() == QNX) || (platform() == BLACKBERRY)) {
dictionary["STACK_PROTECTOR_STRONG"] = "auto";
dictionary["SLOG2"] = "auto";
+ dictionary["QNX_IMF"] = "auto";
dictionary["PPS"] = "auto";
dictionary["QT_XKBCOMMON"] = "no";
dictionary[ "ANGLE" ] = "no";
@@ -1870,7 +1876,8 @@ bool Configure::displayHelp()
if ((platform() == QNX) || (platform() == BLACKBERRY)) {
desc("SLOG2", "yes", "-slog2", "Compile with slog2 support.");
desc("SLOG2", "no", "-no-slog2", "Do not compile with slog2 support.");
-
+ desc("QNX_IMF", "yes", "-imf", "Compile with imf support.");
+ desc("QNX_IMF", "no", "-no-imf", "Do not compile with imf support.");
desc("PPS", "yes", "-pps", "Compile with PPS support.");
desc("PPS", "no", "-no-pps", "Do not compile with PPS support.");
}
@@ -2212,6 +2219,8 @@ bool Configure::checkAvailability(const QString &part)
available = (platform() == QNX || platform() == BLACKBERRY) && compilerSupportsFlag("qcc -fstack-protector-strong");
} else if (part == "SLOG2") {
available = tryCompileProject("unix/slog2");
+ } else if (part == "QNX_IMF") {
+ available = tryCompileProject("unix/qqnx_imf");
} else if (part == "PPS") {
available = (platform() == QNX || platform() == BLACKBERRY) && tryCompileProject("unix/pps");
} else if (part == "NEON") {
@@ -2359,6 +2368,10 @@ void Configure::autoDetection()
dictionary["SLOG2"] = checkAvailability("SLOG2") ? "yes" : "no";
}
+ if ((platform() == QNX || platform() == BLACKBERRY) && dictionary["QNX_IMF"] == "auto") {
+ dictionary["QNX_IMF"] = checkAvailability("QNX_IMF") ? "yes" : "no";
+ }
+
if (dictionary["PPS"] == "auto") {
dictionary["PPS"] = checkAvailability("PPS") ? "yes" : "no";
}
@@ -2988,6 +3001,10 @@ void Configure::detectArch()
.arg(QDir::toNativeSeparators(buildPath + "/bin/qmake.exe"),
QDir::toNativeSeparators(qmakespec),
QDir::toNativeSeparators(sourcePath + "/config.tests/arch/arch.pro"));
+
+ if (qmakespec.startsWith("winrt") || qmakespec.startsWith("winphone"))
+ command.append(" QMAKE_LFLAGS+=/ENTRY:main");
+
int returnValue = 0;
Environment::execute(command, &returnValue);
if (returnValue != 0) {
@@ -3088,6 +3105,13 @@ bool Configure::tryCompileProject(const QString &projectPath, const QString &ext
.arg(QDir::toNativeSeparators(buildPath + "/bin/qmake.exe"),
QDir::toNativeSeparators(sourcePath + "/config.tests/" + projectPath),
extraOptions);
+
+ if (dictionary.contains("XQMAKESPEC")) {
+ const QString qmakespec = dictionary["XQMAKESPEC"];
+ if (qmakespec.startsWith("winrt") || qmakespec.startsWith("winphone"))
+ command.append(" QMAKE_LFLAGS+=/ENTRY:main");
+ }
+
int code = 0;
QString output = Environment::execute(command, &code);
//cout << output << endl;
@@ -3189,6 +3213,9 @@ void Configure::generateQConfigPri()
if (dictionary[ "SLOG2" ] == "yes")
configStream << " slog2";
+ if (dictionary[ "QNX_IMF" ] == "yes")
+ configStream << " qqnx_imf";
+
if (dictionary[ "PPS" ] == "yes")
configStream << " qqnx_pps";
@@ -3560,6 +3587,7 @@ void Configure::displayConfig()
sout << " ICU support............." << dictionary[ "ICU" ] << endl;
if ((platform() == QNX) || (platform() == BLACKBERRY)) {
sout << " SLOG2 support..........." << dictionary[ "SLOG2" ] << endl;
+ sout << " IMF support............." << dictionary[ "QNX_IMF" ] << endl;
sout << " PPS support............." << dictionary[ "PPS" ] << endl;
}
sout << " ANGLE..................." << dictionary[ "ANGLE" ] << endl;