aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libs/3rdparty/sqlite/carray.c5
-rw-r--r--src/libs/3rdparty/sqlite/sqlite3.c14476
-rw-r--r--src/libs/3rdparty/sqlite/sqlite3.h347
-rw-r--r--src/libs/3rdparty/sqlite/sqlite3ext.h6
-rw-r--r--src/libs/extensionsystem/pluginmanager.cpp2
-rw-r--r--src/libs/nanotrace/CMakeLists.txt12
-rw-r--r--src/libs/nanotrace/nanotraceglobals.h18
-rw-r--r--src/libs/nanotrace/nanotracehr.cpp89
-rw-r--r--src/libs/nanotrace/nanotracehr.h642
-rw-r--r--src/libs/nanotrace/staticstring.h116
-rw-r--r--src/libs/qmljs/CMakeLists.txt1
-rw-r--r--src/libs/qmljs/qmljs.qbs1
-rw-r--r--src/libs/qmljs/qmljsindenter.cpp603
-rw-r--r--src/libs/qmljs/qmljsindenter.h51
-rw-r--r--src/libs/qmljs/qmljsplugindumper.cpp2
-rw-r--r--src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h7
-rw-r--r--src/libs/qtcreatorcdbext/CMakeLists.txt2
-rw-r--r--src/libs/sqlite/CMakeLists.txt4
-rw-r--r--src/libs/sqlite/sqlitebasestatement.cpp204
-rw-r--r--src/libs/sqlite/sqlitebasestatement.h68
-rw-r--r--src/libs/sqlite/sqliteexception.cpp30
-rw-r--r--src/libs/sqlite/sqliteexception.h9
-rw-r--r--src/libs/sqlite/sqliteids.h11
-rw-r--r--src/libs/sqlite/sqlitetracing.cpp38
-rw-r--r--src/libs/sqlite/sqlitetracing.h29
-rw-r--r--src/libs/sqlite/sqlitevalue.h24
-rw-r--r--src/libs/utils/CMakeLists.txt2
-rw-r--r--src/libs/utils/buildablehelperlibrary.cpp2
-rw-r--r--src/libs/utils/clangutils.cpp2
-rw-r--r--src/libs/utils/devicefileaccess.cpp2
-rw-r--r--src/libs/utils/deviceshell.cpp2
-rw-r--r--src/libs/utils/externalterminalprocessimpl.cpp2
-rw-r--r--src/libs/utils/filestreamer.cpp2
-rw-r--r--src/libs/utils/pathchooser.cpp2
-rw-r--r--src/libs/utils/processinfo.cpp2
-rw-r--r--src/libs/utils/qtcprocess.cpp (renamed from src/libs/utils/process.cpp)4
-rw-r--r--src/libs/utils/qtcprocess.h (renamed from src/libs/utils/process.h)8
-rw-r--r--src/libs/utils/ranges.h4
-rw-r--r--src/libs/utils/smallstring.h124
-rw-r--r--src/libs/utils/span.h9
-rw-r--r--src/libs/utils/terminalhooks.cpp2
-rw-r--r--src/libs/utils/unarchiver.h2
-rw-r--r--src/libs/utils/utils.qbs4
-rw-r--r--src/plugins/CMakeLists.txt1
-rw-r--r--src/plugins/android/androidavdmanager.cpp2
-rw-r--r--src/plugins/android/androidbuildapkstep.cpp2
-rw-r--r--src/plugins/android/androidconfigurations.cpp2
-rw-r--r--src/plugins/android/androidcreatekeystorecertificate.cpp2
-rw-r--r--src/plugins/android/androiddebugsupport.cpp2
-rw-r--r--src/plugins/android/androiddeployqtstep.cpp2
-rw-r--r--src/plugins/android/androiddevice.cpp2
-rw-r--r--src/plugins/android/androidmanager.cpp2
-rw-r--r--src/plugins/android/androidpackageinstallationstep.cpp2
-rw-r--r--src/plugins/android/androidqmlpreviewworker.cpp2
-rw-r--r--src/plugins/android/androidrunconfiguration.cpp2
-rw-r--r--src/plugins/android/androidrunnerworker.cpp2
-rw-r--r--src/plugins/android/androidsdkmanager.cpp2
-rw-r--r--src/plugins/android/androidsettingswidget.cpp2
-rw-r--r--src/plugins/android/androidsignaloperation.cpp2
-rw-r--r--src/plugins/autotest/boost/boosttestoutputreader.cpp2
-rw-r--r--src/plugins/autotest/gtest/gtestoutputreader.cpp2
-rw-r--r--src/plugins/autotest/testoutputreader.cpp2
-rw-r--r--src/plugins/autotest/testrunner.cpp2
-rw-r--r--src/plugins/autotoolsprojectmanager/autogenstep.cpp2
-rw-r--r--src/plugins/autotoolsprojectmanager/autoreconfstep.cpp2
-rw-r--r--src/plugins/autotoolsprojectmanager/configurestep.cpp2
-rw-r--r--src/plugins/autotoolsprojectmanager/makefileparser.cpp2
-rw-r--r--src/plugins/baremetal/baremetaldebugsupport.cpp2
-rw-r--r--src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp2
-rw-r--r--src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp2
-rw-r--r--src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h2
-rw-r--r--src/plugins/baremetal/iarewtoolchain.cpp2
-rw-r--r--src/plugins/baremetal/keiltoolchain.cpp2
-rw-r--r--src/plugins/baremetal/sdcctoolchain.cpp2
-rw-r--r--src/plugins/beautifier/artisticstyle/artisticstyle.cpp2
-rw-r--r--src/plugins/beautifier/beautifiertool.cpp2
-rw-r--r--src/plugins/beautifier/uncrustify/uncrustify.cpp2
-rw-r--r--src/plugins/boot2qt/device-detection/qdbwatcher.cpp2
-rw-r--r--src/plugins/boot2qt/qdbdevice.cpp2
-rw-r--r--src/plugins/boot2qt/qdbdevicedebugsupport.cpp2
-rw-r--r--src/plugins/boot2qt/qdbmakedefaultappstep.cpp2
-rw-r--r--src/plugins/boot2qt/qdbplugin.cpp2
-rw-r--r--src/plugins/boot2qt/qdbstopapplicationstep.cpp2
-rw-r--r--src/plugins/clangtools/clangtoolrunner.cpp2
-rw-r--r--src/plugins/clangtools/clangtoolsutils.cpp2
-rw-r--r--src/plugins/clangtools/executableinfo.cpp2
-rw-r--r--src/plugins/clearcase/clearcaseplugin.cpp2
-rw-r--r--src/plugins/clearcase/clearcasesync.cpp2
-rw-r--r--src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp2
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprocess.cpp2
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp2
-rw-r--r--src/plugins/cmakeprojectmanager/cmaketool.cpp2
-rw-r--r--src/plugins/cmakeprojectmanager/fileapidataextractor.cpp2
-rw-r--r--src/plugins/coreplugin/dialogs/externaltoolconfig.cpp2
-rw-r--r--src/plugins/coreplugin/externaltool.cpp2
-rw-r--r--src/plugins/coreplugin/fileutils.cpp2
-rw-r--r--src/plugins/coreplugin/locator/executefilter.cpp2
-rw-r--r--src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp2
-rw-r--r--src/plugins/coreplugin/patchtool.cpp2
-rw-r--r--src/plugins/coreplugin/plugininstallwizard.cpp2
-rw-r--r--src/plugins/coreplugin/progressmanager/processprogress.cpp2
-rw-r--r--src/plugins/coreplugin/systemsettings.cpp8
-rw-r--r--src/plugins/coreplugin/themechooser.cpp6
-rw-r--r--src/plugins/cppcheck/cppcheckrunner.h2
-rw-r--r--src/plugins/cppeditor/cppcodemodelsettings.cpp3
-rw-r--r--src/plugins/cppeditor/cppmodelmanager.cpp2
-rw-r--r--src/plugins/debugger/cdb/cdbengine.cpp2
-rw-r--r--src/plugins/debugger/cdb/cdbengine.h2
-rw-r--r--src/plugins/debugger/dap/dapclient.cpp2
-rw-r--r--src/plugins/debugger/dap/dapclient.h2
-rw-r--r--src/plugins/debugger/dap/dapengine.cpp2
-rw-r--r--src/plugins/debugger/dap/dapengine.h2
-rw-r--r--src/plugins/debugger/debuggerengine.cpp2
-rw-r--r--src/plugins/debugger/debuggeritem.cpp2
-rw-r--r--src/plugins/debugger/debuggeritemmanager.cpp2
-rw-r--r--src/plugins/debugger/debuggerruncontrol.cpp2
-rw-r--r--src/plugins/debugger/debuggersourcepathmappingwidget.cpp2
-rw-r--r--src/plugins/debugger/gdb/gdbengine.cpp2
-rw-r--r--src/plugins/debugger/gdb/gdbengine.h2
-rw-r--r--src/plugins/debugger/lldb/lldbengine.cpp2
-rw-r--r--src/plugins/debugger/lldb/lldbengine.h2
-rw-r--r--src/plugins/debugger/moduleshandler.cpp2
-rw-r--r--src/plugins/debugger/pdb/pdbengine.cpp2
-rw-r--r--src/plugins/debugger/pdb/pdbengine.h2
-rw-r--r--src/plugins/debugger/qml/qmlengine.cpp2
-rw-r--r--src/plugins/debugger/terminal.cpp2
-rw-r--r--src/plugins/docker/dockerapi.cpp2
-rw-r--r--src/plugins/docker/dockerdevice.cpp2
-rw-r--r--src/plugins/effectcomposer/compositionnode.cpp7
-rw-r--r--src/plugins/effectcomposer/compositionnode.h4
-rw-r--r--src/plugins/effectcomposer/effectcomposermodel.cpp463
-rw-r--r--src/plugins/effectcomposer/effectcomposermodel.h20
-rw-r--r--src/plugins/effectcomposer/effectcomposernodesmodel.cpp10
-rw-r--r--src/plugins/effectcomposer/effectcomposernodesmodel.h3
-rw-r--r--src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp14
-rw-r--r--src/plugins/effectcomposer/effectcomposeruniformsmodel.h3
-rw-r--r--src/plugins/effectcomposer/effectcomposerview.cpp27
-rw-r--r--src/plugins/effectcomposer/effectcomposerview.h3
-rw-r--r--src/plugins/effectcomposer/effectcomposerwidget.cpp15
-rw-r--r--src/plugins/effectcomposer/effectcomposerwidget.h3
-rw-r--r--src/plugins/effectcomposer/effectnode.cpp7
-rw-r--r--src/plugins/effectcomposer/effectnode.h2
-rw-r--r--src/plugins/effectcomposer/uniform.cpp233
-rw-r--r--src/plugins/effectcomposer/uniform.h13
-rw-r--r--src/plugins/fakevim/fakevimplugin.cpp2
-rw-r--r--src/plugins/fossil/fossilclient.cpp2
-rw-r--r--src/plugins/genericprojectmanager/genericproject.cpp2
-rw-r--r--src/plugins/git/branchmodel.cpp2
-rw-r--r--src/plugins/git/branchview.cpp2
-rw-r--r--src/plugins/git/changeselectiondialog.cpp2
-rw-r--r--src/plugins/git/gerrit/gerritmodel.cpp2
-rw-r--r--src/plugins/git/gerrit/gerritplugin.cpp2
-rw-r--r--src/plugins/git/gitclient.cpp2
-rw-r--r--src/plugins/git/gitgrep.cpp2
-rw-r--r--src/plugins/git/mergetool.h2
-rw-r--r--src/plugins/gitlab/gitlabclonedialog.cpp2
-rw-r--r--src/plugins/gitlab/queryrunner.h2
-rw-r--r--src/plugins/haskell/haskellmanager.cpp2
-rw-r--r--src/plugins/incredibuild/cmakecommandbuilder.cpp2
-rw-r--r--src/plugins/ios/iosbuildstep.cpp2
-rw-r--r--src/plugins/ios/iosconfigurations.cpp2
-rw-r--r--src/plugins/ios/iosdeploystep.cpp2
-rw-r--r--src/plugins/ios/iosdevice.cpp2
-rw-r--r--src/plugins/ios/iosdsymbuildstep.cpp2
-rw-r--r--src/plugins/ios/iosprobe.cpp2
-rw-r--r--src/plugins/ios/iosrunconfiguration.cpp2
-rw-r--r--src/plugins/ios/iosrunner.cpp2
-rw-r--r--src/plugins/ios/iossimulator.cpp2
-rw-r--r--src/plugins/ios/iostoolhandler.cpp2
-rw-r--r--src/plugins/ios/simulatorcontrol.cpp2
-rw-r--r--src/plugins/languageclient/client.cpp2
-rw-r--r--src/plugins/languageclient/languageclientinterface.h2
-rw-r--r--src/plugins/languageclient/languageclientutils.cpp2
-rw-r--r--src/plugins/mcusupport/mcuqmlprojectnode.h2
-rw-r--r--src/plugins/mcusupport/mcusupportversiondetection.cpp2
-rw-r--r--src/plugins/mercurial/mercurialclient.cpp2
-rw-r--r--src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp2
-rw-r--r--src/plugins/mesonprojectmanager/mesonwrapper.h2
-rw-r--r--src/plugins/mesonprojectmanager/toolwrapper.cpp2
-rw-r--r--src/plugins/nim/project/nimblebuildsystem.cpp2
-rw-r--r--src/plugins/nim/project/nimcompilerbuildstep.cpp2
-rw-r--r--src/plugins/nim/project/nimtoolchain.cpp2
-rw-r--r--src/plugins/nim/suggest/server.h2
-rw-r--r--src/plugins/perforce/perforcechecker.h2
-rw-r--r--src/plugins/perforce/perforceplugin.cpp2
-rw-r--r--src/plugins/perfprofiler/perfprofilerruncontrol.cpp2
-rw-r--r--src/plugins/perfprofiler/perfsettings.cpp2
-rw-r--r--src/plugins/perfprofiler/perftracepointdialog.cpp2
-rw-r--r--src/plugins/projectexplorer/abstractprocessstep.cpp2
-rw-r--r--src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp2
-rw-r--r--src/plugins/projectexplorer/devicesupport/desktopdevice.cpp2
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicemanager.cpp2
-rw-r--r--src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp2
-rw-r--r--src/plugins/projectexplorer/devicesupport/sshparameters.cpp2
-rw-r--r--src/plugins/projectexplorer/extracompiler.cpp2
-rw-r--r--src/plugins/projectexplorer/gcctoolchain.cpp2
-rw-r--r--src/plugins/projectexplorer/makestep.cpp2
-rw-r--r--src/plugins/projectexplorer/msvctoolchain.cpp2
-rw-r--r--src/plugins/projectexplorer/processparameters.cpp2
-rw-r--r--src/plugins/projectexplorer/projectmodels.cpp2
-rw-r--r--src/plugins/projectexplorer/runconfigurationaspects.cpp2
-rw-r--r--src/plugins/projectexplorer/runcontrol.cpp2
-rw-r--r--src/plugins/projectexplorer/targetsetuppage.cpp2
-rw-r--r--src/plugins/projectexplorer/taskfile.cpp2
-rw-r--r--src/plugins/projectexplorer/toolchainconfigwidget.cpp2
-rw-r--r--src/plugins/projectexplorer/userfileaccessor.cpp2
-rw-r--r--src/plugins/python/pipsupport.cpp2
-rw-r--r--src/plugins/python/pipsupport.h2
-rw-r--r--src/plugins/python/pyside.cpp2
-rw-r--r--src/plugins/python/pysideuicextracompiler.cpp2
-rw-r--r--src/plugins/python/pythonbuildconfiguration.cpp2
-rw-r--r--src/plugins/python/pythonkitaspect.cpp2
-rw-r--r--src/plugins/python/pythonlanguageclient.cpp2
-rw-r--r--src/plugins/python/pythonsettings.cpp2
-rw-r--r--src/plugins/python/pythonutils.cpp2
-rw-r--r--src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp2
-rw-r--r--src/plugins/qbsprojectmanager/qbsprofilemanager.cpp2
-rw-r--r--src/plugins/qbsprojectmanager/qbssession.cpp2
-rw-r--r--src/plugins/qbsprojectmanager/qbssettings.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/makefileparse.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/qmakemakestep.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeproject.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/qmakestep.cpp2
-rw-r--r--src/plugins/qmldesigner/.clang-format97
-rw-r--r--src/plugins/qmldesigner/CMakeLists.txt45
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp26
-rw-r--r--src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp35
-rw-r--r--src/plugins/qmldesigner/components/bindingeditor/actioneditor.h4
-rw-r--r--src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp49
-rw-r--r--src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h4
-rw-r--r--src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp4
-rw-r--r--src/plugins/qmldesigner/components/bindingeditor/signallist.cpp63
-rw-r--r--src/plugins/qmldesigner/components/bindingeditor/signallist.h6
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp87
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h34
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp803
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h64
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp357
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h11
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h3
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp241
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h17
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp392
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h45
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp725
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h118
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp418
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionview.h102
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp217
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h28
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp58
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h11
-rw-r--r--src/plugins/qmldesigner/components/colortool/colortool.cpp29
-rw-r--r--src/plugins/qmldesigner/components/colortool/colortool.h4
-rw-r--r--src/plugins/qmldesigner/components/componentcore/componentcore_constants.h6
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp97
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designericons.h1
-rw-r--r--src/plugins/qmldesigner/components/componentcore/dialogutils.cpp32
-rw-r--r--src/plugins/qmldesigner/components/componentcore/dialogutils.h17
-rw-r--r--src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp10
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp6
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h20
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp230
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h2
-rw-r--r--src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp2
-rw-r--r--src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp272
-rw-r--r--src/plugins/qmldesigner/components/componentcore/resourcegenerator.h18
-rw-r--r--src/plugins/qmldesigner/components/componentcore/theme.h2
-rw-r--r--src/plugins/qmldesigner/components/componentcore/utils3d.cpp157
-rw-r--r--src/plugins/qmldesigner/components/componentcore/utils3d.h28
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp18
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/backendmodel.h2
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp19
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h71
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp89
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h188
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp140
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionview.h15
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp14
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h9
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp7
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h4
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp31
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h5
-rw-r--r--src/plugins/qmldesigner/components/createtexture.cpp15
-rw-r--r--src/plugins/qmldesigner/components/debugview/debugview.cpp7
-rw-r--r--src/plugins/qmldesigner/components/edit3d/bakelights.cpp44
-rw-r--r--src/plugins/qmldesigner/components/edit3d/bakelights.h3
-rw-r--r--src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp8
-rw-r--r--src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp192
-rw-r--r--src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h76
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp4
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp96
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h19
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dview.cpp169
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dview.h20
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp9
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dwidget.h7
-rw-r--r--src/plugins/qmldesigner/components/eventlist/eventlist.cpp3
-rw-r--r--src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.cpp102
-rw-r--r--src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.h36
-rw-r--r--src/plugins/qmldesigner/components/formeditor/dragtool.cpp7
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditorview.cpp7
-rw-r--r--src/plugins/qmldesigner/components/formeditor/movetool.cpp21
-rw-r--r--src/plugins/qmldesigner/components/formeditor/movetool.h2
-rw-r--r--src/plugins/qmldesigner/components/formeditor/selectiontool.cpp5
-rw-r--r--src/plugins/qmldesigner/components/formeditor/selectiontool.h2
-rw-r--r--src/plugins/qmldesigner/components/formeditor/view3dtool.cpp114
-rw-r--r--src/plugins/qmldesigner/components/formeditor/view3dtool.h52
-rw-r--r--src/plugins/qmldesigner/components/integration/designdocument.cpp18
-rw-r--r--src/plugins/qmldesigner/components/integration/designdocumentview.cpp16
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp7
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp11
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h5
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialutils.cpp7
-rw-r--r--src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp31
-rw-r--r--src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.h20
-rw-r--r--src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp22
-rw-r--r--src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h6
-rw-r--r--src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp31
-rw-r--r--src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp4
-rw-r--r--src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp6
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp13
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatorview.cpp12
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp4
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h2
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp10
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp39
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h23
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp32
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h11
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp53
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h6
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp95
-rw-r--r--src/plugins/qmldesigner/components/stateseditor/propertymodel.cpp9
-rw-r--r--src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp4
-rw-r--r--src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp2
-rw-r--r--src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp35
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp2
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp4
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp4
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp30
-rw-r--r--src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp10
-rw-r--r--src/plugins/qmldesigner/components/toolbar/toolbarbackend.h4
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp73
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp4
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp12
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h6
-rw-r--r--src/plugins/qmldesigner/designercore/include/abstractview.h16
-rw-r--r--src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h2
-rw-r--r--src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h2
-rw-r--r--src/plugins/qmldesigner/designercore/include/model.h49
-rw-r--r--src/plugins/qmldesigner/designercore/include/nodemetainfo.h22
-rw-r--r--src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h9
-rw-r--r--src/plugins/qmldesigner/designercore/include/qmlitemnode.h1
-rw-r--r--src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp3
-rw-r--r--src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp68
-rw-r--r--src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp5
-rw-r--r--src/plugins/qmldesigner/designercore/model/abstractview.cpp173
-rw-r--r--src/plugins/qmldesigner/designercore/model/bindingproperty.cpp3
-rw-r--r--src/plugins/qmldesigner/designercore/model/internalbindingproperty.cpp3
-rw-r--r--src/plugins/qmldesigner/designercore/model/internalnode_p.h16
-rw-r--r--src/plugins/qmldesigner/designercore/model/internalnodelistproperty.cpp10
-rw-r--r--src/plugins/qmldesigner/designercore/model/internalnodeproperty.cpp8
-rw-r--r--src/plugins/qmldesigner/designercore/model/internalproperty.cpp51
-rw-r--r--src/plugins/qmldesigner/designercore/model/internalproperty.h4
-rw-r--r--src/plugins/qmldesigner/designercore/model/internalsignalhandlerproperty.cpp4
-rw-r--r--src/plugins/qmldesigner/designercore/model/internalvariantproperty.cpp2
-rw-r--r--src/plugins/qmldesigner/designercore/model/model.cpp143
-rw-r--r--src/plugins/qmldesigner/designercore/model/model_p.h8
-rw-r--r--src/plugins/qmldesigner/designercore/model/modelmerger.cpp8
-rw-r--r--src/plugins/qmldesigner/designercore/model/modelutils.cpp2
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmlconnections.cpp7
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp27
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp2
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmlstate.cpp8
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp20
-rw-r--r--src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp19
-rw-r--r--src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp117
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h4
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/filestatus.h12
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp17
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h1473
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp114
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h27
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h164
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h43
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h435
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp380
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h12
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp18
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp53
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h6
-rw-r--r--src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp69
-rw-r--r--src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h34
-rw-r--r--src/plugins/qmldesigner/documentwarningwidget.cpp5
-rw-r--r--src/plugins/qmldesigner/dynamiclicensecheck.h68
-rw-r--r--src/plugins/qmldesigner/generateresource.cpp485
-rw-r--r--src/plugins/qmldesigner/generateresource.h18
-rw-r--r--src/plugins/qmldesigner/puppetenvironmentbuilder.cpp2
-rw-r--r--src/plugins/qmldesigner/qmldesignerconstants.h323
-rw-r--r--src/plugins/qmldesigner/qmldesignerplugin.cpp20
-rw-r--r--src/plugins/qmldesigner/qmldesignerprojectmanager.cpp73
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-16px.pngbin0 -> 311 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-24px.pngbin0 -> 384 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-24px@2x.pngbin0 -> 674 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc3
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/quick.metainfo4
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/source/listview.qml40
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/source/listviewv2.qml40
-rw-r--r--src/plugins/qmldesigner/settingspage.cpp4
-rw-r--r--src/plugins/qmldesigner/shortcutmanager.cpp20
-rw-r--r--src/plugins/qmldesigner/shortcutmanager.h1
-rw-r--r--src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp12
-rw-r--r--src/plugins/qmldesignerbase/qmldesignerbaseplugin.h6
-rw-r--r--src/plugins/qmldesignerbase/studio/studioquickwidget.h4
-rw-r--r--src/plugins/qmldesignerlite/CMakeLists.txt20
-rw-r--r--src/plugins/qmldesignerlite/QmlDesignerLite.json.in21
-rw-r--r--src/plugins/qmldesignerlite/qmldesignerliteplugin.cpp22
-rw-r--r--src/plugins/qmldesignerlite/qmldesignerliteplugin.h23
-rw-r--r--src/plugins/qmlpreview/CMakeLists.txt2
-rw-r--r--src/plugins/qmlpreview/qmlpreviewruncontrol.cpp2
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp2
-rw-r--r--src/plugins/qmlprojectmanager/CMakeLists.txt3
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp4
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp12
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h3
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp21
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h6
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc3
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp569
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h103
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp12
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/gencmakeheadercomponents.tpl19
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/gencmakemodule.tpl14
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/gencmakeroot.tpl52
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp17
-rw-r--r--src/plugins/qmlprojectmanager/qmlproject.cpp2
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectplugin.cpp2
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp2
-rw-r--r--src/plugins/qnx/qnxanalyzesupport.cpp2
-rw-r--r--src/plugins/qnx/qnxdebugsupport.cpp2
-rw-r--r--src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp2
-rw-r--r--src/plugins/qnx/qnxdevice.cpp2
-rw-r--r--src/plugins/qnx/qnxdevicetester.cpp2
-rw-r--r--src/plugins/qnx/qnxutils.cpp2
-rw-r--r--src/plugins/qnx/slog2inforunner.cpp2
-rw-r--r--src/plugins/qtapplicationmanager/appmanagerinstallpackagestep.cpp2
-rw-r--r--src/plugins/qtapplicationmanager/appmanagerruncontrol.cpp2
-rw-r--r--src/plugins/qtsupport/baseqtversion.cpp2
-rw-r--r--src/plugins/qtsupport/externaleditors.cpp2
-rw-r--r--src/plugins/qtsupport/qtsupportplugin.cpp2
-rw-r--r--src/plugins/qtsupport/qtversionmanager.cpp2
-rw-r--r--src/plugins/qtsupport/uicgenerator.cpp2
-rw-r--r--src/plugins/remotelinux/customcommanddeploystep.cpp2
-rw-r--r--src/plugins/remotelinux/filesystemaccess_test.cpp2
-rw-r--r--src/plugins/remotelinux/genericdeploystep.cpp2
-rw-r--r--src/plugins/remotelinux/genericdirectuploadstep.cpp2
-rw-r--r--src/plugins/remotelinux/linuxdevice.cpp2
-rw-r--r--src/plugins/remotelinux/linuxdevicetester.cpp4
-rw-r--r--src/plugins/remotelinux/makeinstallstep.cpp2
-rw-r--r--src/plugins/remotelinux/publickeydeploymentdialog.cpp2
-rw-r--r--src/plugins/remotelinux/remotelinuxsignaloperation.cpp2
-rw-r--r--src/plugins/remotelinux/sshkeycreationdialog.cpp2
-rw-r--r--src/plugins/remotelinux/tarpackagedeploystep.cpp2
-rw-r--r--src/plugins/screenrecorder/cropandtrim.cpp2
-rw-r--r--src/plugins/screenrecorder/export.cpp2
-rw-r--r--src/plugins/screenrecorder/ffmpegutils.cpp2
-rw-r--r--src/plugins/screenrecorder/record.cpp2
-rw-r--r--src/plugins/silversearcher/findinfilessilversearcher.cpp2
-rw-r--r--src/plugins/squish/objectsmapdocument.cpp2
-rw-r--r--src/plugins/squish/squishprocessbase.h2
-rw-r--r--src/plugins/squish/squishtools.h2
-rw-r--r--src/plugins/subversion/subversionclient.cpp2
-rw-r--r--src/plugins/terminal/shellintegration.h2
-rw-r--r--src/plugins/terminal/terminalwidget.h2
-rw-r--r--src/plugins/texteditor/basefilefind.cpp2
-rw-r--r--src/plugins/texteditor/formattexteditor.cpp2
-rw-r--r--src/plugins/updateinfo/updateinfoplugin.cpp2
-rw-r--r--src/plugins/valgrind/callgrindengine.cpp2
-rw-r--r--src/plugins/valgrind/callgrindengine.h2
-rw-r--r--src/plugins/valgrind/callgrindtool.cpp2
-rw-r--r--src/plugins/valgrind/memchecktool.cpp2
-rw-r--r--src/plugins/valgrind/valgrindmemcheckparsertest.cpp2
-rw-r--r--src/plugins/valgrind/valgrindprocess.cpp2
-rw-r--r--src/plugins/vcsbase/vcsbaseclient.cpp2
-rw-r--r--src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp2
-rw-r--r--src/plugins/vcsbase/vcsbaseplugin.cpp2
-rw-r--r--src/plugins/vcsbase/vcsbasesubmiteditor.cpp2
-rw-r--r--src/plugins/vcsbase/vcscommand.cpp2
-rw-r--r--src/plugins/vcsbase/vcsoutputwindow.cpp2
-rw-r--r--src/plugins/webassembly/webassemblyemsdk.cpp2
-rw-r--r--src/plugins/webassembly/webassemblyrunconfiguration.cpp2
-rw-r--r--src/tools/qml2puppet/CMakeLists.txt9
-rw-r--r--src/tools/qml2puppet/editor3d_qt6.qrc1
-rw-r--r--src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml110
-rw-r--r--src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml24
-rw-r--r--src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml24
-rw-r--r--src/tools/qml2puppet/mockfiles/qt6/ReflectionProbeBox.qml39
-rw-r--r--src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml10
-rw-r--r--src/tools/qml2puppet/mockfiles/qt6/SelectionBox.qml7
-rw-r--r--src/tools/qml2puppet/qml2puppet/editor3d/boxgeometry.cpp103
-rw-r--r--src/tools/qml2puppet/qml2puppet/editor3d/boxgeometry.h42
-rw-r--r--src/tools/qml2puppet/qml2puppet/editor3d/editor3d.pri19
-rw-r--r--src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp120
-rw-r--r--src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h26
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp8
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp98
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h5
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp35
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.h3
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp6
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp11
-rw-r--r--src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp499
-rw-r--r--src/tools/sdktool/CMakeLists.txt2
-rw-r--r--src/tools/wininterrupt/CMakeLists.txt2
520 files changed, 21672 insertions, 10833 deletions
diff --git a/src/libs/3rdparty/sqlite/carray.c b/src/libs/3rdparty/sqlite/carray.c
index 709c894f27..b1caa98c3f 100644
--- a/src/libs/3rdparty/sqlite/carray.c
+++ b/src/libs/3rdparty/sqlite/carray.c
@@ -409,6 +409,11 @@ static sqlite3_module carrayModule = {
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0, /* xRollbackTo */
+ 0, /* xShadow */
+ 0 /* xIntegrity */
};
/*
diff --git a/src/libs/3rdparty/sqlite/sqlite3.c b/src/libs/3rdparty/sqlite/sqlite3.c
index 1884b08239..139ee46a6a 100644
--- a/src/libs/3rdparty/sqlite/sqlite3.c
+++ b/src/libs/3rdparty/sqlite/sqlite3.c
@@ -1,6 +1,6 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
-** version 3.43.1. By combining all the individual C code files into this
+** version 3.45.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
@@ -18,7 +18,7 @@
** separate file. This file contains only code for the core SQLite library.
**
** The content in this amalgamation comes from Fossil check-in
-** d3a40c05c49e1a49264912b1a05bc2143ac.
+** e876e51a0ed5c5b3126f52e532044363a014.
*/
#define SQLITE_CORE 1
#define SQLITE_AMALGAMATION 1
@@ -459,9 +459,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.43.1"
-#define SQLITE_VERSION_NUMBER 3043001
-#define SQLITE_SOURCE_ID "2023-09-11 12:01:27 2d3a40c05c49e1a49264912b1a05bc2143ac0e7c3df588276ce80a4cbc9bd1b0"
+#define SQLITE_VERSION "3.45.1"
+#define SQLITE_VERSION_NUMBER 3045001
+#define SQLITE_SOURCE_ID "2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -2440,7 +2440,7 @@ struct sqlite3_mem_methods {
** is stored in each sorted record and the required column values loaded
** from the database as records are returned in sorted order. The default
** value for this option is to never use this optimization. Specifying a
-** negative value for this option restores the default behaviour.
+** negative value for this option restores the default behavior.
** This option is only available if SQLite is compiled with the
** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option.
**
@@ -2615,7 +2615,7 @@ struct sqlite3_mem_methods {
** database handle, SQLite checks if this will mean that there are now no
** connections at all to the database. If so, it performs a checkpoint
** operation before closing the connection. This option may be used to
-** override this behaviour. The first parameter passed to this operation
+** override this behavior. The first parameter passed to this operation
** is an integer - positive to disable checkpoints-on-close, or zero (the
** default) to enable them, and negative to leave the setting unchanged.
** The second parameter is a pointer to an integer
@@ -4267,14 +4267,17 @@ SQLITE_API void sqlite3_free_filename(sqlite3_filename);
** </ul>
**
** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
-** text that describes the error, as either UTF-8 or UTF-16 respectively.
+** text that describes the error, as either UTF-8 or UTF-16 respectively,
+** or NULL if no error message is available.
+** (See how SQLite handles [invalid UTF] for exceptions to this rule.)
** ^(Memory to hold the error message string is managed internally.
** The application does not need to worry about freeing the result.
** However, the error string might be overwritten or deallocated by
** subsequent calls to other SQLite interface functions.)^
**
-** ^The sqlite3_errstr() interface returns the English-language text
-** that describes the [result code], as UTF-8.
+** ^The sqlite3_errstr(E) interface returns the English-language text
+** that describes the [result code] E, as UTF-8, or NULL if E is not an
+** result code for which a text error message is available.
** ^(Memory to hold the error message string is managed internally
** and must not be freed by the application)^.
**
@@ -5638,6 +5641,7 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt);
*/
SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
+
/*
** CAPI3REF: Create Or Redefine SQL Functions
** KEYWORDS: {function creation routines}
@@ -5884,13 +5888,27 @@ SQLITE_API int sqlite3_create_window_function(
** </dd>
**
** [[SQLITE_SUBTYPE]] <dt>SQLITE_SUBTYPE</dt><dd>
-** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call
+** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call
** [sqlite3_value_subtype()] to inspect the sub-types of its arguments.
-** Specifying this flag makes no difference for scalar or aggregate user
-** functions. However, if it is not specified for a user-defined window
-** function, then any sub-types belonging to arguments passed to the window
-** function may be discarded before the window function is called (i.e.
-** sqlite3_value_subtype() will always return 0).
+** This flag instructs SQLite to omit some corner-case optimizations that
+** might disrupt the operation of the [sqlite3_value_subtype()] function,
+** causing it to return zero rather than the correct subtype().
+** SQL functions that invokes [sqlite3_value_subtype()] should have this
+** property. If the SQLITE_SUBTYPE property is omitted, then the return
+** value from [sqlite3_value_subtype()] might sometimes be zero even though
+** a non-zero subtype was specified by the function argument expression.
+**
+** [[SQLITE_RESULT_SUBTYPE]] <dt>SQLITE_RESULT_SUBTYPE</dt><dd>
+** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call
+** [sqlite3_result_subtype()] to cause a sub-type to be associated with its
+** result.
+** Every function that invokes [sqlite3_result_subtype()] should have this
+** property. If it does not, then the call to [sqlite3_result_subtype()]
+** might become a no-op if the function is used as term in an
+** [expression index]. On the other hand, SQL functions that never invoke
+** [sqlite3_result_subtype()] should avoid setting this property, as the
+** purpose of this property is to disable certain optimizations that are
+** incompatible with subtypes.
** </dd>
** </dl>
*/
@@ -5898,6 +5916,7 @@ SQLITE_API int sqlite3_create_window_function(
#define SQLITE_DIRECTONLY 0x000080000
#define SQLITE_SUBTYPE 0x000100000
#define SQLITE_INNOCUOUS 0x000200000
+#define SQLITE_RESULT_SUBTYPE 0x001000000
/*
** CAPI3REF: Deprecated Functions
@@ -6094,6 +6113,12 @@ SQLITE_API int sqlite3_value_encoding(sqlite3_value*);
** information can be used to pass a limited amount of context from
** one SQL function to another. Use the [sqlite3_result_subtype()]
** routine to set the subtype for the return value of an SQL function.
+**
+** Every [application-defined SQL function] that invoke this interface
+** should include the [SQLITE_SUBTYPE] property in the text
+** encoding argument when the function is [sqlite3_create_function|registered].
+** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype()
+** might return zero instead of the upstream subtype in some corner cases.
*/
SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*);
@@ -6192,48 +6217,56 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
** METHOD: sqlite3_context
**
** These functions may be used by (non-aggregate) SQL functions to
-** associate metadata with argument values. If the same value is passed to
-** multiple invocations of the same SQL function during query execution, under
-** some circumstances the associated metadata may be preserved. An example
-** of where this might be useful is in a regular-expression matching
-** function. The compiled version of the regular expression can be stored as
-** metadata associated with the pattern string.
+** associate auxiliary data with argument values. If the same argument
+** value is passed to multiple invocations of the same SQL function during
+** query execution, under some circumstances the associated auxiliary data
+** might be preserved. An example of where this might be useful is in a
+** regular-expression matching function. The compiled version of the regular
+** expression can be stored as auxiliary data associated with the pattern string.
** Then as long as the pattern string remains the same,
** the compiled regular expression can be reused on multiple
** invocations of the same function.
**
-** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the metadata
+** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary data
** associated by the sqlite3_set_auxdata(C,N,P,X) function with the Nth argument
** value to the application-defined function. ^N is zero for the left-most
-** function argument. ^If there is no metadata
+** function argument. ^If there is no auxiliary data
** associated with the function argument, the sqlite3_get_auxdata(C,N) interface
** returns a NULL pointer.
**
-** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th
-** argument of the application-defined function. ^Subsequent
+** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as auxiliary data for the
+** N-th argument of the application-defined function. ^Subsequent
** calls to sqlite3_get_auxdata(C,N) return P from the most recent
-** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or
-** NULL if the metadata has been discarded.
+** sqlite3_set_auxdata(C,N,P,X) call if the auxiliary data is still valid or
+** NULL if the auxiliary data has been discarded.
** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL,
** SQLite will invoke the destructor function X with parameter P exactly
-** once, when the metadata is discarded.
-** SQLite is free to discard the metadata at any time, including: <ul>
+** once, when the auxiliary data is discarded.
+** SQLite is free to discard the auxiliary data 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>
+** allocation error occurs.)^
+** <li> ^(during the original sqlite3_set_auxdata() call if the function
+** is evaluated during query planning instead of during query execution,
+** as sometimes happens with [SQLITE_ENABLE_STAT4].)^ </ul>
**
-** Note the last bullet in particular. The destructor X in
+** Note the last two bullets in particular. The destructor X in
** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the
** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata()
** should be called near the end of the function implementation and the
** function implementation should not make any use of P after
-** sqlite3_set_auxdata() has been called.
-**
-** ^(In practice, metadata is preserved between function calls for
+** sqlite3_set_auxdata() has been called. Furthermore, a call to
+** sqlite3_get_auxdata() that occurs immediately after a corresponding call
+** to sqlite3_set_auxdata() might still return NULL if an out-of-memory
+** condition occurred during the sqlite3_set_auxdata() call or if the
+** function is being evaluated during query planning rather than during
+** query execution.
+**
+** ^(In practice, auxiliary data is preserved between function calls for
** function parameters that are compile-time constants, including literal
** values and [parameters] and expressions composed from the same.)^
**
@@ -6243,10 +6276,67 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
**
** These routines must be called from the same thread in which
** the SQL function is running.
+**
+** See also: [sqlite3_get_clientdata()] and [sqlite3_set_clientdata()].
*/
SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N);
SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*));
+/*
+** CAPI3REF: Database Connection Client Data
+** METHOD: sqlite3
+**
+** These functions are used to associate one or more named pointers
+** with a [database connection].
+** A call to sqlite3_set_clientdata(D,N,P,X) causes the pointer P
+** to be attached to [database connection] D using name N. Subsequent
+** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P
+** or a NULL pointer if there were no prior calls to
+** sqlite3_set_clientdata() with the same values of D and N.
+** Names are compared using strcmp() and are thus case sensitive.
+**
+** If P and X are both non-NULL, then the destructor X is invoked with
+** argument P on the first of the following occurrences:
+** <ul>
+** <li> An out-of-memory error occurs during the call to
+** sqlite3_set_clientdata() which attempts to register pointer P.
+** <li> A subsequent call to sqlite3_set_clientdata(D,N,P,X) is made
+** with the same D and N parameters.
+** <li> The database connection closes. SQLite does not make any guarantees
+** about the order in which destructors are called, only that all
+** destructors will be called exactly once at some point during the
+** database connection closing process.
+** </ul>
+**
+** SQLite does not do anything with client data other than invoke
+** destructors on the client data at the appropriate time. The intended
+** use for client data is to provide a mechanism for wrapper libraries
+** to store additional information about an SQLite database connection.
+**
+** There is no limit (other than available memory) on the number of different
+** client data pointers (with different names) that can be attached to a
+** single database connection. However, the implementation is optimized
+** for the case of having only one or two different client data names.
+** Applications and wrapper libraries are discouraged from using more than
+** one client data name each.
+**
+** There is no way to enumerate the client data pointers
+** associated with a database connection. The N parameter can be thought
+** of as a secret key such that only code that knows the secret key is able
+** to access the associated data.
+**
+** Security Warning: These interfaces should not be exposed in scripting
+** languages or in other circumstances where it might be possible for an
+** an attacker to invoke them. Any agent that can invoke these interfaces
+** can probably also take control of the process.
+**
+** Database connection client data is only available for SQLite
+** version 3.44.0 ([dateof:3.44.0]) and later.
+**
+** See also: [sqlite3_set_auxdata()] and [sqlite3_get_auxdata()].
+*/
+SQLITE_API void *sqlite3_get_clientdata(sqlite3*,const char*);
+SQLITE_API int sqlite3_set_clientdata(sqlite3*, const char*, void*, void(*)(void*));
/*
** CAPI3REF: Constants Defining Special Destructor Behavior
@@ -6448,6 +6538,20 @@ SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n);
** higher order bits are discarded.
** The number of subtype bytes preserved by SQLite might increase
** in future releases of SQLite.
+**
+** Every [application-defined SQL function] that invokes this interface
+** should include the [SQLITE_RESULT_SUBTYPE] property in its
+** text encoding argument when the SQL function is
+** [sqlite3_create_function|registered]. If the [SQLITE_RESULT_SUBTYPE]
+** property is omitted from the function that invokes sqlite3_result_subtype(),
+** then in some cases the sqlite3_result_subtype() might fail to set
+** the result subtype.
+**
+** If SQLite is compiled with -DSQLITE_STRICT_SUBTYPE=1, then any
+** SQL function that invokes the sqlite3_result_subtype() interface
+** and that does not have the SQLITE_RESULT_SUBTYPE property will raise
+** an error. Future versions of SQLite might enable -DSQLITE_STRICT_SUBTYPE=1
+** by default.
*/
SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int);
@@ -6879,7 +6983,7 @@ SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName);
SQLITE_API int sqlite3_txn_state(sqlite3*,const char *zSchema);
/*
-** CAPI3REF: Allowed return values from [sqlite3_txn_state()]
+** CAPI3REF: Allowed return values from sqlite3_txn_state()
** KEYWORDS: {transaction state}
**
** These constants define the current transaction state of a database file.
@@ -7011,7 +7115,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
** ^Each call to the sqlite3_autovacuum_pages() interface overrides all
** previous invocations for that database connection. ^If the callback
** argument (C) to sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer,
-** then the autovacuum steps callback is cancelled. The return value
+** then the autovacuum steps callback is canceled. The return value
** from sqlite3_autovacuum_pages() is normally SQLITE_OK, but might
** be some other error code if something goes wrong. The current
** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other
@@ -7530,6 +7634,10 @@ struct sqlite3_module {
/* The methods above are in versions 1 and 2 of the sqlite_module object.
** Those below are for version 3 and greater. */
int (*xShadowName)(const char*);
+ /* The methods above are in versions 1 through 3 of the sqlite_module object.
+ ** Those below are for version 4 and greater. */
+ int (*xIntegrity)(sqlite3_vtab *pVTab, const char *zSchema,
+ const char *zTabName, int mFlags, char **pzErr);
};
/*
@@ -8017,7 +8125,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
** code is returned and the transaction rolled back.
**
** Calling this function with an argument that is not a NULL pointer or an
-** open blob handle results in undefined behaviour. ^Calling this routine
+** open blob handle results in undefined behavior. ^Calling this routine
** with a null pointer (such as would be returned by a failed call to
** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function
** is passed a valid open blob handle, the values returned by the
@@ -8244,9 +8352,11 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
**
** ^(Some systems (for example, Windows 95) do not support the operation
** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try()
-** will always return SQLITE_BUSY. The SQLite core only ever uses
-** sqlite3_mutex_try() as an optimization so this is acceptable
-** behavior.)^
+** will always return SQLITE_BUSY. In most cases the SQLite core only uses
+** sqlite3_mutex_try() as an optimization, so this is acceptable
+** behavior. The exceptions are unix builds that set the
+** SQLITE_ENABLE_SETLK_TIMEOUT build option. In that case a working
+** sqlite3_mutex_try() is required.)^
**
** ^The sqlite3_mutex_leave() routine exits a mutex that was
** previously entered by the same thread. The behavior
@@ -8497,6 +8607,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_PRNG_SAVE 5
#define SQLITE_TESTCTRL_PRNG_RESTORE 6
#define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
+#define SQLITE_TESTCTRL_FK_NO_ACTION 7
#define SQLITE_TESTCTRL_BITVEC_TEST 8
#define SQLITE_TESTCTRL_FAULT_INSTALL 9
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
@@ -8504,6 +8615,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_ASSERT 12
#define SQLITE_TESTCTRL_ALWAYS 13
#define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */
+#define SQLITE_TESTCTRL_JSON_SELFCHECK 14
#define SQLITE_TESTCTRL_OPTIMIZATIONS 15
#define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */
@@ -9558,8 +9670,8 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p);
** blocked connection already has a registered unlock-notify callback,
** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is
** called with a NULL pointer as its second argument, then any existing
-** unlock-notify callback is cancelled. ^The blocked connections
-** unlock-notify callback may also be cancelled by closing the blocked
+** unlock-notify callback is canceled. ^The blocked connections
+** unlock-notify callback may also be canceled by closing the blocked
** connection using [sqlite3_close()].
**
** The unlock-notify callback is not reentrant. If an application invokes
@@ -10862,6 +10974,13 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c
** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy
** of the database exists.
**
+** After the call, if the SQLITE_SERIALIZE_NOCOPY bit had been set,
+** the returned buffer content will remain accessible and unchanged
+** until either the next write operation on the connection or when
+** the connection is closed, and applications must not modify the
+** buffer. If the bit had been clear, the returned buffer will not
+** be accessed by SQLite after the call.
+**
** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the
** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory
** allocation error occurs.
@@ -10910,6 +11029,9 @@ SQLITE_API unsigned char *sqlite3_serialize(
** SQLite will try to increase the buffer size using sqlite3_realloc64()
** if writes on the database cause it to grow larger than M bytes.
**
+** Applications must not modify the buffer P or invalidate it before
+** the database connection D is closed.
+**
** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the
** database is currently in a read transaction or is involved in a backup
** operation.
@@ -10918,6 +11040,13 @@ SQLITE_API unsigned char *sqlite3_serialize(
** S argument to sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the
** function returns SQLITE_ERROR.
**
+** The deserialized database should not be in [WAL mode]. If the database
+** is in WAL mode, then any attempt to use the database file will result
+** in an [SQLITE_CANTOPEN] error. The application can set the
+** [file format version numbers] (bytes 18 and 19) of the input database P
+** to 0x01 prior to invoking sqlite3_deserialize(D,S,P,N,M,F) to force the
+** database file into rollback mode and work around this limitation.
+**
** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the
** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then
** [sqlite3_free()] is invoked on argument P prior to returning.
@@ -11991,6 +12120,18 @@ SQLITE_API int sqlite3changeset_concat(
/*
+** CAPI3REF: Upgrade the Schema of a Changeset/Patchset
+*/
+SQLITE_API int sqlite3changeset_upgrade(
+ sqlite3 *db,
+ const char *zDb,
+ int nIn, const void *pIn, /* Input changeset */
+ int *pnOut, void **ppOut /* OUT: Inverse of input */
+);
+
+
+
+/*
** CAPI3REF: Changegroup Handle
**
** A changegroup is an object used to combine two or more
@@ -12037,6 +12178,38 @@ typedef struct sqlite3_changegroup sqlite3_changegroup;
SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp);
/*
+** CAPI3REF: Add a Schema to a Changegroup
+** METHOD: sqlite3_changegroup_schema
+**
+** This method may be used to optionally enforce the rule that the changesets
+** added to the changegroup handle must match the schema of database zDb
+** ("main", "temp", or the name of an attached database). If
+** sqlite3changegroup_add() is called to add a changeset that is not compatible
+** with the configured schema, SQLITE_SCHEMA is returned and the changegroup
+** object is left in an undefined state.
+**
+** A changeset schema is considered compatible with the database schema in
+** the same way as for sqlite3changeset_apply(). Specifically, for each
+** table in the changeset, there exists a database table with:
+**
+** <ul>
+** <li> The name identified by the changeset, and
+** <li> at least as many columns as recorded in the changeset, and
+** <li> the primary key columns in the same position as recorded in
+** the changeset.
+** </ul>
+**
+** The output of the changegroup object always has the same schema as the
+** database nominated using this function. In cases where changesets passed
+** to sqlite3changegroup_add() have fewer columns than the corresponding table
+** in the database schema, these are filled in using the default column
+** values from the database schema. This makes it possible to combined
+** changesets that have different numbers of columns for a single table
+** within a changegroup, provided that they are otherwise compatible.
+*/
+SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb);
+
+/*
** CAPI3REF: Add A Changeset To A Changegroup
** METHOD: sqlite3_changegroup
**
@@ -12104,13 +12277,18 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp);
** If the new changeset contains changes to a table that is already present
** in the changegroup, then the number of columns and the position of the
** primary key columns for the table must be consistent. If this is not the
-** case, this function fails with SQLITE_SCHEMA. If the input changeset
-** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is
-** returned. Or, if an out-of-memory condition occurs during processing, this
-** function returns SQLITE_NOMEM. In all cases, if an error occurs the state
-** of the final contents of the changegroup is undefined.
+** case, this function fails with SQLITE_SCHEMA. Except, if the changegroup
+** object has been configured with a database schema using the
+** sqlite3changegroup_schema() API, then it is possible to combine changesets
+** with different numbers of columns for a single table, provided that
+** they are otherwise compatible.
+**
+** If the input changeset appears to be corrupt and the corruption is
+** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition
+** occurs during processing, this function returns SQLITE_NOMEM.
**
-** If no error occurs, SQLITE_OK is returned.
+** In all cases, if an error occurs the state of the final contents of the
+** changegroup is undefined. If no error occurs, SQLITE_OK is returned.
*/
SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
@@ -12375,10 +12553,17 @@ SQLITE_API int sqlite3changeset_apply_v2(
** <li>an insert change if all fields of the conflicting row match
** the row being inserted.
** </ul>
+**
+** <dt>SQLITE_CHANGESETAPPLY_FKNOACTION <dd>
+** If this flag it set, then all foreign key constraints in the target
+** database behave as if they were declared with "ON UPDATE NO ACTION ON
+** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL
+** or SET DEFAULT.
*/
#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001
#define SQLITE_CHANGESETAPPLY_INVERT 0x0002
#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004
+#define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008
/*
** CAPI3REF: Constants Passed To The Conflict Handler
@@ -12944,8 +13129,11 @@ struct Fts5PhraseIter {
** created with the "columnsize=0" option.
**
** xColumnText:
-** This function attempts to retrieve the text of column iCol of the
-** current document. If successful, (*pz) is set to point to a buffer
+** If parameter iCol is less than zero, or greater than or equal to the
+** number of columns in the table, SQLITE_RANGE is returned.
+**
+** Otherwise, this function attempts to retrieve the text of column iCol of
+** the current document. If successful, (*pz) is set to point to a buffer
** containing the text in utf-8 encoding, (*pn) is set to the size in bytes
** (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
** if an error occurs, an SQLite error code is returned and the final values
@@ -12955,8 +13143,10 @@ struct Fts5PhraseIter {
** Returns the number of phrases in the current query expression.
**
** xPhraseSize:
-** Returns the number of tokens in phrase iPhrase of the query. Phrases
-** are numbered starting from zero.
+** If parameter iCol is less than zero, or greater than or equal to the
+** number of phrases in the current query, as returned by xPhraseCount,
+** 0 is returned. Otherwise, this function returns the number of tokens in
+** phrase iPhrase of the query. Phrases are numbered starting from zero.
**
** xInstCount:
** Set *pnInst to the total number of occurrences of all phrases within
@@ -12972,12 +13162,13 @@ struct Fts5PhraseIter {
** Query for the details of phrase match iIdx within the current row.
** Phrase matches are numbered starting from zero, so the iIdx argument
** should be greater than or equal to zero and smaller than the value
-** output by xInstCount().
+** output by xInstCount(). If iIdx is less than zero or greater than
+** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned.
**
-** Usually, output parameter *piPhrase is set to the phrase number, *piCol
+** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol
** to the column in which it occurs and *piOff the token offset of the
-** first token of the phrase. Returns SQLITE_OK if successful, or an error
-** code (i.e. SQLITE_NOMEM) if an error occurs.
+** first token of the phrase. SQLITE_OK is returned if successful, or an
+** error code (i.e. SQLITE_NOMEM) if an error occurs.
**
** This API can be quite slow if used with an FTS5 table created with the
** "detail=none" or "detail=column" option.
@@ -13003,6 +13194,10 @@ struct Fts5PhraseIter {
** Invoking Api.xUserData() returns a copy of the pointer passed as
** the third argument to pUserData.
**
+** If parameter iPhrase is less than zero, or greater than or equal to
+** the number of phrases in the query, as returned by xPhraseCount(),
+** this function returns SQLITE_RANGE.
+**
** If the callback function returns any value other than SQLITE_OK, the
** query is abandoned and the xQueryPhrase function returns immediately.
** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
@@ -13117,9 +13312,42 @@ struct Fts5PhraseIter {
**
** xPhraseNextColumn()
** See xPhraseFirstColumn above.
+**
+** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken)
+** This is used to access token iToken of phrase iPhrase of the current
+** query. Before returning, output parameter *ppToken is set to point
+** to a buffer containing the requested token, and *pnToken to the
+** size of this buffer in bytes.
+**
+** If iPhrase or iToken are less than zero, or if iPhrase is greater than
+** or equal to the number of phrases in the query as reported by
+** xPhraseCount(), or if iToken is equal to or greater than the number of
+** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken
+ are both zeroed.
+**
+** The output text is not a copy of the query text that specified the
+** token. It is the output of the tokenizer module. For tokendata=1
+** tables, this includes any embedded 0x00 and trailing data.
+**
+** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken)
+** This is used to access token iToken of phrase hit iIdx within the
+** current row. If iIdx is less than zero or greater than or equal to the
+** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
+** output variable (*ppToken) is set to point to a buffer containing the
+** matching document token, and (*pnToken) to the size of that buffer in
+** bytes. This API is not available if the specified token matches a
+** prefix query term. In that case both output variables are always set
+** to 0.
+**
+** The output text is not a copy of the document text that was tokenized.
+** It is the output of the tokenizer module. For tokendata=1 tables, this
+** includes any embedded 0x00 and trailing data.
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option.
*/
struct Fts5ExtensionApi {
- int iVersion; /* Currently always set to 2 */
+ int iVersion; /* Currently always set to 3 */
void *(*xUserData)(Fts5Context*);
@@ -13154,6 +13382,13 @@ struct Fts5ExtensionApi {
int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
+
+ /* Below this point are iVersion>=3 only */
+ int (*xQueryToken)(Fts5Context*,
+ int iPhrase, int iToken,
+ const char **ppToken, int *pnToken
+ );
+ int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*);
};
/*
@@ -13640,7 +13875,7 @@ struct fts5_api {
** max_page_count macro.
*/
#ifndef SQLITE_MAX_PAGE_COUNT
-# define SQLITE_MAX_PAGE_COUNT 1073741823
+# define SQLITE_MAX_PAGE_COUNT 0xfffffffe /* 4294967294 */
#endif
/*
@@ -13770,6 +14005,29 @@ struct fts5_api {
#endif
/*
+** Enable SQLITE_USE_SEH by default on MSVC builds. Only omit
+** SEH support if the -DSQLITE_OMIT_SEH option is given.
+*/
+#if defined(_MSC_VER) && !defined(SQLITE_OMIT_SEH)
+# define SQLITE_USE_SEH 1
+#else
+# undef SQLITE_USE_SEH
+#endif
+
+/*
+** Enable SQLITE_DIRECT_OVERFLOW_READ, unless the build explicitly
+** disables it using -DSQLITE_DIRECT_OVERFLOW_READ=0
+*/
+#if defined(SQLITE_DIRECT_OVERFLOW_READ) && SQLITE_DIRECT_OVERFLOW_READ+1==1
+ /* Disable if -DSQLITE_DIRECT_OVERFLOW_READ=0 */
+# undef SQLITE_DIRECT_OVERFLOW_READ
+#else
+ /* In all other cases, enable */
+# define SQLITE_DIRECT_OVERFLOW_READ 1
+#endif
+
+
+/*
** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2.
** 0 means mutexes are permanently disable and the library is never
** threadsafe. 1 means the library is serialized which is the highest
@@ -14662,16 +14920,33 @@ typedef INT16_TYPE LogEst;
** using C-preprocessor macros. If that is unsuccessful, or if
** -DSQLITE_BYTEORDER=0 is set, then byte-order is determined
** at run-time.
+**
+** If you are building SQLite on some obscure platform for which the
+** following ifdef magic does not work, you can always include either:
+**
+** -DSQLITE_BYTEORDER=1234
+**
+** or
+**
+** -DSQLITE_BYTEORDER=4321
+**
+** to cause the build to work for little-endian or big-endian processors,
+** respectively.
*/
-#ifndef SQLITE_BYTEORDER
-# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \
+#ifndef SQLITE_BYTEORDER /* Replicate changes at tag-20230904a */
+# if defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__
+# define SQLITE_BYTEORDER 4321
+# elif defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
+# define SQLITE_BYTEORDER 1234
+# elif defined(__BIG_ENDIAN__) && __BIG_ENDIAN__==1
+# define SQLITE_BYTEORDER 4321
+# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \
defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \
defined(__ARMEL__) || defined(__AARCH64EL__) || defined(_M_ARM64)
-# define SQLITE_BYTEORDER 1234
-# elif defined(sparc) || defined(__ppc__) || \
- defined(__ARMEB__) || defined(__AARCH64EB__)
-# define SQLITE_BYTEORDER 4321
+# define SQLITE_BYTEORDER 1234
+# elif defined(sparc) || defined(__ARMEB__) || defined(__AARCH64EB__)
+# define SQLITE_BYTEORDER 4321
# else
# define SQLITE_BYTEORDER 0
# endif
@@ -14995,6 +15270,7 @@ typedef struct Column Column;
typedef struct Cte Cte;
typedef struct CteUse CteUse;
typedef struct Db Db;
+typedef struct DbClientData DbClientData;
typedef struct DbFixer DbFixer;
typedef struct Schema Schema;
typedef struct Expr Expr;
@@ -15633,7 +15909,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager*);
SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*);
SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*);
SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*);
-SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *);
+SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, u64*);
SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*);
SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *);
@@ -16220,6 +16496,7 @@ typedef struct VdbeOpList VdbeOpList;
#define P4_INT64 (-13) /* P4 is a 64-bit signed integer */
#define P4_INTARRAY (-14) /* P4 is a vector of 32-bit integers */
#define P4_FUNCCTX (-15) /* P4 is a pointer to an sqlite3_context object */
+#define P4_TABLEREF (-16) /* Like P4_TABLE, but reference counted */
/* Error message codes for OP_Halt */
#define P5_ConstraintNotNull 1
@@ -16435,19 +16712,22 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_VCreate 171
#define OP_VDestroy 172
#define OP_VOpen 173
-#define OP_VInitIn 174 /* synopsis: r[P2]=ValueList(P1,P3) */
-#define OP_VColumn 175 /* synopsis: r[P3]=vcolumn(P2) */
-#define OP_VRename 176
-#define OP_Pagecount 177
-#define OP_MaxPgcnt 178
-#define OP_ClrSubtype 179 /* synopsis: r[P1].subtype = 0 */
-#define OP_FilterAdd 180 /* synopsis: filter(P1) += key(P3@P4) */
-#define OP_Trace 181
-#define OP_CursorHint 182
-#define OP_ReleaseReg 183 /* synopsis: release r[P1@P2] mask P3 */
-#define OP_Noop 184
-#define OP_Explain 185
-#define OP_Abortable 186
+#define OP_VCheck 174
+#define OP_VInitIn 175 /* synopsis: r[P2]=ValueList(P1,P3) */
+#define OP_VColumn 176 /* synopsis: r[P3]=vcolumn(P2) */
+#define OP_VRename 177
+#define OP_Pagecount 178
+#define OP_MaxPgcnt 179
+#define OP_ClrSubtype 180 /* synopsis: r[P1].subtype = 0 */
+#define OP_GetSubtype 181 /* synopsis: r[P2] = r[P1].subtype */
+#define OP_SetSubtype 182 /* synopsis: r[P2].subtype = r[P1] */
+#define OP_FilterAdd 183 /* synopsis: filter(P1) += key(P3@P4) */
+#define OP_Trace 184
+#define OP_CursorHint 185
+#define OP_ReleaseReg 186 /* synopsis: release r[P1@P2] mask P3 */
+#define OP_Noop 187
+#define OP_Explain 188
+#define OP_Abortable 189
/* Properties such as "out2" or "jump" that are specified in
** comments following the "case" for each opcode in the vdbe.c
@@ -16482,9 +16762,9 @@ typedef struct VdbeOpList VdbeOpList;
/* 144 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\
/* 152 */ 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\
/* 160 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x50, 0x40,\
-/* 176 */ 0x00, 0x10, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00,\
-/* 184 */ 0x00, 0x00, 0x00,}
+/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x50,\
+/* 176 */ 0x40, 0x00, 0x10, 0x10, 0x02, 0x12, 0x12, 0x00,\
+/* 184 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}
/* The resolve3P2Values() routine is able to run faster if it knows
** the value of the largest JUMP opcode. The smaller the maximum
@@ -17393,6 +17673,7 @@ struct sqlite3 {
i64 nDeferredCons; /* Net deferred constraints this transaction. */
i64 nDeferredImmCons; /* Net deferred immediate constraints */
int *pnBytesFreed; /* If not NULL, increment this in DbFree() */
+ DbClientData *pDbData; /* sqlite3_set_clientdata() content */
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
/* The following variables are all protected by the STATIC_MAIN
** mutex, not by sqlite3.mutex. They are used by code in notify.c.
@@ -17475,6 +17756,7 @@ struct sqlite3 {
/* the count using a callback. */
#define SQLITE_CorruptRdOnly HI(0x00002) /* Prohibit writes due to error */
#define SQLITE_ReadUncommit HI(0x00004) /* READ UNCOMMITTED in shared-cache */
+#define SQLITE_FkNoAction HI(0x00008) /* Treat all FK as NO ACTION */
/* Flags used only if debugging */
#ifdef SQLITE_DEBUG
@@ -17643,14 +17925,15 @@ struct FuncDestructor {
#define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a
** single query - might change over time */
#define SQLITE_FUNC_TEST 0x4000 /* Built-in testing functions */
-/* 0x8000 -- available for reuse */
+#define SQLITE_FUNC_RUNONLY 0x8000 /* Cannot be used by valueFromFunction */
#define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */
#define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */
#define SQLITE_FUNC_DIRECT 0x00080000 /* Not for use in TRIGGERs or VIEWs */
-#define SQLITE_FUNC_SUBTYPE 0x00100000 /* Result likely to have sub-type */
+/* SQLITE_SUBTYPE 0x00100000 // Consumer of subtypes */
#define SQLITE_FUNC_UNSAFE 0x00200000 /* Function has side effects */
#define SQLITE_FUNC_INLINE 0x00400000 /* Functions implemented in-line */
#define SQLITE_FUNC_BUILTIN 0x00800000 /* This is a built-in function */
+/* SQLITE_RESULT_SUBTYPE 0x01000000 // Generator of subtypes */
#define SQLITE_FUNC_ANYORDER 0x08000000 /* count/min/max aggregate */
/* Identifier numbers for each in-line function */
@@ -17742,10 +18025,11 @@ struct FuncDestructor {
#define MFUNCTION(zName, nArg, xPtr, xFunc) \
{nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \
xPtr, 0, xFunc, 0, 0, 0, #zName, {0} }
-#define JFUNCTION(zName, nArg, iArg, xFunc) \
- {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|\
- SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \
- SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
+#define JFUNCTION(zName, nArg, bUseCache, bWS, bRS, bJsonB, iArg, xFunc) \
+ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_FUNC_CONSTANT|\
+ SQLITE_UTF8|((bUseCache)*SQLITE_FUNC_RUNONLY)|\
+ ((bRS)*SQLITE_SUBTYPE)|((bWS)*SQLITE_RESULT_SUBTYPE), \
+ SQLITE_INT_TO_PTR(iArg|((bJsonB)*JSON_BLOB)),0,xFunc,0, 0, 0, #zName, {0} }
#define INLINE_FUNC(zName, nArg, iArg, mFlags) \
{nArg, SQLITE_FUNC_BUILTIN|\
SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \
@@ -18380,6 +18664,7 @@ struct Index {
unsigned isCovering:1; /* True if this is a covering index */
unsigned noSkipScan:1; /* Do not try to use skip-scan if true */
unsigned hasStat1:1; /* aiRowLogEst values come from sqlite_stat1 */
+ unsigned bLowQual:1; /* sqlite_stat1 says this is a low-quality index */
unsigned bNoQuery:1; /* Do not use this index to optimize queries */
unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */
unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */
@@ -18490,6 +18775,10 @@ struct AggInfo {
FuncDef *pFunc; /* The aggregate function implementation */
int iDistinct; /* Ephemeral table used to enforce DISTINCT */
int iDistAddr; /* Address of OP_OpenEphemeral */
+ int iOBTab; /* Ephemeral table to implement ORDER BY */
+ u8 bOBPayload; /* iOBTab has payload columns separate from key */
+ u8 bOBUnique; /* Enforce uniqueness on iOBTab keys */
+ u8 bUseSubtype; /* Transfer subtype info through sorter */
} *aFunc;
int nFunc; /* Number of entries in aFunc[] */
u32 selId; /* Select to which this AggInfo belongs */
@@ -18674,7 +18963,7 @@ struct Expr {
#define EP_Reduced 0x004000 /* Expr struct EXPR_REDUCEDSIZE bytes only */
#define EP_Win 0x008000 /* Contains window functions */
#define EP_TokenOnly 0x010000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */
- /* 0x020000 // Available for reuse */
+#define EP_FullSize 0x020000 /* Expr structure must remain full sized */
#define EP_IfNullRow 0x040000 /* The TK_IF_NULL_ROW opcode */
#define EP_Unlikely 0x080000 /* unlikely() or likelihood() function */
#define EP_ConstFunc 0x100000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */
@@ -18704,6 +18993,7 @@ struct Expr {
#define ExprClearProperty(E,P) (E)->flags&=~(P)
#define ExprAlwaysTrue(E) (((E)->flags&(EP_OuterON|EP_IsTrue))==EP_IsTrue)
#define ExprAlwaysFalse(E) (((E)->flags&(EP_OuterON|EP_IsFalse))==EP_IsFalse)
+#define ExprIsFullSize(E) (((E)->flags&(EP_Reduced|EP_TokenOnly))==0)
/* Macros used to ensure that the correct members of unions are accessed
** in Expr.
@@ -18821,6 +19111,7 @@ struct ExprList {
#define ENAME_NAME 0 /* The AS clause of a result set */
#define ENAME_SPAN 1 /* Complete text of the result set expression */
#define ENAME_TAB 2 /* "DB.TABLE.NAME" for the result set */
+#define ENAME_ROWID 3 /* "DB.TABLE._rowid_" for * expansion of rowid */
/*
** An instance of this structure can hold a simple list of identifiers,
@@ -19021,6 +19312,7 @@ struct NameContext {
int nRef; /* Number of names resolved by this context */
int nNcErr; /* Number of errors encountered while resolving names */
int ncFlags; /* Zero or more NC_* flags defined below */
+ u32 nNestedSelect; /* Number of nested selects using this NC */
Select *pWinSelect; /* SELECT statement for any window functions */
};
@@ -19429,6 +19721,7 @@ struct Parse {
int *aLabel; /* Space to hold the labels */
ExprList *pConstExpr;/* Constant expressions */
IndexedExpr *pIdxEpr;/* List of expressions used by active indexes */
+ IndexedExpr *pIdxPartExpr; /* Exprs constrained by index WHERE clauses */
Token constraintName;/* Name of the constraint currently being parsed */
yDbMask writeMask; /* Start a write transaction on these databases */
yDbMask cookieMask; /* Bitmask of schema verified databases */
@@ -19700,6 +19993,7 @@ struct Returning {
int iRetCur; /* Transient table holding RETURNING results */
int nRetCol; /* Number of in pReturnEL after expansion */
int iRetReg; /* Register array for holding a row of RETURNING */
+ char zName[40]; /* Name of trigger: "sqlite_returning_%p" */
};
/*
@@ -19735,6 +20029,9 @@ struct sqlite3_str {
**
** 3. Make a (read-only) copy of a read-only RCStr string using
** sqlite3RCStrRef().
+**
+** "String" is in the name, but an RCStr object can also be used to hold
+** binary data.
*/
struct RCStr {
u64 nRCRef; /* Number of references */
@@ -19793,6 +20090,9 @@ struct Sqlite3Config {
u8 bSmallMalloc; /* Avoid large memory allocations if true */
u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */
u8 bUseLongDouble; /* Make use of long double */
+#ifdef SQLITE_DEBUG
+ u8 bJsonSelfcheck; /* Double-check JSON parsing */
+#endif
int mxStrlen; /* Maximum string length */
int neverCorrupt; /* Database is always well-formed */
int szLookaside; /* Default lookaside buffer size */
@@ -20000,6 +20300,16 @@ struct CteUse {
};
+/* Client data associated with sqlite3_set_clientdata() and
+** sqlite3_get_clientdata().
+*/
+struct DbClientData {
+ DbClientData *pNext; /* Next in a linked list */
+ void *pData; /* The data */
+ void (*xDestructor)(void*); /* Destructor. Might be NULL */
+ char zName[1]; /* Name of this client data. MUST BE LAST */
+};
+
#ifdef SQLITE_DEBUG
/*
** An instance of the TreeView object is used for printing the content of
@@ -20404,9 +20714,12 @@ SQLITE_PRIVATE void sqlite3PExprAddSelect(Parse*, Expr*, Select*);
SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse*,Expr*, Expr*);
SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr*);
SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, const Token*, int);
+SQLITE_PRIVATE void sqlite3ExprAddFunctionOrderBy(Parse*,Expr*,ExprList*);
+SQLITE_PRIVATE void sqlite3ExprOrderByAggregateError(Parse*,Expr*);
SQLITE_PRIVATE void sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*);
SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32);
SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*);
+SQLITE_PRIVATE void sqlite3ExprDeleteGeneric(sqlite3*,void*);
SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse*, Expr*);
SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse*, Expr*);
SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*);
@@ -20416,6 +20729,7 @@ SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList*,int,int);
SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,const Token*,int);
SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*);
SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*);
+SQLITE_PRIVATE void sqlite3ExprListDeleteGeneric(sqlite3*,void*);
SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList*);
SQLITE_PRIVATE int sqlite3IndexHasDuplicateRootPage(Index*);
SQLITE_PRIVATE int sqlite3Init(sqlite3*, char**);
@@ -20506,6 +20820,7 @@ SQLITE_PRIVATE int sqlite3DbMaskAllZero(yDbMask);
SQLITE_PRIVATE void sqlite3DropTable(Parse*, SrcList*, int, int);
SQLITE_PRIVATE void sqlite3CodeDropTable(Parse*, Table*, int, int);
SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3*, Table*);
+SQLITE_PRIVATE void sqlite3DeleteTableGeneric(sqlite3*, void*);
SQLITE_PRIVATE void sqlite3FreeIndex(sqlite3*, Index*);
#ifndef SQLITE_OMIT_AUTOINCREMENT
SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse);
@@ -20542,6 +20857,7 @@ SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
Expr*,ExprList*,u32,Expr*);
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*);
+SQLITE_PRIVATE void sqlite3SelectDeleteGeneric(sqlite3*,void*);
SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*);
SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, Trigger*);
SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
@@ -20640,6 +20956,7 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr*, int*);
SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*);
SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char);
SQLITE_PRIVATE int sqlite3IsRowid(const char*);
+SQLITE_PRIVATE const char *sqlite3RowidAlias(Table *pTab);
SQLITE_PRIVATE void sqlite3GenerateRowDelete(
Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int);
SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int);
@@ -20767,6 +21084,7 @@ SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar);
#endif
SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte);
SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**);
+SQLITE_PRIVATE int sqlite3Utf8ReadLimited(const u8*, int, u32*);
SQLITE_PRIVATE LogEst sqlite3LogEst(u64);
SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst,LogEst);
SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double);
@@ -20911,7 +21229,8 @@ SQLITE_PRIVATE int sqlite3MatchEName(
const struct ExprList_item*,
const char*,
const char*,
- const char*
+ const char*,
+ int*
);
SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr*);
SQLITE_PRIVATE u8 sqlite3StrIHash(const char*);
@@ -20968,7 +21287,7 @@ SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int);
SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *);
SQLITE_PRIVATE char *sqlite3RCStrRef(char*);
-SQLITE_PRIVATE void sqlite3RCStrUnref(char*);
+SQLITE_PRIVATE void sqlite3RCStrUnref(void*);
SQLITE_PRIVATE char *sqlite3RCStrNew(u64);
SQLITE_PRIVATE char *sqlite3RCStrResize(char*,u64);
@@ -21112,6 +21431,7 @@ SQLITE_PRIVATE Cte *sqlite3CteNew(Parse*,Token*,ExprList*,Select*,u8);
SQLITE_PRIVATE void sqlite3CteDelete(sqlite3*,Cte*);
SQLITE_PRIVATE With *sqlite3WithAdd(Parse*,With*,Cte*);
SQLITE_PRIVATE void sqlite3WithDelete(sqlite3*,With*);
+SQLITE_PRIVATE void sqlite3WithDeleteGeneric(sqlite3*,void*);
SQLITE_PRIVATE With *sqlite3WithPush(Parse*, With*, u8);
#else
# define sqlite3CteNew(P,T,E,S) ((void*)0)
@@ -21804,6 +22124,9 @@ static const char * const sqlite3azCompileOpt[] = {
#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
"EXPLAIN_ESTIMATED_ROWS",
#endif
+#ifdef SQLITE_EXTRA_AUTOEXT
+ "EXTRA_AUTOEXT=" CTIMEOPT_VAL(SQLITE_EXTRA_AUTOEXT),
+#endif
#ifdef SQLITE_EXTRA_IFNULLROW
"EXTRA_IFNULLROW",
#endif
@@ -22085,6 +22408,9 @@ static const char * const sqlite3azCompileOpt[] = {
#ifdef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS
"OMIT_SCHEMA_VERSION_PRAGMAS",
#endif
+#ifdef SQLITE_OMIT_SEH
+ "OMIT_SEH",
+#endif
#ifdef SQLITE_OMIT_SHARED_CACHE
"OMIT_SHARED_CACHE",
#endif
@@ -22483,6 +22809,9 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
0, /* bSmallMalloc */
1, /* bExtraSchemaChecks */
sizeof(LONGDOUBLE_TYPE)>8, /* bUseLongDouble */
+#ifdef SQLITE_DEBUG
+ 0, /* bJsonSelfcheck */
+#endif
0x7ffffffe, /* mxStrlen */
0, /* neverCorrupt */
SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */
@@ -23735,7 +24064,7 @@ SQLITE_API int sqlite3_db_status(
case SQLITE_DBSTATUS_CACHE_MISS:
case SQLITE_DBSTATUS_CACHE_WRITE:{
int i;
- int nRet = 0;
+ u64 nRet = 0;
assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 );
assert( SQLITE_DBSTATUS_CACHE_WRITE==SQLITE_DBSTATUS_CACHE_HIT+2 );
@@ -23748,7 +24077,7 @@ SQLITE_API int sqlite3_db_status(
*pHighwater = 0; /* IMP: R-42420-56072 */
/* IMP: R-54100-20147 */
/* IMP: R-29431-39229 */
- *pCurrent = nRet;
+ *pCurrent = (int)nRet & 0x7fffffff;
break;
}
@@ -24817,6 +25146,12 @@ static int isDate(
}
computeJD(p);
if( p->isError || !validJulianDay(p->iJD) ) return 1;
+ if( argc==1 && p->validYMD && p->D>28 ){
+ /* Make sure a YYYY-MM-DD is normalized.
+ ** Example: 2023-02-31 -> 2023-03-03 */
+ assert( p->validJD );
+ p->validYMD = 0;
+ }
return 0;
}
@@ -25044,13 +25379,16 @@ static void strftimeFunc(
computeJD(&x);
computeYMD_HMS(&x);
for(i=j=0; zFmt[i]; i++){
+ char cf;
if( zFmt[i]!='%' ) continue;
if( j<i ) sqlite3_str_append(&sRes, zFmt+j, (int)(i-j));
i++;
j = i + 1;
- switch( zFmt[i] ){
- case 'd': {
- sqlite3_str_appendf(&sRes, "%02d", x.D);
+ cf = zFmt[i];
+ switch( cf ){
+ case 'd': /* Fall thru */
+ case 'e': {
+ sqlite3_str_appendf(&sRes, cf=='d' ? "%02d" : "%2d", x.D);
break;
}
case 'f': {
@@ -25059,8 +25397,21 @@ static void strftimeFunc(
sqlite3_str_appendf(&sRes, "%06.3f", s);
break;
}
- case 'H': {
- sqlite3_str_appendf(&sRes, "%02d", x.h);
+ case 'F': {
+ sqlite3_str_appendf(&sRes, "%04d-%02d-%02d", x.Y, x.M, x.D);
+ break;
+ }
+ case 'H':
+ case 'k': {
+ sqlite3_str_appendf(&sRes, cf=='H' ? "%02d" : "%2d", x.h);
+ break;
+ }
+ case 'I': /* Fall thru */
+ case 'l': {
+ int h = x.h;
+ if( h>12 ) h -= 12;
+ if( h==0 ) h = 12;
+ sqlite3_str_appendf(&sRes, cf=='I' ? "%02d" : "%2d", h);
break;
}
case 'W': /* Fall thru */
@@ -25072,7 +25423,7 @@ static void strftimeFunc(
y.D = 1;
computeJD(&y);
nDay = (int)((x.iJD-y.iJD+43200000)/86400000);
- if( zFmt[i]=='W' ){
+ if( cf=='W' ){
int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */
wd = (int)(((x.iJD+43200000)/86400000)%7);
sqlite3_str_appendf(&sRes,"%02d",(nDay+7-wd)/7);
@@ -25093,6 +25444,19 @@ static void strftimeFunc(
sqlite3_str_appendf(&sRes,"%02d",x.m);
break;
}
+ case 'p': /* Fall thru */
+ case 'P': {
+ if( x.h>=12 ){
+ sqlite3_str_append(&sRes, cf=='p' ? "PM" : "pm", 2);
+ }else{
+ sqlite3_str_append(&sRes, cf=='p' ? "AM" : "am", 2);
+ }
+ break;
+ }
+ case 'R': {
+ sqlite3_str_appendf(&sRes, "%02d:%02d", x.h, x.m);
+ break;
+ }
case 's': {
if( x.useSubsec ){
sqlite3_str_appendf(&sRes,"%.3f",
@@ -25107,9 +25471,15 @@ static void strftimeFunc(
sqlite3_str_appendf(&sRes,"%02d",(int)x.s);
break;
}
+ case 'T': {
+ sqlite3_str_appendf(&sRes,"%02d:%02d:%02d", x.h, x.m, (int)x.s);
+ break;
+ }
+ case 'u': /* Fall thru */
case 'w': {
- sqlite3_str_appendchar(&sRes, 1,
- (char)(((x.iJD+129600000)/86400000) % 7) + '0');
+ char c = (char)(((x.iJD+129600000)/86400000) % 7) + '0';
+ if( c=='0' && cf=='u' ) c = '7';
+ sqlite3_str_appendchar(&sRes, 1, c);
break;
}
case 'Y': {
@@ -28198,7 +28568,7 @@ static void checkMutexFree(sqlite3_mutex *p){
assert( SQLITE_MUTEX_FAST<2 );
assert( SQLITE_MUTEX_WARNONCONTENTION<2 );
-#if SQLITE_ENABLE_API_ARMOR
+#ifdef SQLITE_ENABLE_API_ARMOR
if( ((CheckMutex*)p)->iType<2 )
#endif
{
@@ -28870,7 +29240,7 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
*/
static void pthreadMutexFree(sqlite3_mutex *p){
assert( p->nRef==0 );
-#if SQLITE_ENABLE_API_ARMOR
+#ifdef SQLITE_ENABLE_API_ARMOR
if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE )
#endif
{
@@ -29223,7 +29593,7 @@ SQLITE_PRIVATE void sqlite3MemoryBarrier(void){
SQLITE_MEMORY_BARRIER;
#elif defined(__GNUC__)
__sync_synchronize();
-#elif MSVC_VERSION>=1300
+#elif MSVC_VERSION>=1400
_ReadWriteBarrier();
#elif defined(MemoryBarrier)
MemoryBarrier();
@@ -30434,7 +30804,7 @@ SQLITE_PRIVATE int sqlite3ApiExit(sqlite3* db, int rc){
if( db->mallocFailed || rc ){
return apiHandleError(db, rc);
}
- return rc & db->errMask;
+ return 0;
}
/************** End of malloc.c **********************************************/
@@ -31810,7 +32180,7 @@ SQLITE_API void sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){
/*****************************************************************************
-** Reference counted string storage
+** Reference counted string/blob storage
*****************************************************************************/
/*
@@ -31830,7 +32200,7 @@ SQLITE_PRIVATE char *sqlite3RCStrRef(char *z){
** Decrease the reference count by one. Free the string when the
** reference count reaches zero.
*/
-SQLITE_PRIVATE void sqlite3RCStrUnref(char *z){
+SQLITE_PRIVATE void sqlite3RCStrUnref(void *z){
RCStr *p = (RCStr*)z;
assert( p!=0 );
p--;
@@ -32293,6 +32663,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u
sqlite3TreeViewItem(pView, "FILTER", 1);
sqlite3TreeViewExpr(pView, pWin->pFilter, 0);
sqlite3TreeViewPop(&pView);
+ if( pWin->eFrmType==TK_FILTER ) return;
}
sqlite3TreeViewPush(&pView, more);
if( pWin->zName ){
@@ -32302,7 +32673,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u
}
if( pWin->zBase ) nElement++;
if( pWin->pOrderBy ) nElement++;
- if( pWin->eFrmType ) nElement++;
+ if( pWin->eFrmType!=0 && pWin->eFrmType!=TK_FILTER ) nElement++;
if( pWin->eExclude ) nElement++;
if( pWin->zBase ){
sqlite3TreeViewPush(&pView, (--nElement)>0);
@@ -32315,7 +32686,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u
if( pWin->pOrderBy ){
sqlite3TreeViewExprList(pView, pWin->pOrderBy, (--nElement)>0, "ORDER-BY");
}
- if( pWin->eFrmType ){
+ if( pWin->eFrmType!=0 && pWin->eFrmType!=TK_FILTER ){
char zBuf[30];
const char *zFrmType = "ROWS";
if( pWin->eFrmType==TK_RANGE ) zFrmType = "RANGE";
@@ -32563,7 +32934,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
assert( ExprUseXList(pExpr) );
pFarg = pExpr->x.pList;
#ifndef SQLITE_OMIT_WINDOWFUNC
- pWin = ExprHasProperty(pExpr, EP_WinFunc) ? pExpr->y.pWin : 0;
+ pWin = IsWindowFunc(pExpr) ? pExpr->y.pWin : 0;
#else
pWin = 0;
#endif
@@ -32589,7 +32960,13 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
sqlite3TreeViewLine(pView, "FUNCTION %Q%s", pExpr->u.zToken, zFlgs);
}
if( pFarg ){
- sqlite3TreeViewExprList(pView, pFarg, pWin!=0, 0);
+ sqlite3TreeViewExprList(pView, pFarg, pWin!=0 || pExpr->pLeft, 0);
+ if( pExpr->pLeft ){
+ Expr *pOB = pExpr->pLeft;
+ assert( pOB->op==TK_ORDER );
+ assert( ExprUseXList(pOB) );
+ sqlite3TreeViewExprList(pView, pOB->x.pList, pWin!=0, "ORDERBY");
+ }
}
#ifndef SQLITE_OMIT_WINDOWFUNC
if( pWin ){
@@ -32598,6 +32975,10 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
#endif
break;
}
+ case TK_ORDER: {
+ sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, "ORDERBY");
+ break;
+ }
#ifndef SQLITE_OMIT_SUBQUERY
case TK_EXISTS: {
assert( ExprUseXSelect(pExpr) );
@@ -32651,7 +33032,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
assert( pExpr->x.pList->nExpr==2 );
pY = pExpr->x.pList->a[0].pExpr;
pZ = pExpr->x.pList->a[1].pExpr;
- sqlite3TreeViewLine(pView, "BETWEEN");
+ sqlite3TreeViewLine(pView, "BETWEEN%s", zFlgs);
sqlite3TreeViewExpr(pView, pX, 1);
sqlite3TreeViewExpr(pView, pY, 1);
sqlite3TreeViewExpr(pView, pZ, 0);
@@ -33786,7 +34167,38 @@ SQLITE_PRIVATE u32 sqlite3Utf8Read(
return c;
}
-
+/*
+** Read a single UTF8 character out of buffer z[], but reading no
+** more than n characters from the buffer. z[] is not zero-terminated.
+**
+** Return the number of bytes used to construct the character.
+**
+** Invalid UTF8 might generate a strange result. No effort is made
+** to detect invalid UTF8.
+**
+** At most 4 bytes will be read out of z[]. The return value will always
+** be between 1 and 4.
+*/
+SQLITE_PRIVATE int sqlite3Utf8ReadLimited(
+ const u8 *z,
+ int n,
+ u32 *piOut
+){
+ u32 c;
+ int i = 1;
+ assert( n>0 );
+ c = z[0];
+ if( c>=0xc0 ){
+ c = sqlite3Utf8Trans1[c-0xc0];
+ if( n>4 ) n = 4;
+ while( i<n && (z[i] & 0xc0)==0x80 ){
+ c = (c<<6) + (0x3f & z[i]);
+ i++;
+ }
+ }
+ *piOut = c;
+ return i;
+}
/*
@@ -34301,7 +34713,7 @@ SQLITE_PRIVATE void sqlite3ErrorClear(sqlite3 *db){
*/
SQLITE_PRIVATE void sqlite3SystemError(sqlite3 *db, int rc){
if( rc==SQLITE_IOERR_NOMEM ) return;
-#ifdef SQLITE_USE_SEH
+#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL)
if( rc==SQLITE_IOERR_IN_PAGE ){
int ii;
int iErr;
@@ -34362,12 +34774,16 @@ SQLITE_PRIVATE void sqlite3ProgressCheck(Parse *p){
p->rc = SQLITE_INTERRUPT;
}
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
- if( db->xProgress && (++p->nProgressSteps)>=db->nProgressOps ){
- if( db->xProgress(db->pProgressArg) ){
- p->nErr++;
- p->rc = SQLITE_INTERRUPT;
+ if( db->xProgress ){
+ if( p->rc==SQLITE_INTERRUPT ){
+ p->nProgressSteps = 0;
+ }else if( (++p->nProgressSteps)>=db->nProgressOps ){
+ if( db->xProgress(db->pProgressArg) ){
+ p->nErr++;
+ p->rc = SQLITE_INTERRUPT;
+ }
+ p->nProgressSteps = 0;
}
- p->nProgressSteps = 0;
}
#endif
}
@@ -35185,29 +35601,29 @@ SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRou
double rr[2];
rr[0] = r;
rr[1] = 0.0;
- if( rr[0]>1.84e+19 ){
- while( rr[0]>1.84e+119 ){
+ if( rr[0]>9.223372036854774784e+18 ){
+ while( rr[0]>9.223372036854774784e+118 ){
exp += 100;
dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117);
}
- while( rr[0]>1.84e+29 ){
+ while( rr[0]>9.223372036854774784e+28 ){
exp += 10;
dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27);
}
- while( rr[0]>1.84e+19 ){
+ while( rr[0]>9.223372036854774784e+18 ){
exp += 1;
dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18);
}
}else{
- while( rr[0]<1.84e-82 ){
+ while( rr[0]<9.223372036854774784e-83 ){
exp -= 100;
dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83);
}
- while( rr[0]<1.84e+08 ){
+ while( rr[0]<9.223372036854774784e+07 ){
exp -= 10;
dekkerMul2(rr, 1.0e+10, 0.0);
}
- while( rr[0]<1.84e+18 ){
+ while( rr[0]<9.22337203685477478e+17 ){
exp -= 1;
dekkerMul2(rr, 1.0e+01, 0.0);
}
@@ -35523,121 +35939,32 @@ SQLITE_PRIVATE u8 sqlite3GetVarint(const unsigned char *p, u64 *v){
** this function assumes the single-byte case has already been handled.
*/
SQLITE_PRIVATE u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){
- u32 a,b;
+ u64 v64;
+ u8 n;
- /* The 1-byte case. Overwhelmingly the most common. Handled inline
- ** by the getVarin32() macro */
- a = *p;
- /* a: p0 (unmasked) */
-#ifndef getVarint32
- if (!(a&0x80))
- {
- /* Values between 0 and 127 */
- *v = a;
- return 1;
- }
-#endif
+ /* Assume that the single-byte case has already been handled by
+ ** the getVarint32() macro */
+ assert( (p[0] & 0x80)!=0 );
- /* The 2-byte case */
- p++;
- b = *p;
- /* b: p1 (unmasked) */
- if (!(b&0x80))
- {
- /* Values between 128 and 16383 */
- a &= 0x7f;
- a = a<<7;
- *v = a | b;
+ if( (p[1] & 0x80)==0 ){
+ /* This is the two-byte case */
+ *v = ((p[0]&0x7f)<<7) | p[1];
return 2;
}
-
- /* The 3-byte case */
- p++;
- a = a<<14;
- a |= *p;
- /* a: p0<<14 | p2 (unmasked) */
- if (!(a&0x80))
- {
- /* Values between 16384 and 2097151 */
- a &= (0x7f<<14)|(0x7f);
- b &= 0x7f;
- b = b<<7;
- *v = a | b;
+ if( (p[2] & 0x80)==0 ){
+ /* This is the three-byte case */
+ *v = ((p[0]&0x7f)<<14) | ((p[1]&0x7f)<<7) | p[2];
return 3;
}
-
- /* A 32-bit varint is used to store size information in btrees.
- ** Objects are rarely larger than 2MiB limit of a 3-byte varint.
- ** A 3-byte varint is sufficient, for example, to record the size
- ** of a 1048569-byte BLOB or string.
- **
- ** We only unroll the first 1-, 2-, and 3- byte cases. The very
- ** rare larger cases can be handled by the slower 64-bit varint
- ** routine.
- */
-#if 1
- {
- u64 v64;
- u8 n;
-
- n = sqlite3GetVarint(p-2, &v64);
- assert( n>3 && n<=9 );
- if( (v64 & SQLITE_MAX_U32)!=v64 ){
- *v = 0xffffffff;
- }else{
- *v = (u32)v64;
- }
- return n;
- }
-
-#else
- /* For following code (kept for historical record only) shows an
- ** unrolling for the 3- and 4-byte varint cases. This code is
- ** slightly faster, but it is also larger and much harder to test.
- */
- p++;
- b = b<<14;
- b |= *p;
- /* b: p1<<14 | p3 (unmasked) */
- if (!(b&0x80))
- {
- /* Values between 2097152 and 268435455 */
- b &= (0x7f<<14)|(0x7f);
- a &= (0x7f<<14)|(0x7f);
- a = a<<7;
- *v = a | b;
- return 4;
- }
-
- p++;
- a = a<<14;
- a |= *p;
- /* a: p0<<28 | p2<<14 | p4 (unmasked) */
- if (!(a&0x80))
- {
- /* Values between 268435456 and 34359738367 */
- a &= SLOT_4_2_0;
- b &= SLOT_4_2_0;
- b = b<<7;
- *v = a | b;
- return 5;
- }
-
- /* We can only reach this point when reading a corrupt database
- ** file. In that case we are not in any hurry. Use the (relatively
- ** slow) general-purpose sqlite3GetVarint() routine to extract the
- ** value. */
- {
- u64 v64;
- u8 n;
-
- p -= 4;
- n = sqlite3GetVarint(p, &v64);
- assert( n>5 && n<=9 );
+ /* four or more bytes */
+ n = sqlite3GetVarint(p, &v64);
+ assert( n>3 && n<=9 );
+ if( (v64 & SQLITE_MAX_U32)!=v64 ){
+ *v = 0xffffffff;
+ }else{
*v = (u32)v64;
- return n;
}
-#endif
+ return n;
}
/*
@@ -36633,19 +36960,22 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 171 */ "VCreate" OpHelp(""),
/* 172 */ "VDestroy" OpHelp(""),
/* 173 */ "VOpen" OpHelp(""),
- /* 174 */ "VInitIn" OpHelp("r[P2]=ValueList(P1,P3)"),
- /* 175 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"),
- /* 176 */ "VRename" OpHelp(""),
- /* 177 */ "Pagecount" OpHelp(""),
- /* 178 */ "MaxPgcnt" OpHelp(""),
- /* 179 */ "ClrSubtype" OpHelp("r[P1].subtype = 0"),
- /* 180 */ "FilterAdd" OpHelp("filter(P1) += key(P3@P4)"),
- /* 181 */ "Trace" OpHelp(""),
- /* 182 */ "CursorHint" OpHelp(""),
- /* 183 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"),
- /* 184 */ "Noop" OpHelp(""),
- /* 185 */ "Explain" OpHelp(""),
- /* 186 */ "Abortable" OpHelp(""),
+ /* 174 */ "VCheck" OpHelp(""),
+ /* 175 */ "VInitIn" OpHelp("r[P2]=ValueList(P1,P3)"),
+ /* 176 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"),
+ /* 177 */ "VRename" OpHelp(""),
+ /* 178 */ "Pagecount" OpHelp(""),
+ /* 179 */ "MaxPgcnt" OpHelp(""),
+ /* 180 */ "ClrSubtype" OpHelp("r[P1].subtype = 0"),
+ /* 181 */ "GetSubtype" OpHelp("r[P2] = r[P1].subtype"),
+ /* 182 */ "SetSubtype" OpHelp("r[P2].subtype = r[P1]"),
+ /* 183 */ "FilterAdd" OpHelp("filter(P1) += key(P3@P4)"),
+ /* 184 */ "Trace" OpHelp(""),
+ /* 185 */ "CursorHint" OpHelp(""),
+ /* 186 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"),
+ /* 187 */ "Noop" OpHelp(""),
+ /* 188 */ "Explain" OpHelp(""),
+ /* 189 */ "Abortable" OpHelp(""),
};
return azName[i];
}
@@ -40787,9 +41117,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
unixInodeInfo *pInode;
afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
int skipShared = 0;
-#ifdef SQLITE_TEST
- int h = pFile->h;
-#endif
assert( pFile );
OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (afp)\n", pFile->h, eFileLock,
@@ -40805,9 +41132,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
assert( pInode->nShared!=0 );
if( pFile->eFileLock>SHARED_LOCK ){
assert( pInode->eFileLock==pFile->eFileLock );
- SimulateIOErrorBenign(1);
- SimulateIOError( h=(-1) )
- SimulateIOErrorBenign(0);
#ifdef SQLITE_DEBUG
/* When reducing a lock such that other processes can start
@@ -40856,9 +41180,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
unsigned long long sharedLockByte = SHARED_FIRST+pInode->sharedByte;
pInode->nShared--;
if( pInode->nShared==0 ){
- SimulateIOErrorBenign(1);
- SimulateIOError( h=(-1) )
- SimulateIOErrorBenign(0);
if( !skipShared ){
rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 0);
}
@@ -41700,7 +42021,13 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
case SQLITE_FCNTL_LOCK_TIMEOUT: {
int iOld = pFile->iBusyTimeout;
+#if SQLITE_ENABLE_SETLK_TIMEOUT==1
pFile->iBusyTimeout = *(int*)pArg;
+#elif SQLITE_ENABLE_SETLK_TIMEOUT==2
+ pFile->iBusyTimeout = !!(*(int*)pArg);
+#else
+# error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2"
+#endif
*(int*)pArg = iOld;
return SQLITE_OK;
}
@@ -41953,6 +42280,25 @@ static int unixGetpagesize(void){
** Either unixShmNode.pShmMutex must be held or unixShmNode.nRef==0 and
** unixMutexHeld() is true when reading or writing any other field
** in this structure.
+**
+** aLock[SQLITE_SHM_NLOCK]:
+** This array records the various locks held by clients on each of the
+** SQLITE_SHM_NLOCK slots. If the aLock[] entry is set to 0, then no
+** locks are held by the process on this slot. If it is set to -1, then
+** some client holds an EXCLUSIVE lock on the locking slot. If the aLock[]
+** value is set to a positive value, then it is the number of shared
+** locks currently held on the slot.
+**
+** aMutex[SQLITE_SHM_NLOCK]:
+** Normally, when SQLITE_ENABLE_SETLK_TIMEOUT is not defined, mutex
+** pShmMutex is used to protect the aLock[] array and the right to
+** call fcntl() on unixShmNode.hShm to obtain or release locks.
+**
+** If SQLITE_ENABLE_SETLK_TIMEOUT is defined though, we use an array
+** of mutexes - one for each locking slot. To read or write locking
+** slot aLock[iSlot], the caller must hold the corresponding mutex
+** aMutex[iSlot]. Similarly, to call fcntl() to obtain or release a
+** lock corresponding to slot iSlot, mutex aMutex[iSlot] must be held.
*/
struct unixShmNode {
unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */
@@ -41966,10 +42312,11 @@ struct unixShmNode {
char **apRegion; /* Array of mapped shared-memory regions */
int nRef; /* Number of unixShm objects pointing to this */
unixShm *pFirst; /* All unixShm objects pointing to this */
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ sqlite3_mutex *aMutex[SQLITE_SHM_NLOCK];
+#endif
int aLock[SQLITE_SHM_NLOCK]; /* # shared locks on slot, -1==excl lock */
#ifdef SQLITE_DEBUG
- u8 exclMask; /* Mask of exclusive locks held */
- u8 sharedMask; /* Mask of shared locks held */
u8 nextShmId; /* Next available unixShm.id value */
#endif
};
@@ -42052,16 +42399,35 @@ static int unixShmSystemLock(
struct flock f; /* The posix advisory locking structure */
int rc = SQLITE_OK; /* Result code form fcntl() */
- /* Access to the unixShmNode object is serialized by the caller */
pShmNode = pFile->pInode->pShmNode;
- assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) );
- assert( pShmNode->nRef>0 || unixMutexHeld() );
+
+ /* Assert that the parameters are within expected range and that the
+ ** correct mutex or mutexes are held. */
+ assert( pShmNode->nRef>=0 );
+ assert( (ofst==UNIX_SHM_DMS && n==1)
+ || (ofst>=UNIX_SHM_BASE && ofst+n<=(UNIX_SHM_BASE+SQLITE_SHM_NLOCK))
+ );
+ if( ofst==UNIX_SHM_DMS ){
+ assert( pShmNode->nRef>0 || unixMutexHeld() );
+ assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) );
+ }else{
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ int ii;
+ for(ii=ofst-UNIX_SHM_BASE; ii<ofst-UNIX_SHM_BASE+n; ii++){
+ assert( sqlite3_mutex_held(pShmNode->aMutex[ii]) );
+ }
+#else
+ assert( sqlite3_mutex_held(pShmNode->pShmMutex) );
+ assert( pShmNode->nRef>0 );
+#endif
+ }
/* Shared locks never span more than one byte */
assert( n==1 || lockType!=F_RDLCK );
/* Locks are within range */
assert( n>=1 && n<=SQLITE_SHM_NLOCK );
+ assert( ofst>=UNIX_SHM_BASE && ofst<=(UNIX_SHM_DMS+SQLITE_SHM_NLOCK) );
if( pShmNode->hShm>=0 ){
int res;
@@ -42072,7 +42438,7 @@ static int unixShmSystemLock(
f.l_len = n;
res = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile);
if( res==-1 ){
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && SQLITE_ENABLE_SETLK_TIMEOUT==1
rc = (pFile->iBusyTimeout ? SQLITE_BUSY_TIMEOUT : SQLITE_BUSY);
#else
rc = SQLITE_BUSY;
@@ -42080,39 +42446,28 @@ static int unixShmSystemLock(
}
}
- /* Update the global lock state and do debug tracing */
+ /* Do debug tracing */
#ifdef SQLITE_DEBUG
- { u16 mask;
OSTRACE(("SHM-LOCK "));
- mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<<ofst);
if( rc==SQLITE_OK ){
if( lockType==F_UNLCK ){
- OSTRACE(("unlock %d ok", ofst));
- pShmNode->exclMask &= ~mask;
- pShmNode->sharedMask &= ~mask;
+ OSTRACE(("unlock %d..%d ok\n", ofst, ofst+n-1));
}else if( lockType==F_RDLCK ){
- OSTRACE(("read-lock %d ok", ofst));
- pShmNode->exclMask &= ~mask;
- pShmNode->sharedMask |= mask;
+ OSTRACE(("read-lock %d..%d ok\n", ofst, ofst+n-1));
}else{
assert( lockType==F_WRLCK );
- OSTRACE(("write-lock %d ok", ofst));
- pShmNode->exclMask |= mask;
- pShmNode->sharedMask &= ~mask;
+ OSTRACE(("write-lock %d..%d ok\n", ofst, ofst+n-1));
}
}else{
if( lockType==F_UNLCK ){
- OSTRACE(("unlock %d failed", ofst));
+ OSTRACE(("unlock %d..%d failed\n", ofst, ofst+n-1));
}else if( lockType==F_RDLCK ){
- OSTRACE(("read-lock failed"));
+ OSTRACE(("read-lock %d..%d failed\n", ofst, ofst+n-1));
}else{
assert( lockType==F_WRLCK );
- OSTRACE(("write-lock %d failed", ofst));
+ OSTRACE(("write-lock %d..%d failed\n", ofst, ofst+n-1));
}
}
- OSTRACE((" - afterwards %03x,%03x\n",
- pShmNode->sharedMask, pShmNode->exclMask));
- }
#endif
return rc;
@@ -42149,6 +42504,11 @@ static void unixShmPurge(unixFile *pFd){
int i;
assert( p->pInode==pFd->pInode );
sqlite3_mutex_free(p->pShmMutex);
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ for(i=0; i<SQLITE_SHM_NLOCK; i++){
+ sqlite3_mutex_free(p->aMutex[i]);
+ }
+#endif
for(i=0; i<p->nRegion; i+=nShmPerMap){
if( p->hShm>=0 ){
osMunmap(p->apRegion[i], p->szRegion);
@@ -42208,7 +42568,20 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){
pShmNode->isUnlocked = 1;
rc = SQLITE_READONLY_CANTINIT;
}else{
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ /* Do not use a blocking lock here. If the lock cannot be obtained
+ ** immediately, it means some other connection is truncating the
+ ** *-shm file. And after it has done so, it will not release its
+ ** lock, but only downgrade it to a shared lock. So no point in
+ ** blocking here. The call below to obtain the shared DMS lock may
+ ** use a blocking lock. */
+ int iSaveTimeout = pDbFd->iBusyTimeout;
+ pDbFd->iBusyTimeout = 0;
+#endif
rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1);
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ pDbFd->iBusyTimeout = iSaveTimeout;
+#endif
/* The first connection to attach must truncate the -shm file. We
** truncate to 3 bytes (an arbitrary small number, less than the
** -shm header size) rather than 0 as a system debugging aid, to
@@ -42329,6 +42702,18 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
rc = SQLITE_NOMEM_BKPT;
goto shm_open_err;
}
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ {
+ int ii;
+ for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){
+ pShmNode->aMutex[ii] = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+ if( pShmNode->aMutex[ii]==0 ){
+ rc = SQLITE_NOMEM_BKPT;
+ goto shm_open_err;
+ }
+ }
+ }
+#endif
}
if( pInode->bProcessLock==0 ){
@@ -42550,9 +42935,11 @@ shmpage_out:
*/
#ifdef SQLITE_DEBUG
static int assertLockingArrayOk(unixShmNode *pShmNode){
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ return 1;
+#else
unixShm *pX;
int aLock[SQLITE_SHM_NLOCK];
- assert( sqlite3_mutex_held(pShmNode->pShmMutex) );
memset(aLock, 0, sizeof(aLock));
for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
@@ -42570,13 +42957,14 @@ static int assertLockingArrayOk(unixShmNode *pShmNode){
assert( 0==memcmp(pShmNode->aLock, aLock, sizeof(aLock)) );
return (memcmp(pShmNode->aLock, aLock, sizeof(aLock))==0);
+#endif
}
#endif
/*
** Change the lock state for a shared-memory segment.
**
-** Note that the relationship between SHAREd and EXCLUSIVE locks is a little
+** Note that the relationship between SHARED and EXCLUSIVE locks is a little
** different here than in posix. In xShmLock(), one can go from unlocked
** to shared and back or from unlocked to exclusive and back. But one may
** not go from shared to exclusive or from exclusive to shared.
@@ -42591,7 +42979,7 @@ static int unixShmLock(
unixShm *p; /* The shared memory being locked */
unixShmNode *pShmNode; /* The underlying file iNode */
int rc = SQLITE_OK; /* Result code */
- u16 mask; /* Mask of locks to take or release */
+ u16 mask = (1<<(ofst+n)) - (1<<ofst); /* Mask of locks to take or release */
int *aLock;
p = pDbFd->pShm;
@@ -42626,88 +43014,151 @@ static int unixShmLock(
** It is not permitted to block on the RECOVER lock.
*/
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
- assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
- (ofst!=2) /* not RECOVER */
- && (ofst!=1 || (p->exclMask|p->sharedMask)==0)
- && (ofst!=0 || (p->exclMask|p->sharedMask)<3)
- && (ofst<3 || (p->exclMask|p->sharedMask)<(1<<ofst))
- ));
+ {
+ u16 lockMask = (p->exclMask|p->sharedMask);
+ assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
+ (ofst!=2) /* not RECOVER */
+ && (ofst!=1 || lockMask==0 || lockMask==2)
+ && (ofst!=0 || lockMask<3)
+ && (ofst<3 || lockMask<(1<<ofst))
+ ));
+ }
#endif
- mask = (1<<(ofst+n)) - (1<<ofst);
- assert( n>1 || mask==(1<<ofst) );
- sqlite3_mutex_enter(pShmNode->pShmMutex);
- assert( assertLockingArrayOk(pShmNode) );
- if( flags & SQLITE_SHM_UNLOCK ){
- if( (p->exclMask|p->sharedMask) & mask ){
- int ii;
- int bUnlock = 1;
+ /* Check if there is any work to do. There are three cases:
+ **
+ ** a) An unlock operation where there are locks to unlock,
+ ** b) An shared lock where the requested lock is not already held
+ ** c) An exclusive lock where the requested lock is not already held
+ **
+ ** The SQLite core never requests an exclusive lock that it already holds.
+ ** This is assert()ed below.
+ */
+ assert( flags!=(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)
+ || 0==(p->exclMask & mask)
+ );
+ if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask))
+ || (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask))
+ || (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK))
+ ){
- for(ii=ofst; ii<ofst+n; ii++){
- if( aLock[ii]>((p->sharedMask & (1<<ii)) ? 1 : 0) ){
- bUnlock = 0;
- }
+ /* Take the required mutexes. In SETLK_TIMEOUT mode (blocking locks), if
+ ** this is an attempt on an exclusive lock use sqlite3_mutex_try(). If any
+ ** other thread is holding this mutex, then it is either holding or about
+ ** to hold a lock exclusive to the one being requested, and we may
+ ** therefore return SQLITE_BUSY to the caller.
+ **
+ ** Doing this prevents some deadlock scenarios. For example, thread 1 may
+ ** be a checkpointer blocked waiting on the WRITER lock. And thread 2
+ ** may be a normal SQL client upgrading to a write transaction. In this
+ ** case thread 2 does a non-blocking request for the WRITER lock. But -
+ ** if it were to use sqlite3_mutex_enter() then it would effectively
+ ** become a (doomed) blocking request, as thread 2 would block until thread
+ ** 1 obtained WRITER and released the mutex. Since thread 2 already holds
+ ** a lock on a read-locking slot at this point, this breaks the
+ ** anti-deadlock rules (see above). */
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ int iMutex;
+ for(iMutex=ofst; iMutex<ofst+n; iMutex++){
+ if( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) ){
+ rc = sqlite3_mutex_try(pShmNode->aMutex[iMutex]);
+ if( rc!=SQLITE_OK ) goto leave_shmnode_mutexes;
+ }else{
+ sqlite3_mutex_enter(pShmNode->aMutex[iMutex]);
}
+ }
+#else
+ sqlite3_mutex_enter(pShmNode->pShmMutex);
+#endif
- if( bUnlock ){
- rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
- if( rc==SQLITE_OK ){
- memset(&aLock[ofst], 0, sizeof(int)*n);
+ if( ALWAYS(rc==SQLITE_OK) ){
+ if( flags & SQLITE_SHM_UNLOCK ){
+ /* Case (a) - unlock. */
+ int bUnlock = 1;
+ assert( (p->exclMask & p->sharedMask)==0 );
+ assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask );
+ assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask );
+
+ /* If this is a SHARED lock being unlocked, it is possible that other
+ ** clients within this process are holding the same SHARED lock. In
+ ** this case, set bUnlock to 0 so that the posix lock is not removed
+ ** from the file-descriptor below. */
+ if( flags & SQLITE_SHM_SHARED ){
+ assert( n==1 );
+ assert( aLock[ofst]>=1 );
+ if( aLock[ofst]>1 ){
+ bUnlock = 0;
+ aLock[ofst]--;
+ p->sharedMask &= ~mask;
+ }
}
- }else if( ALWAYS(p->sharedMask & (1<<ofst)) ){
- assert( n==1 && aLock[ofst]>1 );
- aLock[ofst]--;
- }
- /* Undo the local locks */
- if( rc==SQLITE_OK ){
- p->exclMask &= ~mask;
- p->sharedMask &= ~mask;
- }
- }
- }else if( flags & SQLITE_SHM_SHARED ){
- assert( n==1 );
- assert( (p->exclMask & (1<<ofst))==0 );
- if( (p->sharedMask & mask)==0 ){
- if( aLock[ofst]<0 ){
- rc = SQLITE_BUSY;
- }else if( aLock[ofst]==0 ){
- rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n);
- }
+ if( bUnlock ){
+ rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
+ if( rc==SQLITE_OK ){
+ memset(&aLock[ofst], 0, sizeof(int)*n);
+ p->sharedMask &= ~mask;
+ p->exclMask &= ~mask;
+ }
+ }
+ }else if( flags & SQLITE_SHM_SHARED ){
+ /* Case (b) - a shared lock. */
- /* Get the local shared locks */
- if( rc==SQLITE_OK ){
- p->sharedMask |= mask;
- aLock[ofst]++;
- }
- }
- }else{
- /* Make sure no sibling connections hold locks that will block this
- ** lock. If any do, return SQLITE_BUSY right away. */
- int ii;
- for(ii=ofst; ii<ofst+n; ii++){
- assert( (p->sharedMask & mask)==0 );
- if( ALWAYS((p->exclMask & (1<<ii))==0) && aLock[ii] ){
- rc = SQLITE_BUSY;
- break;
- }
- }
+ if( aLock[ofst]<0 ){
+ /* An exclusive lock is held by some other connection. BUSY. */
+ rc = SQLITE_BUSY;
+ }else if( aLock[ofst]==0 ){
+ rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n);
+ }
- /* Get the exclusive locks at the system level. Then if successful
- ** also update the in-memory values. */
- if( rc==SQLITE_OK ){
- rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n);
- if( rc==SQLITE_OK ){
+ /* Get the local shared locks */
+ if( rc==SQLITE_OK ){
+ p->sharedMask |= mask;
+ aLock[ofst]++;
+ }
+ }else{
+ /* Case (c) - an exclusive lock. */
+ int ii;
+
+ assert( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) );
assert( (p->sharedMask & mask)==0 );
- p->exclMask |= mask;
+ assert( (p->exclMask & mask)==0 );
+
+ /* Make sure no sibling connections hold locks that will block this
+ ** lock. If any do, return SQLITE_BUSY right away. */
for(ii=ofst; ii<ofst+n; ii++){
- aLock[ii] = -1;
+ if( aLock[ii] ){
+ rc = SQLITE_BUSY;
+ break;
+ }
+ }
+
+ /* Get the exclusive locks at the system level. Then if successful
+ ** also update the in-memory values. */
+ if( rc==SQLITE_OK ){
+ rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n);
+ if( rc==SQLITE_OK ){
+ p->exclMask |= mask;
+ for(ii=ofst; ii<ofst+n; ii++){
+ aLock[ii] = -1;
+ }
+ }
}
}
+ assert( assertLockingArrayOk(pShmNode) );
+ }
+
+ /* Drop the mutexes acquired above. */
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ leave_shmnode_mutexes:
+ for(iMutex--; iMutex>=ofst; iMutex--){
+ sqlite3_mutex_leave(pShmNode->aMutex[iMutex]);
}
+#else
+ sqlite3_mutex_leave(pShmNode->pShmMutex);
+#endif
}
- assert( assertLockingArrayOk(pShmNode) );
- sqlite3_mutex_leave(pShmNode->pShmMutex);
+
OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n",
p->id, osGetpid(0), p->sharedMask, p->exclMask));
return rc;
@@ -42957,11 +43408,16 @@ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
#if SQLITE_MAX_MMAP_SIZE>0
if( pFd->mmapSizeMax>0 ){
+ /* Ensure that there is always at least a 256 byte buffer of addressable
+ ** memory following the returned page. If the database is corrupt,
+ ** SQLite may overread the page slightly (in practice only a few bytes,
+ ** but 256 is safe, round, number). */
+ const int nEofBuffer = 256;
if( pFd->pMapRegion==0 ){
int rc = unixMapfile(pFd, -1);
if( rc!=SQLITE_OK ) return rc;
}
- if( pFd->mmapSize >= iOff+nAmt ){
+ if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){
*pp = &((u8 *)pFd->pMapRegion)[iOff];
pFd->nFetchOut++;
}
@@ -50314,6 +50770,11 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
#if SQLITE_MAX_MMAP_SIZE>0
if( pFd->mmapSizeMax>0 ){
+ /* Ensure that there is always at least a 256 byte buffer of addressable
+ ** memory following the returned page. If the database is corrupt,
+ ** SQLite may overread the page slightly (in practice only a few bytes,
+ ** but 256 is safe, round, number). */
+ const int nEofBuffer = 256;
if( pFd->pMapRegion==0 ){
int rc = winMapfile(pFd, -1);
if( rc!=SQLITE_OK ){
@@ -50322,7 +50783,7 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
return rc;
}
}
- if( pFd->mmapSize >= iOff+nAmt ){
+ if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){
assert( pFd->pMapRegion!=0 );
*pp = &((u8 *)pFd->pMapRegion)[iOff];
pFd->nFetchOut++;
@@ -56925,7 +57386,7 @@ struct Pager {
char *zJournal; /* Name of the journal file */
int (*xBusyHandler)(void*); /* Function to call when busy */
void *pBusyHandlerArg; /* Context argument for xBusyHandler */
- int aStat[4]; /* Total cache hits, misses, writes, spills */
+ u32 aStat[4]; /* Total cache hits, misses, writes, spills */
#ifdef SQLITE_TEST
int nRead; /* Database pages read */
#endif
@@ -57055,9 +57516,8 @@ SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){
#ifndef SQLITE_OMIT_WAL
if( pPager->pWal ){
u32 iRead = 0;
- int rc;
- rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
- return (rc==SQLITE_OK && iRead==0);
+ (void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
+ return iRead==0;
}
#endif
return 1;
@@ -57729,9 +58189,32 @@ static int writeJournalHdr(Pager *pPager){
memset(zHeader, 0, sizeof(aJournalMagic)+4);
}
+
+
/* The random check-hash initializer */
- sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit);
+ if( pPager->journalMode!=PAGER_JOURNALMODE_MEMORY ){
+ sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit);
+ }
+#ifdef SQLITE_DEBUG
+ else{
+ /* The Pager.cksumInit variable is usually randomized above to protect
+ ** against there being existing records in the journal file. This is
+ ** dangerous, as following a crash they may be mistaken for records
+ ** written by the current transaction and rolled back into the database
+ ** file, causing corruption. The following assert statements verify
+ ** that this is not required in "journal_mode=memory" mode, as in that
+ ** case the journal file is always 0 bytes in size at this point.
+ ** It is advantageous to avoid the sqlite3_randomness() call if possible
+ ** as it takes the global PRNG mutex. */
+ i64 sz = 0;
+ sqlite3OsFileSize(pPager->jfd, &sz);
+ assert( sz==0 );
+ assert( pPager->journalOff==journalHdrOffset(pPager) );
+ assert( sqlite3JournalIsInMemory(pPager->jfd) );
+ }
+#endif
put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit);
+
/* The initial database size */
put32bits(&zHeader[sizeof(aJournalMagic)+8], pPager->dbOrigSize);
/* The assumed sector size for this process */
@@ -58375,6 +58858,9 @@ static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){
return (rc==SQLITE_OK?rc2:rc);
}
+/* Forward reference */
+static int pager_playback(Pager *pPager, int isHot);
+
/*
** Execute a rollback if a transaction is active and unlock the
** database file.
@@ -58403,6 +58889,21 @@ static void pagerUnlockAndRollback(Pager *pPager){
assert( pPager->eState==PAGER_READER );
pager_end_transaction(pPager, 0, 0);
}
+ }else if( pPager->eState==PAGER_ERROR
+ && pPager->journalMode==PAGER_JOURNALMODE_MEMORY
+ && isOpen(pPager->jfd)
+ ){
+ /* Special case for a ROLLBACK due to I/O error with an in-memory
+ ** journal: We have to rollback immediately, before the journal is
+ ** closed, because once it is closed, all content is forgotten. */
+ int errCode = pPager->errCode;
+ u8 eLock = pPager->eLock;
+ pPager->eState = PAGER_OPEN;
+ pPager->errCode = SQLITE_OK;
+ pPager->eLock = EXCLUSIVE_LOCK;
+ pager_playback(pPager, 1);
+ pPager->errCode = errCode;
+ pPager->eLock = eLock;
}
pager_unlock(pPager);
}
@@ -61258,10 +61759,13 @@ act_like_temp_file:
*/
SQLITE_API sqlite3_file *sqlite3_database_file_object(const char *zName){
Pager *pPager;
+ const char *p;
while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){
zName--;
}
- pPager = *(Pager**)(zName - 4 - sizeof(Pager*));
+ p = zName - 4 - sizeof(Pager*);
+ assert( EIGHT_BYTE_ALIGNMENT(p) );
+ pPager = *(Pager**)p;
return pPager->fd;
}
@@ -61895,8 +62399,20 @@ SQLITE_PRIVATE int sqlite3PagerGet(
DbPage **ppPage, /* Write a pointer to the page here */
int flags /* PAGER_GET_XXX flags */
){
- /* printf("PAGE %u\n", pgno); fflush(stdout); */
+#if 0 /* Trace page fetch by setting to 1 */
+ int rc;
+ printf("PAGE %u\n", pgno);
+ fflush(stdout);
+ rc = pPager->xGet(pPager, pgno, ppPage, flags);
+ if( rc ){
+ printf("PAGE %u failed with 0x%02x\n", pgno, rc);
+ fflush(stdout);
+ }
+ return rc;
+#else
+ /* Normal, high-speed version of sqlite3PagerGet() */
return pPager->xGet(pPager, pgno, ppPage, flags);
+#endif
}
/*
@@ -62772,6 +63288,13 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, 0);
if( rc==SQLITE_OK ){
rc = pager_write_pagelist(pPager, pList);
+ if( rc==SQLITE_OK && pPager->dbSize>pPager->dbFileSize ){
+ char *pTmp = pPager->pTmpSpace;
+ int szPage = (int)pPager->pageSize;
+ memset(pTmp, 0, szPage);
+ rc = sqlite3OsWrite(pPager->fd, pTmp, szPage,
+ ((i64)pPager->dbSize*pPager->pageSize)-szPage);
+ }
if( rc==SQLITE_OK ){
rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_COMMIT_ATOMIC_WRITE, 0);
}
@@ -63006,11 +63529,11 @@ SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){
a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize;
a[4] = pPager->eState;
a[5] = pPager->errCode;
- a[6] = pPager->aStat[PAGER_STAT_HIT];
- a[7] = pPager->aStat[PAGER_STAT_MISS];
+ a[6] = (int)pPager->aStat[PAGER_STAT_HIT] & 0x7fffffff;
+ a[7] = (int)pPager->aStat[PAGER_STAT_MISS] & 0x7fffffff;
a[8] = 0; /* Used to be pPager->nOvfl */
a[9] = pPager->nRead;
- a[10] = pPager->aStat[PAGER_STAT_WRITE];
+ a[10] = (int)pPager->aStat[PAGER_STAT_WRITE] & 0x7fffffff;
return a;
}
#endif
@@ -63026,7 +63549,7 @@ SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){
** reset parameter is non-zero, the cache hit or miss count is zeroed before
** returning.
*/
-SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){
+SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, u64 *pnVal){
assert( eStat==SQLITE_DBSTATUS_CACHE_HIT
|| eStat==SQLITE_DBSTATUS_CACHE_MISS
@@ -63583,7 +64106,7 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
}
assert( state==pPager->eState );
}
- }else if( eMode==PAGER_JOURNALMODE_OFF ){
+ }else if( eMode==PAGER_JOURNALMODE_OFF || eMode==PAGER_JOURNALMODE_MEMORY ){
sqlite3OsClose(pPager->jfd);
}
}
@@ -63966,7 +64489,7 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
}
#endif
-#ifdef SQLITE_USE_SEH
+#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL)
SQLITE_PRIVATE int sqlite3PagerWalSystemErrno(Pager *pPager){
return sqlite3WalSystemErrno(pPager->pWal);
}
@@ -65982,6 +66505,19 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){
}
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+
+
+/*
+** Attempt to enable blocking locks that block for nMs ms. Return 1 if
+** blocking locks are successfully enabled, or 0 otherwise.
+*/
+static int walEnableBlockingMs(Wal *pWal, int nMs){
+ int rc = sqlite3OsFileControl(
+ pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&nMs
+ );
+ return (rc==SQLITE_OK);
+}
+
/*
** Attempt to enable blocking locks. Blocking locks are enabled only if (a)
** they are supported by the VFS, and (b) the database handle is configured
@@ -65993,11 +66529,7 @@ static int walEnableBlocking(Wal *pWal){
if( pWal->db ){
int tmout = pWal->db->busyTimeout;
if( tmout ){
- int rc;
- rc = sqlite3OsFileControl(
- pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout
- );
- res = (rc==SQLITE_OK);
+ res = walEnableBlockingMs(pWal, tmout);
}
}
return res;
@@ -66046,20 +66578,10 @@ SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db){
pWal->db = db;
}
-/*
-** Take an exclusive WRITE lock. Blocking if so configured.
-*/
-static int walLockWriter(Wal *pWal){
- int rc;
- walEnableBlocking(pWal);
- rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
- walDisableBlocking(pWal);
- return rc;
-}
#else
# define walEnableBlocking(x) 0
# define walDisableBlocking(x)
-# define walLockWriter(pWal) walLockExclusive((pWal), WAL_WRITE_LOCK, 1)
+# define walEnableBlockingMs(pWal, ms) 0
# define sqlite3WalDb(pWal, db)
#endif /* ifdef SQLITE_ENABLE_SETLK_TIMEOUT */
@@ -66660,7 +67182,9 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){
}
}else{
int bWriteLock = pWal->writeLock;
- if( bWriteLock || SQLITE_OK==(rc = walLockWriter(pWal)) ){
+ if( bWriteLock
+ || SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1))
+ ){
pWal->writeLock = 1;
if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
badHdr = walIndexTryHdr(pWal, pChanged);
@@ -66668,7 +67192,8 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){
/* If the wal-index header is still malformed even while holding
** a WRITE lock, it can only mean that the header is corrupted and
** needs to be reconstructed. So run recovery to do exactly that.
- */
+ ** Disable blocking locks first. */
+ walDisableBlocking(pWal);
rc = walIndexRecover(pWal);
*pChanged = 1;
}
@@ -66879,6 +67404,37 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
}
/*
+** The final argument passed to walTryBeginRead() is of type (int*). The
+** caller should invoke walTryBeginRead as follows:
+**
+** int cnt = 0;
+** do {
+** rc = walTryBeginRead(..., &cnt);
+** }while( rc==WAL_RETRY );
+**
+** The final value of "cnt" is of no use to the caller. It is used by
+** the implementation of walTryBeginRead() as follows:
+**
+** + Each time walTryBeginRead() is called, it is incremented. Once
+** it reaches WAL_RETRY_PROTOCOL_LIMIT - indicating that walTryBeginRead()
+** has many times been invoked and failed with WAL_RETRY - walTryBeginRead()
+** returns SQLITE_PROTOCOL.
+**
+** + If SQLITE_ENABLE_SETLK_TIMEOUT is defined and walTryBeginRead() failed
+** because a blocking lock timed out (SQLITE_BUSY_TIMEOUT from the OS
+** layer), the WAL_RETRY_BLOCKED_MASK bit is set in "cnt". In this case
+** the next invocation of walTryBeginRead() may omit an expected call to
+** sqlite3OsSleep(). There has already been a delay when the previous call
+** waited on a lock.
+*/
+#define WAL_RETRY_PROTOCOL_LIMIT 100
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+# define WAL_RETRY_BLOCKED_MASK 0x10000000
+#else
+# define WAL_RETRY_BLOCKED_MASK 0
+#endif
+
+/*
** Attempt to start a read transaction. This might fail due to a race or
** other transient condition. When that happens, it returns WAL_RETRY to
** indicate to the caller that it is safe to retry immediately.
@@ -66928,13 +67484,16 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
** so it takes care to hold an exclusive lock on the corresponding
** WAL_READ_LOCK() while changing values.
*/
-static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
+static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){
volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */
u32 mxReadMark; /* Largest aReadMark[] value */
int mxI; /* Index of largest aReadMark[] value */
int i; /* Loop counter */
int rc = SQLITE_OK; /* Return code */
u32 mxFrame; /* Wal frame to lock to */
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ int nBlockTmout = 0;
+#endif
assert( pWal->readLock<0 ); /* Not currently locked */
@@ -66958,14 +67517,34 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
** so that on the 100th (and last) RETRY we delay for 323 milliseconds.
** The total delay time before giving up is less than 10 seconds.
*/
- if( cnt>5 ){
+ (*pCnt)++;
+ if( *pCnt>5 ){
int nDelay = 1; /* Pause time in microseconds */
- if( cnt>100 ){
+ int cnt = (*pCnt & ~WAL_RETRY_BLOCKED_MASK);
+ if( cnt>WAL_RETRY_PROTOCOL_LIMIT ){
VVA_ONLY( pWal->lockError = 1; )
return SQLITE_PROTOCOL;
}
- if( cnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39;
+ if( *pCnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39;
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ /* In SQLITE_ENABLE_SETLK_TIMEOUT builds, configure the file-descriptor
+ ** to block for locks for approximately nDelay us. This affects three
+ ** locks: (a) the shared lock taken on the DMS slot in os_unix.c (if
+ ** using os_unix.c), (b) the WRITER lock taken in walIndexReadHdr() if the
+ ** first attempted read fails, and (c) the shared lock taken on the
+ ** read-mark.
+ **
+ ** If the previous call failed due to an SQLITE_BUSY_TIMEOUT error,
+ ** then sleep for the minimum of 1us. The previous call already provided
+ ** an extra delay while it was blocking on the lock.
+ */
+ nBlockTmout = (nDelay+998) / 1000;
+ if( !useWal && walEnableBlockingMs(pWal, nBlockTmout) ){
+ if( *pCnt & WAL_RETRY_BLOCKED_MASK ) nDelay = 1;
+ }
+#endif
sqlite3OsSleep(pWal->pVfs, nDelay);
+ *pCnt &= ~WAL_RETRY_BLOCKED_MASK;
}
if( !useWal ){
@@ -66973,6 +67552,13 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
if( pWal->bShmUnreliable==0 ){
rc = walIndexReadHdr(pWal, pChanged);
}
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ walDisableBlocking(pWal);
+ if( rc==SQLITE_BUSY_TIMEOUT ){
+ rc = SQLITE_BUSY;
+ *pCnt |= WAL_RETRY_BLOCKED_MASK;
+ }
+#endif
if( rc==SQLITE_BUSY ){
/* If there is not a recovery running in another thread or process
** then convert BUSY errors to WAL_RETRY. If recovery is known to
@@ -67087,9 +67673,19 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT;
}
+ (void)walEnableBlockingMs(pWal, nBlockTmout);
rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
+ walDisableBlocking(pWal);
if( rc ){
- return rc==SQLITE_BUSY ? WAL_RETRY : rc;
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ if( rc==SQLITE_BUSY_TIMEOUT ){
+ *pCnt |= WAL_RETRY_BLOCKED_MASK;
+ }
+#else
+ assert( rc!=SQLITE_BUSY_TIMEOUT );
+#endif
+ assert( (rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT );
+ return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc;
}
/* Now that the read-lock has been obtained, check that neither the
** value in the aReadMark[] array or the contents of the wal-index
@@ -67277,7 +67873,7 @@ static int walBeginReadTransaction(Wal *pWal, int *pChanged){
#endif
do{
- rc = walTryBeginRead(pWal, pChanged, 0, ++cnt);
+ rc = walTryBeginRead(pWal, pChanged, 0, &cnt);
}while( rc==WAL_RETRY );
testcase( (rc&0xff)==SQLITE_BUSY );
testcase( (rc&0xff)==SQLITE_IOERR );
@@ -67458,6 +68054,7 @@ static int walFindFrame(
iRead = iFrame;
}
if( (nCollide--)==0 ){
+ *piRead = 0;
return SQLITE_CORRUPT_BKPT;
}
iKey = walNextHash(iKey);
@@ -67761,7 +68358,7 @@ static int walRestartLog(Wal *pWal){
cnt = 0;
do{
int notUsed;
- rc = walTryBeginRead(pWal, &notUsed, 1, ++cnt);
+ rc = walTryBeginRead(pWal, &notUsed, 1, &cnt);
}while( rc==WAL_RETRY );
assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */
testcase( (rc&0xff)==SQLITE_IOERR );
@@ -68182,10 +68779,9 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint(
if( pWal->readOnly ) return SQLITE_READONLY;
WALTRACE(("WAL%p: checkpoint begins\n", pWal));
- /* Enable blocking locks, if possible. If blocking locks are successfully
- ** enabled, set xBusy2=0 so that the busy-handler is never invoked. */
+ /* Enable blocking locks, if possible. */
sqlite3WalDb(pWal, db);
- (void)walEnableBlocking(pWal);
+ if( xBusy2 ) (void)walEnableBlocking(pWal);
/* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive
** "checkpoint" lock on the database file.
@@ -68226,9 +68822,14 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint(
/* Read the wal-index header. */
SEH_TRY {
if( rc==SQLITE_OK ){
+ /* For a passive checkpoint, do not re-enable blocking locks after
+ ** reading the wal-index header. A passive checkpoint should not block
+ ** or invoke the busy handler. The only lock such a checkpoint may
+ ** attempt to obtain is a lock on a read-slot, and it should give up
+ ** immediately and do a partial checkpoint if it cannot obtain it. */
walDisableBlocking(pWal);
rc = walIndexReadHdr(pWal, &isChanged);
- (void)walEnableBlocking(pWal);
+ if( eMode2!=SQLITE_CHECKPOINT_PASSIVE ) (void)walEnableBlocking(pWal);
if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
}
@@ -68565,7 +69166,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal){
** 22 1 Min embedded payload fraction (must be 32)
** 23 1 Min leaf payload fraction (must be 32)
** 24 4 File change counter
-** 28 4 Reserved for future use
+** 28 4 The size of the database in pages
** 32 4 First freelist page
** 36 4 Number of freelist pages in the file
** 40 60 15 4-byte meta values passed to higher layers
@@ -69196,7 +69797,7 @@ struct IntegrityCk {
BtShared *pBt; /* The tree being checked out */
Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */
u8 *aPgRef; /* 1 bit per page in the db (see above) */
- Pgno nPage; /* Number of pages in the database */
+ Pgno nCkPage; /* Pages in the database. 0 for partial check */
int mxErr; /* Stop accumulating errors when this reaches zero */
int nErr; /* Number of messages written to zErrMsg so far */
int rc; /* SQLITE_OK, SQLITE_NOMEM, or SQLITE_INTERRUPT */
@@ -69529,7 +70130,6 @@ SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor *pCur){
/************** End of btmutex.c *********************************************/
/************** Begin file btree.c *******************************************/
-
/*
** 2004 April 6
**
@@ -74693,7 +75293,6 @@ static int accessPayload(
assert( aWrite>=pBufStart ); /* due to (6) */
memcpy(aSave, aWrite, 4);
rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1));
- if( rc && nextPage>pBt->nPage ) rc = SQLITE_CORRUPT_BKPT;
nextPage = get4byte(aWrite);
memcpy(aWrite, aSave, 4);
}else
@@ -75813,7 +76412,10 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur){
}
pPage = pCur->pPage;
- assert( pPage->isInit );
+ if( sqlite3FaultSim(412) ) pPage->isInit = 0;
+ if( !pPage->isInit ){
+ return SQLITE_CORRUPT_BKPT;
+ }
if( !pPage->leaf ){
int idx = pCur->ix;
rc = moveToChild(pCur, get4byte(findCell(pPage, idx)));
@@ -77024,9 +77626,10 @@ static int rebuildPage(
int k; /* Current slot in pCArray->apEnd[] */
u8 *pSrcEnd; /* Current pCArray->apEnd[k] value */
+ assert( nCell>0 );
assert( i<iEnd );
j = get2byte(&aData[hdr+5]);
- if( NEVER(j>(u32)usableSize) ){ j = 0; }
+ if( j>(u32)usableSize ){ j = 0; }
memcpy(&pTmp[j], &aData[j], usableSize - j);
for(k=0; ALWAYS(k<NB*2) && pCArray->ixNx[k]<=i; k++){}
@@ -77330,6 +77933,7 @@ static int editPage(
return SQLITE_OK;
editpage_fail:
/* Unable to edit this page. Rebuild it from scratch instead. */
+ if( nNew<1 ) return SQLITE_CORRUPT_BKPT;
populateCellCache(pCArray, iNew, nNew);
return rebuildPage(pCArray, iNew, nNew, pPg);
}
@@ -79989,7 +80593,8 @@ static void checkAppendMsg(
** corresponds to page iPg is already set.
*/
static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){
- assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 );
+ assert( pCheck->aPgRef!=0 );
+ assert( iPg<=pCheck->nCkPage && sizeof(pCheck->aPgRef[0])==1 );
return (pCheck->aPgRef[iPg/8] & (1 << (iPg & 0x07)));
}
@@ -79997,7 +80602,8 @@ static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){
** Set the bit in the IntegrityCk.aPgRef[] array that corresponds to page iPg.
*/
static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){
- assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 );
+ assert( pCheck->aPgRef!=0 );
+ assert( iPg<=pCheck->nCkPage && sizeof(pCheck->aPgRef[0])==1 );
pCheck->aPgRef[iPg/8] |= (1 << (iPg & 0x07));
}
@@ -80011,7 +80617,7 @@ static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){
** Also check that the page number is in bounds.
*/
static int checkRef(IntegrityCk *pCheck, Pgno iPage){
- if( iPage>pCheck->nPage || iPage==0 ){
+ if( iPage>pCheck->nCkPage || iPage==0 ){
checkAppendMsg(pCheck, "invalid page number %u", iPage);
return 1;
}
@@ -80238,6 +80844,7 @@ static int checkTreePage(
if( (rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0 ){
checkAppendMsg(pCheck,
"unable to get the page. error code=%d", rc);
+ if( rc==SQLITE_IOERR_NOMEM ) pCheck->rc = SQLITE_NOMEM;
goto end_of_check;
}
@@ -80508,15 +81115,15 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck(
sCheck.db = db;
sCheck.pBt = pBt;
sCheck.pPager = pBt->pPager;
- sCheck.nPage = btreePagecount(sCheck.pBt);
+ sCheck.nCkPage = btreePagecount(sCheck.pBt);
sCheck.mxErr = mxErr;
sqlite3StrAccumInit(&sCheck.errMsg, 0, zErr, sizeof(zErr), SQLITE_MAX_LENGTH);
sCheck.errMsg.printfFlags = SQLITE_PRINTF_INTERNAL;
- if( sCheck.nPage==0 ){
+ if( sCheck.nCkPage==0 ){
goto integrity_ck_cleanup;
}
- sCheck.aPgRef = sqlite3MallocZero((sCheck.nPage / 8)+ 1);
+ sCheck.aPgRef = sqlite3MallocZero((sCheck.nCkPage / 8)+ 1);
if( !sCheck.aPgRef ){
checkOom(&sCheck);
goto integrity_ck_cleanup;
@@ -80528,7 +81135,7 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck(
}
i = PENDING_BYTE_PAGE(pBt);
- if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i);
+ if( i<=sCheck.nCkPage ) setPageReferenced(&sCheck, i);
/* Check the integrity of the freelist
*/
@@ -80579,7 +81186,7 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck(
/* Make sure every page in the file is referenced
*/
if( !bPartial ){
- for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
+ for(i=1; i<=sCheck.nCkPage && sCheck.mxErr; i++){
#ifdef SQLITE_OMIT_AUTOVACUUM
if( getPageReferenced(&sCheck, i)==0 ){
checkAppendMsg(&sCheck, "Page %u: never used", i);
@@ -82020,7 +82627,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemZeroTerminateIfAble(Mem *pMem){
pMem->flags |= MEM_Term;
return;
}
- if( pMem->xDel==(void(*)(void*))sqlite3RCStrUnref ){
+ if( pMem->xDel==sqlite3RCStrUnref ){
/* Blindly assume that all RCStr objects are zero-terminated */
pMem->flags |= MEM_Term;
return;
@@ -83199,7 +83806,7 @@ static int valueFromFunction(
#endif
assert( pFunc );
if( (pFunc->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0
- || (pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)
+ || (pFunc->funcFlags & (SQLITE_FUNC_NEEDCOLL|SQLITE_FUNC_RUNONLY))!=0
){
return SQLITE_OK;
}
@@ -83400,6 +84007,7 @@ static int valueFromExpr(
if( pVal ){
pVal->flags = MEM_Int;
pVal->u.i = pExpr->u.zToken[4]==0;
+ sqlite3ValueApplyAffinity(pVal, affinity, enc);
}
}
@@ -83922,10 +84530,11 @@ static int growOpArray(Vdbe *v, int nOp){
** sqlite3CantopenError(lineno)
*/
static void test_addop_breakpoint(int pc, Op *pOp){
- static int n = 0;
+ static u64 n = 0;
(void)pc;
(void)pOp;
n++;
+ if( n==LARGEST_UINT64 ) abort(); /* so that n is used, preventing a warning */
}
#endif
@@ -84713,6 +85322,10 @@ SQLITE_PRIVATE void sqlite3VdbeNoJumpsOutsideSubrtn(
int iDest = pOp->p2; /* Jump destination */
if( iDest==0 ) continue;
if( pOp->opcode==OP_Gosub ) continue;
+ if( pOp->p3==20230325 && pOp->opcode==OP_NotNull ){
+ /* This is a deliberately taken illegal branch. tag-20230325-2 */
+ continue;
+ }
if( iDest<0 ){
int j = ADDR(iDest);
assert( j>=0 );
@@ -85106,6 +85719,10 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4);
break;
}
+ case P4_TABLEREF: {
+ if( db->pnBytesFreed==0 ) sqlite3DeleteTable(db, (Table*)p4);
+ break;
+ }
}
}
@@ -85233,7 +85850,7 @@ static void SQLITE_NOINLINE vdbeChangeP4Full(
int n
){
if( pOp->p4type ){
- freeP4(p->db, pOp->p4type, pOp->p4.p);
+ assert( pOp->p4type > P4_FREE_IF_LE );
pOp->p4type = 0;
pOp->p4.p = 0;
}
@@ -88172,20 +88789,33 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3BlobCompare(const Mem *pB1, const Mem
return n1 - n2;
}
+/* The following two functions are used only within testcase() to prove
+** test coverage. These functions do no exist for production builds.
+** We must use separate SQLITE_NOINLINE functions here, since otherwise
+** optimizer code movement causes gcov to become very confused.
+*/
+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG)
+static int SQLITE_NOINLINE doubleLt(double a, double b){ return a<b; }
+static int SQLITE_NOINLINE doubleEq(double a, double b){ return a==b; }
+#endif
+
/*
** Do a comparison between a 64-bit signed integer and a 64-bit floating-point
** number. Return negative, zero, or positive if the first (i64) is less than,
** equal to, or greater than the second (double).
*/
SQLITE_PRIVATE int sqlite3IntFloatCompare(i64 i, double r){
- if( sizeof(LONGDOUBLE_TYPE)>8 ){
+ if( sqlite3IsNaN(r) ){
+ /* SQLite considers NaN to be a NULL. And all integer values are greater
+ ** than NULL */
+ return 1;
+ }
+ if( sqlite3Config.bUseLongDouble ){
LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i;
testcase( x<r );
testcase( x>r );
testcase( x==r );
- if( x<r ) return -1;
- if( x>r ) return +1; /*NO_TEST*/ /* work around bugs in gcov */
- return 0; /*NO_TEST*/ /* work around bugs in gcov */
+ return (x<r) ? -1 : (x>r);
}else{
i64 y;
double s;
@@ -88195,9 +88825,10 @@ SQLITE_PRIVATE int sqlite3IntFloatCompare(i64 i, double r){
if( i<y ) return -1;
if( i>y ) return +1;
s = (double)i;
- if( s<r ) return -1;
- if( s>r ) return +1;
- return 0;
+ testcase( doubleLt(s,r) );
+ testcase( doubleLt(r,s) );
+ testcase( doubleEq(r,s) );
+ return (s<r) ? -1 : (s>r);
}
}
@@ -89342,7 +89973,15 @@ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt *pStmt){
int rc = SQLITE_OK;
Vdbe *p = (Vdbe*)pStmt;
#if SQLITE_THREADSAFE
- sqlite3_mutex *mutex = ((Vdbe*)pStmt)->db->mutex;
+ sqlite3_mutex *mutex;
+#endif
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pStmt==0 ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+#if SQLITE_THREADSAFE
+ mutex = p->db->mutex;
#endif
sqlite3_mutex_enter(mutex);
for(i=0; i<p->nVar; i++){
@@ -89565,7 +90204,7 @@ SQLITE_API void sqlite3_value_free(sqlite3_value *pOld){
** is too big or if an OOM occurs.
**
** The invokeValueDestructor(P,X) routine invokes destructor function X()
-** on value P is not going to be used and need to be destroyed.
+** on value P if P is not going to be used and need to be destroyed.
*/
static void setResultStrOrError(
sqlite3_context *pCtx, /* Function context */
@@ -89595,7 +90234,7 @@ static void setResultStrOrError(
static int invokeValueDestructor(
const void *p, /* Value to destroy */
void (*xDel)(void*), /* The destructor */
- sqlite3_context *pCtx /* Set a SQLITE_TOOBIG error if no NULL */
+ sqlite3_context *pCtx /* Set a SQLITE_TOOBIG error if not NULL */
){
assert( xDel!=SQLITE_DYNAMIC );
if( xDel==0 ){
@@ -89605,7 +90244,14 @@ static int invokeValueDestructor(
}else{
xDel((void*)p);
}
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx!=0 ){
+ sqlite3_result_error_toobig(pCtx);
+ }
+#else
+ assert( pCtx!=0 );
sqlite3_result_error_toobig(pCtx);
+#endif
return SQLITE_TOOBIG;
}
SQLITE_API void sqlite3_result_blob(
@@ -89614,6 +90260,12 @@ SQLITE_API void sqlite3_result_blob(
int n,
void (*xDel)(void *)
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 || n<0 ){
+ invokeValueDestructor(z, xDel, pCtx);
+ return;
+ }
+#endif
assert( n>=0 );
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
setResultStrOrError(pCtx, z, n, 0, xDel);
@@ -89624,8 +90276,14 @@ SQLITE_API void sqlite3_result_blob64(
sqlite3_uint64 n,
void (*xDel)(void *)
){
- assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
assert( xDel!=SQLITE_DYNAMIC );
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ){
+ invokeValueDestructor(z, xDel, 0);
+ return;
+ }
+#endif
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
if( n>0x7fffffff ){
(void)invokeValueDestructor(z, xDel, pCtx);
}else{
@@ -89633,30 +90291,48 @@ SQLITE_API void sqlite3_result_blob64(
}
}
SQLITE_API void sqlite3_result_double(sqlite3_context *pCtx, double rVal){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetDouble(pCtx->pOut, rVal);
}
SQLITE_API void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
pCtx->isError = SQLITE_ERROR;
sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF8, SQLITE_TRANSIENT);
}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
pCtx->isError = SQLITE_ERROR;
sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT);
}
#endif
SQLITE_API void sqlite3_result_int(sqlite3_context *pCtx, int iVal){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetInt64(pCtx->pOut, (i64)iVal);
}
SQLITE_API void sqlite3_result_int64(sqlite3_context *pCtx, i64 iVal){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetInt64(pCtx->pOut, iVal);
}
SQLITE_API void sqlite3_result_null(sqlite3_context *pCtx){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetNull(pCtx->pOut);
}
@@ -89666,14 +90342,37 @@ SQLITE_API void sqlite3_result_pointer(
const char *zPType,
void (*xDestructor)(void*)
){
- Mem *pOut = pCtx->pOut;
+ Mem *pOut;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ){
+ invokeValueDestructor(pPtr, xDestructor, 0);
+ return;
+ }
+#endif
+ pOut = pCtx->pOut;
assert( sqlite3_mutex_held(pOut->db->mutex) );
sqlite3VdbeMemRelease(pOut);
pOut->flags = MEM_Null;
sqlite3VdbeMemSetPointer(pOut, pPtr, zPType, xDestructor);
}
SQLITE_API void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){
- Mem *pOut = pCtx->pOut;
+ Mem *pOut;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
+#if defined(SQLITE_STRICT_SUBTYPE) && SQLITE_STRICT_SUBTYPE+0!=0
+ if( pCtx->pFunc!=0
+ && (pCtx->pFunc->funcFlags & SQLITE_RESULT_SUBTYPE)==0
+ ){
+ char zErr[200];
+ sqlite3_snprintf(sizeof(zErr), zErr,
+ "misuse of sqlite3_result_subtype() by %s()",
+ pCtx->pFunc->zName);
+ sqlite3_result_error(pCtx, zErr, -1);
+ return;
+ }
+#endif /* SQLITE_STRICT_SUBTYPE */
+ pOut = pCtx->pOut;
assert( sqlite3_mutex_held(pOut->db->mutex) );
pOut->eSubtype = eSubtype & 0xff;
pOut->flags |= MEM_Subtype;
@@ -89684,6 +90383,12 @@ SQLITE_API void sqlite3_result_text(
int n,
void (*xDel)(void *)
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ){
+ invokeValueDestructor(z, xDel, 0);
+ return;
+ }
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
setResultStrOrError(pCtx, z, n, SQLITE_UTF8, xDel);
}
@@ -89694,6 +90399,12 @@ SQLITE_API void sqlite3_result_text64(
void (*xDel)(void *),
unsigned char enc
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ){
+ invokeValueDestructor(z, xDel, 0);
+ return;
+ }
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
assert( xDel!=SQLITE_DYNAMIC );
if( enc!=SQLITE_UTF8 ){
@@ -89737,7 +90448,16 @@ SQLITE_API void sqlite3_result_text16le(
}
#endif /* SQLITE_OMIT_UTF16 */
SQLITE_API void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){
- Mem *pOut = pCtx->pOut;
+ Mem *pOut;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+ if( pValue==0 ){
+ sqlite3_result_null(pCtx);
+ return;
+ }
+#endif
+ pOut = pCtx->pOut;
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemCopy(pOut, pValue);
sqlite3VdbeChangeEncoding(pOut, pCtx->enc);
@@ -89749,7 +90469,12 @@ SQLITE_API void sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){
sqlite3_result_zeroblob64(pCtx, n>0 ? n : 0);
}
SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context *pCtx, u64 n){
- Mem *pOut = pCtx->pOut;
+ Mem *pOut;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return SQLITE_MISUSE_BKPT;
+#endif
+ pOut = pCtx->pOut;
assert( sqlite3_mutex_held(pOut->db->mutex) );
if( n>(u64)pOut->db->aLimit[SQLITE_LIMIT_LENGTH] ){
sqlite3_result_error_toobig(pCtx);
@@ -89763,6 +90488,9 @@ SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context *pCtx, u64 n){
#endif
}
SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
pCtx->isError = errCode ? errCode : -1;
#ifdef SQLITE_DEBUG
if( pCtx->pVdbe ) pCtx->pVdbe->rcApp = errCode;
@@ -89775,6 +90503,9 @@ SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
/* Force an SQLITE_TOOBIG error. */
SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
pCtx->isError = SQLITE_TOOBIG;
sqlite3VdbeMemSetStr(pCtx->pOut, "string or blob too big", -1,
@@ -89783,6 +90514,9 @@ SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){
/* An SQLITE_NOMEM error. */
SQLITE_API void sqlite3_result_error_nomem(sqlite3_context *pCtx){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetNull(pCtx->pOut);
pCtx->isError = SQLITE_NOMEM_BKPT;
@@ -90035,6 +90769,9 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){
** pointer to it.
*/
SQLITE_API void *sqlite3_user_data(sqlite3_context *p){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 ) return 0;
+#endif
assert( p && p->pFunc );
return p->pFunc->pUserData;
}
@@ -90050,7 +90787,11 @@ SQLITE_API void *sqlite3_user_data(sqlite3_context *p){
** application defined function.
*/
SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 ) return 0;
+#else
assert( p && p->pOut );
+#endif
return p->pOut->db;
}
@@ -90069,7 +90810,11 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){
** value, as a signal to the xUpdate routine that the column is unchanged.
*/
SQLITE_API int sqlite3_vtab_nochange(sqlite3_context *p){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 ) return 0;
+#else
assert( p );
+#endif
return sqlite3_value_nochange(p->pOut);
}
@@ -90097,7 +90842,7 @@ static int valueFromValueList(
ValueList *pRhs;
*ppOut = 0;
- if( pVal==0 ) return SQLITE_MISUSE;
+ if( pVal==0 ) return SQLITE_MISUSE_BKPT;
if( (pVal->flags & MEM_Dyn)==0 || pVal->xDel!=sqlite3VdbeValueListFree ){
return SQLITE_ERROR;
}else{
@@ -90228,6 +90973,9 @@ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){
SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){
AuxData *pAuxData;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return 0;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
#if SQLITE_ENABLE_STAT4
if( pCtx->pVdbe==0 ) return 0;
@@ -90260,8 +91008,12 @@ SQLITE_API void sqlite3_set_auxdata(
void (*xDelete)(void*)
){
AuxData *pAuxData;
- Vdbe *pVdbe = pCtx->pVdbe;
+ Vdbe *pVdbe;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
+ pVdbe= pCtx->pVdbe;
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
#ifdef SQLITE_ENABLE_STAT4
if( pVdbe==0 ) goto failed;
@@ -90698,7 +91450,7 @@ static int vdbeUnbind(Vdbe *p, unsigned int i){
}
sqlite3_mutex_enter(p->db->mutex);
if( p->eVdbeState!=VDBE_READY_STATE ){
- sqlite3Error(p->db, SQLITE_MISUSE);
+ sqlite3Error(p->db, SQLITE_MISUSE_BKPT);
sqlite3_mutex_leave(p->db->mutex);
sqlite3_log(SQLITE_MISUSE,
"bind on a busy prepared statement: [%s]", p->zSql);
@@ -90927,6 +91679,9 @@ SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){
SQLITE_API int sqlite3_bind_zeroblob64(sqlite3_stmt *pStmt, int i, sqlite3_uint64 n){
int rc;
Vdbe *p = (Vdbe *)pStmt;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(p->db->mutex);
if( n>(u64)p->db->aLimit[SQLITE_LIMIT_LENGTH] ){
rc = SQLITE_TOOBIG;
@@ -91053,6 +91808,9 @@ SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt){
SQLITE_API int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode){
Vdbe *v = (Vdbe*)pStmt;
int rc;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pStmt==0 ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(v->db->mutex);
if( ((int)v->explain)==eMode ){
rc = SQLITE_OK;
@@ -91219,10 +91977,16 @@ static UnpackedRecord *vdbeUnpackRecord(
** a field of the row currently being updated or deleted.
*/
SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
- PreUpdate *p = db->pPreUpdate;
+ PreUpdate *p;
Mem *pMem;
int rc = SQLITE_OK;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( db==0 || ppValue==0 ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+ p = db->pPreUpdate;
/* Test that this call is being made from within an SQLITE_DELETE or
** SQLITE_UPDATE pre-update callback, and that iIdx is within range. */
if( !p || p->op==SQLITE_INSERT ){
@@ -91283,7 +92047,12 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppVa
** the number of columns in the row being updated, deleted or inserted.
*/
SQLITE_API int sqlite3_preupdate_count(sqlite3 *db){
- PreUpdate *p = db->pPreUpdate;
+ PreUpdate *p;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ p = db!=0 ? db->pPreUpdate : 0;
+#else
+ p = db->pPreUpdate;
+#endif
return (p ? p->keyinfo.nKeyField : 0);
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
@@ -91301,7 +92070,12 @@ SQLITE_API int sqlite3_preupdate_count(sqlite3 *db){
** or SET DEFAULT action is considered a trigger.
*/
SQLITE_API int sqlite3_preupdate_depth(sqlite3 *db){
- PreUpdate *p = db->pPreUpdate;
+ PreUpdate *p;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ p = db!=0 ? db->pPreUpdate : 0;
+#else
+ p = db->pPreUpdate;
+#endif
return (p ? p->v->nFrame : 0);
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
@@ -91312,7 +92086,12 @@ SQLITE_API int sqlite3_preupdate_depth(sqlite3 *db){
** only.
*/
SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *db){
- PreUpdate *p = db->pPreUpdate;
+ PreUpdate *p;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ p = db!=0 ? db->pPreUpdate : 0;
+#else
+ p = db->pPreUpdate;
+#endif
return (p ? p->iBlobWrite : -1);
}
#endif
@@ -91323,10 +92102,16 @@ SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *db){
** a field of the row currently being updated or inserted.
*/
SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
- PreUpdate *p = db->pPreUpdate;
+ PreUpdate *p;
int rc = SQLITE_OK;
Mem *pMem;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( db==0 || ppValue==0 ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+ p = db->pPreUpdate;
if( !p || p->op==SQLITE_DELETE ){
rc = SQLITE_MISUSE_BKPT;
goto preupdate_new_out;
@@ -91405,11 +92190,20 @@ SQLITE_API int sqlite3_stmt_scanstatus_v2(
void *pOut /* OUT: Write the answer here */
){
Vdbe *p = (Vdbe*)pStmt;
- VdbeOp *aOp = p->aOp;
- int nOp = p->nOp;
+ VdbeOp *aOp;
+ int nOp;
ScanStatus *pScan = 0;
int idx;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 || pOut==0
+ || iScanStatusOp<SQLITE_SCANSTAT_NLOOP
+ || iScanStatusOp>SQLITE_SCANSTAT_NCYCLE ){
+ return 1;
+ }
+#endif
+ aOp = p->aOp;
+ nOp = p->nOp;
if( p->pFrame ){
VdbeFrame *pFrame;
for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
@@ -91556,7 +92350,7 @@ SQLITE_API int sqlite3_stmt_scanstatus(
SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){
Vdbe *p = (Vdbe*)pStmt;
int ii;
- for(ii=0; ii<p->nOp; ii++){
+ for(ii=0; p!=0 && ii<p->nOp; ii++){
Op *pOp = &p->aOp[ii];
pOp->nExec = 0;
pOp->nCycle = 0;
@@ -91895,11 +92689,12 @@ SQLITE_API int sqlite3_found_count = 0;
** sqlite3CantopenError(lineno)
*/
static void test_trace_breakpoint(int pc, Op *pOp, Vdbe *v){
- static int n = 0;
+ static u64 n = 0;
(void)pc;
(void)pOp;
(void)v;
n++;
+ if( n==LARGEST_UINT64 ) abort(); /* So that n is used, preventing a warning */
}
#endif
@@ -92525,11 +93320,11 @@ static SQLITE_NOINLINE int vdbeColumnFromOverflow(
sqlite3RCStrRef(pBuf);
if( t&1 ){
rc = sqlite3VdbeMemSetStr(pDest, pBuf, len, encoding,
- (void(*)(void*))sqlite3RCStrUnref);
+ sqlite3RCStrUnref);
pDest->flags |= MEM_Term;
}else{
rc = sqlite3VdbeMemSetStr(pDest, pBuf, len, 0,
- (void(*)(void*))sqlite3RCStrUnref);
+ sqlite3RCStrUnref);
}
}else{
rc = sqlite3VdbeMemFromBtree(pC->uc.pCursor, iOffset, len, pDest);
@@ -93796,7 +94591,7 @@ case OP_AddImm: { /* in1 */
pIn1 = &aMem[pOp->p1];
memAboutToChange(p, pIn1);
sqlite3VdbeMemIntegerify(pIn1);
- pIn1->u.i += pOp->p2;
+ *(u64*)&pIn1->u.i += (u64)pOp->p2;
break;
}
@@ -95404,7 +96199,6 @@ case OP_MakeRecord: {
/* NULL value. No change in zPayload */
}else{
u64 v;
- u32 i;
if( serial_type==7 ){
assert( sizeof(v)==sizeof(pRec->u.r) );
memcpy(&v, &pRec->u.r, sizeof(v));
@@ -95412,12 +96206,17 @@ case OP_MakeRecord: {
}else{
v = pRec->u.i;
}
- len = i = sqlite3SmallTypeSizes[serial_type];
- assert( i>0 );
- while( 1 /*exit-by-break*/ ){
- zPayload[--i] = (u8)(v&0xFF);
- if( i==0 ) break;
- v >>= 8;
+ len = sqlite3SmallTypeSizes[serial_type];
+ assert( len>=1 && len<=8 && len!=5 && len!=7 );
+ switch( len ){
+ default: zPayload[7] = (u8)(v&0xff); v >>= 8;
+ zPayload[6] = (u8)(v&0xff); v >>= 8;
+ case 6: zPayload[5] = (u8)(v&0xff); v >>= 8;
+ zPayload[4] = (u8)(v&0xff); v >>= 8;
+ case 4: zPayload[3] = (u8)(v&0xff); v >>= 8;
+ case 3: zPayload[2] = (u8)(v&0xff); v >>= 8;
+ case 2: zPayload[1] = (u8)(v&0xff); v >>= 8;
+ case 1: zPayload[0] = (u8)(v&0xff);
}
zPayload += len;
}
@@ -97534,8 +98333,13 @@ case OP_RowCell: {
** the "primary" delete. The others are all on OPFLAG_FORDELETE
** cursors or else are marked with the AUXDELETE flag.
**
-** If the OPFLAG_NCHANGE flag of P2 (NB: P2 not P5) is set, then the row
-** change count is incremented (otherwise not).
+** If the OPFLAG_NCHANGE (0x01) flag of P2 (NB: P2 not P5) is set, then
+** the row change count is incremented (otherwise not).
+**
+** If the OPFLAG_ISNOOP (0x40) flag of P2 (not P5!) is set, then the
+** pre-update-hook for deletes is run, but the btree is otherwise unchanged.
+** This happens when the OP_Delete is to be shortly followed by an OP_Insert
+** with the same key, causing the btree entry to be overwritten.
**
** P1 must not be pseudo-table. It has to be a real table with
** multiple rows.
@@ -98660,13 +99464,41 @@ case OP_CreateBtree: { /* out2 */
/* Opcode: SqlExec * * * P4 *
**
** Run the SQL statement or statements specified in the P4 string.
+** Disable Auth and Trace callbacks while those statements are running if
+** P1 is true.
*/
case OP_SqlExec: {
+ char *zErr;
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ sqlite3_xauth xAuth;
+#endif
+ u8 mTrace;
+
sqlite3VdbeIncrWriteCounter(p, 0);
db->nSqlExec++;
- rc = sqlite3_exec(db, pOp->p4.z, 0, 0, 0);
+ zErr = 0;
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ xAuth = db->xAuth;
+#endif
+ mTrace = db->mTrace;
+ if( pOp->p1 ){
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ db->xAuth = 0;
+#endif
+ db->mTrace = 0;
+ }
+ rc = sqlite3_exec(db, pOp->p4.z, 0, 0, &zErr);
db->nSqlExec--;
- if( rc ) goto abort_due_to_error;
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ db->xAuth = xAuth;
+#endif
+ db->mTrace = mTrace;
+ if( zErr || rc ){
+ sqlite3VdbeError(p, "%s", zErr);
+ sqlite3_free(zErr);
+ if( rc==SQLITE_NOMEM ) goto no_mem;
+ goto abort_due_to_error;
+ }
break;
}
@@ -99888,6 +100720,52 @@ case OP_VOpen: { /* ncycle */
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifndef SQLITE_OMIT_VIRTUALTABLE
+/* Opcode: VCheck P1 P2 P3 P4 *
+**
+** P4 is a pointer to a Table object that is a virtual table in schema P1
+** that supports the xIntegrity() method. This opcode runs the xIntegrity()
+** method for that virtual table, using P3 as the integer argument. If
+** an error is reported back, the table name is prepended to the error
+** message and that message is stored in P2. If no errors are seen,
+** register P2 is set to NULL.
+*/
+case OP_VCheck: { /* out2 */
+ Table *pTab;
+ sqlite3_vtab *pVtab;
+ const sqlite3_module *pModule;
+ char *zErr = 0;
+
+ pOut = &aMem[pOp->p2];
+ sqlite3VdbeMemSetNull(pOut); /* Innocent until proven guilty */
+ assert( pOp->p4type==P4_TABLEREF );
+ pTab = pOp->p4.pTab;
+ assert( pTab!=0 );
+ assert( pTab->nTabRef>0 );
+ assert( IsVirtual(pTab) );
+ if( pTab->u.vtab.p==0 ) break;
+ pVtab = pTab->u.vtab.p->pVtab;
+ assert( pVtab!=0 );
+ pModule = pVtab->pModule;
+ assert( pModule!=0 );
+ assert( pModule->iVersion>=4 );
+ assert( pModule->xIntegrity!=0 );
+ sqlite3VtabLock(pTab->u.vtab.p);
+ assert( pOp->p1>=0 && pOp->p1<db->nDb );
+ rc = pModule->xIntegrity(pVtab, db->aDb[pOp->p1].zDbSName, pTab->zName,
+ pOp->p3, &zErr);
+ sqlite3VtabUnlock(pTab->u.vtab.p);
+ if( rc ){
+ sqlite3_free(zErr);
+ goto abort_due_to_error;
+ }
+ if( zErr ){
+ sqlite3VdbeMemSetStr(pOut, zErr, -1, SQLITE_UTF8, sqlite3_free);
+ }
+ break;
+}
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Opcode: VInitIn P1 P2 P3 * *
** Synopsis: r[P2]=ValueList(P1,P3)
**
@@ -100000,6 +100878,7 @@ case OP_VColumn: { /* ncycle */
const sqlite3_module *pModule;
Mem *pDest;
sqlite3_context sContext;
+ FuncDef nullFunc;
VdbeCursor *pCur = p->apCsr[pOp->p1];
assert( pCur!=0 );
@@ -100017,6 +100896,9 @@ case OP_VColumn: { /* ncycle */
memset(&sContext, 0, sizeof(sContext));
sContext.pOut = pDest;
sContext.enc = encoding;
+ nullFunc.pUserData = 0;
+ nullFunc.funcFlags = SQLITE_RESULT_SUBTYPE;
+ sContext.pFunc = &nullFunc;
assert( pOp->p5==OPFLAG_NOCHNG || pOp->p5==0 );
if( pOp->p5 & OPFLAG_NOCHNG ){
sqlite3VdbeMemSetNull(pDest);
@@ -100349,6 +101231,42 @@ case OP_ClrSubtype: { /* in1 */
break;
}
+/* Opcode: GetSubtype P1 P2 * * *
+** Synopsis: r[P2] = r[P1].subtype
+**
+** Extract the subtype value from register P1 and write that subtype
+** into register P2. If P1 has no subtype, then P1 gets a NULL.
+*/
+case OP_GetSubtype: { /* in1 out2 */
+ pIn1 = &aMem[pOp->p1];
+ pOut = &aMem[pOp->p2];
+ if( pIn1->flags & MEM_Subtype ){
+ sqlite3VdbeMemSetInt64(pOut, pIn1->eSubtype);
+ }else{
+ sqlite3VdbeMemSetNull(pOut);
+ }
+ break;
+}
+
+/* Opcode: SetSubtype P1 P2 * * *
+** Synopsis: r[P2].subtype = r[P1]
+**
+** Set the subtype value of register P2 to the integer from register P1.
+** If P1 is NULL, clear the subtype from p2.
+*/
+case OP_SetSubtype: { /* in1 out2 */
+ pIn1 = &aMem[pOp->p1];
+ pOut = &aMem[pOp->p2];
+ if( pIn1->flags & MEM_Null ){
+ pOut->flags &= ~MEM_Subtype;
+ }else{
+ assert( pIn1->flags & MEM_Int );
+ pOut->flags |= MEM_Subtype;
+ pOut->eSubtype = (u8)(pIn1->u.i & 0xff);
+ }
+ break;
+}
+
/* Opcode: FilterAdd P1 * P3 P4 *
** Synopsis: filter(P1) += key(P3@P4)
**
@@ -100833,8 +101751,7 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
/* Set the value of register r[1] in the SQL statement to integer iRow.
** This is done directly as a performance optimization
*/
- v->aMem[1].flags = MEM_Int;
- v->aMem[1].u.i = iRow;
+ sqlite3VdbeMemSetInt64(&v->aMem[1], iRow);
/* If the statement has been run before (and is paused at the OP_ResultRow)
** then back it up to the point where it does the OP_NotExists. This could
@@ -100917,7 +101834,7 @@ SQLITE_API int sqlite3_blob_open(
#endif
*ppBlob = 0;
#ifdef SQLITE_ENABLE_API_ARMOR
- if( !sqlite3SafetyCheckOk(db) || zTable==0 ){
+ if( !sqlite3SafetyCheckOk(db) || zTable==0 || zColumn==0 ){
return SQLITE_MISUSE_BKPT;
}
#endif
@@ -101479,7 +102396,7 @@ struct SorterFile {
struct SorterList {
SorterRecord *pList; /* Linked list of records */
u8 *aMemory; /* If non-NULL, bulk memory to hold pList */
- int szPMA; /* Size of pList as PMA in bytes */
+ i64 szPMA; /* Size of pList as PMA in bytes */
};
/*
@@ -101588,10 +102505,10 @@ typedef int (*SorterCompare)(SortSubtask*,int*,const void*,int,const void*,int);
struct SortSubtask {
SQLiteThread *pThread; /* Background thread, if any */
int bDone; /* Set if thread is finished but not joined */
+ int nPMA; /* Number of PMAs currently in file */
VdbeSorter *pSorter; /* Sorter that owns this sub-task */
UnpackedRecord *pUnpacked; /* Space to unpack a record */
SorterList list; /* List for thread to write to a PMA */
- int nPMA; /* Number of PMAs currently in file */
SorterCompare xCompare; /* Compare function to use */
SorterFile file; /* Temp file for level-0 PMAs */
SorterFile file2; /* Space for other PMAs */
@@ -103065,8 +103982,8 @@ SQLITE_PRIVATE int sqlite3VdbeSorterWrite(
int rc = SQLITE_OK; /* Return Code */
SorterRecord *pNew; /* New list element */
int bFlush; /* True to flush contents of memory to PMA */
- int nReq; /* Bytes of memory required */
- int nPMA; /* Bytes of PMA space required */
+ i64 nReq; /* Bytes of memory required */
+ i64 nPMA; /* Bytes of PMA space required */
int t; /* serial type of first record field */
assert( pCsr->eCurType==CURTYPE_SORTER );
@@ -104490,7 +105407,8 @@ static sqlite3_module bytecodevtabModule = {
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
- /* xShadowName */ 0
+ /* xShadowName */ 0,
+ /* xIntegrity */ 0
};
@@ -105319,21 +106237,36 @@ static void resolveAlias(
}
/*
-** Subqueries stores the original database, table and column names for their
-** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN".
-** Check to see if the zSpan given to this routine matches the zDb, zTab,
-** and zCol. If any of zDb, zTab, and zCol are NULL then those fields will
-** match anything.
+** Subqueries store the original database, table and column names for their
+** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN",
+** and mark the expression-list item by setting ExprList.a[].fg.eEName
+** to ENAME_TAB.
+**
+** Check to see if the zSpan/eEName of the expression-list item passed to this
+** routine matches the zDb, zTab, and zCol. If any of zDb, zTab, and zCol are
+** NULL then those fields will match anything. Return true if there is a match,
+** or false otherwise.
+**
+** SF_NestedFrom subqueries also store an entry for the implicit rowid (or
+** _rowid_, or oid) column by setting ExprList.a[].fg.eEName to ENAME_ROWID,
+** and setting zSpan to "DATABASE.TABLE.<rowid-alias>". This type of pItem
+** argument matches if zCol is a rowid alias. If it is not NULL, (*pbRowid)
+** is set to 1 if there is this kind of match.
*/
SQLITE_PRIVATE int sqlite3MatchEName(
const struct ExprList_item *pItem,
const char *zCol,
const char *zTab,
- const char *zDb
+ const char *zDb,
+ int *pbRowid
){
int n;
const char *zSpan;
- if( pItem->fg.eEName!=ENAME_TAB ) return 0;
+ int eEName = pItem->fg.eEName;
+ if( eEName!=ENAME_TAB && (eEName!=ENAME_ROWID || NEVER(pbRowid==0)) ){
+ return 0;
+ }
+ assert( pbRowid==0 || *pbRowid==0 );
zSpan = pItem->zEName;
for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){}
if( zDb && (sqlite3StrNICmp(zSpan, zDb, n)!=0 || zDb[n]!=0) ){
@@ -105345,9 +106278,11 @@ SQLITE_PRIVATE int sqlite3MatchEName(
return 0;
}
zSpan += n+1;
- if( zCol && sqlite3StrICmp(zSpan, zCol)!=0 ){
- return 0;
+ if( zCol ){
+ if( eEName==ENAME_TAB && sqlite3StrICmp(zSpan, zCol)!=0 ) return 0;
+ if( eEName==ENAME_ROWID && sqlite3IsRowid(zCol)==0 ) return 0;
}
+ if( eEName==ENAME_ROWID ) *pbRowid = 1;
return 1;
}
@@ -105380,6 +106315,7 @@ SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr *pExpr){
assert( ExprUseYTab(pExpr) );
pExTab = pExpr->y.pTab;
assert( pExTab!=0 );
+ assert( n < pExTab->nCol );
if( (pExTab->tabFlags & TF_HasGenerated)!=0
&& (pExTab->aCol[n].colFlags & COLFLAG_GENERATED)!=0
){
@@ -105480,7 +106416,7 @@ static int lookupName(
){
int i, j; /* Loop counters */
int cnt = 0; /* Number of matching column names */
- int cntTab = 0; /* Number of matching table names */
+ int cntTab = 0; /* Number of potential "rowid" matches */
int nSubquery = 0; /* How many levels of subquery */
sqlite3 *db = pParse->db; /* The database connection */
SrcItem *pItem; /* Use for looping over pSrcList items */
@@ -105557,39 +106493,49 @@ static int lookupName(
assert( pEList!=0 );
assert( pEList->nExpr==pTab->nCol );
for(j=0; j<pEList->nExpr; j++){
- if( !sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb) ){
+ int bRowid = 0; /* True if possible rowid match */
+ if( !sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb, &bRowid) ){
continue;
}
- if( cnt>0 ){
- if( pItem->fg.isUsing==0
- || sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0
- ){
- /* Two or more tables have the same column name which is
- ** not joined by USING. This is an error. Signal as much
- ** by clearing pFJMatch and letting cnt go above 1. */
- sqlite3ExprListDelete(db, pFJMatch);
- pFJMatch = 0;
- }else
- if( (pItem->fg.jointype & JT_RIGHT)==0 ){
- /* An INNER or LEFT JOIN. Use the left-most table */
- continue;
- }else
- if( (pItem->fg.jointype & JT_LEFT)==0 ){
- /* A RIGHT JOIN. Use the right-most table */
- cnt = 0;
- sqlite3ExprListDelete(db, pFJMatch);
- pFJMatch = 0;
- }else{
- /* For a FULL JOIN, we must construct a coalesce() func */
- extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn);
+ if( bRowid==0 ){
+ if( cnt>0 ){
+ if( pItem->fg.isUsing==0
+ || sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0
+ ){
+ /* Two or more tables have the same column name which is
+ ** not joined by USING. This is an error. Signal as much
+ ** by clearing pFJMatch and letting cnt go above 1. */
+ sqlite3ExprListDelete(db, pFJMatch);
+ pFJMatch = 0;
+ }else
+ if( (pItem->fg.jointype & JT_RIGHT)==0 ){
+ /* An INNER or LEFT JOIN. Use the left-most table */
+ continue;
+ }else
+ if( (pItem->fg.jointype & JT_LEFT)==0 ){
+ /* A RIGHT JOIN. Use the right-most table */
+ cnt = 0;
+ sqlite3ExprListDelete(db, pFJMatch);
+ pFJMatch = 0;
+ }else{
+ /* For a FULL JOIN, we must construct a coalesce() func */
+ extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn);
+ }
}
+ cnt++;
+ hit = 1;
+ }else if( cnt>0 ){
+ /* This is a potential rowid match, but there has already been
+ ** a real match found. So this can be ignored. */
+ continue;
}
- cnt++;
- cntTab = 2;
+ cntTab++;
pMatch = pItem;
pExpr->iColumn = j;
pEList->a[j].fg.bUsed = 1;
- hit = 1;
+
+ /* rowid cannot be part of a USING clause - assert() this. */
+ assert( bRowid==0 || pEList->a[j].fg.bUsingTerm==0 );
if( pEList->a[j].fg.bUsingTerm ) break;
}
if( hit || zTab==0 ) continue;
@@ -105784,10 +106730,10 @@ static int lookupName(
&& pMatch
&& (pNC->ncFlags & (NC_IdxExpr|NC_GenCol))==0
&& sqlite3IsRowid(zCol)
- && ALWAYS(VisibleRowid(pMatch->pTab))
+ && ALWAYS(VisibleRowid(pMatch->pTab) || pMatch->fg.isNestedFrom)
){
cnt = 1;
- pExpr->iColumn = -1;
+ if( pMatch->fg.isNestedFrom==0 ) pExpr->iColumn = -1;
pExpr->affExpr = SQLITE_AFF_INTEGER;
}
@@ -105946,6 +106892,7 @@ static int lookupName(
sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr);
pParse->checkSchema = 1;
pTopNC->nNcErr++;
+ eNewExprOp = TK_NULL;
}
assert( pFJMatch==0 );
@@ -105972,7 +106919,7 @@ static int lookupName(
** If a generated column is referenced, set bits for every column
** of the table.
*/
- if( pExpr->iColumn>=0 && pMatch!=0 ){
+ if( pExpr->iColumn>=0 && cnt==1 && pMatch!=0 ){
pMatch->colUsed |= sqlite3ExprColUsed(pExpr);
}
@@ -106240,6 +107187,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
Window *pWin = (IsWindowFunc(pExpr) ? pExpr->y.pWin : 0);
#endif
assert( !ExprHasProperty(pExpr, EP_xIsSelect|EP_IntValue) );
+ assert( pExpr->pLeft==0 || pExpr->pLeft->op==TK_ORDER );
zId = pExpr->u.zToken;
pDef = sqlite3FindFunction(pParse->db, zId, n, enc, 0);
if( pDef==0 ){
@@ -106381,6 +107329,10 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
pNC->nNcErr++;
}
#endif
+ else if( is_agg==0 && pExpr->pLeft ){
+ sqlite3ExprOrderByAggregateError(pParse, pExpr);
+ pNC->nNcErr++;
+ }
if( is_agg ){
/* Window functions may not be arguments of aggregate functions.
** Or arguments of other window functions. But aggregate functions
@@ -106399,6 +107351,11 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
#endif
sqlite3WalkExprList(pWalker, pList);
if( is_agg ){
+ if( pExpr->pLeft ){
+ assert( pExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pExpr->pLeft) );
+ sqlite3WalkExprList(pWalker, pExpr->pLeft->x.pList);
+ }
#ifndef SQLITE_OMIT_WINDOWFUNC
if( pWin ){
Select *pSel = pNC->pWinSelect;
@@ -106427,11 +107384,12 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
while( pNC2
&& sqlite3ReferencesSrcList(pParse, pExpr, pNC2->pSrcList)==0
){
- pExpr->op2++;
+ pExpr->op2 += (1 + pNC2->nNestedSelect);
pNC2 = pNC2->pNext;
}
assert( pDef!=0 || IN_RENAME_OBJECT );
if( pNC2 && pDef ){
+ pExpr->op2 += pNC2->nNestedSelect;
assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg );
assert( SQLITE_FUNC_ANYORDER==NC_OrderAgg );
testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 );
@@ -106962,10 +107920,8 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
while( p ){
assert( (p->selFlags & SF_Expanded)!=0 );
assert( (p->selFlags & SF_Resolved)==0 );
- assert( db->suppressErr==0 ); /* SF_Resolved not set if errors suppressed */
p->selFlags |= SF_Resolved;
-
/* Resolve the expressions in the LIMIT and OFFSET clauses. These
** are not allowed to refer to any names, so pass an empty NameContext.
*/
@@ -106992,6 +107948,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
/* Recursively resolve names in all subqueries in the FROM clause
*/
+ if( pOuterNC ) pOuterNC->nNestedSelect++;
for(i=0; i<p->pSrc->nSrc; i++){
SrcItem *pItem = &p->pSrc->a[i];
if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){
@@ -107016,6 +107973,9 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
}
}
}
+ if( pOuterNC && ALWAYS(pOuterNC->nNestedSelect>0) ){
+ pOuterNC->nNestedSelect--;
+ }
/* Set up the local name-context to pass to sqlite3ResolveExprNames() to
** resolve the result-set expression list.
@@ -107971,6 +108931,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprForVectorField(
*/
pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, 0, 0);
if( pRet ){
+ ExprSetProperty(pRet, EP_FullSize);
pRet->iTable = nField;
pRet->iColumn = iField;
pRet->pLeft = pVector;
@@ -108562,6 +109523,67 @@ SQLITE_PRIVATE Expr *sqlite3ExprFunction(
}
/*
+** Report an error when attempting to use an ORDER BY clause within
+** the arguments of a non-aggregate function.
+*/
+SQLITE_PRIVATE void sqlite3ExprOrderByAggregateError(Parse *pParse, Expr *p){
+ sqlite3ErrorMsg(pParse,
+ "ORDER BY may not be used with non-aggregate %#T()", p
+ );
+}
+
+/*
+** Attach an ORDER BY clause to a function call.
+**
+** functionname( arguments ORDER BY sortlist )
+** \_____________________/ \______/
+** pExpr pOrderBy
+**
+** The ORDER BY clause is inserted into a new Expr node of type TK_ORDER
+** and added to the Expr.pLeft field of the parent TK_FUNCTION node.
+*/
+SQLITE_PRIVATE void sqlite3ExprAddFunctionOrderBy(
+ Parse *pParse, /* Parsing context */
+ Expr *pExpr, /* The function call to which ORDER BY is to be added */
+ ExprList *pOrderBy /* The ORDER BY clause to add */
+){
+ Expr *pOB;
+ sqlite3 *db = pParse->db;
+ if( NEVER(pOrderBy==0) ){
+ assert( db->mallocFailed );
+ return;
+ }
+ if( pExpr==0 ){
+ assert( db->mallocFailed );
+ sqlite3ExprListDelete(db, pOrderBy);
+ return;
+ }
+ assert( pExpr->op==TK_FUNCTION );
+ assert( pExpr->pLeft==0 );
+ assert( ExprUseXList(pExpr) );
+ if( pExpr->x.pList==0 || NEVER(pExpr->x.pList->nExpr==0) ){
+ /* Ignore ORDER BY on zero-argument aggregates */
+ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pOrderBy);
+ return;
+ }
+ if( IsWindowFunc(pExpr) ){
+ sqlite3ExprOrderByAggregateError(pParse, pExpr);
+ sqlite3ExprListDelete(db, pOrderBy);
+ return;
+ }
+
+ pOB = sqlite3ExprAlloc(db, TK_ORDER, 0, 0);
+ if( pOB==0 ){
+ sqlite3ExprListDelete(db, pOrderBy);
+ return;
+ }
+ pOB->x.pList = pOrderBy;
+ assert( ExprUseXList(pOB) );
+ pExpr->pLeft = pOB;
+ ExprSetProperty(pOB, EP_FullSize);
+}
+
+/*
** Check to see if a function is usable according to current access
** rules:
**
@@ -108722,6 +109744,9 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){
if( p ) sqlite3ExprDeleteNN(db, p);
}
+SQLITE_PRIVATE void sqlite3ExprDeleteGeneric(sqlite3 *db, void *p){
+ if( ALWAYS(p) ) sqlite3ExprDeleteNN(db, (Expr*)p);
+}
/*
** Clear both elements of an OnOrUsing object
@@ -108747,9 +109772,7 @@ SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){
** pExpr to the pParse->pConstExpr list with a register number of 0.
*/
SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3ExprDelete,
- pExpr);
+ sqlite3ParserAddCleanup(pParse, sqlite3ExprDeleteGeneric, pExpr);
}
/* Invoke sqlite3RenameExprUnmap() and sqlite3ExprDelete() on the
@@ -108814,11 +109837,7 @@ static int dupedExprStructSize(const Expr *p, int flags){
assert( flags==EXPRDUP_REDUCE || flags==0 ); /* Only one flag value allowed */
assert( EXPR_FULLSIZE<=0xfff );
assert( (0xfff & (EP_Reduced|EP_TokenOnly))==0 );
- if( 0==flags || p->op==TK_SELECT_COLUMN
-#ifndef SQLITE_OMIT_WINDOWFUNC
- || ExprHasProperty(p, EP_WinFunc)
-#endif
- ){
+ if( 0==flags || ExprHasProperty(p, EP_FullSize) ){
nSize = EXPR_FULLSIZE;
}else{
assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
@@ -108849,56 +109868,93 @@ static int dupedExprNodeSize(const Expr *p, int flags){
/*
** Return the number of bytes required to create a duplicate of the
-** expression passed as the first argument. The second argument is a
-** mask containing EXPRDUP_XXX flags.
+** expression passed as the first argument.
**
** The value returned includes space to create a copy of the Expr struct
** itself and the buffer referred to by Expr.u.zToken, if any.
**
-** If the EXPRDUP_REDUCE flag is set, then the return value includes
-** space to duplicate all Expr nodes in the tree formed by Expr.pLeft
-** and Expr.pRight variables (but not for any structures pointed to or
-** descended from the Expr.x.pList or Expr.x.pSelect variables).
+** The return value includes space to duplicate all Expr nodes in the
+** tree formed by Expr.pLeft and Expr.pRight, but not any other
+** substructure such as Expr.x.pList, Expr.x.pSelect, and Expr.y.pWin.
*/
-static int dupedExprSize(const Expr *p, int flags){
- int nByte = 0;
- if( p ){
- nByte = dupedExprNodeSize(p, flags);
- if( flags&EXPRDUP_REDUCE ){
- nByte += dupedExprSize(p->pLeft, flags) + dupedExprSize(p->pRight, flags);
- }
- }
+static int dupedExprSize(const Expr *p){
+ int nByte;
+ assert( p!=0 );
+ nByte = dupedExprNodeSize(p, EXPRDUP_REDUCE);
+ if( p->pLeft ) nByte += dupedExprSize(p->pLeft);
+ if( p->pRight ) nByte += dupedExprSize(p->pRight);
+ assert( nByte==ROUND8(nByte) );
return nByte;
}
/*
-** This function is similar to sqlite3ExprDup(), except that if pzBuffer
-** is not NULL then *pzBuffer is assumed to point to a buffer large enough
-** to store the copy of expression p, the copies of p->u.zToken
-** (if applicable), and the copies of the p->pLeft and p->pRight expressions,
-** if any. Before returning, *pzBuffer is set to the first byte past the
-** portion of the buffer copied into by this function.
+** An EdupBuf is a memory allocation used to stored multiple Expr objects
+** together with their Expr.zToken content. This is used to help implement
+** compression while doing sqlite3ExprDup(). The top-level Expr does the
+** allocation for itself and many of its decendents, then passes an instance
+** of the structure down into exprDup() so that they decendents can have
+** access to that memory.
+*/
+typedef struct EdupBuf EdupBuf;
+struct EdupBuf {
+ u8 *zAlloc; /* Memory space available for storage */
+#ifdef SQLITE_DEBUG
+ u8 *zEnd; /* First byte past the end of memory */
+#endif
+};
+
+/*
+** This function is similar to sqlite3ExprDup(), except that if pEdupBuf
+** is not NULL then it points to memory that can be used to store a copy
+** of the input Expr p together with its p->u.zToken (if any). pEdupBuf
+** is updated with the new buffer tail prior to returning.
*/
-static Expr *exprDup(sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){
+static Expr *exprDup(
+ sqlite3 *db, /* Database connection (for memory allocation) */
+ const Expr *p, /* Expr tree to be duplicated */
+ int dupFlags, /* EXPRDUP_REDUCE for compression. 0 if not */
+ EdupBuf *pEdupBuf /* Preallocated storage space, or NULL */
+){
Expr *pNew; /* Value to return */
- u8 *zAlloc; /* Memory space from which to build Expr object */
+ EdupBuf sEdupBuf; /* Memory space from which to build Expr object */
u32 staticFlag; /* EP_Static if space not obtained from malloc */
+ int nToken = -1; /* Space needed for p->u.zToken. -1 means unknown */
assert( db!=0 );
assert( p );
assert( dupFlags==0 || dupFlags==EXPRDUP_REDUCE );
- assert( pzBuffer==0 || dupFlags==EXPRDUP_REDUCE );
+ assert( pEdupBuf==0 || dupFlags==EXPRDUP_REDUCE );
/* Figure out where to write the new Expr structure. */
- if( pzBuffer ){
- zAlloc = *pzBuffer;
+ if( pEdupBuf ){
+ sEdupBuf.zAlloc = pEdupBuf->zAlloc;
+#ifdef SQLITE_DEBUG
+ sEdupBuf.zEnd = pEdupBuf->zEnd;
+#endif
staticFlag = EP_Static;
- assert( zAlloc!=0 );
+ assert( sEdupBuf.zAlloc!=0 );
+ assert( dupFlags==EXPRDUP_REDUCE );
}else{
- zAlloc = sqlite3DbMallocRawNN(db, dupedExprSize(p, dupFlags));
+ int nAlloc;
+ if( dupFlags ){
+ nAlloc = dupedExprSize(p);
+ }else if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
+ nToken = sqlite3Strlen30NN(p->u.zToken)+1;
+ nAlloc = ROUND8(EXPR_FULLSIZE + nToken);
+ }else{
+ nToken = 0;
+ nAlloc = ROUND8(EXPR_FULLSIZE);
+ }
+ assert( nAlloc==ROUND8(nAlloc) );
+ sEdupBuf.zAlloc = sqlite3DbMallocRawNN(db, nAlloc);
+#ifdef SQLITE_DEBUG
+ sEdupBuf.zEnd = sEdupBuf.zAlloc ? sEdupBuf.zAlloc+nAlloc : 0;
+#endif
+
staticFlag = 0;
}
- pNew = (Expr *)zAlloc;
+ pNew = (Expr *)sEdupBuf.zAlloc;
+ assert( EIGHT_BYTE_ALIGNMENT(pNew) );
if( pNew ){
/* Set nNewSize to the size allocated for the structure pointed to
@@ -108907,22 +109963,27 @@ static Expr *exprDup(sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){
** by the copy of the p->u.zToken string (if any).
*/
const unsigned nStructSize = dupedExprStructSize(p, dupFlags);
- const int nNewSize = nStructSize & 0xfff;
- int nToken;
- if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
- nToken = sqlite3Strlen30(p->u.zToken) + 1;
- }else{
- nToken = 0;
+ int nNewSize = nStructSize & 0xfff;
+ if( nToken<0 ){
+ if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
+ nToken = sqlite3Strlen30(p->u.zToken) + 1;
+ }else{
+ nToken = 0;
+ }
}
if( dupFlags ){
+ assert( (int)(sEdupBuf.zEnd - sEdupBuf.zAlloc) >= nNewSize+nToken );
assert( ExprHasProperty(p, EP_Reduced)==0 );
- memcpy(zAlloc, p, nNewSize);
+ memcpy(sEdupBuf.zAlloc, p, nNewSize);
}else{
u32 nSize = (u32)exprStructSize(p);
- memcpy(zAlloc, p, nSize);
+ assert( (int)(sEdupBuf.zEnd - sEdupBuf.zAlloc) >=
+ (int)EXPR_FULLSIZE+nToken );
+ memcpy(sEdupBuf.zAlloc, p, nSize);
if( nSize<EXPR_FULLSIZE ){
- memset(&zAlloc[nSize], 0, EXPR_FULLSIZE-nSize);
+ memset(&sEdupBuf.zAlloc[nSize], 0, EXPR_FULLSIZE-nSize);
}
+ nNewSize = EXPR_FULLSIZE;
}
/* Set the EP_Reduced, EP_TokenOnly, and EP_Static flags appropriately. */
@@ -108935,44 +109996,50 @@ static Expr *exprDup(sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){
}
/* Copy the p->u.zToken string, if any. */
- if( nToken ){
- char *zToken = pNew->u.zToken = (char*)&zAlloc[nNewSize];
+ assert( nToken>=0 );
+ if( nToken>0 ){
+ char *zToken = pNew->u.zToken = (char*)&sEdupBuf.zAlloc[nNewSize];
memcpy(zToken, p->u.zToken, nToken);
+ nNewSize += nToken;
}
+ sEdupBuf.zAlloc += ROUND8(nNewSize);
+
+ if( ((p->flags|pNew->flags)&(EP_TokenOnly|EP_Leaf))==0 ){
- if( 0==((p->flags|pNew->flags) & (EP_TokenOnly|EP_Leaf)) ){
/* Fill in the pNew->x.pSelect or pNew->x.pList member. */
if( ExprUseXSelect(p) ){
pNew->x.pSelect = sqlite3SelectDup(db, p->x.pSelect, dupFlags);
}else{
- pNew->x.pList = sqlite3ExprListDup(db, p->x.pList, dupFlags);
+ pNew->x.pList = sqlite3ExprListDup(db, p->x.pList,
+ p->op!=TK_ORDER ? dupFlags : 0);
}
- }
- /* Fill in pNew->pLeft and pNew->pRight. */
- if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly|EP_WinFunc) ){
- zAlloc += dupedExprNodeSize(p, dupFlags);
- if( !ExprHasProperty(pNew, EP_TokenOnly|EP_Leaf) ){
- pNew->pLeft = p->pLeft ?
- exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc) : 0;
- pNew->pRight = p->pRight ?
- exprDup(db, p->pRight, EXPRDUP_REDUCE, &zAlloc) : 0;
- }
#ifndef SQLITE_OMIT_WINDOWFUNC
if( ExprHasProperty(p, EP_WinFunc) ){
pNew->y.pWin = sqlite3WindowDup(db, pNew, p->y.pWin);
assert( ExprHasProperty(pNew, EP_WinFunc) );
}
#endif /* SQLITE_OMIT_WINDOWFUNC */
- if( pzBuffer ){
- *pzBuffer = zAlloc;
- }
- }else{
- if( !ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){
- if( pNew->op==TK_SELECT_COLUMN ){
+
+ /* Fill in pNew->pLeft and pNew->pRight. */
+ if( dupFlags ){
+ if( p->op==TK_SELECT_COLUMN ){
pNew->pLeft = p->pLeft;
- assert( p->pRight==0 || p->pRight==p->pLeft
- || ExprHasProperty(p->pLeft, EP_Subquery) );
+ assert( p->pRight==0
+ || p->pRight==p->pLeft
+ || ExprHasProperty(p->pLeft, EP_Subquery) );
+ }else{
+ pNew->pLeft = p->pLeft ?
+ exprDup(db, p->pLeft, EXPRDUP_REDUCE, &sEdupBuf) : 0;
+ }
+ pNew->pRight = p->pRight ?
+ exprDup(db, p->pRight, EXPRDUP_REDUCE, &sEdupBuf) : 0;
+ }else{
+ if( p->op==TK_SELECT_COLUMN ){
+ pNew->pLeft = p->pLeft;
+ assert( p->pRight==0
+ || p->pRight==p->pLeft
+ || ExprHasProperty(p->pLeft, EP_Subquery) );
}else{
pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0);
}
@@ -108980,6 +110047,8 @@ static Expr *exprDup(sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){
}
}
}
+ if( pEdupBuf ) memcpy(pEdupBuf, &sEdupBuf, sizeof(sEdupBuf));
+ assert( sEdupBuf.zAlloc <= sEdupBuf.zEnd );
return pNew;
}
@@ -109244,11 +110313,7 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, const Select *p, int flags)
** initially NULL, then create a new expression list.
**
** The pList argument must be either NULL or a pointer to an ExprList
-** obtained from a prior call to sqlite3ExprListAppend(). This routine
-** may not be used with an ExprList obtained from sqlite3ExprListDup().
-** Reason: This routine assumes that the number of slots in pList->a[]
-** is a power of two. That is true for sqlite3ExprListAppend() returns
-** but is not necessarily true from the return value of sqlite3ExprListDup().
+** obtained from a prior call to sqlite3ExprListAppend().
**
** If a memory allocation error occurs, the entire list is freed and
** NULL is returned. If non-NULL is returned, then it is guaranteed
@@ -109513,6 +110578,9 @@ static SQLITE_NOINLINE void exprListDeleteNN(sqlite3 *db, ExprList *pList){
SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
if( pList ) exprListDeleteNN(db, pList);
}
+SQLITE_PRIVATE void sqlite3ExprListDeleteGeneric(sqlite3 *db, void *pList){
+ if( ALWAYS(pList) ) exprListDeleteNN(db, (ExprList*)pList);
+}
/*
** Return the bitwise-OR of all Expr.flags fields in the given
@@ -110012,9 +111080,10 @@ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){
case TK_COLUMN:
assert( ExprUseYTab(p) );
return ExprHasProperty(p, EP_CanBeNull) ||
- p->y.pTab==0 || /* Reference to column of index on expression */
+ NEVER(p->y.pTab==0) || /* Reference to column of index on expr */
(p->iColumn>=0
&& p->y.pTab->aCol!=0 /* Possible due to prior error */
+ && ALWAYS(p->iColumn<p->y.pTab->nCol)
&& p->y.pTab->aCol[p->iColumn].notNull==0);
default:
return 1;
@@ -110075,6 +111144,27 @@ SQLITE_PRIVATE int sqlite3IsRowid(const char *z){
}
/*
+** Return a pointer to a buffer containing a usable rowid alias for table
+** pTab. An alias is usable if there is not an explicit user-defined column
+** of the same name.
+*/
+SQLITE_PRIVATE const char *sqlite3RowidAlias(Table *pTab){
+ const char *azOpt[] = {"_ROWID_", "ROWID", "OID"};
+ int ii;
+ assert( VisibleRowid(pTab) );
+ for(ii=0; ii<ArraySize(azOpt); ii++){
+ int iCol;
+ for(iCol=0; iCol<pTab->nCol; iCol++){
+ if( sqlite3_stricmp(azOpt[ii], pTab->aCol[iCol].zCnName)==0 ) break;
+ }
+ if( iCol==pTab->nCol ){
+ return azOpt[ii];
+ }
+ }
+ return 0;
+}
+
+/*
** pX is the RHS of an IN operator. If pX is a SELECT statement
** that can be simplified to a direct table access, then return
** a pointer to the SELECT statement. If pX is not a SELECT statement,
@@ -111612,6 +112702,41 @@ static SQLITE_NOINLINE int sqlite3IndexedExprLookup(
/*
+** Expresion pExpr is guaranteed to be a TK_COLUMN or equivalent. This
+** function checks the Parse.pIdxPartExpr list to see if this column
+** can be replaced with a constant value. If so, it generates code to
+** put the constant value in a register (ideally, but not necessarily,
+** register iTarget) and returns the register number.
+**
+** Or, if the TK_COLUMN cannot be replaced by a constant, zero is
+** returned.
+*/
+static int exprPartidxExprLookup(Parse *pParse, Expr *pExpr, int iTarget){
+ IndexedExpr *p;
+ for(p=pParse->pIdxPartExpr; p; p=p->pIENext){
+ if( pExpr->iColumn==p->iIdxCol && pExpr->iTable==p->iDataCur ){
+ Vdbe *v = pParse->pVdbe;
+ int addr = 0;
+ int ret;
+
+ if( p->bMaybeNullRow ){
+ addr = sqlite3VdbeAddOp1(v, OP_IfNullRow, p->iIdxCur);
+ }
+ ret = sqlite3ExprCodeTarget(pParse, p->pExpr, iTarget);
+ sqlite3VdbeAddOp4(pParse->pVdbe, OP_Affinity, ret, 1, 0,
+ (const char*)&p->aff, 1);
+ if( addr ){
+ sqlite3VdbeJumpHere(v, addr);
+ sqlite3VdbeChangeP3(v, addr, ret);
+ }
+ return ret;
+ }
+ }
+ return 0;
+}
+
+
+/*
** Generate code into the current Vdbe to evaluate the given
** expression. Attempt to store the results in register "target".
** Return the register where results are stored.
@@ -111647,6 +112772,7 @@ expr_code_doover:
assert( !ExprHasVVAProperty(pExpr,EP_Immutable) );
op = pExpr->op;
}
+ assert( op!=TK_ORDER );
switch( op ){
case TK_AGG_COLUMN: {
AggInfo *pAggInfo = pExpr->pAggInfo;
@@ -111660,7 +112786,7 @@ expr_code_doover:
#ifdef SQLITE_VDBE_COVERAGE
/* Verify that the OP_Null above is exercised by tests
** tag-20230325-2 */
- sqlite3VdbeAddOp2(v, OP_NotNull, target, 1);
+ sqlite3VdbeAddOp3(v, OP_NotNull, target, 1, 20230325);
VdbeCoverageNeverTaken(v);
#endif
break;
@@ -111768,6 +112894,11 @@ expr_code_doover:
iTab = pParse->iSelfTab - 1;
}
}
+ else if( pParse->pIdxPartExpr
+ && 0!=(r1 = exprPartidxExprLookup(pParse, pExpr, target))
+ ){
+ return r1;
+ }
assert( ExprUseYTab(pExpr) );
assert( pExpr->y.pTab!=0 );
iReg = sqlite3ExprCodeGetColumn(pParse, pExpr->y.pTab,
@@ -112428,7 +113559,7 @@ expr_code_doover:
** once. If no functions are involved, then factor the code out and put it at
** the end of the prepared statement in the initialization section.
**
-** If regDest>=0 then the result is always stored in that register and the
+** If regDest>0 then the result is always stored in that register and the
** result is not reusable. If regDest<0 then this routine is free to
** store the value wherever it wants. The register where the expression
** is stored is returned. When regDest<0, two identical expressions might
@@ -112443,6 +113574,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce(
){
ExprList *p;
assert( ConstFactorOk(pParse) );
+ assert( regDest!=0 );
p = pParse->pConstExpr;
if( regDest<0 && p ){
struct ExprList_item *pItem;
@@ -112533,8 +113665,10 @@ SQLITE_PRIVATE void sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
if( inReg!=target ){
u8 op;
- if( ALWAYS(pExpr)
- && (ExprHasProperty(pExpr,EP_Subquery) || pExpr->op==TK_REGISTER)
+ Expr *pX = sqlite3ExprSkipCollateAndLikely(pExpr);
+ testcase( pX!=pExpr );
+ if( ALWAYS(pX)
+ && (ExprHasProperty(pX,EP_Subquery) || pX->op==TK_REGISTER)
){
op = OP_Copy;
}else{
@@ -113254,8 +114388,8 @@ SQLITE_PRIVATE int sqlite3ExprListCompare(const ExprList *pA, const ExprList *pB
*/
SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr *pA,Expr *pB, int iTab){
return sqlite3ExprCompare(0,
- sqlite3ExprSkipCollateAndLikely(pA),
- sqlite3ExprSkipCollateAndLikely(pB),
+ sqlite3ExprSkipCollate(pA),
+ sqlite3ExprSkipCollate(pB),
iTab);
}
@@ -113727,6 +114861,12 @@ SQLITE_PRIVATE int sqlite3ReferencesSrcList(Parse *pParse, Expr *pExpr, SrcList
assert( pExpr->op==TK_AGG_FUNCTION );
assert( ExprUseXList(pExpr) );
sqlite3WalkExprList(&w, pExpr->x.pList);
+ if( pExpr->pLeft ){
+ assert( pExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pExpr->pLeft) );
+ assert( pExpr->pLeft->x.pList!=0 );
+ sqlite3WalkExprList(&w, pExpr->pLeft->x.pList);
+ }
#ifndef SQLITE_OMIT_WINDOWFUNC
if( ExprHasProperty(pExpr, EP_WinFunc) ){
sqlite3WalkExpr(&w, pExpr->y.pWin->pFilter);
@@ -113974,13 +115114,14 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
case TK_AGG_FUNCTION: {
if( (pNC->ncFlags & NC_InAggFunc)==0
&& pWalker->walkerDepth==pExpr->op2
+ && pExpr->pAggInfo==0
){
/* Check to see if pExpr is a duplicate of another aggregate
** function that is already in the pAggInfo structure
*/
struct AggInfo_func *pItem = pAggInfo->aFunc;
for(i=0; i<pAggInfo->nFunc; i++, pItem++){
- if( pItem->pFExpr==pExpr ) break;
+ if( NEVER(pItem->pFExpr==pExpr) ) break;
if( sqlite3ExprCompare(0, pItem->pFExpr, pExpr, -1)==0 ){
break;
}
@@ -113991,14 +115132,44 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
u8 enc = ENC(pParse->db);
i = addAggInfoFunc(pParse->db, pAggInfo);
if( i>=0 ){
+ int nArg;
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
pItem = &pAggInfo->aFunc[i];
pItem->pFExpr = pExpr;
assert( ExprUseUToken(pExpr) );
+ nArg = pExpr->x.pList ? pExpr->x.pList->nExpr : 0;
pItem->pFunc = sqlite3FindFunction(pParse->db,
- pExpr->u.zToken,
- pExpr->x.pList ? pExpr->x.pList->nExpr : 0, enc, 0);
- if( pExpr->flags & EP_Distinct ){
+ pExpr->u.zToken, nArg, enc, 0);
+ assert( pItem->bOBUnique==0 );
+ if( pExpr->pLeft
+ && (pItem->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)==0
+ ){
+ /* The NEEDCOLL test above causes any ORDER BY clause on
+ ** aggregate min() or max() to be ignored. */
+ ExprList *pOBList;
+ assert( nArg>0 );
+ assert( pExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pExpr->pLeft) );
+ pItem->iOBTab = pParse->nTab++;
+ pOBList = pExpr->pLeft->x.pList;
+ assert( pOBList->nExpr>0 );
+ assert( pItem->bOBUnique==0 );
+ if( pOBList->nExpr==1
+ && nArg==1
+ && sqlite3ExprCompare(0,pOBList->a[0].pExpr,
+ pExpr->x.pList->a[0].pExpr,0)==0
+ ){
+ pItem->bOBPayload = 0;
+ pItem->bOBUnique = ExprHasProperty(pExpr, EP_Distinct);
+ }else{
+ pItem->bOBPayload = 1;
+ }
+ pItem->bUseSubtype =
+ (pItem->pFunc->funcFlags & SQLITE_SUBTYPE)!=0;
+ }else{
+ pItem->iOBTab = -1;
+ }
+ if( ExprHasProperty(pExpr, EP_Distinct) && !pItem->bOBUnique ){
pItem->iDistinct = pParse->nTab++;
}else{
pItem->iDistinct = -1;
@@ -114634,14 +115805,19 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
/* Verify that constraints are still satisfied */
if( pNew->pCheck!=0
|| (pCol->notNull && (pCol->colFlags & COLFLAG_GENERATED)!=0)
+ || (pTab->tabFlags & TF_Strict)!=0
){
sqlite3NestedParse(pParse,
"SELECT CASE WHEN quick_check GLOB 'CHECK*'"
" THEN raise(ABORT,'CHECK constraint failed')"
+ " WHEN quick_check GLOB 'non-* value in*'"
+ " THEN raise(ABORT,'type mismatch on DEFAULT')"
" ELSE raise(ABORT,'NOT NULL constraint failed')"
" END"
" FROM pragma_quick_check(%Q,%Q)"
- " WHERE quick_check GLOB 'CHECK*' OR quick_check GLOB 'NULL*'",
+ " WHERE quick_check GLOB 'CHECK*'"
+ " OR quick_check GLOB 'NULL*'"
+ " OR quick_check GLOB 'non-* value in*'",
zTab, zDb
);
}
@@ -116756,9 +117932,9 @@ static void openStatTable(
typedef struct StatAccum StatAccum;
typedef struct StatSample StatSample;
struct StatSample {
- tRowcnt *anEq; /* sqlite_stat4.nEq */
tRowcnt *anDLt; /* sqlite_stat4.nDLt */
#ifdef SQLITE_ENABLE_STAT4
+ tRowcnt *anEq; /* sqlite_stat4.nEq */
tRowcnt *anLt; /* sqlite_stat4.nLt */
union {
i64 iRowid; /* Rowid in main table of the key */
@@ -116916,9 +118092,9 @@ static void statInit(
/* Allocate the space required for the StatAccum object */
n = sizeof(*p)
- + sizeof(tRowcnt)*nColUp /* StatAccum.anEq */
- + sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */
+ + sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */
#ifdef SQLITE_ENABLE_STAT4
+ n += sizeof(tRowcnt)*nColUp; /* StatAccum.anEq */
if( mxSample ){
n += sizeof(tRowcnt)*nColUp /* StatAccum.anLt */
+ sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */
@@ -116939,9 +118115,9 @@ static void statInit(
p->nKeyCol = nKeyCol;
p->nSkipAhead = 0;
p->current.anDLt = (tRowcnt*)&p[1];
- p->current.anEq = &p->current.anDLt[nColUp];
#ifdef SQLITE_ENABLE_STAT4
+ p->current.anEq = &p->current.anDLt[nColUp];
p->mxSample = p->nLimit==0 ? mxSample : 0;
if( mxSample ){
u8 *pSpace; /* Allocated space not yet assigned */
@@ -117208,7 +118384,9 @@ static void statPush(
if( p->nRow==0 ){
/* This is the first call to this function. Do initialization. */
+#ifdef SQLITE_ENABLE_STAT4
for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1;
+#endif
}else{
/* Second and subsequent calls get processed here */
#ifdef SQLITE_ENABLE_STAT4
@@ -117217,15 +118395,17 @@ static void statPush(
/* Update anDLt[], anLt[] and anEq[] to reflect the values that apply
** to the current row of the index. */
+#ifdef SQLITE_ENABLE_STAT4
for(i=0; i<iChng; i++){
p->current.anEq[i]++;
}
+#endif
for(i=iChng; i<p->nCol; i++){
p->current.anDLt[i]++;
#ifdef SQLITE_ENABLE_STAT4
if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i];
-#endif
p->current.anEq[i] = 1;
+#endif
}
}
@@ -117359,7 +118539,9 @@ static void statGet(
u64 iVal = (p->nRow + nDistinct - 1) / nDistinct;
if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1;
sqlite3_str_appendf(&sStat, " %llu", iVal);
+#ifdef SQLITE_ENABLE_STAT4
assert( p->current.anEq[i] );
+#endif
}
sqlite3ResultStrAccum(context, &sStat);
}
@@ -118048,6 +119230,16 @@ static void decodeIntArray(
while( z[0]!=0 && z[0]!=' ' ) z++;
while( z[0]==' ' ) z++;
}
+
+ /* Set the bLowQual flag if the peak number of rows obtained
+ ** from a full equality match is so large that a full table scan
+ ** seems likely to be faster than using the index.
+ */
+ if( aLog[0] > 66 /* Index has more than 100 rows */
+ && aLog[0] <= aLog[nOut-1] /* And only a single value seen */
+ ){
+ pIndex->bLowQual = 1;
+ }
}
}
@@ -119620,19 +120812,14 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
*/
if( pParse->pAinc ) sqlite3AutoincrementBegin(pParse);
- /* Code constant expressions that where factored out of inner loops.
- **
- ** The pConstExpr list might also contain expressions that we simply
- ** want to keep around until the Parse object is deleted. Such
- ** expressions have iConstExprReg==0. Do not generate code for
- ** those expressions, of course.
+ /* Code constant expressions that were factored out of inner loops.
*/
if( pParse->pConstExpr ){
ExprList *pEL = pParse->pConstExpr;
pParse->okConstFactor = 0;
for(i=0; i<pEL->nExpr; i++){
- int iReg = pEL->a[i].u.iConstExprReg;
- sqlite3ExprCode(pParse, pEL->a[i].pExpr, iReg);
+ assert( pEL->a[i].u.iConstExprReg>0 );
+ sqlite3ExprCode(pParse, pEL->a[i].pExpr, pEL->a[i].u.iConstExprReg);
}
}
@@ -120099,7 +121286,7 @@ SQLITE_PRIVATE void sqlite3ColumnSetExpr(
*/
SQLITE_PRIVATE Expr *sqlite3ColumnExpr(Table *pTab, Column *pCol){
if( pCol->iDflt==0 ) return 0;
- if( NEVER(!IsOrdinaryTable(pTab)) ) return 0;
+ if( !IsOrdinaryTable(pTab) ) return 0;
if( NEVER(pTab->u.tab.pDfltList==0) ) return 0;
if( NEVER(pTab->u.tab.pDfltList->nExpr<pCol->iDflt) ) return 0;
return pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr;
@@ -120252,6 +121439,9 @@ SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
if( db->pnBytesFreed==0 && (--pTable->nTabRef)>0 ) return;
deleteTable(db, pTable);
}
+SQLITE_PRIVATE void sqlite3DeleteTableGeneric(sqlite3 *db, void *pTable){
+ sqlite3DeleteTable(db, (Table*)pTable);
+}
/*
@@ -120787,19 +121977,13 @@ SQLITE_PRIVATE void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){
#endif
/*
-** Name of the special TEMP trigger used to implement RETURNING. The
-** name begins with "sqlite_" so that it is guaranteed not to collide
-** with any application-generated triggers.
-*/
-#define RETURNING_TRIGGER_NAME "sqlite_returning"
-
-/*
** Clean up the data structures associated with the RETURNING clause.
*/
-static void sqlite3DeleteReturning(sqlite3 *db, Returning *pRet){
+static void sqlite3DeleteReturning(sqlite3 *db, void *pArg){
+ Returning *pRet = (Returning*)pArg;
Hash *pHash;
pHash = &(db->aDb[1].pSchema->trigHash);
- sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, 0);
+ sqlite3HashInsert(pHash, pRet->zName, 0);
sqlite3ExprListDelete(db, pRet->pReturnEL);
sqlite3DbFree(db, pRet);
}
@@ -120838,11 +122022,12 @@ SQLITE_PRIVATE void sqlite3AddReturning(Parse *pParse, ExprList *pList){
pParse->u1.pReturning = pRet;
pRet->pParse = pParse;
pRet->pReturnEL = pList;
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3DeleteReturning, pRet);
+ sqlite3ParserAddCleanup(pParse, sqlite3DeleteReturning, pRet);
testcase( pParse->earlyCleanup );
if( db->mallocFailed ) return;
- pRet->retTrig.zName = RETURNING_TRIGGER_NAME;
+ sqlite3_snprintf(sizeof(pRet->zName), pRet->zName,
+ "sqlite_returning_%p", pParse);
+ pRet->retTrig.zName = pRet->zName;
pRet->retTrig.op = TK_RETURNING;
pRet->retTrig.tr_tm = TRIGGER_AFTER;
pRet->retTrig.bReturning = 1;
@@ -120853,9 +122038,9 @@ SQLITE_PRIVATE void sqlite3AddReturning(Parse *pParse, ExprList *pList){
pRet->retTStep.pTrig = &pRet->retTrig;
pRet->retTStep.pExprList = pList;
pHash = &(db->aDb[1].pSchema->trigHash);
- assert( sqlite3HashFind(pHash, RETURNING_TRIGGER_NAME)==0
+ assert( sqlite3HashFind(pHash, pRet->zName)==0
|| pParse->nErr || pParse->ifNotExists );
- if( sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, &pRet->retTrig)
+ if( sqlite3HashInsert(pHash, pRet->zName, &pRet->retTrig)
==&pRet->retTrig ){
sqlite3OomFault(db);
}
@@ -121036,7 +122221,8 @@ SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn, Column *pCol){
assert( zIn!=0 );
while( zIn[0] ){
- h = (h<<8) + sqlite3UpperToLower[(*zIn)&0xff];
+ u8 x = *(u8*)zIn;
+ h = (h<<8) + sqlite3UpperToLower[x];
zIn++;
if( h==(('c'<<24)+('h'<<16)+('a'<<8)+'r') ){ /* CHAR */
aff = SQLITE_AFF_TEXT;
@@ -122299,6 +123485,17 @@ SQLITE_PRIVATE void sqlite3EndTable(
/* Reparse everything to update our internal data structures */
sqlite3VdbeAddParseSchemaOp(v, iDb,
sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName),0);
+
+ /* Test for cycles in generated columns and illegal expressions
+ ** in CHECK constraints and in DEFAULT clauses. */
+ if( p->tabFlags & TF_HasGenerated ){
+ sqlite3VdbeAddOp4(v, OP_SqlExec, 1, 0, 0,
+ sqlite3MPrintf(db, "SELECT*FROM\"%w\".\"%w\"",
+ db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC);
+ }
+ sqlite3VdbeAddOp4(v, OP_SqlExec, 1, 0, 0,
+ sqlite3MPrintf(db, "PRAGMA \"%w\".integrity_check(%Q)",
+ db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC);
}
/* Add the table to the in-memory representation of the database.
@@ -124890,7 +126087,7 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
if( iDb<0 ) return;
z = sqlite3NameFromToken(db, pObjName);
if( z==0 ) return;
- zDb = db->aDb[iDb].zDbSName;
+ zDb = pName2->n ? db->aDb[iDb].zDbSName : 0;
pTab = sqlite3FindTable(db, z, zDb);
if( pTab ){
reindexTable(pParse, pTab, 0);
@@ -124900,6 +126097,7 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
pIndex = sqlite3FindIndex(db, z, zDb);
sqlite3DbFree(db, z);
if( pIndex ){
+ iDb = sqlite3SchemaToIndex(db, pIndex->pTable->pSchema);
sqlite3BeginWriteOperation(pParse, 0, iDb);
sqlite3RefillIndex(pParse, pIndex, -1);
return;
@@ -125065,6 +126263,9 @@ SQLITE_PRIVATE void sqlite3WithDelete(sqlite3 *db, With *pWith){
sqlite3DbFree(db, pWith);
}
}
+SQLITE_PRIVATE void sqlite3WithDeleteGeneric(sqlite3 *db, void *pWith){
+ sqlite3WithDelete(db, (With*)pWith);
+}
#endif /* !defined(SQLITE_OMIT_CTE) */
/************** End of build.c ***********************************************/
@@ -127901,7 +129102,8 @@ static void hexFunc(
*(z++) = hexdigits[c&0xf];
}
*z = 0;
- sqlite3_result_text(context, zHex, n*2, sqlite3_free);
+ sqlite3_result_text64(context, zHex, (u64)(z-zHex),
+ sqlite3_free, SQLITE_UTF8);
}
}
@@ -128195,6 +129397,81 @@ static void trimFunc(
sqlite3_result_text(context, (char*)zIn, nIn, SQLITE_TRANSIENT);
}
+/* The core implementation of the CONCAT(...) and CONCAT_WS(SEP,...)
+** functions.
+**
+** Return a string value that is the concatenation of all non-null
+** entries in argv[]. Use zSep as the separator.
+*/
+static void concatFuncCore(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv,
+ int nSep,
+ const char *zSep
+){
+ i64 j, k, n = 0;
+ int i;
+ char *z;
+ for(i=0; i<argc; i++){
+ n += sqlite3_value_bytes(argv[i]);
+ }
+ n += (argc-1)*nSep;
+ z = sqlite3_malloc64(n+1);
+ if( z==0 ){
+ sqlite3_result_error_nomem(context);
+ return;
+ }
+ j = 0;
+ for(i=0; i<argc; i++){
+ k = sqlite3_value_bytes(argv[i]);
+ if( k>0 ){
+ const char *v = (const char*)sqlite3_value_text(argv[i]);
+ if( v!=0 ){
+ if( j>0 && nSep>0 ){
+ memcpy(&z[j], zSep, nSep);
+ j += nSep;
+ }
+ memcpy(&z[j], v, k);
+ j += k;
+ }
+ }
+ }
+ z[j] = 0;
+ assert( j<=n );
+ sqlite3_result_text64(context, z, j, sqlite3_free, SQLITE_UTF8);
+}
+
+/*
+** The CONCAT(...) function. Generate a string result that is the
+** concatentation of all non-null arguments.
+*/
+static void concatFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ concatFuncCore(context, argc, argv, 0, "");
+}
+
+/*
+** The CONCAT_WS(separator, ...) function.
+**
+** Generate a string that is the concatenation of 2nd through the Nth
+** argument. Use the first argument (which must be non-NULL) as the
+** separator.
+*/
+static void concatwsFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int nSep = sqlite3_value_bytes(argv[0]);
+ const char *zSep = (const char*)sqlite3_value_text(argv[0]);
+ if( zSep==0 ) return;
+ concatFuncCore(context, argc-1, argv+1, nSep, zSep);
+}
+
#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
/*
@@ -128616,6 +129893,7 @@ static void minMaxFinalize(sqlite3_context *context){
/*
** group_concat(EXPR, ?SEPARATOR?)
+** string_agg(EXPR, SEPARATOR)
**
** The SEPARATOR goes before the EXPR string. This is tragic. The
** groupConcatInverse() implementation would have been easier if the
@@ -129206,6 +130484,11 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
FUNCTION(hex, 1, 0, 0, hexFunc ),
FUNCTION(unhex, 1, 0, 0, unhexFunc ),
FUNCTION(unhex, 2, 0, 0, unhexFunc ),
+ FUNCTION(concat, -1, 0, 0, concatFunc ),
+ FUNCTION(concat, 0, 0, 0, 0 ),
+ FUNCTION(concat_ws, -1, 0, 0, concatwsFunc ),
+ FUNCTION(concat_ws, 0, 0, 0, 0 ),
+ FUNCTION(concat_ws, 1, 0, 0, 0 ),
INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, 0 ),
VFUNCTION(random, 0, 0, 0, randomFunc ),
VFUNCTION(randomblob, 1, 0, 0, randomBlob ),
@@ -129235,6 +130518,8 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
WAGGREGATE(group_concat, 2, 0, 0, groupConcatStep,
groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
+ WAGGREGATE(string_agg, 2, 0, 0, groupConcatStep,
+ groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
#ifdef SQLITE_CASE_SENSITIVE_LIKE
@@ -130177,6 +131462,7 @@ static int isSetNullAction(Parse *pParse, FKey *pFKey){
if( (p==pFKey->apTrigger[0] && pFKey->aAction[0]==OE_SetNull)
|| (p==pFKey->apTrigger[1] && pFKey->aAction[1]==OE_SetNull)
){
+ assert( (pTop->db->flags & SQLITE_FkNoAction)==0 );
return 1;
}
}
@@ -130371,6 +131657,8 @@ SQLITE_PRIVATE void sqlite3FkCheck(
}
if( regOld!=0 ){
int eAction = pFKey->aAction[aChange!=0];
+ if( (db->flags & SQLITE_FkNoAction) ) eAction = OE_None;
+
fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regOld, 1);
/* If this is a deferred FK constraint, or a CASCADE or SET NULL
** action applies, then any foreign key violations caused by
@@ -130486,7 +131774,11 @@ SQLITE_PRIVATE int sqlite3FkRequired(
/* Check if any parent key columns are being modified. */
for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
if( fkParentIsModified(pTab, p, aChange, chngRowid) ){
- if( p->aAction[1]!=OE_None ) return 2;
+ if( (pParse->db->flags & SQLITE_FkNoAction)==0
+ && p->aAction[1]!=OE_None
+ ){
+ return 2;
+ }
bHaveFK = 1;
}
}
@@ -130536,6 +131828,7 @@ static Trigger *fkActionTrigger(
int iAction = (pChanges!=0); /* 1 for UPDATE, 0 for DELETE */
action = pFKey->aAction[iAction];
+ if( (db->flags & SQLITE_FkNoAction) ) action = OE_None;
if( action==OE_Restrict && (db->flags & SQLITE_DeferFKs) ){
return 0;
}
@@ -134491,6 +135784,9 @@ struct sqlite3_api_routines {
int (*is_interrupted)(sqlite3*);
/* Version 3.43.0 and later */
int (*stmt_explain)(sqlite3_stmt*,int);
+ /* Version 3.44.0 and later */
+ void *(*get_clientdata)(sqlite3*,const char*);
+ int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
};
/*
@@ -134821,6 +136117,9 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_is_interrupted sqlite3_api->is_interrupted
/* Version 3.43.0 and later */
#define sqlite3_stmt_explain sqlite3_api->stmt_explain
+/* Version 3.44.0 and later */
+#define sqlite3_get_clientdata sqlite3_api->get_clientdata
+#define sqlite3_set_clientdata sqlite3_api->set_clientdata
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
@@ -135339,7 +136638,10 @@ static const sqlite3_api_routines sqlite3Apis = {
/* Version 3.41.0 and later */
sqlite3_is_interrupted,
/* Version 3.43.0 and later */
- sqlite3_stmt_explain
+ sqlite3_stmt_explain,
+ /* Version 3.44.0 and later */
+ sqlite3_get_clientdata,
+ sqlite3_set_clientdata
};
/* True if x is the directory separator character
@@ -135555,6 +136857,9 @@ SQLITE_PRIVATE void sqlite3CloseExtensions(sqlite3 *db){
** default so as not to open security holes in older applications.
*/
SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
if( onoff ){
db->flags |= SQLITE_LoadExtension|SQLITE_LoadExtFunc;
@@ -135604,6 +136909,9 @@ SQLITE_API int sqlite3_auto_extension(
void (*xInit)(void)
){
int rc = SQLITE_OK;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( xInit==0 ) return SQLITE_MISUSE_BKPT;
+#endif
#ifndef SQLITE_OMIT_AUTOINIT
rc = sqlite3_initialize();
if( rc ){
@@ -135656,6 +136964,9 @@ SQLITE_API int sqlite3_cancel_auto_extension(
int i;
int n = 0;
wsdAutoextInit;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( xInit==0 ) return 0;
+#endif
sqlite3_mutex_enter(mutex);
for(i=(int)wsdAutoext.nExt-1; i>=0; i--){
if( wsdAutoext.aExt[i]==xInit ){
@@ -137525,7 +138836,11 @@ SQLITE_PRIVATE void sqlite3Pragma(
#endif
if( sqlite3GetBoolean(zRight, 0) ){
- db->flags |= mask;
+ if( (mask & SQLITE_WriteSchema)==0
+ || (db->flags & SQLITE_Defensive)==0
+ ){
+ db->flags |= mask;
+ }
}else{
db->flags &= ~mask;
if( mask==SQLITE_DeferFKs ) db->nDeferredImmCons = 0;
@@ -138158,8 +139473,32 @@ SQLITE_PRIVATE void sqlite3Pragma(
int r2; /* Previous key for WITHOUT ROWID tables */
int mxCol; /* Maximum non-virtual column number */
- if( !IsOrdinaryTable(pTab) ) continue;
if( pObjTab && pObjTab!=pTab ) continue;
+ if( !IsOrdinaryTable(pTab) ){
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ sqlite3_vtab *pVTab;
+ int a1;
+ if( !IsVirtual(pTab) ) continue;
+ if( pTab->nCol<=0 ){
+ const char *zMod = pTab->u.vtab.azArg[0];
+ if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue;
+ }
+ sqlite3ViewGetColumnNames(pParse, pTab);
+ if( pTab->u.vtab.p==0 ) continue;
+ pVTab = pTab->u.vtab.p->pVtab;
+ if( NEVER(pVTab==0) ) continue;
+ if( NEVER(pVTab->pModule==0) ) continue;
+ if( pVTab->pModule->iVersion<4 ) continue;
+ if( pVTab->pModule->xIntegrity==0 ) continue;
+ sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick);
+ pTab->nTabRef++;
+ sqlite3VdbeAppendP4(v, pTab, P4_TABLEREF);
+ a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v);
+ integrityCheckResultRow(v);
+ sqlite3VdbeJumpHere(v, a1);
+#endif
+ continue;
+ }
if( isQuick || HasRowid(pTab) ){
pPk = 0;
r2 = 0;
@@ -139285,7 +140624,8 @@ static const sqlite3_module pragmaVtabModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
/*
@@ -139909,8 +141249,6 @@ SQLITE_PRIVATE void sqlite3ParseObjectReset(Parse *pParse){
db->lookaside.sz = db->lookaside.bDisable ? 0 : db->lookaside.szTrue;
assert( pParse->db->pParse==pParse );
db->pParse = pParse->pOuterParse;
- pParse->db = 0;
- pParse->disableLookaside = 0;
}
/*
@@ -140181,6 +141519,7 @@ static int sqlite3LockAndPrepare(
assert( (rc&db->errMask)==rc );
db->busyHandler.nBusy = 0;
sqlite3_mutex_leave(db->mutex);
+ assert( rc==SQLITE_OK || (*ppStmt)==0 );
return rc;
}
@@ -140578,6 +141917,9 @@ SQLITE_PRIVATE Select *sqlite3SelectNew(
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){
if( OK_IF_ALWAYS_TRUE(p) ) clearSelect(db, p, 1);
}
+SQLITE_PRIVATE void sqlite3SelectDeleteGeneric(sqlite3 *db, void *p){
+ if( ALWAYS(p) ) clearSelect(db, (Select*)p, 1);
+}
/*
** Return a pointer to the right-most SELECT statement in a compound.
@@ -140848,6 +142190,7 @@ static void unsetJoinExpr(Expr *p, int iTable, int nullable){
}
if( p->op==TK_FUNCTION ){
assert( ExprUseXList(p) );
+ assert( p->pLeft==0 );
if( p->x.pList ){
int i;
for(i=0; i<p->x.pList->nExpr; i++){
@@ -142712,7 +144055,8 @@ SQLITE_PRIVATE void sqlite3SubqueryColumnTypes(
NameContext sNC;
assert( pSelect!=0 );
- assert( (pSelect->selFlags & SF_Resolved)!=0 );
+ testcase( (pSelect->selFlags & SF_Resolved)==0 );
+ assert( (pSelect->selFlags & SF_Resolved)!=0 || IN_RENAME_OBJECT );
assert( pTab->nCol==pSelect->pEList->nExpr || pParse->nErr>0 );
assert( aff==SQLITE_AFF_NONE || aff==SQLITE_AFF_BLOB );
if( db->mallocFailed || IN_RENAME_OBJECT ) return;
@@ -143596,9 +144940,7 @@ multi_select_end:
pDest->iSdst = dest.iSdst;
pDest->nSdst = dest.nSdst;
if( pDelete ){
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3SelectDelete,
- pDelete);
+ sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pDelete);
}
return rc;
}
@@ -144149,8 +145491,7 @@ static int multiSelectOrderBy(
/* Make arrangements to free the 2nd and subsequent arms of the compound
** after the parse has finished */
if( pSplit->pPrior ){
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3SelectDelete, pSplit->pPrior);
+ sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pSplit->pPrior);
}
pSplit->pPrior = pPrior;
pPrior->pNext = pSplit;
@@ -144971,9 +146312,7 @@ static int flattenSubquery(
Table *pTabToDel = pSubitem->pTab;
if( pTabToDel->nTabRef==1 ){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
- sqlite3ParserAddCleanup(pToplevel,
- (void(*)(sqlite3*,void*))sqlite3DeleteTable,
- pTabToDel);
+ sqlite3ParserAddCleanup(pToplevel, sqlite3DeleteTableGeneric, pTabToDel);
testcase( pToplevel->earlyCleanup );
}else{
pTabToDel->nTabRef--;
@@ -146020,8 +147359,7 @@ static struct Cte *searchWith(
SQLITE_PRIVATE With *sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
if( pWith ){
if( bFree ){
- pWith = (With*)sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3WithDelete,
+ pWith = (With*)sqlite3ParserAddCleanup(pParse, sqlite3WithDeleteGeneric,
pWith);
if( pWith==0 ) return 0;
}
@@ -146508,6 +147846,7 @@ static int selectExpander(Walker *pWalker, Select *p){
char *zTName = 0; /* text of name of TABLE */
int iErrOfst;
if( pE->op==TK_DOT ){
+ assert( (selFlags & SF_NestedFrom)==0 );
assert( pE->pLeft!=0 );
assert( !ExprHasProperty(pE->pLeft, EP_IntValue) );
zTName = pE->pLeft->u.zToken;
@@ -146518,6 +147857,7 @@ static int selectExpander(Walker *pWalker, Select *p){
iErrOfst = pE->w.iOfst;
}
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
+ int nAdd; /* Number of cols including rowid */
Table *pTab = pFrom->pTab; /* Table for this data source */
ExprList *pNestedFrom; /* Result-set of a nested FROM clause */
char *zTabName; /* AS name for this data source */
@@ -146535,6 +147875,7 @@ static int selectExpander(Walker *pWalker, Select *p){
pNestedFrom = pFrom->pSelect->pEList;
assert( pNestedFrom!=0 );
assert( pNestedFrom->nExpr==pTab->nCol );
+ assert( VisibleRowid(pTab)==0 );
}else{
if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
continue;
@@ -146565,33 +147906,48 @@ static int selectExpander(Walker *pWalker, Select *p){
}else{
pUsing = 0;
}
- for(j=0; j<pTab->nCol; j++){
- char *zName = pTab->aCol[j].zCnName;
+
+ nAdd = pTab->nCol + (VisibleRowid(pTab) && (selFlags&SF_NestedFrom));
+ for(j=0; j<nAdd; j++){
+ const char *zName;
struct ExprList_item *pX; /* Newly added ExprList term */
- assert( zName );
- if( zTName
- && pNestedFrom
- && sqlite3MatchEName(&pNestedFrom->a[j], 0, zTName, 0)==0
- ){
- continue;
- }
+ if( j==pTab->nCol ){
+ zName = sqlite3RowidAlias(pTab);
+ if( zName==0 ) continue;
+ }else{
+ zName = pTab->aCol[j].zCnName;
- /* If a column is marked as 'hidden', omit it from the expanded
- ** result-set list unless the SELECT has the SF_IncludeHidden
- ** bit set.
- */
- if( (p->selFlags & SF_IncludeHidden)==0
- && IsHiddenColumn(&pTab->aCol[j])
- ){
- continue;
- }
- if( (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0
- && zTName==0
- && (selFlags & (SF_NestedFrom))==0
- ){
- continue;
+ /* If pTab is actually an SF_NestedFrom sub-select, do not
+ ** expand any ENAME_ROWID columns. */
+ if( pNestedFrom && pNestedFrom->a[j].fg.eEName==ENAME_ROWID ){
+ continue;
+ }
+
+ if( zTName
+ && pNestedFrom
+ && sqlite3MatchEName(&pNestedFrom->a[j], 0, zTName, 0, 0)==0
+ ){
+ continue;
+ }
+
+ /* If a column is marked as 'hidden', omit it from the expanded
+ ** result-set list unless the SELECT has the SF_IncludeHidden
+ ** bit set.
+ */
+ if( (p->selFlags & SF_IncludeHidden)==0
+ && IsHiddenColumn(&pTab->aCol[j])
+ ){
+ continue;
+ }
+ if( (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0
+ && zTName==0
+ && (selFlags & (SF_NestedFrom))==0
+ ){
+ continue;
+ }
}
+ assert( zName );
tableSeen = 1;
if( i>0 && zTName==0 && (selFlags & SF_NestedFrom)==0 ){
@@ -146641,11 +147997,11 @@ static int selectExpander(Walker *pWalker, Select *p){
zSchemaName, zTabName, zName);
testcase( pX->zEName==0 );
}
- pX->fg.eEName = ENAME_TAB;
+ pX->fg.eEName = (j==pTab->nCol ? ENAME_ROWID : ENAME_TAB);
if( (pFrom->fg.isUsing
&& sqlite3IdListIndex(pFrom->u3.pUsing, zName)>=0)
|| (pUsing && sqlite3IdListIndex(pUsing, zName)>=0)
- || (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0
+ || (j<pTab->nCol && (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND))
){
pX->fg.bNoExpand = 1;
}
@@ -146747,10 +148103,11 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
SrcList *pTabList;
SrcItem *pFrom;
- assert( p->selFlags & SF_Resolved );
if( p->selFlags & SF_HasTypeInfo ) return;
p->selFlags |= SF_HasTypeInfo;
pParse = pWalker->pParse;
+ testcase( (p->selFlags & SF_Resolved)==0 );
+ assert( (p->selFlags & SF_Resolved) || IN_RENAME_OBJECT );
pTabList = p->pSrc;
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
Table *pTab = pFrom->pTab;
@@ -146866,8 +148223,14 @@ static void analyzeAggFuncArgs(
pNC->ncFlags |= NC_InAggFunc;
for(i=0; i<pAggInfo->nFunc; i++){
Expr *pExpr = pAggInfo->aFunc[i].pFExpr;
+ assert( pExpr->op==TK_FUNCTION || pExpr->op==TK_AGG_FUNCTION );
assert( ExprUseXList(pExpr) );
sqlite3ExprAnalyzeAggList(pNC, pExpr->x.pList);
+ if( pExpr->pLeft ){
+ assert( pExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pExpr->pLeft) );
+ sqlite3ExprAnalyzeAggList(pNC, pExpr->pLeft->x.pList);
+ }
#ifndef SQLITE_OMIT_WINDOWFUNC
assert( !IsWindowFunc(pExpr) );
if( ExprHasProperty(pExpr, EP_WinFunc) ){
@@ -147022,6 +148385,36 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
pFunc->pFunc->zName));
}
}
+ if( pFunc->iOBTab>=0 ){
+ ExprList *pOBList;
+ KeyInfo *pKeyInfo;
+ int nExtra = 0;
+ assert( pFunc->pFExpr->pLeft!=0 );
+ assert( pFunc->pFExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pFunc->pFExpr->pLeft) );
+ assert( pFunc->pFunc!=0 );
+ pOBList = pFunc->pFExpr->pLeft->x.pList;
+ if( !pFunc->bOBUnique ){
+ nExtra++; /* One extra column for the OP_Sequence */
+ }
+ if( pFunc->bOBPayload ){
+ /* extra columns for the function arguments */
+ assert( ExprUseXList(pFunc->pFExpr) );
+ nExtra += pFunc->pFExpr->x.pList->nExpr;
+ }
+ if( pFunc->bUseSubtype ){
+ nExtra += pFunc->pFExpr->x.pList->nExpr;
+ }
+ pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOBList, 0, nExtra);
+ if( !pFunc->bOBUnique && pParse->nErr==0 ){
+ pKeyInfo->nKeyField++;
+ }
+ sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
+ pFunc->iOBTab, pOBList->nExpr+nExtra, 0,
+ (char*)pKeyInfo, P4_KEYINFO);
+ ExplainQueryPlan((pParse, 0, "USE TEMP B-TREE FOR %s(ORDER BY)",
+ pFunc->pFunc->zName));
+ }
}
}
@@ -147037,13 +148430,56 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
ExprList *pList;
assert( ExprUseXList(pF->pFExpr) );
pList = pF->pFExpr->x.pList;
+ if( pF->iOBTab>=0 ){
+ /* For an ORDER BY aggregate, calls to OP_AggStep were deferred. Inputs
+ ** were stored in emphermal table pF->iOBTab. Here, we extract those
+ ** inputs (in ORDER BY order) and make all calls to OP_AggStep
+ ** before doing the OP_AggFinal call. */
+ int iTop; /* Start of loop for extracting columns */
+ int nArg; /* Number of columns to extract */
+ int nKey; /* Key columns to be skipped */
+ int regAgg; /* Extract into this array */
+ int j; /* Loop counter */
+
+ assert( pF->pFunc!=0 );
+ nArg = pList->nExpr;
+ regAgg = sqlite3GetTempRange(pParse, nArg);
+
+ if( pF->bOBPayload==0 ){
+ nKey = 0;
+ }else{
+ assert( pF->pFExpr->pLeft!=0 );
+ assert( ExprUseXList(pF->pFExpr->pLeft) );
+ assert( pF->pFExpr->pLeft->x.pList!=0 );
+ nKey = pF->pFExpr->pLeft->x.pList->nExpr;
+ if( ALWAYS(!pF->bOBUnique) ) nKey++;
+ }
+ iTop = sqlite3VdbeAddOp1(v, OP_Rewind, pF->iOBTab); VdbeCoverage(v);
+ for(j=nArg-1; j>=0; j--){
+ sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, nKey+j, regAgg+j);
+ }
+ if( pF->bUseSubtype ){
+ int regSubtype = sqlite3GetTempReg(pParse);
+ int iBaseCol = nKey + nArg + (pF->bOBPayload==0 && pF->bOBUnique==0);
+ for(j=nArg-1; j>=0; j--){
+ sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, iBaseCol+j, regSubtype);
+ sqlite3VdbeAddOp2(v, OP_SetSubtype, regSubtype, regAgg+j);
+ }
+ sqlite3ReleaseTempReg(pParse, regSubtype);
+ }
+ sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
+ sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, (u8)nArg);
+ sqlite3VdbeAddOp2(v, OP_Next, pF->iOBTab, iTop+1); VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, iTop);
+ sqlite3ReleaseTempRange(pParse, regAgg, nArg);
+ }
sqlite3VdbeAddOp2(v, OP_AggFinal, AggInfoFuncReg(pAggInfo,i),
pList ? pList->nExpr : 0);
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
}
}
-
/*
** Generate code that will update the accumulator memory cells for an
** aggregate based on the current cursor position.
@@ -147052,6 +148488,13 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
** in pAggInfo, then only populate the pAggInfo->nAccumulator accumulator
** registers if register regAcc contains 0. The caller will take care
** of setting and clearing regAcc.
+**
+** For an ORDER BY aggregate, the actual accumulator memory cell update
+** is deferred until after all input rows have been received, so that they
+** can be run in the requested order. In that case, instead of invoking
+** OP_AggStep to update the accumulator, just add the arguments that would
+** have been passed into OP_AggStep into the sorting ephemeral table
+** (along with the appropriate sort key).
*/
static void updateAccumulator(
Parse *pParse,
@@ -147073,9 +148516,12 @@ static void updateAccumulator(
int nArg;
int addrNext = 0;
int regAgg;
+ int regAggSz = 0;
+ int regDistinct = 0;
ExprList *pList;
assert( ExprUseXList(pF->pFExpr) );
assert( !IsWindowFunc(pF->pFExpr) );
+ assert( pF->pFunc!=0 );
pList = pF->pFExpr->x.pList;
if( ExprHasProperty(pF->pFExpr, EP_WinFunc) ){
Expr *pFilter = pF->pFExpr->y.pWin->pFilter;
@@ -147099,9 +148545,55 @@ static void updateAccumulator(
addrNext = sqlite3VdbeMakeLabel(pParse);
sqlite3ExprIfFalse(pParse, pFilter, addrNext, SQLITE_JUMPIFNULL);
}
- if( pList ){
+ if( pF->iOBTab>=0 ){
+ /* Instead of invoking AggStep, we must push the arguments that would
+ ** have been passed to AggStep onto the sorting table. */
+ int jj; /* Registered used so far in building the record */
+ ExprList *pOBList; /* The ORDER BY clause */
+ assert( pList!=0 );
+ nArg = pList->nExpr;
+ assert( nArg>0 );
+ assert( pF->pFExpr->pLeft!=0 );
+ assert( pF->pFExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pF->pFExpr->pLeft) );
+ pOBList = pF->pFExpr->pLeft->x.pList;
+ assert( pOBList!=0 );
+ assert( pOBList->nExpr>0 );
+ regAggSz = pOBList->nExpr;
+ if( !pF->bOBUnique ){
+ regAggSz++; /* One register for OP_Sequence */
+ }
+ if( pF->bOBPayload ){
+ regAggSz += nArg;
+ }
+ if( pF->bUseSubtype ){
+ regAggSz += nArg;
+ }
+ regAggSz++; /* One extra register to hold result of MakeRecord */
+ regAgg = sqlite3GetTempRange(pParse, regAggSz);
+ regDistinct = regAgg;
+ sqlite3ExprCodeExprList(pParse, pOBList, regAgg, 0, SQLITE_ECEL_DUP);
+ jj = pOBList->nExpr;
+ if( !pF->bOBUnique ){
+ sqlite3VdbeAddOp2(v, OP_Sequence, pF->iOBTab, regAgg+jj);
+ jj++;
+ }
+ if( pF->bOBPayload ){
+ regDistinct = regAgg+jj;
+ sqlite3ExprCodeExprList(pParse, pList, regDistinct, 0, SQLITE_ECEL_DUP);
+ jj += nArg;
+ }
+ if( pF->bUseSubtype ){
+ int kk;
+ int regBase = pF->bOBPayload ? regDistinct : regAgg;
+ for(kk=0; kk<nArg; kk++, jj++){
+ sqlite3VdbeAddOp2(v, OP_GetSubtype, regBase+kk, regAgg+jj);
+ }
+ }
+ }else if( pList ){
nArg = pList->nExpr;
regAgg = sqlite3GetTempRange(pParse, nArg);
+ regDistinct = regAgg;
sqlite3ExprCodeExprList(pParse, pList, regAgg, 0, SQLITE_ECEL_DUP);
}else{
nArg = 0;
@@ -147112,26 +148604,37 @@ static void updateAccumulator(
addrNext = sqlite3VdbeMakeLabel(pParse);
}
pF->iDistinct = codeDistinct(pParse, eDistinctType,
- pF->iDistinct, addrNext, pList, regAgg);
- }
- if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
- CollSeq *pColl = 0;
- struct ExprList_item *pItem;
- int j;
- assert( pList!=0 ); /* pList!=0 if pF->pFunc has NEEDCOLL */
- for(j=0, pItem=pList->a; !pColl && j<nArg; j++, pItem++){
- pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr);
- }
- if( !pColl ){
- pColl = pParse->db->pDfltColl;
+ pF->iDistinct, addrNext, pList, regDistinct);
+ }
+ if( pF->iOBTab>=0 ){
+ /* Insert a new record into the ORDER BY table */
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regAgg, regAggSz-1,
+ regAgg+regAggSz-1);
+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pF->iOBTab, regAgg+regAggSz-1,
+ regAgg, regAggSz-1);
+ sqlite3ReleaseTempRange(pParse, regAgg, regAggSz);
+ }else{
+ /* Invoke the AggStep function */
+ if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
+ CollSeq *pColl = 0;
+ struct ExprList_item *pItem;
+ int j;
+ assert( pList!=0 ); /* pList!=0 if pF->pFunc has NEEDCOLL */
+ for(j=0, pItem=pList->a; !pColl && j<nArg; j++, pItem++){
+ pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr);
+ }
+ if( !pColl ){
+ pColl = pParse->db->pDfltColl;
+ }
+ if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem;
+ sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0,
+ (char *)pColl, P4_COLLSEQ);
}
- if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem;
- sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, (char *)pColl, P4_COLLSEQ);
+ sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
+ sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, (u8)nArg);
+ sqlite3ReleaseTempRange(pParse, regAgg, nArg);
}
- sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
- sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, (u8)nArg);
- sqlite3ReleaseTempRange(pParse, regAgg, nArg);
if( addrNext ){
sqlite3VdbeResolveLabel(v, addrNext);
}
@@ -147290,7 +148793,8 @@ static SrcItem *isSelfJoinView(
/*
** Deallocate a single AggInfo object
*/
-static void agginfoFree(sqlite3 *db, AggInfo *p){
+static void agginfoFree(sqlite3 *db, void *pArg){
+ AggInfo *p = (AggInfo*)pArg;
sqlite3DbFree(db, p->aCol);
sqlite3DbFree(db, p->aFunc);
sqlite3DbFreeNN(db, p);
@@ -147364,7 +148868,7 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
pSub->selFlags |= SF_Aggregate;
pSub->selFlags &= ~SF_Compound;
pSub->nSelectRow = 0;
- sqlite3ExprListDelete(db, pSub->pEList);
+ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pSub->pEList);
pTerm = pPrior ? sqlite3ExprDup(db, pCount, 0) : pCount;
pSub->pEList = sqlite3ExprListAppend(pParse, 0, pTerm);
pTerm = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
@@ -147544,9 +149048,8 @@ SQLITE_PRIVATE int sqlite3Select(
sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY");
}
#endif
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3ExprListDelete,
- p->pOrderBy);
+ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric,
+ p->pOrderBy);
testcase( pParse->earlyCleanup );
p->pOrderBy = 0;
}
@@ -147652,6 +149155,7 @@ SQLITE_PRIVATE int sqlite3Select(
TREETRACE(0x1000,pParse,p,
("LEFT-JOIN simplifies to JOIN on term %d\n",i));
pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER);
+ unsetJoinExpr(p->pWhere, pItem->iCursor, 0);
}
}
if( pItem->fg.jointype & JT_LTORJ ){
@@ -147666,17 +149170,15 @@ SQLITE_PRIVATE int sqlite3Select(
TREETRACE(0x1000,pParse,p,
("RIGHT-JOIN simplifies to JOIN on term %d\n",j));
pI2->fg.jointype &= ~(JT_RIGHT|JT_OUTER);
+ unsetJoinExpr(p->pWhere, pI2->iCursor, 1);
}
}
}
- for(j=pTabList->nSrc-1; j>=i; j--){
+ for(j=pTabList->nSrc-1; j>=0; j--){
pTabList->a[j].fg.jointype &= ~JT_LTORJ;
if( pTabList->a[j].fg.jointype & JT_RIGHT ) break;
}
}
- assert( pItem->iCursor>=0 );
- unsetJoinExpr(p->pWhere, pItem->iCursor,
- pTabList->a[0].fg.jointype & JT_LTORJ);
}
/* No further action if this term of the FROM clause is not a subquery */
@@ -147739,9 +149241,8 @@ SQLITE_PRIVATE int sqlite3Select(
){
TREETRACE(0x800,pParse,p,
("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1));
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3ExprListDelete,
- pSub->pOrderBy);
+ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric,
+ pSub->pOrderBy);
pSub->pOrderBy = 0;
}
@@ -148270,8 +149771,7 @@ SQLITE_PRIVATE int sqlite3Select(
*/
pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) );
if( pAggInfo ){
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))agginfoFree, pAggInfo);
+ sqlite3ParserAddCleanup(pParse, agginfoFree, pAggInfo);
testcase( pParse->earlyCleanup );
}
if( db->mallocFailed ){
@@ -149192,6 +150692,10 @@ SQLITE_PRIVATE void sqlite3BeginTrigger(
sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables");
goto trigger_orphan_error;
}
+ if( (pTab->tabFlags & TF_Shadow)!=0 && sqlite3ReadOnlyShadowTables(db) ){
+ sqlite3ErrorMsg(pParse, "cannot create triggers on shadow tables");
+ goto trigger_orphan_error;
+ }
/* Check that the trigger name is not reserved and that no trigger of the
** specified name exists */
@@ -149975,10 +151479,17 @@ static void codeReturningTrigger(
SrcList sFrom;
assert( v!=0 );
- assert( pParse->bReturning );
+ if( !pParse->bReturning ){
+ /* This RETURNING trigger must be for a different statement as
+ ** this statement lacks a RETURNING clause. */
+ return;
+ }
assert( db->pParse==pParse );
pReturning = pParse->u1.pReturning;
- assert( pTrigger == &(pReturning->retTrig) );
+ if( pTrigger != &(pReturning->retTrig) ){
+ /* This RETURNING trigger is for a different statement */
+ return;
+ }
memset(&sSelect, 0, sizeof(sSelect));
memset(&sFrom, 0, sizeof(sFrom));
sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0);
@@ -152909,7 +154420,6 @@ SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3 *db){
if( p ){
db->pDisconnect = 0;
- sqlite3ExpirePreparedStatements(db, 0);
do {
VTable *pNext = p->pNext;
sqlite3VtabUnlock(p);
@@ -153415,7 +154925,7 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
sqlite3_mutex_enter(db->mutex);
pCtx = db->pVtabCtx;
if( !pCtx || pCtx->bDeclared ){
- sqlite3Error(db, SQLITE_MISUSE);
+ sqlite3Error(db, SQLITE_MISUSE_BKPT);
sqlite3_mutex_leave(db->mutex);
return SQLITE_MISUSE_BKPT;
}
@@ -154475,7 +155985,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet*,int);
#ifdef WHERETRACE_ENABLED
SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC);
SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm);
-SQLITE_PRIVATE void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC);
+SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC);
#endif
SQLITE_PRIVATE WhereTerm *sqlite3WhereFindTerm(
WhereClause *pWC, /* The WHERE clause to be searched */
@@ -154606,7 +156116,7 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*);
#define WHERE_BLOOMFILTER 0x00400000 /* Consider using a Bloom-filter */
#define WHERE_SELFCULL 0x00800000 /* nOut reduced by extra WHERE terms */
#define WHERE_OMIT_OFFSET 0x01000000 /* Set offset counter to zero */
-#define WHERE_VIEWSCAN 0x02000000 /* A full-scan of a VIEW or subquery */
+ /* 0x02000000 -- available for reuse */
#define WHERE_EXPRIDX 0x04000000 /* Uses an index-on-expressions */
#endif /* !defined(SQLITE_WHEREINT_H) */
@@ -159937,12 +161447,22 @@ static void translateColumnToCopy(
for(; iStart<iEnd; iStart++, pOp++){
if( pOp->p1!=iTabCur ) continue;
if( pOp->opcode==OP_Column ){
+#ifdef SQLITE_DEBUG
+ if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
+ printf("TRANSLATE OP_Column to OP_Copy at %d\n", iStart);
+ }
+#endif
pOp->opcode = OP_Copy;
pOp->p1 = pOp->p2 + iRegister;
pOp->p2 = pOp->p3;
pOp->p3 = 0;
pOp->p5 = 2; /* Cause the MEM_Subtype flag to be cleared */
}else if( pOp->opcode==OP_Rowid ){
+#ifdef SQLITE_DEBUG
+ if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
+ printf("TRANSLATE OP_Rowid to OP_Sequence at %d\n", iStart);
+ }
+#endif
pOp->opcode = OP_Sequence;
pOp->p1 = iAutoidxCur;
#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
@@ -160401,13 +161921,17 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
WhereLoop *pLoop = pLevel->pWLoop; /* The loop being coded */
int iCur; /* Cursor for table getting the filter */
IndexedExpr *saved_pIdxEpr; /* saved copy of Parse.pIdxEpr */
+ IndexedExpr *saved_pIdxPartExpr; /* saved copy of Parse.pIdxPartExpr */
saved_pIdxEpr = pParse->pIdxEpr;
+ saved_pIdxPartExpr = pParse->pIdxPartExpr;
pParse->pIdxEpr = 0;
+ pParse->pIdxPartExpr = 0;
assert( pLoop!=0 );
assert( v!=0 );
assert( pLoop->wsFlags & WHERE_BLOOMFILTER );
+ assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 );
addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
do{
@@ -160497,6 +162021,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
}while( iLevel < pWInfo->nLevel );
sqlite3VdbeJumpHere(v, addrOnce);
pParse->pIdxEpr = saved_pIdxEpr;
+ pParse->pIdxPartExpr = saved_pIdxPartExpr;
}
@@ -161264,7 +162789,8 @@ static int whereRangeScanEst(
** sample, then assume they are 4x more selective. This brings
** the estimated selectivity more in line with what it would be
** if estimated without the use of STAT4 tables. */
- if( iLwrIdx==iUprIdx ) nNew -= 20; assert( 20==sqlite3LogEst(4) );
+ if( iLwrIdx==iUprIdx ){ nNew -= 20; }
+ assert( 20==sqlite3LogEst(4) );
}else{
nNew = 10; assert( 10==sqlite3LogEst(2) );
}
@@ -161488,17 +163014,34 @@ SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC){
#ifdef WHERETRACE_ENABLED
/*
** Print a WhereLoop object for debugging purposes
-*/
-SQLITE_PRIVATE void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC){
- WhereInfo *pWInfo = pWC->pWInfo;
- int nb = 1+(pWInfo->pTabList->nSrc+3)/4;
- SrcItem *pItem = pWInfo->pTabList->a + p->iTab;
- Table *pTab = pItem->pTab;
- Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1;
- sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId,
- p->iTab, nb, p->maskSelf, nb, p->prereq & mAll);
- sqlite3DebugPrintf(" %12s",
- pItem->zAlias ? pItem->zAlias : pTab->zName);
+**
+** Format example:
+**
+** .--- Position in WHERE clause rSetup, rRun, nOut ---.
+** | |
+** | .--- selfMask nTerm ------. |
+** | | | |
+** | | .-- prereq Idx wsFlags----. | |
+** | | | Name | | |
+** | | | __|__ nEq ---. ___|__ | __|__
+** | / \ / \ / \ | / \ / \ / \
+** 1.002.001 t2.t2xy 2 f 010241 N 2 cost 0,56,31
+*/
+SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC){
+ if( pWC ){
+ WhereInfo *pWInfo = pWC->pWInfo;
+ int nb = 1+(pWInfo->pTabList->nSrc+3)/4;
+ SrcItem *pItem = pWInfo->pTabList->a + p->iTab;
+ Table *pTab = pItem->pTab;
+ Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1;
+ sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId,
+ p->iTab, nb, p->maskSelf, nb, p->prereq & mAll);
+ sqlite3DebugPrintf(" %12s",
+ pItem->zAlias ? pItem->zAlias : pTab->zName);
+ }else{
+ sqlite3DebugPrintf("%c%2d.%03llx.%03llx %c%d",
+ p->cId, p->iTab, p->maskSelf, p->prereq & 0xfff, p->cId, p->iTab);
+ }
if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
const char *zName;
if( p->u.btree.pIndex && (zName = p->u.btree.pIndex->zName)!=0 ){
@@ -161535,6 +163078,15 @@ SQLITE_PRIVATE void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC){
}
}
}
+SQLITE_PRIVATE void sqlite3ShowWhereLoop(const WhereLoop *p){
+ if( p ) sqlite3WhereLoopPrint(p, 0);
+}
+SQLITE_PRIVATE void sqlite3ShowWhereLoopList(const WhereLoop *p){
+ while( p ){
+ sqlite3ShowWhereLoop(p);
+ p = p->pNextLoop;
+ }
+}
#endif
/*
@@ -161647,46 +163199,60 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
}
/*
-** Return TRUE if all of the following are true:
+** Return TRUE if X is a proper subset of Y but is of equal or less cost.
+** In other words, return true if all constraints of X are also part of Y
+** and Y has additional constraints that might speed the search that X lacks
+** but the cost of running X is not more than the cost of running Y.
+**
+** In other words, return true if the cost relationwship between X and Y
+** is inverted and needs to be adjusted.
+**
+** Case 1:
+**
+** (1a) X and Y use the same index.
+** (1b) X has fewer == terms than Y
+** (1c) Neither X nor Y use skip-scan
+** (1d) X does not have a a greater cost than Y
**
-** (1) X has the same or lower cost, or returns the same or fewer rows,
-** than Y.
-** (2) X uses fewer WHERE clause terms than Y
-** (3) Every WHERE clause term used by X is also used by Y
-** (4) X skips at least as many columns as Y
-** (5) If X is a covering index, than Y is too
+** Case 2:
**
-** Conditions (2) and (3) mean that X is a "proper subset" of Y.
-** If X is a proper subset of Y then Y is a better choice and ought
-** to have a lower cost. This routine returns TRUE when that cost
-** relationship is inverted and needs to be adjusted. Constraint (4)
-** was added because if X uses skip-scan less than Y it still might
-** deserve a lower cost even if it is a proper subset of Y. Constraint (5)
-** was added because a covering index probably deserves to have a lower cost
-** than a non-covering index even if it is a proper subset.
+** (2a) X has the same or lower cost, or returns the same or fewer rows,
+** than Y.
+** (2b) X uses fewer WHERE clause terms than Y
+** (2c) Every WHERE clause term used by X is also used by Y
+** (2d) X skips at least as many columns as Y
+** (2e) If X is a covering index, than Y is too
*/
static int whereLoopCheaperProperSubset(
const WhereLoop *pX, /* First WhereLoop to compare */
const WhereLoop *pY /* Compare against this WhereLoop */
){
int i, j;
+ if( pX->rRun>pY->rRun && pX->nOut>pY->nOut ) return 0; /* (1d) and (2a) */
+ assert( (pX->wsFlags & WHERE_VIRTUALTABLE)==0 );
+ assert( (pY->wsFlags & WHERE_VIRTUALTABLE)==0 );
+ if( pX->u.btree.nEq < pY->u.btree.nEq /* (1b) */
+ && pX->u.btree.pIndex==pY->u.btree.pIndex /* (1a) */
+ && pX->nSkip==0 && pY->nSkip==0 /* (1c) */
+ ){
+ return 1; /* Case 1 is true */
+ }
if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){
- return 0; /* X is not a subset of Y */
+ return 0; /* (2b) */
}
- if( pX->rRun>pY->rRun && pX->nOut>pY->nOut ) return 0;
- if( pY->nSkip > pX->nSkip ) return 0;
+ if( pY->nSkip > pX->nSkip ) return 0; /* (2d) */
for(i=pX->nLTerm-1; i>=0; i--){
if( pX->aLTerm[i]==0 ) continue;
for(j=pY->nLTerm-1; j>=0; j--){
if( pY->aLTerm[j]==pX->aLTerm[i] ) break;
}
- if( j<0 ) return 0; /* X not a subset of Y since term X[i] not used by Y */
+ if( j<0 ) return 0; /* (2c) */
}
if( (pX->wsFlags&WHERE_IDX_ONLY)!=0
&& (pY->wsFlags&WHERE_IDX_ONLY)==0 ){
- return 0; /* Constraint (5) */
+ return 0; /* (2e) */
}
- return 1; /* All conditions meet */
+ return 1; /* Case 2 is true */
}
/*
@@ -162176,7 +163742,10 @@ static int whereLoopAddBtreeIndex(
assert( pNew->u.btree.nBtm==0 );
opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
}
- if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
+ if( pProbe->bUnordered || pProbe->bLowQual ){
+ if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
+ if( pProbe->bLowQual ) opMask &= ~(WO_EQ|WO_IN|WO_IS);
+ }
assert( pNew->u.btree.nEq<pProbe->nColumn );
assert( pNew->u.btree.nEq<pProbe->nKeyCol
@@ -162757,6 +164326,100 @@ static SQLITE_NOINLINE u32 whereIsCoveringIndex(
}
/*
+** This is an sqlite3ParserAddCleanup() callback that is invoked to
+** free the Parse->pIdxEpr list when the Parse object is destroyed.
+*/
+static void whereIndexedExprCleanup(sqlite3 *db, void *pObject){
+ IndexedExpr **pp = (IndexedExpr**)pObject;
+ while( *pp!=0 ){
+ IndexedExpr *p = *pp;
+ *pp = p->pIENext;
+ sqlite3ExprDelete(db, p->pExpr);
+ sqlite3DbFreeNN(db, p);
+ }
+}
+
+/*
+** This function is called for a partial index - one with a WHERE clause - in
+** two scenarios. In both cases, it determines whether or not the WHERE
+** clause on the index implies that a column of the table may be safely
+** replaced by a constant expression. For example, in the following
+** SELECT:
+**
+** CREATE INDEX i1 ON t1(b, c) WHERE a=<expr>;
+** SELECT a, b, c FROM t1 WHERE a=<expr> AND b=?;
+**
+** The "a" in the select-list may be replaced by <expr>, iff:
+**
+** (a) <expr> is a constant expression, and
+** (b) The (a=<expr>) comparison uses the BINARY collation sequence, and
+** (c) Column "a" has an affinity other than NONE or BLOB.
+**
+** If argument pItem is NULL, then pMask must not be NULL. In this case this
+** function is being called as part of determining whether or not pIdx
+** is a covering index. This function clears any bits in (*pMask)
+** corresponding to columns that may be replaced by constants as described
+** above.
+**
+** Otherwise, if pItem is not NULL, then this function is being called
+** as part of coding a loop that uses index pIdx. In this case, add entries
+** to the Parse.pIdxPartExpr list for each column that can be replaced
+** by a constant.
+*/
+static void wherePartIdxExpr(
+ Parse *pParse, /* Parse context */
+ Index *pIdx, /* Partial index being processed */
+ Expr *pPart, /* WHERE clause being processed */
+ Bitmask *pMask, /* Mask to clear bits in */
+ int iIdxCur, /* Cursor number for index */
+ SrcItem *pItem /* The FROM clause entry for the table */
+){
+ assert( pItem==0 || (pItem->fg.jointype & JT_RIGHT)==0 );
+ assert( (pItem==0 || pMask==0) && (pMask!=0 || pItem!=0) );
+
+ if( pPart->op==TK_AND ){
+ wherePartIdxExpr(pParse, pIdx, pPart->pRight, pMask, iIdxCur, pItem);
+ pPart = pPart->pLeft;
+ }
+
+ if( (pPart->op==TK_EQ || pPart->op==TK_IS) ){
+ Expr *pLeft = pPart->pLeft;
+ Expr *pRight = pPart->pRight;
+ u8 aff;
+
+ if( pLeft->op!=TK_COLUMN ) return;
+ if( !sqlite3ExprIsConstant(pRight) ) return;
+ if( !sqlite3IsBinary(sqlite3ExprCompareCollSeq(pParse, pPart)) ) return;
+ if( pLeft->iColumn<0 ) return;
+ aff = pIdx->pTable->aCol[pLeft->iColumn].affinity;
+ if( aff>=SQLITE_AFF_TEXT ){
+ if( pItem ){
+ sqlite3 *db = pParse->db;
+ IndexedExpr *p = (IndexedExpr*)sqlite3DbMallocRaw(db, sizeof(*p));
+ if( p ){
+ int bNullRow = (pItem->fg.jointype&(JT_LEFT|JT_LTORJ))!=0;
+ p->pExpr = sqlite3ExprDup(db, pRight, 0);
+ p->iDataCur = pItem->iCursor;
+ p->iIdxCur = iIdxCur;
+ p->iIdxCol = pLeft->iColumn;
+ p->bMaybeNullRow = bNullRow;
+ p->pIENext = pParse->pIdxPartExpr;
+ p->aff = aff;
+ pParse->pIdxPartExpr = p;
+ if( p->pIENext==0 ){
+ void *pArg = (void*)&pParse->pIdxPartExpr;
+ sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pArg);
+ }
+ }
+ }else if( pLeft->iColumn<(BMS-1) ){
+ *pMask &= ~((Bitmask)1 << pLeft->iColumn);
+ }
+ }
+ }
+}
+
+
+/*
** Add all WhereLoop objects for a single table of the join where the table
** is identified by pBuilder->pNew->iTab. That table is guaranteed to be
** a b-tree table, not a virtual table.
@@ -162959,9 +164622,6 @@ static int whereLoopAddBtree(
#else
pNew->rRun = rSize + 16;
#endif
- if( IsView(pTab) || (pTab->tabFlags & TF_Ephemeral)!=0 ){
- pNew->wsFlags |= WHERE_VIEWSCAN;
- }
ApplyCostMultiplier(pNew->rRun, pTab->costMult);
whereLoopOutputAdjust(pWC, pNew, rSize);
rc = whereLoopInsert(pBuilder, pNew);
@@ -162974,6 +164634,11 @@ static int whereLoopAddBtree(
pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED;
}else{
m = pSrc->colUsed & pProbe->colNotIdxed;
+ if( pProbe->pPartIdxWhere ){
+ wherePartIdxExpr(
+ pWInfo->pParse, pProbe, pProbe->pPartIdxWhere, &m, 0, 0
+ );
+ }
pNew->wsFlags = WHERE_INDEXED;
if( m==TOPBIT || (pProbe->bHasExpr && !pProbe->bHasVCol && m!=0) ){
u32 isCov = whereIsCoveringIndex(pWInfo, pProbe, pSrc->iCursor);
@@ -163356,7 +165021,7 @@ SQLITE_API int sqlite3_vtab_rhs_value(
sqlite3_value *pVal = 0;
int rc = SQLITE_OK;
if( iCons<0 || iCons>=pIdxInfo->nConstraint ){
- rc = SQLITE_MISUSE; /* EV: R-30545-25046 */
+ rc = SQLITE_MISUSE_BKPT; /* EV: R-30545-25046 */
}else{
if( pH->aRhs[iCons]==0 ){
WhereTerm *pTerm = &pH->pWC->a[pIdxInfo->aConstraint[iCons].iTermOffset];
@@ -164380,14 +166045,6 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
rUnsorted -= 2; /* TUNING: Slight bias in favor of no-sort plans */
}
- /* TUNING: A full-scan of a VIEW or subquery in the outer loop
- ** is not so bad. */
- if( iLoop==0 && (pWLoop->wsFlags & WHERE_VIEWSCAN)!=0 && nLoop>1 ){
- rCost += -10;
- nOut += -30;
- WHERETRACE(0x80,("VIEWSCAN cost reduction for %c\n",pWLoop->cId));
- }
-
/* Check to see if pWLoop should be added to the set of
** mxChoice best-so-far paths.
**
@@ -164938,20 +166595,6 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
}
/*
-** This is an sqlite3ParserAddCleanup() callback that is invoked to
-** free the Parse->pIdxEpr list when the Parse object is destroyed.
-*/
-static void whereIndexedExprCleanup(sqlite3 *db, void *pObject){
- Parse *pParse = (Parse*)pObject;
- while( pParse->pIdxEpr!=0 ){
- IndexedExpr *p = pParse->pIdxEpr;
- pParse->pIdxEpr = p->pIENext;
- sqlite3ExprDelete(db, p->pExpr);
- sqlite3DbFreeNN(db, p);
- }
-}
-
-/*
** The index pIdx is used by a query and contains one or more expressions.
** In other words pIdx is an index on an expression. iIdxCur is the cursor
** number for the index and iDataCur is the cursor number for the corresponding
@@ -164990,6 +166633,20 @@ static SQLITE_NOINLINE void whereAddIndexedExpr(
continue;
}
if( sqlite3ExprIsConstant(pExpr) ) continue;
+ if( pExpr->op==TK_FUNCTION ){
+ /* Functions that might set a subtype should not be replaced by the
+ ** value taken from an expression index since the index omits the
+ ** subtype. https://sqlite.org/forum/forumpost/68d284c86b082c3e */
+ int n;
+ FuncDef *pDef;
+ sqlite3 *db = pParse->db;
+ assert( ExprUseXList(pExpr) );
+ n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0;
+ pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0);
+ if( pDef==0 || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){
+ continue;
+ }
+ }
p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr));
if( p==0 ) break;
p->pIENext = pParse->pIdxEpr;
@@ -165012,7 +166669,8 @@ static SQLITE_NOINLINE void whereAddIndexedExpr(
#endif
pParse->pIdxEpr = p;
if( p->pIENext==0 ){
- sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pParse);
+ void *pArg = (void*)&pParse->pIdxEpr;
+ sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pArg);
}
}
}
@@ -165167,7 +166825,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
/* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */
testcase( pOrderBy && pOrderBy->nExpr==BMS-1 );
- if( pOrderBy && pOrderBy->nExpr>=BMS ) pOrderBy = 0;
+ if( pOrderBy && pOrderBy->nExpr>=BMS ){
+ pOrderBy = 0;
+ wctrlFlags &= ~WHERE_WANT_DISTINCT;
+ }
/* The number of tables in the FROM clause is limited by the number of
** bits in a Bitmask
@@ -165192,7 +166853,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
** field (type Bitmask) it must be aligned on an 8-byte boundary on
** some architectures. Hence the ROUND8() below.
*/
- nByteWInfo = ROUND8P(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel));
+ nByteWInfo = ROUND8P(sizeof(WhereInfo));
+ if( nTabList>1 ){
+ nByteWInfo = ROUND8P(nByteWInfo + (nTabList-1)*sizeof(WhereLevel));
+ }
pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop));
if( db->mallocFailed ){
sqlite3DbFree(db, pWInfo);
@@ -165402,6 +167066,16 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
wherePathSolver(pWInfo, pWInfo->nRowOut+1);
if( db->mallocFailed ) goto whereBeginError;
}
+
+ /* TUNING: Assume that a DISTINCT clause on a subquery reduces
+ ** the output size by a factor of 8 (LogEst -30).
+ */
+ if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)!=0 ){
+ WHERETRACE(0x0080,("nRowOut reduced from %d to %d due to DISTINCT\n",
+ pWInfo->nRowOut, pWInfo->nRowOut-30));
+ pWInfo->nRowOut -= 30;
+ }
+
}
assert( pWInfo->pTabList!=0 );
if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){
@@ -165614,6 +167288,11 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
if( pIx->bHasExpr && OptimizationEnabled(db, SQLITE_IndexedExpr) ){
whereAddIndexedExpr(pParse, pIx, iIndexCur, pTabItem);
}
+ if( pIx->pPartIdxWhere && (pTabItem->fg.jointype & JT_RIGHT)==0 ){
+ wherePartIdxExpr(
+ pParse, pIx, pIx->pPartIdxWhere, 0, iIndexCur, pTabItem
+ );
+ }
}
pLevel->iIdxCur = iIndexCur;
assert( pIx!=0 );
@@ -165739,6 +167418,11 @@ whereBeginError:
pParse->nQueryLoop = pWInfo->savedNQueryLoop;
whereInfoFree(db, pWInfo);
}
+#ifdef WHERETRACE_ENABLED
+ /* Prevent harmless compiler warnings about debugging routines
+ ** being declared but never used */
+ sqlite3ShowWhereLoopList(0);
+#endif /* WHERETRACE_ENABLED */
return 0;
}
@@ -167156,7 +168840,7 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
assert( ExprUseXList(pWin->pOwner) );
assert( pWin->pWFunc!=0 );
pArgs = pWin->pOwner->x.pList;
- if( pWin->pWFunc->funcFlags & SQLITE_FUNC_SUBTYPE ){
+ if( pWin->pWFunc->funcFlags & SQLITE_SUBTYPE ){
selectWindowRewriteEList(pParse, pMWin, pSrc, pArgs, pTab, &pSublist);
pWin->iArgCol = (pSublist ? pSublist->nExpr : 0);
pWin->bExprArgs = 1;
@@ -167430,8 +169114,9 @@ SQLITE_PRIVATE void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
if( p ){
assert( p->op==TK_FUNCTION );
assert( pWin );
+ assert( ExprIsFullSize(p) );
p->y.pWin = pWin;
- ExprSetProperty(p, EP_WinFunc);
+ ExprSetProperty(p, EP_WinFunc|EP_FullSize);
pWin->pOwner = p;
if( (p->flags & EP_Distinct) && pWin->eFrmType!=TK_FILTER ){
sqlite3ErrorMsg(pParse,
@@ -169733,18 +171418,18 @@ typedef union {
#define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse;
#define sqlite3ParserCTX_STORE yypParser->pParse=pParse;
#define YYFALLBACK 1
-#define YYNSTATE 575
-#define YYNRULE 403
-#define YYNRULE_WITH_ACTION 338
+#define YYNSTATE 579
+#define YYNRULE 405
+#define YYNRULE_WITH_ACTION 340
#define YYNTOKEN 185
-#define YY_MAX_SHIFT 574
-#define YY_MIN_SHIFTREDUCE 833
-#define YY_MAX_SHIFTREDUCE 1235
-#define YY_ERROR_ACTION 1236
-#define YY_ACCEPT_ACTION 1237
-#define YY_NO_ACTION 1238
-#define YY_MIN_REDUCE 1239
-#define YY_MAX_REDUCE 1641
+#define YY_MAX_SHIFT 578
+#define YY_MIN_SHIFTREDUCE 838
+#define YY_MAX_SHIFTREDUCE 1242
+#define YY_ERROR_ACTION 1243
+#define YY_ACCEPT_ACTION 1244
+#define YY_NO_ACTION 1245
+#define YY_MIN_REDUCE 1246
+#define YY_MAX_REDUCE 1650
/************* End control #defines *******************************************/
#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))
@@ -169811,218 +171496,218 @@ typedef union {
** yy_default[] Default action for each state.
**
*********** Begin parsing tables **********************************************/
-#define YY_ACTTAB_COUNT (2096)
+#define YY_ACTTAB_COUNT (2100)
static const YYACTIONTYPE yy_action[] = {
- /* 0 */ 568, 208, 568, 118, 115, 229, 568, 118, 115, 229,
- /* 10 */ 568, 1310, 377, 1289, 408, 562, 562, 562, 568, 409,
- /* 20 */ 378, 1310, 1272, 41, 41, 41, 41, 208, 1520, 71,
- /* 30 */ 71, 969, 419, 41, 41, 491, 303, 279, 303, 970,
- /* 40 */ 397, 71, 71, 125, 126, 80, 1210, 1210, 1047, 1050,
- /* 50 */ 1037, 1037, 123, 123, 124, 124, 124, 124, 476, 409,
- /* 60 */ 1237, 1, 1, 574, 2, 1241, 550, 118, 115, 229,
- /* 70 */ 317, 480, 146, 480, 524, 118, 115, 229, 529, 1323,
- /* 80 */ 417, 523, 142, 125, 126, 80, 1210, 1210, 1047, 1050,
- /* 90 */ 1037, 1037, 123, 123, 124, 124, 124, 124, 118, 115,
- /* 100 */ 229, 327, 122, 122, 122, 122, 121, 121, 120, 120,
- /* 110 */ 120, 119, 116, 444, 284, 284, 284, 284, 442, 442,
- /* 120 */ 442, 1559, 376, 1561, 1186, 375, 1157, 565, 1157, 565,
- /* 130 */ 409, 1559, 537, 259, 226, 444, 101, 145, 449, 316,
- /* 140 */ 559, 240, 122, 122, 122, 122, 121, 121, 120, 120,
- /* 150 */ 120, 119, 116, 444, 125, 126, 80, 1210, 1210, 1047,
- /* 160 */ 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, 142,
- /* 170 */ 294, 1186, 339, 448, 120, 120, 120, 119, 116, 444,
- /* 180 */ 127, 1186, 1187, 1186, 148, 441, 440, 568, 119, 116,
- /* 190 */ 444, 124, 124, 124, 124, 117, 122, 122, 122, 122,
- /* 200 */ 121, 121, 120, 120, 120, 119, 116, 444, 454, 113,
- /* 210 */ 13, 13, 546, 122, 122, 122, 122, 121, 121, 120,
- /* 220 */ 120, 120, 119, 116, 444, 422, 316, 559, 1186, 1187,
- /* 230 */ 1186, 149, 1218, 409, 1218, 124, 124, 124, 124, 122,
- /* 240 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116,
- /* 250 */ 444, 465, 342, 1034, 1034, 1048, 1051, 125, 126, 80,
- /* 260 */ 1210, 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124,
- /* 270 */ 124, 124, 1275, 522, 222, 1186, 568, 409, 224, 514,
- /* 280 */ 175, 82, 83, 122, 122, 122, 122, 121, 121, 120,
- /* 290 */ 120, 120, 119, 116, 444, 1005, 16, 16, 1186, 133,
- /* 300 */ 133, 125, 126, 80, 1210, 1210, 1047, 1050, 1037, 1037,
- /* 310 */ 123, 123, 124, 124, 124, 124, 122, 122, 122, 122,
- /* 320 */ 121, 121, 120, 120, 120, 119, 116, 444, 1038, 546,
- /* 330 */ 1186, 373, 1186, 1187, 1186, 252, 1429, 399, 504, 501,
- /* 340 */ 500, 111, 560, 566, 4, 924, 924, 433, 499, 340,
- /* 350 */ 460, 328, 360, 394, 1231, 1186, 1187, 1186, 563, 568,
- /* 360 */ 122, 122, 122, 122, 121, 121, 120, 120, 120, 119,
- /* 370 */ 116, 444, 284, 284, 369, 1572, 1598, 441, 440, 154,
- /* 380 */ 409, 445, 71, 71, 1282, 565, 1215, 1186, 1187, 1186,
- /* 390 */ 85, 1217, 271, 557, 543, 515, 515, 568, 98, 1216,
- /* 400 */ 6, 1274, 472, 142, 125, 126, 80, 1210, 1210, 1047,
- /* 410 */ 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, 550,
- /* 420 */ 13, 13, 1024, 507, 1218, 1186, 1218, 549, 109, 109,
- /* 430 */ 222, 568, 1232, 175, 568, 427, 110, 197, 445, 569,
- /* 440 */ 445, 430, 1546, 1014, 325, 551, 1186, 270, 287, 368,
- /* 450 */ 510, 363, 509, 257, 71, 71, 543, 71, 71, 359,
- /* 460 */ 316, 559, 1604, 122, 122, 122, 122, 121, 121, 120,
- /* 470 */ 120, 120, 119, 116, 444, 1014, 1014, 1016, 1017, 27,
- /* 480 */ 284, 284, 1186, 1187, 1186, 1152, 568, 1603, 409, 899,
- /* 490 */ 190, 550, 356, 565, 550, 935, 533, 517, 1152, 516,
- /* 500 */ 413, 1152, 552, 1186, 1187, 1186, 568, 544, 544, 51,
- /* 510 */ 51, 214, 125, 126, 80, 1210, 1210, 1047, 1050, 1037,
- /* 520 */ 1037, 123, 123, 124, 124, 124, 124, 1186, 474, 135,
- /* 530 */ 135, 409, 284, 284, 1484, 505, 121, 121, 120, 120,
- /* 540 */ 120, 119, 116, 444, 1005, 565, 518, 217, 541, 541,
- /* 550 */ 316, 559, 142, 6, 532, 125, 126, 80, 1210, 1210,
- /* 560 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124,
- /* 570 */ 1548, 122, 122, 122, 122, 121, 121, 120, 120, 120,
- /* 580 */ 119, 116, 444, 485, 1186, 1187, 1186, 482, 281, 1263,
- /* 590 */ 955, 252, 1186, 373, 504, 501, 500, 1186, 340, 570,
- /* 600 */ 1186, 570, 409, 292, 499, 955, 874, 191, 480, 316,
- /* 610 */ 559, 384, 290, 380, 122, 122, 122, 122, 121, 121,
- /* 620 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1210,
- /* 630 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
- /* 640 */ 124, 409, 394, 1132, 1186, 867, 100, 284, 284, 1186,
- /* 650 */ 1187, 1186, 373, 1089, 1186, 1187, 1186, 1186, 1187, 1186,
- /* 660 */ 565, 455, 32, 373, 233, 125, 126, 80, 1210, 1210,
- /* 670 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124,
- /* 680 */ 1428, 957, 568, 228, 956, 122, 122, 122, 122, 121,
- /* 690 */ 121, 120, 120, 120, 119, 116, 444, 1152, 228, 1186,
- /* 700 */ 157, 1186, 1187, 1186, 1547, 13, 13, 301, 955, 1226,
- /* 710 */ 1152, 153, 409, 1152, 373, 1575, 1170, 5, 369, 1572,
- /* 720 */ 429, 1232, 3, 955, 122, 122, 122, 122, 121, 121,
- /* 730 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1210,
- /* 740 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
- /* 750 */ 124, 409, 208, 567, 1186, 1025, 1186, 1187, 1186, 1186,
- /* 760 */ 388, 850, 155, 1546, 286, 402, 1094, 1094, 488, 568,
- /* 770 */ 465, 342, 1315, 1315, 1546, 125, 126, 80, 1210, 1210,
- /* 780 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124,
- /* 790 */ 129, 568, 13, 13, 374, 122, 122, 122, 122, 121,
- /* 800 */ 121, 120, 120, 120, 119, 116, 444, 302, 568, 453,
- /* 810 */ 528, 1186, 1187, 1186, 13, 13, 1186, 1187, 1186, 1293,
- /* 820 */ 463, 1263, 409, 1313, 1313, 1546, 1010, 453, 452, 200,
- /* 830 */ 299, 71, 71, 1261, 122, 122, 122, 122, 121, 121,
- /* 840 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1210,
- /* 850 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
- /* 860 */ 124, 409, 227, 1069, 1152, 284, 284, 419, 312, 278,
- /* 870 */ 278, 285, 285, 1415, 406, 405, 382, 1152, 565, 568,
- /* 880 */ 1152, 1189, 565, 1592, 565, 125, 126, 80, 1210, 1210,
- /* 890 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124,
- /* 900 */ 453, 1476, 13, 13, 1530, 122, 122, 122, 122, 121,
- /* 910 */ 121, 120, 120, 120, 119, 116, 444, 201, 568, 354,
- /* 920 */ 1578, 574, 2, 1241, 838, 839, 840, 1554, 317, 1205,
- /* 930 */ 146, 6, 409, 255, 254, 253, 206, 1323, 9, 1189,
- /* 940 */ 262, 71, 71, 424, 122, 122, 122, 122, 121, 121,
- /* 950 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1210,
- /* 960 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
- /* 970 */ 124, 568, 284, 284, 568, 1206, 409, 573, 313, 1241,
- /* 980 */ 349, 1292, 352, 419, 317, 565, 146, 491, 525, 1635,
- /* 990 */ 395, 371, 491, 1323, 70, 70, 1291, 71, 71, 240,
- /* 1000 */ 1321, 104, 80, 1210, 1210, 1047, 1050, 1037, 1037, 123,
- /* 1010 */ 123, 124, 124, 124, 124, 122, 122, 122, 122, 121,
- /* 1020 */ 121, 120, 120, 120, 119, 116, 444, 1110, 284, 284,
- /* 1030 */ 428, 448, 1519, 1206, 439, 284, 284, 1483, 1348, 311,
- /* 1040 */ 474, 565, 1111, 969, 491, 491, 217, 1259, 565, 1532,
- /* 1050 */ 568, 970, 207, 568, 1024, 240, 383, 1112, 519, 122,
- /* 1060 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116,
- /* 1070 */ 444, 1015, 107, 71, 71, 1014, 13, 13, 910, 568,
- /* 1080 */ 1489, 568, 284, 284, 97, 526, 491, 448, 911, 1322,
- /* 1090 */ 1318, 545, 409, 284, 284, 565, 151, 209, 1489, 1491,
- /* 1100 */ 262, 450, 55, 55, 56, 56, 565, 1014, 1014, 1016,
- /* 1110 */ 443, 332, 409, 527, 12, 295, 125, 126, 80, 1210,
- /* 1120 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
- /* 1130 */ 124, 347, 409, 862, 1528, 1206, 125, 126, 80, 1210,
- /* 1140 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
- /* 1150 */ 124, 1133, 1633, 474, 1633, 371, 125, 114, 80, 1210,
- /* 1160 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
- /* 1170 */ 124, 1489, 329, 474, 331, 122, 122, 122, 122, 121,
- /* 1180 */ 121, 120, 120, 120, 119, 116, 444, 203, 1415, 568,
- /* 1190 */ 1290, 862, 464, 1206, 436, 122, 122, 122, 122, 121,
- /* 1200 */ 121, 120, 120, 120, 119, 116, 444, 553, 1133, 1634,
- /* 1210 */ 539, 1634, 15, 15, 890, 122, 122, 122, 122, 121,
- /* 1220 */ 121, 120, 120, 120, 119, 116, 444, 568, 298, 538,
- /* 1230 */ 1131, 1415, 1552, 1553, 1327, 409, 6, 6, 1163, 1264,
- /* 1240 */ 415, 320, 284, 284, 1415, 508, 565, 525, 300, 457,
- /* 1250 */ 43, 43, 568, 891, 12, 565, 330, 478, 425, 407,
- /* 1260 */ 126, 80, 1210, 1210, 1047, 1050, 1037, 1037, 123, 123,
- /* 1270 */ 124, 124, 124, 124, 568, 57, 57, 288, 1186, 1415,
- /* 1280 */ 496, 458, 392, 392, 391, 273, 389, 1131, 1551, 847,
- /* 1290 */ 1163, 407, 6, 568, 321, 1152, 470, 44, 44, 1550,
- /* 1300 */ 1110, 426, 234, 6, 323, 256, 540, 256, 1152, 431,
- /* 1310 */ 568, 1152, 322, 17, 487, 1111, 58, 58, 122, 122,
- /* 1320 */ 122, 122, 121, 121, 120, 120, 120, 119, 116, 444,
- /* 1330 */ 1112, 216, 481, 59, 59, 1186, 1187, 1186, 111, 560,
- /* 1340 */ 324, 4, 236, 456, 526, 568, 237, 456, 568, 437,
- /* 1350 */ 168, 556, 420, 141, 479, 563, 568, 293, 568, 1091,
- /* 1360 */ 568, 293, 568, 1091, 531, 568, 870, 8, 60, 60,
- /* 1370 */ 235, 61, 61, 568, 414, 568, 414, 568, 445, 62,
- /* 1380 */ 62, 45, 45, 46, 46, 47, 47, 199, 49, 49,
- /* 1390 */ 557, 568, 359, 568, 100, 486, 50, 50, 63, 63,
- /* 1400 */ 64, 64, 561, 415, 535, 410, 568, 1024, 568, 534,
- /* 1410 */ 316, 559, 316, 559, 65, 65, 14, 14, 568, 1024,
- /* 1420 */ 568, 512, 930, 870, 1015, 109, 109, 929, 1014, 66,
- /* 1430 */ 66, 131, 131, 110, 451, 445, 569, 445, 416, 177,
- /* 1440 */ 1014, 132, 132, 67, 67, 568, 467, 568, 930, 471,
- /* 1450 */ 1360, 283, 226, 929, 315, 1359, 407, 568, 459, 407,
- /* 1460 */ 1014, 1014, 1016, 239, 407, 86, 213, 1346, 52, 52,
- /* 1470 */ 68, 68, 1014, 1014, 1016, 1017, 27, 1577, 1174, 447,
- /* 1480 */ 69, 69, 288, 97, 108, 1535, 106, 392, 392, 391,
- /* 1490 */ 273, 389, 568, 877, 847, 881, 568, 111, 560, 466,
- /* 1500 */ 4, 568, 152, 30, 38, 568, 1128, 234, 396, 323,
- /* 1510 */ 111, 560, 527, 4, 563, 53, 53, 322, 568, 163,
- /* 1520 */ 163, 568, 337, 468, 164, 164, 333, 563, 76, 76,
- /* 1530 */ 568, 289, 1508, 568, 31, 1507, 568, 445, 338, 483,
- /* 1540 */ 100, 54, 54, 344, 72, 72, 296, 236, 1076, 557,
- /* 1550 */ 445, 877, 1356, 134, 134, 168, 73, 73, 141, 161,
- /* 1560 */ 161, 1566, 557, 535, 568, 319, 568, 348, 536, 1007,
- /* 1570 */ 473, 261, 261, 889, 888, 235, 535, 568, 1024, 568,
- /* 1580 */ 475, 534, 261, 367, 109, 109, 521, 136, 136, 130,
- /* 1590 */ 130, 1024, 110, 366, 445, 569, 445, 109, 109, 1014,
- /* 1600 */ 162, 162, 156, 156, 568, 110, 1076, 445, 569, 445,
- /* 1610 */ 410, 351, 1014, 568, 353, 316, 559, 568, 343, 568,
- /* 1620 */ 100, 497, 357, 258, 100, 896, 897, 140, 140, 355,
- /* 1630 */ 1306, 1014, 1014, 1016, 1017, 27, 139, 139, 362, 451,
- /* 1640 */ 137, 137, 138, 138, 1014, 1014, 1016, 1017, 27, 1174,
- /* 1650 */ 447, 568, 372, 288, 111, 560, 1018, 4, 392, 392,
- /* 1660 */ 391, 273, 389, 568, 1137, 847, 568, 1072, 568, 258,
- /* 1670 */ 492, 563, 568, 211, 75, 75, 555, 960, 234, 261,
- /* 1680 */ 323, 111, 560, 927, 4, 113, 77, 77, 322, 74,
- /* 1690 */ 74, 42, 42, 1369, 445, 48, 48, 1414, 563, 972,
- /* 1700 */ 973, 1088, 1087, 1088, 1087, 860, 557, 150, 928, 1342,
- /* 1710 */ 113, 1354, 554, 1419, 1018, 1271, 1262, 1250, 236, 1249,
- /* 1720 */ 1251, 445, 1585, 1339, 308, 276, 168, 309, 11, 141,
- /* 1730 */ 393, 310, 232, 557, 1401, 1024, 335, 291, 1396, 219,
- /* 1740 */ 336, 109, 109, 934, 297, 1406, 235, 341, 477, 110,
- /* 1750 */ 502, 445, 569, 445, 1389, 1405, 1014, 400, 1289, 365,
- /* 1760 */ 223, 1480, 1024, 1479, 1351, 1352, 1350, 1349, 109, 109,
- /* 1770 */ 204, 1588, 1226, 558, 265, 218, 110, 205, 445, 569,
- /* 1780 */ 445, 410, 387, 1014, 1527, 179, 316, 559, 1014, 1014,
- /* 1790 */ 1016, 1017, 27, 230, 1525, 1223, 79, 560, 85, 4,
- /* 1800 */ 418, 215, 548, 81, 84, 188, 1402, 173, 181, 461,
- /* 1810 */ 451, 35, 462, 563, 183, 1014, 1014, 1016, 1017, 27,
- /* 1820 */ 184, 1485, 185, 186, 495, 242, 98, 398, 1408, 36,
- /* 1830 */ 1407, 484, 91, 469, 401, 1410, 445, 192, 1474, 246,
- /* 1840 */ 1496, 490, 346, 277, 248, 196, 493, 511, 557, 350,
- /* 1850 */ 1252, 249, 250, 403, 1309, 1308, 111, 560, 432, 4,
- /* 1860 */ 1307, 1300, 93, 1602, 881, 1601, 224, 404, 434, 520,
- /* 1870 */ 263, 435, 1571, 563, 1279, 1278, 364, 1024, 306, 1277,
- /* 1880 */ 264, 1600, 1557, 109, 109, 370, 1299, 307, 1556, 438,
- /* 1890 */ 128, 110, 1374, 445, 569, 445, 445, 546, 1014, 10,
- /* 1900 */ 1461, 105, 381, 1373, 34, 571, 99, 1332, 557, 314,
- /* 1910 */ 1180, 530, 272, 274, 379, 210, 1331, 547, 385, 386,
- /* 1920 */ 275, 572, 1247, 1242, 411, 412, 1512, 165, 178, 1513,
- /* 1930 */ 1014, 1014, 1016, 1017, 27, 1511, 1510, 1024, 78, 147,
- /* 1940 */ 166, 220, 221, 109, 109, 834, 304, 167, 446, 212,
- /* 1950 */ 318, 110, 231, 445, 569, 445, 144, 1086, 1014, 1084,
- /* 1960 */ 326, 180, 169, 1205, 182, 334, 238, 913, 241, 1100,
- /* 1970 */ 187, 170, 171, 421, 87, 88, 423, 189, 89, 90,
- /* 1980 */ 172, 1103, 243, 1099, 244, 158, 18, 245, 345, 247,
- /* 1990 */ 1014, 1014, 1016, 1017, 27, 261, 1092, 193, 1220, 489,
- /* 2000 */ 194, 37, 366, 849, 494, 251, 195, 506, 92, 19,
- /* 2010 */ 498, 358, 20, 503, 879, 361, 94, 892, 305, 159,
- /* 2020 */ 513, 39, 95, 1168, 160, 1053, 964, 1139, 96, 174,
- /* 2030 */ 1138, 225, 280, 282, 198, 958, 113, 1158, 1154, 260,
- /* 2040 */ 21, 22, 23, 1156, 1162, 1161, 1143, 24, 33, 25,
- /* 2050 */ 202, 542, 26, 100, 1067, 102, 1054, 103, 7, 1052,
- /* 2060 */ 1056, 1109, 1057, 1108, 266, 267, 28, 40, 390, 1019,
- /* 2070 */ 861, 112, 29, 564, 1176, 1175, 268, 176, 143, 923,
- /* 2080 */ 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
- /* 2090 */ 1238, 1238, 1238, 1238, 269, 1593,
+ /* 0 */ 572, 210, 572, 119, 116, 231, 572, 119, 116, 231,
+ /* 10 */ 572, 1317, 379, 1296, 410, 566, 566, 566, 572, 411,
+ /* 20 */ 380, 1317, 1279, 42, 42, 42, 42, 210, 1529, 72,
+ /* 30 */ 72, 974, 421, 42, 42, 495, 305, 281, 305, 975,
+ /* 40 */ 399, 72, 72, 126, 127, 81, 1217, 1217, 1054, 1057,
+ /* 50 */ 1044, 1044, 124, 124, 125, 125, 125, 125, 480, 411,
+ /* 60 */ 1244, 1, 1, 578, 2, 1248, 554, 119, 116, 231,
+ /* 70 */ 319, 484, 147, 484, 528, 119, 116, 231, 533, 1330,
+ /* 80 */ 419, 527, 143, 126, 127, 81, 1217, 1217, 1054, 1057,
+ /* 90 */ 1044, 1044, 124, 124, 125, 125, 125, 125, 119, 116,
+ /* 100 */ 231, 329, 123, 123, 123, 123, 122, 122, 121, 121,
+ /* 110 */ 121, 120, 117, 448, 286, 286, 286, 286, 446, 446,
+ /* 120 */ 446, 1568, 378, 1570, 1193, 377, 1164, 569, 1164, 569,
+ /* 130 */ 411, 1568, 541, 261, 228, 448, 102, 146, 453, 318,
+ /* 140 */ 563, 242, 123, 123, 123, 123, 122, 122, 121, 121,
+ /* 150 */ 121, 120, 117, 448, 126, 127, 81, 1217, 1217, 1054,
+ /* 160 */ 1057, 1044, 1044, 124, 124, 125, 125, 125, 125, 143,
+ /* 170 */ 296, 1193, 341, 452, 121, 121, 121, 120, 117, 448,
+ /* 180 */ 128, 1193, 1194, 1193, 149, 445, 444, 572, 120, 117,
+ /* 190 */ 448, 125, 125, 125, 125, 118, 123, 123, 123, 123,
+ /* 200 */ 122, 122, 121, 121, 121, 120, 117, 448, 458, 114,
+ /* 210 */ 13, 13, 550, 123, 123, 123, 123, 122, 122, 121,
+ /* 220 */ 121, 121, 120, 117, 448, 424, 318, 563, 1193, 1194,
+ /* 230 */ 1193, 150, 1225, 411, 1225, 125, 125, 125, 125, 123,
+ /* 240 */ 123, 123, 123, 122, 122, 121, 121, 121, 120, 117,
+ /* 250 */ 448, 469, 344, 1041, 1041, 1055, 1058, 126, 127, 81,
+ /* 260 */ 1217, 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125,
+ /* 270 */ 125, 125, 1282, 526, 224, 1193, 572, 411, 226, 519,
+ /* 280 */ 177, 83, 84, 123, 123, 123, 123, 122, 122, 121,
+ /* 290 */ 121, 121, 120, 117, 448, 1010, 16, 16, 1193, 134,
+ /* 300 */ 134, 126, 127, 81, 1217, 1217, 1054, 1057, 1044, 1044,
+ /* 310 */ 124, 124, 125, 125, 125, 125, 123, 123, 123, 123,
+ /* 320 */ 122, 122, 121, 121, 121, 120, 117, 448, 1045, 550,
+ /* 330 */ 1193, 375, 1193, 1194, 1193, 254, 1438, 401, 508, 505,
+ /* 340 */ 504, 112, 564, 570, 4, 929, 929, 435, 503, 342,
+ /* 350 */ 464, 330, 362, 396, 1238, 1193, 1194, 1193, 567, 572,
+ /* 360 */ 123, 123, 123, 123, 122, 122, 121, 121, 121, 120,
+ /* 370 */ 117, 448, 286, 286, 371, 1581, 1607, 445, 444, 155,
+ /* 380 */ 411, 449, 72, 72, 1289, 569, 1222, 1193, 1194, 1193,
+ /* 390 */ 86, 1224, 273, 561, 547, 520, 520, 572, 99, 1223,
+ /* 400 */ 6, 1281, 476, 143, 126, 127, 81, 1217, 1217, 1054,
+ /* 410 */ 1057, 1044, 1044, 124, 124, 125, 125, 125, 125, 554,
+ /* 420 */ 13, 13, 1031, 511, 1225, 1193, 1225, 553, 110, 110,
+ /* 430 */ 224, 572, 1239, 177, 572, 429, 111, 199, 449, 573,
+ /* 440 */ 449, 432, 1555, 1019, 327, 555, 1193, 272, 289, 370,
+ /* 450 */ 514, 365, 513, 259, 72, 72, 547, 72, 72, 361,
+ /* 460 */ 318, 563, 1613, 123, 123, 123, 123, 122, 122, 121,
+ /* 470 */ 121, 121, 120, 117, 448, 1019, 1019, 1021, 1022, 28,
+ /* 480 */ 286, 286, 1193, 1194, 1193, 1159, 572, 1612, 411, 904,
+ /* 490 */ 192, 554, 358, 569, 554, 940, 537, 521, 1159, 437,
+ /* 500 */ 415, 1159, 556, 1193, 1194, 1193, 572, 548, 548, 52,
+ /* 510 */ 52, 216, 126, 127, 81, 1217, 1217, 1054, 1057, 1044,
+ /* 520 */ 1044, 124, 124, 125, 125, 125, 125, 1193, 478, 136,
+ /* 530 */ 136, 411, 286, 286, 1493, 509, 122, 122, 121, 121,
+ /* 540 */ 121, 120, 117, 448, 1010, 569, 522, 219, 545, 545,
+ /* 550 */ 318, 563, 143, 6, 536, 126, 127, 81, 1217, 1217,
+ /* 560 */ 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, 125,
+ /* 570 */ 1557, 123, 123, 123, 123, 122, 122, 121, 121, 121,
+ /* 580 */ 120, 117, 448, 489, 1193, 1194, 1193, 486, 283, 1270,
+ /* 590 */ 960, 254, 1193, 375, 508, 505, 504, 1193, 342, 574,
+ /* 600 */ 1193, 574, 411, 294, 503, 960, 879, 193, 484, 318,
+ /* 610 */ 563, 386, 292, 382, 123, 123, 123, 123, 122, 122,
+ /* 620 */ 121, 121, 121, 120, 117, 448, 126, 127, 81, 1217,
+ /* 630 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
+ /* 640 */ 125, 411, 396, 1139, 1193, 872, 101, 286, 286, 1193,
+ /* 650 */ 1194, 1193, 375, 1096, 1193, 1194, 1193, 1193, 1194, 1193,
+ /* 660 */ 569, 459, 33, 375, 235, 126, 127, 81, 1217, 1217,
+ /* 670 */ 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, 125,
+ /* 680 */ 1437, 962, 572, 230, 961, 123, 123, 123, 123, 122,
+ /* 690 */ 122, 121, 121, 121, 120, 117, 448, 1159, 230, 1193,
+ /* 700 */ 158, 1193, 1194, 1193, 1556, 13, 13, 303, 960, 1233,
+ /* 710 */ 1159, 154, 411, 1159, 375, 1584, 1177, 5, 371, 1581,
+ /* 720 */ 431, 1239, 3, 960, 123, 123, 123, 123, 122, 122,
+ /* 730 */ 121, 121, 121, 120, 117, 448, 126, 127, 81, 1217,
+ /* 740 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
+ /* 750 */ 125, 411, 210, 571, 1193, 1032, 1193, 1194, 1193, 1193,
+ /* 760 */ 390, 855, 156, 1555, 376, 404, 1101, 1101, 492, 572,
+ /* 770 */ 469, 344, 1322, 1322, 1555, 126, 127, 81, 1217, 1217,
+ /* 780 */ 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, 125,
+ /* 790 */ 130, 572, 13, 13, 532, 123, 123, 123, 123, 122,
+ /* 800 */ 122, 121, 121, 121, 120, 117, 448, 304, 572, 457,
+ /* 810 */ 229, 1193, 1194, 1193, 13, 13, 1193, 1194, 1193, 1300,
+ /* 820 */ 467, 1270, 411, 1320, 1320, 1555, 1015, 457, 456, 436,
+ /* 830 */ 301, 72, 72, 1268, 123, 123, 123, 123, 122, 122,
+ /* 840 */ 121, 121, 121, 120, 117, 448, 126, 127, 81, 1217,
+ /* 850 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
+ /* 860 */ 125, 411, 384, 1076, 1159, 286, 286, 421, 314, 280,
+ /* 870 */ 280, 287, 287, 461, 408, 407, 1539, 1159, 569, 572,
+ /* 880 */ 1159, 1196, 569, 409, 569, 126, 127, 81, 1217, 1217,
+ /* 890 */ 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, 125,
+ /* 900 */ 457, 1485, 13, 13, 1541, 123, 123, 123, 123, 122,
+ /* 910 */ 122, 121, 121, 121, 120, 117, 448, 202, 572, 462,
+ /* 920 */ 1587, 578, 2, 1248, 843, 844, 845, 1563, 319, 409,
+ /* 930 */ 147, 6, 411, 257, 256, 255, 208, 1330, 9, 1196,
+ /* 940 */ 264, 72, 72, 1436, 123, 123, 123, 123, 122, 122,
+ /* 950 */ 121, 121, 121, 120, 117, 448, 126, 127, 81, 1217,
+ /* 960 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
+ /* 970 */ 125, 572, 286, 286, 572, 1213, 411, 577, 315, 1248,
+ /* 980 */ 421, 371, 1581, 356, 319, 569, 147, 495, 529, 1644,
+ /* 990 */ 397, 935, 495, 1330, 71, 71, 934, 72, 72, 242,
+ /* 1000 */ 1328, 105, 81, 1217, 1217, 1054, 1057, 1044, 1044, 124,
+ /* 1010 */ 124, 125, 125, 125, 125, 123, 123, 123, 123, 122,
+ /* 1020 */ 122, 121, 121, 121, 120, 117, 448, 1117, 286, 286,
+ /* 1030 */ 1422, 452, 1528, 1213, 443, 286, 286, 1492, 1355, 313,
+ /* 1040 */ 478, 569, 1118, 454, 351, 495, 354, 1266, 569, 209,
+ /* 1050 */ 572, 418, 179, 572, 1031, 242, 385, 1119, 523, 123,
+ /* 1060 */ 123, 123, 123, 122, 122, 121, 121, 121, 120, 117,
+ /* 1070 */ 448, 1020, 108, 72, 72, 1019, 13, 13, 915, 572,
+ /* 1080 */ 1498, 572, 286, 286, 98, 530, 1537, 452, 916, 1334,
+ /* 1090 */ 1329, 203, 411, 286, 286, 569, 152, 211, 1498, 1500,
+ /* 1100 */ 426, 569, 56, 56, 57, 57, 569, 1019, 1019, 1021,
+ /* 1110 */ 447, 572, 411, 531, 12, 297, 126, 127, 81, 1217,
+ /* 1120 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
+ /* 1130 */ 125, 572, 411, 867, 15, 15, 126, 127, 81, 1217,
+ /* 1140 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
+ /* 1150 */ 125, 373, 529, 264, 44, 44, 126, 115, 81, 1217,
+ /* 1160 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
+ /* 1170 */ 125, 1498, 478, 1271, 417, 123, 123, 123, 123, 122,
+ /* 1180 */ 122, 121, 121, 121, 120, 117, 448, 205, 1213, 495,
+ /* 1190 */ 430, 867, 468, 322, 495, 123, 123, 123, 123, 122,
+ /* 1200 */ 122, 121, 121, 121, 120, 117, 448, 572, 557, 1140,
+ /* 1210 */ 1642, 1422, 1642, 543, 572, 123, 123, 123, 123, 122,
+ /* 1220 */ 122, 121, 121, 121, 120, 117, 448, 572, 1422, 572,
+ /* 1230 */ 13, 13, 542, 323, 1325, 411, 334, 58, 58, 349,
+ /* 1240 */ 1422, 1170, 326, 286, 286, 549, 1213, 300, 895, 530,
+ /* 1250 */ 45, 45, 59, 59, 1140, 1643, 569, 1643, 565, 417,
+ /* 1260 */ 127, 81, 1217, 1217, 1054, 1057, 1044, 1044, 124, 124,
+ /* 1270 */ 125, 125, 125, 125, 1367, 373, 500, 290, 1193, 512,
+ /* 1280 */ 1366, 427, 394, 394, 393, 275, 391, 896, 1138, 852,
+ /* 1290 */ 478, 258, 1422, 1170, 463, 1159, 12, 331, 428, 333,
+ /* 1300 */ 1117, 460, 236, 258, 325, 460, 544, 1544, 1159, 1098,
+ /* 1310 */ 491, 1159, 324, 1098, 440, 1118, 335, 516, 123, 123,
+ /* 1320 */ 123, 123, 122, 122, 121, 121, 121, 120, 117, 448,
+ /* 1330 */ 1119, 318, 563, 1138, 572, 1193, 1194, 1193, 112, 564,
+ /* 1340 */ 201, 4, 238, 433, 935, 490, 285, 228, 1517, 934,
+ /* 1350 */ 170, 560, 572, 142, 1516, 567, 572, 60, 60, 572,
+ /* 1360 */ 416, 572, 441, 572, 535, 302, 875, 8, 487, 572,
+ /* 1370 */ 237, 572, 416, 572, 485, 61, 61, 572, 449, 62,
+ /* 1380 */ 62, 332, 63, 63, 46, 46, 47, 47, 361, 572,
+ /* 1390 */ 561, 572, 48, 48, 50, 50, 51, 51, 572, 295,
+ /* 1400 */ 64, 64, 482, 295, 539, 412, 471, 1031, 572, 538,
+ /* 1410 */ 318, 563, 65, 65, 66, 66, 409, 475, 572, 1031,
+ /* 1420 */ 572, 14, 14, 875, 1020, 110, 110, 409, 1019, 572,
+ /* 1430 */ 474, 67, 67, 111, 455, 449, 573, 449, 98, 317,
+ /* 1440 */ 1019, 132, 132, 133, 133, 572, 1561, 572, 974, 409,
+ /* 1450 */ 6, 1562, 68, 68, 1560, 6, 975, 572, 6, 1559,
+ /* 1460 */ 1019, 1019, 1021, 6, 346, 218, 101, 531, 53, 53,
+ /* 1470 */ 69, 69, 1019, 1019, 1021, 1022, 28, 1586, 1181, 451,
+ /* 1480 */ 70, 70, 290, 87, 215, 31, 1363, 394, 394, 393,
+ /* 1490 */ 275, 391, 350, 109, 852, 107, 572, 112, 564, 483,
+ /* 1500 */ 4, 1212, 572, 239, 153, 572, 39, 236, 1299, 325,
+ /* 1510 */ 112, 564, 1298, 4, 567, 572, 32, 324, 572, 54,
+ /* 1520 */ 54, 572, 1135, 353, 398, 165, 165, 567, 166, 166,
+ /* 1530 */ 572, 291, 355, 572, 17, 357, 572, 449, 77, 77,
+ /* 1540 */ 1313, 55, 55, 1297, 73, 73, 572, 238, 470, 561,
+ /* 1550 */ 449, 472, 364, 135, 135, 170, 74, 74, 142, 163,
+ /* 1560 */ 163, 374, 561, 539, 572, 321, 572, 886, 540, 137,
+ /* 1570 */ 137, 339, 1353, 422, 298, 237, 539, 572, 1031, 572,
+ /* 1580 */ 340, 538, 101, 369, 110, 110, 162, 131, 131, 164,
+ /* 1590 */ 164, 1031, 111, 368, 449, 573, 449, 110, 110, 1019,
+ /* 1600 */ 157, 157, 141, 141, 572, 111, 572, 449, 573, 449,
+ /* 1610 */ 412, 288, 1019, 572, 882, 318, 563, 572, 219, 572,
+ /* 1620 */ 241, 1012, 477, 263, 263, 894, 893, 140, 140, 138,
+ /* 1630 */ 138, 1019, 1019, 1021, 1022, 28, 139, 139, 525, 455,
+ /* 1640 */ 76, 76, 78, 78, 1019, 1019, 1021, 1022, 28, 1181,
+ /* 1650 */ 451, 572, 1083, 290, 112, 564, 1575, 4, 394, 394,
+ /* 1660 */ 393, 275, 391, 572, 1023, 852, 572, 479, 345, 263,
+ /* 1670 */ 101, 567, 882, 1376, 75, 75, 1421, 501, 236, 260,
+ /* 1680 */ 325, 112, 564, 359, 4, 101, 43, 43, 324, 49,
+ /* 1690 */ 49, 901, 902, 161, 449, 101, 977, 978, 567, 1079,
+ /* 1700 */ 1349, 260, 965, 932, 263, 114, 561, 1095, 517, 1095,
+ /* 1710 */ 1083, 1094, 865, 1094, 151, 933, 1144, 114, 238, 1361,
+ /* 1720 */ 558, 449, 1023, 559, 1426, 1278, 170, 1269, 1257, 142,
+ /* 1730 */ 1601, 1256, 1258, 561, 1594, 1031, 496, 278, 213, 1346,
+ /* 1740 */ 310, 110, 110, 939, 311, 312, 237, 11, 234, 111,
+ /* 1750 */ 221, 449, 573, 449, 293, 395, 1019, 1408, 337, 1403,
+ /* 1760 */ 1396, 338, 1031, 299, 343, 1413, 1412, 481, 110, 110,
+ /* 1770 */ 506, 402, 225, 1296, 206, 367, 111, 1358, 449, 573,
+ /* 1780 */ 449, 412, 1359, 1019, 1489, 1488, 318, 563, 1019, 1019,
+ /* 1790 */ 1021, 1022, 28, 562, 207, 220, 80, 564, 389, 4,
+ /* 1800 */ 1597, 1357, 552, 1356, 1233, 181, 267, 232, 1536, 1534,
+ /* 1810 */ 455, 1230, 420, 567, 82, 1019, 1019, 1021, 1022, 28,
+ /* 1820 */ 86, 217, 85, 1494, 190, 175, 183, 465, 185, 466,
+ /* 1830 */ 36, 1409, 186, 187, 188, 499, 449, 244, 37, 99,
+ /* 1840 */ 400, 1415, 1414, 488, 1417, 194, 473, 403, 561, 1483,
+ /* 1850 */ 248, 92, 1505, 494, 198, 279, 112, 564, 250, 4,
+ /* 1860 */ 348, 497, 405, 352, 1259, 251, 252, 515, 1316, 434,
+ /* 1870 */ 1315, 1314, 94, 567, 1307, 886, 1306, 1031, 226, 406,
+ /* 1880 */ 1611, 1610, 438, 110, 110, 1580, 1286, 524, 439, 308,
+ /* 1890 */ 266, 111, 1285, 449, 573, 449, 449, 309, 1019, 366,
+ /* 1900 */ 1284, 1609, 265, 1566, 1565, 442, 372, 1381, 561, 129,
+ /* 1910 */ 550, 1380, 10, 1470, 383, 106, 316, 551, 100, 35,
+ /* 1920 */ 534, 575, 212, 1339, 381, 387, 1187, 1338, 274, 276,
+ /* 1930 */ 1019, 1019, 1021, 1022, 28, 277, 413, 1031, 576, 1254,
+ /* 1940 */ 388, 1521, 1249, 110, 110, 167, 1522, 168, 148, 1520,
+ /* 1950 */ 1519, 111, 306, 449, 573, 449, 222, 223, 1019, 839,
+ /* 1960 */ 169, 79, 450, 214, 414, 233, 320, 145, 1093, 1091,
+ /* 1970 */ 328, 182, 171, 1212, 918, 184, 240, 336, 243, 1107,
+ /* 1980 */ 189, 172, 173, 423, 425, 88, 180, 191, 89, 90,
+ /* 1990 */ 1019, 1019, 1021, 1022, 28, 91, 174, 1110, 245, 1106,
+ /* 2000 */ 246, 159, 18, 247, 347, 1099, 263, 195, 1227, 493,
+ /* 2010 */ 249, 196, 38, 854, 498, 368, 253, 360, 897, 197,
+ /* 2020 */ 502, 93, 19, 20, 507, 884, 363, 510, 95, 307,
+ /* 2030 */ 160, 96, 518, 97, 1175, 1060, 1146, 40, 21, 227,
+ /* 2040 */ 176, 1145, 282, 284, 969, 200, 963, 114, 262, 1165,
+ /* 2050 */ 22, 23, 24, 1161, 1169, 25, 1163, 1150, 34, 26,
+ /* 2060 */ 1168, 546, 27, 204, 101, 103, 104, 1074, 7, 1061,
+ /* 2070 */ 1059, 1063, 1116, 1064, 1115, 268, 269, 29, 41, 270,
+ /* 2080 */ 1024, 866, 113, 30, 568, 392, 1183, 144, 178, 1182,
+ /* 2090 */ 271, 928, 1245, 1245, 1245, 1245, 1245, 1245, 1245, 1602,
};
static const YYCODETYPE yy_lookahead[] = {
/* 0 */ 193, 193, 193, 274, 275, 276, 193, 274, 275, 276,
@@ -170101,7 +171786,7 @@ static const YYCODETYPE yy_lookahead[] = {
/* 730 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46,
/* 740 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
/* 750 */ 57, 19, 193, 193, 59, 23, 116, 117, 118, 59,
- /* 760 */ 201, 21, 241, 304, 22, 206, 127, 128, 129, 193,
+ /* 760 */ 201, 21, 241, 304, 193, 206, 127, 128, 129, 193,
/* 770 */ 128, 129, 235, 236, 304, 43, 44, 45, 46, 47,
/* 780 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
/* 790 */ 22, 193, 216, 217, 193, 102, 103, 104, 105, 106,
@@ -170112,129 +171797,129 @@ static const YYCODETYPE yy_lookahead[] = {
/* 840 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46,
/* 850 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
/* 860 */ 57, 19, 193, 123, 76, 239, 240, 193, 253, 239,
- /* 870 */ 240, 239, 240, 193, 106, 107, 193, 89, 252, 193,
- /* 880 */ 92, 59, 252, 141, 252, 43, 44, 45, 46, 47,
+ /* 870 */ 240, 239, 240, 244, 106, 107, 193, 89, 252, 193,
+ /* 880 */ 92, 59, 252, 254, 252, 43, 44, 45, 46, 47,
/* 890 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
/* 900 */ 284, 161, 216, 217, 193, 102, 103, 104, 105, 106,
- /* 910 */ 107, 108, 109, 110, 111, 112, 113, 231, 193, 16,
- /* 920 */ 187, 188, 189, 190, 7, 8, 9, 309, 195, 25,
+ /* 910 */ 107, 108, 109, 110, 111, 112, 113, 231, 193, 244,
+ /* 920 */ 187, 188, 189, 190, 7, 8, 9, 309, 195, 254,
/* 930 */ 197, 313, 19, 127, 128, 129, 262, 204, 22, 117,
- /* 940 */ 24, 216, 217, 263, 102, 103, 104, 105, 106, 107,
+ /* 940 */ 24, 216, 217, 273, 102, 103, 104, 105, 106, 107,
/* 950 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46,
/* 960 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
/* 970 */ 57, 193, 239, 240, 193, 59, 19, 188, 253, 190,
- /* 980 */ 77, 226, 79, 193, 195, 252, 197, 193, 19, 301,
- /* 990 */ 302, 193, 193, 204, 216, 217, 226, 216, 217, 266,
+ /* 980 */ 193, 311, 312, 16, 195, 252, 197, 193, 19, 301,
+ /* 990 */ 302, 135, 193, 204, 216, 217, 140, 216, 217, 266,
/* 1000 */ 204, 159, 45, 46, 47, 48, 49, 50, 51, 52,
/* 1010 */ 53, 54, 55, 56, 57, 102, 103, 104, 105, 106,
/* 1020 */ 107, 108, 109, 110, 111, 112, 113, 12, 239, 240,
- /* 1030 */ 232, 298, 238, 117, 253, 239, 240, 238, 259, 260,
- /* 1040 */ 193, 252, 27, 31, 193, 193, 142, 204, 252, 193,
- /* 1050 */ 193, 39, 262, 193, 100, 266, 278, 42, 204, 102,
+ /* 1030 */ 193, 298, 238, 117, 253, 239, 240, 238, 259, 260,
+ /* 1040 */ 193, 252, 27, 193, 77, 193, 79, 204, 252, 262,
+ /* 1050 */ 193, 299, 300, 193, 100, 266, 278, 42, 204, 102,
/* 1060 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
/* 1070 */ 113, 117, 159, 216, 217, 121, 216, 217, 63, 193,
- /* 1080 */ 193, 193, 239, 240, 115, 116, 193, 298, 73, 238,
+ /* 1080 */ 193, 193, 239, 240, 115, 116, 193, 298, 73, 240,
/* 1090 */ 238, 231, 19, 239, 240, 252, 22, 24, 211, 212,
- /* 1100 */ 24, 193, 216, 217, 216, 217, 252, 153, 154, 155,
- /* 1110 */ 253, 16, 19, 144, 213, 268, 43, 44, 45, 46,
+ /* 1100 */ 263, 252, 216, 217, 216, 217, 252, 153, 154, 155,
+ /* 1110 */ 253, 193, 19, 144, 213, 268, 43, 44, 45, 46,
/* 1120 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 1130 */ 57, 238, 19, 59, 193, 59, 43, 44, 45, 46,
+ /* 1130 */ 57, 193, 19, 59, 216, 217, 43, 44, 45, 46,
/* 1140 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 1150 */ 57, 22, 23, 193, 25, 193, 43, 44, 45, 46,
+ /* 1150 */ 57, 193, 19, 24, 216, 217, 43, 44, 45, 46,
/* 1160 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 1170 */ 57, 284, 77, 193, 79, 102, 103, 104, 105, 106,
- /* 1180 */ 107, 108, 109, 110, 111, 112, 113, 286, 193, 193,
- /* 1190 */ 193, 117, 291, 117, 232, 102, 103, 104, 105, 106,
- /* 1200 */ 107, 108, 109, 110, 111, 112, 113, 204, 22, 23,
- /* 1210 */ 66, 25, 216, 217, 35, 102, 103, 104, 105, 106,
- /* 1220 */ 107, 108, 109, 110, 111, 112, 113, 193, 268, 85,
- /* 1230 */ 101, 193, 309, 309, 240, 19, 313, 313, 94, 208,
- /* 1240 */ 209, 193, 239, 240, 193, 66, 252, 19, 268, 244,
- /* 1250 */ 216, 217, 193, 74, 213, 252, 161, 19, 263, 254,
+ /* 1170 */ 57, 284, 193, 208, 209, 102, 103, 104, 105, 106,
+ /* 1180 */ 107, 108, 109, 110, 111, 112, 113, 286, 59, 193,
+ /* 1190 */ 232, 117, 291, 193, 193, 102, 103, 104, 105, 106,
+ /* 1200 */ 107, 108, 109, 110, 111, 112, 113, 193, 204, 22,
+ /* 1210 */ 23, 193, 25, 66, 193, 102, 103, 104, 105, 106,
+ /* 1220 */ 107, 108, 109, 110, 111, 112, 113, 193, 193, 193,
+ /* 1230 */ 216, 217, 85, 193, 238, 19, 16, 216, 217, 238,
+ /* 1240 */ 193, 94, 193, 239, 240, 231, 117, 268, 35, 116,
+ /* 1250 */ 216, 217, 216, 217, 22, 23, 252, 25, 208, 209,
/* 1260 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- /* 1270 */ 54, 55, 56, 57, 193, 216, 217, 5, 59, 193,
- /* 1280 */ 19, 244, 10, 11, 12, 13, 14, 101, 309, 17,
- /* 1290 */ 146, 254, 313, 193, 193, 76, 115, 216, 217, 309,
- /* 1300 */ 12, 263, 30, 313, 32, 46, 87, 46, 89, 130,
- /* 1310 */ 193, 92, 40, 22, 263, 27, 216, 217, 102, 103,
+ /* 1270 */ 54, 55, 56, 57, 193, 193, 19, 5, 59, 66,
+ /* 1280 */ 193, 263, 10, 11, 12, 13, 14, 74, 101, 17,
+ /* 1290 */ 193, 46, 193, 146, 193, 76, 213, 77, 263, 79,
+ /* 1300 */ 12, 260, 30, 46, 32, 264, 87, 193, 89, 29,
+ /* 1310 */ 263, 92, 40, 33, 232, 27, 193, 108, 102, 103,
/* 1320 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
- /* 1330 */ 42, 150, 291, 216, 217, 116, 117, 118, 19, 20,
- /* 1340 */ 193, 22, 70, 260, 116, 193, 24, 264, 193, 263,
- /* 1350 */ 78, 63, 61, 81, 116, 36, 193, 260, 193, 29,
- /* 1360 */ 193, 264, 193, 33, 145, 193, 59, 48, 216, 217,
- /* 1370 */ 98, 216, 217, 193, 115, 193, 115, 193, 59, 216,
- /* 1380 */ 217, 216, 217, 216, 217, 216, 217, 255, 216, 217,
- /* 1390 */ 71, 193, 131, 193, 25, 65, 216, 217, 216, 217,
- /* 1400 */ 216, 217, 208, 209, 85, 133, 193, 100, 193, 90,
- /* 1410 */ 138, 139, 138, 139, 216, 217, 216, 217, 193, 100,
- /* 1420 */ 193, 108, 135, 116, 117, 106, 107, 140, 121, 216,
- /* 1430 */ 217, 216, 217, 114, 162, 116, 117, 118, 299, 300,
- /* 1440 */ 121, 216, 217, 216, 217, 193, 244, 193, 135, 244,
- /* 1450 */ 193, 256, 257, 140, 244, 193, 254, 193, 193, 254,
- /* 1460 */ 153, 154, 155, 141, 254, 149, 150, 258, 216, 217,
+ /* 1330 */ 42, 138, 139, 101, 193, 116, 117, 118, 19, 20,
+ /* 1340 */ 255, 22, 70, 130, 135, 65, 256, 257, 193, 140,
+ /* 1350 */ 78, 63, 193, 81, 193, 36, 193, 216, 217, 193,
+ /* 1360 */ 115, 193, 263, 193, 145, 268, 59, 48, 193, 193,
+ /* 1370 */ 98, 193, 115, 193, 291, 216, 217, 193, 59, 216,
+ /* 1380 */ 217, 161, 216, 217, 216, 217, 216, 217, 131, 193,
+ /* 1390 */ 71, 193, 216, 217, 216, 217, 216, 217, 193, 260,
+ /* 1400 */ 216, 217, 19, 264, 85, 133, 244, 100, 193, 90,
+ /* 1410 */ 138, 139, 216, 217, 216, 217, 254, 244, 193, 100,
+ /* 1420 */ 193, 216, 217, 116, 117, 106, 107, 254, 121, 193,
+ /* 1430 */ 115, 216, 217, 114, 162, 116, 117, 118, 115, 244,
+ /* 1440 */ 121, 216, 217, 216, 217, 193, 309, 193, 31, 254,
+ /* 1450 */ 313, 309, 216, 217, 309, 313, 39, 193, 313, 309,
+ /* 1460 */ 153, 154, 155, 313, 193, 150, 25, 144, 216, 217,
/* 1470 */ 216, 217, 153, 154, 155, 156, 157, 0, 1, 2,
- /* 1480 */ 216, 217, 5, 115, 158, 193, 160, 10, 11, 12,
- /* 1490 */ 13, 14, 193, 59, 17, 126, 193, 19, 20, 129,
- /* 1500 */ 22, 193, 22, 22, 24, 193, 23, 30, 25, 32,
- /* 1510 */ 19, 20, 144, 22, 36, 216, 217, 40, 193, 216,
- /* 1520 */ 217, 193, 152, 129, 216, 217, 193, 36, 216, 217,
- /* 1530 */ 193, 99, 193, 193, 53, 193, 193, 59, 23, 193,
- /* 1540 */ 25, 216, 217, 193, 216, 217, 152, 70, 59, 71,
- /* 1550 */ 59, 117, 193, 216, 217, 78, 216, 217, 81, 216,
- /* 1560 */ 217, 318, 71, 85, 193, 133, 193, 193, 90, 23,
- /* 1570 */ 23, 25, 25, 120, 121, 98, 85, 193, 100, 193,
- /* 1580 */ 23, 90, 25, 121, 106, 107, 19, 216, 217, 216,
+ /* 1480 */ 216, 217, 5, 149, 150, 22, 193, 10, 11, 12,
+ /* 1490 */ 13, 14, 193, 158, 17, 160, 193, 19, 20, 116,
+ /* 1500 */ 22, 25, 193, 24, 22, 193, 24, 30, 226, 32,
+ /* 1510 */ 19, 20, 226, 22, 36, 193, 53, 40, 193, 216,
+ /* 1520 */ 217, 193, 23, 193, 25, 216, 217, 36, 216, 217,
+ /* 1530 */ 193, 99, 193, 193, 22, 193, 193, 59, 216, 217,
+ /* 1540 */ 193, 216, 217, 193, 216, 217, 193, 70, 129, 71,
+ /* 1550 */ 59, 129, 193, 216, 217, 78, 216, 217, 81, 216,
+ /* 1560 */ 217, 193, 71, 85, 193, 133, 193, 126, 90, 216,
+ /* 1570 */ 217, 152, 258, 61, 152, 98, 85, 193, 100, 193,
+ /* 1580 */ 23, 90, 25, 121, 106, 107, 23, 216, 217, 216,
/* 1590 */ 217, 100, 114, 131, 116, 117, 118, 106, 107, 121,
- /* 1600 */ 216, 217, 216, 217, 193, 114, 117, 116, 117, 118,
- /* 1610 */ 133, 193, 121, 193, 193, 138, 139, 193, 23, 193,
- /* 1620 */ 25, 23, 23, 25, 25, 7, 8, 216, 217, 193,
- /* 1630 */ 193, 153, 154, 155, 156, 157, 216, 217, 193, 162,
+ /* 1600 */ 216, 217, 216, 217, 193, 114, 193, 116, 117, 118,
+ /* 1610 */ 133, 22, 121, 193, 59, 138, 139, 193, 142, 193,
+ /* 1620 */ 141, 23, 23, 25, 25, 120, 121, 216, 217, 216,
+ /* 1630 */ 217, 153, 154, 155, 156, 157, 216, 217, 19, 162,
/* 1640 */ 216, 217, 216, 217, 153, 154, 155, 156, 157, 1,
- /* 1650 */ 2, 193, 193, 5, 19, 20, 59, 22, 10, 11,
- /* 1660 */ 12, 13, 14, 193, 97, 17, 193, 23, 193, 25,
- /* 1670 */ 288, 36, 193, 242, 216, 217, 236, 23, 30, 25,
+ /* 1650 */ 2, 193, 59, 5, 19, 20, 318, 22, 10, 11,
+ /* 1660 */ 12, 13, 14, 193, 59, 17, 193, 23, 23, 25,
+ /* 1670 */ 25, 36, 117, 193, 216, 217, 193, 23, 30, 25,
/* 1680 */ 32, 19, 20, 23, 22, 25, 216, 217, 40, 216,
- /* 1690 */ 217, 216, 217, 193, 59, 216, 217, 193, 36, 83,
- /* 1700 */ 84, 153, 153, 155, 155, 23, 71, 25, 23, 193,
- /* 1710 */ 25, 193, 193, 193, 117, 193, 193, 193, 70, 193,
- /* 1720 */ 193, 59, 193, 255, 255, 287, 78, 255, 243, 81,
- /* 1730 */ 191, 255, 297, 71, 271, 100, 293, 245, 267, 214,
- /* 1740 */ 246, 106, 107, 108, 246, 271, 98, 245, 293, 114,
- /* 1750 */ 220, 116, 117, 118, 267, 271, 121, 271, 225, 219,
- /* 1760 */ 229, 219, 100, 219, 259, 259, 259, 259, 106, 107,
- /* 1770 */ 249, 196, 60, 280, 141, 243, 114, 249, 116, 117,
- /* 1780 */ 118, 133, 245, 121, 200, 297, 138, 139, 153, 154,
- /* 1790 */ 155, 156, 157, 297, 200, 38, 19, 20, 151, 22,
- /* 1800 */ 200, 150, 140, 294, 294, 22, 272, 43, 234, 18,
- /* 1810 */ 162, 270, 200, 36, 237, 153, 154, 155, 156, 157,
- /* 1820 */ 237, 283, 237, 237, 18, 199, 149, 246, 272, 270,
- /* 1830 */ 272, 200, 158, 246, 246, 234, 59, 234, 246, 199,
- /* 1840 */ 290, 62, 289, 200, 199, 22, 221, 115, 71, 200,
- /* 1850 */ 200, 199, 199, 221, 218, 218, 19, 20, 64, 22,
- /* 1860 */ 218, 227, 22, 224, 126, 224, 165, 221, 24, 305,
- /* 1870 */ 200, 113, 312, 36, 218, 220, 218, 100, 282, 218,
- /* 1880 */ 91, 218, 317, 106, 107, 221, 227, 282, 317, 82,
- /* 1890 */ 148, 114, 265, 116, 117, 118, 59, 145, 121, 22,
- /* 1900 */ 277, 158, 200, 265, 25, 202, 147, 250, 71, 279,
- /* 1910 */ 13, 146, 194, 194, 249, 248, 250, 140, 247, 246,
- /* 1920 */ 6, 192, 192, 192, 303, 303, 213, 207, 300, 213,
- /* 1930 */ 153, 154, 155, 156, 157, 213, 213, 100, 213, 222,
- /* 1940 */ 207, 214, 214, 106, 107, 4, 222, 207, 3, 22,
- /* 1950 */ 163, 114, 15, 116, 117, 118, 16, 23, 121, 23,
- /* 1960 */ 139, 151, 130, 25, 142, 16, 24, 20, 144, 1,
- /* 1970 */ 142, 130, 130, 61, 53, 53, 37, 151, 53, 53,
- /* 1980 */ 130, 116, 34, 1, 141, 5, 22, 115, 161, 141,
- /* 1990 */ 153, 154, 155, 156, 157, 25, 68, 68, 75, 41,
- /* 2000 */ 115, 24, 131, 20, 19, 125, 22, 96, 22, 22,
- /* 2010 */ 67, 23, 22, 67, 59, 24, 22, 28, 67, 23,
- /* 2020 */ 22, 22, 149, 23, 23, 23, 116, 23, 25, 37,
- /* 2030 */ 97, 141, 23, 23, 22, 143, 25, 75, 88, 34,
- /* 2040 */ 34, 34, 34, 86, 75, 93, 23, 34, 22, 34,
- /* 2050 */ 25, 24, 34, 25, 23, 142, 23, 142, 44, 23,
- /* 2060 */ 23, 23, 11, 23, 25, 22, 22, 22, 15, 23,
- /* 2070 */ 23, 22, 22, 25, 1, 1, 141, 25, 23, 135,
- /* 2080 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2090 */ 319, 319, 319, 319, 141, 141, 319, 319, 319, 319,
+ /* 1690 */ 217, 7, 8, 23, 59, 25, 83, 84, 36, 23,
+ /* 1700 */ 193, 25, 23, 23, 25, 25, 71, 153, 145, 155,
+ /* 1710 */ 117, 153, 23, 155, 25, 23, 97, 25, 70, 193,
+ /* 1720 */ 193, 59, 117, 236, 193, 193, 78, 193, 193, 81,
+ /* 1730 */ 141, 193, 193, 71, 193, 100, 288, 287, 242, 255,
+ /* 1740 */ 255, 106, 107, 108, 255, 255, 98, 243, 297, 114,
+ /* 1750 */ 214, 116, 117, 118, 245, 191, 121, 271, 293, 267,
+ /* 1760 */ 267, 246, 100, 246, 245, 271, 271, 293, 106, 107,
+ /* 1770 */ 220, 271, 229, 225, 249, 219, 114, 259, 116, 117,
+ /* 1780 */ 118, 133, 259, 121, 219, 219, 138, 139, 153, 154,
+ /* 1790 */ 155, 156, 157, 280, 249, 243, 19, 20, 245, 22,
+ /* 1800 */ 196, 259, 140, 259, 60, 297, 141, 297, 200, 200,
+ /* 1810 */ 162, 38, 200, 36, 294, 153, 154, 155, 156, 157,
+ /* 1820 */ 151, 150, 294, 283, 22, 43, 234, 18, 237, 200,
+ /* 1830 */ 270, 272, 237, 237, 237, 18, 59, 199, 270, 149,
+ /* 1840 */ 246, 272, 272, 200, 234, 234, 246, 246, 71, 246,
+ /* 1850 */ 199, 158, 290, 62, 22, 200, 19, 20, 199, 22,
+ /* 1860 */ 289, 221, 221, 200, 200, 199, 199, 115, 218, 64,
+ /* 1870 */ 218, 218, 22, 36, 227, 126, 227, 100, 165, 221,
+ /* 1880 */ 224, 224, 24, 106, 107, 312, 218, 305, 113, 282,
+ /* 1890 */ 91, 114, 220, 116, 117, 118, 59, 282, 121, 218,
+ /* 1900 */ 218, 218, 200, 317, 317, 82, 221, 265, 71, 148,
+ /* 1910 */ 145, 265, 22, 277, 200, 158, 279, 140, 147, 25,
+ /* 1920 */ 146, 202, 248, 250, 249, 247, 13, 250, 194, 194,
+ /* 1930 */ 153, 154, 155, 156, 157, 6, 303, 100, 192, 192,
+ /* 1940 */ 246, 213, 192, 106, 107, 207, 213, 207, 222, 213,
+ /* 1950 */ 213, 114, 222, 116, 117, 118, 214, 214, 121, 4,
+ /* 1960 */ 207, 213, 3, 22, 303, 15, 163, 16, 23, 23,
+ /* 1970 */ 139, 151, 130, 25, 20, 142, 24, 16, 144, 1,
+ /* 1980 */ 142, 130, 130, 61, 37, 53, 300, 151, 53, 53,
+ /* 1990 */ 153, 154, 155, 156, 157, 53, 130, 116, 34, 1,
+ /* 2000 */ 141, 5, 22, 115, 161, 68, 25, 68, 75, 41,
+ /* 2010 */ 141, 115, 24, 20, 19, 131, 125, 23, 28, 22,
+ /* 2020 */ 67, 22, 22, 22, 67, 59, 24, 96, 22, 67,
+ /* 2030 */ 23, 149, 22, 25, 23, 23, 23, 22, 34, 141,
+ /* 2040 */ 37, 97, 23, 23, 116, 22, 143, 25, 34, 75,
+ /* 2050 */ 34, 34, 34, 88, 75, 34, 86, 23, 22, 34,
+ /* 2060 */ 93, 24, 34, 25, 25, 142, 142, 23, 44, 23,
+ /* 2070 */ 23, 23, 23, 11, 23, 25, 22, 22, 22, 141,
+ /* 2080 */ 23, 23, 22, 22, 25, 15, 1, 23, 25, 1,
+ /* 2090 */ 141, 135, 319, 319, 319, 319, 319, 319, 319, 141,
/* 2100 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2110 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2120 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
@@ -170253,176 +171938,177 @@ static const YYCODETYPE yy_lookahead[] = {
/* 2250 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2260 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2270 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2280 */ 319,
+ /* 2280 */ 319, 319, 319, 319, 319,
};
-#define YY_SHIFT_COUNT (574)
+#define YY_SHIFT_COUNT (578)
#define YY_SHIFT_MIN (0)
-#define YY_SHIFT_MAX (2074)
+#define YY_SHIFT_MAX (2088)
static const unsigned short int yy_shift_ofst[] = {
/* 0 */ 1648, 1477, 1272, 322, 322, 1, 1319, 1478, 1491, 1837,
/* 10 */ 1837, 1837, 471, 0, 0, 214, 1093, 1837, 1837, 1837,
/* 20 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
- /* 30 */ 271, 271, 1219, 1219, 216, 88, 1, 1, 1, 1,
- /* 40 */ 1, 40, 111, 258, 361, 469, 512, 583, 622, 693,
- /* 50 */ 732, 803, 842, 913, 1073, 1093, 1093, 1093, 1093, 1093,
+ /* 30 */ 1837, 271, 271, 1219, 1219, 216, 88, 1, 1, 1,
+ /* 40 */ 1, 1, 40, 111, 258, 361, 469, 512, 583, 622,
+ /* 50 */ 693, 732, 803, 842, 913, 1073, 1093, 1093, 1093, 1093,
/* 60 */ 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093,
- /* 70 */ 1093, 1093, 1093, 1113, 1093, 1216, 957, 957, 1635, 1662,
- /* 80 */ 1777, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
+ /* 70 */ 1093, 1093, 1093, 1093, 1113, 1093, 1216, 957, 957, 1635,
+ /* 80 */ 1662, 1777, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
/* 90 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
/* 100 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
/* 110 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
/* 120 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
- /* 130 */ 137, 181, 181, 181, 181, 181, 181, 181, 94, 430,
- /* 140 */ 66, 65, 112, 366, 533, 533, 740, 1261, 533, 533,
- /* 150 */ 79, 79, 533, 412, 412, 412, 77, 412, 123, 113,
- /* 160 */ 113, 22, 22, 2096, 2096, 328, 328, 328, 239, 468,
- /* 170 */ 468, 468, 468, 1015, 1015, 409, 366, 1129, 1186, 533,
- /* 180 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 533,
- /* 190 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 969,
- /* 200 */ 621, 621, 533, 642, 788, 788, 1228, 1228, 822, 822,
- /* 210 */ 67, 1274, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 1307,
- /* 220 */ 954, 954, 585, 472, 640, 387, 695, 538, 541, 700,
- /* 230 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 533,
- /* 240 */ 222, 533, 533, 533, 533, 533, 533, 533, 533, 533,
- /* 250 */ 533, 533, 533, 1179, 1179, 1179, 533, 533, 533, 565,
- /* 260 */ 533, 533, 533, 916, 1144, 533, 533, 1288, 533, 533,
- /* 270 */ 533, 533, 533, 533, 533, 533, 639, 1330, 209, 1076,
- /* 280 */ 1076, 1076, 1076, 580, 209, 209, 1313, 768, 917, 649,
- /* 290 */ 1181, 1316, 405, 1316, 1238, 249, 1181, 1181, 249, 1181,
- /* 300 */ 405, 1238, 1369, 464, 1259, 1012, 1012, 1012, 1368, 1368,
- /* 310 */ 1368, 1368, 184, 184, 1326, 904, 1287, 1480, 1712, 1712,
- /* 320 */ 1633, 1633, 1757, 1757, 1633, 1647, 1651, 1783, 1764, 1791,
- /* 330 */ 1791, 1791, 1791, 1633, 1806, 1677, 1651, 1651, 1677, 1783,
- /* 340 */ 1764, 1677, 1764, 1677, 1633, 1806, 1674, 1779, 1633, 1806,
- /* 350 */ 1823, 1633, 1806, 1633, 1806, 1823, 1732, 1732, 1732, 1794,
- /* 360 */ 1840, 1840, 1823, 1732, 1738, 1732, 1794, 1732, 1732, 1701,
- /* 370 */ 1844, 1758, 1758, 1823, 1633, 1789, 1789, 1807, 1807, 1742,
- /* 380 */ 1752, 1877, 1633, 1743, 1742, 1759, 1765, 1677, 1879, 1897,
- /* 390 */ 1897, 1914, 1914, 1914, 2096, 2096, 2096, 2096, 2096, 2096,
- /* 400 */ 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 207,
- /* 410 */ 1095, 331, 620, 903, 806, 1074, 1483, 1432, 1481, 1322,
- /* 420 */ 1370, 1394, 1515, 1291, 1546, 1547, 1557, 1595, 1598, 1599,
- /* 430 */ 1434, 1453, 1618, 1462, 1567, 1489, 1644, 1654, 1616, 1660,
- /* 440 */ 1548, 1549, 1682, 1685, 1597, 742, 1941, 1945, 1927, 1787,
- /* 450 */ 1937, 1940, 1934, 1936, 1821, 1810, 1832, 1938, 1938, 1942,
- /* 460 */ 1822, 1947, 1824, 1949, 1968, 1828, 1841, 1938, 1842, 1912,
- /* 470 */ 1939, 1938, 1826, 1921, 1922, 1925, 1926, 1850, 1865, 1948,
- /* 480 */ 1843, 1982, 1980, 1964, 1872, 1827, 1928, 1970, 1929, 1923,
- /* 490 */ 1958, 1848, 1885, 1977, 1983, 1985, 1871, 1880, 1984, 1943,
- /* 500 */ 1986, 1987, 1988, 1990, 1946, 1955, 1991, 1911, 1989, 1994,
- /* 510 */ 1951, 1992, 1996, 1873, 1998, 2000, 2001, 2002, 2003, 2004,
- /* 520 */ 1999, 1933, 1890, 2009, 2010, 1910, 2005, 2012, 1892, 2011,
- /* 530 */ 2006, 2007, 2008, 2013, 1950, 1962, 1957, 2014, 1969, 1952,
- /* 540 */ 2015, 2023, 2026, 2027, 2025, 2028, 2018, 1913, 1915, 2031,
- /* 550 */ 2011, 2033, 2036, 2037, 2038, 2039, 2040, 2043, 2051, 2044,
- /* 560 */ 2045, 2046, 2047, 2049, 2050, 2048, 1944, 1935, 1953, 1954,
- /* 570 */ 2052, 2055, 2053, 2073, 2074,
+ /* 130 */ 1837, 137, 181, 181, 181, 181, 181, 181, 181, 94,
+ /* 140 */ 430, 66, 65, 112, 366, 533, 533, 740, 1257, 533,
+ /* 150 */ 533, 79, 79, 533, 412, 412, 412, 77, 412, 123,
+ /* 160 */ 113, 113, 113, 22, 22, 2100, 2100, 328, 328, 328,
+ /* 170 */ 239, 468, 468, 468, 468, 1015, 1015, 409, 366, 1187,
+ /* 180 */ 1232, 533, 533, 533, 533, 533, 533, 533, 533, 533,
+ /* 190 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 533,
+ /* 200 */ 533, 969, 621, 621, 533, 642, 788, 788, 1133, 1133,
+ /* 210 */ 822, 822, 67, 1193, 2100, 2100, 2100, 2100, 2100, 2100,
+ /* 220 */ 2100, 1307, 954, 954, 585, 472, 640, 387, 695, 538,
+ /* 230 */ 541, 700, 533, 533, 533, 533, 533, 533, 533, 533,
+ /* 240 */ 533, 533, 222, 533, 533, 533, 533, 533, 533, 533,
+ /* 250 */ 533, 533, 533, 533, 533, 1213, 1213, 1213, 533, 533,
+ /* 260 */ 533, 565, 533, 533, 533, 916, 1147, 533, 533, 1288,
+ /* 270 */ 533, 533, 533, 533, 533, 533, 533, 533, 639, 1280,
+ /* 280 */ 209, 1129, 1129, 1129, 1129, 580, 209, 209, 1209, 768,
+ /* 290 */ 917, 649, 1315, 1334, 405, 1334, 1383, 249, 1315, 1315,
+ /* 300 */ 249, 1315, 405, 1383, 1441, 464, 1245, 1417, 1417, 1417,
+ /* 310 */ 1323, 1323, 1323, 1323, 184, 184, 1335, 1476, 856, 1482,
+ /* 320 */ 1744, 1744, 1665, 1665, 1773, 1773, 1665, 1669, 1671, 1802,
+ /* 330 */ 1782, 1809, 1809, 1809, 1809, 1665, 1817, 1690, 1671, 1671,
+ /* 340 */ 1690, 1802, 1782, 1690, 1782, 1690, 1665, 1817, 1693, 1791,
+ /* 350 */ 1665, 1817, 1832, 1665, 1817, 1665, 1817, 1832, 1752, 1752,
+ /* 360 */ 1752, 1805, 1850, 1850, 1832, 1752, 1749, 1752, 1805, 1752,
+ /* 370 */ 1752, 1713, 1858, 1775, 1775, 1832, 1665, 1799, 1799, 1823,
+ /* 380 */ 1823, 1761, 1765, 1890, 1665, 1757, 1761, 1771, 1774, 1690,
+ /* 390 */ 1894, 1913, 1913, 1929, 1929, 1929, 2100, 2100, 2100, 2100,
+ /* 400 */ 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100,
+ /* 410 */ 2100, 207, 1220, 331, 620, 967, 806, 1074, 1499, 1432,
+ /* 420 */ 1463, 1479, 1419, 1422, 1557, 1512, 1598, 1599, 1644, 1645,
+ /* 430 */ 1654, 1660, 1555, 1505, 1684, 1462, 1670, 1563, 1619, 1593,
+ /* 440 */ 1676, 1679, 1613, 1680, 1554, 1558, 1689, 1692, 1605, 1589,
+ /* 450 */ 1955, 1959, 1941, 1803, 1950, 1951, 1945, 1946, 1831, 1820,
+ /* 460 */ 1842, 1948, 1948, 1952, 1833, 1954, 1834, 1961, 1978, 1838,
+ /* 470 */ 1851, 1948, 1852, 1922, 1947, 1948, 1836, 1932, 1935, 1936,
+ /* 480 */ 1942, 1866, 1881, 1964, 1859, 1998, 1996, 1980, 1888, 1843,
+ /* 490 */ 1937, 1981, 1939, 1933, 1968, 1869, 1896, 1988, 1993, 1995,
+ /* 500 */ 1884, 1891, 1997, 1953, 1999, 2000, 1994, 2001, 1957, 1966,
+ /* 510 */ 2002, 1931, 1990, 2006, 1962, 2003, 2007, 2004, 1882, 2010,
+ /* 520 */ 2011, 2012, 2008, 2013, 2015, 1944, 1898, 2019, 2020, 1928,
+ /* 530 */ 2014, 2023, 1903, 2022, 2016, 2017, 2018, 2021, 1965, 1974,
+ /* 540 */ 1970, 2024, 1979, 1967, 2025, 2034, 2036, 2037, 2038, 2039,
+ /* 550 */ 2028, 1923, 1924, 2044, 2022, 2046, 2047, 2048, 2049, 2050,
+ /* 560 */ 2051, 2054, 2062, 2055, 2056, 2057, 2058, 2060, 2061, 2059,
+ /* 570 */ 1956, 1938, 1949, 1958, 2063, 2064, 2070, 2085, 2088,
};
-#define YY_REDUCE_COUNT (408)
+#define YY_REDUCE_COUNT (410)
#define YY_REDUCE_MIN (-271)
-#define YY_REDUCE_MAX (1740)
+#define YY_REDUCE_MAX (1753)
static const short yy_reduce_ofst[] = {
/* 0 */ -125, 733, 789, 241, 293, -123, -193, -191, -183, -187,
/* 10 */ 166, 238, 133, -207, -199, -267, -176, -6, 204, 489,
- /* 20 */ 576, -175, 598, 686, 615, 725, 860, 778, 781, 857,
- /* 30 */ 616, 887, 87, 240, -192, 408, 626, 796, 843, 854,
- /* 40 */ 1003, -271, -271, -271, -271, -271, -271, -271, -271, -271,
+ /* 20 */ 576, 598, -175, 686, 860, 615, 725, 1014, 778, 781,
+ /* 30 */ 857, 616, 887, 87, 240, -192, 408, 626, 796, 843,
+ /* 40 */ 854, 1004, -271, -271, -271, -271, -271, -271, -271, -271,
/* 50 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271,
/* 60 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271,
- /* 70 */ -271, -271, -271, -271, -271, -271, -271, -271, 80, 83,
- /* 80 */ 313, 886, 888, 996, 1034, 1059, 1081, 1100, 1117, 1152,
- /* 90 */ 1155, 1163, 1165, 1167, 1169, 1172, 1180, 1182, 1184, 1198,
- /* 100 */ 1200, 1213, 1215, 1225, 1227, 1252, 1254, 1264, 1299, 1303,
- /* 110 */ 1308, 1312, 1325, 1328, 1337, 1340, 1343, 1371, 1373, 1384,
- /* 120 */ 1386, 1411, 1420, 1424, 1426, 1458, 1470, 1473, 1475, 1479,
- /* 130 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271,
- /* 140 */ -271, 138, 459, 396, -158, 470, 302, -212, 521, 201,
- /* 150 */ -195, -92, 559, 630, 632, 630, -271, 632, 901, 63,
- /* 160 */ 407, -271, -271, -271, -271, 161, 161, 161, 251, 335,
- /* 170 */ 847, 960, 980, 537, 588, 618, 628, 688, 688, -166,
- /* 180 */ -161, 674, 790, 794, 799, 851, 852, -122, 680, -120,
- /* 190 */ 995, 1038, 415, 1051, 893, 798, 962, 400, 1086, 779,
- /* 200 */ 923, 924, 263, 1041, 979, 990, 1083, 1097, 1031, 1194,
- /* 210 */ 362, 994, 1139, 1005, 1037, 1202, 1205, 1195, 1210, -194,
- /* 220 */ 56, 185, -135, 232, 522, 560, 601, 617, 669, 683,
- /* 230 */ 711, 856, 908, 941, 1048, 1101, 1147, 1257, 1262, 1265,
- /* 240 */ 392, 1292, 1333, 1339, 1342, 1346, 1350, 1359, 1374, 1418,
- /* 250 */ 1421, 1436, 1437, 593, 755, 770, 997, 1445, 1459, 1209,
- /* 260 */ 1500, 1504, 1516, 1132, 1243, 1518, 1519, 1440, 1520, 560,
- /* 270 */ 1522, 1523, 1524, 1526, 1527, 1529, 1382, 1438, 1431, 1468,
- /* 280 */ 1469, 1472, 1476, 1209, 1431, 1431, 1485, 1525, 1539, 1435,
- /* 290 */ 1463, 1471, 1492, 1487, 1443, 1494, 1474, 1484, 1498, 1486,
- /* 300 */ 1502, 1455, 1530, 1531, 1533, 1540, 1542, 1544, 1505, 1506,
- /* 310 */ 1507, 1508, 1521, 1528, 1493, 1537, 1532, 1575, 1488, 1496,
- /* 320 */ 1584, 1594, 1509, 1510, 1600, 1538, 1534, 1541, 1574, 1577,
- /* 330 */ 1583, 1585, 1586, 1612, 1626, 1581, 1556, 1558, 1587, 1559,
- /* 340 */ 1601, 1588, 1603, 1592, 1631, 1640, 1550, 1553, 1643, 1645,
- /* 350 */ 1625, 1649, 1652, 1650, 1653, 1632, 1636, 1637, 1642, 1634,
- /* 360 */ 1639, 1641, 1646, 1656, 1655, 1658, 1659, 1661, 1663, 1560,
- /* 370 */ 1564, 1596, 1605, 1664, 1670, 1565, 1571, 1627, 1638, 1657,
- /* 380 */ 1665, 1623, 1702, 1630, 1666, 1667, 1671, 1673, 1703, 1718,
- /* 390 */ 1719, 1729, 1730, 1731, 1621, 1622, 1628, 1720, 1713, 1716,
- /* 400 */ 1722, 1723, 1733, 1717, 1724, 1727, 1728, 1725, 1740,
+ /* 70 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, 80,
+ /* 80 */ 83, 313, 886, 888, 918, 938, 1021, 1034, 1036, 1141,
+ /* 90 */ 1159, 1163, 1166, 1168, 1170, 1176, 1178, 1180, 1184, 1196,
+ /* 100 */ 1198, 1205, 1215, 1225, 1227, 1236, 1252, 1254, 1264, 1303,
+ /* 110 */ 1309, 1312, 1322, 1325, 1328, 1337, 1340, 1343, 1353, 1371,
+ /* 120 */ 1373, 1384, 1386, 1411, 1413, 1420, 1424, 1426, 1458, 1470,
+ /* 130 */ 1473, -271, -271, -271, -271, -271, -271, -271, -271, -271,
+ /* 140 */ -271, -271, 138, 459, 396, -158, 470, 302, -212, 521,
+ /* 150 */ 201, -195, -92, 559, 630, 632, 630, -271, 632, 901,
+ /* 160 */ 63, 407, 670, -271, -271, -271, -271, 161, 161, 161,
+ /* 170 */ 251, 335, 847, 979, 1097, 537, 588, 618, 628, 688,
+ /* 180 */ 688, -166, -161, 674, 787, 794, 799, 852, 996, -122,
+ /* 190 */ 837, -120, 1018, 1035, 415, 1047, 1001, 958, 1082, 400,
+ /* 200 */ 1099, 779, 1137, 1142, 263, 1083, 1145, 1150, 1041, 1139,
+ /* 210 */ 965, 1050, 362, 849, 752, 629, 675, 1162, 1173, 1090,
+ /* 220 */ 1195, -194, 56, 185, -135, 232, 522, 560, 571, 601,
+ /* 230 */ 617, 669, 683, 711, 850, 893, 1000, 1040, 1049, 1081,
+ /* 240 */ 1087, 1101, 392, 1114, 1123, 1155, 1161, 1175, 1271, 1293,
+ /* 250 */ 1299, 1330, 1339, 1342, 1347, 593, 1282, 1286, 1350, 1359,
+ /* 260 */ 1368, 1314, 1480, 1483, 1507, 1085, 1338, 1526, 1527, 1487,
+ /* 270 */ 1531, 560, 1532, 1534, 1535, 1538, 1539, 1541, 1448, 1450,
+ /* 280 */ 1496, 1484, 1485, 1489, 1490, 1314, 1496, 1496, 1504, 1536,
+ /* 290 */ 1564, 1451, 1486, 1492, 1509, 1493, 1465, 1515, 1494, 1495,
+ /* 300 */ 1517, 1500, 1519, 1474, 1550, 1543, 1548, 1556, 1565, 1566,
+ /* 310 */ 1518, 1523, 1542, 1544, 1525, 1545, 1513, 1553, 1552, 1604,
+ /* 320 */ 1508, 1510, 1608, 1609, 1520, 1528, 1612, 1540, 1559, 1560,
+ /* 330 */ 1592, 1591, 1595, 1596, 1597, 1629, 1638, 1594, 1569, 1570,
+ /* 340 */ 1600, 1568, 1610, 1601, 1611, 1603, 1643, 1651, 1562, 1571,
+ /* 350 */ 1655, 1659, 1640, 1663, 1666, 1664, 1667, 1641, 1650, 1652,
+ /* 360 */ 1653, 1647, 1656, 1657, 1658, 1668, 1672, 1681, 1649, 1682,
+ /* 370 */ 1683, 1573, 1582, 1607, 1615, 1685, 1702, 1586, 1587, 1642,
+ /* 380 */ 1646, 1673, 1675, 1636, 1714, 1637, 1677, 1674, 1678, 1694,
+ /* 390 */ 1719, 1734, 1735, 1746, 1747, 1750, 1633, 1661, 1686, 1738,
+ /* 400 */ 1728, 1733, 1736, 1737, 1740, 1726, 1730, 1742, 1743, 1748,
+ /* 410 */ 1753,
};
static const YYACTIONTYPE yy_default[] = {
- /* 0 */ 1639, 1639, 1639, 1469, 1236, 1347, 1236, 1236, 1236, 1469,
- /* 10 */ 1469, 1469, 1236, 1377, 1377, 1522, 1269, 1236, 1236, 1236,
- /* 20 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1468, 1236, 1236,
- /* 30 */ 1236, 1236, 1555, 1555, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 40 */ 1236, 1236, 1386, 1236, 1393, 1236, 1236, 1236, 1236, 1236,
- /* 50 */ 1470, 1471, 1236, 1236, 1236, 1521, 1523, 1486, 1400, 1399,
- /* 60 */ 1398, 1397, 1504, 1365, 1391, 1384, 1388, 1465, 1466, 1464,
- /* 70 */ 1617, 1471, 1470, 1236, 1387, 1433, 1449, 1432, 1236, 1236,
- /* 80 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 90 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 100 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 110 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 120 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 130 */ 1441, 1448, 1447, 1446, 1455, 1445, 1442, 1435, 1434, 1436,
- /* 140 */ 1437, 1236, 1236, 1260, 1236, 1236, 1257, 1311, 1236, 1236,
- /* 150 */ 1236, 1236, 1236, 1541, 1540, 1236, 1438, 1236, 1269, 1427,
- /* 160 */ 1426, 1452, 1439, 1451, 1450, 1529, 1591, 1590, 1487, 1236,
- /* 170 */ 1236, 1236, 1236, 1236, 1236, 1555, 1236, 1236, 1236, 1236,
- /* 180 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 190 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1367,
- /* 200 */ 1555, 1555, 1236, 1269, 1555, 1555, 1368, 1368, 1265, 1265,
- /* 210 */ 1371, 1236, 1536, 1338, 1338, 1338, 1338, 1347, 1338, 1236,
- /* 220 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 230 */ 1236, 1236, 1236, 1236, 1526, 1524, 1236, 1236, 1236, 1236,
- /* 240 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 250 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 260 */ 1236, 1236, 1236, 1343, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 270 */ 1236, 1236, 1236, 1236, 1236, 1584, 1236, 1499, 1325, 1343,
- /* 280 */ 1343, 1343, 1343, 1345, 1326, 1324, 1337, 1270, 1243, 1631,
- /* 290 */ 1403, 1392, 1344, 1392, 1628, 1390, 1403, 1403, 1390, 1403,
- /* 300 */ 1344, 1628, 1286, 1606, 1281, 1377, 1377, 1377, 1367, 1367,
- /* 310 */ 1367, 1367, 1371, 1371, 1467, 1344, 1337, 1236, 1631, 1631,
- /* 320 */ 1353, 1353, 1630, 1630, 1353, 1487, 1614, 1412, 1314, 1320,
- /* 330 */ 1320, 1320, 1320, 1353, 1254, 1390, 1614, 1614, 1390, 1412,
- /* 340 */ 1314, 1390, 1314, 1390, 1353, 1254, 1503, 1625, 1353, 1254,
- /* 350 */ 1477, 1353, 1254, 1353, 1254, 1477, 1312, 1312, 1312, 1301,
- /* 360 */ 1236, 1236, 1477, 1312, 1286, 1312, 1301, 1312, 1312, 1573,
- /* 370 */ 1236, 1481, 1481, 1477, 1353, 1565, 1565, 1380, 1380, 1385,
- /* 380 */ 1371, 1472, 1353, 1236, 1385, 1383, 1381, 1390, 1304, 1587,
- /* 390 */ 1587, 1583, 1583, 1583, 1636, 1636, 1536, 1599, 1269, 1269,
- /* 400 */ 1269, 1269, 1599, 1288, 1288, 1270, 1270, 1269, 1599, 1236,
- /* 410 */ 1236, 1236, 1236, 1236, 1236, 1594, 1236, 1531, 1488, 1357,
- /* 420 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 430 */ 1236, 1236, 1236, 1236, 1542, 1236, 1236, 1236, 1236, 1236,
- /* 440 */ 1236, 1236, 1236, 1236, 1236, 1417, 1236, 1239, 1533, 1236,
- /* 450 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1394, 1395, 1358,
- /* 460 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1409, 1236, 1236,
- /* 470 */ 1236, 1404, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 480 */ 1627, 1236, 1236, 1236, 1236, 1236, 1236, 1502, 1501, 1236,
- /* 490 */ 1236, 1355, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 500 */ 1236, 1236, 1236, 1236, 1236, 1284, 1236, 1236, 1236, 1236,
- /* 510 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 520 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1382,
- /* 530 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 540 */ 1236, 1236, 1236, 1236, 1570, 1372, 1236, 1236, 1236, 1236,
- /* 550 */ 1618, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 560 */ 1236, 1236, 1236, 1236, 1236, 1610, 1328, 1418, 1236, 1421,
- /* 570 */ 1258, 1236, 1248, 1236, 1236,
+ /* 0 */ 1648, 1648, 1648, 1478, 1243, 1354, 1243, 1243, 1243, 1478,
+ /* 10 */ 1478, 1478, 1243, 1384, 1384, 1531, 1276, 1243, 1243, 1243,
+ /* 20 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1477, 1243,
+ /* 30 */ 1243, 1243, 1243, 1564, 1564, 1243, 1243, 1243, 1243, 1243,
+ /* 40 */ 1243, 1243, 1243, 1393, 1243, 1400, 1243, 1243, 1243, 1243,
+ /* 50 */ 1243, 1479, 1480, 1243, 1243, 1243, 1530, 1532, 1495, 1407,
+ /* 60 */ 1406, 1405, 1404, 1513, 1372, 1398, 1391, 1395, 1474, 1475,
+ /* 70 */ 1473, 1626, 1480, 1479, 1243, 1394, 1442, 1458, 1441, 1243,
+ /* 80 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 90 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 100 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 110 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 120 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 130 */ 1243, 1450, 1457, 1456, 1455, 1464, 1454, 1451, 1444, 1443,
+ /* 140 */ 1445, 1446, 1243, 1243, 1267, 1243, 1243, 1264, 1318, 1243,
+ /* 150 */ 1243, 1243, 1243, 1243, 1550, 1549, 1243, 1447, 1243, 1276,
+ /* 160 */ 1435, 1434, 1433, 1461, 1448, 1460, 1459, 1538, 1600, 1599,
+ /* 170 */ 1496, 1243, 1243, 1243, 1243, 1243, 1243, 1564, 1243, 1243,
+ /* 180 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 190 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 200 */ 1243, 1374, 1564, 1564, 1243, 1276, 1564, 1564, 1375, 1375,
+ /* 210 */ 1272, 1272, 1378, 1243, 1545, 1345, 1345, 1345, 1345, 1354,
+ /* 220 */ 1345, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 230 */ 1243, 1243, 1243, 1243, 1243, 1243, 1535, 1533, 1243, 1243,
+ /* 240 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 250 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 260 */ 1243, 1243, 1243, 1243, 1243, 1350, 1243, 1243, 1243, 1243,
+ /* 270 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1593, 1243, 1508,
+ /* 280 */ 1332, 1350, 1350, 1350, 1350, 1352, 1333, 1331, 1344, 1277,
+ /* 290 */ 1250, 1640, 1410, 1399, 1351, 1399, 1637, 1397, 1410, 1410,
+ /* 300 */ 1397, 1410, 1351, 1637, 1293, 1615, 1288, 1384, 1384, 1384,
+ /* 310 */ 1374, 1374, 1374, 1374, 1378, 1378, 1476, 1351, 1344, 1243,
+ /* 320 */ 1640, 1640, 1360, 1360, 1639, 1639, 1360, 1496, 1623, 1419,
+ /* 330 */ 1321, 1327, 1327, 1327, 1327, 1360, 1261, 1397, 1623, 1623,
+ /* 340 */ 1397, 1419, 1321, 1397, 1321, 1397, 1360, 1261, 1512, 1634,
+ /* 350 */ 1360, 1261, 1486, 1360, 1261, 1360, 1261, 1486, 1319, 1319,
+ /* 360 */ 1319, 1308, 1243, 1243, 1486, 1319, 1293, 1319, 1308, 1319,
+ /* 370 */ 1319, 1582, 1243, 1490, 1490, 1486, 1360, 1574, 1574, 1387,
+ /* 380 */ 1387, 1392, 1378, 1481, 1360, 1243, 1392, 1390, 1388, 1397,
+ /* 390 */ 1311, 1596, 1596, 1592, 1592, 1592, 1645, 1645, 1545, 1608,
+ /* 400 */ 1276, 1276, 1276, 1276, 1608, 1295, 1295, 1277, 1277, 1276,
+ /* 410 */ 1608, 1243, 1243, 1243, 1243, 1243, 1243, 1603, 1243, 1540,
+ /* 420 */ 1497, 1364, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 430 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1551, 1243,
+ /* 440 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1424,
+ /* 450 */ 1243, 1246, 1542, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 460 */ 1243, 1401, 1402, 1365, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 470 */ 1243, 1416, 1243, 1243, 1243, 1411, 1243, 1243, 1243, 1243,
+ /* 480 */ 1243, 1243, 1243, 1243, 1636, 1243, 1243, 1243, 1243, 1243,
+ /* 490 */ 1243, 1511, 1510, 1243, 1243, 1362, 1243, 1243, 1243, 1243,
+ /* 500 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1291,
+ /* 510 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 520 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 530 */ 1243, 1243, 1243, 1389, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 540 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1579, 1379,
+ /* 550 */ 1243, 1243, 1243, 1243, 1627, 1243, 1243, 1243, 1243, 1243,
+ /* 560 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1619,
+ /* 570 */ 1335, 1425, 1243, 1428, 1265, 1243, 1255, 1243, 1243,
};
/********** End of lemon-generated parsing tables *****************************/
@@ -171229,221 +172915,223 @@ static const char *const yyRuleName[] = {
/* 185 */ "expr ::= expr COLLATE ID|STRING",
/* 186 */ "expr ::= CAST LP expr AS typetoken RP",
/* 187 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP",
- /* 188 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP",
- /* 189 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over",
- /* 190 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over",
- /* 191 */ "term ::= CTIME_KW",
- /* 192 */ "expr ::= LP nexprlist COMMA expr RP",
- /* 193 */ "expr ::= expr AND expr",
- /* 194 */ "expr ::= expr OR expr",
- /* 195 */ "expr ::= expr LT|GT|GE|LE expr",
- /* 196 */ "expr ::= expr EQ|NE expr",
- /* 197 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
- /* 198 */ "expr ::= expr PLUS|MINUS expr",
- /* 199 */ "expr ::= expr STAR|SLASH|REM expr",
- /* 200 */ "expr ::= expr CONCAT expr",
- /* 201 */ "likeop ::= NOT LIKE_KW|MATCH",
- /* 202 */ "expr ::= expr likeop expr",
- /* 203 */ "expr ::= expr likeop expr ESCAPE expr",
- /* 204 */ "expr ::= expr ISNULL|NOTNULL",
- /* 205 */ "expr ::= expr NOT NULL",
- /* 206 */ "expr ::= expr IS expr",
- /* 207 */ "expr ::= expr IS NOT expr",
- /* 208 */ "expr ::= expr IS NOT DISTINCT FROM expr",
- /* 209 */ "expr ::= expr IS DISTINCT FROM expr",
- /* 210 */ "expr ::= NOT expr",
- /* 211 */ "expr ::= BITNOT expr",
- /* 212 */ "expr ::= PLUS|MINUS expr",
- /* 213 */ "expr ::= expr PTR expr",
- /* 214 */ "between_op ::= BETWEEN",
- /* 215 */ "between_op ::= NOT BETWEEN",
- /* 216 */ "expr ::= expr between_op expr AND expr",
- /* 217 */ "in_op ::= IN",
- /* 218 */ "in_op ::= NOT IN",
- /* 219 */ "expr ::= expr in_op LP exprlist RP",
- /* 220 */ "expr ::= LP select RP",
- /* 221 */ "expr ::= expr in_op LP select RP",
- /* 222 */ "expr ::= expr in_op nm dbnm paren_exprlist",
- /* 223 */ "expr ::= EXISTS LP select RP",
- /* 224 */ "expr ::= CASE case_operand case_exprlist case_else END",
- /* 225 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
- /* 226 */ "case_exprlist ::= WHEN expr THEN expr",
- /* 227 */ "case_else ::= ELSE expr",
- /* 228 */ "case_else ::=",
- /* 229 */ "case_operand ::=",
- /* 230 */ "exprlist ::=",
- /* 231 */ "nexprlist ::= nexprlist COMMA expr",
- /* 232 */ "nexprlist ::= expr",
- /* 233 */ "paren_exprlist ::=",
- /* 234 */ "paren_exprlist ::= LP exprlist RP",
- /* 235 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt",
- /* 236 */ "uniqueflag ::= UNIQUE",
- /* 237 */ "uniqueflag ::=",
- /* 238 */ "eidlist_opt ::=",
- /* 239 */ "eidlist_opt ::= LP eidlist RP",
- /* 240 */ "eidlist ::= eidlist COMMA nm collate sortorder",
- /* 241 */ "eidlist ::= nm collate sortorder",
- /* 242 */ "collate ::=",
- /* 243 */ "collate ::= COLLATE ID|STRING",
- /* 244 */ "cmd ::= DROP INDEX ifexists fullname",
- /* 245 */ "cmd ::= VACUUM vinto",
- /* 246 */ "cmd ::= VACUUM nm vinto",
- /* 247 */ "vinto ::= INTO expr",
- /* 248 */ "vinto ::=",
- /* 249 */ "cmd ::= PRAGMA nm dbnm",
- /* 250 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
- /* 251 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
- /* 252 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
- /* 253 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
- /* 254 */ "plus_num ::= PLUS INTEGER|FLOAT",
- /* 255 */ "minus_num ::= MINUS INTEGER|FLOAT",
- /* 256 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
- /* 257 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
- /* 258 */ "trigger_time ::= BEFORE|AFTER",
- /* 259 */ "trigger_time ::= INSTEAD OF",
- /* 260 */ "trigger_time ::=",
- /* 261 */ "trigger_event ::= DELETE|INSERT",
- /* 262 */ "trigger_event ::= UPDATE",
- /* 263 */ "trigger_event ::= UPDATE OF idlist",
- /* 264 */ "when_clause ::=",
- /* 265 */ "when_clause ::= WHEN expr",
- /* 266 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
- /* 267 */ "trigger_cmd_list ::= trigger_cmd SEMI",
- /* 268 */ "trnm ::= nm DOT nm",
- /* 269 */ "tridxby ::= INDEXED BY nm",
- /* 270 */ "tridxby ::= NOT INDEXED",
- /* 271 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt",
- /* 272 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt",
- /* 273 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt",
- /* 274 */ "trigger_cmd ::= scanpt select scanpt",
- /* 275 */ "expr ::= RAISE LP IGNORE RP",
- /* 276 */ "expr ::= RAISE LP raisetype COMMA nm RP",
- /* 277 */ "raisetype ::= ROLLBACK",
- /* 278 */ "raisetype ::= ABORT",
- /* 279 */ "raisetype ::= FAIL",
- /* 280 */ "cmd ::= DROP TRIGGER ifexists fullname",
- /* 281 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
- /* 282 */ "cmd ::= DETACH database_kw_opt expr",
- /* 283 */ "key_opt ::=",
- /* 284 */ "key_opt ::= KEY expr",
- /* 285 */ "cmd ::= REINDEX",
- /* 286 */ "cmd ::= REINDEX nm dbnm",
- /* 287 */ "cmd ::= ANALYZE",
- /* 288 */ "cmd ::= ANALYZE nm dbnm",
- /* 289 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
- /* 290 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
- /* 291 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm",
- /* 292 */ "add_column_fullname ::= fullname",
- /* 293 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm",
- /* 294 */ "cmd ::= create_vtab",
- /* 295 */ "cmd ::= create_vtab LP vtabarglist RP",
- /* 296 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
- /* 297 */ "vtabarg ::=",
- /* 298 */ "vtabargtoken ::= ANY",
- /* 299 */ "vtabargtoken ::= lp anylist RP",
- /* 300 */ "lp ::= LP",
- /* 301 */ "with ::= WITH wqlist",
- /* 302 */ "with ::= WITH RECURSIVE wqlist",
- /* 303 */ "wqas ::= AS",
- /* 304 */ "wqas ::= AS MATERIALIZED",
- /* 305 */ "wqas ::= AS NOT MATERIALIZED",
- /* 306 */ "wqitem ::= nm eidlist_opt wqas LP select RP",
- /* 307 */ "wqlist ::= wqitem",
- /* 308 */ "wqlist ::= wqlist COMMA wqitem",
- /* 309 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn",
- /* 310 */ "windowdefn ::= nm AS LP window RP",
- /* 311 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt",
- /* 312 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt",
- /* 313 */ "window ::= ORDER BY sortlist frame_opt",
- /* 314 */ "window ::= nm ORDER BY sortlist frame_opt",
- /* 315 */ "window ::= nm frame_opt",
- /* 316 */ "frame_opt ::=",
- /* 317 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt",
- /* 318 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt",
- /* 319 */ "range_or_rows ::= RANGE|ROWS|GROUPS",
- /* 320 */ "frame_bound_s ::= frame_bound",
- /* 321 */ "frame_bound_s ::= UNBOUNDED PRECEDING",
- /* 322 */ "frame_bound_e ::= frame_bound",
- /* 323 */ "frame_bound_e ::= UNBOUNDED FOLLOWING",
- /* 324 */ "frame_bound ::= expr PRECEDING|FOLLOWING",
- /* 325 */ "frame_bound ::= CURRENT ROW",
- /* 326 */ "frame_exclude_opt ::=",
- /* 327 */ "frame_exclude_opt ::= EXCLUDE frame_exclude",
- /* 328 */ "frame_exclude ::= NO OTHERS",
- /* 329 */ "frame_exclude ::= CURRENT ROW",
- /* 330 */ "frame_exclude ::= GROUP|TIES",
- /* 331 */ "window_clause ::= WINDOW windowdefn_list",
- /* 332 */ "filter_over ::= filter_clause over_clause",
- /* 333 */ "filter_over ::= over_clause",
- /* 334 */ "filter_over ::= filter_clause",
- /* 335 */ "over_clause ::= OVER LP window RP",
- /* 336 */ "over_clause ::= OVER nm",
- /* 337 */ "filter_clause ::= FILTER LP WHERE expr RP",
- /* 338 */ "input ::= cmdlist",
- /* 339 */ "cmdlist ::= cmdlist ecmd",
- /* 340 */ "cmdlist ::= ecmd",
- /* 341 */ "ecmd ::= SEMI",
- /* 342 */ "ecmd ::= cmdx SEMI",
- /* 343 */ "ecmd ::= explain cmdx SEMI",
- /* 344 */ "trans_opt ::=",
- /* 345 */ "trans_opt ::= TRANSACTION",
- /* 346 */ "trans_opt ::= TRANSACTION nm",
- /* 347 */ "savepoint_opt ::= SAVEPOINT",
- /* 348 */ "savepoint_opt ::=",
- /* 349 */ "cmd ::= create_table create_table_args",
- /* 350 */ "table_option_set ::= table_option",
- /* 351 */ "columnlist ::= columnlist COMMA columnname carglist",
- /* 352 */ "columnlist ::= columnname carglist",
- /* 353 */ "nm ::= ID|INDEXED|JOIN_KW",
- /* 354 */ "nm ::= STRING",
- /* 355 */ "typetoken ::= typename",
- /* 356 */ "typename ::= ID|STRING",
- /* 357 */ "signed ::= plus_num",
- /* 358 */ "signed ::= minus_num",
- /* 359 */ "carglist ::= carglist ccons",
- /* 360 */ "carglist ::=",
- /* 361 */ "ccons ::= NULL onconf",
- /* 362 */ "ccons ::= GENERATED ALWAYS AS generated",
- /* 363 */ "ccons ::= AS generated",
- /* 364 */ "conslist_opt ::= COMMA conslist",
- /* 365 */ "conslist ::= conslist tconscomma tcons",
- /* 366 */ "conslist ::= tcons",
- /* 367 */ "tconscomma ::=",
- /* 368 */ "defer_subclause_opt ::= defer_subclause",
- /* 369 */ "resolvetype ::= raisetype",
- /* 370 */ "selectnowith ::= oneselect",
- /* 371 */ "oneselect ::= values",
- /* 372 */ "sclp ::= selcollist COMMA",
- /* 373 */ "as ::= ID|STRING",
- /* 374 */ "indexed_opt ::= indexed_by",
- /* 375 */ "returning ::=",
- /* 376 */ "expr ::= term",
- /* 377 */ "likeop ::= LIKE_KW|MATCH",
- /* 378 */ "case_operand ::= expr",
- /* 379 */ "exprlist ::= nexprlist",
- /* 380 */ "nmnum ::= plus_num",
- /* 381 */ "nmnum ::= nm",
- /* 382 */ "nmnum ::= ON",
- /* 383 */ "nmnum ::= DELETE",
- /* 384 */ "nmnum ::= DEFAULT",
- /* 385 */ "plus_num ::= INTEGER|FLOAT",
- /* 386 */ "foreach_clause ::=",
- /* 387 */ "foreach_clause ::= FOR EACH ROW",
- /* 388 */ "trnm ::= nm",
- /* 389 */ "tridxby ::=",
- /* 390 */ "database_kw_opt ::= DATABASE",
- /* 391 */ "database_kw_opt ::=",
- /* 392 */ "kwcolumn_opt ::=",
- /* 393 */ "kwcolumn_opt ::= COLUMNKW",
- /* 394 */ "vtabarglist ::= vtabarg",
- /* 395 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
- /* 396 */ "vtabarg ::= vtabarg vtabargtoken",
- /* 397 */ "anylist ::=",
- /* 398 */ "anylist ::= anylist LP anylist RP",
- /* 399 */ "anylist ::= anylist ANY",
- /* 400 */ "with ::=",
- /* 401 */ "windowdefn_list ::= windowdefn",
- /* 402 */ "window ::= frame_opt",
+ /* 188 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP",
+ /* 189 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP",
+ /* 190 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over",
+ /* 191 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over",
+ /* 192 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over",
+ /* 193 */ "term ::= CTIME_KW",
+ /* 194 */ "expr ::= LP nexprlist COMMA expr RP",
+ /* 195 */ "expr ::= expr AND expr",
+ /* 196 */ "expr ::= expr OR expr",
+ /* 197 */ "expr ::= expr LT|GT|GE|LE expr",
+ /* 198 */ "expr ::= expr EQ|NE expr",
+ /* 199 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
+ /* 200 */ "expr ::= expr PLUS|MINUS expr",
+ /* 201 */ "expr ::= expr STAR|SLASH|REM expr",
+ /* 202 */ "expr ::= expr CONCAT expr",
+ /* 203 */ "likeop ::= NOT LIKE_KW|MATCH",
+ /* 204 */ "expr ::= expr likeop expr",
+ /* 205 */ "expr ::= expr likeop expr ESCAPE expr",
+ /* 206 */ "expr ::= expr ISNULL|NOTNULL",
+ /* 207 */ "expr ::= expr NOT NULL",
+ /* 208 */ "expr ::= expr IS expr",
+ /* 209 */ "expr ::= expr IS NOT expr",
+ /* 210 */ "expr ::= expr IS NOT DISTINCT FROM expr",
+ /* 211 */ "expr ::= expr IS DISTINCT FROM expr",
+ /* 212 */ "expr ::= NOT expr",
+ /* 213 */ "expr ::= BITNOT expr",
+ /* 214 */ "expr ::= PLUS|MINUS expr",
+ /* 215 */ "expr ::= expr PTR expr",
+ /* 216 */ "between_op ::= BETWEEN",
+ /* 217 */ "between_op ::= NOT BETWEEN",
+ /* 218 */ "expr ::= expr between_op expr AND expr",
+ /* 219 */ "in_op ::= IN",
+ /* 220 */ "in_op ::= NOT IN",
+ /* 221 */ "expr ::= expr in_op LP exprlist RP",
+ /* 222 */ "expr ::= LP select RP",
+ /* 223 */ "expr ::= expr in_op LP select RP",
+ /* 224 */ "expr ::= expr in_op nm dbnm paren_exprlist",
+ /* 225 */ "expr ::= EXISTS LP select RP",
+ /* 226 */ "expr ::= CASE case_operand case_exprlist case_else END",
+ /* 227 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
+ /* 228 */ "case_exprlist ::= WHEN expr THEN expr",
+ /* 229 */ "case_else ::= ELSE expr",
+ /* 230 */ "case_else ::=",
+ /* 231 */ "case_operand ::=",
+ /* 232 */ "exprlist ::=",
+ /* 233 */ "nexprlist ::= nexprlist COMMA expr",
+ /* 234 */ "nexprlist ::= expr",
+ /* 235 */ "paren_exprlist ::=",
+ /* 236 */ "paren_exprlist ::= LP exprlist RP",
+ /* 237 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt",
+ /* 238 */ "uniqueflag ::= UNIQUE",
+ /* 239 */ "uniqueflag ::=",
+ /* 240 */ "eidlist_opt ::=",
+ /* 241 */ "eidlist_opt ::= LP eidlist RP",
+ /* 242 */ "eidlist ::= eidlist COMMA nm collate sortorder",
+ /* 243 */ "eidlist ::= nm collate sortorder",
+ /* 244 */ "collate ::=",
+ /* 245 */ "collate ::= COLLATE ID|STRING",
+ /* 246 */ "cmd ::= DROP INDEX ifexists fullname",
+ /* 247 */ "cmd ::= VACUUM vinto",
+ /* 248 */ "cmd ::= VACUUM nm vinto",
+ /* 249 */ "vinto ::= INTO expr",
+ /* 250 */ "vinto ::=",
+ /* 251 */ "cmd ::= PRAGMA nm dbnm",
+ /* 252 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
+ /* 253 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
+ /* 254 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
+ /* 255 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
+ /* 256 */ "plus_num ::= PLUS INTEGER|FLOAT",
+ /* 257 */ "minus_num ::= MINUS INTEGER|FLOAT",
+ /* 258 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
+ /* 259 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
+ /* 260 */ "trigger_time ::= BEFORE|AFTER",
+ /* 261 */ "trigger_time ::= INSTEAD OF",
+ /* 262 */ "trigger_time ::=",
+ /* 263 */ "trigger_event ::= DELETE|INSERT",
+ /* 264 */ "trigger_event ::= UPDATE",
+ /* 265 */ "trigger_event ::= UPDATE OF idlist",
+ /* 266 */ "when_clause ::=",
+ /* 267 */ "when_clause ::= WHEN expr",
+ /* 268 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
+ /* 269 */ "trigger_cmd_list ::= trigger_cmd SEMI",
+ /* 270 */ "trnm ::= nm DOT nm",
+ /* 271 */ "tridxby ::= INDEXED BY nm",
+ /* 272 */ "tridxby ::= NOT INDEXED",
+ /* 273 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt",
+ /* 274 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt",
+ /* 275 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt",
+ /* 276 */ "trigger_cmd ::= scanpt select scanpt",
+ /* 277 */ "expr ::= RAISE LP IGNORE RP",
+ /* 278 */ "expr ::= RAISE LP raisetype COMMA nm RP",
+ /* 279 */ "raisetype ::= ROLLBACK",
+ /* 280 */ "raisetype ::= ABORT",
+ /* 281 */ "raisetype ::= FAIL",
+ /* 282 */ "cmd ::= DROP TRIGGER ifexists fullname",
+ /* 283 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
+ /* 284 */ "cmd ::= DETACH database_kw_opt expr",
+ /* 285 */ "key_opt ::=",
+ /* 286 */ "key_opt ::= KEY expr",
+ /* 287 */ "cmd ::= REINDEX",
+ /* 288 */ "cmd ::= REINDEX nm dbnm",
+ /* 289 */ "cmd ::= ANALYZE",
+ /* 290 */ "cmd ::= ANALYZE nm dbnm",
+ /* 291 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
+ /* 292 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
+ /* 293 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm",
+ /* 294 */ "add_column_fullname ::= fullname",
+ /* 295 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm",
+ /* 296 */ "cmd ::= create_vtab",
+ /* 297 */ "cmd ::= create_vtab LP vtabarglist RP",
+ /* 298 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
+ /* 299 */ "vtabarg ::=",
+ /* 300 */ "vtabargtoken ::= ANY",
+ /* 301 */ "vtabargtoken ::= lp anylist RP",
+ /* 302 */ "lp ::= LP",
+ /* 303 */ "with ::= WITH wqlist",
+ /* 304 */ "with ::= WITH RECURSIVE wqlist",
+ /* 305 */ "wqas ::= AS",
+ /* 306 */ "wqas ::= AS MATERIALIZED",
+ /* 307 */ "wqas ::= AS NOT MATERIALIZED",
+ /* 308 */ "wqitem ::= nm eidlist_opt wqas LP select RP",
+ /* 309 */ "wqlist ::= wqitem",
+ /* 310 */ "wqlist ::= wqlist COMMA wqitem",
+ /* 311 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn",
+ /* 312 */ "windowdefn ::= nm AS LP window RP",
+ /* 313 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt",
+ /* 314 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt",
+ /* 315 */ "window ::= ORDER BY sortlist frame_opt",
+ /* 316 */ "window ::= nm ORDER BY sortlist frame_opt",
+ /* 317 */ "window ::= nm frame_opt",
+ /* 318 */ "frame_opt ::=",
+ /* 319 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt",
+ /* 320 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt",
+ /* 321 */ "range_or_rows ::= RANGE|ROWS|GROUPS",
+ /* 322 */ "frame_bound_s ::= frame_bound",
+ /* 323 */ "frame_bound_s ::= UNBOUNDED PRECEDING",
+ /* 324 */ "frame_bound_e ::= frame_bound",
+ /* 325 */ "frame_bound_e ::= UNBOUNDED FOLLOWING",
+ /* 326 */ "frame_bound ::= expr PRECEDING|FOLLOWING",
+ /* 327 */ "frame_bound ::= CURRENT ROW",
+ /* 328 */ "frame_exclude_opt ::=",
+ /* 329 */ "frame_exclude_opt ::= EXCLUDE frame_exclude",
+ /* 330 */ "frame_exclude ::= NO OTHERS",
+ /* 331 */ "frame_exclude ::= CURRENT ROW",
+ /* 332 */ "frame_exclude ::= GROUP|TIES",
+ /* 333 */ "window_clause ::= WINDOW windowdefn_list",
+ /* 334 */ "filter_over ::= filter_clause over_clause",
+ /* 335 */ "filter_over ::= over_clause",
+ /* 336 */ "filter_over ::= filter_clause",
+ /* 337 */ "over_clause ::= OVER LP window RP",
+ /* 338 */ "over_clause ::= OVER nm",
+ /* 339 */ "filter_clause ::= FILTER LP WHERE expr RP",
+ /* 340 */ "input ::= cmdlist",
+ /* 341 */ "cmdlist ::= cmdlist ecmd",
+ /* 342 */ "cmdlist ::= ecmd",
+ /* 343 */ "ecmd ::= SEMI",
+ /* 344 */ "ecmd ::= cmdx SEMI",
+ /* 345 */ "ecmd ::= explain cmdx SEMI",
+ /* 346 */ "trans_opt ::=",
+ /* 347 */ "trans_opt ::= TRANSACTION",
+ /* 348 */ "trans_opt ::= TRANSACTION nm",
+ /* 349 */ "savepoint_opt ::= SAVEPOINT",
+ /* 350 */ "savepoint_opt ::=",
+ /* 351 */ "cmd ::= create_table create_table_args",
+ /* 352 */ "table_option_set ::= table_option",
+ /* 353 */ "columnlist ::= columnlist COMMA columnname carglist",
+ /* 354 */ "columnlist ::= columnname carglist",
+ /* 355 */ "nm ::= ID|INDEXED|JOIN_KW",
+ /* 356 */ "nm ::= STRING",
+ /* 357 */ "typetoken ::= typename",
+ /* 358 */ "typename ::= ID|STRING",
+ /* 359 */ "signed ::= plus_num",
+ /* 360 */ "signed ::= minus_num",
+ /* 361 */ "carglist ::= carglist ccons",
+ /* 362 */ "carglist ::=",
+ /* 363 */ "ccons ::= NULL onconf",
+ /* 364 */ "ccons ::= GENERATED ALWAYS AS generated",
+ /* 365 */ "ccons ::= AS generated",
+ /* 366 */ "conslist_opt ::= COMMA conslist",
+ /* 367 */ "conslist ::= conslist tconscomma tcons",
+ /* 368 */ "conslist ::= tcons",
+ /* 369 */ "tconscomma ::=",
+ /* 370 */ "defer_subclause_opt ::= defer_subclause",
+ /* 371 */ "resolvetype ::= raisetype",
+ /* 372 */ "selectnowith ::= oneselect",
+ /* 373 */ "oneselect ::= values",
+ /* 374 */ "sclp ::= selcollist COMMA",
+ /* 375 */ "as ::= ID|STRING",
+ /* 376 */ "indexed_opt ::= indexed_by",
+ /* 377 */ "returning ::=",
+ /* 378 */ "expr ::= term",
+ /* 379 */ "likeop ::= LIKE_KW|MATCH",
+ /* 380 */ "case_operand ::= expr",
+ /* 381 */ "exprlist ::= nexprlist",
+ /* 382 */ "nmnum ::= plus_num",
+ /* 383 */ "nmnum ::= nm",
+ /* 384 */ "nmnum ::= ON",
+ /* 385 */ "nmnum ::= DELETE",
+ /* 386 */ "nmnum ::= DEFAULT",
+ /* 387 */ "plus_num ::= INTEGER|FLOAT",
+ /* 388 */ "foreach_clause ::=",
+ /* 389 */ "foreach_clause ::= FOR EACH ROW",
+ /* 390 */ "trnm ::= nm",
+ /* 391 */ "tridxby ::=",
+ /* 392 */ "database_kw_opt ::= DATABASE",
+ /* 393 */ "database_kw_opt ::=",
+ /* 394 */ "kwcolumn_opt ::=",
+ /* 395 */ "kwcolumn_opt ::= COLUMNKW",
+ /* 396 */ "vtabarglist ::= vtabarg",
+ /* 397 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
+ /* 398 */ "vtabarg ::= vtabarg vtabargtoken",
+ /* 399 */ "anylist ::=",
+ /* 400 */ "anylist ::= anylist LP anylist RP",
+ /* 401 */ "anylist ::= anylist ANY",
+ /* 402 */ "with ::=",
+ /* 403 */ "windowdefn_list ::= windowdefn",
+ /* 404 */ "window ::= frame_opt",
};
#endif /* NDEBUG */
@@ -172138,221 +173826,223 @@ static const YYCODETYPE yyRuleInfoLhs[] = {
217, /* (185) expr ::= expr COLLATE ID|STRING */
217, /* (186) expr ::= CAST LP expr AS typetoken RP */
217, /* (187) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
- 217, /* (188) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
- 217, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
- 217, /* (190) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
- 216, /* (191) term ::= CTIME_KW */
- 217, /* (192) expr ::= LP nexprlist COMMA expr RP */
- 217, /* (193) expr ::= expr AND expr */
- 217, /* (194) expr ::= expr OR expr */
- 217, /* (195) expr ::= expr LT|GT|GE|LE expr */
- 217, /* (196) expr ::= expr EQ|NE expr */
- 217, /* (197) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
- 217, /* (198) expr ::= expr PLUS|MINUS expr */
- 217, /* (199) expr ::= expr STAR|SLASH|REM expr */
- 217, /* (200) expr ::= expr CONCAT expr */
- 274, /* (201) likeop ::= NOT LIKE_KW|MATCH */
- 217, /* (202) expr ::= expr likeop expr */
- 217, /* (203) expr ::= expr likeop expr ESCAPE expr */
- 217, /* (204) expr ::= expr ISNULL|NOTNULL */
- 217, /* (205) expr ::= expr NOT NULL */
- 217, /* (206) expr ::= expr IS expr */
- 217, /* (207) expr ::= expr IS NOT expr */
- 217, /* (208) expr ::= expr IS NOT DISTINCT FROM expr */
- 217, /* (209) expr ::= expr IS DISTINCT FROM expr */
- 217, /* (210) expr ::= NOT expr */
- 217, /* (211) expr ::= BITNOT expr */
- 217, /* (212) expr ::= PLUS|MINUS expr */
- 217, /* (213) expr ::= expr PTR expr */
- 275, /* (214) between_op ::= BETWEEN */
- 275, /* (215) between_op ::= NOT BETWEEN */
- 217, /* (216) expr ::= expr between_op expr AND expr */
- 276, /* (217) in_op ::= IN */
- 276, /* (218) in_op ::= NOT IN */
- 217, /* (219) expr ::= expr in_op LP exprlist RP */
- 217, /* (220) expr ::= LP select RP */
- 217, /* (221) expr ::= expr in_op LP select RP */
- 217, /* (222) expr ::= expr in_op nm dbnm paren_exprlist */
- 217, /* (223) expr ::= EXISTS LP select RP */
- 217, /* (224) expr ::= CASE case_operand case_exprlist case_else END */
- 279, /* (225) case_exprlist ::= case_exprlist WHEN expr THEN expr */
- 279, /* (226) case_exprlist ::= WHEN expr THEN expr */
- 280, /* (227) case_else ::= ELSE expr */
- 280, /* (228) case_else ::= */
- 278, /* (229) case_operand ::= */
- 261, /* (230) exprlist ::= */
- 253, /* (231) nexprlist ::= nexprlist COMMA expr */
- 253, /* (232) nexprlist ::= expr */
- 277, /* (233) paren_exprlist ::= */
- 277, /* (234) paren_exprlist ::= LP exprlist RP */
- 190, /* (235) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
- 281, /* (236) uniqueflag ::= UNIQUE */
- 281, /* (237) uniqueflag ::= */
- 221, /* (238) eidlist_opt ::= */
- 221, /* (239) eidlist_opt ::= LP eidlist RP */
- 232, /* (240) eidlist ::= eidlist COMMA nm collate sortorder */
- 232, /* (241) eidlist ::= nm collate sortorder */
- 282, /* (242) collate ::= */
- 282, /* (243) collate ::= COLLATE ID|STRING */
- 190, /* (244) cmd ::= DROP INDEX ifexists fullname */
- 190, /* (245) cmd ::= VACUUM vinto */
- 190, /* (246) cmd ::= VACUUM nm vinto */
- 283, /* (247) vinto ::= INTO expr */
- 283, /* (248) vinto ::= */
- 190, /* (249) cmd ::= PRAGMA nm dbnm */
- 190, /* (250) cmd ::= PRAGMA nm dbnm EQ nmnum */
- 190, /* (251) cmd ::= PRAGMA nm dbnm LP nmnum RP */
- 190, /* (252) cmd ::= PRAGMA nm dbnm EQ minus_num */
- 190, /* (253) cmd ::= PRAGMA nm dbnm LP minus_num RP */
- 211, /* (254) plus_num ::= PLUS INTEGER|FLOAT */
- 212, /* (255) minus_num ::= MINUS INTEGER|FLOAT */
- 190, /* (256) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
- 285, /* (257) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
- 287, /* (258) trigger_time ::= BEFORE|AFTER */
- 287, /* (259) trigger_time ::= INSTEAD OF */
- 287, /* (260) trigger_time ::= */
- 288, /* (261) trigger_event ::= DELETE|INSERT */
- 288, /* (262) trigger_event ::= UPDATE */
- 288, /* (263) trigger_event ::= UPDATE OF idlist */
- 290, /* (264) when_clause ::= */
- 290, /* (265) when_clause ::= WHEN expr */
- 286, /* (266) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
- 286, /* (267) trigger_cmd_list ::= trigger_cmd SEMI */
- 292, /* (268) trnm ::= nm DOT nm */
- 293, /* (269) tridxby ::= INDEXED BY nm */
- 293, /* (270) tridxby ::= NOT INDEXED */
- 291, /* (271) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
- 291, /* (272) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
- 291, /* (273) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
- 291, /* (274) trigger_cmd ::= scanpt select scanpt */
- 217, /* (275) expr ::= RAISE LP IGNORE RP */
- 217, /* (276) expr ::= RAISE LP raisetype COMMA nm RP */
- 236, /* (277) raisetype ::= ROLLBACK */
- 236, /* (278) raisetype ::= ABORT */
- 236, /* (279) raisetype ::= FAIL */
- 190, /* (280) cmd ::= DROP TRIGGER ifexists fullname */
- 190, /* (281) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
- 190, /* (282) cmd ::= DETACH database_kw_opt expr */
- 295, /* (283) key_opt ::= */
- 295, /* (284) key_opt ::= KEY expr */
- 190, /* (285) cmd ::= REINDEX */
- 190, /* (286) cmd ::= REINDEX nm dbnm */
- 190, /* (287) cmd ::= ANALYZE */
- 190, /* (288) cmd ::= ANALYZE nm dbnm */
- 190, /* (289) cmd ::= ALTER TABLE fullname RENAME TO nm */
- 190, /* (290) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
- 190, /* (291) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
- 296, /* (292) add_column_fullname ::= fullname */
- 190, /* (293) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
- 190, /* (294) cmd ::= create_vtab */
- 190, /* (295) cmd ::= create_vtab LP vtabarglist RP */
- 298, /* (296) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
- 300, /* (297) vtabarg ::= */
- 301, /* (298) vtabargtoken ::= ANY */
- 301, /* (299) vtabargtoken ::= lp anylist RP */
- 302, /* (300) lp ::= LP */
- 266, /* (301) with ::= WITH wqlist */
- 266, /* (302) with ::= WITH RECURSIVE wqlist */
- 305, /* (303) wqas ::= AS */
- 305, /* (304) wqas ::= AS MATERIALIZED */
- 305, /* (305) wqas ::= AS NOT MATERIALIZED */
- 304, /* (306) wqitem ::= nm eidlist_opt wqas LP select RP */
- 241, /* (307) wqlist ::= wqitem */
- 241, /* (308) wqlist ::= wqlist COMMA wqitem */
- 306, /* (309) windowdefn_list ::= windowdefn_list COMMA windowdefn */
- 307, /* (310) windowdefn ::= nm AS LP window RP */
- 308, /* (311) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
- 308, /* (312) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
- 308, /* (313) window ::= ORDER BY sortlist frame_opt */
- 308, /* (314) window ::= nm ORDER BY sortlist frame_opt */
- 308, /* (315) window ::= nm frame_opt */
- 309, /* (316) frame_opt ::= */
- 309, /* (317) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
- 309, /* (318) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
- 313, /* (319) range_or_rows ::= RANGE|ROWS|GROUPS */
- 315, /* (320) frame_bound_s ::= frame_bound */
- 315, /* (321) frame_bound_s ::= UNBOUNDED PRECEDING */
- 316, /* (322) frame_bound_e ::= frame_bound */
- 316, /* (323) frame_bound_e ::= UNBOUNDED FOLLOWING */
- 314, /* (324) frame_bound ::= expr PRECEDING|FOLLOWING */
- 314, /* (325) frame_bound ::= CURRENT ROW */
- 317, /* (326) frame_exclude_opt ::= */
- 317, /* (327) frame_exclude_opt ::= EXCLUDE frame_exclude */
- 318, /* (328) frame_exclude ::= NO OTHERS */
- 318, /* (329) frame_exclude ::= CURRENT ROW */
- 318, /* (330) frame_exclude ::= GROUP|TIES */
- 251, /* (331) window_clause ::= WINDOW windowdefn_list */
- 273, /* (332) filter_over ::= filter_clause over_clause */
- 273, /* (333) filter_over ::= over_clause */
- 273, /* (334) filter_over ::= filter_clause */
- 312, /* (335) over_clause ::= OVER LP window RP */
- 312, /* (336) over_clause ::= OVER nm */
- 311, /* (337) filter_clause ::= FILTER LP WHERE expr RP */
- 185, /* (338) input ::= cmdlist */
- 186, /* (339) cmdlist ::= cmdlist ecmd */
- 186, /* (340) cmdlist ::= ecmd */
- 187, /* (341) ecmd ::= SEMI */
- 187, /* (342) ecmd ::= cmdx SEMI */
- 187, /* (343) ecmd ::= explain cmdx SEMI */
- 192, /* (344) trans_opt ::= */
- 192, /* (345) trans_opt ::= TRANSACTION */
- 192, /* (346) trans_opt ::= TRANSACTION nm */
- 194, /* (347) savepoint_opt ::= SAVEPOINT */
- 194, /* (348) savepoint_opt ::= */
- 190, /* (349) cmd ::= create_table create_table_args */
- 203, /* (350) table_option_set ::= table_option */
- 201, /* (351) columnlist ::= columnlist COMMA columnname carglist */
- 201, /* (352) columnlist ::= columnname carglist */
- 193, /* (353) nm ::= ID|INDEXED|JOIN_KW */
- 193, /* (354) nm ::= STRING */
- 208, /* (355) typetoken ::= typename */
- 209, /* (356) typename ::= ID|STRING */
- 210, /* (357) signed ::= plus_num */
- 210, /* (358) signed ::= minus_num */
- 207, /* (359) carglist ::= carglist ccons */
- 207, /* (360) carglist ::= */
- 215, /* (361) ccons ::= NULL onconf */
- 215, /* (362) ccons ::= GENERATED ALWAYS AS generated */
- 215, /* (363) ccons ::= AS generated */
- 202, /* (364) conslist_opt ::= COMMA conslist */
- 228, /* (365) conslist ::= conslist tconscomma tcons */
- 228, /* (366) conslist ::= tcons */
- 229, /* (367) tconscomma ::= */
- 233, /* (368) defer_subclause_opt ::= defer_subclause */
- 235, /* (369) resolvetype ::= raisetype */
- 239, /* (370) selectnowith ::= oneselect */
- 240, /* (371) oneselect ::= values */
- 254, /* (372) sclp ::= selcollist COMMA */
- 255, /* (373) as ::= ID|STRING */
- 264, /* (374) indexed_opt ::= indexed_by */
- 272, /* (375) returning ::= */
- 217, /* (376) expr ::= term */
- 274, /* (377) likeop ::= LIKE_KW|MATCH */
- 278, /* (378) case_operand ::= expr */
- 261, /* (379) exprlist ::= nexprlist */
- 284, /* (380) nmnum ::= plus_num */
- 284, /* (381) nmnum ::= nm */
- 284, /* (382) nmnum ::= ON */
- 284, /* (383) nmnum ::= DELETE */
- 284, /* (384) nmnum ::= DEFAULT */
- 211, /* (385) plus_num ::= INTEGER|FLOAT */
- 289, /* (386) foreach_clause ::= */
- 289, /* (387) foreach_clause ::= FOR EACH ROW */
- 292, /* (388) trnm ::= nm */
- 293, /* (389) tridxby ::= */
- 294, /* (390) database_kw_opt ::= DATABASE */
- 294, /* (391) database_kw_opt ::= */
- 297, /* (392) kwcolumn_opt ::= */
- 297, /* (393) kwcolumn_opt ::= COLUMNKW */
- 299, /* (394) vtabarglist ::= vtabarg */
- 299, /* (395) vtabarglist ::= vtabarglist COMMA vtabarg */
- 300, /* (396) vtabarg ::= vtabarg vtabargtoken */
- 303, /* (397) anylist ::= */
- 303, /* (398) anylist ::= anylist LP anylist RP */
- 303, /* (399) anylist ::= anylist ANY */
- 266, /* (400) with ::= */
- 306, /* (401) windowdefn_list ::= windowdefn */
- 308, /* (402) window ::= frame_opt */
+ 217, /* (188) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */
+ 217, /* (189) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
+ 217, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
+ 217, /* (191) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */
+ 217, /* (192) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
+ 216, /* (193) term ::= CTIME_KW */
+ 217, /* (194) expr ::= LP nexprlist COMMA expr RP */
+ 217, /* (195) expr ::= expr AND expr */
+ 217, /* (196) expr ::= expr OR expr */
+ 217, /* (197) expr ::= expr LT|GT|GE|LE expr */
+ 217, /* (198) expr ::= expr EQ|NE expr */
+ 217, /* (199) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
+ 217, /* (200) expr ::= expr PLUS|MINUS expr */
+ 217, /* (201) expr ::= expr STAR|SLASH|REM expr */
+ 217, /* (202) expr ::= expr CONCAT expr */
+ 274, /* (203) likeop ::= NOT LIKE_KW|MATCH */
+ 217, /* (204) expr ::= expr likeop expr */
+ 217, /* (205) expr ::= expr likeop expr ESCAPE expr */
+ 217, /* (206) expr ::= expr ISNULL|NOTNULL */
+ 217, /* (207) expr ::= expr NOT NULL */
+ 217, /* (208) expr ::= expr IS expr */
+ 217, /* (209) expr ::= expr IS NOT expr */
+ 217, /* (210) expr ::= expr IS NOT DISTINCT FROM expr */
+ 217, /* (211) expr ::= expr IS DISTINCT FROM expr */
+ 217, /* (212) expr ::= NOT expr */
+ 217, /* (213) expr ::= BITNOT expr */
+ 217, /* (214) expr ::= PLUS|MINUS expr */
+ 217, /* (215) expr ::= expr PTR expr */
+ 275, /* (216) between_op ::= BETWEEN */
+ 275, /* (217) between_op ::= NOT BETWEEN */
+ 217, /* (218) expr ::= expr between_op expr AND expr */
+ 276, /* (219) in_op ::= IN */
+ 276, /* (220) in_op ::= NOT IN */
+ 217, /* (221) expr ::= expr in_op LP exprlist RP */
+ 217, /* (222) expr ::= LP select RP */
+ 217, /* (223) expr ::= expr in_op LP select RP */
+ 217, /* (224) expr ::= expr in_op nm dbnm paren_exprlist */
+ 217, /* (225) expr ::= EXISTS LP select RP */
+ 217, /* (226) expr ::= CASE case_operand case_exprlist case_else END */
+ 279, /* (227) case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ 279, /* (228) case_exprlist ::= WHEN expr THEN expr */
+ 280, /* (229) case_else ::= ELSE expr */
+ 280, /* (230) case_else ::= */
+ 278, /* (231) case_operand ::= */
+ 261, /* (232) exprlist ::= */
+ 253, /* (233) nexprlist ::= nexprlist COMMA expr */
+ 253, /* (234) nexprlist ::= expr */
+ 277, /* (235) paren_exprlist ::= */
+ 277, /* (236) paren_exprlist ::= LP exprlist RP */
+ 190, /* (237) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ 281, /* (238) uniqueflag ::= UNIQUE */
+ 281, /* (239) uniqueflag ::= */
+ 221, /* (240) eidlist_opt ::= */
+ 221, /* (241) eidlist_opt ::= LP eidlist RP */
+ 232, /* (242) eidlist ::= eidlist COMMA nm collate sortorder */
+ 232, /* (243) eidlist ::= nm collate sortorder */
+ 282, /* (244) collate ::= */
+ 282, /* (245) collate ::= COLLATE ID|STRING */
+ 190, /* (246) cmd ::= DROP INDEX ifexists fullname */
+ 190, /* (247) cmd ::= VACUUM vinto */
+ 190, /* (248) cmd ::= VACUUM nm vinto */
+ 283, /* (249) vinto ::= INTO expr */
+ 283, /* (250) vinto ::= */
+ 190, /* (251) cmd ::= PRAGMA nm dbnm */
+ 190, /* (252) cmd ::= PRAGMA nm dbnm EQ nmnum */
+ 190, /* (253) cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ 190, /* (254) cmd ::= PRAGMA nm dbnm EQ minus_num */
+ 190, /* (255) cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ 211, /* (256) plus_num ::= PLUS INTEGER|FLOAT */
+ 212, /* (257) minus_num ::= MINUS INTEGER|FLOAT */
+ 190, /* (258) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ 285, /* (259) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ 287, /* (260) trigger_time ::= BEFORE|AFTER */
+ 287, /* (261) trigger_time ::= INSTEAD OF */
+ 287, /* (262) trigger_time ::= */
+ 288, /* (263) trigger_event ::= DELETE|INSERT */
+ 288, /* (264) trigger_event ::= UPDATE */
+ 288, /* (265) trigger_event ::= UPDATE OF idlist */
+ 290, /* (266) when_clause ::= */
+ 290, /* (267) when_clause ::= WHEN expr */
+ 286, /* (268) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ 286, /* (269) trigger_cmd_list ::= trigger_cmd SEMI */
+ 292, /* (270) trnm ::= nm DOT nm */
+ 293, /* (271) tridxby ::= INDEXED BY nm */
+ 293, /* (272) tridxby ::= NOT INDEXED */
+ 291, /* (273) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
+ 291, /* (274) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+ 291, /* (275) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
+ 291, /* (276) trigger_cmd ::= scanpt select scanpt */
+ 217, /* (277) expr ::= RAISE LP IGNORE RP */
+ 217, /* (278) expr ::= RAISE LP raisetype COMMA nm RP */
+ 236, /* (279) raisetype ::= ROLLBACK */
+ 236, /* (280) raisetype ::= ABORT */
+ 236, /* (281) raisetype ::= FAIL */
+ 190, /* (282) cmd ::= DROP TRIGGER ifexists fullname */
+ 190, /* (283) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ 190, /* (284) cmd ::= DETACH database_kw_opt expr */
+ 295, /* (285) key_opt ::= */
+ 295, /* (286) key_opt ::= KEY expr */
+ 190, /* (287) cmd ::= REINDEX */
+ 190, /* (288) cmd ::= REINDEX nm dbnm */
+ 190, /* (289) cmd ::= ANALYZE */
+ 190, /* (290) cmd ::= ANALYZE nm dbnm */
+ 190, /* (291) cmd ::= ALTER TABLE fullname RENAME TO nm */
+ 190, /* (292) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ 190, /* (293) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
+ 296, /* (294) add_column_fullname ::= fullname */
+ 190, /* (295) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+ 190, /* (296) cmd ::= create_vtab */
+ 190, /* (297) cmd ::= create_vtab LP vtabarglist RP */
+ 298, /* (298) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ 300, /* (299) vtabarg ::= */
+ 301, /* (300) vtabargtoken ::= ANY */
+ 301, /* (301) vtabargtoken ::= lp anylist RP */
+ 302, /* (302) lp ::= LP */
+ 266, /* (303) with ::= WITH wqlist */
+ 266, /* (304) with ::= WITH RECURSIVE wqlist */
+ 305, /* (305) wqas ::= AS */
+ 305, /* (306) wqas ::= AS MATERIALIZED */
+ 305, /* (307) wqas ::= AS NOT MATERIALIZED */
+ 304, /* (308) wqitem ::= nm eidlist_opt wqas LP select RP */
+ 241, /* (309) wqlist ::= wqitem */
+ 241, /* (310) wqlist ::= wqlist COMMA wqitem */
+ 306, /* (311) windowdefn_list ::= windowdefn_list COMMA windowdefn */
+ 307, /* (312) windowdefn ::= nm AS LP window RP */
+ 308, /* (313) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+ 308, /* (314) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+ 308, /* (315) window ::= ORDER BY sortlist frame_opt */
+ 308, /* (316) window ::= nm ORDER BY sortlist frame_opt */
+ 308, /* (317) window ::= nm frame_opt */
+ 309, /* (318) frame_opt ::= */
+ 309, /* (319) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+ 309, /* (320) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+ 313, /* (321) range_or_rows ::= RANGE|ROWS|GROUPS */
+ 315, /* (322) frame_bound_s ::= frame_bound */
+ 315, /* (323) frame_bound_s ::= UNBOUNDED PRECEDING */
+ 316, /* (324) frame_bound_e ::= frame_bound */
+ 316, /* (325) frame_bound_e ::= UNBOUNDED FOLLOWING */
+ 314, /* (326) frame_bound ::= expr PRECEDING|FOLLOWING */
+ 314, /* (327) frame_bound ::= CURRENT ROW */
+ 317, /* (328) frame_exclude_opt ::= */
+ 317, /* (329) frame_exclude_opt ::= EXCLUDE frame_exclude */
+ 318, /* (330) frame_exclude ::= NO OTHERS */
+ 318, /* (331) frame_exclude ::= CURRENT ROW */
+ 318, /* (332) frame_exclude ::= GROUP|TIES */
+ 251, /* (333) window_clause ::= WINDOW windowdefn_list */
+ 273, /* (334) filter_over ::= filter_clause over_clause */
+ 273, /* (335) filter_over ::= over_clause */
+ 273, /* (336) filter_over ::= filter_clause */
+ 312, /* (337) over_clause ::= OVER LP window RP */
+ 312, /* (338) over_clause ::= OVER nm */
+ 311, /* (339) filter_clause ::= FILTER LP WHERE expr RP */
+ 185, /* (340) input ::= cmdlist */
+ 186, /* (341) cmdlist ::= cmdlist ecmd */
+ 186, /* (342) cmdlist ::= ecmd */
+ 187, /* (343) ecmd ::= SEMI */
+ 187, /* (344) ecmd ::= cmdx SEMI */
+ 187, /* (345) ecmd ::= explain cmdx SEMI */
+ 192, /* (346) trans_opt ::= */
+ 192, /* (347) trans_opt ::= TRANSACTION */
+ 192, /* (348) trans_opt ::= TRANSACTION nm */
+ 194, /* (349) savepoint_opt ::= SAVEPOINT */
+ 194, /* (350) savepoint_opt ::= */
+ 190, /* (351) cmd ::= create_table create_table_args */
+ 203, /* (352) table_option_set ::= table_option */
+ 201, /* (353) columnlist ::= columnlist COMMA columnname carglist */
+ 201, /* (354) columnlist ::= columnname carglist */
+ 193, /* (355) nm ::= ID|INDEXED|JOIN_KW */
+ 193, /* (356) nm ::= STRING */
+ 208, /* (357) typetoken ::= typename */
+ 209, /* (358) typename ::= ID|STRING */
+ 210, /* (359) signed ::= plus_num */
+ 210, /* (360) signed ::= minus_num */
+ 207, /* (361) carglist ::= carglist ccons */
+ 207, /* (362) carglist ::= */
+ 215, /* (363) ccons ::= NULL onconf */
+ 215, /* (364) ccons ::= GENERATED ALWAYS AS generated */
+ 215, /* (365) ccons ::= AS generated */
+ 202, /* (366) conslist_opt ::= COMMA conslist */
+ 228, /* (367) conslist ::= conslist tconscomma tcons */
+ 228, /* (368) conslist ::= tcons */
+ 229, /* (369) tconscomma ::= */
+ 233, /* (370) defer_subclause_opt ::= defer_subclause */
+ 235, /* (371) resolvetype ::= raisetype */
+ 239, /* (372) selectnowith ::= oneselect */
+ 240, /* (373) oneselect ::= values */
+ 254, /* (374) sclp ::= selcollist COMMA */
+ 255, /* (375) as ::= ID|STRING */
+ 264, /* (376) indexed_opt ::= indexed_by */
+ 272, /* (377) returning ::= */
+ 217, /* (378) expr ::= term */
+ 274, /* (379) likeop ::= LIKE_KW|MATCH */
+ 278, /* (380) case_operand ::= expr */
+ 261, /* (381) exprlist ::= nexprlist */
+ 284, /* (382) nmnum ::= plus_num */
+ 284, /* (383) nmnum ::= nm */
+ 284, /* (384) nmnum ::= ON */
+ 284, /* (385) nmnum ::= DELETE */
+ 284, /* (386) nmnum ::= DEFAULT */
+ 211, /* (387) plus_num ::= INTEGER|FLOAT */
+ 289, /* (388) foreach_clause ::= */
+ 289, /* (389) foreach_clause ::= FOR EACH ROW */
+ 292, /* (390) trnm ::= nm */
+ 293, /* (391) tridxby ::= */
+ 294, /* (392) database_kw_opt ::= DATABASE */
+ 294, /* (393) database_kw_opt ::= */
+ 297, /* (394) kwcolumn_opt ::= */
+ 297, /* (395) kwcolumn_opt ::= COLUMNKW */
+ 299, /* (396) vtabarglist ::= vtabarg */
+ 299, /* (397) vtabarglist ::= vtabarglist COMMA vtabarg */
+ 300, /* (398) vtabarg ::= vtabarg vtabargtoken */
+ 303, /* (399) anylist ::= */
+ 303, /* (400) anylist ::= anylist LP anylist RP */
+ 303, /* (401) anylist ::= anylist ANY */
+ 266, /* (402) with ::= */
+ 306, /* (403) windowdefn_list ::= windowdefn */
+ 308, /* (404) window ::= frame_opt */
};
/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
@@ -172546,221 +174236,223 @@ static const signed char yyRuleInfoNRhs[] = {
-3, /* (185) expr ::= expr COLLATE ID|STRING */
-6, /* (186) expr ::= CAST LP expr AS typetoken RP */
-5, /* (187) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
- -4, /* (188) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
- -6, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
- -5, /* (190) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
- -1, /* (191) term ::= CTIME_KW */
- -5, /* (192) expr ::= LP nexprlist COMMA expr RP */
- -3, /* (193) expr ::= expr AND expr */
- -3, /* (194) expr ::= expr OR expr */
- -3, /* (195) expr ::= expr LT|GT|GE|LE expr */
- -3, /* (196) expr ::= expr EQ|NE expr */
- -3, /* (197) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
- -3, /* (198) expr ::= expr PLUS|MINUS expr */
- -3, /* (199) expr ::= expr STAR|SLASH|REM expr */
- -3, /* (200) expr ::= expr CONCAT expr */
- -2, /* (201) likeop ::= NOT LIKE_KW|MATCH */
- -3, /* (202) expr ::= expr likeop expr */
- -5, /* (203) expr ::= expr likeop expr ESCAPE expr */
- -2, /* (204) expr ::= expr ISNULL|NOTNULL */
- -3, /* (205) expr ::= expr NOT NULL */
- -3, /* (206) expr ::= expr IS expr */
- -4, /* (207) expr ::= expr IS NOT expr */
- -6, /* (208) expr ::= expr IS NOT DISTINCT FROM expr */
- -5, /* (209) expr ::= expr IS DISTINCT FROM expr */
- -2, /* (210) expr ::= NOT expr */
- -2, /* (211) expr ::= BITNOT expr */
- -2, /* (212) expr ::= PLUS|MINUS expr */
- -3, /* (213) expr ::= expr PTR expr */
- -1, /* (214) between_op ::= BETWEEN */
- -2, /* (215) between_op ::= NOT BETWEEN */
- -5, /* (216) expr ::= expr between_op expr AND expr */
- -1, /* (217) in_op ::= IN */
- -2, /* (218) in_op ::= NOT IN */
- -5, /* (219) expr ::= expr in_op LP exprlist RP */
- -3, /* (220) expr ::= LP select RP */
- -5, /* (221) expr ::= expr in_op LP select RP */
- -5, /* (222) expr ::= expr in_op nm dbnm paren_exprlist */
- -4, /* (223) expr ::= EXISTS LP select RP */
- -5, /* (224) expr ::= CASE case_operand case_exprlist case_else END */
- -5, /* (225) case_exprlist ::= case_exprlist WHEN expr THEN expr */
- -4, /* (226) case_exprlist ::= WHEN expr THEN expr */
- -2, /* (227) case_else ::= ELSE expr */
- 0, /* (228) case_else ::= */
- 0, /* (229) case_operand ::= */
- 0, /* (230) exprlist ::= */
- -3, /* (231) nexprlist ::= nexprlist COMMA expr */
- -1, /* (232) nexprlist ::= expr */
- 0, /* (233) paren_exprlist ::= */
- -3, /* (234) paren_exprlist ::= LP exprlist RP */
- -12, /* (235) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
- -1, /* (236) uniqueflag ::= UNIQUE */
- 0, /* (237) uniqueflag ::= */
- 0, /* (238) eidlist_opt ::= */
- -3, /* (239) eidlist_opt ::= LP eidlist RP */
- -5, /* (240) eidlist ::= eidlist COMMA nm collate sortorder */
- -3, /* (241) eidlist ::= nm collate sortorder */
- 0, /* (242) collate ::= */
- -2, /* (243) collate ::= COLLATE ID|STRING */
- -4, /* (244) cmd ::= DROP INDEX ifexists fullname */
- -2, /* (245) cmd ::= VACUUM vinto */
- -3, /* (246) cmd ::= VACUUM nm vinto */
- -2, /* (247) vinto ::= INTO expr */
- 0, /* (248) vinto ::= */
- -3, /* (249) cmd ::= PRAGMA nm dbnm */
- -5, /* (250) cmd ::= PRAGMA nm dbnm EQ nmnum */
- -6, /* (251) cmd ::= PRAGMA nm dbnm LP nmnum RP */
- -5, /* (252) cmd ::= PRAGMA nm dbnm EQ minus_num */
- -6, /* (253) cmd ::= PRAGMA nm dbnm LP minus_num RP */
- -2, /* (254) plus_num ::= PLUS INTEGER|FLOAT */
- -2, /* (255) minus_num ::= MINUS INTEGER|FLOAT */
- -5, /* (256) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
- -11, /* (257) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
- -1, /* (258) trigger_time ::= BEFORE|AFTER */
- -2, /* (259) trigger_time ::= INSTEAD OF */
- 0, /* (260) trigger_time ::= */
- -1, /* (261) trigger_event ::= DELETE|INSERT */
- -1, /* (262) trigger_event ::= UPDATE */
- -3, /* (263) trigger_event ::= UPDATE OF idlist */
- 0, /* (264) when_clause ::= */
- -2, /* (265) when_clause ::= WHEN expr */
- -3, /* (266) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
- -2, /* (267) trigger_cmd_list ::= trigger_cmd SEMI */
- -3, /* (268) trnm ::= nm DOT nm */
- -3, /* (269) tridxby ::= INDEXED BY nm */
- -2, /* (270) tridxby ::= NOT INDEXED */
- -9, /* (271) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
- -8, /* (272) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
- -6, /* (273) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
- -3, /* (274) trigger_cmd ::= scanpt select scanpt */
- -4, /* (275) expr ::= RAISE LP IGNORE RP */
- -6, /* (276) expr ::= RAISE LP raisetype COMMA nm RP */
- -1, /* (277) raisetype ::= ROLLBACK */
- -1, /* (278) raisetype ::= ABORT */
- -1, /* (279) raisetype ::= FAIL */
- -4, /* (280) cmd ::= DROP TRIGGER ifexists fullname */
- -6, /* (281) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
- -3, /* (282) cmd ::= DETACH database_kw_opt expr */
- 0, /* (283) key_opt ::= */
- -2, /* (284) key_opt ::= KEY expr */
- -1, /* (285) cmd ::= REINDEX */
- -3, /* (286) cmd ::= REINDEX nm dbnm */
- -1, /* (287) cmd ::= ANALYZE */
- -3, /* (288) cmd ::= ANALYZE nm dbnm */
- -6, /* (289) cmd ::= ALTER TABLE fullname RENAME TO nm */
- -7, /* (290) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
- -6, /* (291) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
- -1, /* (292) add_column_fullname ::= fullname */
- -8, /* (293) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
- -1, /* (294) cmd ::= create_vtab */
- -4, /* (295) cmd ::= create_vtab LP vtabarglist RP */
- -8, /* (296) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
- 0, /* (297) vtabarg ::= */
- -1, /* (298) vtabargtoken ::= ANY */
- -3, /* (299) vtabargtoken ::= lp anylist RP */
- -1, /* (300) lp ::= LP */
- -2, /* (301) with ::= WITH wqlist */
- -3, /* (302) with ::= WITH RECURSIVE wqlist */
- -1, /* (303) wqas ::= AS */
- -2, /* (304) wqas ::= AS MATERIALIZED */
- -3, /* (305) wqas ::= AS NOT MATERIALIZED */
- -6, /* (306) wqitem ::= nm eidlist_opt wqas LP select RP */
- -1, /* (307) wqlist ::= wqitem */
- -3, /* (308) wqlist ::= wqlist COMMA wqitem */
- -3, /* (309) windowdefn_list ::= windowdefn_list COMMA windowdefn */
- -5, /* (310) windowdefn ::= nm AS LP window RP */
- -5, /* (311) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
- -6, /* (312) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
- -4, /* (313) window ::= ORDER BY sortlist frame_opt */
- -5, /* (314) window ::= nm ORDER BY sortlist frame_opt */
- -2, /* (315) window ::= nm frame_opt */
- 0, /* (316) frame_opt ::= */
- -3, /* (317) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
- -6, /* (318) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
- -1, /* (319) range_or_rows ::= RANGE|ROWS|GROUPS */
- -1, /* (320) frame_bound_s ::= frame_bound */
- -2, /* (321) frame_bound_s ::= UNBOUNDED PRECEDING */
- -1, /* (322) frame_bound_e ::= frame_bound */
- -2, /* (323) frame_bound_e ::= UNBOUNDED FOLLOWING */
- -2, /* (324) frame_bound ::= expr PRECEDING|FOLLOWING */
- -2, /* (325) frame_bound ::= CURRENT ROW */
- 0, /* (326) frame_exclude_opt ::= */
- -2, /* (327) frame_exclude_opt ::= EXCLUDE frame_exclude */
- -2, /* (328) frame_exclude ::= NO OTHERS */
- -2, /* (329) frame_exclude ::= CURRENT ROW */
- -1, /* (330) frame_exclude ::= GROUP|TIES */
- -2, /* (331) window_clause ::= WINDOW windowdefn_list */
- -2, /* (332) filter_over ::= filter_clause over_clause */
- -1, /* (333) filter_over ::= over_clause */
- -1, /* (334) filter_over ::= filter_clause */
- -4, /* (335) over_clause ::= OVER LP window RP */
- -2, /* (336) over_clause ::= OVER nm */
- -5, /* (337) filter_clause ::= FILTER LP WHERE expr RP */
- -1, /* (338) input ::= cmdlist */
- -2, /* (339) cmdlist ::= cmdlist ecmd */
- -1, /* (340) cmdlist ::= ecmd */
- -1, /* (341) ecmd ::= SEMI */
- -2, /* (342) ecmd ::= cmdx SEMI */
- -3, /* (343) ecmd ::= explain cmdx SEMI */
- 0, /* (344) trans_opt ::= */
- -1, /* (345) trans_opt ::= TRANSACTION */
- -2, /* (346) trans_opt ::= TRANSACTION nm */
- -1, /* (347) savepoint_opt ::= SAVEPOINT */
- 0, /* (348) savepoint_opt ::= */
- -2, /* (349) cmd ::= create_table create_table_args */
- -1, /* (350) table_option_set ::= table_option */
- -4, /* (351) columnlist ::= columnlist COMMA columnname carglist */
- -2, /* (352) columnlist ::= columnname carglist */
- -1, /* (353) nm ::= ID|INDEXED|JOIN_KW */
- -1, /* (354) nm ::= STRING */
- -1, /* (355) typetoken ::= typename */
- -1, /* (356) typename ::= ID|STRING */
- -1, /* (357) signed ::= plus_num */
- -1, /* (358) signed ::= minus_num */
- -2, /* (359) carglist ::= carglist ccons */
- 0, /* (360) carglist ::= */
- -2, /* (361) ccons ::= NULL onconf */
- -4, /* (362) ccons ::= GENERATED ALWAYS AS generated */
- -2, /* (363) ccons ::= AS generated */
- -2, /* (364) conslist_opt ::= COMMA conslist */
- -3, /* (365) conslist ::= conslist tconscomma tcons */
- -1, /* (366) conslist ::= tcons */
- 0, /* (367) tconscomma ::= */
- -1, /* (368) defer_subclause_opt ::= defer_subclause */
- -1, /* (369) resolvetype ::= raisetype */
- -1, /* (370) selectnowith ::= oneselect */
- -1, /* (371) oneselect ::= values */
- -2, /* (372) sclp ::= selcollist COMMA */
- -1, /* (373) as ::= ID|STRING */
- -1, /* (374) indexed_opt ::= indexed_by */
- 0, /* (375) returning ::= */
- -1, /* (376) expr ::= term */
- -1, /* (377) likeop ::= LIKE_KW|MATCH */
- -1, /* (378) case_operand ::= expr */
- -1, /* (379) exprlist ::= nexprlist */
- -1, /* (380) nmnum ::= plus_num */
- -1, /* (381) nmnum ::= nm */
- -1, /* (382) nmnum ::= ON */
- -1, /* (383) nmnum ::= DELETE */
- -1, /* (384) nmnum ::= DEFAULT */
- -1, /* (385) plus_num ::= INTEGER|FLOAT */
- 0, /* (386) foreach_clause ::= */
- -3, /* (387) foreach_clause ::= FOR EACH ROW */
- -1, /* (388) trnm ::= nm */
- 0, /* (389) tridxby ::= */
- -1, /* (390) database_kw_opt ::= DATABASE */
- 0, /* (391) database_kw_opt ::= */
- 0, /* (392) kwcolumn_opt ::= */
- -1, /* (393) kwcolumn_opt ::= COLUMNKW */
- -1, /* (394) vtabarglist ::= vtabarg */
- -3, /* (395) vtabarglist ::= vtabarglist COMMA vtabarg */
- -2, /* (396) vtabarg ::= vtabarg vtabargtoken */
- 0, /* (397) anylist ::= */
- -4, /* (398) anylist ::= anylist LP anylist RP */
- -2, /* (399) anylist ::= anylist ANY */
- 0, /* (400) with ::= */
- -1, /* (401) windowdefn_list ::= windowdefn */
- -1, /* (402) window ::= frame_opt */
+ -8, /* (188) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */
+ -4, /* (189) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
+ -6, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
+ -9, /* (191) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */
+ -5, /* (192) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
+ -1, /* (193) term ::= CTIME_KW */
+ -5, /* (194) expr ::= LP nexprlist COMMA expr RP */
+ -3, /* (195) expr ::= expr AND expr */
+ -3, /* (196) expr ::= expr OR expr */
+ -3, /* (197) expr ::= expr LT|GT|GE|LE expr */
+ -3, /* (198) expr ::= expr EQ|NE expr */
+ -3, /* (199) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
+ -3, /* (200) expr ::= expr PLUS|MINUS expr */
+ -3, /* (201) expr ::= expr STAR|SLASH|REM expr */
+ -3, /* (202) expr ::= expr CONCAT expr */
+ -2, /* (203) likeop ::= NOT LIKE_KW|MATCH */
+ -3, /* (204) expr ::= expr likeop expr */
+ -5, /* (205) expr ::= expr likeop expr ESCAPE expr */
+ -2, /* (206) expr ::= expr ISNULL|NOTNULL */
+ -3, /* (207) expr ::= expr NOT NULL */
+ -3, /* (208) expr ::= expr IS expr */
+ -4, /* (209) expr ::= expr IS NOT expr */
+ -6, /* (210) expr ::= expr IS NOT DISTINCT FROM expr */
+ -5, /* (211) expr ::= expr IS DISTINCT FROM expr */
+ -2, /* (212) expr ::= NOT expr */
+ -2, /* (213) expr ::= BITNOT expr */
+ -2, /* (214) expr ::= PLUS|MINUS expr */
+ -3, /* (215) expr ::= expr PTR expr */
+ -1, /* (216) between_op ::= BETWEEN */
+ -2, /* (217) between_op ::= NOT BETWEEN */
+ -5, /* (218) expr ::= expr between_op expr AND expr */
+ -1, /* (219) in_op ::= IN */
+ -2, /* (220) in_op ::= NOT IN */
+ -5, /* (221) expr ::= expr in_op LP exprlist RP */
+ -3, /* (222) expr ::= LP select RP */
+ -5, /* (223) expr ::= expr in_op LP select RP */
+ -5, /* (224) expr ::= expr in_op nm dbnm paren_exprlist */
+ -4, /* (225) expr ::= EXISTS LP select RP */
+ -5, /* (226) expr ::= CASE case_operand case_exprlist case_else END */
+ -5, /* (227) case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ -4, /* (228) case_exprlist ::= WHEN expr THEN expr */
+ -2, /* (229) case_else ::= ELSE expr */
+ 0, /* (230) case_else ::= */
+ 0, /* (231) case_operand ::= */
+ 0, /* (232) exprlist ::= */
+ -3, /* (233) nexprlist ::= nexprlist COMMA expr */
+ -1, /* (234) nexprlist ::= expr */
+ 0, /* (235) paren_exprlist ::= */
+ -3, /* (236) paren_exprlist ::= LP exprlist RP */
+ -12, /* (237) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ -1, /* (238) uniqueflag ::= UNIQUE */
+ 0, /* (239) uniqueflag ::= */
+ 0, /* (240) eidlist_opt ::= */
+ -3, /* (241) eidlist_opt ::= LP eidlist RP */
+ -5, /* (242) eidlist ::= eidlist COMMA nm collate sortorder */
+ -3, /* (243) eidlist ::= nm collate sortorder */
+ 0, /* (244) collate ::= */
+ -2, /* (245) collate ::= COLLATE ID|STRING */
+ -4, /* (246) cmd ::= DROP INDEX ifexists fullname */
+ -2, /* (247) cmd ::= VACUUM vinto */
+ -3, /* (248) cmd ::= VACUUM nm vinto */
+ -2, /* (249) vinto ::= INTO expr */
+ 0, /* (250) vinto ::= */
+ -3, /* (251) cmd ::= PRAGMA nm dbnm */
+ -5, /* (252) cmd ::= PRAGMA nm dbnm EQ nmnum */
+ -6, /* (253) cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ -5, /* (254) cmd ::= PRAGMA nm dbnm EQ minus_num */
+ -6, /* (255) cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ -2, /* (256) plus_num ::= PLUS INTEGER|FLOAT */
+ -2, /* (257) minus_num ::= MINUS INTEGER|FLOAT */
+ -5, /* (258) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ -11, /* (259) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ -1, /* (260) trigger_time ::= BEFORE|AFTER */
+ -2, /* (261) trigger_time ::= INSTEAD OF */
+ 0, /* (262) trigger_time ::= */
+ -1, /* (263) trigger_event ::= DELETE|INSERT */
+ -1, /* (264) trigger_event ::= UPDATE */
+ -3, /* (265) trigger_event ::= UPDATE OF idlist */
+ 0, /* (266) when_clause ::= */
+ -2, /* (267) when_clause ::= WHEN expr */
+ -3, /* (268) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ -2, /* (269) trigger_cmd_list ::= trigger_cmd SEMI */
+ -3, /* (270) trnm ::= nm DOT nm */
+ -3, /* (271) tridxby ::= INDEXED BY nm */
+ -2, /* (272) tridxby ::= NOT INDEXED */
+ -9, /* (273) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
+ -8, /* (274) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+ -6, /* (275) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
+ -3, /* (276) trigger_cmd ::= scanpt select scanpt */
+ -4, /* (277) expr ::= RAISE LP IGNORE RP */
+ -6, /* (278) expr ::= RAISE LP raisetype COMMA nm RP */
+ -1, /* (279) raisetype ::= ROLLBACK */
+ -1, /* (280) raisetype ::= ABORT */
+ -1, /* (281) raisetype ::= FAIL */
+ -4, /* (282) cmd ::= DROP TRIGGER ifexists fullname */
+ -6, /* (283) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ -3, /* (284) cmd ::= DETACH database_kw_opt expr */
+ 0, /* (285) key_opt ::= */
+ -2, /* (286) key_opt ::= KEY expr */
+ -1, /* (287) cmd ::= REINDEX */
+ -3, /* (288) cmd ::= REINDEX nm dbnm */
+ -1, /* (289) cmd ::= ANALYZE */
+ -3, /* (290) cmd ::= ANALYZE nm dbnm */
+ -6, /* (291) cmd ::= ALTER TABLE fullname RENAME TO nm */
+ -7, /* (292) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ -6, /* (293) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
+ -1, /* (294) add_column_fullname ::= fullname */
+ -8, /* (295) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+ -1, /* (296) cmd ::= create_vtab */
+ -4, /* (297) cmd ::= create_vtab LP vtabarglist RP */
+ -8, /* (298) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ 0, /* (299) vtabarg ::= */
+ -1, /* (300) vtabargtoken ::= ANY */
+ -3, /* (301) vtabargtoken ::= lp anylist RP */
+ -1, /* (302) lp ::= LP */
+ -2, /* (303) with ::= WITH wqlist */
+ -3, /* (304) with ::= WITH RECURSIVE wqlist */
+ -1, /* (305) wqas ::= AS */
+ -2, /* (306) wqas ::= AS MATERIALIZED */
+ -3, /* (307) wqas ::= AS NOT MATERIALIZED */
+ -6, /* (308) wqitem ::= nm eidlist_opt wqas LP select RP */
+ -1, /* (309) wqlist ::= wqitem */
+ -3, /* (310) wqlist ::= wqlist COMMA wqitem */
+ -3, /* (311) windowdefn_list ::= windowdefn_list COMMA windowdefn */
+ -5, /* (312) windowdefn ::= nm AS LP window RP */
+ -5, /* (313) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+ -6, /* (314) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+ -4, /* (315) window ::= ORDER BY sortlist frame_opt */
+ -5, /* (316) window ::= nm ORDER BY sortlist frame_opt */
+ -2, /* (317) window ::= nm frame_opt */
+ 0, /* (318) frame_opt ::= */
+ -3, /* (319) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+ -6, /* (320) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+ -1, /* (321) range_or_rows ::= RANGE|ROWS|GROUPS */
+ -1, /* (322) frame_bound_s ::= frame_bound */
+ -2, /* (323) frame_bound_s ::= UNBOUNDED PRECEDING */
+ -1, /* (324) frame_bound_e ::= frame_bound */
+ -2, /* (325) frame_bound_e ::= UNBOUNDED FOLLOWING */
+ -2, /* (326) frame_bound ::= expr PRECEDING|FOLLOWING */
+ -2, /* (327) frame_bound ::= CURRENT ROW */
+ 0, /* (328) frame_exclude_opt ::= */
+ -2, /* (329) frame_exclude_opt ::= EXCLUDE frame_exclude */
+ -2, /* (330) frame_exclude ::= NO OTHERS */
+ -2, /* (331) frame_exclude ::= CURRENT ROW */
+ -1, /* (332) frame_exclude ::= GROUP|TIES */
+ -2, /* (333) window_clause ::= WINDOW windowdefn_list */
+ -2, /* (334) filter_over ::= filter_clause over_clause */
+ -1, /* (335) filter_over ::= over_clause */
+ -1, /* (336) filter_over ::= filter_clause */
+ -4, /* (337) over_clause ::= OVER LP window RP */
+ -2, /* (338) over_clause ::= OVER nm */
+ -5, /* (339) filter_clause ::= FILTER LP WHERE expr RP */
+ -1, /* (340) input ::= cmdlist */
+ -2, /* (341) cmdlist ::= cmdlist ecmd */
+ -1, /* (342) cmdlist ::= ecmd */
+ -1, /* (343) ecmd ::= SEMI */
+ -2, /* (344) ecmd ::= cmdx SEMI */
+ -3, /* (345) ecmd ::= explain cmdx SEMI */
+ 0, /* (346) trans_opt ::= */
+ -1, /* (347) trans_opt ::= TRANSACTION */
+ -2, /* (348) trans_opt ::= TRANSACTION nm */
+ -1, /* (349) savepoint_opt ::= SAVEPOINT */
+ 0, /* (350) savepoint_opt ::= */
+ -2, /* (351) cmd ::= create_table create_table_args */
+ -1, /* (352) table_option_set ::= table_option */
+ -4, /* (353) columnlist ::= columnlist COMMA columnname carglist */
+ -2, /* (354) columnlist ::= columnname carglist */
+ -1, /* (355) nm ::= ID|INDEXED|JOIN_KW */
+ -1, /* (356) nm ::= STRING */
+ -1, /* (357) typetoken ::= typename */
+ -1, /* (358) typename ::= ID|STRING */
+ -1, /* (359) signed ::= plus_num */
+ -1, /* (360) signed ::= minus_num */
+ -2, /* (361) carglist ::= carglist ccons */
+ 0, /* (362) carglist ::= */
+ -2, /* (363) ccons ::= NULL onconf */
+ -4, /* (364) ccons ::= GENERATED ALWAYS AS generated */
+ -2, /* (365) ccons ::= AS generated */
+ -2, /* (366) conslist_opt ::= COMMA conslist */
+ -3, /* (367) conslist ::= conslist tconscomma tcons */
+ -1, /* (368) conslist ::= tcons */
+ 0, /* (369) tconscomma ::= */
+ -1, /* (370) defer_subclause_opt ::= defer_subclause */
+ -1, /* (371) resolvetype ::= raisetype */
+ -1, /* (372) selectnowith ::= oneselect */
+ -1, /* (373) oneselect ::= values */
+ -2, /* (374) sclp ::= selcollist COMMA */
+ -1, /* (375) as ::= ID|STRING */
+ -1, /* (376) indexed_opt ::= indexed_by */
+ 0, /* (377) returning ::= */
+ -1, /* (378) expr ::= term */
+ -1, /* (379) likeop ::= LIKE_KW|MATCH */
+ -1, /* (380) case_operand ::= expr */
+ -1, /* (381) exprlist ::= nexprlist */
+ -1, /* (382) nmnum ::= plus_num */
+ -1, /* (383) nmnum ::= nm */
+ -1, /* (384) nmnum ::= ON */
+ -1, /* (385) nmnum ::= DELETE */
+ -1, /* (386) nmnum ::= DEFAULT */
+ -1, /* (387) plus_num ::= INTEGER|FLOAT */
+ 0, /* (388) foreach_clause ::= */
+ -3, /* (389) foreach_clause ::= FOR EACH ROW */
+ -1, /* (390) trnm ::= nm */
+ 0, /* (391) tridxby ::= */
+ -1, /* (392) database_kw_opt ::= DATABASE */
+ 0, /* (393) database_kw_opt ::= */
+ 0, /* (394) kwcolumn_opt ::= */
+ -1, /* (395) kwcolumn_opt ::= COLUMNKW */
+ -1, /* (396) vtabarglist ::= vtabarg */
+ -3, /* (397) vtabarglist ::= vtabarglist COMMA vtabarg */
+ -2, /* (398) vtabarg ::= vtabarg vtabargtoken */
+ 0, /* (399) anylist ::= */
+ -4, /* (400) anylist ::= anylist LP anylist RP */
+ -2, /* (401) anylist ::= anylist ANY */
+ 0, /* (402) with ::= */
+ -1, /* (403) windowdefn_list ::= windowdefn */
+ -1, /* (404) window ::= frame_opt */
};
static void yy_accept(yyParser*); /* Forward Declaration */
@@ -172820,7 +174512,7 @@ static YYACTIONTYPE yy_reduce(
case 5: /* transtype ::= DEFERRED */
case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6);
case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7);
- case 319: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==319);
+ case 321: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==321);
{yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/}
break;
case 8: /* cmd ::= COMMIT|END trans_opt */
@@ -172857,7 +174549,7 @@ static YYACTIONTYPE yy_reduce(
case 72: /* defer_subclause_opt ::= */ yytestcase(yyruleno==72);
case 81: /* ifexists ::= */ yytestcase(yyruleno==81);
case 98: /* distinct ::= */ yytestcase(yyruleno==98);
- case 242: /* collate ::= */ yytestcase(yyruleno==242);
+ case 244: /* collate ::= */ yytestcase(yyruleno==244);
{yymsp[1].minor.yy394 = 0;}
break;
case 16: /* ifnotexists ::= IF NOT EXISTS */
@@ -173041,9 +174733,9 @@ static YYACTIONTYPE yy_reduce(
break;
case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80);
- case 215: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==215);
- case 218: /* in_op ::= NOT IN */ yytestcase(yyruleno==218);
- case 243: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==243);
+ case 217: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==217);
+ case 220: /* in_op ::= NOT IN */ yytestcase(yyruleno==220);
+ case 245: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==245);
{yymsp[-1].minor.yy394 = 1;}
break;
case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
@@ -173192,9 +174884,9 @@ static YYACTIONTYPE yy_reduce(
case 99: /* sclp ::= */
case 132: /* orderby_opt ::= */ yytestcase(yyruleno==132);
case 142: /* groupby_opt ::= */ yytestcase(yyruleno==142);
- case 230: /* exprlist ::= */ yytestcase(yyruleno==230);
- case 233: /* paren_exprlist ::= */ yytestcase(yyruleno==233);
- case 238: /* eidlist_opt ::= */ yytestcase(yyruleno==238);
+ case 232: /* exprlist ::= */ yytestcase(yyruleno==232);
+ case 235: /* paren_exprlist ::= */ yytestcase(yyruleno==235);
+ case 240: /* eidlist_opt ::= */ yytestcase(yyruleno==240);
{yymsp[1].minor.yy322 = 0;}
break;
case 100: /* selcollist ::= sclp scanpt expr scanpt as */
@@ -173223,8 +174915,8 @@ static YYACTIONTYPE yy_reduce(
break;
case 103: /* as ::= AS nm */
case 115: /* dbnm ::= DOT nm */ yytestcase(yyruleno==115);
- case 254: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==254);
- case 255: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==255);
+ case 256: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==256);
+ case 257: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==257);
{yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;}
break;
case 105: /* from ::= */
@@ -173396,16 +175088,16 @@ static YYACTIONTYPE yy_reduce(
case 146: /* limit_opt ::= */ yytestcase(yyruleno==146);
case 151: /* where_opt ::= */ yytestcase(yyruleno==151);
case 153: /* where_opt_ret ::= */ yytestcase(yyruleno==153);
- case 228: /* case_else ::= */ yytestcase(yyruleno==228);
- case 229: /* case_operand ::= */ yytestcase(yyruleno==229);
- case 248: /* vinto ::= */ yytestcase(yyruleno==248);
+ case 230: /* case_else ::= */ yytestcase(yyruleno==230);
+ case 231: /* case_operand ::= */ yytestcase(yyruleno==231);
+ case 250: /* vinto ::= */ yytestcase(yyruleno==250);
{yymsp[1].minor.yy528 = 0;}
break;
case 145: /* having_opt ::= HAVING expr */
case 152: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==152);
case 154: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==154);
- case 227: /* case_else ::= ELSE expr */ yytestcase(yyruleno==227);
- case 247: /* vinto ::= INTO expr */ yytestcase(yyruleno==247);
+ case 229: /* case_else ::= ELSE expr */ yytestcase(yyruleno==229);
+ case 249: /* vinto ::= INTO expr */ yytestcase(yyruleno==249);
{yymsp[-1].minor.yy528 = yymsp[0].minor.yy528;}
break;
case 147: /* limit_opt ::= LIMIT expr */
@@ -173591,33 +175283,48 @@ static YYACTIONTYPE yy_reduce(
}
yymsp[-4].minor.yy528 = yylhsminor.yy528;
break;
- case 188: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
+ case 188: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */
+{
+ yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-4].minor.yy322, &yymsp[-7].minor.yy0, yymsp[-5].minor.yy394);
+ sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy528, yymsp[-1].minor.yy322);
+}
+ yymsp[-7].minor.yy528 = yylhsminor.yy528;
+ break;
+ case 189: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
{
yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
}
yymsp[-3].minor.yy528 = yylhsminor.yy528;
break;
- case 189: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
+ case 190: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
{
yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy322, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy394);
sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41);
}
yymsp[-5].minor.yy528 = yylhsminor.yy528;
break;
- case 190: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
+ case 191: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */
+{
+ yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-5].minor.yy322, &yymsp[-8].minor.yy0, yymsp[-6].minor.yy394);
+ sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41);
+ sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy528, yymsp[-2].minor.yy322);
+}
+ yymsp[-8].minor.yy528 = yylhsminor.yy528;
+ break;
+ case 192: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
{
yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41);
}
yymsp[-4].minor.yy528 = yylhsminor.yy528;
break;
- case 191: /* term ::= CTIME_KW */
+ case 193: /* term ::= CTIME_KW */
{
yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
}
yymsp[0].minor.yy528 = yylhsminor.yy528;
break;
- case 192: /* expr ::= LP nexprlist COMMA expr RP */
+ case 194: /* expr ::= LP nexprlist COMMA expr RP */
{
ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528);
yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
@@ -173631,22 +175338,22 @@ static YYACTIONTYPE yy_reduce(
}
}
break;
- case 193: /* expr ::= expr AND expr */
+ case 195: /* expr ::= expr AND expr */
{yymsp[-2].minor.yy528=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);}
break;
- case 194: /* expr ::= expr OR expr */
- case 195: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==195);
- case 196: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==196);
- case 197: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==197);
- case 198: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==198);
- case 199: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==199);
- case 200: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==200);
+ case 196: /* expr ::= expr OR expr */
+ case 197: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==197);
+ case 198: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==198);
+ case 199: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==199);
+ case 200: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==200);
+ case 201: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==201);
+ case 202: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==202);
{yymsp[-2].minor.yy528=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);}
break;
- case 201: /* likeop ::= NOT LIKE_KW|MATCH */
+ case 203: /* likeop ::= NOT LIKE_KW|MATCH */
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/}
break;
- case 202: /* expr ::= expr likeop expr */
+ case 204: /* expr ::= expr likeop expr */
{
ExprList *pList;
int bNot = yymsp[-1].minor.yy0.n & 0x80000000;
@@ -173658,7 +175365,7 @@ static YYACTIONTYPE yy_reduce(
if( yymsp[-2].minor.yy528 ) yymsp[-2].minor.yy528->flags |= EP_InfixFunc;
}
break;
- case 203: /* expr ::= expr likeop expr ESCAPE expr */
+ case 205: /* expr ::= expr likeop expr ESCAPE expr */
{
ExprList *pList;
int bNot = yymsp[-3].minor.yy0.n & 0x80000000;
@@ -173671,47 +175378,47 @@ static YYACTIONTYPE yy_reduce(
if( yymsp[-4].minor.yy528 ) yymsp[-4].minor.yy528->flags |= EP_InfixFunc;
}
break;
- case 204: /* expr ::= expr ISNULL|NOTNULL */
+ case 206: /* expr ::= expr ISNULL|NOTNULL */
{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy528,0);}
break;
- case 205: /* expr ::= expr NOT NULL */
+ case 207: /* expr ::= expr NOT NULL */
{yymsp[-2].minor.yy528 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy528,0);}
break;
- case 206: /* expr ::= expr IS expr */
+ case 208: /* expr ::= expr IS expr */
{
yymsp[-2].minor.yy528 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);
binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-2].minor.yy528, TK_ISNULL);
}
break;
- case 207: /* expr ::= expr IS NOT expr */
+ case 209: /* expr ::= expr IS NOT expr */
{
yymsp[-3].minor.yy528 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy528,yymsp[0].minor.yy528);
binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-3].minor.yy528, TK_NOTNULL);
}
break;
- case 208: /* expr ::= expr IS NOT DISTINCT FROM expr */
+ case 210: /* expr ::= expr IS NOT DISTINCT FROM expr */
{
yymsp[-5].minor.yy528 = sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy528,yymsp[0].minor.yy528);
binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-5].minor.yy528, TK_ISNULL);
}
break;
- case 209: /* expr ::= expr IS DISTINCT FROM expr */
+ case 211: /* expr ::= expr IS DISTINCT FROM expr */
{
yymsp[-4].minor.yy528 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy528,yymsp[0].minor.yy528);
binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-4].minor.yy528, TK_NOTNULL);
}
break;
- case 210: /* expr ::= NOT expr */
- case 211: /* expr ::= BITNOT expr */ yytestcase(yyruleno==211);
+ case 212: /* expr ::= NOT expr */
+ case 213: /* expr ::= BITNOT expr */ yytestcase(yyruleno==213);
{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy528, 0);/*A-overwrites-B*/}
break;
- case 212: /* expr ::= PLUS|MINUS expr */
+ case 214: /* expr ::= PLUS|MINUS expr */
{
yymsp[-1].minor.yy528 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy528, 0);
/*A-overwrites-B*/
}
break;
- case 213: /* expr ::= expr PTR expr */
+ case 215: /* expr ::= expr PTR expr */
{
ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy528);
pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy528);
@@ -173719,11 +175426,11 @@ static YYACTIONTYPE yy_reduce(
}
yymsp[-2].minor.yy528 = yylhsminor.yy528;
break;
- case 214: /* between_op ::= BETWEEN */
- case 217: /* in_op ::= IN */ yytestcase(yyruleno==217);
+ case 216: /* between_op ::= BETWEEN */
+ case 219: /* in_op ::= IN */ yytestcase(yyruleno==219);
{yymsp[0].minor.yy394 = 0;}
break;
- case 216: /* expr ::= expr between_op expr AND expr */
+ case 218: /* expr ::= expr between_op expr AND expr */
{
ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528);
pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528);
@@ -173736,7 +175443,7 @@ static YYACTIONTYPE yy_reduce(
if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
}
break;
- case 219: /* expr ::= expr in_op LP exprlist RP */
+ case 221: /* expr ::= expr in_op LP exprlist RP */
{
if( yymsp[-1].minor.yy322==0 ){
/* Expressions of the form
@@ -173782,20 +175489,20 @@ static YYACTIONTYPE yy_reduce(
}
}
break;
- case 220: /* expr ::= LP select RP */
+ case 222: /* expr ::= LP select RP */
{
yymsp[-2].minor.yy528 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy528, yymsp[-1].minor.yy47);
}
break;
- case 221: /* expr ::= expr in_op LP select RP */
+ case 223: /* expr ::= expr in_op LP select RP */
{
yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0);
sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, yymsp[-1].minor.yy47);
if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
}
break;
- case 222: /* expr ::= expr in_op nm dbnm paren_exprlist */
+ case 224: /* expr ::= expr in_op nm dbnm paren_exprlist */
{
SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);
Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0);
@@ -173805,14 +175512,14 @@ static YYACTIONTYPE yy_reduce(
if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
}
break;
- case 223: /* expr ::= EXISTS LP select RP */
+ case 225: /* expr ::= EXISTS LP select RP */
{
Expr *p;
p = yymsp[-3].minor.yy528 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy47);
}
break;
- case 224: /* expr ::= CASE case_operand case_exprlist case_else END */
+ case 226: /* expr ::= CASE case_operand case_exprlist case_else END */
{
yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy528, 0);
if( yymsp[-4].minor.yy528 ){
@@ -173824,29 +175531,29 @@ static YYACTIONTYPE yy_reduce(
}
}
break;
- case 225: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ case 227: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
{
yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[-2].minor.yy528);
yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[0].minor.yy528);
}
break;
- case 226: /* case_exprlist ::= WHEN expr THEN expr */
+ case 228: /* case_exprlist ::= WHEN expr THEN expr */
{
yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528);
yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy322, yymsp[0].minor.yy528);
}
break;
- case 231: /* nexprlist ::= nexprlist COMMA expr */
+ case 233: /* nexprlist ::= nexprlist COMMA expr */
{yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[0].minor.yy528);}
break;
- case 232: /* nexprlist ::= expr */
+ case 234: /* nexprlist ::= expr */
{yymsp[0].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy528); /*A-overwrites-Y*/}
break;
- case 234: /* paren_exprlist ::= LP exprlist RP */
- case 239: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==239);
+ case 236: /* paren_exprlist ::= LP exprlist RP */
+ case 241: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==241);
{yymsp[-2].minor.yy322 = yymsp[-1].minor.yy322;}
break;
- case 235: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ case 237: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
{
sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0,
sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy322, yymsp[-10].minor.yy394,
@@ -173856,48 +175563,48 @@ static YYACTIONTYPE yy_reduce(
}
}
break;
- case 236: /* uniqueflag ::= UNIQUE */
- case 278: /* raisetype ::= ABORT */ yytestcase(yyruleno==278);
+ case 238: /* uniqueflag ::= UNIQUE */
+ case 280: /* raisetype ::= ABORT */ yytestcase(yyruleno==280);
{yymsp[0].minor.yy394 = OE_Abort;}
break;
- case 237: /* uniqueflag ::= */
+ case 239: /* uniqueflag ::= */
{yymsp[1].minor.yy394 = OE_None;}
break;
- case 240: /* eidlist ::= eidlist COMMA nm collate sortorder */
+ case 242: /* eidlist ::= eidlist COMMA nm collate sortorder */
{
yymsp[-4].minor.yy322 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394);
}
break;
- case 241: /* eidlist ::= nm collate sortorder */
+ case 243: /* eidlist ::= nm collate sortorder */
{
yymsp[-2].minor.yy322 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); /*A-overwrites-Y*/
}
break;
- case 244: /* cmd ::= DROP INDEX ifexists fullname */
+ case 246: /* cmd ::= DROP INDEX ifexists fullname */
{sqlite3DropIndex(pParse, yymsp[0].minor.yy131, yymsp[-1].minor.yy394);}
break;
- case 245: /* cmd ::= VACUUM vinto */
+ case 247: /* cmd ::= VACUUM vinto */
{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy528);}
break;
- case 246: /* cmd ::= VACUUM nm vinto */
+ case 248: /* cmd ::= VACUUM nm vinto */
{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy528);}
break;
- case 249: /* cmd ::= PRAGMA nm dbnm */
+ case 251: /* cmd ::= PRAGMA nm dbnm */
{sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);}
break;
- case 250: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
+ case 252: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);}
break;
- case 251: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ case 253: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);}
break;
- case 252: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
+ case 254: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);}
break;
- case 253: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ case 255: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);}
break;
- case 256: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ case 258: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
{
Token all;
all.z = yymsp[-3].minor.yy0.z;
@@ -173905,50 +175612,50 @@ static YYACTIONTYPE yy_reduce(
sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy33, &all);
}
break;
- case 257: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ case 259: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
{
sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy394, yymsp[-4].minor.yy180.a, yymsp[-4].minor.yy180.b, yymsp[-2].minor.yy131, yymsp[0].minor.yy528, yymsp[-10].minor.yy394, yymsp[-8].minor.yy394);
yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/
}
break;
- case 258: /* trigger_time ::= BEFORE|AFTER */
+ case 260: /* trigger_time ::= BEFORE|AFTER */
{ yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/ }
break;
- case 259: /* trigger_time ::= INSTEAD OF */
+ case 261: /* trigger_time ::= INSTEAD OF */
{ yymsp[-1].minor.yy394 = TK_INSTEAD;}
break;
- case 260: /* trigger_time ::= */
+ case 262: /* trigger_time ::= */
{ yymsp[1].minor.yy394 = TK_BEFORE; }
break;
- case 261: /* trigger_event ::= DELETE|INSERT */
- case 262: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==262);
+ case 263: /* trigger_event ::= DELETE|INSERT */
+ case 264: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==264);
{yymsp[0].minor.yy180.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy180.b = 0;}
break;
- case 263: /* trigger_event ::= UPDATE OF idlist */
+ case 265: /* trigger_event ::= UPDATE OF idlist */
{yymsp[-2].minor.yy180.a = TK_UPDATE; yymsp[-2].minor.yy180.b = yymsp[0].minor.yy254;}
break;
- case 264: /* when_clause ::= */
- case 283: /* key_opt ::= */ yytestcase(yyruleno==283);
+ case 266: /* when_clause ::= */
+ case 285: /* key_opt ::= */ yytestcase(yyruleno==285);
{ yymsp[1].minor.yy528 = 0; }
break;
- case 265: /* when_clause ::= WHEN expr */
- case 284: /* key_opt ::= KEY expr */ yytestcase(yyruleno==284);
+ case 267: /* when_clause ::= WHEN expr */
+ case 286: /* key_opt ::= KEY expr */ yytestcase(yyruleno==286);
{ yymsp[-1].minor.yy528 = yymsp[0].minor.yy528; }
break;
- case 266: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ case 268: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
{
assert( yymsp[-2].minor.yy33!=0 );
yymsp[-2].minor.yy33->pLast->pNext = yymsp[-1].minor.yy33;
yymsp[-2].minor.yy33->pLast = yymsp[-1].minor.yy33;
}
break;
- case 267: /* trigger_cmd_list ::= trigger_cmd SEMI */
+ case 269: /* trigger_cmd_list ::= trigger_cmd SEMI */
{
assert( yymsp[-1].minor.yy33!=0 );
yymsp[-1].minor.yy33->pLast = yymsp[-1].minor.yy33;
}
break;
- case 268: /* trnm ::= nm DOT nm */
+ case 270: /* trnm ::= nm DOT nm */
{
yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;
sqlite3ErrorMsg(pParse,
@@ -173956,39 +175663,39 @@ static YYACTIONTYPE yy_reduce(
"statements within triggers");
}
break;
- case 269: /* tridxby ::= INDEXED BY nm */
+ case 271: /* tridxby ::= INDEXED BY nm */
{
sqlite3ErrorMsg(pParse,
"the INDEXED BY clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
- case 270: /* tridxby ::= NOT INDEXED */
+ case 272: /* tridxby ::= NOT INDEXED */
{
sqlite3ErrorMsg(pParse,
"the NOT INDEXED clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
- case 271: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
+ case 273: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
{yylhsminor.yy33 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy131, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528, yymsp[-7].minor.yy394, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy522);}
yymsp[-8].minor.yy33 = yylhsminor.yy33;
break;
- case 272: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+ case 274: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
{
yylhsminor.yy33 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy254,yymsp[-2].minor.yy47,yymsp[-6].minor.yy394,yymsp[-1].minor.yy444,yymsp[-7].minor.yy522,yymsp[0].minor.yy522);/*yylhsminor.yy33-overwrites-yymsp[-6].minor.yy394*/
}
yymsp[-7].minor.yy33 = yylhsminor.yy33;
break;
- case 273: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
+ case 275: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
{yylhsminor.yy33 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy528, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy522);}
yymsp[-5].minor.yy33 = yylhsminor.yy33;
break;
- case 274: /* trigger_cmd ::= scanpt select scanpt */
+ case 276: /* trigger_cmd ::= scanpt select scanpt */
{yylhsminor.yy33 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy47, yymsp[-2].minor.yy522, yymsp[0].minor.yy522); /*yylhsminor.yy33-overwrites-yymsp[-1].minor.yy47*/}
yymsp[-2].minor.yy33 = yylhsminor.yy33;
break;
- case 275: /* expr ::= RAISE LP IGNORE RP */
+ case 277: /* expr ::= RAISE LP IGNORE RP */
{
yymsp[-3].minor.yy528 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
if( yymsp[-3].minor.yy528 ){
@@ -173996,7 +175703,7 @@ static YYACTIONTYPE yy_reduce(
}
}
break;
- case 276: /* expr ::= RAISE LP raisetype COMMA nm RP */
+ case 278: /* expr ::= RAISE LP raisetype COMMA nm RP */
{
yymsp[-5].minor.yy528 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1);
if( yymsp[-5].minor.yy528 ) {
@@ -174004,114 +175711,114 @@ static YYACTIONTYPE yy_reduce(
}
}
break;
- case 277: /* raisetype ::= ROLLBACK */
+ case 279: /* raisetype ::= ROLLBACK */
{yymsp[0].minor.yy394 = OE_Rollback;}
break;
- case 279: /* raisetype ::= FAIL */
+ case 281: /* raisetype ::= FAIL */
{yymsp[0].minor.yy394 = OE_Fail;}
break;
- case 280: /* cmd ::= DROP TRIGGER ifexists fullname */
+ case 282: /* cmd ::= DROP TRIGGER ifexists fullname */
{
sqlite3DropTrigger(pParse,yymsp[0].minor.yy131,yymsp[-1].minor.yy394);
}
break;
- case 281: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ case 283: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
{
sqlite3Attach(pParse, yymsp[-3].minor.yy528, yymsp[-1].minor.yy528, yymsp[0].minor.yy528);
}
break;
- case 282: /* cmd ::= DETACH database_kw_opt expr */
+ case 284: /* cmd ::= DETACH database_kw_opt expr */
{
sqlite3Detach(pParse, yymsp[0].minor.yy528);
}
break;
- case 285: /* cmd ::= REINDEX */
+ case 287: /* cmd ::= REINDEX */
{sqlite3Reindex(pParse, 0, 0);}
break;
- case 286: /* cmd ::= REINDEX nm dbnm */
+ case 288: /* cmd ::= REINDEX nm dbnm */
{sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
- case 287: /* cmd ::= ANALYZE */
+ case 289: /* cmd ::= ANALYZE */
{sqlite3Analyze(pParse, 0, 0);}
break;
- case 288: /* cmd ::= ANALYZE nm dbnm */
+ case 290: /* cmd ::= ANALYZE nm dbnm */
{sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
- case 289: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
+ case 291: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
{
sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy131,&yymsp[0].minor.yy0);
}
break;
- case 290: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ case 292: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
{
yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n;
sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0);
}
break;
- case 291: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
+ case 293: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
{
sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy131, &yymsp[0].minor.yy0);
}
break;
- case 292: /* add_column_fullname ::= fullname */
+ case 294: /* add_column_fullname ::= fullname */
{
disableLookaside(pParse);
sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy131);
}
break;
- case 293: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+ case 295: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
{
sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy131, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
}
break;
- case 294: /* cmd ::= create_vtab */
+ case 296: /* cmd ::= create_vtab */
{sqlite3VtabFinishParse(pParse,0);}
break;
- case 295: /* cmd ::= create_vtab LP vtabarglist RP */
+ case 297: /* cmd ::= create_vtab LP vtabarglist RP */
{sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);}
break;
- case 296: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ case 298: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
{
sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy394);
}
break;
- case 297: /* vtabarg ::= */
+ case 299: /* vtabarg ::= */
{sqlite3VtabArgInit(pParse);}
break;
- case 298: /* vtabargtoken ::= ANY */
- case 299: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==299);
- case 300: /* lp ::= LP */ yytestcase(yyruleno==300);
+ case 300: /* vtabargtoken ::= ANY */
+ case 301: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==301);
+ case 302: /* lp ::= LP */ yytestcase(yyruleno==302);
{sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);}
break;
- case 301: /* with ::= WITH wqlist */
- case 302: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==302);
+ case 303: /* with ::= WITH wqlist */
+ case 304: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==304);
{ sqlite3WithPush(pParse, yymsp[0].minor.yy521, 1); }
break;
- case 303: /* wqas ::= AS */
+ case 305: /* wqas ::= AS */
{yymsp[0].minor.yy516 = M10d_Any;}
break;
- case 304: /* wqas ::= AS MATERIALIZED */
+ case 306: /* wqas ::= AS MATERIALIZED */
{yymsp[-1].minor.yy516 = M10d_Yes;}
break;
- case 305: /* wqas ::= AS NOT MATERIALIZED */
+ case 307: /* wqas ::= AS NOT MATERIALIZED */
{yymsp[-2].minor.yy516 = M10d_No;}
break;
- case 306: /* wqitem ::= nm eidlist_opt wqas LP select RP */
+ case 308: /* wqitem ::= nm eidlist_opt wqas LP select RP */
{
yymsp[-5].minor.yy385 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy322, yymsp[-1].minor.yy47, yymsp[-3].minor.yy516); /*A-overwrites-X*/
}
break;
- case 307: /* wqlist ::= wqitem */
+ case 309: /* wqlist ::= wqitem */
{
yymsp[0].minor.yy521 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy385); /*A-overwrites-X*/
}
break;
- case 308: /* wqlist ::= wqlist COMMA wqitem */
+ case 310: /* wqlist ::= wqlist COMMA wqitem */
{
yymsp[-2].minor.yy521 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy521, yymsp[0].minor.yy385);
}
break;
- case 309: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
+ case 311: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
{
assert( yymsp[0].minor.yy41!=0 );
sqlite3WindowChain(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy41);
@@ -174120,7 +175827,7 @@ static YYACTIONTYPE yy_reduce(
}
yymsp[-2].minor.yy41 = yylhsminor.yy41;
break;
- case 310: /* windowdefn ::= nm AS LP window RP */
+ case 312: /* windowdefn ::= nm AS LP window RP */
{
if( ALWAYS(yymsp[-1].minor.yy41) ){
yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n);
@@ -174129,83 +175836,83 @@ static YYACTIONTYPE yy_reduce(
}
yymsp[-4].minor.yy41 = yylhsminor.yy41;
break;
- case 311: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+ case 313: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
{
yymsp[-4].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, 0);
}
break;
- case 312: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+ case 314: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
{
yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, &yymsp[-5].minor.yy0);
}
yymsp[-5].minor.yy41 = yylhsminor.yy41;
break;
- case 313: /* window ::= ORDER BY sortlist frame_opt */
+ case 315: /* window ::= ORDER BY sortlist frame_opt */
{
yymsp[-3].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, 0);
}
break;
- case 314: /* window ::= nm ORDER BY sortlist frame_opt */
+ case 316: /* window ::= nm ORDER BY sortlist frame_opt */
{
yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0);
}
yymsp[-4].minor.yy41 = yylhsminor.yy41;
break;
- case 315: /* window ::= nm frame_opt */
+ case 317: /* window ::= nm frame_opt */
{
yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, 0, &yymsp[-1].minor.yy0);
}
yymsp[-1].minor.yy41 = yylhsminor.yy41;
break;
- case 316: /* frame_opt ::= */
+ case 318: /* frame_opt ::= */
{
yymsp[1].minor.yy41 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0);
}
break;
- case 317: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+ case 319: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
{
yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy394, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy516);
}
yymsp[-2].minor.yy41 = yylhsminor.yy41;
break;
- case 318: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+ case 320: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
{
yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy394, yymsp[-3].minor.yy595.eType, yymsp[-3].minor.yy595.pExpr, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, yymsp[0].minor.yy516);
}
yymsp[-5].minor.yy41 = yylhsminor.yy41;
break;
- case 320: /* frame_bound_s ::= frame_bound */
- case 322: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==322);
+ case 322: /* frame_bound_s ::= frame_bound */
+ case 324: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==324);
{yylhsminor.yy595 = yymsp[0].minor.yy595;}
yymsp[0].minor.yy595 = yylhsminor.yy595;
break;
- case 321: /* frame_bound_s ::= UNBOUNDED PRECEDING */
- case 323: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==323);
- case 325: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==325);
+ case 323: /* frame_bound_s ::= UNBOUNDED PRECEDING */
+ case 325: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==325);
+ case 327: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==327);
{yylhsminor.yy595.eType = yymsp[-1].major; yylhsminor.yy595.pExpr = 0;}
yymsp[-1].minor.yy595 = yylhsminor.yy595;
break;
- case 324: /* frame_bound ::= expr PRECEDING|FOLLOWING */
+ case 326: /* frame_bound ::= expr PRECEDING|FOLLOWING */
{yylhsminor.yy595.eType = yymsp[0].major; yylhsminor.yy595.pExpr = yymsp[-1].minor.yy528;}
yymsp[-1].minor.yy595 = yylhsminor.yy595;
break;
- case 326: /* frame_exclude_opt ::= */
+ case 328: /* frame_exclude_opt ::= */
{yymsp[1].minor.yy516 = 0;}
break;
- case 327: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
+ case 329: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
{yymsp[-1].minor.yy516 = yymsp[0].minor.yy516;}
break;
- case 328: /* frame_exclude ::= NO OTHERS */
- case 329: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==329);
+ case 330: /* frame_exclude ::= NO OTHERS */
+ case 331: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==331);
{yymsp[-1].minor.yy516 = yymsp[-1].major; /*A-overwrites-X*/}
break;
- case 330: /* frame_exclude ::= GROUP|TIES */
+ case 332: /* frame_exclude ::= GROUP|TIES */
{yymsp[0].minor.yy516 = yymsp[0].major; /*A-overwrites-X*/}
break;
- case 331: /* window_clause ::= WINDOW windowdefn_list */
+ case 333: /* window_clause ::= WINDOW windowdefn_list */
{ yymsp[-1].minor.yy41 = yymsp[0].minor.yy41; }
break;
- case 332: /* filter_over ::= filter_clause over_clause */
+ case 334: /* filter_over ::= filter_clause over_clause */
{
if( yymsp[0].minor.yy41 ){
yymsp[0].minor.yy41->pFilter = yymsp[-1].minor.yy528;
@@ -174216,13 +175923,13 @@ static YYACTIONTYPE yy_reduce(
}
yymsp[-1].minor.yy41 = yylhsminor.yy41;
break;
- case 333: /* filter_over ::= over_clause */
+ case 335: /* filter_over ::= over_clause */
{
yylhsminor.yy41 = yymsp[0].minor.yy41;
}
yymsp[0].minor.yy41 = yylhsminor.yy41;
break;
- case 334: /* filter_over ::= filter_clause */
+ case 336: /* filter_over ::= filter_clause */
{
yylhsminor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
if( yylhsminor.yy41 ){
@@ -174234,13 +175941,13 @@ static YYACTIONTYPE yy_reduce(
}
yymsp[0].minor.yy41 = yylhsminor.yy41;
break;
- case 335: /* over_clause ::= OVER LP window RP */
+ case 337: /* over_clause ::= OVER LP window RP */
{
yymsp[-3].minor.yy41 = yymsp[-1].minor.yy41;
assert( yymsp[-3].minor.yy41!=0 );
}
break;
- case 336: /* over_clause ::= OVER nm */
+ case 338: /* over_clause ::= OVER nm */
{
yymsp[-1].minor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
if( yymsp[-1].minor.yy41 ){
@@ -174248,75 +175955,75 @@ static YYACTIONTYPE yy_reduce(
}
}
break;
- case 337: /* filter_clause ::= FILTER LP WHERE expr RP */
+ case 339: /* filter_clause ::= FILTER LP WHERE expr RP */
{ yymsp[-4].minor.yy528 = yymsp[-1].minor.yy528; }
break;
default:
- /* (338) input ::= cmdlist */ yytestcase(yyruleno==338);
- /* (339) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==339);
- /* (340) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=340);
- /* (341) ecmd ::= SEMI */ yytestcase(yyruleno==341);
- /* (342) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==342);
- /* (343) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=343);
- /* (344) trans_opt ::= */ yytestcase(yyruleno==344);
- /* (345) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==345);
- /* (346) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==346);
- /* (347) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==347);
- /* (348) savepoint_opt ::= */ yytestcase(yyruleno==348);
- /* (349) cmd ::= create_table create_table_args */ yytestcase(yyruleno==349);
- /* (350) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=350);
- /* (351) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==351);
- /* (352) columnlist ::= columnname carglist */ yytestcase(yyruleno==352);
- /* (353) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==353);
- /* (354) nm ::= STRING */ yytestcase(yyruleno==354);
- /* (355) typetoken ::= typename */ yytestcase(yyruleno==355);
- /* (356) typename ::= ID|STRING */ yytestcase(yyruleno==356);
- /* (357) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=357);
- /* (358) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=358);
- /* (359) carglist ::= carglist ccons */ yytestcase(yyruleno==359);
- /* (360) carglist ::= */ yytestcase(yyruleno==360);
- /* (361) ccons ::= NULL onconf */ yytestcase(yyruleno==361);
- /* (362) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==362);
- /* (363) ccons ::= AS generated */ yytestcase(yyruleno==363);
- /* (364) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==364);
- /* (365) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==365);
- /* (366) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=366);
- /* (367) tconscomma ::= */ yytestcase(yyruleno==367);
- /* (368) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=368);
- /* (369) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=369);
- /* (370) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=370);
- /* (371) oneselect ::= values */ yytestcase(yyruleno==371);
- /* (372) sclp ::= selcollist COMMA */ yytestcase(yyruleno==372);
- /* (373) as ::= ID|STRING */ yytestcase(yyruleno==373);
- /* (374) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=374);
- /* (375) returning ::= */ yytestcase(yyruleno==375);
- /* (376) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=376);
- /* (377) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==377);
- /* (378) case_operand ::= expr */ yytestcase(yyruleno==378);
- /* (379) exprlist ::= nexprlist */ yytestcase(yyruleno==379);
- /* (380) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=380);
- /* (381) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=381);
- /* (382) nmnum ::= ON */ yytestcase(yyruleno==382);
- /* (383) nmnum ::= DELETE */ yytestcase(yyruleno==383);
- /* (384) nmnum ::= DEFAULT */ yytestcase(yyruleno==384);
- /* (385) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==385);
- /* (386) foreach_clause ::= */ yytestcase(yyruleno==386);
- /* (387) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==387);
- /* (388) trnm ::= nm */ yytestcase(yyruleno==388);
- /* (389) tridxby ::= */ yytestcase(yyruleno==389);
- /* (390) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==390);
- /* (391) database_kw_opt ::= */ yytestcase(yyruleno==391);
- /* (392) kwcolumn_opt ::= */ yytestcase(yyruleno==392);
- /* (393) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==393);
- /* (394) vtabarglist ::= vtabarg */ yytestcase(yyruleno==394);
- /* (395) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==395);
- /* (396) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==396);
- /* (397) anylist ::= */ yytestcase(yyruleno==397);
- /* (398) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==398);
- /* (399) anylist ::= anylist ANY */ yytestcase(yyruleno==399);
- /* (400) with ::= */ yytestcase(yyruleno==400);
- /* (401) windowdefn_list ::= windowdefn (OPTIMIZED OUT) */ assert(yyruleno!=401);
- /* (402) window ::= frame_opt (OPTIMIZED OUT) */ assert(yyruleno!=402);
+ /* (340) input ::= cmdlist */ yytestcase(yyruleno==340);
+ /* (341) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==341);
+ /* (342) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=342);
+ /* (343) ecmd ::= SEMI */ yytestcase(yyruleno==343);
+ /* (344) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==344);
+ /* (345) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=345);
+ /* (346) trans_opt ::= */ yytestcase(yyruleno==346);
+ /* (347) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==347);
+ /* (348) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==348);
+ /* (349) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==349);
+ /* (350) savepoint_opt ::= */ yytestcase(yyruleno==350);
+ /* (351) cmd ::= create_table create_table_args */ yytestcase(yyruleno==351);
+ /* (352) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=352);
+ /* (353) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==353);
+ /* (354) columnlist ::= columnname carglist */ yytestcase(yyruleno==354);
+ /* (355) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==355);
+ /* (356) nm ::= STRING */ yytestcase(yyruleno==356);
+ /* (357) typetoken ::= typename */ yytestcase(yyruleno==357);
+ /* (358) typename ::= ID|STRING */ yytestcase(yyruleno==358);
+ /* (359) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=359);
+ /* (360) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=360);
+ /* (361) carglist ::= carglist ccons */ yytestcase(yyruleno==361);
+ /* (362) carglist ::= */ yytestcase(yyruleno==362);
+ /* (363) ccons ::= NULL onconf */ yytestcase(yyruleno==363);
+ /* (364) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==364);
+ /* (365) ccons ::= AS generated */ yytestcase(yyruleno==365);
+ /* (366) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==366);
+ /* (367) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==367);
+ /* (368) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=368);
+ /* (369) tconscomma ::= */ yytestcase(yyruleno==369);
+ /* (370) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=370);
+ /* (371) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=371);
+ /* (372) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=372);
+ /* (373) oneselect ::= values */ yytestcase(yyruleno==373);
+ /* (374) sclp ::= selcollist COMMA */ yytestcase(yyruleno==374);
+ /* (375) as ::= ID|STRING */ yytestcase(yyruleno==375);
+ /* (376) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=376);
+ /* (377) returning ::= */ yytestcase(yyruleno==377);
+ /* (378) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=378);
+ /* (379) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==379);
+ /* (380) case_operand ::= expr */ yytestcase(yyruleno==380);
+ /* (381) exprlist ::= nexprlist */ yytestcase(yyruleno==381);
+ /* (382) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=382);
+ /* (383) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=383);
+ /* (384) nmnum ::= ON */ yytestcase(yyruleno==384);
+ /* (385) nmnum ::= DELETE */ yytestcase(yyruleno==385);
+ /* (386) nmnum ::= DEFAULT */ yytestcase(yyruleno==386);
+ /* (387) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==387);
+ /* (388) foreach_clause ::= */ yytestcase(yyruleno==388);
+ /* (389) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==389);
+ /* (390) trnm ::= nm */ yytestcase(yyruleno==390);
+ /* (391) tridxby ::= */ yytestcase(yyruleno==391);
+ /* (392) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==392);
+ /* (393) database_kw_opt ::= */ yytestcase(yyruleno==393);
+ /* (394) kwcolumn_opt ::= */ yytestcase(yyruleno==394);
+ /* (395) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==395);
+ /* (396) vtabarglist ::= vtabarg */ yytestcase(yyruleno==396);
+ /* (397) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==397);
+ /* (398) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==398);
+ /* (399) anylist ::= */ yytestcase(yyruleno==399);
+ /* (400) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==400);
+ /* (401) anylist ::= anylist ANY */ yytestcase(yyruleno==401);
+ /* (402) with ::= */ yytestcase(yyruleno==402);
+ /* (403) windowdefn_list ::= windowdefn (OPTIMIZED OUT) */ assert(yyruleno!=403);
+ /* (404) window ::= frame_opt (OPTIMIZED OUT) */ assert(yyruleno!=404);
break;
/********** End reduce actions ************************************************/
};
@@ -176440,7 +178147,9 @@ SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3*);
#ifdef SQLITE_ENABLE_STMTVTAB
SQLITE_PRIVATE int sqlite3StmtVtabInit(sqlite3*);
#endif
-
+#ifdef SQLITE_EXTRA_AUTOEXT
+int SQLITE_EXTRA_AUTOEXT(sqlite3*);
+#endif
/*
** An array of pointers to extension initializer functions for
** built-in extensions.
@@ -176474,6 +178183,9 @@ static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = {
#ifdef SQLITE_ENABLE_BYTECODE_VTAB
sqlite3VdbeBytecodeVtabInit,
#endif
+#ifdef SQLITE_EXTRA_AUTOEXT
+ SQLITE_EXTRA_AUTOEXT,
+#endif
};
#ifndef SQLITE_AMALGAMATION
@@ -176548,6 +178260,32 @@ SQLITE_API char *sqlite3_temp_directory = 0;
SQLITE_API char *sqlite3_data_directory = 0;
/*
+** Determine whether or not high-precision (long double) floating point
+** math works correctly on CPU currently running.
+*/
+static SQLITE_NOINLINE int hasHighPrecisionDouble(int rc){
+ if( sizeof(LONGDOUBLE_TYPE)<=8 ){
+ /* If the size of "long double" is not more than 8, then
+ ** high-precision math is not possible. */
+ return 0;
+ }else{
+ /* Just because sizeof(long double)>8 does not mean that the underlying
+ ** hardware actually supports high-precision floating point. For example,
+ ** clearing the 0x100 bit in the floating-point control word on Intel
+ ** processors will make long double work like double, even though long
+ ** double takes up more space. The only way to determine if long double
+ ** actually works is to run an experiment. */
+ LONGDOUBLE_TYPE a, b, c;
+ rc++;
+ a = 1.0+rc*0.1;
+ b = 1.0e+18+rc*25.0;
+ c = a+b;
+ return b!=c;
+ }
+}
+
+
+/*
** Initialize SQLite.
**
** This routine must be called to initialize the memory allocation,
@@ -176742,6 +178480,12 @@ SQLITE_API int sqlite3_initialize(void){
}
#endif
+ /* Experimentally determine if high-precision floating point is
+ ** available. */
+#ifndef SQLITE_OMIT_WSD
+ sqlite3Config.bUseLongDouble = hasHighPrecisionDouble(rc);
+#endif
+
return rc;
}
@@ -177312,6 +179056,10 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3 *db){
SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
va_list ap;
int rc;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
va_start(ap, op);
switch( op ){
@@ -177641,6 +179389,14 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){
}
#endif
+ while( db->pDbData ){
+ DbClientData *p = db->pDbData;
+ db->pDbData = p->pNext;
+ assert( p->pData!=0 );
+ if( p->xDestructor ) p->xDestructor(p->pData);
+ sqlite3_free(p);
+ }
+
/* Convert the connection into a zombie and then close it.
*/
db->eOpenState = SQLITE_STATE_ZOMBIE;
@@ -178258,7 +180014,7 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC );
assert( SQLITE_FUNC_DIRECT==SQLITE_DIRECTONLY );
extraFlags = enc & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY|
- SQLITE_SUBTYPE|SQLITE_INNOCUOUS);
+ SQLITE_SUBTYPE|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE);
enc &= (SQLITE_FUNC_ENCMASK|SQLITE_ANY);
/* The SQLITE_INNOCUOUS flag is the same bit as SQLITE_FUNC_UNSAFE. But
@@ -178715,6 +180471,12 @@ SQLITE_API void *sqlite3_preupdate_hook(
void *pArg /* First callback argument */
){
void *pRet;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( db==0 ){
+ return 0;
+ }
+#endif
sqlite3_mutex_enter(db->mutex);
pRet = db->pPreUpdateArg;
db->xPreUpdateCallback = xCallback;
@@ -178861,7 +180623,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
if( eMode<SQLITE_CHECKPOINT_PASSIVE || eMode>SQLITE_CHECKPOINT_TRUNCATE ){
/* EVIDENCE-OF: R-03996-12088 The M parameter must be a valid checkpoint
** mode: */
- return SQLITE_MISUSE;
+ return SQLITE_MISUSE_BKPT;
}
sqlite3_mutex_enter(db->mutex);
@@ -180098,6 +181860,69 @@ SQLITE_API int sqlite3_collation_needed16(
}
#endif /* SQLITE_OMIT_UTF16 */
+/*
+** Find existing client data.
+*/
+SQLITE_API void *sqlite3_get_clientdata(sqlite3 *db, const char *zName){
+ DbClientData *p;
+ sqlite3_mutex_enter(db->mutex);
+ for(p=db->pDbData; p; p=p->pNext){
+ if( strcmp(p->zName, zName)==0 ){
+ void *pResult = p->pData;
+ sqlite3_mutex_leave(db->mutex);
+ return pResult;
+ }
+ }
+ sqlite3_mutex_leave(db->mutex);
+ return 0;
+}
+
+/*
+** Add new client data to a database connection.
+*/
+SQLITE_API int sqlite3_set_clientdata(
+ sqlite3 *db, /* Attach client data to this connection */
+ const char *zName, /* Name of the client data */
+ void *pData, /* The client data itself */
+ void (*xDestructor)(void*) /* Destructor */
+){
+ DbClientData *p, **pp;
+ sqlite3_mutex_enter(db->mutex);
+ pp = &db->pDbData;
+ for(p=db->pDbData; p && strcmp(p->zName,zName); p=p->pNext){
+ pp = &p->pNext;
+ }
+ if( p ){
+ assert( p->pData!=0 );
+ if( p->xDestructor ) p->xDestructor(p->pData);
+ if( pData==0 ){
+ *pp = p->pNext;
+ sqlite3_free(p);
+ sqlite3_mutex_leave(db->mutex);
+ return SQLITE_OK;
+ }
+ }else if( pData==0 ){
+ sqlite3_mutex_leave(db->mutex);
+ return SQLITE_OK;
+ }else{
+ size_t n = strlen(zName);
+ p = sqlite3_malloc64( sizeof(DbClientData)+n+1 );
+ if( p==0 ){
+ if( xDestructor ) xDestructor(pData);
+ sqlite3_mutex_leave(db->mutex);
+ return SQLITE_NOMEM;
+ }
+ memcpy(p->zName, zName, n+1);
+ p->pNext = db->pDbData;
+ db->pDbData = p;
+ }
+ p->pData = pData;
+ p->xDestructor = xDestructor;
+ sqlite3_mutex_leave(db->mutex);
+ return SQLITE_OK;
+}
+
+
#ifndef SQLITE_OMIT_DEPRECATED
/*
** This function is now an anachronism. It used to be used to recover from a
@@ -180447,6 +182272,28 @@ SQLITE_API int sqlite3_test_control(int op, ...){
}
#endif
+ /* sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, sqlite3 *db, int b);
+ **
+ ** If b is true, then activate the SQLITE_FkNoAction setting. If b is
+ ** false then clearn that setting. If the SQLITE_FkNoAction setting is
+ ** abled, all foreign key ON DELETE and ON UPDATE actions behave as if
+ ** they were NO ACTION, regardless of how they are defined.
+ **
+ ** NB: One must usually run "PRAGMA writable_schema=RESET" after
+ ** using this test-control, before it will take full effect. failing
+ ** to reset the schema can result in some unexpected behavior.
+ */
+ case SQLITE_TESTCTRL_FK_NO_ACTION: {
+ sqlite3 *db = va_arg(ap, sqlite3*);
+ int b = va_arg(ap, int);
+ if( b ){
+ db->flags |= SQLITE_FkNoAction;
+ }else{
+ db->flags &= ~SQLITE_FkNoAction;
+ }
+ break;
+ }
+
/*
** sqlite3_test_control(BITVEC_TEST, size, program)
**
@@ -180871,11 +182718,11 @@ SQLITE_API int sqlite3_test_control(int op, ...){
** X<0 Make no changes to the bUseLongDouble. Just report value.
** X==0 Disable bUseLongDouble
** X==1 Enable bUseLongDouble
- ** X==2 Set bUseLongDouble to its default value for this platform
+ ** X>=2 Set bUseLongDouble to its default value for this platform
*/
case SQLITE_TESTCTRL_USELONGDOUBLE: {
int b = va_arg(ap, int);
- if( b==2 ) b = sizeof(LONGDOUBLE_TYPE)>8;
+ if( b>=2 ) b = hasHighPrecisionDouble(b);
if( b>=0 ) sqlite3Config.bUseLongDouble = b>0;
rc = sqlite3Config.bUseLongDouble!=0;
break;
@@ -180912,6 +182759,28 @@ SQLITE_API int sqlite3_test_control(int op, ...){
break;
}
#endif
+
+ /* sqlite3_test_control(SQLITE_TESTCTRL_JSON_SELFCHECK, &onOff);
+ **
+ ** Activate or deactivate validation of JSONB that is generated from
+ ** text. Off by default, as the validation is slow. Validation is
+ ** only available if compiled using SQLITE_DEBUG.
+ **
+ ** If onOff is initially 1, then turn it on. If onOff is initially
+ ** off, turn it off. If onOff is initially -1, then change onOff
+ ** to be the current setting.
+ */
+ case SQLITE_TESTCTRL_JSON_SELFCHECK: {
+#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD)
+ int *pOnOff = va_arg(ap, int*);
+ if( *pOnOff<0 ){
+ *pOnOff = sqlite3Config.bJsonSelfcheck;
+ }else{
+ sqlite3Config.bJsonSelfcheck = (u8)((*pOnOff)&0xff);
+ }
+#endif
+ break;
+ }
}
va_end(ap);
#endif /* SQLITE_UNTESTABLE */
@@ -181289,7 +183158,7 @@ SQLITE_API int sqlite3_compileoption_used(const char *zOptName){
int nOpt;
const char **azCompileOpt;
-#if SQLITE_ENABLE_API_ARMOR
+#ifdef SQLITE_ENABLE_API_ARMOR
if( zOptName==0 ){
(void)SQLITE_MISUSE_BKPT;
return 0;
@@ -181484,6 +183353,9 @@ SQLITE_API int sqlite3_unlock_notify(
){
int rc = SQLITE_OK;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
enterMutex();
@@ -182505,6 +184377,7 @@ struct Fts3Table {
int nPgsz; /* Page size for host database */
char *zSegmentsTbl; /* Name of %_segments table */
sqlite3_blob *pSegments; /* Blob handle open on %_segments table */
+ int iSavepoint;
/*
** The following array of hash tables is used to buffer pending index
@@ -182892,6 +184765,8 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int);
SQLITE_PRIVATE int sqlite3Fts3ExprIterate(Fts3Expr*, int (*x)(Fts3Expr*,int,void*), void*);
+SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk);
+
#endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */
#endif /* _FTSINT_H */
@@ -183248,6 +185123,7 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){
zLanguageid = (p->zLanguageid ? p->zLanguageid : "__langid");
sqlite3_vtab_config(p->db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
+ sqlite3_vtab_config(p->db, SQLITE_VTAB_INNOCUOUS);
/* Create a list of user columns for the virtual table */
zCols = sqlite3_mprintf("%Q, ", p->azColumn[0]);
@@ -186497,6 +188373,8 @@ static int fts3RenameMethod(
rc = sqlite3Fts3PendingTermsFlush(p);
}
+ p->bIgnoreSavepoint = 1;
+
if( p->zContentTbl==0 ){
fts3DbExec(&rc, db,
"ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';",
@@ -186524,6 +188402,8 @@ static int fts3RenameMethod(
"ALTER TABLE %Q.'%q_segdir' RENAME TO '%q_segdir';",
p->zDb, p->zName, zName
);
+
+ p->bIgnoreSavepoint = 0;
return rc;
}
@@ -186534,12 +188414,28 @@ static int fts3RenameMethod(
*/
static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
int rc = SQLITE_OK;
- UNUSED_PARAMETER(iSavepoint);
- assert( ((Fts3Table *)pVtab)->inTransaction );
- assert( ((Fts3Table *)pVtab)->mxSavepoint <= iSavepoint );
- TESTONLY( ((Fts3Table *)pVtab)->mxSavepoint = iSavepoint );
- if( ((Fts3Table *)pVtab)->bIgnoreSavepoint==0 ){
- rc = fts3SyncMethod(pVtab);
+ Fts3Table *pTab = (Fts3Table*)pVtab;
+ assert( pTab->inTransaction );
+ assert( pTab->mxSavepoint<=iSavepoint );
+ TESTONLY( pTab->mxSavepoint = iSavepoint );
+
+ if( pTab->bIgnoreSavepoint==0 ){
+ if( fts3HashCount(&pTab->aIndex[0].hPending)>0 ){
+ char *zSql = sqlite3_mprintf("INSERT INTO %Q.%Q(%Q) VALUES('flush')",
+ pTab->zDb, pTab->zName, pTab->zName
+ );
+ if( zSql ){
+ pTab->bIgnoreSavepoint = 1;
+ rc = sqlite3_exec(pTab->db, zSql, 0, 0, 0);
+ pTab->bIgnoreSavepoint = 0;
+ sqlite3_free(zSql);
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ }
+ if( rc==SQLITE_OK ){
+ pTab->iSavepoint = iSavepoint+1;
+ }
}
return rc;
}
@@ -186550,12 +188446,11 @@ static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
** This is a no-op.
*/
static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
- TESTONLY( Fts3Table *p = (Fts3Table*)pVtab );
- UNUSED_PARAMETER(iSavepoint);
- UNUSED_PARAMETER(pVtab);
- assert( p->inTransaction );
- assert( p->mxSavepoint >= iSavepoint );
- TESTONLY( p->mxSavepoint = iSavepoint-1 );
+ Fts3Table *pTab = (Fts3Table*)pVtab;
+ assert( pTab->inTransaction );
+ assert( pTab->mxSavepoint >= iSavepoint );
+ TESTONLY( pTab->mxSavepoint = iSavepoint-1 );
+ pTab->iSavepoint = iSavepoint;
return SQLITE_OK;
}
@@ -186565,11 +188460,13 @@ static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
** Discard the contents of the pending terms table.
*/
static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
- Fts3Table *p = (Fts3Table*)pVtab;
+ Fts3Table *pTab = (Fts3Table*)pVtab;
UNUSED_PARAMETER(iSavepoint);
- assert( p->inTransaction );
- TESTONLY( p->mxSavepoint = iSavepoint );
- sqlite3Fts3PendingTermsClear(p);
+ assert( pTab->inTransaction );
+ TESTONLY( pTab->mxSavepoint = iSavepoint );
+ if( (iSavepoint+1)<=pTab->iSavepoint ){
+ sqlite3Fts3PendingTermsClear(pTab);
+ }
return SQLITE_OK;
}
@@ -186588,8 +188485,40 @@ static int fts3ShadowName(const char *zName){
return 0;
}
+/*
+** Implementation of the xIntegrity() method on the FTS3/FTS4 virtual
+** table.
+*/
+static int fts3IntegrityMethod(
+ sqlite3_vtab *pVtab, /* The virtual table to be checked */
+ const char *zSchema, /* Name of schema in which pVtab lives */
+ const char *zTabname, /* Name of the pVTab table */
+ int isQuick, /* True if this is a quick_check */
+ char **pzErr /* Write error message here */
+){
+ Fts3Table *p = (Fts3Table*)pVtab;
+ int rc;
+ int bOk = 0;
+
+ UNUSED_PARAMETER(isQuick);
+ rc = sqlite3Fts3IntegrityCheck(p, &bOk);
+ assert( rc!=SQLITE_CORRUPT_VTAB || bOk==0 );
+ if( rc!=SQLITE_OK && rc!=SQLITE_CORRUPT_VTAB ){
+ *pzErr = sqlite3_mprintf("unable to validate the inverted index for"
+ " FTS%d table %s.%s: %s",
+ p->bFts4 ? 4 : 3, zSchema, zTabname, sqlite3_errstr(rc));
+ }else if( bOk==0 ){
+ *pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s",
+ p->bFts4 ? 4 : 3, zSchema, zTabname);
+ }
+ sqlite3Fts3SegmentsClose(p);
+ return SQLITE_OK;
+}
+
+
+
static const sqlite3_module fts3Module = {
- /* iVersion */ 3,
+ /* iVersion */ 4,
/* xCreate */ fts3CreateMethod,
/* xConnect */ fts3ConnectMethod,
/* xBestIndex */ fts3BestIndexMethod,
@@ -186613,6 +188542,7 @@ static const sqlite3_module fts3Module = {
/* xRelease */ fts3ReleaseMethod,
/* xRollbackTo */ fts3RollbackToMethod,
/* xShadowName */ fts3ShadowName,
+ /* xIntegrity */ fts3IntegrityMethod,
};
/*
@@ -189288,7 +191218,8 @@ SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
int rc; /* Return code */
@@ -192854,7 +194785,8 @@ SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash, void(*xDestr
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
int rc; /* Return code */
@@ -196195,7 +198127,6 @@ SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
rc = fts3SegmentMerge(p, p->iPrevLangid, i, FTS3_SEGCURSOR_PENDING);
if( rc==SQLITE_DONE ) rc = SQLITE_OK;
}
- sqlite3Fts3PendingTermsClear(p);
/* Determine the auto-incr-merge setting if unknown. If enabled,
** estimate the number of leaf blocks of content to be written
@@ -196217,6 +198148,10 @@ SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
rc = sqlite3_reset(pStmt);
}
}
+
+ if( rc==SQLITE_OK ){
+ sqlite3Fts3PendingTermsClear(p);
+ }
return rc;
}
@@ -196848,6 +198783,8 @@ static int fts3AppendToNode(
blobGrowBuffer(pPrev, nTerm, &rc);
if( rc!=SQLITE_OK ) return rc;
+ assert( pPrev!=0 );
+ assert( pPrev->a!=0 );
nPrefix = fts3PrefixCompress(pPrev->a, pPrev->n, zTerm, nTerm);
nSuffix = nTerm - nPrefix;
@@ -196904,9 +198841,13 @@ static int fts3IncrmergeAppend(
nSpace += sqlite3Fts3VarintLen(nDoclist) + nDoclist;
/* If the current block is not empty, and if adding this term/doclist
- ** to the current block would make it larger than Fts3Table.nNodeSize
- ** bytes, write this block out to the database. */
- if( pLeaf->block.n>0 && (pLeaf->block.n + nSpace)>p->nNodeSize ){
+ ** to the current block would make it larger than Fts3Table.nNodeSize bytes,
+ ** and if there is still room for another leaf page, write this block out to
+ ** the database. */
+ if( pLeaf->block.n>0
+ && (pLeaf->block.n + nSpace)>p->nNodeSize
+ && pLeaf->iBlock < (pWriter->iStart + pWriter->nLeafEst)
+ ){
rc = fts3WriteSegment(p, pLeaf->iBlock, pLeaf->block.a, pLeaf->block.n);
pWriter->nWork++;
@@ -197238,7 +199179,7 @@ static int fts3IncrmergeLoad(
rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock,0);
blobGrowBuffer(&pNode->block,
MAX(nBlock, p->nNodeSize)+FTS3_NODE_PADDING, &rc
- );
+ );
if( rc==SQLITE_OK ){
memcpy(pNode->block.a, aBlock, nBlock);
pNode->block.n = nBlock;
@@ -198155,7 +200096,7 @@ static u64 fts3ChecksumIndex(
** If an error occurs (e.g. an OOM or IO error), return an SQLite error
** code. The final value of *pbOk is undefined in this case.
*/
-static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
+SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){
int rc = SQLITE_OK; /* Return code */
u64 cksum1 = 0; /* Checksum based on FTS index contents */
u64 cksum2 = 0; /* Checksum based on %_content contents */
@@ -198233,7 +200174,7 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
sqlite3_finalize(pStmt);
}
- *pbOk = (cksum1==cksum2);
+ *pbOk = (rc==SQLITE_OK && cksum1==cksum2);
return rc;
}
@@ -198273,7 +200214,7 @@ static int fts3DoIntegrityCheck(
){
int rc;
int bOk = 0;
- rc = fts3IntegrityCheck(p, &bOk);
+ rc = sqlite3Fts3IntegrityCheck(p, &bOk);
if( rc==SQLITE_OK && bOk==0 ) rc = FTS_CORRUPT_VTAB;
return rc;
}
@@ -198303,8 +200244,11 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
rc = fts3DoIncrmerge(p, &zVal[6]);
}else if( nVal>10 && 0==sqlite3_strnicmp(zVal, "automerge=", 10) ){
rc = fts3DoAutoincrmerge(p, &zVal[10]);
+ }else if( nVal==5 && 0==sqlite3_strnicmp(zVal, "flush", 5) ){
+ rc = sqlite3Fts3PendingTermsFlush(p);
+ }
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
- }else{
+ else{
int v;
if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
v = atoi(&zVal[9]);
@@ -198322,8 +200266,8 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
if( v>=4 && v<=FTS3_MERGE_COUNT && (v&1)==0 ) p->nMergeCount = v;
rc = SQLITE_OK;
}
-#endif
}
+#endif
return rc;
}
@@ -201244,24 +203188,145 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int eRemoveDiacritic){
**
******************************************************************************
**
-** This SQLite JSON functions.
+** SQLite JSON functions.
**
** This file began as an extension in ext/misc/json1.c in 2015. That
** extension proved so useful that it has now been moved into the core.
**
-** For the time being, all JSON is stored as pure text. (We might add
-** a JSONB type in the future which stores a binary encoding of JSON in
-** a BLOB, but there is no support for JSONB in the current implementation.
-** This implementation parses JSON text at 250 MB/s, so it is hard to see
-** how JSONB might improve on that.)
+** The original design stored all JSON as pure text, canonical RFC-8259.
+** Support for JSON-5 extensions was added with version 3.42.0 (2023-05-16).
+** All generated JSON text still conforms strictly to RFC-8259, but text
+** with JSON-5 extensions is accepted as input.
+**
+** Beginning with version 3.45.0 (circa 2024-01-01), these routines also
+** accept BLOB values that have JSON encoded using a binary representation
+** called "JSONB". The name JSONB comes from PostgreSQL, however the on-disk
+** format SQLite JSONB is completely different and incompatible with
+** PostgreSQL JSONB.
+**
+** Decoding and interpreting JSONB is still O(N) where N is the size of
+** the input, the same as text JSON. However, the constant of proportionality
+** for JSONB is much smaller due to faster parsing. The size of each
+** element in JSONB is encoded in its header, so there is no need to search
+** for delimiters using persnickety syntax rules. JSONB seems to be about
+** 3x faster than text JSON as a result. JSONB is also tends to be slightly
+** smaller than text JSON, by 5% or 10%, but there are corner cases where
+** JSONB can be slightly larger. So you are not far mistaken to say that
+** a JSONB blob is the same size as the equivalent RFC-8259 text.
+**
+**
+** THE JSONB ENCODING:
+**
+** Every JSON element is encoded in JSONB as a header and a payload.
+** The header is between 1 and 9 bytes in size. The payload is zero
+** or more bytes.
+**
+** The lower 4 bits of the first byte of the header determines the
+** element type:
+**
+** 0: NULL
+** 1: TRUE
+** 2: FALSE
+** 3: INT -- RFC-8259 integer literal
+** 4: INT5 -- JSON5 integer literal
+** 5: FLOAT -- RFC-8259 floating point literal
+** 6: FLOAT5 -- JSON5 floating point literal
+** 7: TEXT -- Text literal acceptable to both SQL and JSON
+** 8: TEXTJ -- Text containing RFC-8259 escapes
+** 9: TEXT5 -- Text containing JSON5 and/or RFC-8259 escapes
+** 10: TEXTRAW -- Text containing unescaped syntax characters
+** 11: ARRAY
+** 12: OBJECT
+**
+** The other three possible values (13-15) are reserved for future
+** enhancements.
+**
+** The upper 4 bits of the first byte determine the size of the header
+** and sometimes also the size of the payload. If X is the first byte
+** of the element and if X>>4 is between 0 and 11, then the payload
+** will be that many bytes in size and the header is exactly one byte
+** in size. Other four values for X>>4 (12-15) indicate that the header
+** is more than one byte in size and that the payload size is determined
+** by the remainder of the header, interpreted as a unsigned big-endian
+** integer.
+**
+** Value of X>>4 Size integer Total header size
+** ------------- -------------------- -----------------
+** 12 1 byte (0-255) 2
+** 13 2 byte (0-65535) 3
+** 14 4 byte (0-4294967295) 5
+** 15 8 byte (0-1.8e19) 9
+**
+** The payload size need not be expressed in its minimal form. For example,
+** if the payload size is 10, the size can be expressed in any of 5 different
+** ways: (1) (X>>4)==10, (2) (X>>4)==12 following by on 0x0a byte,
+** (3) (X>>4)==13 followed by 0x00 and 0x0a, (4) (X>>4)==14 followed by
+** 0x00 0x00 0x00 0x0a, or (5) (X>>4)==15 followed by 7 bytes of 0x00 and
+** a single byte of 0x0a. The shorter forms are preferred, of course, but
+** sometimes when generating JSONB, the payload size is not known in advance
+** and it is convenient to reserve sufficient header space to cover the
+** largest possible payload size and then come back later and patch up
+** the size when it becomes known, resulting in a non-minimal encoding.
+**
+** The value (X>>4)==15 is not actually used in the current implementation
+** (as SQLite is currently unable handle BLOBs larger than about 2GB)
+** but is included in the design to allow for future enhancements.
+**
+** The payload follows the header. NULL, TRUE, and FALSE have no payload and
+** their payload size must always be zero. The payload for INT, INT5,
+** FLOAT, FLOAT5, TEXT, TEXTJ, TEXT5, and TEXTROW is text. Note that the
+** "..." or '...' delimiters are omitted from the various text encodings.
+** The payload for ARRAY and OBJECT is a list of additional elements that
+** are the content for the array or object. The payload for an OBJECT
+** must be an even number of elements. The first element of each pair is
+** the label and must be of type TEXT, TEXTJ, TEXT5, or TEXTRAW.
+**
+** A valid JSONB blob consists of a single element, as described above.
+** Usually this will be an ARRAY or OBJECT element which has many more
+** elements as its content. But the overall blob is just a single element.
+**
+** Input validation for JSONB blobs simply checks that the element type
+** code is between 0 and 12 and that the total size of the element
+** (header plus payload) is the same as the size of the BLOB. If those
+** checks are true, the BLOB is assumed to be JSONB and processing continues.
+** Errors are only raised if some other miscoding is discovered during
+** processing.
+**
+** Additional information can be found in the doc/jsonb.md file of the
+** canonical SQLite source tree.
*/
#ifndef SQLITE_OMIT_JSON
/* #include "sqliteInt.h" */
+/* JSONB element types
+*/
+#define JSONB_NULL 0 /* "null" */
+#define JSONB_TRUE 1 /* "true" */
+#define JSONB_FALSE 2 /* "false" */
+#define JSONB_INT 3 /* integer acceptable to JSON and SQL */
+#define JSONB_INT5 4 /* integer in 0x000 notation */
+#define JSONB_FLOAT 5 /* float acceptable to JSON and SQL */
+#define JSONB_FLOAT5 6 /* float with JSON5 extensions */
+#define JSONB_TEXT 7 /* Text compatible with both JSON and SQL */
+#define JSONB_TEXTJ 8 /* Text with JSON escapes */
+#define JSONB_TEXT5 9 /* Text with JSON-5 escape */
+#define JSONB_TEXTRAW 10 /* SQL text that needs escaping for JSON */
+#define JSONB_ARRAY 11 /* An array */
+#define JSONB_OBJECT 12 /* An object */
+
+/* Human-readable names for the JSONB values. The index for each
+** string must correspond to the JSONB_* integer above.
+*/
+static const char * const jsonbType[] = {
+ "null", "true", "false", "integer", "integer",
+ "real", "real", "text", "text", "text",
+ "text", "array", "object", "", "", "", ""
+};
+
/*
** Growing our own isspace() routine this way is twice as fast as
** the library isspace() function, resulting in a 7% overall performance
-** increase for the parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
+** increase for the text-JSON parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
*/
static const char jsonIsSpace[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
@@ -201282,11 +203347,19 @@ static const char jsonIsSpace[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
-#define fast_isspace(x) (jsonIsSpace[(unsigned char)x])
+#define jsonIsspace(x) (jsonIsSpace[(unsigned char)x])
/*
-** Characters that are special to JSON. Control charaters,
-** '"' and '\\'.
+** The set of all space characters recognized by jsonIsspace().
+** Useful as the second argument to strspn().
+*/
+static const char jsonSpaces[] = "\011\012\015\040";
+
+/*
+** Characters that are special to JSON. Control characters,
+** '"' and '\\' and '\''. Actually, '\'' is not special to
+** canonical JSON, but it is special in JSON-5, so we include
+** it in the set of special characters.
*/
static const char jsonIsOk[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -201308,22 +203381,49 @@ static const char jsonIsOk[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
};
-
-#if !defined(SQLITE_DEBUG) && !defined(SQLITE_COVERAGE_TEST)
-# define VVA(X)
-#else
-# define VVA(X) X
-#endif
-
/* Objects */
+typedef struct JsonCache JsonCache;
typedef struct JsonString JsonString;
-typedef struct JsonNode JsonNode;
typedef struct JsonParse JsonParse;
-typedef struct JsonCleanup JsonCleanup;
+
+/*
+** Magic number used for the JSON parse cache in sqlite3_get_auxdata()
+*/
+#define JSON_CACHE_ID (-429938) /* Cache entry */
+#define JSON_CACHE_SIZE 4 /* Max number of cache entries */
+
+/*
+** jsonUnescapeOneChar() returns this invalid code point if it encounters
+** a syntax error.
+*/
+#define JSON_INVALID_CHAR 0x99999
+
+/* A cache mapping JSON text into JSONB blobs.
+**
+** Each cache entry is a JsonParse object with the following restrictions:
+**
+** * The bReadOnly flag must be set
+**
+** * The aBlob[] array must be owned by the JsonParse object. In other
+** words, nBlobAlloc must be non-zero.
+**
+** * eEdit and delta must be zero.
+**
+** * zJson must be an RCStr. In other words bJsonIsRCStr must be true.
+*/
+struct JsonCache {
+ sqlite3 *db; /* Database connection */
+ int nUsed; /* Number of active entries in the cache */
+ JsonParse *a[JSON_CACHE_SIZE]; /* One line for each cache entry */
+};
/* An instance of this object represents a JSON string
** under construction. Really, this is a generic string accumulator
** that can be and is used to create strings other than JSON.
+**
+** If the generated string is longer than will fit into the zSpace[] buffer,
+** then it will be an RCStr string. This aids with caching of large
+** JSON strings.
*/
struct JsonString {
sqlite3_context *pCtx; /* Function context - put error messages here */
@@ -201331,121 +203431,75 @@ struct JsonString {
u64 nAlloc; /* Bytes of storage available in zBuf[] */
u64 nUsed; /* Bytes of zBuf[] currently used */
u8 bStatic; /* True if zBuf is static space */
- u8 bErr; /* True if an error has been encountered */
+ u8 eErr; /* True if an error has been encountered */
char zSpace[100]; /* Initial static space */
};
-/* A deferred cleanup task. A list of JsonCleanup objects might be
-** run when the JsonParse object is destroyed.
-*/
-struct JsonCleanup {
- JsonCleanup *pJCNext; /* Next in a list */
- void (*xOp)(void*); /* Routine to run */
- void *pArg; /* Argument to xOp() */
-};
+/* Allowed values for JsonString.eErr */
+#define JSTRING_OOM 0x01 /* Out of memory */
+#define JSTRING_MALFORMED 0x02 /* Malformed JSONB */
+#define JSTRING_ERR 0x04 /* Error already sent to sqlite3_result */
-/* JSON type values
+/* The "subtype" set for text JSON values passed through using
+** sqlite3_result_subtype() and sqlite3_value_subtype().
*/
-#define JSON_SUBST 0 /* Special edit node. Uses u.iPrev */
-#define JSON_NULL 1
-#define JSON_TRUE 2
-#define JSON_FALSE 3
-#define JSON_INT 4
-#define JSON_REAL 5
-#define JSON_STRING 6
-#define JSON_ARRAY 7
-#define JSON_OBJECT 8
-
-/* The "subtype" set for JSON values */
#define JSON_SUBTYPE 74 /* Ascii for "J" */
/*
-** Names of the various JSON types:
-*/
-static const char * const jsonType[] = {
- "subst",
- "null", "true", "false", "integer", "real", "text", "array", "object"
-};
-
-/* Bit values for the JsonNode.jnFlag field
+** Bit values for the flags passed into various SQL function implementations
+** via the sqlite3_user_data() value.
*/
-#define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */
-#define JNODE_ESCAPE 0x02 /* Content is text with \ escapes */
-#define JNODE_REMOVE 0x04 /* Do not output */
-#define JNODE_REPLACE 0x08 /* Target of a JSON_SUBST node */
-#define JNODE_APPEND 0x10 /* More ARRAY/OBJECT entries at u.iAppend */
-#define JNODE_LABEL 0x20 /* Is a label of an object */
-#define JNODE_JSON5 0x40 /* Node contains JSON5 enhancements */
-
-
-/* A single node of parsed JSON. An array of these nodes describes
-** a parse of JSON + edits.
-**
-** Use the json_parse() SQL function (available when compiled with
-** -DSQLITE_DEBUG) to see a dump of complete JsonParse objects, including
-** a complete listing and decoding of the array of JsonNodes.
-*/
-struct JsonNode {
- u8 eType; /* One of the JSON_ type values */
- u8 jnFlags; /* JNODE flags */
- u8 eU; /* Which union element to use */
- u32 n; /* Bytes of content for INT, REAL or STRING
- ** Number of sub-nodes for ARRAY and OBJECT
- ** Node that SUBST applies to */
- union {
- const char *zJContent; /* 1: Content for INT, REAL, and STRING */
- u32 iAppend; /* 2: More terms for ARRAY and OBJECT */
- u32 iKey; /* 3: Key for ARRAY objects in json_tree() */
- u32 iPrev; /* 4: Previous SUBST node, or 0 */
- } u;
-};
+#define JSON_JSON 0x01 /* Result is always JSON */
+#define JSON_SQL 0x02 /* Result is always SQL */
+#define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */
+#define JSON_ISSET 0x04 /* json_set(), not json_insert() */
+#define JSON_BLOB 0x08 /* Use the BLOB output format */
-/* A parsed and possibly edited JSON string. Lifecycle:
-**
-** 1. JSON comes in and is parsed into an array aNode[]. The original
-** JSON text is stored in zJson.
+/* A parsed JSON value. Lifecycle:
**
-** 2. Zero or more changes are made (via json_remove() or json_replace()
-** or similar) to the aNode[] array.
+** 1. JSON comes in and is parsed into a JSONB value in aBlob. The
+** original text is stored in zJson. This step is skipped if the
+** input is JSONB instead of text JSON.
**
-** 3. A new, edited and mimified JSON string is generated from aNode
-** and stored in zAlt. The JsonParse object always owns zAlt.
+** 2. The aBlob[] array is searched using the JSON path notation, if needed.
**
-** Step 1 always happens. Step 2 and 3 may or may not happen, depending
-** on the operation.
+** 3. Zero or more changes are made to aBlob[] (via json_remove() or
+** json_replace() or json_patch() or similar).
**
-** aNode[].u.zJContent entries typically point into zJson. Hence zJson
-** must remain valid for the lifespan of the parse. For edits,
-** aNode[].u.zJContent might point to malloced space other than zJson.
-** Entries in pClup are responsible for freeing that extra malloced space.
-**
-** When walking the parse tree in aNode[], edits are ignored if useMod is
-** false.
+** 4. New JSON text is generated from the aBlob[] for output. This step
+** is skipped if the function is one of the jsonb_* functions that
+** returns JSONB instead of text JSON.
*/
struct JsonParse {
- u32 nNode; /* Number of slots of aNode[] used */
- u32 nAlloc; /* Number of slots of aNode[] allocated */
- JsonNode *aNode; /* Array of nodes containing the parse */
- char *zJson; /* Original JSON string (before edits) */
- char *zAlt; /* Revised and/or mimified JSON */
- u32 *aUp; /* Index of parent of each node */
- JsonCleanup *pClup;/* Cleanup operations prior to freeing this object */
+ u8 *aBlob; /* JSONB representation of JSON value */
+ u32 nBlob; /* Bytes of aBlob[] actually used */
+ u32 nBlobAlloc; /* Bytes allocated to aBlob[]. 0 if aBlob is external */
+ char *zJson; /* Json text used for parsing */
+ sqlite3 *db; /* The database connection to which this object belongs */
+ int nJson; /* Length of the zJson string in bytes */
+ u32 nJPRef; /* Number of references to this object */
+ u32 iErr; /* Error location in zJson[] */
u16 iDepth; /* Nesting depth */
u8 nErr; /* Number of errors seen */
u8 oom; /* Set to true if out of memory */
u8 bJsonIsRCStr; /* True if zJson is an RCStr */
u8 hasNonstd; /* True if input uses non-standard features like JSON5 */
- u8 useMod; /* Actually use the edits contain inside aNode */
- u8 hasMod; /* aNode contains edits from the original zJson */
- u32 nJPRef; /* Number of references to this object */
- int nJson; /* Length of the zJson string in bytes */
- int nAlt; /* Length of alternative JSON string zAlt, in bytes */
- u32 iErr; /* Error location in zJson[] */
- u32 iSubst; /* Last JSON_SUBST entry in aNode[] */
- u32 iHold; /* Age of this entry in the cache for LRU replacement */
+ u8 bReadOnly; /* Do not modify. */
+ /* Search and edit information. See jsonLookupStep() */
+ u8 eEdit; /* Edit operation to apply */
+ int delta; /* Size change due to the edit */
+ u32 nIns; /* Number of bytes to insert */
+ u32 iLabel; /* Location of label if search landed on an object value */
+ u8 *aIns; /* Content to be inserted */
};
+/* Allowed values for JsonParse.eEdit */
+#define JEDIT_DEL 1 /* Delete if exists */
+#define JEDIT_REPL 2 /* Overwrite if exists */
+#define JEDIT_INS 3 /* Insert if not exists */
+#define JEDIT_SET 4 /* Insert or overwrite */
+
/*
** Maximum nesting depth of JSON for this implementation.
**
@@ -201453,15 +203507,151 @@ struct JsonParse {
** descent parser. A depth of 1000 is far deeper than any sane JSON
** should go. Historical note: This limit was 2000 prior to version 3.42.0
*/
-#define JSON_MAX_DEPTH 1000
+#ifndef SQLITE_JSON_MAX_DEPTH
+# define JSON_MAX_DEPTH 1000
+#else
+# define JSON_MAX_DEPTH SQLITE_JSON_MAX_DEPTH
+#endif
+
+/*
+** Allowed values for the flgs argument to jsonParseFuncArg();
+*/
+#define JSON_EDITABLE 0x01 /* Generate a writable JsonParse object */
+#define JSON_KEEPERROR 0x02 /* Return non-NULL even if there is an error */
+
+/**************************************************************************
+** Forward references
+**************************************************************************/
+static void jsonReturnStringAsBlob(JsonString*);
+static int jsonFuncArgMightBeBinary(sqlite3_value *pJson);
+static u32 jsonTranslateBlobToText(const JsonParse*,u32,JsonString*);
+static void jsonReturnParse(sqlite3_context*,JsonParse*);
+static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32);
+static void jsonParseFree(JsonParse*);
+static u32 jsonbPayloadSize(const JsonParse*, u32, u32*);
+static u32 jsonUnescapeOneChar(const char*, u32, u32*);
+
+/**************************************************************************
+** Utility routines for dealing with JsonCache objects
+**************************************************************************/
+
+/*
+** Free a JsonCache object.
+*/
+static void jsonCacheDelete(JsonCache *p){
+ int i;
+ for(i=0; i<p->nUsed; i++){
+ jsonParseFree(p->a[i]);
+ }
+ sqlite3DbFree(p->db, p);
+}
+static void jsonCacheDeleteGeneric(void *p){
+ jsonCacheDelete((JsonCache*)p);
+}
+
+/*
+** Insert a new entry into the cache. If the cache is full, expel
+** the least recently used entry. Return SQLITE_OK on success or a
+** result code otherwise.
+**
+** Cache entries are stored in age order, oldest first.
+*/
+static int jsonCacheInsert(
+ sqlite3_context *ctx, /* The SQL statement context holding the cache */
+ JsonParse *pParse /* The parse object to be added to the cache */
+){
+ JsonCache *p;
+
+ assert( pParse->zJson!=0 );
+ assert( pParse->bJsonIsRCStr );
+ assert( pParse->delta==0 );
+ p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID);
+ if( p==0 ){
+ sqlite3 *db = sqlite3_context_db_handle(ctx);
+ p = sqlite3DbMallocZero(db, sizeof(*p));
+ if( p==0 ) return SQLITE_NOMEM;
+ p->db = db;
+ sqlite3_set_auxdata(ctx, JSON_CACHE_ID, p, jsonCacheDeleteGeneric);
+ p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID);
+ if( p==0 ) return SQLITE_NOMEM;
+ }
+ if( p->nUsed >= JSON_CACHE_SIZE ){
+ jsonParseFree(p->a[0]);
+ memmove(p->a, &p->a[1], (JSON_CACHE_SIZE-1)*sizeof(p->a[0]));
+ p->nUsed = JSON_CACHE_SIZE-1;
+ }
+ assert( pParse->nBlobAlloc>0 );
+ pParse->eEdit = 0;
+ pParse->nJPRef++;
+ pParse->bReadOnly = 1;
+ p->a[p->nUsed] = pParse;
+ p->nUsed++;
+ return SQLITE_OK;
+}
+
+/*
+** Search for a cached translation the json text supplied by pArg. Return
+** the JsonParse object if found. Return NULL if not found.
+**
+** When a match if found, the matching entry is moved to become the
+** most-recently used entry if it isn't so already.
+**
+** The JsonParse object returned still belongs to the Cache and might
+** be deleted at any moment. If the caller whants the JsonParse to
+** linger, it needs to increment the nPJRef reference counter.
+*/
+static JsonParse *jsonCacheSearch(
+ sqlite3_context *ctx, /* The SQL statement context holding the cache */
+ sqlite3_value *pArg /* Function argument containing SQL text */
+){
+ JsonCache *p;
+ int i;
+ const char *zJson;
+ int nJson;
+
+ if( sqlite3_value_type(pArg)!=SQLITE_TEXT ){
+ return 0;
+ }
+ zJson = (const char*)sqlite3_value_text(pArg);
+ if( zJson==0 ) return 0;
+ nJson = sqlite3_value_bytes(pArg);
+
+ p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID);
+ if( p==0 ){
+ return 0;
+ }
+ for(i=0; i<p->nUsed; i++){
+ if( p->a[i]->zJson==zJson ) break;
+ }
+ if( i>=p->nUsed ){
+ for(i=0; i<p->nUsed; i++){
+ if( p->a[i]->nJson!=nJson ) continue;
+ if( memcmp(p->a[i]->zJson, zJson, nJson)==0 ) break;
+ }
+ }
+ if( i<p->nUsed ){
+ if( i<p->nUsed-1 ){
+ /* Make the matching entry the most recently used entry */
+ JsonParse *tmp = p->a[i];
+ memmove(&p->a[i], &p->a[i+1], (p->nUsed-i-1)*sizeof(tmp));
+ p->a[p->nUsed-1] = tmp;
+ i = p->nUsed - 1;
+ }
+ assert( p->a[i]->delta==0 );
+ return p->a[i];
+ }else{
+ return 0;
+ }
+}
/**************************************************************************
** Utility routines for dealing with JsonString objects
**************************************************************************/
-/* Set the JsonString object to an empty string
+/* Turn uninitialized bulk memory into a valid JsonString object
+** holding a zero-length string.
*/
-static void jsonZero(JsonString *p){
+static void jsonStringZero(JsonString *p){
p->zBuf = p->zSpace;
p->nAlloc = sizeof(p->zSpace);
p->nUsed = 0;
@@ -201470,39 +203660,39 @@ static void jsonZero(JsonString *p){
/* Initialize the JsonString object
*/
-static void jsonInit(JsonString *p, sqlite3_context *pCtx){
+static void jsonStringInit(JsonString *p, sqlite3_context *pCtx){
p->pCtx = pCtx;
- p->bErr = 0;
- jsonZero(p);
+ p->eErr = 0;
+ jsonStringZero(p);
}
/* Free all allocated memory and reset the JsonString object back to its
** initial state.
*/
-static void jsonReset(JsonString *p){
+static void jsonStringReset(JsonString *p){
if( !p->bStatic ) sqlite3RCStrUnref(p->zBuf);
- jsonZero(p);
+ jsonStringZero(p);
}
/* Report an out-of-memory (OOM) condition
*/
-static void jsonOom(JsonString *p){
- p->bErr = 1;
- sqlite3_result_error_nomem(p->pCtx);
- jsonReset(p);
+static void jsonStringOom(JsonString *p){
+ p->eErr |= JSTRING_OOM;
+ if( p->pCtx ) sqlite3_result_error_nomem(p->pCtx);
+ jsonStringReset(p);
}
/* Enlarge pJson->zBuf so that it can hold at least N more bytes.
** Return zero on success. Return non-zero on an OOM error
*/
-static int jsonGrow(JsonString *p, u32 N){
+static int jsonStringGrow(JsonString *p, u32 N){
u64 nTotal = N<p->nAlloc ? p->nAlloc*2 : p->nAlloc+N+10;
char *zNew;
if( p->bStatic ){
- if( p->bErr ) return 1;
+ if( p->eErr ) return 1;
zNew = sqlite3RCStrNew(nTotal);
if( zNew==0 ){
- jsonOom(p);
+ jsonStringOom(p);
return SQLITE_NOMEM;
}
memcpy(zNew, p->zBuf, (size_t)p->nUsed);
@@ -201511,8 +203701,8 @@ static int jsonGrow(JsonString *p, u32 N){
}else{
p->zBuf = sqlite3RCStrResize(p->zBuf, nTotal);
if( p->zBuf==0 ){
- p->bErr = 1;
- jsonZero(p);
+ p->eErr |= JSTRING_OOM;
+ jsonStringZero(p);
return SQLITE_NOMEM;
}
}
@@ -201522,20 +203712,20 @@ static int jsonGrow(JsonString *p, u32 N){
/* Append N bytes from zIn onto the end of the JsonString string.
*/
-static SQLITE_NOINLINE void jsonAppendExpand(
+static SQLITE_NOINLINE void jsonStringExpandAndAppend(
JsonString *p,
const char *zIn,
u32 N
){
assert( N>0 );
- if( jsonGrow(p,N) ) return;
+ if( jsonStringGrow(p,N) ) return;
memcpy(p->zBuf+p->nUsed, zIn, N);
p->nUsed += N;
}
static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){
if( N==0 ) return;
if( N+p->nUsed >= p->nAlloc ){
- jsonAppendExpand(p,zIn,N);
+ jsonStringExpandAndAppend(p,zIn,N);
}else{
memcpy(p->zBuf+p->nUsed, zIn, N);
p->nUsed += N;
@@ -201544,7 +203734,7 @@ static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){
static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){
assert( N>0 );
if( N+p->nUsed >= p->nAlloc ){
- jsonAppendExpand(p,zIn,N);
+ jsonStringExpandAndAppend(p,zIn,N);
}else{
memcpy(p->zBuf+p->nUsed, zIn, N);
p->nUsed += N;
@@ -201556,7 +203746,7 @@ static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){
*/
static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){
va_list ap;
- if( (p->nUsed + N >= p->nAlloc) && jsonGrow(p, N) ) return;
+ if( (p->nUsed + N >= p->nAlloc) && jsonStringGrow(p, N) ) return;
va_start(ap, zFormat);
sqlite3_vsnprintf(N, p->zBuf+p->nUsed, zFormat, ap);
va_end(ap);
@@ -201566,7 +203756,7 @@ static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){
/* Append a single character
*/
static SQLITE_NOINLINE void jsonAppendCharExpand(JsonString *p, char c){
- if( jsonGrow(p,1) ) return;
+ if( jsonStringGrow(p,1) ) return;
p->zBuf[p->nUsed++] = c;
}
static void jsonAppendChar(JsonString *p, char c){
@@ -201577,24 +203767,27 @@ static void jsonAppendChar(JsonString *p, char c){
}
}
-/* Try to force the string to be a zero-terminated RCStr string.
+/* Remove a single character from the end of the string
+*/
+static void jsonStringTrimOneChar(JsonString *p){
+ if( p->eErr==0 ){
+ assert( p->nUsed>0 );
+ p->nUsed--;
+ }
+}
+
+
+/* Make sure there is a zero terminator on p->zBuf[]
**
** Return true on success. Return false if an OOM prevents this
** from happening.
*/
-static int jsonForceRCStr(JsonString *p){
+static int jsonStringTerminate(JsonString *p){
jsonAppendChar(p, 0);
- if( p->bErr ) return 0;
- p->nUsed--;
- if( p->bStatic==0 ) return 1;
- p->nAlloc = 0;
- p->nUsed++;
- jsonGrow(p, p->nUsed);
- p->nUsed--;
- return p->bStatic==0;
+ jsonStringTrimOneChar(p);
+ return p->eErr==0;
}
-
/* Append a comma separator to the output buffer, if the previous
** character is not '[' or '{'.
*/
@@ -201607,21 +203800,66 @@ static void jsonAppendSeparator(JsonString *p){
}
/* Append the N-byte string in zIn to the end of the JsonString string
-** under construction. Enclose the string in "..." and escape
-** any double-quotes or backslash characters contained within the
+** under construction. Enclose the string in double-quotes ("...") and
+** escape any double-quotes or backslash characters contained within the
** string.
+**
+** This routine is a high-runner. There is a measurable performance
+** increase associated with unwinding the jsonIsOk[] loop.
*/
static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
- u32 i;
- if( zIn==0 || ((N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0) ) return;
+ u32 k;
+ u8 c;
+ const u8 *z = (const u8*)zIn;
+ if( z==0 ) return;
+ if( (N+p->nUsed+2 >= p->nAlloc) && jsonStringGrow(p,N+2)!=0 ) return;
p->zBuf[p->nUsed++] = '"';
- for(i=0; i<N; i++){
- unsigned char c = ((unsigned const char*)zIn)[i];
- if( jsonIsOk[c] ){
- p->zBuf[p->nUsed++] = c;
- }else if( c=='"' || c=='\\' ){
+ while( 1 /*exit-by-break*/ ){
+ k = 0;
+ /* The following while() is the 4-way unwound equivalent of
+ **
+ ** while( k<N && jsonIsOk[z[k]] ){ k++; }
+ */
+ while( 1 /* Exit by break */ ){
+ if( k+3>=N ){
+ while( k<N && jsonIsOk[z[k]] ){ k++; }
+ break;
+ }
+ if( !jsonIsOk[z[k]] ){
+ break;
+ }
+ if( !jsonIsOk[z[k+1]] ){
+ k += 1;
+ break;
+ }
+ if( !jsonIsOk[z[k+2]] ){
+ k += 2;
+ break;
+ }
+ if( !jsonIsOk[z[k+3]] ){
+ k += 3;
+ break;
+ }else{
+ k += 4;
+ }
+ }
+ if( k>=N ){
+ if( k>0 ){
+ memcpy(&p->zBuf[p->nUsed], z, k);
+ p->nUsed += k;
+ }
+ break;
+ }
+ if( k>0 ){
+ memcpy(&p->zBuf[p->nUsed], z, k);
+ p->nUsed += k;
+ z += k;
+ N -= k;
+ }
+ c = z[0];
+ if( c=='"' || c=='\\' ){
json_simple_escape:
- if( (p->nUsed+N+3-i > p->nAlloc) && jsonGrow(p,N+3-i)!=0 ) return;
+ if( (p->nUsed+N+3 > p->nAlloc) && jsonStringGrow(p,N+3)!=0 ) return;
p->zBuf[p->nUsed++] = '\\';
p->zBuf[p->nUsed++] = c;
}else if( c=='\'' ){
@@ -201642,7 +203880,7 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
c = aSpecial[c];
goto json_simple_escape;
}
- if( (p->nUsed+N+7+i > p->nAlloc) && jsonGrow(p,N+7-i)!=0 ) return;
+ if( (p->nUsed+N+7 > p->nAlloc) && jsonStringGrow(p,N+7)!=0 ) return;
p->zBuf[p->nUsed++] = '\\';
p->zBuf[p->nUsed++] = 'u';
p->zBuf[p->nUsed++] = '0';
@@ -201650,140 +203888,18 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
p->zBuf[p->nUsed++] = "0123456789abcdef"[c>>4];
p->zBuf[p->nUsed++] = "0123456789abcdef"[c&0xf];
}
+ z++;
+ N--;
}
p->zBuf[p->nUsed++] = '"';
assert( p->nUsed<p->nAlloc );
}
/*
-** The zIn[0..N] string is a JSON5 string literal. Append to p a translation
-** of the string literal that standard JSON and that omits all JSON5
-** features.
-*/
-static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){
- u32 i;
- jsonAppendChar(p, '"');
- zIn++;
- N -= 2;
- while( N>0 ){
- for(i=0; i<N && zIn[i]!='\\'; i++){}
- if( i>0 ){
- jsonAppendRawNZ(p, zIn, i);
- zIn += i;
- N -= i;
- if( N==0 ) break;
- }
- assert( zIn[0]=='\\' );
- switch( (u8)zIn[1] ){
- case '\'':
- jsonAppendChar(p, '\'');
- break;
- case 'v':
- jsonAppendRawNZ(p, "\\u0009", 6);
- break;
- case 'x':
- jsonAppendRawNZ(p, "\\u00", 4);
- jsonAppendRawNZ(p, &zIn[2], 2);
- zIn += 2;
- N -= 2;
- break;
- case '0':
- jsonAppendRawNZ(p, "\\u0000", 6);
- break;
- case '\r':
- if( zIn[2]=='\n' ){
- zIn++;
- N--;
- }
- break;
- case '\n':
- break;
- case 0xe2:
- assert( N>=4 );
- assert( 0x80==(u8)zIn[2] );
- assert( 0xa8==(u8)zIn[3] || 0xa9==(u8)zIn[3] );
- zIn += 2;
- N -= 2;
- break;
- default:
- jsonAppendRawNZ(p, zIn, 2);
- break;
- }
- zIn += 2;
- N -= 2;
- }
- jsonAppendChar(p, '"');
-}
-
-/*
-** The zIn[0..N] string is a JSON5 integer literal. Append to p a translation
-** of the string literal that standard JSON and that omits all JSON5
-** features.
-*/
-static void jsonAppendNormalizedInt(JsonString *p, const char *zIn, u32 N){
- if( zIn[0]=='+' ){
- zIn++;
- N--;
- }else if( zIn[0]=='-' ){
- jsonAppendChar(p, '-');
- zIn++;
- N--;
- }
- if( zIn[0]=='0' && (zIn[1]=='x' || zIn[1]=='X') ){
- sqlite3_int64 i = 0;
- int rc = sqlite3DecOrHexToI64(zIn, &i);
- if( rc<=1 ){
- jsonPrintf(100,p,"%lld",i);
- }else{
- assert( rc==2 );
- jsonAppendRawNZ(p, "9.0e999", 7);
- }
- return;
- }
- assert( N>0 );
- jsonAppendRawNZ(p, zIn, N);
-}
-
-/*
-** The zIn[0..N] string is a JSON5 real literal. Append to p a translation
-** of the string literal that standard JSON and that omits all JSON5
-** features.
-*/
-static void jsonAppendNormalizedReal(JsonString *p, const char *zIn, u32 N){
- u32 i;
- if( zIn[0]=='+' ){
- zIn++;
- N--;
- }else if( zIn[0]=='-' ){
- jsonAppendChar(p, '-');
- zIn++;
- N--;
- }
- if( zIn[0]=='.' ){
- jsonAppendChar(p, '0');
- }
- for(i=0; i<N; i++){
- if( zIn[i]=='.' && (i+1==N || !sqlite3Isdigit(zIn[i+1])) ){
- i++;
- jsonAppendRaw(p, zIn, i);
- zIn += i;
- N -= i;
- jsonAppendChar(p, '0');
- break;
- }
- }
- if( N>0 ){
- jsonAppendRawNZ(p, zIn, N);
- }
-}
-
-
-
-/*
-** Append a function parameter value to the JSON string under
-** construction.
+** Append an sqlite3_value (such as a function parameter) to the JSON
+** string under construction in p.
*/
-static void jsonAppendValue(
+static void jsonAppendSqlValue(
JsonString *p, /* Append to this JSON string */
sqlite3_value *pValue /* Value to append */
){
@@ -201813,290 +203929,127 @@ static void jsonAppendValue(
break;
}
default: {
- if( p->bErr==0 ){
+ if( jsonFuncArgMightBeBinary(pValue) ){
+ JsonParse px;
+ memset(&px, 0, sizeof(px));
+ px.aBlob = (u8*)sqlite3_value_blob(pValue);
+ px.nBlob = sqlite3_value_bytes(pValue);
+ jsonTranslateBlobToText(&px, 0, p);
+ }else if( p->eErr==0 ){
sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1);
- p->bErr = 2;
- jsonReset(p);
+ p->eErr = JSTRING_ERR;
+ jsonStringReset(p);
}
break;
}
}
}
-
-/* Make the JSON in p the result of the SQL function.
+/* Make the text in p (which is probably a generated JSON text string)
+** the result of the SQL function.
**
-** The JSON string is reset.
+** The JsonString is reset.
+**
+** If pParse and ctx are both non-NULL, then the SQL string in p is
+** loaded into the zJson field of the pParse object as a RCStr and the
+** pParse is added to the cache.
*/
-static void jsonResult(JsonString *p){
- if( p->bErr==0 ){
- if( p->bStatic ){
+static void jsonReturnString(
+ JsonString *p, /* String to return */
+ JsonParse *pParse, /* JSONB source or NULL */
+ sqlite3_context *ctx /* Where to cache */
+){
+ assert( (pParse!=0)==(ctx!=0) );
+ assert( ctx==0 || ctx==p->pCtx );
+ if( p->eErr==0 ){
+ int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(p->pCtx));
+ if( flags & JSON_BLOB ){
+ jsonReturnStringAsBlob(p);
+ }else if( p->bStatic ){
sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed,
SQLITE_TRANSIENT, SQLITE_UTF8);
- }else if( jsonForceRCStr(p) ){
- sqlite3RCStrRef(p->zBuf);
- sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed,
- (void(*)(void*))sqlite3RCStrUnref,
+ }else if( jsonStringTerminate(p) ){
+ if( pParse && pParse->bJsonIsRCStr==0 && pParse->nBlobAlloc>0 ){
+ int rc;
+ pParse->zJson = sqlite3RCStrRef(p->zBuf);
+ pParse->nJson = p->nUsed;
+ pParse->bJsonIsRCStr = 1;
+ rc = jsonCacheInsert(ctx, pParse);
+ if( rc==SQLITE_NOMEM ){
+ sqlite3_result_error_nomem(ctx);
+ jsonStringReset(p);
+ return;
+ }
+ }
+ sqlite3_result_text64(p->pCtx, sqlite3RCStrRef(p->zBuf), p->nUsed,
+ sqlite3RCStrUnref,
SQLITE_UTF8);
+ }else{
+ sqlite3_result_error_nomem(p->pCtx);
}
- }
- if( p->bErr==1 ){
+ }else if( p->eErr & JSTRING_OOM ){
sqlite3_result_error_nomem(p->pCtx);
+ }else if( p->eErr & JSTRING_MALFORMED ){
+ sqlite3_result_error(p->pCtx, "malformed JSON", -1);
}
- jsonReset(p);
+ jsonStringReset(p);
}
/**************************************************************************
-** Utility routines for dealing with JsonNode and JsonParse objects
+** Utility routines for dealing with JsonParse objects
**************************************************************************/
/*
-** Return the number of consecutive JsonNode slots need to represent
-** the parsed JSON at pNode. The minimum answer is 1. For ARRAY and
-** OBJECT types, the number might be larger.
-**
-** Appended elements are not counted. The value returned is the number
-** by which the JsonNode counter should increment in order to go to the
-** next peer value.
-*/
-static u32 jsonNodeSize(JsonNode *pNode){
- return pNode->eType>=JSON_ARRAY ? pNode->n+1 : 1;
-}
-
-/*
** Reclaim all memory allocated by a JsonParse object. But do not
** delete the JsonParse object itself.
*/
static void jsonParseReset(JsonParse *pParse){
- while( pParse->pClup ){
- JsonCleanup *pTask = pParse->pClup;
- pParse->pClup = pTask->pJCNext;
- pTask->xOp(pTask->pArg);
- sqlite3_free(pTask);
- }
assert( pParse->nJPRef<=1 );
- if( pParse->aNode ){
- sqlite3_free(pParse->aNode);
- pParse->aNode = 0;
- }
- pParse->nNode = 0;
- pParse->nAlloc = 0;
- if( pParse->aUp ){
- sqlite3_free(pParse->aUp);
- pParse->aUp = 0;
- }
if( pParse->bJsonIsRCStr ){
sqlite3RCStrUnref(pParse->zJson);
pParse->zJson = 0;
+ pParse->nJson = 0;
pParse->bJsonIsRCStr = 0;
}
- if( pParse->zAlt ){
- sqlite3RCStrUnref(pParse->zAlt);
- pParse->zAlt = 0;
+ if( pParse->nBlobAlloc ){
+ sqlite3DbFree(pParse->db, pParse->aBlob);
+ pParse->aBlob = 0;
+ pParse->nBlob = 0;
+ pParse->nBlobAlloc = 0;
}
}
/*
-** Free a JsonParse object that was obtained from sqlite3_malloc().
-**
-** Note that destroying JsonParse might call sqlite3RCStrUnref() to
-** destroy the zJson value. The RCStr object might recursively invoke
-** JsonParse to destroy this pParse object again. Take care to ensure
-** that this recursive destructor sequence terminates harmlessly.
+** Decrement the reference count on the JsonParse object. When the
+** count reaches zero, free the object.
*/
static void jsonParseFree(JsonParse *pParse){
- if( pParse->nJPRef>1 ){
- pParse->nJPRef--;
- }else{
- jsonParseReset(pParse);
- sqlite3_free(pParse);
- }
-}
-
-/*
-** Add a cleanup task to the JsonParse object.
-**
-** If an OOM occurs, the cleanup operation happens immediately
-** and this function returns SQLITE_NOMEM.
-*/
-static int jsonParseAddCleanup(
- JsonParse *pParse, /* Add the cleanup task to this parser */
- void(*xOp)(void*), /* The cleanup task */
- void *pArg /* Argument to the cleanup */
-){
- JsonCleanup *pTask = sqlite3_malloc64( sizeof(*pTask) );
- if( pTask==0 ){
- pParse->oom = 1;
- xOp(pArg);
- return SQLITE_ERROR;
- }
- pTask->pJCNext = pParse->pClup;
- pParse->pClup = pTask;
- pTask->xOp = xOp;
- pTask->pArg = pArg;
- return SQLITE_OK;
-}
-
-/*
-** Convert the JsonNode pNode into a pure JSON string and
-** append to pOut. Subsubstructure is also included. Return
-** the number of JsonNode objects that are encoded.
-*/
-static void jsonRenderNode(
- JsonParse *pParse, /* the complete parse of the JSON */
- JsonNode *pNode, /* The node to render */
- JsonString *pOut /* Write JSON here */
-){
- assert( pNode!=0 );
- while( (pNode->jnFlags & JNODE_REPLACE)!=0 && pParse->useMod ){
- u32 idx = (u32)(pNode - pParse->aNode);
- u32 i = pParse->iSubst;
- while( 1 /*exit-by-break*/ ){
- assert( i<pParse->nNode );
- assert( pParse->aNode[i].eType==JSON_SUBST );
- assert( pParse->aNode[i].eU==4 );
- assert( pParse->aNode[i].u.iPrev<i );
- if( pParse->aNode[i].n==idx ){
- pNode = &pParse->aNode[i+1];
- break;
- }
- i = pParse->aNode[i].u.iPrev;
- }
- }
- switch( pNode->eType ){
- default: {
- assert( pNode->eType==JSON_NULL );
- jsonAppendRawNZ(pOut, "null", 4);
- break;
- }
- case JSON_TRUE: {
- jsonAppendRawNZ(pOut, "true", 4);
- break;
- }
- case JSON_FALSE: {
- jsonAppendRawNZ(pOut, "false", 5);
- break;
- }
- case JSON_STRING: {
- assert( pNode->eU==1 );
- if( pNode->jnFlags & JNODE_RAW ){
- if( pNode->jnFlags & JNODE_LABEL ){
- jsonAppendChar(pOut, '"');
- jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n);
- jsonAppendChar(pOut, '"');
- }else{
- jsonAppendString(pOut, pNode->u.zJContent, pNode->n);
- }
- }else if( pNode->jnFlags & JNODE_JSON5 ){
- jsonAppendNormalizedString(pOut, pNode->u.zJContent, pNode->n);
- }else{
- assert( pNode->n>0 );
- jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n);
- }
- break;
- }
- case JSON_REAL: {
- assert( pNode->eU==1 );
- if( pNode->jnFlags & JNODE_JSON5 ){
- jsonAppendNormalizedReal(pOut, pNode->u.zJContent, pNode->n);
- }else{
- assert( pNode->n>0 );
- jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n);
- }
- break;
- }
- case JSON_INT: {
- assert( pNode->eU==1 );
- if( pNode->jnFlags & JNODE_JSON5 ){
- jsonAppendNormalizedInt(pOut, pNode->u.zJContent, pNode->n);
- }else{
- assert( pNode->n>0 );
- jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n);
- }
- break;
- }
- case JSON_ARRAY: {
- u32 j = 1;
- jsonAppendChar(pOut, '[');
- for(;;){
- while( j<=pNode->n ){
- if( (pNode[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){
- jsonAppendSeparator(pOut);
- jsonRenderNode(pParse, &pNode[j], pOut);
- }
- j += jsonNodeSize(&pNode[j]);
- }
- if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
- if( pParse->useMod==0 ) break;
- assert( pNode->eU==2 );
- pNode = &pParse->aNode[pNode->u.iAppend];
- j = 1;
- }
- jsonAppendChar(pOut, ']');
- break;
- }
- case JSON_OBJECT: {
- u32 j = 1;
- jsonAppendChar(pOut, '{');
- for(;;){
- while( j<=pNode->n ){
- if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){
- jsonAppendSeparator(pOut);
- jsonRenderNode(pParse, &pNode[j], pOut);
- jsonAppendChar(pOut, ':');
- jsonRenderNode(pParse, &pNode[j+1], pOut);
- }
- j += 1 + jsonNodeSize(&pNode[j+1]);
- }
- if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
- if( pParse->useMod==0 ) break;
- assert( pNode->eU==2 );
- pNode = &pParse->aNode[pNode->u.iAppend];
- j = 1;
- }
- jsonAppendChar(pOut, '}');
- break;
+ if( pParse ){
+ if( pParse->nJPRef>1 ){
+ pParse->nJPRef--;
+ }else{
+ jsonParseReset(pParse);
+ sqlite3DbFree(pParse->db, pParse);
}
}
}
-/*
-** Return a JsonNode and all its descendants as a JSON string.
-*/
-static void jsonReturnJson(
- JsonParse *pParse, /* The complete JSON */
- JsonNode *pNode, /* Node to return */
- sqlite3_context *pCtx, /* Return value for this function */
- int bGenerateAlt /* Also store the rendered text in zAlt */
-){
- JsonString s;
- if( pParse->oom ){
- sqlite3_result_error_nomem(pCtx);
- return;
- }
- if( pParse->nErr==0 ){
- jsonInit(&s, pCtx);
- jsonRenderNode(pParse, pNode, &s);
- if( bGenerateAlt && pParse->zAlt==0 && jsonForceRCStr(&s) ){
- pParse->zAlt = sqlite3RCStrRef(s.zBuf);
- pParse->nAlt = s.nUsed;
- }
- jsonResult(&s);
- sqlite3_result_subtype(pCtx, JSON_SUBTYPE);
- }
-}
+/**************************************************************************
+** Utility routines for the JSON text parser
+**************************************************************************/
/*
** Translate a single byte of Hex into an integer.
-** This routine only works if h really is a valid hexadecimal
-** character: 0..9a..fA..F
+** This routine only gives a correct answer if h really is a valid hexadecimal
+** character: 0..9a..fA..F. But unlike sqlite3HexToInt(), it does not
+** assert() if the digit is not hex.
*/
static u8 jsonHexToInt(int h){
- assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') );
+#ifdef SQLITE_ASCII
+ h += 9*(1&(h>>6));
+#endif
#ifdef SQLITE_EBCDIC
h += 9*(1&~(h>>4));
-#else
- h += 9*(1&(h>>6));
#endif
return (u8)(h & 0xf);
}
@@ -202106,10 +204059,6 @@ static u8 jsonHexToInt(int h){
*/
static u32 jsonHexToInt4(const char *z){
u32 v;
- assert( sqlite3Isxdigit(z[0]) );
- assert( sqlite3Isxdigit(z[1]) );
- assert( sqlite3Isxdigit(z[2]) );
- assert( sqlite3Isxdigit(z[3]) );
v = (jsonHexToInt(z[0])<<12)
+ (jsonHexToInt(z[1])<<8)
+ (jsonHexToInt(z[2])<<4)
@@ -202118,281 +204067,6 @@ static u32 jsonHexToInt4(const char *z){
}
/*
-** Make the JsonNode the return value of the function.
-*/
-static void jsonReturn(
- JsonParse *pParse, /* Complete JSON parse tree */
- JsonNode *pNode, /* Node to return */
- sqlite3_context *pCtx /* Return value for this function */
-){
- switch( pNode->eType ){
- default: {
- assert( pNode->eType==JSON_NULL );
- sqlite3_result_null(pCtx);
- break;
- }
- case JSON_TRUE: {
- sqlite3_result_int(pCtx, 1);
- break;
- }
- case JSON_FALSE: {
- sqlite3_result_int(pCtx, 0);
- break;
- }
- case JSON_INT: {
- sqlite3_int64 i = 0;
- int rc;
- int bNeg = 0;
- const char *z;
-
- assert( pNode->eU==1 );
- z = pNode->u.zJContent;
- if( z[0]=='-' ){ z++; bNeg = 1; }
- else if( z[0]=='+' ){ z++; }
- rc = sqlite3DecOrHexToI64(z, &i);
- if( rc<=1 ){
- sqlite3_result_int64(pCtx, bNeg ? -i : i);
- }else if( rc==3 && bNeg ){
- sqlite3_result_int64(pCtx, SMALLEST_INT64);
- }else{
- goto to_double;
- }
- break;
- }
- case JSON_REAL: {
- double r;
- const char *z;
- assert( pNode->eU==1 );
- to_double:
- z = pNode->u.zJContent;
- sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
- sqlite3_result_double(pCtx, r);
- break;
- }
- case JSON_STRING: {
- if( pNode->jnFlags & JNODE_RAW ){
- assert( pNode->eU==1 );
- sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n,
- SQLITE_TRANSIENT);
- }else if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){
- /* JSON formatted without any backslash-escapes */
- assert( pNode->eU==1 );
- sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2,
- SQLITE_TRANSIENT);
- }else{
- /* Translate JSON formatted string into raw text */
- u32 i;
- u32 n = pNode->n;
- const char *z;
- char *zOut;
- u32 j;
- u32 nOut = n;
- assert( pNode->eU==1 );
- z = pNode->u.zJContent;
- zOut = sqlite3_malloc( nOut+1 );
- if( zOut==0 ){
- sqlite3_result_error_nomem(pCtx);
- break;
- }
- for(i=1, j=0; i<n-1; i++){
- char c = z[i];
- if( c=='\\' ){
- c = z[++i];
- if( c=='u' ){
- u32 v = jsonHexToInt4(z+i+1);
- i += 4;
- if( v==0 ) break;
- if( v<=0x7f ){
- zOut[j++] = (char)v;
- }else if( v<=0x7ff ){
- zOut[j++] = (char)(0xc0 | (v>>6));
- zOut[j++] = 0x80 | (v&0x3f);
- }else{
- u32 vlo;
- if( (v&0xfc00)==0xd800
- && i<n-6
- && z[i+1]=='\\'
- && z[i+2]=='u'
- && ((vlo = jsonHexToInt4(z+i+3))&0xfc00)==0xdc00
- ){
- /* We have a surrogate pair */
- v = ((v&0x3ff)<<10) + (vlo&0x3ff) + 0x10000;
- i += 6;
- zOut[j++] = 0xf0 | (v>>18);
- zOut[j++] = 0x80 | ((v>>12)&0x3f);
- zOut[j++] = 0x80 | ((v>>6)&0x3f);
- zOut[j++] = 0x80 | (v&0x3f);
- }else{
- zOut[j++] = 0xe0 | (v>>12);
- zOut[j++] = 0x80 | ((v>>6)&0x3f);
- zOut[j++] = 0x80 | (v&0x3f);
- }
- }
- continue;
- }else if( c=='b' ){
- c = '\b';
- }else if( c=='f' ){
- c = '\f';
- }else if( c=='n' ){
- c = '\n';
- }else if( c=='r' ){
- c = '\r';
- }else if( c=='t' ){
- c = '\t';
- }else if( c=='v' ){
- c = '\v';
- }else if( c=='\'' || c=='"' || c=='/' || c=='\\' ){
- /* pass through unchanged */
- }else if( c=='0' ){
- c = 0;
- }else if( c=='x' ){
- c = (jsonHexToInt(z[i+1])<<4) | jsonHexToInt(z[i+2]);
- i += 2;
- }else if( c=='\r' && z[i+1]=='\n' ){
- i++;
- continue;
- }else if( 0xe2==(u8)c ){
- assert( 0x80==(u8)z[i+1] );
- assert( 0xa8==(u8)z[i+2] || 0xa9==(u8)z[i+2] );
- i += 2;
- continue;
- }else{
- continue;
- }
- } /* end if( c=='\\' ) */
- zOut[j++] = c;
- } /* end for() */
- zOut[j] = 0;
- sqlite3_result_text(pCtx, zOut, j, sqlite3_free);
- }
- break;
- }
- case JSON_ARRAY:
- case JSON_OBJECT: {
- jsonReturnJson(pParse, pNode, pCtx, 0);
- break;
- }
- }
-}
-
-/* Forward reference */
-static int jsonParseAddNode(JsonParse*,u32,u32,const char*);
-
-/*
-** A macro to hint to the compiler that a function should not be
-** inlined.
-*/
-#if defined(__GNUC__)
-# define JSON_NOINLINE __attribute__((noinline))
-#elif defined(_MSC_VER) && _MSC_VER>=1310
-# define JSON_NOINLINE __declspec(noinline)
-#else
-# define JSON_NOINLINE
-#endif
-
-
-/*
-** Add a single node to pParse->aNode after first expanding the
-** size of the aNode array. Return the index of the new node.
-**
-** If an OOM error occurs, set pParse->oom and return -1.
-*/
-static JSON_NOINLINE int jsonParseAddNodeExpand(
- JsonParse *pParse, /* Append the node to this object */
- u32 eType, /* Node type */
- u32 n, /* Content size or sub-node count */
- const char *zContent /* Content */
-){
- u32 nNew;
- JsonNode *pNew;
- assert( pParse->nNode>=pParse->nAlloc );
- if( pParse->oom ) return -1;
- nNew = pParse->nAlloc*2 + 10;
- pNew = sqlite3_realloc64(pParse->aNode, sizeof(JsonNode)*nNew);
- if( pNew==0 ){
- pParse->oom = 1;
- return -1;
- }
- pParse->nAlloc = sqlite3_msize(pNew)/sizeof(JsonNode);
- pParse->aNode = pNew;
- assert( pParse->nNode<pParse->nAlloc );
- return jsonParseAddNode(pParse, eType, n, zContent);
-}
-
-/*
-** Create a new JsonNode instance based on the arguments and append that
-** instance to the JsonParse. Return the index in pParse->aNode[] of the
-** new node, or -1 if a memory allocation fails.
-*/
-static int jsonParseAddNode(
- JsonParse *pParse, /* Append the node to this object */
- u32 eType, /* Node type */
- u32 n, /* Content size or sub-node count */
- const char *zContent /* Content */
-){
- JsonNode *p;
- assert( pParse->aNode!=0 || pParse->nNode>=pParse->nAlloc );
- if( pParse->nNode>=pParse->nAlloc ){
- return jsonParseAddNodeExpand(pParse, eType, n, zContent);
- }
- assert( pParse->aNode!=0 );
- p = &pParse->aNode[pParse->nNode];
- assert( p!=0 );
- p->eType = (u8)(eType & 0xff);
- p->jnFlags = (u8)(eType >> 8);
- VVA( p->eU = zContent ? 1 : 0 );
- p->n = n;
- p->u.zJContent = zContent;
- return pParse->nNode++;
-}
-
-/*
-** Add an array of new nodes to the current pParse->aNode array.
-** Return the index of the first node added.
-**
-** If an OOM error occurs, set pParse->oom.
-*/
-static void jsonParseAddNodeArray(
- JsonParse *pParse, /* Append the node to this object */
- JsonNode *aNode, /* Array of nodes to add */
- u32 nNode /* Number of elements in aNew */
-){
- assert( aNode!=0 );
- assert( nNode>=1 );
- if( pParse->nNode + nNode > pParse->nAlloc ){
- u32 nNew = pParse->nNode + nNode;
- JsonNode *aNew = sqlite3_realloc64(pParse->aNode, nNew*sizeof(JsonNode));
- if( aNew==0 ){
- pParse->oom = 1;
- return;
- }
- pParse->nAlloc = sqlite3_msize(aNew)/sizeof(JsonNode);
- pParse->aNode = aNew;
- }
- memcpy(&pParse->aNode[pParse->nNode], aNode, nNode*sizeof(JsonNode));
- pParse->nNode += nNode;
-}
-
-/*
-** Add a new JSON_SUBST node. The node immediately following
-** this new node will be the substitute content for iNode.
-*/
-static int jsonParseAddSubstNode(
- JsonParse *pParse, /* Add the JSON_SUBST here */
- u32 iNode /* References this node */
-){
- int idx = jsonParseAddNode(pParse, JSON_SUBST, iNode, 0);
- if( pParse->oom ) return -1;
- pParse->aNode[iNode].jnFlags |= JNODE_REPLACE;
- pParse->aNode[idx].eU = 4;
- pParse->aNode[idx].u.iPrev = pParse->iSubst;
- pParse->iSubst = idx;
- pParse->hasMod = 1;
- pParse->useMod = 1;
- return idx;
-}
-
-/*
** Return true if z[] begins with 2 (or more) hexadecimal digits
*/
static int jsonIs2Hex(const char *z){
@@ -202545,63 +204219,500 @@ static const struct NanInfName {
char *zMatch;
char *zRepl;
} aNanInfName[] = {
- { 'i', 'I', 3, JSON_REAL, 7, "inf", "9.0e999" },
- { 'i', 'I', 8, JSON_REAL, 7, "infinity", "9.0e999" },
- { 'n', 'N', 3, JSON_NULL, 4, "NaN", "null" },
- { 'q', 'Q', 4, JSON_NULL, 4, "QNaN", "null" },
- { 's', 'S', 4, JSON_NULL, 4, "SNaN", "null" },
+ { 'i', 'I', 3, JSONB_FLOAT, 7, "inf", "9.0e999" },
+ { 'i', 'I', 8, JSONB_FLOAT, 7, "infinity", "9.0e999" },
+ { 'n', 'N', 3, JSONB_NULL, 4, "NaN", "null" },
+ { 'q', 'Q', 4, JSONB_NULL, 4, "QNaN", "null" },
+ { 's', 'S', 4, JSONB_NULL, 4, "SNaN", "null" },
};
+
+/*
+** Report the wrong number of arguments for json_insert(), json_replace()
+** or json_set().
+*/
+static void jsonWrongNumArgs(
+ sqlite3_context *pCtx,
+ const char *zFuncName
+){
+ char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments",
+ zFuncName);
+ sqlite3_result_error(pCtx, zMsg, -1);
+ sqlite3_free(zMsg);
+}
+
+/****************************************************************************
+** Utility routines for dealing with the binary BLOB representation of JSON
+****************************************************************************/
+
+/*
+** Expand pParse->aBlob so that it holds at least N bytes.
+**
+** Return the number of errors.
+*/
+static int jsonBlobExpand(JsonParse *pParse, u32 N){
+ u8 *aNew;
+ u32 t;
+ assert( N>pParse->nBlobAlloc );
+ if( pParse->nBlobAlloc==0 ){
+ t = 100;
+ }else{
+ t = pParse->nBlobAlloc*2;
+ }
+ if( t<N ) t = N+100;
+ aNew = sqlite3DbRealloc(pParse->db, pParse->aBlob, t);
+ if( aNew==0 ){ pParse->oom = 1; return 1; }
+ pParse->aBlob = aNew;
+ pParse->nBlobAlloc = t;
+ return 0;
+}
+
+/*
+** If pParse->aBlob is not previously editable (because it is taken
+** from sqlite3_value_blob(), as indicated by the fact that
+** pParse->nBlobAlloc==0 and pParse->nBlob>0) then make it editable
+** by making a copy into space obtained from malloc.
+**
+** Return true on success. Return false on OOM.
+*/
+static int jsonBlobMakeEditable(JsonParse *pParse, u32 nExtra){
+ u8 *aOld;
+ u32 nSize;
+ assert( !pParse->bReadOnly );
+ if( pParse->oom ) return 0;
+ if( pParse->nBlobAlloc>0 ) return 1;
+ aOld = pParse->aBlob;
+ nSize = pParse->nBlob + nExtra;
+ pParse->aBlob = 0;
+ if( jsonBlobExpand(pParse, nSize) ){
+ return 0;
+ }
+ assert( pParse->nBlobAlloc >= pParse->nBlob + nExtra );
+ memcpy(pParse->aBlob, aOld, pParse->nBlob);
+ return 1;
+}
+
+/* Expand pParse->aBlob and append one bytes.
+*/
+static SQLITE_NOINLINE void jsonBlobExpandAndAppendOneByte(
+ JsonParse *pParse,
+ u8 c
+){
+ jsonBlobExpand(pParse, pParse->nBlob+1);
+ if( pParse->oom==0 ){
+ assert( pParse->nBlob+1<=pParse->nBlobAlloc );
+ pParse->aBlob[pParse->nBlob++] = c;
+ }
+}
+
+/* Append a single character.
+*/
+static void jsonBlobAppendOneByte(JsonParse *pParse, u8 c){
+ if( pParse->nBlob >= pParse->nBlobAlloc ){
+ jsonBlobExpandAndAppendOneByte(pParse, c);
+ }else{
+ pParse->aBlob[pParse->nBlob++] = c;
+ }
+}
+
+/* Slow version of jsonBlobAppendNode() that first resizes the
+** pParse->aBlob structure.
+*/
+static void jsonBlobAppendNode(JsonParse*,u8,u32,const void*);
+static SQLITE_NOINLINE void jsonBlobExpandAndAppendNode(
+ JsonParse *pParse,
+ u8 eType,
+ u32 szPayload,
+ const void *aPayload
+){
+ if( jsonBlobExpand(pParse, pParse->nBlob+szPayload+9) ) return;
+ jsonBlobAppendNode(pParse, eType, szPayload, aPayload);
+}
+
+
+/* Append an node type byte together with the payload size and
+** possibly also the payload.
+**
+** If aPayload is not NULL, then it is a pointer to the payload which
+** is also appended. If aPayload is NULL, the pParse->aBlob[] array
+** is resized (if necessary) so that it is big enough to hold the
+** payload, but the payload is not appended and pParse->nBlob is left
+** pointing to where the first byte of payload will eventually be.
+*/
+static void jsonBlobAppendNode(
+ JsonParse *pParse, /* The JsonParse object under construction */
+ u8 eType, /* Node type. One of JSONB_* */
+ u32 szPayload, /* Number of bytes of payload */
+ const void *aPayload /* The payload. Might be NULL */
+){
+ u8 *a;
+ if( pParse->nBlob+szPayload+9 > pParse->nBlobAlloc ){
+ jsonBlobExpandAndAppendNode(pParse,eType,szPayload,aPayload);
+ return;
+ }
+ assert( pParse->aBlob!=0 );
+ a = &pParse->aBlob[pParse->nBlob];
+ if( szPayload<=11 ){
+ a[0] = eType | (szPayload<<4);
+ pParse->nBlob += 1;
+ }else if( szPayload<=0xff ){
+ a[0] = eType | 0xc0;
+ a[1] = szPayload & 0xff;
+ pParse->nBlob += 2;
+ }else if( szPayload<=0xffff ){
+ a[0] = eType | 0xd0;
+ a[1] = (szPayload >> 8) & 0xff;
+ a[2] = szPayload & 0xff;
+ pParse->nBlob += 3;
+ }else{
+ a[0] = eType | 0xe0;
+ a[1] = (szPayload >> 24) & 0xff;
+ a[2] = (szPayload >> 16) & 0xff;
+ a[3] = (szPayload >> 8) & 0xff;
+ a[4] = szPayload & 0xff;
+ pParse->nBlob += 5;
+ }
+ if( aPayload ){
+ pParse->nBlob += szPayload;
+ memcpy(&pParse->aBlob[pParse->nBlob-szPayload], aPayload, szPayload);
+ }
+}
+
+/* Change the payload size for the node at index i to be szPayload.
+*/
+static int jsonBlobChangePayloadSize(
+ JsonParse *pParse,
+ u32 i,
+ u32 szPayload
+){
+ u8 *a;
+ u8 szType;
+ u8 nExtra;
+ u8 nNeeded;
+ int delta;
+ if( pParse->oom ) return 0;
+ a = &pParse->aBlob[i];
+ szType = a[0]>>4;
+ if( szType<=11 ){
+ nExtra = 0;
+ }else if( szType==12 ){
+ nExtra = 1;
+ }else if( szType==13 ){
+ nExtra = 2;
+ }else{
+ nExtra = 4;
+ }
+ if( szPayload<=11 ){
+ nNeeded = 0;
+ }else if( szPayload<=0xff ){
+ nNeeded = 1;
+ }else if( szPayload<=0xffff ){
+ nNeeded = 2;
+ }else{
+ nNeeded = 4;
+ }
+ delta = nNeeded - nExtra;
+ if( delta ){
+ u32 newSize = pParse->nBlob + delta;
+ if( delta>0 ){
+ if( newSize>pParse->nBlobAlloc && jsonBlobExpand(pParse, newSize) ){
+ return 0; /* OOM error. Error state recorded in pParse->oom. */
+ }
+ a = &pParse->aBlob[i];
+ memmove(&a[1+delta], &a[1], pParse->nBlob - (i+1));
+ }else{
+ memmove(&a[1], &a[1-delta], pParse->nBlob - (i+1-delta));
+ }
+ pParse->nBlob = newSize;
+ }
+ if( nNeeded==0 ){
+ a[0] = (a[0] & 0x0f) | (szPayload<<4);
+ }else if( nNeeded==1 ){
+ a[0] = (a[0] & 0x0f) | 0xc0;
+ a[1] = szPayload & 0xff;
+ }else if( nNeeded==2 ){
+ a[0] = (a[0] & 0x0f) | 0xd0;
+ a[1] = (szPayload >> 8) & 0xff;
+ a[2] = szPayload & 0xff;
+ }else{
+ a[0] = (a[0] & 0x0f) | 0xe0;
+ a[1] = (szPayload >> 24) & 0xff;
+ a[2] = (szPayload >> 16) & 0xff;
+ a[3] = (szPayload >> 8) & 0xff;
+ a[4] = szPayload & 0xff;
+ }
+ return delta;
+}
+
+/*
+** If z[0] is 'u' and is followed by exactly 4 hexadecimal character,
+** then set *pOp to JSONB_TEXTJ and return true. If not, do not make
+** any changes to *pOp and return false.
+*/
+static int jsonIs4HexB(const char *z, int *pOp){
+ if( z[0]!='u' ) return 0;
+ if( !jsonIs4Hex(&z[1]) ) return 0;
+ *pOp = JSONB_TEXTJ;
+ return 1;
+}
+
+/*
+** Check a single element of the JSONB in pParse for validity.
+**
+** The element to be checked starts at offset i and must end at on the
+** last byte before iEnd.
+**
+** Return 0 if everything is correct. Return the 1-based byte offset of the
+** error if a problem is detected. (In other words, if the error is at offset
+** 0, return 1).
+*/
+static u32 jsonbValidityCheck(
+ const JsonParse *pParse, /* Input JSONB. Only aBlob and nBlob are used */
+ u32 i, /* Start of element as pParse->aBlob[i] */
+ u32 iEnd, /* One more than the last byte of the element */
+ u32 iDepth /* Current nesting depth */
+){
+ u32 n, sz, j, k;
+ const u8 *z;
+ u8 x;
+ if( iDepth>JSON_MAX_DEPTH ) return i+1;
+ sz = 0;
+ n = jsonbPayloadSize(pParse, i, &sz);
+ if( NEVER(n==0) ) return i+1; /* Checked by caller */
+ if( NEVER(i+n+sz!=iEnd) ) return i+1; /* Checked by caller */
+ z = pParse->aBlob;
+ x = z[i] & 0x0f;
+ switch( x ){
+ case JSONB_NULL:
+ case JSONB_TRUE:
+ case JSONB_FALSE: {
+ return n+sz==1 ? 0 : i+1;
+ }
+ case JSONB_INT: {
+ if( sz<1 ) return i+1;
+ j = i+n;
+ if( z[j]=='-' ){
+ j++;
+ if( sz<2 ) return i+1;
+ }
+ k = i+n+sz;
+ while( j<k ){
+ if( sqlite3Isdigit(z[j]) ){
+ j++;
+ }else{
+ return j+1;
+ }
+ }
+ return 0;
+ }
+ case JSONB_INT5: {
+ if( sz<3 ) return i+1;
+ j = i+n;
+ if( z[j]=='-' ){
+ if( sz<4 ) return i+1;
+ j++;
+ }
+ if( z[j]!='0' ) return i+1;
+ if( z[j+1]!='x' && z[j+1]!='X' ) return j+2;
+ j += 2;
+ k = i+n+sz;
+ while( j<k ){
+ if( sqlite3Isxdigit(z[j]) ){
+ j++;
+ }else{
+ return j+1;
+ }
+ }
+ return 0;
+ }
+ case JSONB_FLOAT:
+ case JSONB_FLOAT5: {
+ u8 seen = 0; /* 0: initial. 1: '.' seen 2: 'e' seen */
+ if( sz<2 ) return i+1;
+ j = i+n;
+ k = j+sz;
+ if( z[j]=='-' ){
+ j++;
+ if( sz<3 ) return i+1;
+ }
+ if( z[j]=='.' ){
+ if( x==JSONB_FLOAT ) return j+1;
+ if( !sqlite3Isdigit(z[j+1]) ) return j+1;
+ j += 2;
+ seen = 1;
+ }else if( z[j]=='0' && x==JSONB_FLOAT ){
+ if( j+3>k ) return j+1;
+ if( z[j+1]!='.' && z[j+1]!='e' && z[j+1]!='E' ) return j+1;
+ j++;
+ }
+ for(; j<k; j++){
+ if( sqlite3Isdigit(z[j]) ) continue;
+ if( z[j]=='.' ){
+ if( seen>0 ) return j+1;
+ if( x==JSONB_FLOAT && (j==k-1 || !sqlite3Isdigit(z[j+1])) ){
+ return j+1;
+ }
+ seen = 1;
+ continue;
+ }
+ if( z[j]=='e' || z[j]=='E' ){
+ if( seen==2 ) return j+1;
+ if( j==k-1 ) return j+1;
+ if( z[j+1]=='+' || z[j+1]=='-' ){
+ j++;
+ if( j==k-1 ) return j+1;
+ }
+ seen = 2;
+ continue;
+ }
+ return j+1;
+ }
+ if( seen==0 ) return i+1;
+ return 0;
+ }
+ case JSONB_TEXT: {
+ j = i+n;
+ k = j+sz;
+ while( j<k ){
+ if( !jsonIsOk[z[j]] && z[j]!='\'' ) return j+1;
+ j++;
+ }
+ return 0;
+ }
+ case JSONB_TEXTJ:
+ case JSONB_TEXT5: {
+ j = i+n;
+ k = j+sz;
+ while( j<k ){
+ if( !jsonIsOk[z[j]] && z[j]!='\'' ){
+ if( z[j]=='"' ){
+ if( x==JSONB_TEXTJ ) return j+1;
+ }else if( z[j]!='\\' || j+1>=k ){
+ return j+1;
+ }else if( strchr("\"\\/bfnrt",z[j+1])!=0 ){
+ j++;
+ }else if( z[j+1]=='u' ){
+ if( j+5>=k ) return j+1;
+ if( !jsonIs4Hex((const char*)&z[j+2]) ) return j+1;
+ j++;
+ }else if( x!=JSONB_TEXT5 ){
+ return j+1;
+ }else{
+ u32 c = 0;
+ u32 szC = jsonUnescapeOneChar((const char*)&z[j], k-j, &c);
+ if( c==JSON_INVALID_CHAR ) return j+1;
+ j += szC - 1;
+ }
+ }
+ j++;
+ }
+ return 0;
+ }
+ case JSONB_TEXTRAW: {
+ return 0;
+ }
+ case JSONB_ARRAY: {
+ u32 sub;
+ j = i+n;
+ k = j+sz;
+ while( j<k ){
+ sz = 0;
+ n = jsonbPayloadSize(pParse, j, &sz);
+ if( n==0 ) return j+1;
+ if( j+n+sz>k ) return j+1;
+ sub = jsonbValidityCheck(pParse, j, j+n+sz, iDepth+1);
+ if( sub ) return sub;
+ j += n + sz;
+ }
+ assert( j==k );
+ return 0;
+ }
+ case JSONB_OBJECT: {
+ u32 cnt = 0;
+ u32 sub;
+ j = i+n;
+ k = j+sz;
+ while( j<k ){
+ sz = 0;
+ n = jsonbPayloadSize(pParse, j, &sz);
+ if( n==0 ) return j+1;
+ if( j+n+sz>k ) return j+1;
+ if( (cnt & 1)==0 ){
+ x = z[j] & 0x0f;
+ if( x<JSONB_TEXT || x>JSONB_TEXTRAW ) return j+1;
+ }
+ sub = jsonbValidityCheck(pParse, j, j+n+sz, iDepth+1);
+ if( sub ) return sub;
+ cnt++;
+ j += n + sz;
+ }
+ assert( j==k );
+ if( (cnt & 1)!=0 ) return j+1;
+ return 0;
+ }
+ default: {
+ return i+1;
+ }
+ }
+}
+
/*
-** Parse a single JSON value which begins at pParse->zJson[i]. Return the
-** index of the first character past the end of the value parsed.
+** Translate a single element of JSON text at pParse->zJson[i] into
+** its equivalent binary JSONB representation. Append the translation into
+** pParse->aBlob[] beginning at pParse->nBlob. The size of
+** pParse->aBlob[] is increased as necessary.
**
-** Special return values:
+** Return the index of the first character past the end of the element parsed,
+** or one of the following special result codes:
**
** 0 End of input
-** -1 Syntax error
-** -2 '}' seen
-** -3 ']' seen
-** -4 ',' seen
-** -5 ':' seen
+** -1 Syntax error or OOM
+** -2 '}' seen \
+** -3 ']' seen \___ For these returns, pParse->iErr is set to
+** -4 ',' seen / the index in zJson[] of the seen character
+** -5 ':' seen /
*/
-static int jsonParseValue(JsonParse *pParse, u32 i){
+static int jsonTranslateTextToBlob(JsonParse *pParse, u32 i){
char c;
u32 j;
- int iThis;
+ u32 iThis, iStart;
int x;
- JsonNode *pNode;
+ u8 t;
const char *z = pParse->zJson;
json_parse_restart:
switch( (u8)z[i] ){
case '{': {
/* Parse object */
- iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
- if( iThis<0 ) return -1;
+ iThis = pParse->nBlob;
+ jsonBlobAppendNode(pParse, JSONB_OBJECT, pParse->nJson-i, 0);
if( ++pParse->iDepth > JSON_MAX_DEPTH ){
pParse->iErr = i;
return -1;
}
+ iStart = pParse->nBlob;
for(j=i+1;;j++){
- u32 nNode = pParse->nNode;
- x = jsonParseValue(pParse, j);
+ u32 iBlob = pParse->nBlob;
+ x = jsonTranslateTextToBlob(pParse, j);
if( x<=0 ){
+ int op;
if( x==(-2) ){
j = pParse->iErr;
- if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1;
+ if( pParse->nBlob!=(u32)iStart ) pParse->hasNonstd = 1;
break;
}
j += json5Whitespace(&z[j]);
+ op = JSONB_TEXT;
if( sqlite3JsonId1(z[j])
- || (z[j]=='\\' && z[j+1]=='u' && jsonIs4Hex(&z[j+2]))
+ || (z[j]=='\\' && jsonIs4HexB(&z[j+1], &op))
){
int k = j+1;
while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0)
- || (z[k]=='\\' && z[k+1]=='u' && jsonIs4Hex(&z[k+2]))
+ || (z[k]=='\\' && jsonIs4HexB(&z[k+1], &op))
){
k++;
}
- jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), k-j, &z[j]);
+ assert( iBlob==pParse->nBlob );
+ jsonBlobAppendNode(pParse, op, k-j, &z[j]);
pParse->hasNonstd = 1;
x = k;
}else{
@@ -202610,24 +204721,24 @@ json_parse_restart:
}
}
if( pParse->oom ) return -1;
- pNode = &pParse->aNode[nNode];
- if( pNode->eType!=JSON_STRING ){
+ t = pParse->aBlob[iBlob] & 0x0f;
+ if( t<JSONB_TEXT || t>JSONB_TEXTRAW ){
pParse->iErr = j;
return -1;
}
- pNode->jnFlags |= JNODE_LABEL;
j = x;
if( z[j]==':' ){
j++;
}else{
- if( fast_isspace(z[j]) ){
- do{ j++; }while( fast_isspace(z[j]) );
+ if( jsonIsspace(z[j]) ){
+ /* strspn() is not helpful here */
+ do{ j++; }while( jsonIsspace(z[j]) );
if( z[j]==':' ){
j++;
goto parse_object_value;
}
}
- x = jsonParseValue(pParse, j);
+ x = jsonTranslateTextToBlob(pParse, j);
if( x!=(-5) ){
if( x!=(-1) ) pParse->iErr = j;
return -1;
@@ -202635,7 +204746,7 @@ json_parse_restart:
j = pParse->iErr+1;
}
parse_object_value:
- x = jsonParseValue(pParse, j);
+ x = jsonTranslateTextToBlob(pParse, j);
if( x<=0 ){
if( x!=(-1) ) pParse->iErr = j;
return -1;
@@ -202646,15 +204757,15 @@ json_parse_restart:
}else if( z[j]=='}' ){
break;
}else{
- if( fast_isspace(z[j]) ){
- do{ j++; }while( fast_isspace(z[j]) );
+ if( jsonIsspace(z[j]) ){
+ j += 1 + (u32)strspn(&z[j+1], jsonSpaces);
if( z[j]==',' ){
continue;
}else if( z[j]=='}' ){
break;
}
}
- x = jsonParseValue(pParse, j);
+ x = jsonTranslateTextToBlob(pParse, j);
if( x==(-4) ){
j = pParse->iErr;
continue;
@@ -202667,25 +204778,26 @@ json_parse_restart:
pParse->iErr = j;
return -1;
}
- pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
+ jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart);
pParse->iDepth--;
return j+1;
}
case '[': {
/* Parse array */
- iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
- if( iThis<0 ) return -1;
+ iThis = pParse->nBlob;
+ jsonBlobAppendNode(pParse, JSONB_ARRAY, pParse->nJson - i, 0);
+ iStart = pParse->nBlob;
+ if( pParse->oom ) return -1;
if( ++pParse->iDepth > JSON_MAX_DEPTH ){
pParse->iErr = i;
return -1;
}
- memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u));
for(j=i+1;;j++){
- x = jsonParseValue(pParse, j);
+ x = jsonTranslateTextToBlob(pParse, j);
if( x<=0 ){
if( x==(-3) ){
j = pParse->iErr;
- if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1;
+ if( pParse->nBlob!=iStart ) pParse->hasNonstd = 1;
break;
}
if( x!=(-1) ) pParse->iErr = j;
@@ -202697,15 +204809,15 @@ json_parse_restart:
}else if( z[j]==']' ){
break;
}else{
- if( fast_isspace(z[j]) ){
- do{ j++; }while( fast_isspace(z[j]) );
+ if( jsonIsspace(z[j]) ){
+ j += 1 + (u32)strspn(&z[j+1], jsonSpaces);
if( z[j]==',' ){
continue;
}else if( z[j]==']' ){
break;
}
}
- x = jsonParseValue(pParse, j);
+ x = jsonTranslateTextToBlob(pParse, j);
if( x==(-4) ){
j = pParse->iErr;
continue;
@@ -202718,23 +204830,33 @@ json_parse_restart:
pParse->iErr = j;
return -1;
}
- pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
+ jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart);
pParse->iDepth--;
return j+1;
}
case '\'': {
- u8 jnFlags;
+ u8 opcode;
char cDelim;
pParse->hasNonstd = 1;
- jnFlags = JNODE_JSON5;
+ opcode = JSONB_TEXT;
goto parse_string;
case '"':
/* Parse string */
- jnFlags = 0;
+ opcode = JSONB_TEXT;
parse_string:
cDelim = z[i];
- for(j=i+1; 1; j++){
- if( jsonIsOk[(unsigned char)z[j]] ) continue;
+ j = i+1;
+ while( 1 /*exit-by-break*/ ){
+ if( jsonIsOk[(u8)z[j]] ){
+ if( !jsonIsOk[(u8)z[j+1]] ){
+ j += 1;
+ }else if( !jsonIsOk[(u8)z[j+2]] ){
+ j += 2;
+ }else{
+ j += 3;
+ continue;
+ }
+ }
c = z[j];
if( c==cDelim ){
break;
@@ -202743,16 +204865,16 @@ json_parse_restart:
if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
|| c=='n' || c=='r' || c=='t'
|| (c=='u' && jsonIs4Hex(&z[j+1])) ){
- jnFlags |= JNODE_ESCAPE;
+ if( opcode==JSONB_TEXT ) opcode = JSONB_TEXTJ;
}else if( c=='\'' || c=='0' || c=='v' || c=='\n'
|| (0xe2==(u8)c && 0x80==(u8)z[j+1]
&& (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]))
|| (c=='x' && jsonIs2Hex(&z[j+1])) ){
- jnFlags |= (JNODE_ESCAPE|JNODE_JSON5);
+ opcode = JSONB_TEXT5;
pParse->hasNonstd = 1;
}else if( c=='\r' ){
if( z[j+1]=='\n' ) j++;
- jnFlags |= (JNODE_ESCAPE|JNODE_JSON5);
+ opcode = JSONB_TEXT5;
pParse->hasNonstd = 1;
}else{
pParse->iErr = j;
@@ -202762,14 +204884,17 @@ json_parse_restart:
/* Control characters are not allowed in strings */
pParse->iErr = j;
return -1;
+ }else if( c=='"' ){
+ opcode = JSONB_TEXT5;
}
+ j++;
}
- jsonParseAddNode(pParse, JSON_STRING | (jnFlags<<8), j+1-i, &z[i]);
+ jsonBlobAppendNode(pParse, opcode, j-1-i, &z[i+1]);
return j+1;
}
case 't': {
if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){
- jsonParseAddNode(pParse, JSON_TRUE, 0, 0);
+ jsonBlobAppendOneByte(pParse, JSONB_TRUE);
return i+4;
}
pParse->iErr = i;
@@ -202777,23 +204902,22 @@ json_parse_restart:
}
case 'f': {
if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){
- jsonParseAddNode(pParse, JSON_FALSE, 0, 0);
+ jsonBlobAppendOneByte(pParse, JSONB_FALSE);
return i+5;
}
pParse->iErr = i;
return -1;
}
case '+': {
- u8 seenDP, seenE, jnFlags;
+ u8 seenE;
pParse->hasNonstd = 1;
- jnFlags = JNODE_JSON5;
+ t = 0x00; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */
goto parse_number;
case '.':
if( sqlite3Isdigit(z[i+1]) ){
pParse->hasNonstd = 1;
- jnFlags = JNODE_JSON5;
+ t = 0x03; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */
seenE = 0;
- seenDP = JSON_REAL;
goto parse_number_2;
}
pParse->iErr = i;
@@ -202810,9 +204934,8 @@ json_parse_restart:
case '8':
case '9':
/* Parse number */
- jnFlags = 0;
+ t = 0x00; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */
parse_number:
- seenDP = JSON_INT;
seenE = 0;
assert( '-' < '0' );
assert( '+' < '0' );
@@ -202822,9 +204945,9 @@ json_parse_restart:
if( c<='0' ){
if( c=='0' ){
if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){
- assert( seenDP==JSON_INT );
+ assert( t==0x00 );
pParse->hasNonstd = 1;
- jnFlags |= JNODE_JSON5;
+ t = 0x01;
for(j=i+3; sqlite3Isxdigit(z[j]); j++){}
goto parse_number_finish;
}else if( sqlite3Isdigit(z[i+1]) ){
@@ -202841,15 +204964,15 @@ json_parse_restart:
){
pParse->hasNonstd = 1;
if( z[i]=='-' ){
- jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999");
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999");
}else{
- jsonParseAddNode(pParse, JSON_REAL, 7, "9.0e999");
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999");
}
return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4);
}
if( z[i+1]=='.' ){
pParse->hasNonstd = 1;
- jnFlags |= JNODE_JSON5;
+ t |= 0x01;
goto parse_number_2;
}
pParse->iErr = i;
@@ -202861,30 +204984,31 @@ json_parse_restart:
return -1;
}else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){
pParse->hasNonstd = 1;
- jnFlags |= JNODE_JSON5;
+ t |= 0x01;
for(j=i+4; sqlite3Isxdigit(z[j]); j++){}
goto parse_number_finish;
}
}
}
}
+
parse_number_2:
for(j=i+1;; j++){
c = z[j];
if( sqlite3Isdigit(c) ) continue;
if( c=='.' ){
- if( seenDP==JSON_REAL ){
+ if( (t & 0x02)!=0 ){
pParse->iErr = j;
return -1;
}
- seenDP = JSON_REAL;
+ t |= 0x02;
continue;
}
if( c=='e' || c=='E' ){
if( z[j-1]<'0' ){
if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
pParse->hasNonstd = 1;
- jnFlags |= JNODE_JSON5;
+ t |= 0x01;
}else{
pParse->iErr = j;
return -1;
@@ -202894,7 +205018,7 @@ json_parse_restart:
pParse->iErr = j;
return -1;
}
- seenDP = JSON_REAL;
+ t |= 0x02;
seenE = 1;
c = z[j+1];
if( c=='+' || c=='-' ){
@@ -202912,14 +205036,18 @@ json_parse_restart:
if( z[j-1]<'0' ){
if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
pParse->hasNonstd = 1;
- jnFlags |= JNODE_JSON5;
+ t |= 0x01;
}else{
pParse->iErr = j;
return -1;
}
}
parse_number_finish:
- jsonParseAddNode(pParse, seenDP | (jnFlags<<8), j - i, &z[i]);
+ assert( JSONB_INT+0x01==JSONB_INT5 );
+ assert( JSONB_FLOAT+0x01==JSONB_FLOAT5 );
+ assert( JSONB_INT+0x02==JSONB_FLOAT );
+ if( z[i]=='+' ) i++;
+ jsonBlobAppendNode(pParse, JSONB_INT+t, j-i, &z[i]);
return j;
}
case '}': {
@@ -202945,9 +205073,7 @@ json_parse_restart:
case 0x0a:
case 0x0d:
case 0x20: {
- do{
- i++;
- }while( fast_isspace(z[i]) );
+ i += 1 + (u32)strspn(&z[i+1], jsonSpaces);
goto json_parse_restart;
}
case 0x0b:
@@ -202969,7 +205095,7 @@ json_parse_restart:
}
case 'n': {
if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){
- jsonParseAddNode(pParse, JSON_NULL, 0, 0);
+ jsonBlobAppendOneByte(pParse, JSONB_NULL);
return i+4;
}
/* fall-through into the default case that checks for NaN */
@@ -202985,8 +205111,11 @@ json_parse_restart:
continue;
}
if( sqlite3Isalnum(z[i+nn]) ) continue;
- jsonParseAddNode(pParse, aNanInfName[k].eType,
- aNanInfName[k].nRepl, aNanInfName[k].zRepl);
+ if( aNanInfName[k].eType==JSONB_FLOAT ){
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999");
+ }else{
+ jsonBlobAppendOneByte(pParse, JSONB_NULL);
+ }
pParse->hasNonstd = 1;
return i + nn;
}
@@ -202996,6 +205125,7 @@ json_parse_restart:
} /* End switch(z[i]) */
}
+
/*
** Parse a complete JSON string. Return 0 on success or non-zero if there
** are any errors. If an error occurs, free all memory held by pParse,
@@ -203004,20 +205134,26 @@ json_parse_restart:
** pParse must be initialized to an empty parse object prior to calling
** this routine.
*/
-static int jsonParse(
+static int jsonConvertTextToBlob(
JsonParse *pParse, /* Initialize and fill this JsonParse object */
sqlite3_context *pCtx /* Report errors here */
){
int i;
const char *zJson = pParse->zJson;
- i = jsonParseValue(pParse, 0);
+ i = jsonTranslateTextToBlob(pParse, 0);
if( pParse->oom ) i = -1;
if( i>0 ){
+#ifdef SQLITE_DEBUG
assert( pParse->iDepth==0 );
- while( fast_isspace(zJson[i]) ) i++;
+ if( sqlite3Config.bJsonSelfcheck ){
+ assert( jsonbValidityCheck(pParse, 0, pParse->nBlob, 0)==0 );
+ }
+#endif
+ while( jsonIsspace(zJson[i]) ) i++;
if( zJson[i] ){
i += json5Whitespace(&zJson[i]);
if( zJson[i] ){
+ if( pCtx ) sqlite3_result_error(pCtx, "malformed JSON", -1);
jsonParseReset(pParse);
return 1;
}
@@ -203038,248 +205174,715 @@ static int jsonParse(
return 0;
}
+/*
+** The input string pStr is a well-formed JSON text string. Convert
+** this into the JSONB format and make it the return value of the
+** SQL function.
+*/
+static void jsonReturnStringAsBlob(JsonString *pStr){
+ JsonParse px;
+ memset(&px, 0, sizeof(px));
+ jsonStringTerminate(pStr);
+ px.zJson = pStr->zBuf;
+ px.nJson = pStr->nUsed;
+ px.db = sqlite3_context_db_handle(pStr->pCtx);
+ (void)jsonTranslateTextToBlob(&px, 0);
+ if( px.oom ){
+ sqlite3DbFree(px.db, px.aBlob);
+ sqlite3_result_error_nomem(pStr->pCtx);
+ }else{
+ assert( px.nBlobAlloc>0 );
+ assert( !px.bReadOnly );
+ sqlite3_result_blob(pStr->pCtx, px.aBlob, px.nBlob, SQLITE_DYNAMIC);
+ }
+}
-/* Mark node i of pParse as being a child of iParent. Call recursively
-** to fill in all the descendants of node i.
+/* The byte at index i is a node type-code. This routine
+** determines the payload size for that node and writes that
+** payload size in to *pSz. It returns the offset from i to the
+** beginning of the payload. Return 0 on error.
*/
-static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){
- JsonNode *pNode = &pParse->aNode[i];
- u32 j;
- pParse->aUp[i] = iParent;
- switch( pNode->eType ){
- case JSON_ARRAY: {
- for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j)){
- jsonParseFillInParentage(pParse, i+j, i);
+static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){
+ u8 x;
+ u32 sz;
+ u32 n;
+ if( NEVER(i>pParse->nBlob) ){
+ *pSz = 0;
+ return 0;
+ }
+ x = pParse->aBlob[i]>>4;
+ if( x<=11 ){
+ sz = x;
+ n = 1;
+ }else if( x==12 ){
+ if( i+1>=pParse->nBlob ){
+ *pSz = 0;
+ return 0;
+ }
+ sz = pParse->aBlob[i+1];
+ n = 2;
+ }else if( x==13 ){
+ if( i+2>=pParse->nBlob ){
+ *pSz = 0;
+ return 0;
+ }
+ sz = (pParse->aBlob[i+1]<<8) + pParse->aBlob[i+2];
+ n = 3;
+ }else if( x==14 ){
+ if( i+4>=pParse->nBlob ){
+ *pSz = 0;
+ return 0;
+ }
+ sz = ((u32)pParse->aBlob[i+1]<<24) + (pParse->aBlob[i+2]<<16) +
+ (pParse->aBlob[i+3]<<8) + pParse->aBlob[i+4];
+ n = 5;
+ }else{
+ if( i+8>=pParse->nBlob
+ || pParse->aBlob[i+1]!=0
+ || pParse->aBlob[i+2]!=0
+ || pParse->aBlob[i+3]!=0
+ || pParse->aBlob[i+4]!=0
+ ){
+ *pSz = 0;
+ return 0;
+ }
+ sz = (pParse->aBlob[i+5]<<24) + (pParse->aBlob[i+6]<<16) +
+ (pParse->aBlob[i+7]<<8) + pParse->aBlob[i+8];
+ n = 9;
+ }
+ if( (i64)i+sz+n > pParse->nBlob
+ && (i64)i+sz+n > pParse->nBlob-pParse->delta
+ ){
+ sz = 0;
+ n = 0;
+ }
+ *pSz = sz;
+ return n;
+}
+
+
+/*
+** Translate the binary JSONB representation of JSON beginning at
+** pParse->aBlob[i] into a JSON text string. Append the JSON
+** text onto the end of pOut. Return the index in pParse->aBlob[]
+** of the first byte past the end of the element that is translated.
+**
+** If an error is detected in the BLOB input, the pOut->eErr flag
+** might get set to JSTRING_MALFORMED. But not all BLOB input errors
+** are detected. So a malformed JSONB input might either result
+** in an error, or in incorrect JSON.
+**
+** The pOut->eErr JSTRING_OOM flag is set on a OOM.
+*/
+static u32 jsonTranslateBlobToText(
+ const JsonParse *pParse, /* the complete parse of the JSON */
+ u32 i, /* Start rendering at this index */
+ JsonString *pOut /* Write JSON here */
+){
+ u32 sz, n, j, iEnd;
+
+ n = jsonbPayloadSize(pParse, i, &sz);
+ if( n==0 ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ return pParse->nBlob+1;
+ }
+ switch( pParse->aBlob[i] & 0x0f ){
+ case JSONB_NULL: {
+ jsonAppendRawNZ(pOut, "null", 4);
+ return i+1;
+ }
+ case JSONB_TRUE: {
+ jsonAppendRawNZ(pOut, "true", 4);
+ return i+1;
+ }
+ case JSONB_FALSE: {
+ jsonAppendRawNZ(pOut, "false", 5);
+ return i+1;
+ }
+ case JSONB_INT:
+ case JSONB_FLOAT: {
+ if( sz==0 ) goto malformed_jsonb;
+ jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz);
+ break;
+ }
+ case JSONB_INT5: { /* Integer literal in hexadecimal notation */
+ u32 k = 2;
+ sqlite3_uint64 u = 0;
+ const char *zIn = (const char*)&pParse->aBlob[i+n];
+ int bOverflow = 0;
+ if( sz==0 ) goto malformed_jsonb;
+ if( zIn[0]=='-' ){
+ jsonAppendChar(pOut, '-');
+ k++;
+ }else if( zIn[0]=='+' ){
+ k++;
+ }
+ for(; k<sz; k++){
+ if( !sqlite3Isxdigit(zIn[k]) ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ break;
+ }else if( (u>>60)!=0 ){
+ bOverflow = 1;
+ }else{
+ u = u*16 + sqlite3HexToInt(zIn[k]);
+ }
}
+ jsonPrintf(100,pOut,bOverflow?"9.0e999":"%llu", u);
break;
}
- case JSON_OBJECT: {
- for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j+1)+1){
- pParse->aUp[i+j] = i;
- jsonParseFillInParentage(pParse, i+j+1, i);
+ case JSONB_FLOAT5: { /* Float literal missing digits beside "." */
+ u32 k = 0;
+ const char *zIn = (const char*)&pParse->aBlob[i+n];
+ if( sz==0 ) goto malformed_jsonb;
+ if( zIn[0]=='-' ){
+ jsonAppendChar(pOut, '-');
+ k++;
+ }
+ if( zIn[k]=='.' ){
+ jsonAppendChar(pOut, '0');
+ }
+ for(; k<sz; k++){
+ jsonAppendChar(pOut, zIn[k]);
+ if( zIn[k]=='.' && (k+1==sz || !sqlite3Isdigit(zIn[k+1])) ){
+ jsonAppendChar(pOut, '0');
+ }
+ }
+ break;
+ }
+ case JSONB_TEXT:
+ case JSONB_TEXTJ: {
+ jsonAppendChar(pOut, '"');
+ jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz);
+ jsonAppendChar(pOut, '"');
+ break;
+ }
+ case JSONB_TEXT5: {
+ const char *zIn;
+ u32 k;
+ u32 sz2 = sz;
+ zIn = (const char*)&pParse->aBlob[i+n];
+ jsonAppendChar(pOut, '"');
+ while( sz2>0 ){
+ for(k=0; k<sz2 && zIn[k]!='\\' && zIn[k]!='"'; k++){}
+ if( k>0 ){
+ jsonAppendRawNZ(pOut, zIn, k);
+ if( k>=sz2 ){
+ break;
+ }
+ zIn += k;
+ sz2 -= k;
+ }
+ if( zIn[0]=='"' ){
+ jsonAppendRawNZ(pOut, "\\\"", 2);
+ zIn++;
+ sz2--;
+ continue;
+ }
+ assert( zIn[0]=='\\' );
+ assert( sz2>=1 );
+ if( sz2<2 ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ break;
+ }
+ switch( (u8)zIn[1] ){
+ case '\'':
+ jsonAppendChar(pOut, '\'');
+ break;
+ case 'v':
+ jsonAppendRawNZ(pOut, "\\u0009", 6);
+ break;
+ case 'x':
+ if( sz2<4 ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ sz2 = 2;
+ break;
+ }
+ jsonAppendRawNZ(pOut, "\\u00", 4);
+ jsonAppendRawNZ(pOut, &zIn[2], 2);
+ zIn += 2;
+ sz2 -= 2;
+ break;
+ case '0':
+ jsonAppendRawNZ(pOut, "\\u0000", 6);
+ break;
+ case '\r':
+ if( sz2>2 && zIn[2]=='\n' ){
+ zIn++;
+ sz2--;
+ }
+ break;
+ case '\n':
+ break;
+ case 0xe2:
+ /* '\' followed by either U+2028 or U+2029 is ignored as
+ ** whitespace. Not that in UTF8, U+2028 is 0xe2 0x80 0x29.
+ ** U+2029 is the same except for the last byte */
+ if( sz2<4
+ || 0x80!=(u8)zIn[2]
+ || (0xa8!=(u8)zIn[3] && 0xa9!=(u8)zIn[3])
+ ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ sz2 = 2;
+ break;
+ }
+ zIn += 2;
+ sz2 -= 2;
+ break;
+ default:
+ jsonAppendRawNZ(pOut, zIn, 2);
+ break;
+ }
+ assert( sz2>=2 );
+ zIn += 2;
+ sz2 -= 2;
}
+ jsonAppendChar(pOut, '"');
+ break;
+ }
+ case JSONB_TEXTRAW: {
+ jsonAppendString(pOut, (const char*)&pParse->aBlob[i+n], sz);
+ break;
+ }
+ case JSONB_ARRAY: {
+ jsonAppendChar(pOut, '[');
+ j = i+n;
+ iEnd = j+sz;
+ while( j<iEnd && pOut->eErr==0 ){
+ j = jsonTranslateBlobToText(pParse, j, pOut);
+ jsonAppendChar(pOut, ',');
+ }
+ if( j>iEnd ) pOut->eErr |= JSTRING_MALFORMED;
+ if( sz>0 ) jsonStringTrimOneChar(pOut);
+ jsonAppendChar(pOut, ']');
+ break;
+ }
+ case JSONB_OBJECT: {
+ int x = 0;
+ jsonAppendChar(pOut, '{');
+ j = i+n;
+ iEnd = j+sz;
+ while( j<iEnd && pOut->eErr==0 ){
+ j = jsonTranslateBlobToText(pParse, j, pOut);
+ jsonAppendChar(pOut, (x++ & 1) ? ',' : ':');
+ }
+ if( (x & 1)!=0 || j>iEnd ) pOut->eErr |= JSTRING_MALFORMED;
+ if( sz>0 ) jsonStringTrimOneChar(pOut);
+ jsonAppendChar(pOut, '}');
break;
}
+
default: {
+ malformed_jsonb:
+ pOut->eErr |= JSTRING_MALFORMED;
break;
}
}
+ return i+n+sz;
+}
+
+/* Return true if the input pJson
+**
+** For performance reasons, this routine does not do a detailed check of the
+** input BLOB to ensure that it is well-formed. Hence, false positives are
+** possible. False negatives should never occur, however.
+*/
+static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){
+ u32 sz, n;
+ const u8 *aBlob;
+ int nBlob;
+ JsonParse s;
+ if( sqlite3_value_type(pJson)!=SQLITE_BLOB ) return 0;
+ aBlob = sqlite3_value_blob(pJson);
+ nBlob = sqlite3_value_bytes(pJson);
+ if( nBlob<1 ) return 0;
+ if( NEVER(aBlob==0) || (aBlob[0] & 0x0f)>JSONB_OBJECT ) return 0;
+ memset(&s, 0, sizeof(s));
+ s.aBlob = (u8*)aBlob;
+ s.nBlob = nBlob;
+ n = jsonbPayloadSize(&s, 0, &sz);
+ if( n==0 ) return 0;
+ if( sz+n!=(u32)nBlob ) return 0;
+ if( (aBlob[0] & 0x0f)<=JSONB_FALSE && sz>0 ) return 0;
+ return sz+n==(u32)nBlob;
}
/*
-** Compute the parentage of all nodes in a completed parse.
+** Given that a JSONB_ARRAY object starts at offset i, return
+** the number of entries in that array.
*/
-static int jsonParseFindParents(JsonParse *pParse){
- u32 *aUp;
- assert( pParse->aUp==0 );
- aUp = pParse->aUp = sqlite3_malloc64( sizeof(u32)*pParse->nNode );
- if( aUp==0 ){
- pParse->oom = 1;
- return SQLITE_NOMEM;
+static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){
+ u32 n, sz, i, iEnd;
+ u32 k = 0;
+ n = jsonbPayloadSize(pParse, iRoot, &sz);
+ iEnd = iRoot+n+sz;
+ for(i=iRoot+n; n>0 && i<iEnd; i+=sz+n, k++){
+ n = jsonbPayloadSize(pParse, i, &sz);
}
- jsonParseFillInParentage(pParse, 0, 0);
- return SQLITE_OK;
+ return k;
}
/*
-** Magic number used for the JSON parse cache in sqlite3_get_auxdata()
+** Edit the payload size of the element at iRoot by the amount in
+** pParse->delta.
*/
-#define JSON_CACHE_ID (-429938) /* First cache entry */
-#define JSON_CACHE_SZ 4 /* Max number of cache entries */
+static void jsonAfterEditSizeAdjust(JsonParse *pParse, u32 iRoot){
+ u32 sz = 0;
+ u32 nBlob;
+ assert( pParse->delta!=0 );
+ assert( pParse->nBlobAlloc >= pParse->nBlob );
+ nBlob = pParse->nBlob;
+ pParse->nBlob = pParse->nBlobAlloc;
+ (void)jsonbPayloadSize(pParse, iRoot, &sz);
+ pParse->nBlob = nBlob;
+ sz += pParse->delta;
+ pParse->delta += jsonBlobChangePayloadSize(pParse, iRoot, sz);
+}
/*
-** Obtain a complete parse of the JSON found in the pJson argument
-**
-** Use the sqlite3_get_auxdata() cache to find a preexisting parse
-** if it is available. If the cache is not available or if it
-** is no longer valid, parse the JSON again and return the new parse.
-** Also register the new parse so that it will be available for
-** future sqlite3_get_auxdata() calls.
-**
-** If an error occurs and pErrCtx!=0 then report the error on pErrCtx
-** and return NULL.
-**
-** The returned pointer (if it is not NULL) is owned by the cache in
-** most cases, not the caller. The caller does NOT need to invoke
-** jsonParseFree(), in most cases.
+** Modify the JSONB blob at pParse->aBlob by removing nDel bytes of
+** content beginning at iDel, and replacing them with nIns bytes of
+** content given by aIns.
**
-** Except, if an error occurs and pErrCtx==0 then return the JsonParse
-** object with JsonParse.nErr non-zero and the caller will own the JsonParse
-** object. In that case, it will be the responsibility of the caller to
-** invoke jsonParseFree(). To summarize:
+** nDel may be zero, in which case no bytes are removed. But iDel is
+** still important as new bytes will be insert beginning at iDel.
**
-** pErrCtx!=0 || p->nErr==0 ==> Return value p is owned by the
-** cache. Call does not need to
-** free it.
+** aIns may be zero, in which case space is created to hold nIns bytes
+** beginning at iDel, but that space is uninitialized.
**
-** pErrCtx==0 && p->nErr!=0 ==> Return value is owned by the caller
-** and so the caller must free it.
+** Set pParse->oom if an OOM occurs.
*/
-static JsonParse *jsonParseCached(
- sqlite3_context *pCtx, /* Context to use for cache search */
- sqlite3_value *pJson, /* Function param containing JSON text */
- sqlite3_context *pErrCtx, /* Write parse errors here if not NULL */
- int bUnedited /* No prior edits allowed */
+static void jsonBlobEdit(
+ JsonParse *pParse, /* The JSONB to be modified is in pParse->aBlob */
+ u32 iDel, /* First byte to be removed */
+ u32 nDel, /* Number of bytes to remove */
+ const u8 *aIns, /* Content to insert */
+ u32 nIns /* Bytes of content to insert */
){
- char *zJson = (char*)sqlite3_value_text(pJson);
- int nJson = sqlite3_value_bytes(pJson);
- JsonParse *p;
- JsonParse *pMatch = 0;
- int iKey;
- int iMinKey = 0;
- u32 iMinHold = 0xffffffff;
- u32 iMaxHold = 0;
- int bJsonRCStr;
+ i64 d = (i64)nIns - (i64)nDel;
+ if( d!=0 ){
+ if( pParse->nBlob + d > pParse->nBlobAlloc ){
+ jsonBlobExpand(pParse, pParse->nBlob+d);
+ if( pParse->oom ) return;
+ }
+ memmove(&pParse->aBlob[iDel+nIns],
+ &pParse->aBlob[iDel+nDel],
+ pParse->nBlob - (iDel+nDel));
+ pParse->nBlob += d;
+ pParse->delta += d;
+ }
+ if( nIns && aIns ) memcpy(&pParse->aBlob[iDel], aIns, nIns);
+}
- if( zJson==0 ) return 0;
- for(iKey=0; iKey<JSON_CACHE_SZ; iKey++){
- p = (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iKey);
- if( p==0 ){
- iMinKey = iKey;
- break;
+/*
+** Return the number of escaped newlines to be ignored.
+** An escaped newline is a one of the following byte sequences:
+**
+** 0x5c 0x0a
+** 0x5c 0x0d
+** 0x5c 0x0d 0x0a
+** 0x5c 0xe2 0x80 0xa8
+** 0x5c 0xe2 0x80 0xa9
+*/
+static u32 jsonBytesToBypass(const char *z, u32 n){
+ u32 i = 0;
+ while( i+1<n ){
+ if( z[i]!='\\' ) return i;
+ if( z[i+1]=='\n' ){
+ i += 2;
+ continue;
}
- if( pMatch==0
- && p->nJson==nJson
- && (p->hasMod==0 || bUnedited==0)
- && (p->zJson==zJson || memcmp(p->zJson,zJson,nJson)==0)
- ){
- p->nErr = 0;
- p->useMod = 0;
- pMatch = p;
- }else
- if( pMatch==0
- && p->zAlt!=0
- && bUnedited==0
- && p->nAlt==nJson
- && memcmp(p->zAlt, zJson, nJson)==0
- ){
- p->nErr = 0;
- p->useMod = 1;
- pMatch = p;
- }else if( p->iHold<iMinHold ){
- iMinHold = p->iHold;
- iMinKey = iKey;
+ if( z[i+1]=='\r' ){
+ if( i+2<n && z[i+2]=='\n' ){
+ i += 3;
+ }else{
+ i += 2;
+ }
+ continue;
}
- if( p->iHold>iMaxHold ){
- iMaxHold = p->iHold;
+ if( 0xe2==(u8)z[i+1]
+ && i+3<n
+ && 0x80==(u8)z[i+2]
+ && (0xa8==(u8)z[i+3] || 0xa9==(u8)z[i+3])
+ ){
+ i += 4;
+ continue;
}
+ break;
}
- if( pMatch ){
- /* The input JSON text was found in the cache. Use the preexisting
- ** parse of this JSON */
- pMatch->nErr = 0;
- pMatch->iHold = iMaxHold+1;
- assert( pMatch->nJPRef>0 ); /* pMatch is owned by the cache */
- return pMatch;
- }
+ return i;
+}
- /* The input JSON was not found anywhere in the cache. We will need
- ** to parse it ourselves and generate a new JsonParse object.
- */
- bJsonRCStr = sqlite3ValueIsOfClass(pJson,(void(*)(void*))sqlite3RCStrUnref);
- p = sqlite3_malloc64( sizeof(*p) + (bJsonRCStr ? 0 : nJson+1) );
- if( p==0 ){
- sqlite3_result_error_nomem(pCtx);
- return 0;
+/*
+** Input z[0..n] defines JSON escape sequence including the leading '\\'.
+** Decode that escape sequence into a single character. Write that
+** character into *piOut. Return the number of bytes in the escape sequence.
+**
+** If there is a syntax error of some kind (for example too few characters
+** after the '\\' to complete the encoding) then *piOut is set to
+** JSON_INVALID_CHAR.
+*/
+static u32 jsonUnescapeOneChar(const char *z, u32 n, u32 *piOut){
+ assert( n>0 );
+ assert( z[0]=='\\' );
+ if( n<2 ){
+ *piOut = JSON_INVALID_CHAR;
+ return n;
}
- memset(p, 0, sizeof(*p));
- if( bJsonRCStr ){
- p->zJson = sqlite3RCStrRef(zJson);
- p->bJsonIsRCStr = 1;
- }else{
- p->zJson = (char*)&p[1];
- memcpy(p->zJson, zJson, nJson+1);
+ switch( (u8)z[1] ){
+ case 'u': {
+ u32 v, vlo;
+ if( n<6 ){
+ *piOut = JSON_INVALID_CHAR;
+ return n;
+ }
+ v = jsonHexToInt4(&z[2]);
+ if( (v & 0xfc00)==0xd800
+ && n>=12
+ && z[6]=='\\'
+ && z[7]=='u'
+ && ((vlo = jsonHexToInt4(&z[8]))&0xfc00)==0xdc00
+ ){
+ *piOut = ((v&0x3ff)<<10) + (vlo&0x3ff) + 0x10000;
+ return 12;
+ }else{
+ *piOut = v;
+ return 6;
+ }
+ }
+ case 'b': { *piOut = '\b'; return 2; }
+ case 'f': { *piOut = '\f'; return 2; }
+ case 'n': { *piOut = '\n'; return 2; }
+ case 'r': { *piOut = '\r'; return 2; }
+ case 't': { *piOut = '\t'; return 2; }
+ case 'v': { *piOut = '\v'; return 2; }
+ case '0': { *piOut = 0; return 2; }
+ case '\'':
+ case '"':
+ case '/':
+ case '\\':{ *piOut = z[1]; return 2; }
+ case 'x': {
+ if( n<4 ){
+ *piOut = JSON_INVALID_CHAR;
+ return n;
+ }
+ *piOut = (jsonHexToInt(z[2])<<4) | jsonHexToInt(z[3]);
+ return 4;
+ }
+ case 0xe2:
+ case '\r':
+ case '\n': {
+ u32 nSkip = jsonBytesToBypass(z, n);
+ if( nSkip==0 ){
+ *piOut = JSON_INVALID_CHAR;
+ return n;
+ }else if( nSkip==n ){
+ *piOut = 0;
+ return n;
+ }else if( z[nSkip]=='\\' ){
+ return nSkip + jsonUnescapeOneChar(&z[nSkip], n-nSkip, piOut);
+ }else{
+ int sz = sqlite3Utf8ReadLimited((u8*)&z[nSkip], n-nSkip, piOut);
+ return nSkip + sz;
+ }
+ }
+ default: {
+ *piOut = JSON_INVALID_CHAR;
+ return 2;
+ }
}
- p->nJPRef = 1;
- if( jsonParse(p, pErrCtx) ){
- if( pErrCtx==0 ){
- p->nErr = 1;
- assert( p->nJPRef==1 ); /* Caller will own the new JsonParse object p */
- return p;
+}
+
+
+/*
+** Compare two object labels. Return 1 if they are equal and
+** 0 if they differ.
+**
+** In this version, we know that one or the other or both of the
+** two comparands contains an escape sequence.
+*/
+static SQLITE_NOINLINE int jsonLabelCompareEscaped(
+ const char *zLeft, /* The left label */
+ u32 nLeft, /* Size of the left label in bytes */
+ int rawLeft, /* True if zLeft contains no escapes */
+ const char *zRight, /* The right label */
+ u32 nRight, /* Size of the right label in bytes */
+ int rawRight /* True if zRight is escape-free */
+){
+ u32 cLeft, cRight;
+ assert( rawLeft==0 || rawRight==0 );
+ while( 1 /*exit-by-return*/ ){
+ if( nLeft==0 ){
+ cLeft = 0;
+ }else if( rawLeft || zLeft[0]!='\\' ){
+ cLeft = ((u8*)zLeft)[0];
+ if( cLeft>=0xc0 ){
+ int sz = sqlite3Utf8ReadLimited((u8*)zLeft, nLeft, &cLeft);
+ zLeft += sz;
+ nLeft -= sz;
+ }else{
+ zLeft++;
+ nLeft--;
+ }
+ }else{
+ u32 n = jsonUnescapeOneChar(zLeft, nLeft, &cLeft);
+ zLeft += n;
+ assert( n<=nLeft );
+ nLeft -= n;
+ }
+ if( nRight==0 ){
+ cRight = 0;
+ }else if( rawRight || zRight[0]!='\\' ){
+ cRight = ((u8*)zRight)[0];
+ if( cRight>=0xc0 ){
+ int sz = sqlite3Utf8ReadLimited((u8*)zRight, nRight, &cRight);
+ zRight += sz;
+ nRight -= sz;
+ }else{
+ zRight++;
+ nRight--;
+ }
+ }else{
+ u32 n = jsonUnescapeOneChar(zRight, nRight, &cRight);
+ zRight += n;
+ assert( n<=nRight );
+ nRight -= n;
}
- jsonParseFree(p);
- return 0;
+ if( cLeft!=cRight ) return 0;
+ if( cLeft==0 ) return 1;
}
- p->nJson = nJson;
- p->iHold = iMaxHold+1;
- /* Transfer ownership of the new JsonParse to the cache */
- sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p,
- (void(*)(void*))jsonParseFree);
- return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey);
}
/*
-** Compare the OBJECT label at pNode against zKey,nKey. Return true on
-** a match.
+** Compare two object labels. Return 1 if they are equal and
+** 0 if they differ. Return -1 if an OOM occurs.
*/
-static int jsonLabelCompare(const JsonNode *pNode, const char *zKey, u32 nKey){
- assert( pNode->eU==1 );
- if( pNode->jnFlags & JNODE_RAW ){
- if( pNode->n!=nKey ) return 0;
- return strncmp(pNode->u.zJContent, zKey, nKey)==0;
+static int jsonLabelCompare(
+ const char *zLeft, /* The left label */
+ u32 nLeft, /* Size of the left label in bytes */
+ int rawLeft, /* True if zLeft contains no escapes */
+ const char *zRight, /* The right label */
+ u32 nRight, /* Size of the right label in bytes */
+ int rawRight /* True if zRight is escape-free */
+){
+ if( rawLeft && rawRight ){
+ /* Simpliest case: Neither label contains escapes. A simple
+ ** memcmp() is sufficient. */
+ if( nLeft!=nRight ) return 0;
+ return memcmp(zLeft, zRight, nLeft)==0;
}else{
- if( pNode->n!=nKey+2 ) return 0;
- return strncmp(pNode->u.zJContent+1, zKey, nKey)==0;
+ return jsonLabelCompareEscaped(zLeft, nLeft, rawLeft,
+ zRight, nRight, rawRight);
}
}
-static int jsonSameLabel(const JsonNode *p1, const JsonNode *p2){
- if( p1->jnFlags & JNODE_RAW ){
- return jsonLabelCompare(p2, p1->u.zJContent, p1->n);
- }else if( p2->jnFlags & JNODE_RAW ){
- return jsonLabelCompare(p1, p2->u.zJContent, p2->n);
+
+/*
+** Error returns from jsonLookupStep()
+*/
+#define JSON_LOOKUP_ERROR 0xffffffff
+#define JSON_LOOKUP_NOTFOUND 0xfffffffe
+#define JSON_LOOKUP_PATHERROR 0xfffffffd
+#define JSON_LOOKUP_ISERROR(x) ((x)>=JSON_LOOKUP_PATHERROR)
+
+/* Forward declaration */
+static u32 jsonLookupStep(JsonParse*,u32,const char*,u32);
+
+
+/* This helper routine for jsonLookupStep() populates pIns with
+** binary data that is to be inserted into pParse.
+**
+** In the common case, pIns just points to pParse->aIns and pParse->nIns.
+** But if the zPath of the original edit operation includes path elements
+** that go deeper, additional substructure must be created.
+**
+** For example:
+**
+** json_insert('{}', '$.a.b.c', 123);
+**
+** The search stops at '$.a' But additional substructure must be
+** created for the ".b.c" part of the patch so that the final result
+** is: {"a":{"b":{"c"::123}}}. This routine populates pIns with
+** the binary equivalent of {"b":{"c":123}} so that it can be inserted.
+**
+** The caller is responsible for resetting pIns when it has finished
+** using the substructure.
+*/
+static u32 jsonCreateEditSubstructure(
+ JsonParse *pParse, /* The original JSONB that is being edited */
+ JsonParse *pIns, /* Populate this with the blob data to insert */
+ const char *zTail /* Tail of the path that determins substructure */
+){
+ static const u8 emptyObject[] = { JSONB_ARRAY, JSONB_OBJECT };
+ int rc;
+ memset(pIns, 0, sizeof(*pIns));
+ pIns->db = pParse->db;
+ if( zTail[0]==0 ){
+ /* No substructure. Just insert what is given in pParse. */
+ pIns->aBlob = pParse->aIns;
+ pIns->nBlob = pParse->nIns;
+ rc = 0;
}else{
- return p1->n==p2->n && strncmp(p1->u.zJContent,p2->u.zJContent,p1->n)==0;
+ /* Construct the binary substructure */
+ pIns->nBlob = 1;
+ pIns->aBlob = (u8*)&emptyObject[zTail[0]=='.'];
+ pIns->eEdit = pParse->eEdit;
+ pIns->nIns = pParse->nIns;
+ pIns->aIns = pParse->aIns;
+ rc = jsonLookupStep(pIns, 0, zTail, 0);
+ pParse->oom |= pIns->oom;
}
+ return rc; /* Error code only */
}
-/* forward declaration */
-static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**);
-
/*
-** Search along zPath to find the node specified. Return a pointer
-** to that node, or NULL if zPath is malformed or if there is no such
-** node.
+** Search along zPath to find the Json element specified. Return an
+** index into pParse->aBlob[] for the start of that element's value.
+**
+** If the value found by this routine is the value half of label/value pair
+** within an object, then set pPath->iLabel to the start of the corresponding
+** label, before returning.
+**
+** Return one of the JSON_LOOKUP error codes if problems are seen.
**
-** If pApnd!=0, then try to append new nodes to complete zPath if it is
-** possible to do so and if no existing node corresponds to zPath. If
-** new nodes are appended *pApnd is set to 1.
+** This routine will also modify the blob. If pParse->eEdit is one of
+** JEDIT_DEL, JEDIT_REPL, JEDIT_INS, or JEDIT_SET, then changes might be
+** made to the selected value. If an edit is performed, then the return
+** value does not necessarily point to the select element. If an edit
+** is performed, the return value is only useful for detecting error
+** conditions.
*/
-static JsonNode *jsonLookupStep(
+static u32 jsonLookupStep(
JsonParse *pParse, /* The JSON to search */
- u32 iRoot, /* Begin the search at this node */
+ u32 iRoot, /* Begin the search at this element of aBlob[] */
const char *zPath, /* The path to search */
- int *pApnd, /* Append nodes to complete path if not NULL */
- const char **pzErr /* Make *pzErr point to any syntax error in zPath */
+ u32 iLabel /* Label if iRoot is a value of in an object */
){
- u32 i, j, nKey;
+ u32 i, j, k, nKey, sz, n, iEnd, rc;
const char *zKey;
- JsonNode *pRoot;
- if( pParse->oom ) return 0;
- pRoot = &pParse->aNode[iRoot];
- if( pRoot->jnFlags & (JNODE_REPLACE|JNODE_REMOVE) && pParse->useMod ){
- while( (pRoot->jnFlags & JNODE_REPLACE)!=0 ){
- u32 idx = (u32)(pRoot - pParse->aNode);
- i = pParse->iSubst;
- while( 1 /*exit-by-break*/ ){
- assert( i<pParse->nNode );
- assert( pParse->aNode[i].eType==JSON_SUBST );
- assert( pParse->aNode[i].eU==4 );
- assert( pParse->aNode[i].u.iPrev<i );
- if( pParse->aNode[i].n==idx ){
- pRoot = &pParse->aNode[i+1];
- iRoot = i+1;
- break;
- }
- i = pParse->aNode[i].u.iPrev;
+ u8 x;
+
+ if( zPath[0]==0 ){
+ if( pParse->eEdit && jsonBlobMakeEditable(pParse, pParse->nIns) ){
+ n = jsonbPayloadSize(pParse, iRoot, &sz);
+ sz += n;
+ if( pParse->eEdit==JEDIT_DEL ){
+ if( iLabel>0 ){
+ sz += iRoot - iLabel;
+ iRoot = iLabel;
+ }
+ jsonBlobEdit(pParse, iRoot, sz, 0, 0);
+ }else if( pParse->eEdit==JEDIT_INS ){
+ /* Already exists, so json_insert() is a no-op */
+ }else{
+ /* json_set() or json_replace() */
+ jsonBlobEdit(pParse, iRoot, sz, pParse->aIns, pParse->nIns);
}
}
- if( pRoot->jnFlags & JNODE_REMOVE ){
- return 0;
- }
+ pParse->iLabel = iLabel;
+ return iRoot;
}
- if( zPath[0]==0 ) return pRoot;
if( zPath[0]=='.' ){
- if( pRoot->eType!=JSON_OBJECT ) return 0;
+ int rawKey = 1;
+ x = pParse->aBlob[iRoot];
zPath++;
if( zPath[0]=='"' ){
zKey = zPath + 1;
@@ -203288,250 +205891,704 @@ static JsonNode *jsonLookupStep(
if( zPath[i] ){
i++;
}else{
- *pzErr = zPath;
- return 0;
+ return JSON_LOOKUP_PATHERROR;
}
testcase( nKey==0 );
+ rawKey = memchr(zKey, '\\', nKey)==0;
}else{
zKey = zPath;
for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
nKey = i;
if( nKey==0 ){
- *pzErr = zPath;
- return 0;
- }
- }
- j = 1;
- for(;;){
- while( j<=pRoot->n ){
- if( jsonLabelCompare(pRoot+j, zKey, nKey) ){
- return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr);
- }
- j++;
- j += jsonNodeSize(&pRoot[j]);
+ return JSON_LOOKUP_PATHERROR;
+ }
+ }
+ if( (x & 0x0f)!=JSONB_OBJECT ) return JSON_LOOKUP_NOTFOUND;
+ n = jsonbPayloadSize(pParse, iRoot, &sz);
+ j = iRoot + n; /* j is the index of a label */
+ iEnd = j+sz;
+ while( j<iEnd ){
+ int rawLabel;
+ const char *zLabel;
+ x = pParse->aBlob[j] & 0x0f;
+ if( x<JSONB_TEXT || x>JSONB_TEXTRAW ) return JSON_LOOKUP_ERROR;
+ n = jsonbPayloadSize(pParse, j, &sz);
+ if( n==0 ) return JSON_LOOKUP_ERROR;
+ k = j+n; /* k is the index of the label text */
+ if( k+sz>=iEnd ) return JSON_LOOKUP_ERROR;
+ zLabel = (const char*)&pParse->aBlob[k];
+ rawLabel = x==JSONB_TEXT || x==JSONB_TEXTRAW;
+ if( jsonLabelCompare(zKey, nKey, rawKey, zLabel, sz, rawLabel) ){
+ u32 v = k+sz; /* v is the index of the value */
+ if( ((pParse->aBlob[v])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR;
+ n = jsonbPayloadSize(pParse, v, &sz);
+ if( n==0 || v+n+sz>iEnd ) return JSON_LOOKUP_ERROR;
+ assert( j>0 );
+ rc = jsonLookupStep(pParse, v, &zPath[i], j);
+ if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
+ return rc;
}
- if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
- if( pParse->useMod==0 ) break;
- assert( pRoot->eU==2 );
- iRoot = pRoot->u.iAppend;
- pRoot = &pParse->aNode[iRoot];
- j = 1;
- }
- if( pApnd ){
- u32 iStart, iLabel;
- JsonNode *pNode;
- assert( pParse->useMod );
- iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0);
- iLabel = jsonParseAddNode(pParse, JSON_STRING, nKey, zKey);
- zPath += i;
- pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
- if( pParse->oom ) return 0;
- if( pNode ){
- pRoot = &pParse->aNode[iRoot];
- assert( pRoot->eU==0 );
- pRoot->u.iAppend = iStart;
- pRoot->jnFlags |= JNODE_APPEND;
- VVA( pRoot->eU = 2 );
- pParse->aNode[iLabel].jnFlags |= JNODE_RAW;
- }
- return pNode;
+ j = k+sz;
+ if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR;
+ n = jsonbPayloadSize(pParse, j, &sz);
+ if( n==0 ) return JSON_LOOKUP_ERROR;
+ j += n+sz;
+ }
+ if( j>iEnd ) return JSON_LOOKUP_ERROR;
+ if( pParse->eEdit>=JEDIT_INS ){
+ u32 nIns; /* Total bytes to insert (label+value) */
+ JsonParse v; /* BLOB encoding of the value to be inserted */
+ JsonParse ix; /* Header of the label to be inserted */
+ testcase( pParse->eEdit==JEDIT_INS );
+ testcase( pParse->eEdit==JEDIT_SET );
+ memset(&ix, 0, sizeof(ix));
+ ix.db = pParse->db;
+ jsonBlobAppendNode(&ix, rawKey?JSONB_TEXTRAW:JSONB_TEXT5, nKey, 0);
+ pParse->oom |= ix.oom;
+ rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i]);
+ if( !JSON_LOOKUP_ISERROR(rc)
+ && jsonBlobMakeEditable(pParse, ix.nBlob+nKey+v.nBlob)
+ ){
+ assert( !pParse->oom );
+ nIns = ix.nBlob + nKey + v.nBlob;
+ jsonBlobEdit(pParse, j, 0, 0, nIns);
+ if( !pParse->oom ){
+ assert( pParse->aBlob!=0 ); /* Because pParse->oom!=0 */
+ assert( ix.aBlob!=0 ); /* Because pPasre->oom!=0 */
+ memcpy(&pParse->aBlob[j], ix.aBlob, ix.nBlob);
+ k = j + ix.nBlob;
+ memcpy(&pParse->aBlob[k], zKey, nKey);
+ k += nKey;
+ memcpy(&pParse->aBlob[k], v.aBlob, v.nBlob);
+ if( ALWAYS(pParse->delta) ) jsonAfterEditSizeAdjust(pParse, iRoot);
+ }
+ }
+ jsonParseReset(&v);
+ jsonParseReset(&ix);
+ return rc;
}
}else if( zPath[0]=='[' ){
- i = 0;
- j = 1;
- while( sqlite3Isdigit(zPath[j]) ){
- i = i*10 + zPath[j] - '0';
- j++;
+ x = pParse->aBlob[iRoot] & 0x0f;
+ if( x!=JSONB_ARRAY ) return JSON_LOOKUP_NOTFOUND;
+ n = jsonbPayloadSize(pParse, iRoot, &sz);
+ k = 0;
+ i = 1;
+ while( sqlite3Isdigit(zPath[i]) ){
+ k = k*10 + zPath[i] - '0';
+ i++;
}
- if( j<2 || zPath[j]!=']' ){
+ if( i<2 || zPath[i]!=']' ){
if( zPath[1]=='#' ){
- JsonNode *pBase = pRoot;
- int iBase = iRoot;
- if( pRoot->eType!=JSON_ARRAY ) return 0;
- for(;;){
- while( j<=pBase->n ){
- if( (pBase[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i++;
- j += jsonNodeSize(&pBase[j]);
- }
- if( (pBase->jnFlags & JNODE_APPEND)==0 ) break;
- if( pParse->useMod==0 ) break;
- assert( pBase->eU==2 );
- iBase = pBase->u.iAppend;
- pBase = &pParse->aNode[iBase];
- j = 1;
- }
- j = 2;
+ k = jsonbArrayCount(pParse, iRoot);
+ i = 2;
if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){
- unsigned int x = 0;
- j = 3;
+ unsigned int nn = 0;
+ i = 3;
do{
- x = x*10 + zPath[j] - '0';
- j++;
- }while( sqlite3Isdigit(zPath[j]) );
- if( x>i ) return 0;
- i -= x;
+ nn = nn*10 + zPath[i] - '0';
+ i++;
+ }while( sqlite3Isdigit(zPath[i]) );
+ if( nn>k ) return JSON_LOOKUP_NOTFOUND;
+ k -= nn;
}
- if( zPath[j]!=']' ){
- *pzErr = zPath;
- return 0;
+ if( zPath[i]!=']' ){
+ return JSON_LOOKUP_PATHERROR;
}
}else{
- *pzErr = zPath;
- return 0;
+ return JSON_LOOKUP_PATHERROR;
}
}
- if( pRoot->eType!=JSON_ARRAY ) return 0;
- zPath += j + 1;
- j = 1;
- for(;;){
- while( j<=pRoot->n
- && (i>0 || ((pRoot[j].jnFlags & JNODE_REMOVE)!=0 && pParse->useMod))
+ j = iRoot+n;
+ iEnd = j+sz;
+ while( j<iEnd ){
+ if( k==0 ){
+ rc = jsonLookupStep(pParse, j, &zPath[i+1], 0);
+ if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
+ return rc;
+ }
+ k--;
+ n = jsonbPayloadSize(pParse, j, &sz);
+ if( n==0 ) return JSON_LOOKUP_ERROR;
+ j += n+sz;
+ }
+ if( j>iEnd ) return JSON_LOOKUP_ERROR;
+ if( k>0 ) return JSON_LOOKUP_NOTFOUND;
+ if( pParse->eEdit>=JEDIT_INS ){
+ JsonParse v;
+ testcase( pParse->eEdit==JEDIT_INS );
+ testcase( pParse->eEdit==JEDIT_SET );
+ rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i+1]);
+ if( !JSON_LOOKUP_ISERROR(rc)
+ && jsonBlobMakeEditable(pParse, v.nBlob)
){
- if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i--;
- j += jsonNodeSize(&pRoot[j]);
- }
- if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
- if( pParse->useMod==0 ) break;
- assert( pRoot->eU==2 );
- iRoot = pRoot->u.iAppend;
- pRoot = &pParse->aNode[iRoot];
- j = 1;
- }
- if( j<=pRoot->n ){
- return jsonLookupStep(pParse, iRoot+j, zPath, pApnd, pzErr);
- }
- if( i==0 && pApnd ){
- u32 iStart;
- JsonNode *pNode;
- assert( pParse->useMod );
- iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0);
- pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
- if( pParse->oom ) return 0;
- if( pNode ){
- pRoot = &pParse->aNode[iRoot];
- assert( pRoot->eU==0 );
- pRoot->u.iAppend = iStart;
- pRoot->jnFlags |= JNODE_APPEND;
- VVA( pRoot->eU = 2 );
+ assert( !pParse->oom );
+ jsonBlobEdit(pParse, j, 0, v.aBlob, v.nBlob);
}
- return pNode;
+ jsonParseReset(&v);
+ if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
+ return rc;
}
}else{
- *pzErr = zPath;
+ return JSON_LOOKUP_PATHERROR;
}
- return 0;
+ return JSON_LOOKUP_NOTFOUND;
}
/*
-** Append content to pParse that will complete zPath. Return a pointer
-** to the inserted node, or return NULL if the append fails.
+** Convert a JSON BLOB into text and make that text the return value
+** of an SQL function.
*/
-static JsonNode *jsonLookupAppend(
- JsonParse *pParse, /* Append content to the JSON parse */
- const char *zPath, /* Description of content to append */
- int *pApnd, /* Set this flag to 1 */
- const char **pzErr /* Make this point to any syntax error */
+static void jsonReturnTextJsonFromBlob(
+ sqlite3_context *ctx,
+ const u8 *aBlob,
+ u32 nBlob
){
- *pApnd = 1;
- if( zPath[0]==0 ){
- jsonParseAddNode(pParse, JSON_NULL, 0, 0);
- return pParse->oom ? 0 : &pParse->aNode[pParse->nNode-1];
- }
- if( zPath[0]=='.' ){
- jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
- }else if( strncmp(zPath,"[0]",3)==0 ){
- jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
- }else{
- return 0;
- }
- if( pParse->oom ) return 0;
- return jsonLookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr);
+ JsonParse x;
+ JsonString s;
+
+ if( NEVER(aBlob==0) ) return;
+ memset(&x, 0, sizeof(x));
+ x.aBlob = (u8*)aBlob;
+ x.nBlob = nBlob;
+ jsonStringInit(&s, ctx);
+ jsonTranslateBlobToText(&x, 0, &s);
+ jsonReturnString(&s, 0, 0);
}
+
/*
-** Return the text of a syntax error message on a JSON path. Space is
-** obtained from sqlite3_malloc().
+** Return the value of the BLOB node at index i.
+**
+** If the value is a primitive, return it as an SQL value.
+** If the value is an array or object, return it as either
+** JSON text or the BLOB encoding, depending on the JSON_B flag
+** on the userdata.
*/
-static char *jsonPathSyntaxError(const char *zErr){
- return sqlite3_mprintf("JSON path error near '%q'", zErr);
+static void jsonReturnFromBlob(
+ JsonParse *pParse, /* Complete JSON parse tree */
+ u32 i, /* Index of the node */
+ sqlite3_context *pCtx, /* Return value for this function */
+ int textOnly /* return text JSON. Disregard user-data */
+){
+ u32 n, sz;
+ int rc;
+ sqlite3 *db = sqlite3_context_db_handle(pCtx);
+
+ n = jsonbPayloadSize(pParse, i, &sz);
+ if( n==0 ){
+ sqlite3_result_error(pCtx, "malformed JSON", -1);
+ return;
+ }
+ switch( pParse->aBlob[i] & 0x0f ){
+ case JSONB_NULL: {
+ if( sz ) goto returnfromblob_malformed;
+ sqlite3_result_null(pCtx);
+ break;
+ }
+ case JSONB_TRUE: {
+ if( sz ) goto returnfromblob_malformed;
+ sqlite3_result_int(pCtx, 1);
+ break;
+ }
+ case JSONB_FALSE: {
+ if( sz ) goto returnfromblob_malformed;
+ sqlite3_result_int(pCtx, 0);
+ break;
+ }
+ case JSONB_INT5:
+ case JSONB_INT: {
+ sqlite3_int64 iRes = 0;
+ char *z;
+ int bNeg = 0;
+ char x;
+ if( sz==0 ) goto returnfromblob_malformed;
+ x = (char)pParse->aBlob[i+n];
+ if( x=='-' ){
+ if( sz<2 ) goto returnfromblob_malformed;
+ n++;
+ sz--;
+ bNeg = 1;
+ }
+ z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz);
+ if( z==0 ) goto returnfromblob_oom;
+ rc = sqlite3DecOrHexToI64(z, &iRes);
+ sqlite3DbFree(db, z);
+ if( rc==0 ){
+ sqlite3_result_int64(pCtx, bNeg ? -iRes : iRes);
+ }else if( rc==3 && bNeg ){
+ sqlite3_result_int64(pCtx, SMALLEST_INT64);
+ }else if( rc==1 ){
+ goto returnfromblob_malformed;
+ }else{
+ if( bNeg ){ n--; sz++; }
+ goto to_double;
+ }
+ break;
+ }
+ case JSONB_FLOAT5:
+ case JSONB_FLOAT: {
+ double r;
+ char *z;
+ if( sz==0 ) goto returnfromblob_malformed;
+ to_double:
+ z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz);
+ if( z==0 ) goto returnfromblob_oom;
+ rc = sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
+ sqlite3DbFree(db, z);
+ if( rc<=0 ) goto returnfromblob_malformed;
+ sqlite3_result_double(pCtx, r);
+ break;
+ }
+ case JSONB_TEXTRAW:
+ case JSONB_TEXT: {
+ sqlite3_result_text(pCtx, (char*)&pParse->aBlob[i+n], sz,
+ SQLITE_TRANSIENT);
+ break;
+ }
+ case JSONB_TEXT5:
+ case JSONB_TEXTJ: {
+ /* Translate JSON formatted string into raw text */
+ u32 iIn, iOut;
+ const char *z;
+ char *zOut;
+ u32 nOut = sz;
+ z = (const char*)&pParse->aBlob[i+n];
+ zOut = sqlite3DbMallocRaw(db, nOut+1);
+ if( zOut==0 ) goto returnfromblob_oom;
+ for(iIn=iOut=0; iIn<sz; iIn++){
+ char c = z[iIn];
+ if( c=='\\' ){
+ u32 v;
+ u32 szEscape = jsonUnescapeOneChar(&z[iIn], sz-iIn, &v);
+ if( v<=0x7f ){
+ zOut[iOut++] = (char)v;
+ }else if( v<=0x7ff ){
+ assert( szEscape>=2 );
+ zOut[iOut++] = (char)(0xc0 | (v>>6));
+ zOut[iOut++] = 0x80 | (v&0x3f);
+ }else if( v<0x10000 ){
+ assert( szEscape>=3 );
+ zOut[iOut++] = 0xe0 | (v>>12);
+ zOut[iOut++] = 0x80 | ((v>>6)&0x3f);
+ zOut[iOut++] = 0x80 | (v&0x3f);
+ }else if( v==JSON_INVALID_CHAR ){
+ /* Silently ignore illegal unicode */
+ }else{
+ assert( szEscape>=4 );
+ zOut[iOut++] = 0xf0 | (v>>18);
+ zOut[iOut++] = 0x80 | ((v>>12)&0x3f);
+ zOut[iOut++] = 0x80 | ((v>>6)&0x3f);
+ zOut[iOut++] = 0x80 | (v&0x3f);
+ }
+ iIn += szEscape - 1;
+ }else{
+ zOut[iOut++] = c;
+ }
+ } /* end for() */
+ assert( iOut<=nOut );
+ zOut[iOut] = 0;
+ sqlite3_result_text(pCtx, zOut, iOut, SQLITE_DYNAMIC);
+ break;
+ }
+ case JSONB_ARRAY:
+ case JSONB_OBJECT: {
+ int flags = textOnly ? 0 : SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx));
+ if( flags & JSON_BLOB ){
+ sqlite3_result_blob(pCtx, &pParse->aBlob[i], sz+n, SQLITE_TRANSIENT);
+ }else{
+ jsonReturnTextJsonFromBlob(pCtx, &pParse->aBlob[i], sz+n);
+ }
+ break;
+ }
+ default: {
+ goto returnfromblob_malformed;
+ }
+ }
+ return;
+
+returnfromblob_oom:
+ sqlite3_result_error_nomem(pCtx);
+ return;
+
+returnfromblob_malformed:
+ sqlite3_result_error(pCtx, "malformed JSON", -1);
+ return;
}
/*
-** Do a node lookup using zPath. Return a pointer to the node on success.
-** Return NULL if not found or if there is an error.
+** pArg is a function argument that might be an SQL value or a JSON
+** value. Figure out what it is and encode it as a JSONB blob.
+** Return the results in pParse.
**
-** On an error, write an error message into pCtx and increment the
-** pParse->nErr counter.
+** pParse is uninitialized upon entry. This routine will handle the
+** initialization of pParse. The result will be contained in
+** pParse->aBlob and pParse->nBlob. pParse->aBlob might be dynamically
+** allocated (if pParse->nBlobAlloc is greater than zero) in which case
+** the caller is responsible for freeing the space allocated to pParse->aBlob
+** when it has finished with it. Or pParse->aBlob might be a static string
+** or a value obtained from sqlite3_value_blob(pArg).
**
-** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if
-** nodes are appended.
+** If the argument is a BLOB that is clearly not a JSONB, then this
+** function might set an error message in ctx and return non-zero.
+** It might also set an error message and return non-zero on an OOM error.
*/
-static JsonNode *jsonLookup(
- JsonParse *pParse, /* The JSON to search */
- const char *zPath, /* The path to search */
- int *pApnd, /* Append nodes to complete path if not NULL */
- sqlite3_context *pCtx /* Report errors here, if not NULL */
-){
- const char *zErr = 0;
- JsonNode *pNode = 0;
- char *zMsg;
-
- if( zPath==0 ) return 0;
- if( zPath[0]!='$' ){
- zErr = zPath;
- goto lookup_err;
+static int jsonFunctionArgToBlob(
+ sqlite3_context *ctx,
+ sqlite3_value *pArg,
+ JsonParse *pParse
+){
+ int eType = sqlite3_value_type(pArg);
+ static u8 aNull[] = { 0x00 };
+ memset(pParse, 0, sizeof(pParse[0]));
+ pParse->db = sqlite3_context_db_handle(ctx);
+ switch( eType ){
+ default: {
+ pParse->aBlob = aNull;
+ pParse->nBlob = 1;
+ return 0;
+ }
+ case SQLITE_BLOB: {
+ if( jsonFuncArgMightBeBinary(pArg) ){
+ pParse->aBlob = (u8*)sqlite3_value_blob(pArg);
+ pParse->nBlob = sqlite3_value_bytes(pArg);
+ }else{
+ sqlite3_result_error(ctx, "JSON cannot hold BLOB values", -1);
+ return 1;
+ }
+ break;
+ }
+ case SQLITE_TEXT: {
+ const char *zJson = (const char*)sqlite3_value_text(pArg);
+ int nJson = sqlite3_value_bytes(pArg);
+ if( zJson==0 ) return 1;
+ if( sqlite3_value_subtype(pArg)==JSON_SUBTYPE ){
+ pParse->zJson = (char*)zJson;
+ pParse->nJson = nJson;
+ if( jsonConvertTextToBlob(pParse, ctx) ){
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ sqlite3DbFree(pParse->db, pParse->aBlob);
+ memset(pParse, 0, sizeof(pParse[0]));
+ return 1;
+ }
+ }else{
+ jsonBlobAppendNode(pParse, JSONB_TEXTRAW, nJson, zJson);
+ }
+ break;
+ }
+ case SQLITE_FLOAT: {
+ double r = sqlite3_value_double(pArg);
+ if( NEVER(sqlite3IsNaN(r)) ){
+ jsonBlobAppendNode(pParse, JSONB_NULL, 0, 0);
+ }else{
+ int n = sqlite3_value_bytes(pArg);
+ const char *z = (const char*)sqlite3_value_text(pArg);
+ if( z==0 ) return 1;
+ if( z[0]=='I' ){
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999");
+ }else if( z[0]=='-' && z[1]=='I' ){
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999");
+ }else{
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, n, z);
+ }
+ }
+ break;
+ }
+ case SQLITE_INTEGER: {
+ int n = sqlite3_value_bytes(pArg);
+ const char *z = (const char*)sqlite3_value_text(pArg);
+ if( z==0 ) return 1;
+ jsonBlobAppendNode(pParse, JSONB_INT, n, z);
+ break;
+ }
+ }
+ if( pParse->oom ){
+ sqlite3_result_error_nomem(ctx);
+ return 1;
+ }else{
+ return 0;
}
- zPath++;
- pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr);
- if( zErr==0 ) return pNode;
+}
-lookup_err:
- pParse->nErr++;
- assert( zErr!=0 && pCtx!=0 );
- zMsg = jsonPathSyntaxError(zErr);
+/*
+** Generate a bad path error.
+**
+** If ctx is not NULL then push the error message into ctx and return NULL.
+** If ctx is NULL, then return the text of the error message.
+*/
+static char *jsonBadPathError(
+ sqlite3_context *ctx, /* The function call containing the error */
+ const char *zPath /* The path with the problem */
+){
+ char *zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath);
+ if( ctx==0 ) return zMsg;
if( zMsg ){
- sqlite3_result_error(pCtx, zMsg, -1);
+ sqlite3_result_error(ctx, zMsg, -1);
sqlite3_free(zMsg);
}else{
- sqlite3_result_error_nomem(pCtx);
+ sqlite3_result_error_nomem(ctx);
}
return 0;
}
+/* argv[0] is a BLOB that seems likely to be a JSONB. Subsequent
+** arguments come in parse where each pair contains a JSON path and
+** content to insert or set at that patch. Do the updates
+** and return the result.
+**
+** The specific operation is determined by eEdit, which can be one
+** of JEDIT_INS, JEDIT_REPL, or JEDIT_SET.
+*/
+static void jsonInsertIntoBlob(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv,
+ int eEdit /* JEDIT_INS, JEDIT_REPL, or JEDIT_SET */
+){
+ int i;
+ u32 rc = 0;
+ const char *zPath = 0;
+ int flgs;
+ JsonParse *p;
+ JsonParse ax;
+
+ assert( (argc&1)==1 );
+ flgs = argc==1 ? 0 : JSON_EDITABLE;
+ p = jsonParseFuncArg(ctx, argv[0], flgs);
+ if( p==0 ) return;
+ for(i=1; i<argc-1; i+=2){
+ if( sqlite3_value_type(argv[i])==SQLITE_NULL ) continue;
+ zPath = (const char*)sqlite3_value_text(argv[i]);
+ if( zPath==0 ){
+ sqlite3_result_error_nomem(ctx);
+ jsonParseFree(p);
+ return;
+ }
+ if( zPath[0]!='$' ) goto jsonInsertIntoBlob_patherror;
+ if( jsonFunctionArgToBlob(ctx, argv[i+1], &ax) ){
+ jsonParseReset(&ax);
+ jsonParseFree(p);
+ return;
+ }
+ if( zPath[1]==0 ){
+ if( eEdit==JEDIT_REPL || eEdit==JEDIT_SET ){
+ jsonBlobEdit(p, 0, p->nBlob, ax.aBlob, ax.nBlob);
+ }
+ rc = 0;
+ }else{
+ p->eEdit = eEdit;
+ p->nIns = ax.nBlob;
+ p->aIns = ax.aBlob;
+ p->delta = 0;
+ rc = jsonLookupStep(p, 0, zPath+1, 0);
+ }
+ jsonParseReset(&ax);
+ if( rc==JSON_LOOKUP_NOTFOUND ) continue;
+ if( JSON_LOOKUP_ISERROR(rc) ) goto jsonInsertIntoBlob_patherror;
+ }
+ jsonReturnParse(ctx, p);
+ jsonParseFree(p);
+ return;
+
+jsonInsertIntoBlob_patherror:
+ jsonParseFree(p);
+ if( rc==JSON_LOOKUP_ERROR ){
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ }else{
+ jsonBadPathError(ctx, zPath);
+ }
+ return;
+}
/*
-** Report the wrong number of arguments for json_insert(), json_replace()
-** or json_set().
+** If pArg is a blob that seems like a JSONB blob, then initialize
+** p to point to that JSONB and return TRUE. If pArg does not seem like
+** a JSONB blob, then return FALSE;
+**
+** This routine is only called if it is already known that pArg is a
+** blob. The only open question is whether or not the blob appears
+** to be a JSONB blob.
*/
-static void jsonWrongNumArgs(
- sqlite3_context *pCtx,
- const char *zFuncName
-){
- char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments",
- zFuncName);
- sqlite3_result_error(pCtx, zMsg, -1);
- sqlite3_free(zMsg);
+static int jsonArgIsJsonb(sqlite3_value *pArg, JsonParse *p){
+ u32 n, sz = 0;
+ p->aBlob = (u8*)sqlite3_value_blob(pArg);
+ p->nBlob = (u32)sqlite3_value_bytes(pArg);
+ if( p->nBlob==0 ){
+ p->aBlob = 0;
+ return 0;
+ }
+ if( NEVER(p->aBlob==0) ){
+ return 0;
+ }
+ if( (p->aBlob[0] & 0x0f)<=JSONB_OBJECT
+ && (n = jsonbPayloadSize(p, 0, &sz))>0
+ && sz+n==p->nBlob
+ && ((p->aBlob[0] & 0x0f)>JSONB_FALSE || sz==0)
+ ){
+ return 1;
+ }
+ p->aBlob = 0;
+ p->nBlob = 0;
+ return 0;
}
/*
-** Mark all NULL entries in the Object passed in as JNODE_REMOVE.
+** Generate a JsonParse object, containing valid JSONB in aBlob and nBlob,
+** from the SQL function argument pArg. Return a pointer to the new
+** JsonParse object.
+**
+** Ownership of the new JsonParse object is passed to the caller. The
+** caller should invoke jsonParseFree() on the return value when it
+** has finished using it.
+**
+** If any errors are detected, an appropriate error messages is set
+** using sqlite3_result_error() or the equivalent and this routine
+** returns NULL. This routine also returns NULL if the pArg argument
+** is an SQL NULL value, but no error message is set in that case. This
+** is so that SQL functions that are given NULL arguments will return
+** a NULL value.
*/
-static void jsonRemoveAllNulls(JsonNode *pNode){
- int i, n;
- assert( pNode->eType==JSON_OBJECT );
- n = pNode->n;
- for(i=2; i<=n; i += jsonNodeSize(&pNode[i])+1){
- switch( pNode[i].eType ){
- case JSON_NULL:
- pNode[i].jnFlags |= JNODE_REMOVE;
- break;
- case JSON_OBJECT:
- jsonRemoveAllNulls(&pNode[i]);
- break;
+static JsonParse *jsonParseFuncArg(
+ sqlite3_context *ctx,
+ sqlite3_value *pArg,
+ u32 flgs
+){
+ int eType; /* Datatype of pArg */
+ JsonParse *p = 0; /* Value to be returned */
+ JsonParse *pFromCache = 0; /* Value taken from cache */
+ sqlite3 *db; /* The database connection */
+
+ assert( ctx!=0 );
+ eType = sqlite3_value_type(pArg);
+ if( eType==SQLITE_NULL ){
+ return 0;
+ }
+ pFromCache = jsonCacheSearch(ctx, pArg);
+ if( pFromCache ){
+ pFromCache->nJPRef++;
+ if( (flgs & JSON_EDITABLE)==0 ){
+ return pFromCache;
}
}
+ db = sqlite3_context_db_handle(ctx);
+rebuild_from_cache:
+ p = sqlite3DbMallocZero(db, sizeof(*p));
+ if( p==0 ) goto json_pfa_oom;
+ memset(p, 0, sizeof(*p));
+ p->db = db;
+ p->nJPRef = 1;
+ if( pFromCache!=0 ){
+ u32 nBlob = pFromCache->nBlob;
+ p->aBlob = sqlite3DbMallocRaw(db, nBlob);
+ if( p->aBlob==0 ) goto json_pfa_oom;
+ memcpy(p->aBlob, pFromCache->aBlob, nBlob);
+ p->nBlobAlloc = p->nBlob = nBlob;
+ p->hasNonstd = pFromCache->hasNonstd;
+ jsonParseFree(pFromCache);
+ return p;
+ }
+ if( eType==SQLITE_BLOB ){
+ if( jsonArgIsJsonb(pArg,p) ){
+ if( (flgs & JSON_EDITABLE)!=0 && jsonBlobMakeEditable(p, 0)==0 ){
+ goto json_pfa_oom;
+ }
+ return p;
+ }
+ /* If the blob is not valid JSONB, fall through into trying to cast
+ ** the blob into text which is then interpreted as JSON. (tag-20240123-a)
+ **
+ ** This goes against all historical documentation about how the SQLite
+ ** JSON functions were suppose to work. From the beginning, blob was
+ ** reserved for expansion and a blob value should have raised an error.
+ ** But it did not, due to a bug. And many applications came to depend
+ ** upon this buggy behavior, espeically when using the CLI and reading
+ ** JSON text using readfile(), which returns a blob. For this reason
+ ** we will continue to support the bug moving forward.
+ ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d
+ */
+ }
+ p->zJson = (char*)sqlite3_value_text(pArg);
+ p->nJson = sqlite3_value_bytes(pArg);
+ if( p->nJson==0 ) goto json_pfa_malformed;
+ if( NEVER(p->zJson==0) ) goto json_pfa_oom;
+ if( jsonConvertTextToBlob(p, (flgs & JSON_KEEPERROR) ? 0 : ctx) ){
+ if( flgs & JSON_KEEPERROR ){
+ p->nErr = 1;
+ return p;
+ }else{
+ jsonParseFree(p);
+ return 0;
+ }
+ }else{
+ int isRCStr = sqlite3ValueIsOfClass(pArg, sqlite3RCStrUnref);
+ int rc;
+ if( !isRCStr ){
+ char *zNew = sqlite3RCStrNew( p->nJson );
+ if( zNew==0 ) goto json_pfa_oom;
+ memcpy(zNew, p->zJson, p->nJson);
+ p->zJson = zNew;
+ p->zJson[p->nJson] = 0;
+ }else{
+ sqlite3RCStrRef(p->zJson);
+ }
+ p->bJsonIsRCStr = 1;
+ rc = jsonCacheInsert(ctx, p);
+ if( rc==SQLITE_NOMEM ) goto json_pfa_oom;
+ if( flgs & JSON_EDITABLE ){
+ pFromCache = p;
+ p = 0;
+ goto rebuild_from_cache;
+ }
+ }
+ return p;
+
+json_pfa_malformed:
+ if( flgs & JSON_KEEPERROR ){
+ p->nErr = 1;
+ return p;
+ }else{
+ jsonParseFree(p);
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ return 0;
+ }
+
+json_pfa_oom:
+ jsonParseFree(pFromCache);
+ jsonParseFree(p);
+ sqlite3_result_error_nomem(ctx);
+ return 0;
}
+/*
+** Make the return value of a JSON function either the raw JSONB blob
+** or make it JSON text, depending on whether the JSON_BLOB flag is
+** set on the function.
+*/
+static void jsonReturnParse(
+ sqlite3_context *ctx,
+ JsonParse *p
+){
+ int flgs;
+ if( p->oom ){
+ sqlite3_result_error_nomem(ctx);
+ return;
+ }
+ flgs = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+ if( flgs & JSON_BLOB ){
+ if( p->nBlobAlloc>0 && !p->bReadOnly ){
+ sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_DYNAMIC);
+ p->nBlobAlloc = 0;
+ }else{
+ sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_TRANSIENT);
+ }
+ }else{
+ JsonString s;
+ jsonStringInit(&s, ctx);
+ p->delta = 0;
+ jsonTranslateBlobToText(p, 0, &s);
+ jsonReturnString(&s, p, ctx);
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+ }
+}
/****************************************************************************
** SQL functions used for testing and debugging
@@ -203539,63 +206596,124 @@ static void jsonRemoveAllNulls(JsonNode *pNode){
#if SQLITE_DEBUG
/*
-** Print N node entries.
-*/
-static void jsonDebugPrintNodeEntries(
- JsonNode *aNode, /* First node entry to print */
- int N /* Number of node entries to print */
-){
- int i;
- for(i=0; i<N; i++){
- const char *zType;
- if( aNode[i].jnFlags & JNODE_LABEL ){
- zType = "label";
- }else{
- zType = jsonType[aNode[i].eType];
+** Decode JSONB bytes in aBlob[] starting at iStart through but not
+** including iEnd. Indent the
+** content by nIndent spaces.
+*/
+static void jsonDebugPrintBlob(
+ JsonParse *pParse, /* JSON content */
+ u32 iStart, /* Start rendering here */
+ u32 iEnd, /* Do not render this byte or any byte after this one */
+ int nIndent, /* Indent by this many spaces */
+ sqlite3_str *pOut /* Generate output into this sqlite3_str object */
+){
+ while( iStart<iEnd ){
+ u32 i, n, nn, sz = 0;
+ int showContent = 1;
+ u8 x = pParse->aBlob[iStart] & 0x0f;
+ u32 savedNBlob = pParse->nBlob;
+ sqlite3_str_appendf(pOut, "%5d:%*s", iStart, nIndent, "");
+ if( pParse->nBlobAlloc>pParse->nBlob ){
+ pParse->nBlob = pParse->nBlobAlloc;
+ }
+ nn = n = jsonbPayloadSize(pParse, iStart, &sz);
+ if( nn==0 ) nn = 1;
+ if( sz>0 && x<JSONB_ARRAY ){
+ nn += sz;
+ }
+ for(i=0; i<nn; i++){
+ sqlite3_str_appendf(pOut, " %02x", pParse->aBlob[iStart+i]);
+ }
+ if( n==0 ){
+ sqlite3_str_appendf(pOut, " ERROR invalid node size\n");
+ iStart = n==0 ? iStart+1 : iEnd;
+ continue;
}
- printf("node %4u: %-7s n=%-5d", i, zType, aNode[i].n);
- if( (aNode[i].jnFlags & ~JNODE_LABEL)!=0 ){
- u8 f = aNode[i].jnFlags;
- if( f & JNODE_RAW ) printf(" RAW");
- if( f & JNODE_ESCAPE ) printf(" ESCAPE");
- if( f & JNODE_REMOVE ) printf(" REMOVE");
- if( f & JNODE_REPLACE ) printf(" REPLACE");
- if( f & JNODE_APPEND ) printf(" APPEND");
- if( f & JNODE_JSON5 ) printf(" JSON5");
+ pParse->nBlob = savedNBlob;
+ if( iStart+n+sz>iEnd ){
+ iEnd = iStart+n+sz;
+ if( iEnd>pParse->nBlob ){
+ if( pParse->nBlobAlloc>0 && iEnd>pParse->nBlobAlloc ){
+ iEnd = pParse->nBlobAlloc;
+ }else{
+ iEnd = pParse->nBlob;
+ }
+ }
+ }
+ sqlite3_str_appendall(pOut," <-- ");
+ switch( x ){
+ case JSONB_NULL: sqlite3_str_appendall(pOut,"null"); break;
+ case JSONB_TRUE: sqlite3_str_appendall(pOut,"true"); break;
+ case JSONB_FALSE: sqlite3_str_appendall(pOut,"false"); break;
+ case JSONB_INT: sqlite3_str_appendall(pOut,"int"); break;
+ case JSONB_INT5: sqlite3_str_appendall(pOut,"int5"); break;
+ case JSONB_FLOAT: sqlite3_str_appendall(pOut,"float"); break;
+ case JSONB_FLOAT5: sqlite3_str_appendall(pOut,"float5"); break;
+ case JSONB_TEXT: sqlite3_str_appendall(pOut,"text"); break;
+ case JSONB_TEXTJ: sqlite3_str_appendall(pOut,"textj"); break;
+ case JSONB_TEXT5: sqlite3_str_appendall(pOut,"text5"); break;
+ case JSONB_TEXTRAW: sqlite3_str_appendall(pOut,"textraw"); break;
+ case JSONB_ARRAY: {
+ sqlite3_str_appendf(pOut,"array, %u bytes\n", sz);
+ jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2, pOut);
+ showContent = 0;
+ break;
+ }
+ case JSONB_OBJECT: {
+ sqlite3_str_appendf(pOut, "object, %u bytes\n", sz);
+ jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2, pOut);
+ showContent = 0;
+ break;
+ }
+ default: {
+ sqlite3_str_appendall(pOut, "ERROR: unknown node type\n");
+ showContent = 0;
+ break;
+ }
}
- switch( aNode[i].eU ){
- case 1: printf(" zJContent=[%.*s]\n",
- aNode[i].n, aNode[i].u.zJContent); break;
- case 2: printf(" iAppend=%u\n", aNode[i].u.iAppend); break;
- case 3: printf(" iKey=%u\n", aNode[i].u.iKey); break;
- case 4: printf(" iPrev=%u\n", aNode[i].u.iPrev); break;
- default: printf("\n");
+ if( showContent ){
+ if( sz==0 && x<=JSONB_FALSE ){
+ sqlite3_str_append(pOut, "\n", 1);
+ }else{
+ u32 i;
+ sqlite3_str_appendall(pOut, ": \"");
+ for(i=iStart+n; i<iStart+n+sz; i++){
+ u8 c = pParse->aBlob[i];
+ if( c<0x20 || c>=0x7f ) c = '.';
+ sqlite3_str_append(pOut, (char*)&c, 1);
+ }
+ sqlite3_str_append(pOut, "\"\n", 2);
+ }
}
+ iStart += n + sz;
}
}
-#endif /* SQLITE_DEBUG */
-
-
-#if 0 /* 1 for debugging. 0 normally. Requires -DSQLITE_DEBUG too */
-static void jsonDebugPrintParse(JsonParse *p){
- jsonDebugPrintNodeEntries(p->aNode, p->nNode);
-}
-static void jsonDebugPrintNode(JsonNode *pNode){
- jsonDebugPrintNodeEntries(pNode, jsonNodeSize(pNode));
+static void jsonShowParse(JsonParse *pParse){
+ sqlite3_str out;
+ char zBuf[1000];
+ if( pParse==0 ){
+ printf("NULL pointer\n");
+ return;
+ }else{
+ printf("nBlobAlloc = %u\n", pParse->nBlobAlloc);
+ printf("nBlob = %u\n", pParse->nBlob);
+ printf("delta = %d\n", pParse->delta);
+ if( pParse->nBlob==0 ) return;
+ printf("content (bytes 0..%u):\n", pParse->nBlob-1);
+ }
+ sqlite3StrAccumInit(&out, 0, zBuf, sizeof(zBuf), 1000000);
+ jsonDebugPrintBlob(pParse, 0, pParse->nBlob, 0, &out);
+ printf("%s", sqlite3_str_value(&out));
+ sqlite3_str_reset(&out);
}
-#else
- /* The usual case */
-# define jsonDebugPrintNode(X)
-# define jsonDebugPrintParse(X)
-#endif
+#endif /* SQLITE_DEBUG */
#ifdef SQLITE_DEBUG
/*
** SQL function: json_parse(JSON)
**
-** Parse JSON using jsonParseCached(). Then print a dump of that
-** parse on standard output. Return the mimified JSON result, just
-** like the json() function.
+** Parse JSON using jsonParseFuncArg(). Return text that is a
+** human-readable dump of the binary JSONB for the input parameter.
*/
static void jsonParseFunc(
sqlite3_context *ctx,
@@ -203603,38 +206721,19 @@ static void jsonParseFunc(
sqlite3_value **argv
){
JsonParse *p; /* The parse */
+ sqlite3_str out;
- assert( argc==1 );
- p = jsonParseCached(ctx, argv[0], ctx, 0);
+ assert( argc>=1 );
+ sqlite3StrAccumInit(&out, 0, 0, 0, 1000000);
+ p = jsonParseFuncArg(ctx, argv[0], 0);
if( p==0 ) return;
- printf("nNode = %u\n", p->nNode);
- printf("nAlloc = %u\n", p->nAlloc);
- printf("nJson = %d\n", p->nJson);
- printf("nAlt = %d\n", p->nAlt);
- printf("nErr = %u\n", p->nErr);
- printf("oom = %u\n", p->oom);
- printf("hasNonstd = %u\n", p->hasNonstd);
- printf("useMod = %u\n", p->useMod);
- printf("hasMod = %u\n", p->hasMod);
- printf("nJPRef = %u\n", p->nJPRef);
- printf("iSubst = %u\n", p->iSubst);
- printf("iHold = %u\n", p->iHold);
- jsonDebugPrintNodeEntries(p->aNode, p->nNode);
- jsonReturnJson(p, p->aNode, ctx, 1);
-}
-
-/*
-** The json_test1(JSON) function return true (1) if the input is JSON
-** text generated by another json function. It returns (0) if the input
-** is not known to be JSON.
-*/
-static void jsonTest1Func(
- sqlite3_context *ctx,
- int argc,
- sqlite3_value **argv
-){
- UNUSED_PARAMETER(argc);
- sqlite3_result_int(ctx, sqlite3_value_subtype(argv[0])==JSON_SUBTYPE);
+ if( argc==1 ){
+ jsonDebugPrintBlob(p, 0, p->nBlob, 0, &out);
+ sqlite3_result_text64(ctx, out.zText, out.nChar, SQLITE_DYNAMIC, SQLITE_UTF8);
+ }else{
+ jsonShowParse(p);
+ }
+ jsonParseFree(p);
}
#endif /* SQLITE_DEBUG */
@@ -203643,7 +206742,7 @@ static void jsonTest1Func(
****************************************************************************/
/*
-** Implementation of the json_QUOTE(VALUE) function. Return a JSON value
+** Implementation of the json_quote(VALUE) function. Return a JSON value
** corresponding to the SQL value input. Mostly this means putting
** double-quotes around strings and returning the unquoted string "null"
** when given a NULL input.
@@ -203656,9 +206755,9 @@ static void jsonQuoteFunc(
JsonString jx;
UNUSED_PARAMETER(argc);
- jsonInit(&jx, ctx);
- jsonAppendValue(&jx, argv[0]);
- jsonResult(&jx);
+ jsonStringInit(&jx, ctx);
+ jsonAppendSqlValue(&jx, argv[0]);
+ jsonReturnString(&jx, 0, 0);
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
@@ -203675,18 +206774,17 @@ static void jsonArrayFunc(
int i;
JsonString jx;
- jsonInit(&jx, ctx);
+ jsonStringInit(&jx, ctx);
jsonAppendChar(&jx, '[');
for(i=0; i<argc; i++){
jsonAppendSeparator(&jx);
- jsonAppendValue(&jx, argv[i]);
+ jsonAppendSqlValue(&jx, argv[i]);
}
jsonAppendChar(&jx, ']');
- jsonResult(&jx);
+ jsonReturnString(&jx, 0, 0);
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
-
/*
** json_array_length(JSON)
** json_array_length(JSON, PATH)
@@ -203700,46 +206798,53 @@ static void jsonArrayLengthFunc(
sqlite3_value **argv
){
JsonParse *p; /* The parse */
- sqlite3_int64 n = 0;
+ sqlite3_int64 cnt = 0;
u32 i;
- JsonNode *pNode;
+ u8 eErr = 0;
- p = jsonParseCached(ctx, argv[0], ctx, 0);
+ p = jsonParseFuncArg(ctx, argv[0], 0);
if( p==0 ) return;
- assert( p->nNode );
if( argc==2 ){
const char *zPath = (const char*)sqlite3_value_text(argv[1]);
- pNode = jsonLookup(p, zPath, 0, ctx);
- }else{
- pNode = p->aNode;
- }
- if( pNode==0 ){
- return;
- }
- if( pNode->eType==JSON_ARRAY ){
- while( 1 /*exit-by-break*/ ){
- i = 1;
- while( i<=pNode->n ){
- if( (pNode[i].jnFlags & JNODE_REMOVE)==0 ) n++;
- i += jsonNodeSize(&pNode[i]);
+ if( zPath==0 ){
+ jsonParseFree(p);
+ return;
+ }
+ i = jsonLookupStep(p, 0, zPath[0]=='$' ? zPath+1 : "@", 0);
+ if( JSON_LOOKUP_ISERROR(i) ){
+ if( i==JSON_LOOKUP_NOTFOUND ){
+ /* no-op */
+ }else if( i==JSON_LOOKUP_PATHERROR ){
+ jsonBadPathError(ctx, zPath);
+ }else{
+ sqlite3_result_error(ctx, "malformed JSON", -1);
}
- if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
- if( p->useMod==0 ) break;
- assert( pNode->eU==2 );
- pNode = &p->aNode[pNode->u.iAppend];
+ eErr = 1;
+ i = 0;
}
+ }else{
+ i = 0;
+ }
+ if( (p->aBlob[i] & 0x0f)==JSONB_ARRAY ){
+ cnt = jsonbArrayCount(p, i);
}
- sqlite3_result_int64(ctx, n);
+ if( !eErr ) sqlite3_result_int64(ctx, cnt);
+ jsonParseFree(p);
}
-/*
-** Bit values for the flags passed into jsonExtractFunc() or
-** jsonSetFunc() via the user-data value.
-*/
-#define JSON_JSON 0x01 /* Result is always JSON */
-#define JSON_SQL 0x02 /* Result is always SQL */
-#define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */
-#define JSON_ISSET 0x04 /* json_set(), not json_insert() */
+/* True if the string is all digits */
+static int jsonAllDigits(const char *z, int n){
+ int i;
+ for(i=0; i<n && sqlite3Isdigit(z[i]); i++){}
+ return i==n;
+}
+
+/* True if the string is all alphanumerics and underscores */
+static int jsonAllAlphanum(const char *z, int n){
+ int i;
+ for(i=0; i<n && (sqlite3Isalnum(z[i]) || z[i]=='_'); i++){}
+ return i==n;
+}
/*
** json_extract(JSON, PATH, ...)
@@ -203766,152 +206871,307 @@ static void jsonExtractFunc(
int argc,
sqlite3_value **argv
){
- JsonParse *p; /* The parse */
- JsonNode *pNode;
- const char *zPath;
- int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
- JsonString jx;
+ JsonParse *p = 0; /* The parse */
+ int flags; /* Flags associated with the function */
+ int i; /* Loop counter */
+ JsonString jx; /* String for array result */
if( argc<2 ) return;
- p = jsonParseCached(ctx, argv[0], ctx, 0);
+ p = jsonParseFuncArg(ctx, argv[0], 0);
if( p==0 ) return;
- if( argc==2 ){
+ flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+ jsonStringInit(&jx, ctx);
+ if( argc>2 ){
+ jsonAppendChar(&jx, '[');
+ }
+ for(i=1; i<argc; i++){
/* With a single PATH argument */
- zPath = (const char*)sqlite3_value_text(argv[1]);
- if( zPath==0 ) return;
- if( flags & JSON_ABPATH ){
- if( zPath[0]!='$' || (zPath[1]!='.' && zPath[1]!='[' && zPath[1]!=0) ){
- /* The -> and ->> operators accept abbreviated PATH arguments. This
- ** is mostly for compatibility with PostgreSQL, but also for
- ** convenience.
- **
- ** NUMBER ==> $[NUMBER] // PG compatible
- ** LABEL ==> $.LABEL // PG compatible
- ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience
- */
- jsonInit(&jx, ctx);
- if( sqlite3Isdigit(zPath[0]) ){
- jsonAppendRawNZ(&jx, "$[", 2);
- jsonAppendRaw(&jx, zPath, (int)strlen(zPath));
- jsonAppendRawNZ(&jx, "]", 2);
- }else{
- jsonAppendRawNZ(&jx, "$.", 1 + (zPath[0]!='['));
- jsonAppendRaw(&jx, zPath, (int)strlen(zPath));
- jsonAppendChar(&jx, 0);
- }
- pNode = jx.bErr ? 0 : jsonLookup(p, jx.zBuf, 0, ctx);
- jsonReset(&jx);
+ const char *zPath = (const char*)sqlite3_value_text(argv[i]);
+ int nPath;
+ u32 j;
+ if( zPath==0 ) goto json_extract_error;
+ nPath = sqlite3Strlen30(zPath);
+ if( zPath[0]=='$' ){
+ j = jsonLookupStep(p, 0, zPath+1, 0);
+ }else if( (flags & JSON_ABPATH) ){
+ /* The -> and ->> operators accept abbreviated PATH arguments. This
+ ** is mostly for compatibility with PostgreSQL, but also for
+ ** convenience.
+ **
+ ** NUMBER ==> $[NUMBER] // PG compatible
+ ** LABEL ==> $.LABEL // PG compatible
+ ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience
+ */
+ jsonStringInit(&jx, ctx);
+ if( jsonAllDigits(zPath, nPath) ){
+ jsonAppendRawNZ(&jx, "[", 1);
+ jsonAppendRaw(&jx, zPath, nPath);
+ jsonAppendRawNZ(&jx, "]", 2);
+ }else if( jsonAllAlphanum(zPath, nPath) ){
+ jsonAppendRawNZ(&jx, ".", 1);
+ jsonAppendRaw(&jx, zPath, nPath);
+ }else if( zPath[0]=='[' && nPath>=3 && zPath[nPath-1]==']' ){
+ jsonAppendRaw(&jx, zPath, nPath);
}else{
- pNode = jsonLookup(p, zPath, 0, ctx);
+ jsonAppendRawNZ(&jx, ".\"", 2);
+ jsonAppendRaw(&jx, zPath, nPath);
+ jsonAppendRawNZ(&jx, "\"", 1);
}
- if( pNode ){
+ jsonStringTerminate(&jx);
+ j = jsonLookupStep(p, 0, jx.zBuf, 0);
+ jsonStringReset(&jx);
+ }else{
+ jsonBadPathError(ctx, zPath);
+ goto json_extract_error;
+ }
+ if( j<p->nBlob ){
+ if( argc==2 ){
if( flags & JSON_JSON ){
- jsonReturnJson(p, pNode, ctx, 0);
+ jsonStringInit(&jx, ctx);
+ jsonTranslateBlobToText(p, j, &jx);
+ jsonReturnString(&jx, 0, 0);
+ jsonStringReset(&jx);
+ assert( (flags & JSON_BLOB)==0 );
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}else{
- jsonReturn(p, pNode, ctx);
- sqlite3_result_subtype(ctx, 0);
+ jsonReturnFromBlob(p, j, ctx, 0);
+ if( (flags & (JSON_SQL|JSON_BLOB))==0
+ && (p->aBlob[j]&0x0f)>=JSONB_ARRAY
+ ){
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+ }
}
+ }else{
+ jsonAppendSeparator(&jx);
+ jsonTranslateBlobToText(p, j, &jx);
}
- }else{
- pNode = jsonLookup(p, zPath, 0, ctx);
- if( p->nErr==0 && pNode ) jsonReturn(p, pNode, ctx);
- }
- }else{
- /* Two or more PATH arguments results in a JSON array with each
- ** element of the array being the value selected by one of the PATHs */
- int i;
- jsonInit(&jx, ctx);
- jsonAppendChar(&jx, '[');
- for(i=1; i<argc; i++){
- zPath = (const char*)sqlite3_value_text(argv[i]);
- pNode = jsonLookup(p, zPath, 0, ctx);
- if( p->nErr ) break;
- jsonAppendSeparator(&jx);
- if( pNode ){
- jsonRenderNode(p, pNode, &jx);
+ }else if( j==JSON_LOOKUP_NOTFOUND ){
+ if( argc==2 ){
+ goto json_extract_error; /* Return NULL if not found */
}else{
+ jsonAppendSeparator(&jx);
jsonAppendRawNZ(&jx, "null", 4);
}
+ }else if( j==JSON_LOOKUP_ERROR ){
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ goto json_extract_error;
+ }else{
+ jsonBadPathError(ctx, zPath);
+ goto json_extract_error;
}
- if( i==argc ){
- jsonAppendChar(&jx, ']');
- jsonResult(&jx);
+ }
+ if( argc>2 ){
+ jsonAppendChar(&jx, ']');
+ jsonReturnString(&jx, 0, 0);
+ if( (flags & JSON_BLOB)==0 ){
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
- jsonReset(&jx);
}
+json_extract_error:
+ jsonStringReset(&jx);
+ jsonParseFree(p);
+ return;
}
-/* This is the RFC 7396 MergePatch algorithm.
-*/
-static JsonNode *jsonMergePatch(
- JsonParse *pParse, /* The JSON parser that contains the TARGET */
- u32 iTarget, /* Node of the TARGET in pParse */
- JsonNode *pPatch /* The PATCH */
-){
- u32 i, j;
- u32 iRoot;
- JsonNode *pTarget;
- if( pPatch->eType!=JSON_OBJECT ){
- return pPatch;
- }
- assert( iTarget<pParse->nNode );
- pTarget = &pParse->aNode[iTarget];
- assert( (pPatch->jnFlags & JNODE_APPEND)==0 );
- if( pTarget->eType!=JSON_OBJECT ){
- jsonRemoveAllNulls(pPatch);
- return pPatch;
- }
- iRoot = iTarget;
- for(i=1; i<pPatch->n; i += jsonNodeSize(&pPatch[i+1])+1){
- u32 nKey;
- const char *zKey;
- assert( pPatch[i].eType==JSON_STRING );
- assert( pPatch[i].jnFlags & JNODE_LABEL );
- assert( pPatch[i].eU==1 );
- nKey = pPatch[i].n;
- zKey = pPatch[i].u.zJContent;
- for(j=1; j<pTarget->n; j += jsonNodeSize(&pTarget[j+1])+1 ){
- assert( pTarget[j].eType==JSON_STRING );
- assert( pTarget[j].jnFlags & JNODE_LABEL );
- if( jsonSameLabel(&pPatch[i], &pTarget[j]) ){
- if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_REPLACE) ) break;
- if( pPatch[i+1].eType==JSON_NULL ){
- pTarget[j+1].jnFlags |= JNODE_REMOVE;
- }else{
- JsonNode *pNew = jsonMergePatch(pParse, iTarget+j+1, &pPatch[i+1]);
- if( pNew==0 ) return 0;
- if( pNew!=&pParse->aNode[iTarget+j+1] ){
- jsonParseAddSubstNode(pParse, iTarget+j+1);
- jsonParseAddNodeArray(pParse, pNew, jsonNodeSize(pNew));
- }
- pTarget = &pParse->aNode[iTarget];
- }
- break;
+/*
+** Return codes for jsonMergePatch()
+*/
+#define JSON_MERGE_OK 0 /* Success */
+#define JSON_MERGE_BADTARGET 1 /* Malformed TARGET blob */
+#define JSON_MERGE_BADPATCH 2 /* Malformed PATCH blob */
+#define JSON_MERGE_OOM 3 /* Out-of-memory condition */
+
+/*
+** RFC-7396 MergePatch for two JSONB blobs.
+**
+** pTarget is the target. pPatch is the patch. The target is updated
+** in place. The patch is read-only.
+**
+** The original RFC-7396 algorithm is this:
+**
+** define MergePatch(Target, Patch):
+** if Patch is an Object:
+** if Target is not an Object:
+** Target = {} # Ignore the contents and set it to an empty Object
+** for each Name/Value pair in Patch:
+** if Value is null:
+** if Name exists in Target:
+** remove the Name/Value pair from Target
+** else:
+** Target[Name] = MergePatch(Target[Name], Value)
+** return Target
+** else:
+** return Patch
+**
+** Here is an equivalent algorithm restructured to show the actual
+** implementation:
+**
+** 01 define MergePatch(Target, Patch):
+** 02 if Patch is not an Object:
+** 03 return Patch
+** 04 else: // if Patch is an Object
+** 05 if Target is not an Object:
+** 06 Target = {}
+** 07 for each Name/Value pair in Patch:
+** 08 if Name exists in Target:
+** 09 if Value is null:
+** 10 remove the Name/Value pair from Target
+** 11 else
+** 12 Target[name] = MergePatch(Target[Name], Value)
+** 13 else if Value is not NULL:
+** 14 if Value is not an Object:
+** 15 Target[name] = Value
+** 16 else:
+** 17 Target[name] = MergePatch('{}',value)
+** 18 return Target
+** |
+** ^---- Line numbers referenced in comments in the implementation
+*/
+static int jsonMergePatch(
+ JsonParse *pTarget, /* The JSON parser that contains the TARGET */
+ u32 iTarget, /* Index of TARGET in pTarget->aBlob[] */
+ const JsonParse *pPatch, /* The PATCH */
+ u32 iPatch /* Index of PATCH in pPatch->aBlob[] */
+){
+ u8 x; /* Type of a single node */
+ u32 n, sz=0; /* Return values from jsonbPayloadSize() */
+ u32 iTCursor; /* Cursor position while scanning the target object */
+ u32 iTStart; /* First label in the target object */
+ u32 iTEndBE; /* Original first byte past end of target, before edit */
+ u32 iTEnd; /* Current first byte past end of target */
+ u8 eTLabel; /* Node type of the target label */
+ u32 iTLabel = 0; /* Index of the label */
+ u32 nTLabel = 0; /* Header size in bytes for the target label */
+ u32 szTLabel = 0; /* Size of the target label payload */
+ u32 iTValue = 0; /* Index of the target value */
+ u32 nTValue = 0; /* Header size of the target value */
+ u32 szTValue = 0; /* Payload size for the target value */
+
+ u32 iPCursor; /* Cursor position while scanning the patch */
+ u32 iPEnd; /* First byte past the end of the patch */
+ u8 ePLabel; /* Node type of the patch label */
+ u32 iPLabel; /* Start of patch label */
+ u32 nPLabel; /* Size of header on the patch label */
+ u32 szPLabel; /* Payload size of the patch label */
+ u32 iPValue; /* Start of patch value */
+ u32 nPValue; /* Header size for the patch value */
+ u32 szPValue; /* Payload size of the patch value */
+
+ assert( iTarget>=0 && iTarget<pTarget->nBlob );
+ assert( iPatch>=0 && iPatch<pPatch->nBlob );
+ x = pPatch->aBlob[iPatch] & 0x0f;
+ if( x!=JSONB_OBJECT ){ /* Algorithm line 02 */
+ u32 szPatch; /* Total size of the patch, header+payload */
+ u32 szTarget; /* Total size of the target, header+payload */
+ n = jsonbPayloadSize(pPatch, iPatch, &sz);
+ szPatch = n+sz;
+ sz = 0;
+ n = jsonbPayloadSize(pTarget, iTarget, &sz);
+ szTarget = n+sz;
+ jsonBlobEdit(pTarget, iTarget, szTarget, pPatch->aBlob+iPatch, szPatch);
+ return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK; /* Line 03 */
+ }
+ x = pTarget->aBlob[iTarget] & 0x0f;
+ if( x!=JSONB_OBJECT ){ /* Algorithm line 05 */
+ n = jsonbPayloadSize(pTarget, iTarget, &sz);
+ jsonBlobEdit(pTarget, iTarget+n, sz, 0, 0);
+ x = pTarget->aBlob[iTarget];
+ pTarget->aBlob[iTarget] = (x & 0xf0) | JSONB_OBJECT;
+ }
+ n = jsonbPayloadSize(pPatch, iPatch, &sz);
+ if( NEVER(n==0) ) return JSON_MERGE_BADPATCH;
+ iPCursor = iPatch+n;
+ iPEnd = iPCursor+sz;
+ n = jsonbPayloadSize(pTarget, iTarget, &sz);
+ if( NEVER(n==0) ) return JSON_MERGE_BADTARGET;
+ iTStart = iTarget+n;
+ iTEndBE = iTStart+sz;
+
+ while( iPCursor<iPEnd ){ /* Algorithm line 07 */
+ iPLabel = iPCursor;
+ ePLabel = pPatch->aBlob[iPCursor] & 0x0f;
+ if( ePLabel<JSONB_TEXT || ePLabel>JSONB_TEXTRAW ){
+ return JSON_MERGE_BADPATCH;
+ }
+ nPLabel = jsonbPayloadSize(pPatch, iPCursor, &szPLabel);
+ if( nPLabel==0 ) return JSON_MERGE_BADPATCH;
+ iPValue = iPCursor + nPLabel + szPLabel;
+ if( iPValue>=iPEnd ) return JSON_MERGE_BADPATCH;
+ nPValue = jsonbPayloadSize(pPatch, iPValue, &szPValue);
+ if( nPValue==0 ) return JSON_MERGE_BADPATCH;
+ iPCursor = iPValue + nPValue + szPValue;
+ if( iPCursor>iPEnd ) return JSON_MERGE_BADPATCH;
+
+ iTCursor = iTStart;
+ iTEnd = iTEndBE + pTarget->delta;
+ while( iTCursor<iTEnd ){
+ int isEqual; /* true if the patch and target labels match */
+ iTLabel = iTCursor;
+ eTLabel = pTarget->aBlob[iTCursor] & 0x0f;
+ if( eTLabel<JSONB_TEXT || eTLabel>JSONB_TEXTRAW ){
+ return JSON_MERGE_BADTARGET;
+ }
+ nTLabel = jsonbPayloadSize(pTarget, iTCursor, &szTLabel);
+ if( nTLabel==0 ) return JSON_MERGE_BADTARGET;
+ iTValue = iTLabel + nTLabel + szTLabel;
+ if( iTValue>=iTEnd ) return JSON_MERGE_BADTARGET;
+ nTValue = jsonbPayloadSize(pTarget, iTValue, &szTValue);
+ if( nTValue==0 ) return JSON_MERGE_BADTARGET;
+ if( iTValue + nTValue + szTValue > iTEnd ) return JSON_MERGE_BADTARGET;
+ isEqual = jsonLabelCompare(
+ (const char*)&pPatch->aBlob[iPLabel+nPLabel],
+ szPLabel,
+ (ePLabel==JSONB_TEXT || ePLabel==JSONB_TEXTRAW),
+ (const char*)&pTarget->aBlob[iTLabel+nTLabel],
+ szTLabel,
+ (eTLabel==JSONB_TEXT || eTLabel==JSONB_TEXTRAW));
+ if( isEqual ) break;
+ iTCursor = iTValue + nTValue + szTValue;
+ }
+ x = pPatch->aBlob[iPValue] & 0x0f;
+ if( iTCursor<iTEnd ){
+ /* A match was found. Algorithm line 08 */
+ if( x==0 ){
+ /* Patch value is NULL. Algorithm line 09 */
+ jsonBlobEdit(pTarget, iTLabel, nTLabel+szTLabel+nTValue+szTValue, 0,0);
+ /* vvvvvv----- No OOM on a delete-only edit */
+ if( NEVER(pTarget->oom) ) return JSON_MERGE_OOM;
+ }else{
+ /* Algorithm line 12 */
+ int rc, savedDelta = pTarget->delta;
+ pTarget->delta = 0;
+ rc = jsonMergePatch(pTarget, iTValue, pPatch, iPValue);
+ if( rc ) return rc;
+ pTarget->delta += savedDelta;
+ }
+ }else if( x>0 ){ /* Algorithm line 13 */
+ /* No match and patch value is not NULL */
+ u32 szNew = szPLabel+nPLabel;
+ if( (pPatch->aBlob[iPValue] & 0x0f)!=JSONB_OBJECT ){ /* Line 14 */
+ jsonBlobEdit(pTarget, iTEnd, 0, 0, szPValue+nPValue+szNew);
+ if( pTarget->oom ) return JSON_MERGE_OOM;
+ memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew);
+ memcpy(&pTarget->aBlob[iTEnd+szNew],
+ &pPatch->aBlob[iPValue], szPValue+nPValue);
+ }else{
+ int rc, savedDelta;
+ jsonBlobEdit(pTarget, iTEnd, 0, 0, szNew+1);
+ if( pTarget->oom ) return JSON_MERGE_OOM;
+ memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew);
+ pTarget->aBlob[iTEnd+szNew] = 0x00;
+ savedDelta = pTarget->delta;
+ pTarget->delta = 0;
+ rc = jsonMergePatch(pTarget, iTEnd+szNew,pPatch,iPValue);
+ if( rc ) return rc;
+ pTarget->delta += savedDelta;
}
}
- if( j>=pTarget->n && pPatch[i+1].eType!=JSON_NULL ){
- int iStart;
- JsonNode *pApnd;
- u32 nApnd;
- iStart = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
- jsonParseAddNode(pParse, JSON_STRING, nKey, zKey);
- pApnd = &pPatch[i+1];
- if( pApnd->eType==JSON_OBJECT ) jsonRemoveAllNulls(pApnd);
- nApnd = jsonNodeSize(pApnd);
- jsonParseAddNodeArray(pParse, pApnd, jsonNodeSize(pApnd));
- if( pParse->oom ) return 0;
- pParse->aNode[iStart].n = 1+nApnd;
- pParse->aNode[iRoot].jnFlags |= JNODE_APPEND;
- pParse->aNode[iRoot].u.iAppend = iStart;
- VVA( pParse->aNode[iRoot].eU = 2 );
- iRoot = iStart;
- pTarget = &pParse->aNode[iTarget];
- }
}
- return pTarget;
+ if( pTarget->delta ) jsonAfterEditSizeAdjust(pTarget, iTarget);
+ return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK;
}
+
/*
** Implementation of the json_mergepatch(JSON1,JSON2) function. Return a JSON
** object that is the result of running the RFC 7396 MergePatch() algorithm
@@ -203922,28 +207182,27 @@ static void jsonPatchFunc(
int argc,
sqlite3_value **argv
){
- JsonParse *pX; /* The JSON that is being patched */
- JsonParse *pY; /* The patch */
- JsonNode *pResult; /* The result of the merge */
+ JsonParse *pTarget; /* The TARGET */
+ JsonParse *pPatch; /* The PATCH */
+ int rc; /* Result code */
UNUSED_PARAMETER(argc);
- pX = jsonParseCached(ctx, argv[0], ctx, 1);
- if( pX==0 ) return;
- assert( pX->hasMod==0 );
- pX->hasMod = 1;
- pY = jsonParseCached(ctx, argv[1], ctx, 1);
- if( pY==0 ) return;
- pX->useMod = 1;
- pY->useMod = 1;
- pResult = jsonMergePatch(pX, 0, pY->aNode);
- assert( pResult!=0 || pX->oom );
- if( pResult && pX->oom==0 ){
- jsonDebugPrintParse(pX);
- jsonDebugPrintNode(pResult);
- jsonReturnJson(pX, pResult, ctx, 0);
- }else{
- sqlite3_result_error_nomem(ctx);
+ assert( argc==2 );
+ pTarget = jsonParseFuncArg(ctx, argv[0], JSON_EDITABLE);
+ if( pTarget==0 ) return;
+ pPatch = jsonParseFuncArg(ctx, argv[1], 0);
+ if( pPatch ){
+ rc = jsonMergePatch(pTarget, 0, pPatch, 0);
+ if( rc==JSON_MERGE_OK ){
+ jsonReturnParse(ctx, pTarget);
+ }else if( rc==JSON_MERGE_OOM ){
+ sqlite3_result_error_nomem(ctx);
+ }else{
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ }
+ jsonParseFree(pPatch);
}
+ jsonParseFree(pTarget);
}
@@ -203967,23 +207226,23 @@ static void jsonObjectFunc(
"of arguments", -1);
return;
}
- jsonInit(&jx, ctx);
+ jsonStringInit(&jx, ctx);
jsonAppendChar(&jx, '{');
for(i=0; i<argc; i+=2){
if( sqlite3_value_type(argv[i])!=SQLITE_TEXT ){
sqlite3_result_error(ctx, "json_object() labels must be TEXT", -1);
- jsonReset(&jx);
+ jsonStringReset(&jx);
return;
}
jsonAppendSeparator(&jx);
z = (const char*)sqlite3_value_text(argv[i]);
- n = (u32)sqlite3_value_bytes(argv[i]);
+ n = sqlite3_value_bytes(argv[i]);
jsonAppendString(&jx, z, n);
jsonAppendChar(&jx, ':');
- jsonAppendValue(&jx, argv[i+1]);
+ jsonAppendSqlValue(&jx, argv[i+1]);
}
jsonAppendChar(&jx, '}');
- jsonResult(&jx);
+ jsonReturnString(&jx, 0, 0);
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
@@ -203999,118 +207258,50 @@ static void jsonRemoveFunc(
int argc,
sqlite3_value **argv
){
- JsonParse *pParse; /* The parse */
- JsonNode *pNode;
- const char *zPath;
- u32 i;
+ JsonParse *p; /* The parse */
+ const char *zPath = 0; /* Path of element to be removed */
+ int i; /* Loop counter */
+ u32 rc; /* Subroutine return code */
if( argc<1 ) return;
- pParse = jsonParseCached(ctx, argv[0], ctx, argc>1);
- if( pParse==0 ) return;
- for(i=1; i<(u32)argc; i++){
+ p = jsonParseFuncArg(ctx, argv[0], argc>1 ? JSON_EDITABLE : 0);
+ if( p==0 ) return;
+ for(i=1; i<argc; i++){
zPath = (const char*)sqlite3_value_text(argv[i]);
- if( zPath==0 ) goto remove_done;
- pNode = jsonLookup(pParse, zPath, 0, ctx);
- if( pParse->nErr ) goto remove_done;
- if( pNode ){
- pNode->jnFlags |= JNODE_REMOVE;
- pParse->hasMod = 1;
- pParse->useMod = 1;
+ if( zPath==0 ){
+ goto json_remove_done;
+ }
+ if( zPath[0]!='$' ){
+ goto json_remove_patherror;
+ }
+ if( zPath[1]==0 ){
+ /* json_remove(j,'$') returns NULL */
+ goto json_remove_done;
+ }
+ p->eEdit = JEDIT_DEL;
+ p->delta = 0;
+ rc = jsonLookupStep(p, 0, zPath+1, 0);
+ if( JSON_LOOKUP_ISERROR(rc) ){
+ if( rc==JSON_LOOKUP_NOTFOUND ){
+ continue; /* No-op */
+ }else if( rc==JSON_LOOKUP_PATHERROR ){
+ jsonBadPathError(ctx, zPath);
+ }else{
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ }
+ goto json_remove_done;
}
}
- if( (pParse->aNode[0].jnFlags & JNODE_REMOVE)==0 ){
- jsonReturnJson(pParse, pParse->aNode, ctx, 1);
- }
-remove_done:
- jsonDebugPrintParse(p);
-}
+ jsonReturnParse(ctx, p);
+ jsonParseFree(p);
+ return;
-/*
-** Substitute the value at iNode with the pValue parameter.
-*/
-static void jsonReplaceNode(
- sqlite3_context *pCtx,
- JsonParse *p,
- int iNode,
- sqlite3_value *pValue
-){
- int idx = jsonParseAddSubstNode(p, iNode);
- if( idx<=0 ){
- assert( p->oom );
- return;
- }
- switch( sqlite3_value_type(pValue) ){
- case SQLITE_NULL: {
- jsonParseAddNode(p, JSON_NULL, 0, 0);
- break;
- }
- case SQLITE_FLOAT: {
- char *z = sqlite3_mprintf("%!0.15g", sqlite3_value_double(pValue));
- int n;
- if( z==0 ){
- p->oom = 1;
- break;
- }
- n = sqlite3Strlen30(z);
- jsonParseAddNode(p, JSON_REAL, n, z);
- jsonParseAddCleanup(p, sqlite3_free, z);
- break;
- }
- case SQLITE_INTEGER: {
- char *z = sqlite3_mprintf("%lld", sqlite3_value_int64(pValue));
- int n;
- if( z==0 ){
- p->oom = 1;
- break;
- }
- n = sqlite3Strlen30(z);
- jsonParseAddNode(p, JSON_INT, n, z);
- jsonParseAddCleanup(p, sqlite3_free, z);
+json_remove_patherror:
+ jsonBadPathError(ctx, zPath);
- break;
- }
- case SQLITE_TEXT: {
- const char *z = (const char*)sqlite3_value_text(pValue);
- u32 n = (u32)sqlite3_value_bytes(pValue);
- if( z==0 ){
- p->oom = 1;
- break;
- }
- if( sqlite3_value_subtype(pValue)!=JSON_SUBTYPE ){
- char *zCopy = sqlite3DbStrDup(0, z);
- int k;
- if( zCopy ){
- jsonParseAddCleanup(p, sqlite3_free, zCopy);
- }else{
- p->oom = 1;
- sqlite3_result_error_nomem(pCtx);
- }
- k = jsonParseAddNode(p, JSON_STRING, n, zCopy);
- assert( k>0 || p->oom );
- if( p->oom==0 ) p->aNode[k].jnFlags |= JNODE_RAW;
- }else{
- JsonParse *pPatch = jsonParseCached(pCtx, pValue, pCtx, 1);
- if( pPatch==0 ){
- p->oom = 1;
- break;
- }
- jsonParseAddNodeArray(p, pPatch->aNode, pPatch->nNode);
- /* The nodes copied out of pPatch and into p likely contain
- ** u.zJContent pointers into pPatch->zJson. So preserve the
- ** content of pPatch until p is destroyed. */
- assert( pPatch->nJPRef>=1 );
- pPatch->nJPRef++;
- jsonParseAddCleanup(p, (void(*)(void*))jsonParseFree, pPatch);
- }
- break;
- }
- default: {
- jsonParseAddNode(p, JSON_NULL, 0, 0);
- sqlite3_result_error(pCtx, "JSON cannot hold BLOB values", -1);
- p->nErr++;
- break;
- }
- }
+json_remove_done:
+ jsonParseFree(p);
+ return;
}
/*
@@ -204124,30 +207315,12 @@ static void jsonReplaceFunc(
int argc,
sqlite3_value **argv
){
- JsonParse *pParse; /* The parse */
- JsonNode *pNode;
- const char *zPath;
- u32 i;
-
if( argc<1 ) return;
if( (argc&1)==0 ) {
jsonWrongNumArgs(ctx, "replace");
return;
}
- pParse = jsonParseCached(ctx, argv[0], ctx, argc>1);
- if( pParse==0 ) return;
- for(i=1; i<(u32)argc; i+=2){
- zPath = (const char*)sqlite3_value_text(argv[i]);
- pParse->useMod = 1;
- pNode = jsonLookup(pParse, zPath, 0, ctx);
- if( pParse->nErr ) goto replace_err;
- if( pNode ){
- jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]);
- }
- }
- jsonReturnJson(pParse, pParse->aNode, ctx, 1);
-replace_err:
- jsonDebugPrintParse(pParse);
+ jsonInsertIntoBlob(ctx, argc, argv, JEDIT_REPL);
}
@@ -204168,39 +207341,16 @@ static void jsonSetFunc(
int argc,
sqlite3_value **argv
){
- JsonParse *pParse; /* The parse */
- JsonNode *pNode;
- const char *zPath;
- u32 i;
- int bApnd;
- int bIsSet = sqlite3_user_data(ctx)!=0;
+
+ int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+ int bIsSet = (flags&JSON_ISSET)!=0;
if( argc<1 ) return;
if( (argc&1)==0 ) {
jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert");
return;
}
- pParse = jsonParseCached(ctx, argv[0], ctx, argc>1);
- if( pParse==0 ) return;
- for(i=1; i<(u32)argc; i+=2){
- zPath = (const char*)sqlite3_value_text(argv[i]);
- bApnd = 0;
- pParse->useMod = 1;
- pNode = jsonLookup(pParse, zPath, &bApnd, ctx);
- if( pParse->oom ){
- sqlite3_result_error_nomem(ctx);
- goto jsonSetDone;
- }else if( pParse->nErr ){
- goto jsonSetDone;
- }else if( pNode && (bApnd || bIsSet) ){
- jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]);
- }
- }
- jsonDebugPrintParse(pParse);
- jsonReturnJson(pParse, pParse->aNode, ctx, 1);
-
-jsonSetDone:
- /* no cleanup required */;
+ jsonInsertIntoBlob(ctx, argc, argv, bIsSet ? JEDIT_SET : JEDIT_INS);
}
/*
@@ -204216,27 +207366,93 @@ static void jsonTypeFunc(
sqlite3_value **argv
){
JsonParse *p; /* The parse */
- const char *zPath;
- JsonNode *pNode;
+ const char *zPath = 0;
+ u32 i;
- p = jsonParseCached(ctx, argv[0], ctx, 0);
+ p = jsonParseFuncArg(ctx, argv[0], 0);
if( p==0 ) return;
if( argc==2 ){
zPath = (const char*)sqlite3_value_text(argv[1]);
- pNode = jsonLookup(p, zPath, 0, ctx);
+ if( zPath==0 ) goto json_type_done;
+ if( zPath[0]!='$' ){
+ jsonBadPathError(ctx, zPath);
+ goto json_type_done;
+ }
+ i = jsonLookupStep(p, 0, zPath+1, 0);
+ if( JSON_LOOKUP_ISERROR(i) ){
+ if( i==JSON_LOOKUP_NOTFOUND ){
+ /* no-op */
+ }else if( i==JSON_LOOKUP_PATHERROR ){
+ jsonBadPathError(ctx, zPath);
+ }else{
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ }
+ goto json_type_done;
+ }
}else{
- pNode = p->aNode;
- }
- if( pNode ){
- sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC);
+ i = 0;
}
+ sqlite3_result_text(ctx, jsonbType[p->aBlob[i]&0x0f], -1, SQLITE_STATIC);
+json_type_done:
+ jsonParseFree(p);
}
/*
** json_valid(JSON)
-**
-** Return 1 if JSON is a well-formed canonical JSON string according
-** to RFC-7159. Return 0 otherwise.
+** json_valid(JSON, FLAGS)
+**
+** Check the JSON argument to see if it is well-formed. The FLAGS argument
+** encodes the various constraints on what is meant by "well-formed":
+**
+** 0x01 Canonical RFC-8259 JSON text
+** 0x02 JSON text with optional JSON-5 extensions
+** 0x04 Superficially appears to be JSONB
+** 0x08 Strictly well-formed JSONB
+**
+** If the FLAGS argument is omitted, it defaults to 1. Useful values for
+** FLAGS include:
+**
+** 1 Strict canonical JSON text
+** 2 JSON text perhaps with JSON-5 extensions
+** 4 Superficially appears to be JSONB
+** 5 Canonical JSON text or superficial JSONB
+** 6 JSON-5 text or superficial JSONB
+** 8 Strict JSONB
+** 9 Canonical JSON text or strict JSONB
+** 10 JSON-5 text or strict JSONB
+**
+** Other flag combinations are redundant. For example, every canonical
+** JSON text is also well-formed JSON-5 text, so FLAG values 2 and 3
+** are the same. Similarly, any input that passes a strict JSONB validation
+** will also pass the superficial validation so 12 through 15 are the same
+** as 8 through 11 respectively.
+**
+** This routine runs in linear time to validate text and when doing strict
+** JSONB validation. Superficial JSONB validation is constant time,
+** assuming the BLOB is already in memory. The performance advantage
+** of superficial JSONB validation is why that option is provided.
+** Application developers can choose to do fast superficial validation or
+** slower strict validation, according to their specific needs.
+**
+** Only the lower four bits of the FLAGS argument are currently used.
+** Higher bits are reserved for future expansion. To facilitate
+** compatibility, the current implementation raises an error if any bit
+** in FLAGS is set other than the lower four bits.
+**
+** The original circa 2015 implementation of the JSON routines in
+** SQLite only supported canonical RFC-8259 JSON text and the json_valid()
+** function only accepted one argument. That is why the default value
+** for the FLAGS argument is 1, since FLAGS=1 causes this routine to only
+** recognize canonical RFC-8259 JSON text as valid. The extra FLAGS
+** argument was added when the JSON routines were extended to support
+** JSON5-like extensions and binary JSONB stored in BLOBs.
+**
+** Return Values:
+**
+** * Raise an error if FLAGS is outside the range of 1 to 15.
+** * Return NULL if the input is NULL
+** * Return 1 if the input is well-formed.
+** * Return 0 if the input is not well-formed.
*/
static void jsonValidFunc(
sqlite3_context *ctx,
@@ -204244,79 +207460,128 @@ static void jsonValidFunc(
sqlite3_value **argv
){
JsonParse *p; /* The parse */
- UNUSED_PARAMETER(argc);
- if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
+ u8 flags = 1;
+ u8 res = 0;
+ if( argc==2 ){
+ i64 f = sqlite3_value_int64(argv[1]);
+ if( f<1 || f>15 ){
+ sqlite3_result_error(ctx, "FLAGS parameter to json_valid() must be"
+ " between 1 and 15", -1);
+ return;
+ }
+ flags = f & 0x0f;
+ }
+ switch( sqlite3_value_type(argv[0]) ){
+ case SQLITE_NULL: {
#ifdef SQLITE_LEGACY_JSON_VALID
- /* Incorrect legacy behavior was to return FALSE for a NULL input */
- sqlite3_result_int(ctx, 0);
+ /* Incorrect legacy behavior was to return FALSE for a NULL input */
+ sqlite3_result_int(ctx, 0);
#endif
- return;
- }
- p = jsonParseCached(ctx, argv[0], 0, 0);
- if( p==0 || p->oom ){
- sqlite3_result_error_nomem(ctx);
- sqlite3_free(p);
- }else{
- sqlite3_result_int(ctx, p->nErr==0 && (p->hasNonstd==0 || p->useMod));
- if( p->nErr ) jsonParseFree(p);
+ return;
+ }
+ case SQLITE_BLOB: {
+ if( jsonFuncArgMightBeBinary(argv[0]) ){
+ if( flags & 0x04 ){
+ /* Superficial checking only - accomplished by the
+ ** jsonFuncArgMightBeBinary() call above. */
+ res = 1;
+ }else if( flags & 0x08 ){
+ /* Strict checking. Check by translating BLOB->TEXT->BLOB. If
+ ** no errors occur, call that a "strict check". */
+ JsonParse px;
+ u32 iErr;
+ memset(&px, 0, sizeof(px));
+ px.aBlob = (u8*)sqlite3_value_blob(argv[0]);
+ px.nBlob = sqlite3_value_bytes(argv[0]);
+ iErr = jsonbValidityCheck(&px, 0, px.nBlob, 1);
+ res = iErr==0;
+ }
+ break;
+ }
+ /* Fall through into interpreting the input as text. See note
+ ** above at tag-20240123-a. */
+ /* no break */ deliberate_fall_through
+ }
+ default: {
+ JsonParse px;
+ if( (flags & 0x3)==0 ) break;
+ memset(&px, 0, sizeof(px));
+
+ p = jsonParseFuncArg(ctx, argv[0], JSON_KEEPERROR);
+ if( p ){
+ if( p->oom ){
+ sqlite3_result_error_nomem(ctx);
+ }else if( p->nErr ){
+ /* no-op */
+ }else if( (flags & 0x02)!=0 || p->hasNonstd==0 ){
+ res = 1;
+ }
+ jsonParseFree(p);
+ }else{
+ sqlite3_result_error_nomem(ctx);
+ }
+ break;
+ }
}
+ sqlite3_result_int(ctx, res);
}
/*
** json_error_position(JSON)
**
-** If the argument is not an interpretable JSON string, then return the 1-based
-** character position at which the parser first recognized that the input
-** was in error. The left-most character is 1. If the string is valid
-** JSON, then return 0.
-**
-** Note that json_valid() is only true for strictly conforming canonical JSON.
-** But this routine returns zero if the input contains extension. Thus:
-**
-** (1) If the input X is strictly conforming canonical JSON:
-**
-** json_valid(X) returns true
-** json_error_position(X) returns 0
-**
-** (2) If the input X is JSON but it includes extension (such as JSON5) that
-** are not part of RFC-8259:
-**
-** json_valid(X) returns false
-** json_error_position(X) return 0
+** If the argument is NULL, return NULL
**
-** (3) If the input X cannot be interpreted as JSON even taking extensions
-** into account:
+** If the argument is BLOB, do a full validity check and return non-zero
+** if the check fails. The return value is the approximate 1-based offset
+** to the byte of the element that contains the first error.
**
-** json_valid(X) return false
-** json_error_position(X) returns 1 or more
+** Otherwise interpret the argument is TEXT (even if it is numeric) and
+** return the 1-based character position for where the parser first recognized
+** that the input was not valid JSON, or return 0 if the input text looks
+** ok. JSON-5 extensions are accepted.
*/
static void jsonErrorFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
- JsonParse *p; /* The parse */
+ i64 iErrPos = 0; /* Error position to be returned */
+ JsonParse s;
+
+ assert( argc==1 );
UNUSED_PARAMETER(argc);
- if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
- p = jsonParseCached(ctx, argv[0], 0, 0);
- if( p==0 || p->oom ){
+ memset(&s, 0, sizeof(s));
+ s.db = sqlite3_context_db_handle(ctx);
+ if( jsonFuncArgMightBeBinary(argv[0]) ){
+ s.aBlob = (u8*)sqlite3_value_blob(argv[0]);
+ s.nBlob = sqlite3_value_bytes(argv[0]);
+ iErrPos = (i64)jsonbValidityCheck(&s, 0, s.nBlob, 1);
+ }else{
+ s.zJson = (char*)sqlite3_value_text(argv[0]);
+ if( s.zJson==0 ) return; /* NULL input or OOM */
+ s.nJson = sqlite3_value_bytes(argv[0]);
+ if( jsonConvertTextToBlob(&s,0) ){
+ if( s.oom ){
+ iErrPos = -1;
+ }else{
+ /* Convert byte-offset s.iErr into a character offset */
+ u32 k;
+ assert( s.zJson!=0 ); /* Because s.oom is false */
+ for(k=0; k<s.iErr && ALWAYS(s.zJson[k]); k++){
+ if( (s.zJson[k] & 0xc0)!=0x80 ) iErrPos++;
+ }
+ iErrPos++;
+ }
+ }
+ }
+ jsonParseReset(&s);
+ if( iErrPos<0 ){
sqlite3_result_error_nomem(ctx);
- sqlite3_free(p);
- }else if( p->nErr==0 ){
- sqlite3_result_int(ctx, 0);
}else{
- int n = 1;
- u32 i;
- const char *z = (const char*)sqlite3_value_text(argv[0]);
- for(i=0; i<p->iErr && ALWAYS(z[i]); i++){
- if( (z[i]&0xc0)!=0x80 ) n++;
- }
- sqlite3_result_int(ctx, n);
- jsonParseFree(p);
+ sqlite3_result_int64(ctx, iErrPos);
}
}
-
/****************************************************************************
** Aggregate SQL function implementations
****************************************************************************/
@@ -204335,32 +207600,42 @@ static void jsonArrayStep(
pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
if( pStr ){
if( pStr->zBuf==0 ){
- jsonInit(pStr, ctx);
+ jsonStringInit(pStr, ctx);
jsonAppendChar(pStr, '[');
}else if( pStr->nUsed>1 ){
jsonAppendChar(pStr, ',');
}
pStr->pCtx = ctx;
- jsonAppendValue(pStr, argv[0]);
+ jsonAppendSqlValue(pStr, argv[0]);
}
}
static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){
JsonString *pStr;
pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
if( pStr ){
+ int flags;
pStr->pCtx = ctx;
jsonAppendChar(pStr, ']');
- if( pStr->bErr ){
- if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
- assert( pStr->bStatic );
+ flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+ if( pStr->eErr ){
+ jsonReturnString(pStr, 0, 0);
+ return;
+ }else if( flags & JSON_BLOB ){
+ jsonReturnStringAsBlob(pStr);
+ if( isFinal ){
+ if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf);
+ }else{
+ jsonStringTrimOneChar(pStr);
+ }
+ return;
}else if( isFinal ){
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
pStr->bStatic ? SQLITE_TRANSIENT :
- (void(*)(void*))sqlite3RCStrUnref);
+ sqlite3RCStrUnref);
pStr->bStatic = 1;
}else{
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
- pStr->nUsed--;
+ jsonStringTrimOneChar(pStr);
}
}else{
sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC);
@@ -204441,35 +207716,46 @@ static void jsonObjectStep(
pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
if( pStr ){
if( pStr->zBuf==0 ){
- jsonInit(pStr, ctx);
+ jsonStringInit(pStr, ctx);
jsonAppendChar(pStr, '{');
}else if( pStr->nUsed>1 ){
jsonAppendChar(pStr, ',');
}
pStr->pCtx = ctx;
z = (const char*)sqlite3_value_text(argv[0]);
- n = (u32)sqlite3_value_bytes(argv[0]);
+ n = sqlite3Strlen30(z);
jsonAppendString(pStr, z, n);
jsonAppendChar(pStr, ':');
- jsonAppendValue(pStr, argv[1]);
+ jsonAppendSqlValue(pStr, argv[1]);
}
}
static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){
JsonString *pStr;
pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
if( pStr ){
+ int flags;
jsonAppendChar(pStr, '}');
- if( pStr->bErr ){
- if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
- assert( pStr->bStatic );
+ pStr->pCtx = ctx;
+ flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+ if( pStr->eErr ){
+ jsonReturnString(pStr, 0, 0);
+ return;
+ }else if( flags & JSON_BLOB ){
+ jsonReturnStringAsBlob(pStr);
+ if( isFinal ){
+ if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf);
+ }else{
+ jsonStringTrimOneChar(pStr);
+ }
+ return;
}else if( isFinal ){
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
pStr->bStatic ? SQLITE_TRANSIENT :
- (void(*)(void*))sqlite3RCStrUnref);
+ sqlite3RCStrUnref);
pStr->bStatic = 1;
}else{
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
- pStr->nUsed--;
+ jsonStringTrimOneChar(pStr);
}
}else{
sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC);
@@ -204489,19 +207775,37 @@ static void jsonObjectFinal(sqlite3_context *ctx){
/****************************************************************************
** The json_each virtual table
****************************************************************************/
+typedef struct JsonParent JsonParent;
+struct JsonParent {
+ u32 iHead; /* Start of object or array */
+ u32 iValue; /* Start of the value */
+ u32 iEnd; /* First byte past the end */
+ u32 nPath; /* Length of path */
+ i64 iKey; /* Key for JSONB_ARRAY */
+};
+
typedef struct JsonEachCursor JsonEachCursor;
struct JsonEachCursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
u32 iRowid; /* The rowid */
- u32 iBegin; /* The first node of the scan */
- u32 i; /* Index in sParse.aNode[] of current row */
+ u32 i; /* Index in sParse.aBlob[] of current row */
u32 iEnd; /* EOF when i equals or exceeds this value */
- u8 eType; /* Type of top-level element */
+ u32 nRoot; /* Size of the root path in bytes */
+ u8 eType; /* Type of the container for element i */
u8 bRecursive; /* True for json_tree(). False for json_each() */
- char *zJson; /* Input JSON */
- char *zRoot; /* Path by which to filter zJson */
+ u32 nParent; /* Current nesting depth */
+ u32 nParentAlloc; /* Space allocated for aParent[] */
+ JsonParent *aParent; /* Parent elements of i */
+ sqlite3 *db; /* Database connection */
+ JsonString path; /* Current path */
JsonParse sParse; /* Parse of the input JSON */
};
+typedef struct JsonEachConnection JsonEachConnection;
+struct JsonEachConnection {
+ sqlite3_vtab base; /* Base class - must be first */
+ sqlite3 *db; /* Database connection */
+};
+
/* Constructor for the json_each virtual table */
static int jsonEachConnect(
@@ -204511,7 +207815,7 @@ static int jsonEachConnect(
sqlite3_vtab **ppVtab,
char **pzErr
){
- sqlite3_vtab *pNew;
+ JsonEachConnection *pNew;
int rc;
/* Column numbers */
@@ -204537,28 +207841,32 @@ static int jsonEachConnect(
"CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path,"
"json HIDDEN,root HIDDEN)");
if( rc==SQLITE_OK ){
- pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
+ pNew = (JsonEachConnection*)sqlite3DbMallocZero(db, sizeof(*pNew));
+ *ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
- memset(pNew, 0, sizeof(*pNew));
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
+ pNew->db = db;
}
return rc;
}
/* destructor for json_each virtual table */
static int jsonEachDisconnect(sqlite3_vtab *pVtab){
- sqlite3_free(pVtab);
+ JsonEachConnection *p = (JsonEachConnection*)pVtab;
+ sqlite3DbFree(p->db, pVtab);
return SQLITE_OK;
}
/* constructor for a JsonEachCursor object for json_each(). */
static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
+ JsonEachConnection *pVtab = (JsonEachConnection*)p;
JsonEachCursor *pCur;
UNUSED_PARAMETER(p);
- pCur = sqlite3_malloc( sizeof(*pCur) );
+ pCur = sqlite3DbMallocZero(pVtab->db, sizeof(*pCur));
if( pCur==0 ) return SQLITE_NOMEM;
- memset(pCur, 0, sizeof(*pCur));
+ pCur->db = pVtab->db;
+ jsonStringZero(&pCur->path);
*ppCursor = &pCur->base;
return SQLITE_OK;
}
@@ -204576,21 +207884,24 @@ static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
/* Reset a JsonEachCursor back to its original state. Free any memory
** held. */
static void jsonEachCursorReset(JsonEachCursor *p){
- sqlite3_free(p->zRoot);
jsonParseReset(&p->sParse);
+ jsonStringReset(&p->path);
+ sqlite3DbFree(p->db, p->aParent);
p->iRowid = 0;
p->i = 0;
+ p->aParent = 0;
+ p->nParent = 0;
+ p->nParentAlloc = 0;
p->iEnd = 0;
p->eType = 0;
- p->zJson = 0;
- p->zRoot = 0;
}
/* Destructor for a jsonEachCursor object */
static int jsonEachClose(sqlite3_vtab_cursor *cur){
JsonEachCursor *p = (JsonEachCursor*)cur;
jsonEachCursorReset(p);
- sqlite3_free(cur);
+
+ sqlite3DbFree(p->db, cur);
return SQLITE_OK;
}
@@ -204601,200 +207912,230 @@ static int jsonEachEof(sqlite3_vtab_cursor *cur){
return p->i >= p->iEnd;
}
-/* Advance the cursor to the next element for json_tree() */
-static int jsonEachNext(sqlite3_vtab_cursor *cur){
- JsonEachCursor *p = (JsonEachCursor*)cur;
- if( p->bRecursive ){
- if( p->sParse.aNode[p->i].jnFlags & JNODE_LABEL ) p->i++;
- p->i++;
- p->iRowid++;
- if( p->i<p->iEnd ){
- u32 iUp = p->sParse.aUp[p->i];
- JsonNode *pUp = &p->sParse.aNode[iUp];
- p->eType = pUp->eType;
- if( pUp->eType==JSON_ARRAY ){
- assert( pUp->eU==0 || pUp->eU==3 );
- testcase( pUp->eU==3 );
- VVA( pUp->eU = 3 );
- if( iUp==p->i-1 ){
- pUp->u.iKey = 0;
- }else{
- pUp->u.iKey++;
+/*
+** If the cursor is currently pointing at the label of a object entry,
+** then return the index of the value. For all other cases, return the
+** current pointer position, which is the value.
+*/
+static int jsonSkipLabel(JsonEachCursor *p){
+ if( p->eType==JSONB_OBJECT ){
+ u32 sz = 0;
+ u32 n = jsonbPayloadSize(&p->sParse, p->i, &sz);
+ return p->i + n + sz;
+ }else{
+ return p->i;
+ }
+}
+
+/*
+** Append the path name for the current element.
+*/
+static void jsonAppendPathName(JsonEachCursor *p){
+ assert( p->nParent>0 );
+ assert( p->eType==JSONB_ARRAY || p->eType==JSONB_OBJECT );
+ if( p->eType==JSONB_ARRAY ){
+ jsonPrintf(30, &p->path, "[%lld]", p->aParent[p->nParent-1].iKey);
+ }else{
+ u32 n, sz = 0, k, i;
+ const char *z;
+ int needQuote = 0;
+ n = jsonbPayloadSize(&p->sParse, p->i, &sz);
+ k = p->i + n;
+ z = (const char*)&p->sParse.aBlob[k];
+ if( sz==0 || !sqlite3Isalpha(z[0]) ){
+ needQuote = 1;
+ }else{
+ for(i=0; i<sz; i++){
+ if( !sqlite3Isalnum(z[i]) ){
+ needQuote = 1;
+ break;
}
}
}
- }else{
- switch( p->eType ){
- case JSON_ARRAY: {
- p->i += jsonNodeSize(&p->sParse.aNode[p->i]);
- p->iRowid++;
- break;
- }
- case JSON_OBJECT: {
- p->i += 1 + jsonNodeSize(&p->sParse.aNode[p->i+1]);
- p->iRowid++;
- break;
- }
- default: {
- p->i = p->iEnd;
- break;
- }
+ if( needQuote ){
+ jsonPrintf(sz+4,&p->path,".\"%.*s\"", sz, z);
+ }else{
+ jsonPrintf(sz+2,&p->path,".%.*s", sz, z);
}
}
- return SQLITE_OK;
}
-/* Append an object label to the JSON Path being constructed
-** in pStr.
-*/
-static void jsonAppendObjectPathElement(
- JsonString *pStr,
- JsonNode *pNode
-){
- int jj, nn;
- const char *z;
- assert( pNode->eType==JSON_STRING );
- assert( pNode->jnFlags & JNODE_LABEL );
- assert( pNode->eU==1 );
- z = pNode->u.zJContent;
- nn = pNode->n;
- if( (pNode->jnFlags & JNODE_RAW)==0 ){
- assert( nn>=2 );
- assert( z[0]=='"' || z[0]=='\'' );
- assert( z[nn-1]=='"' || z[0]=='\'' );
- if( nn>2 && sqlite3Isalpha(z[1]) ){
- for(jj=2; jj<nn-1 && sqlite3Isalnum(z[jj]); jj++){}
- if( jj==nn-1 ){
- z++;
- nn -= 2;
+/* Advance the cursor to the next element for json_tree() */
+static int jsonEachNext(sqlite3_vtab_cursor *cur){
+ JsonEachCursor *p = (JsonEachCursor*)cur;
+ int rc = SQLITE_OK;
+ if( p->bRecursive ){
+ u8 x;
+ u8 levelChange = 0;
+ u32 n, sz = 0;
+ u32 i = jsonSkipLabel(p);
+ x = p->sParse.aBlob[i] & 0x0f;
+ n = jsonbPayloadSize(&p->sParse, i, &sz);
+ if( x==JSONB_OBJECT || x==JSONB_ARRAY ){
+ JsonParent *pParent;
+ if( p->nParent>=p->nParentAlloc ){
+ JsonParent *pNew;
+ u64 nNew;
+ nNew = p->nParentAlloc*2 + 3;
+ pNew = sqlite3DbRealloc(p->db, p->aParent, sizeof(JsonParent)*nNew);
+ if( pNew==0 ) return SQLITE_NOMEM;
+ p->nParentAlloc = (u32)nNew;
+ p->aParent = pNew;
+ }
+ levelChange = 1;
+ pParent = &p->aParent[p->nParent];
+ pParent->iHead = p->i;
+ pParent->iValue = i;
+ pParent->iEnd = i + n + sz;
+ pParent->iKey = -1;
+ pParent->nPath = (u32)p->path.nUsed;
+ if( p->eType && p->nParent ){
+ jsonAppendPathName(p);
+ if( p->path.eErr ) rc = SQLITE_NOMEM;
+ }
+ p->nParent++;
+ p->i = i + n;
+ }else{
+ p->i = i + n + sz;
+ }
+ while( p->nParent>0 && p->i >= p->aParent[p->nParent-1].iEnd ){
+ p->nParent--;
+ p->path.nUsed = p->aParent[p->nParent].nPath;
+ levelChange = 1;
+ }
+ if( levelChange ){
+ if( p->nParent>0 ){
+ JsonParent *pParent = &p->aParent[p->nParent-1];
+ u32 iVal = pParent->iValue;
+ p->eType = p->sParse.aBlob[iVal] & 0x0f;
+ }else{
+ p->eType = 0;
}
}
+ }else{
+ u32 n, sz = 0;
+ u32 i = jsonSkipLabel(p);
+ n = jsonbPayloadSize(&p->sParse, i, &sz);
+ p->i = i + n + sz;
+ }
+ if( p->eType==JSONB_ARRAY && p->nParent ){
+ p->aParent[p->nParent-1].iKey++;
}
- jsonPrintf(nn+2, pStr, ".%.*s", nn, z);
+ p->iRowid++;
+ return rc;
}
-/* Append the name of the path for element i to pStr
+/* Length of the path for rowid==0 in bRecursive mode.
*/
-static void jsonEachComputePath(
- JsonEachCursor *p, /* The cursor */
- JsonString *pStr, /* Write the path here */
- u32 i /* Path to this element */
-){
- JsonNode *pNode, *pUp;
- u32 iUp;
- if( i==0 ){
- jsonAppendChar(pStr, '$');
- return;
- }
- iUp = p->sParse.aUp[i];
- jsonEachComputePath(p, pStr, iUp);
- pNode = &p->sParse.aNode[i];
- pUp = &p->sParse.aNode[iUp];
- if( pUp->eType==JSON_ARRAY ){
- assert( pUp->eU==3 || (pUp->eU==0 && pUp->u.iKey==0) );
- testcase( pUp->eU==0 );
- jsonPrintf(30, pStr, "[%d]", pUp->u.iKey);
- }else{
- assert( pUp->eType==JSON_OBJECT );
- if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--;
- jsonAppendObjectPathElement(pStr, pNode);
+static int jsonEachPathLength(JsonEachCursor *p){
+ u32 n = p->path.nUsed;
+ char *z = p->path.zBuf;
+ if( p->iRowid==0 && p->bRecursive && n>=2 ){
+ while( n>1 ){
+ n--;
+ if( z[n]=='[' || z[n]=='.' ){
+ u32 x, sz = 0;
+ char cSaved = z[n];
+ z[n] = 0;
+ assert( p->sParse.eEdit==0 );
+ x = jsonLookupStep(&p->sParse, 0, z+1, 0);
+ z[n] = cSaved;
+ if( JSON_LOOKUP_ISERROR(x) ) continue;
+ if( x + jsonbPayloadSize(&p->sParse, x, &sz) == p->i ) break;
+ }
+ }
}
+ return n;
}
/* Return the value of a column */
static int jsonEachColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
- int i /* Which column to return */
+ int iColumn /* Which column to return */
){
JsonEachCursor *p = (JsonEachCursor*)cur;
- JsonNode *pThis = &p->sParse.aNode[p->i];
- switch( i ){
+ switch( iColumn ){
case JEACH_KEY: {
- if( p->i==0 ) break;
- if( p->eType==JSON_OBJECT ){
- jsonReturn(&p->sParse, pThis, ctx);
- }else if( p->eType==JSON_ARRAY ){
- u32 iKey;
- if( p->bRecursive ){
- if( p->iRowid==0 ) break;
- assert( p->sParse.aNode[p->sParse.aUp[p->i]].eU==3 );
- iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey;
+ if( p->nParent==0 ){
+ u32 n, j;
+ if( p->nRoot==1 ) break;
+ j = jsonEachPathLength(p);
+ n = p->nRoot - j;
+ if( n==0 ){
+ break;
+ }else if( p->path.zBuf[j]=='[' ){
+ i64 x;
+ sqlite3Atoi64(&p->path.zBuf[j+1], &x, n-1, SQLITE_UTF8);
+ sqlite3_result_int64(ctx, x);
+ }else if( p->path.zBuf[j+1]=='"' ){
+ sqlite3_result_text(ctx, &p->path.zBuf[j+2], n-3, SQLITE_TRANSIENT);
}else{
- iKey = p->iRowid;
+ sqlite3_result_text(ctx, &p->path.zBuf[j+1], n-1, SQLITE_TRANSIENT);
}
- sqlite3_result_int64(ctx, (sqlite3_int64)iKey);
+ break;
+ }
+ if( p->eType==JSONB_OBJECT ){
+ jsonReturnFromBlob(&p->sParse, p->i, ctx, 1);
+ }else{
+ assert( p->eType==JSONB_ARRAY );
+ sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iKey);
}
break;
}
case JEACH_VALUE: {
- if( pThis->jnFlags & JNODE_LABEL ) pThis++;
- jsonReturn(&p->sParse, pThis, ctx);
+ u32 i = jsonSkipLabel(p);
+ jsonReturnFromBlob(&p->sParse, i, ctx, 1);
break;
}
case JEACH_TYPE: {
- if( pThis->jnFlags & JNODE_LABEL ) pThis++;
- sqlite3_result_text(ctx, jsonType[pThis->eType], -1, SQLITE_STATIC);
+ u32 i = jsonSkipLabel(p);
+ u8 eType = p->sParse.aBlob[i] & 0x0f;
+ sqlite3_result_text(ctx, jsonbType[eType], -1, SQLITE_STATIC);
break;
}
case JEACH_ATOM: {
- if( pThis->jnFlags & JNODE_LABEL ) pThis++;
- if( pThis->eType>=JSON_ARRAY ) break;
- jsonReturn(&p->sParse, pThis, ctx);
+ u32 i = jsonSkipLabel(p);
+ if( (p->sParse.aBlob[i] & 0x0f)<JSONB_ARRAY ){
+ jsonReturnFromBlob(&p->sParse, i, ctx, 1);
+ }
break;
}
case JEACH_ID: {
- sqlite3_result_int64(ctx,
- (sqlite3_int64)p->i + ((pThis->jnFlags & JNODE_LABEL)!=0));
+ sqlite3_result_int64(ctx, (sqlite3_int64)p->i);
break;
}
case JEACH_PARENT: {
- if( p->i>p->iBegin && p->bRecursive ){
- sqlite3_result_int64(ctx, (sqlite3_int64)p->sParse.aUp[p->i]);
+ if( p->nParent>0 && p->bRecursive ){
+ sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iHead);
}
break;
}
case JEACH_FULLKEY: {
- JsonString x;
- jsonInit(&x, ctx);
- if( p->bRecursive ){
- jsonEachComputePath(p, &x, p->i);
- }else{
- if( p->zRoot ){
- jsonAppendRaw(&x, p->zRoot, (int)strlen(p->zRoot));
- }else{
- jsonAppendChar(&x, '$');
- }
- if( p->eType==JSON_ARRAY ){
- jsonPrintf(30, &x, "[%d]", p->iRowid);
- }else if( p->eType==JSON_OBJECT ){
- jsonAppendObjectPathElement(&x, pThis);
- }
- }
- jsonResult(&x);
+ u64 nBase = p->path.nUsed;
+ if( p->nParent ) jsonAppendPathName(p);
+ sqlite3_result_text64(ctx, p->path.zBuf, p->path.nUsed,
+ SQLITE_TRANSIENT, SQLITE_UTF8);
+ p->path.nUsed = nBase;
break;
}
case JEACH_PATH: {
- if( p->bRecursive ){
- JsonString x;
- jsonInit(&x, ctx);
- jsonEachComputePath(p, &x, p->sParse.aUp[p->i]);
- jsonResult(&x);
- break;
- }
- /* For json_each() path and root are the same so fall through
- ** into the root case */
- /* no break */ deliberate_fall_through
+ u32 n = jsonEachPathLength(p);
+ sqlite3_result_text64(ctx, p->path.zBuf, n,
+ SQLITE_TRANSIENT, SQLITE_UTF8);
+ break;
}
default: {
- const char *zRoot = p->zRoot;
- if( zRoot==0 ) zRoot = "$";
- sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC);
+ sqlite3_result_text(ctx, p->path.zBuf, p->nRoot, SQLITE_STATIC);
break;
}
case JEACH_JSON: {
- assert( i==JEACH_JSON );
- sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
+ if( p->sParse.zJson==0 ){
+ sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob,
+ SQLITE_STATIC);
+ }else{
+ sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
+ }
break;
}
}
@@ -204885,86 +208226,97 @@ static int jsonEachFilter(
int argc, sqlite3_value **argv
){
JsonEachCursor *p = (JsonEachCursor*)cur;
- const char *z;
const char *zRoot = 0;
- sqlite3_int64 n;
+ u32 i, n, sz;
UNUSED_PARAMETER(idxStr);
UNUSED_PARAMETER(argc);
jsonEachCursorReset(p);
if( idxNum==0 ) return SQLITE_OK;
- z = (const char*)sqlite3_value_text(argv[0]);
- if( z==0 ) return SQLITE_OK;
memset(&p->sParse, 0, sizeof(p->sParse));
p->sParse.nJPRef = 1;
- if( sqlite3ValueIsOfClass(argv[0], (void(*)(void*))sqlite3RCStrUnref) ){
- p->sParse.zJson = sqlite3RCStrRef((char*)z);
- }else{
- n = sqlite3_value_bytes(argv[0]);
- p->sParse.zJson = sqlite3RCStrNew( n+1 );
- if( p->sParse.zJson==0 ) return SQLITE_NOMEM;
- memcpy(p->sParse.zJson, z, (size_t)n+1);
- }
- p->sParse.bJsonIsRCStr = 1;
- p->zJson = p->sParse.zJson;
- if( jsonParse(&p->sParse, 0) ){
- int rc = SQLITE_NOMEM;
- if( p->sParse.oom==0 ){
- sqlite3_free(cur->pVtab->zErrMsg);
- cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON");
- if( cur->pVtab->zErrMsg ) rc = SQLITE_ERROR;
+ p->sParse.db = p->db;
+ if( jsonFuncArgMightBeBinary(argv[0]) ){
+ p->sParse.nBlob = sqlite3_value_bytes(argv[0]);
+ p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]);
+ }else{
+ p->sParse.zJson = (char*)sqlite3_value_text(argv[0]);
+ p->sParse.nJson = sqlite3_value_bytes(argv[0]);
+ if( p->sParse.zJson==0 ){
+ p->i = p->iEnd = 0;
+ return SQLITE_OK;
}
- jsonEachCursorReset(p);
- return rc;
- }else if( p->bRecursive && jsonParseFindParents(&p->sParse) ){
- jsonEachCursorReset(p);
- return SQLITE_NOMEM;
- }else{
- JsonNode *pNode = 0;
- if( idxNum==3 ){
- const char *zErr = 0;
- zRoot = (const char*)sqlite3_value_text(argv[1]);
- if( zRoot==0 ) return SQLITE_OK;
- n = sqlite3_value_bytes(argv[1]);
- p->zRoot = sqlite3_malloc64( n+1 );
- if( p->zRoot==0 ) return SQLITE_NOMEM;
- memcpy(p->zRoot, zRoot, (size_t)n+1);
- if( zRoot[0]!='$' ){
- zErr = zRoot;
- }else{
- pNode = jsonLookupStep(&p->sParse, 0, p->zRoot+1, 0, &zErr);
+ if( jsonConvertTextToBlob(&p->sParse, 0) ){
+ if( p->sParse.oom ){
+ return SQLITE_NOMEM;
}
- if( zErr ){
+ goto json_each_malformed_input;
+ }
+ }
+ if( idxNum==3 ){
+ zRoot = (const char*)sqlite3_value_text(argv[1]);
+ if( zRoot==0 ) return SQLITE_OK;
+ if( zRoot[0]!='$' ){
+ sqlite3_free(cur->pVtab->zErrMsg);
+ cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot);
+ jsonEachCursorReset(p);
+ return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
+ }
+ p->nRoot = sqlite3Strlen30(zRoot);
+ if( zRoot[1]==0 ){
+ i = p->i = 0;
+ p->eType = 0;
+ }else{
+ i = jsonLookupStep(&p->sParse, 0, zRoot+1, 0);
+ if( JSON_LOOKUP_ISERROR(i) ){
+ if( i==JSON_LOOKUP_NOTFOUND ){
+ p->i = 0;
+ p->eType = 0;
+ p->iEnd = 0;
+ return SQLITE_OK;
+ }
sqlite3_free(cur->pVtab->zErrMsg);
- cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr);
+ cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot);
jsonEachCursorReset(p);
return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
- }else if( pNode==0 ){
- return SQLITE_OK;
}
- }else{
- pNode = p->sParse.aNode;
- }
- p->iBegin = p->i = (int)(pNode - p->sParse.aNode);
- p->eType = pNode->eType;
- if( p->eType>=JSON_ARRAY ){
- assert( pNode->eU==0 );
- VVA( pNode->eU = 3 );
- pNode->u.iKey = 0;
- p->iEnd = p->i + pNode->n + 1;
- if( p->bRecursive ){
- p->eType = p->sParse.aNode[p->sParse.aUp[p->i]].eType;
- if( p->i>0 && (p->sParse.aNode[p->i-1].jnFlags & JNODE_LABEL)!=0 ){
- p->i--;
- }
+ if( p->sParse.iLabel ){
+ p->i = p->sParse.iLabel;
+ p->eType = JSONB_OBJECT;
}else{
- p->i++;
- }
- }else{
- p->iEnd = p->i+1;
- }
+ p->i = i;
+ p->eType = JSONB_ARRAY;
+ }
+ }
+ jsonAppendRaw(&p->path, zRoot, p->nRoot);
+ }else{
+ i = p->i = 0;
+ p->eType = 0;
+ p->nRoot = 1;
+ jsonAppendRaw(&p->path, "$", 1);
+ }
+ p->nParent = 0;
+ n = jsonbPayloadSize(&p->sParse, i, &sz);
+ p->iEnd = i+n+sz;
+ if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY && !p->bRecursive ){
+ p->i = i + n;
+ p->eType = p->sParse.aBlob[i] & 0x0f;
+ p->aParent = sqlite3DbMallocZero(p->db, sizeof(JsonParent));
+ if( p->aParent==0 ) return SQLITE_NOMEM;
+ p->nParent = 1;
+ p->nParentAlloc = 1;
+ p->aParent[0].iKey = 0;
+ p->aParent[0].iEnd = p->iEnd;
+ p->aParent[0].iHead = p->i;
+ p->aParent[0].iValue = i;
}
return SQLITE_OK;
+
+json_each_malformed_input:
+ sqlite3_free(cur->pVtab->zErrMsg);
+ cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON");
+ jsonEachCursorReset(p);
+ return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
}
/* The methods of the json_each virtual table */
@@ -204992,7 +208344,8 @@ static sqlite3_module jsonEachModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
/* The methods of the json_tree virtual table. */
@@ -205020,7 +208373,8 @@ static sqlite3_module jsonTreeModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#endif /* !defined(SQLITE_OMIT_JSON) */
@@ -205031,34 +208385,57 @@ static sqlite3_module jsonTreeModule = {
SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void){
#ifndef SQLITE_OMIT_JSON
static FuncDef aJsonFunc[] = {
- JFUNCTION(json, 1, 0, jsonRemoveFunc),
- JFUNCTION(json_array, -1, 0, jsonArrayFunc),
- JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc),
- JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc),
- JFUNCTION(json_error_position,1, 0, jsonErrorFunc),
- JFUNCTION(json_extract, -1, 0, jsonExtractFunc),
- JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc),
- JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc),
- JFUNCTION(json_insert, -1, 0, jsonSetFunc),
- JFUNCTION(json_object, -1, 0, jsonObjectFunc),
- JFUNCTION(json_patch, 2, 0, jsonPatchFunc),
- JFUNCTION(json_quote, 1, 0, jsonQuoteFunc),
- JFUNCTION(json_remove, -1, 0, jsonRemoveFunc),
- JFUNCTION(json_replace, -1, 0, jsonReplaceFunc),
- JFUNCTION(json_set, -1, JSON_ISSET, jsonSetFunc),
- JFUNCTION(json_type, 1, 0, jsonTypeFunc),
- JFUNCTION(json_type, 2, 0, jsonTypeFunc),
- JFUNCTION(json_valid, 1, 0, jsonValidFunc),
+ /* sqlite3_result_subtype() ----, ,--- sqlite3_value_subtype() */
+ /* | | */
+ /* Uses cache ------, | | ,---- Returns JSONB */
+ /* | | | | */
+ /* Number of arguments ---, | | | | ,--- Flags */
+ /* | | | | | | */
+ JFUNCTION(json, 1,1,1, 0,0,0, jsonRemoveFunc),
+ JFUNCTION(jsonb, 1,1,0, 0,1,0, jsonRemoveFunc),
+ JFUNCTION(json_array, -1,0,1, 1,0,0, jsonArrayFunc),
+ JFUNCTION(jsonb_array, -1,0,1, 1,1,0, jsonArrayFunc),
+ JFUNCTION(json_array_length, 1,1,0, 0,0,0, jsonArrayLengthFunc),
+ JFUNCTION(json_array_length, 2,1,0, 0,0,0, jsonArrayLengthFunc),
+ JFUNCTION(json_error_position,1,1,0, 0,0,0, jsonErrorFunc),
+ JFUNCTION(json_extract, -1,1,1, 0,0,0, jsonExtractFunc),
+ JFUNCTION(jsonb_extract, -1,1,0, 0,1,0, jsonExtractFunc),
+ JFUNCTION(->, 2,1,1, 0,0,JSON_JSON, jsonExtractFunc),
+ JFUNCTION(->>, 2,1,0, 0,0,JSON_SQL, jsonExtractFunc),
+ JFUNCTION(json_insert, -1,1,1, 1,0,0, jsonSetFunc),
+ JFUNCTION(jsonb_insert, -1,1,0, 1,1,0, jsonSetFunc),
+ JFUNCTION(json_object, -1,0,1, 1,0,0, jsonObjectFunc),
+ JFUNCTION(jsonb_object, -1,0,1, 1,1,0, jsonObjectFunc),
+ JFUNCTION(json_patch, 2,1,1, 0,0,0, jsonPatchFunc),
+ JFUNCTION(jsonb_patch, 2,1,0, 0,1,0, jsonPatchFunc),
+ JFUNCTION(json_quote, 1,0,1, 1,0,0, jsonQuoteFunc),
+ JFUNCTION(json_remove, -1,1,1, 0,0,0, jsonRemoveFunc),
+ JFUNCTION(jsonb_remove, -1,1,0, 0,1,0, jsonRemoveFunc),
+ JFUNCTION(json_replace, -1,1,1, 1,0,0, jsonReplaceFunc),
+ JFUNCTION(jsonb_replace, -1,1,0, 1,1,0, jsonReplaceFunc),
+ JFUNCTION(json_set, -1,1,1, 1,0,JSON_ISSET, jsonSetFunc),
+ JFUNCTION(jsonb_set, -1,1,0, 1,1,JSON_ISSET, jsonSetFunc),
+ JFUNCTION(json_type, 1,1,0, 0,0,0, jsonTypeFunc),
+ JFUNCTION(json_type, 2,1,0, 0,0,0, jsonTypeFunc),
+ JFUNCTION(json_valid, 1,1,0, 0,0,0, jsonValidFunc),
+ JFUNCTION(json_valid, 2,1,0, 0,0,0, jsonValidFunc),
#if SQLITE_DEBUG
- JFUNCTION(json_parse, 1, 0, jsonParseFunc),
- JFUNCTION(json_test1, 1, 0, jsonTest1Func),
+ JFUNCTION(json_parse, 1,1,0, 0,0,0, jsonParseFunc),
#endif
WAGGREGATE(json_group_array, 1, 0, 0,
jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse,
- SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
+ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|
+ SQLITE_DETERMINISTIC),
+ WAGGREGATE(jsonb_group_array, 1, JSON_BLOB, 0,
+ jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse,
+ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
WAGGREGATE(json_group_object, 2, 0, 0,
jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse,
- SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC)
+ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
+ WAGGREGATE(jsonb_group_object,2, JSON_BLOB, 0,
+ jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse,
+ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|
+ SQLITE_DETERMINISTIC)
};
sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc));
#endif
@@ -205255,6 +208632,7 @@ struct Rtree {
int iDepth; /* Current depth of the r-tree structure */
char *zDb; /* Name of database containing r-tree table */
char *zName; /* Name of r-tree table */
+ char *zNodeName; /* Name of the %_node table */
u32 nBusy; /* Current number of users of this structure */
i64 nRowEst; /* Estimated number of rows in this table */
u32 nCursor; /* Number of open cursors */
@@ -205267,7 +208645,6 @@ struct Rtree {
** headed by the node (leaf nodes have RtreeNode.iNode==0).
*/
RtreeNode *pDeleted;
- int iReinsertHeight; /* Height of sub-trees Reinsert() has run on */
/* Blob I/O on xxx_node */
sqlite3_blob *pNodeBlob;
@@ -205564,15 +208941,20 @@ struct RtreeMatchArg {
** -DSQLITE_RUNTIME_BYTEORDER=1 is set, then byte-order is determined
** at run-time.
*/
-#ifndef SQLITE_BYTEORDER
-# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \
+#ifndef SQLITE_BYTEORDER /* Replicate changes at tag-20230904a */
+# if defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__
+# define SQLITE_BYTEORDER 4321
+# elif defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
+# define SQLITE_BYTEORDER 1234
+# elif defined(__BIG_ENDIAN__) && __BIG_ENDIAN__==1
+# define SQLITE_BYTEORDER 4321
+# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \
defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \
defined(__ARMEL__) || defined(__AARCH64EL__) || defined(_M_ARM64)
-# define SQLITE_BYTEORDER 1234
-# elif defined(sparc) || defined(__ppc__) || \
- defined(__ARMEB__) || defined(__AARCH64EB__)
-# define SQLITE_BYTEORDER 4321
+# define SQLITE_BYTEORDER 1234
+# elif defined(sparc) || defined(__ARMEB__) || defined(__AARCH64EB__)
+# define SQLITE_BYTEORDER 4321
# else
# define SQLITE_BYTEORDER 0
# endif
@@ -205801,7 +209183,7 @@ static int nodeAcquire(
** increase its reference count and return it.
*/
if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){
- if( pParent && pParent!=pNode->pParent ){
+ if( pParent && ALWAYS(pParent!=pNode->pParent) ){
RTREE_IS_CORRUPT(pRtree);
return SQLITE_CORRUPT_VTAB;
}
@@ -205821,11 +209203,9 @@ static int nodeAcquire(
}
}
if( pRtree->pNodeBlob==0 ){
- char *zTab = sqlite3_mprintf("%s_node", pRtree->zName);
- if( zTab==0 ) return SQLITE_NOMEM;
- rc = sqlite3_blob_open(pRtree->db, pRtree->zDb, zTab, "data", iNode, 0,
+ rc = sqlite3_blob_open(pRtree->db, pRtree->zDb, pRtree->zNodeName,
+ "data", iNode, 0,
&pRtree->pNodeBlob);
- sqlite3_free(zTab);
}
if( rc ){
nodeBlobReset(pRtree);
@@ -207166,8 +210546,12 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
pIdxInfo->idxNum = 2;
pIdxInfo->needToFreeIdxStr = 1;
- if( iIdx>0 && 0==(pIdxInfo->idxStr = sqlite3_mprintf("%s", zIdxStr)) ){
- return SQLITE_NOMEM;
+ if( iIdx>0 ){
+ pIdxInfo->idxStr = sqlite3_malloc( iIdx+1 );
+ if( pIdxInfo->idxStr==0 ){
+ return SQLITE_NOMEM;
+ }
+ memcpy(pIdxInfo->idxStr, zIdxStr, iIdx+1);
}
nRow = pRtree->nRowEst >> (iIdx/2);
@@ -207246,31 +210630,22 @@ static void cellUnion(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){
*/
static int cellContains(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){
int ii;
- int isInt = (pRtree->eCoordType==RTREE_COORD_INT32);
- for(ii=0; ii<pRtree->nDim2; ii+=2){
- RtreeCoord *a1 = &p1->aCoord[ii];
- RtreeCoord *a2 = &p2->aCoord[ii];
- if( (!isInt && (a2[0].f<a1[0].f || a2[1].f>a1[1].f))
- || ( isInt && (a2[0].i<a1[0].i || a2[1].i>a1[1].i))
- ){
- return 0;
+ if( pRtree->eCoordType==RTREE_COORD_INT32 ){
+ for(ii=0; ii<pRtree->nDim2; ii+=2){
+ RtreeCoord *a1 = &p1->aCoord[ii];
+ RtreeCoord *a2 = &p2->aCoord[ii];
+ if( a2[0].i<a1[0].i || a2[1].i>a1[1].i ) return 0;
+ }
+ }else{
+ for(ii=0; ii<pRtree->nDim2; ii+=2){
+ RtreeCoord *a1 = &p1->aCoord[ii];
+ RtreeCoord *a2 = &p2->aCoord[ii];
+ if( a2[0].f<a1[0].f || a2[1].f>a1[1].f ) return 0;
}
}
return 1;
}
-/*
-** Return the amount cell p would grow by if it were unioned with pCell.
-*/
-static RtreeDValue cellGrowth(Rtree *pRtree, RtreeCell *p, RtreeCell *pCell){
- RtreeDValue area;
- RtreeCell cell;
- memcpy(&cell, p, sizeof(RtreeCell));
- area = cellArea(pRtree, &cell);
- cellUnion(pRtree, &cell, pCell);
- return (cellArea(pRtree, &cell)-area);
-}
-
static RtreeDValue cellOverlap(
Rtree *pRtree,
RtreeCell *p,
@@ -207317,38 +210692,52 @@ static int ChooseLeaf(
for(ii=0; rc==SQLITE_OK && ii<(pRtree->iDepth-iHeight); ii++){
int iCell;
sqlite3_int64 iBest = 0;
-
+ int bFound = 0;
RtreeDValue fMinGrowth = RTREE_ZERO;
RtreeDValue fMinArea = RTREE_ZERO;
-
int nCell = NCELL(pNode);
- RtreeCell cell;
RtreeNode *pChild = 0;
- RtreeCell *aCell = 0;
-
- /* Select the child node which will be enlarged the least if pCell
- ** is inserted into it. Resolve ties by choosing the entry with
- ** the smallest area.
+ /* First check to see if there is are any cells in pNode that completely
+ ** contains pCell. If two or more cells in pNode completely contain pCell
+ ** then pick the smallest.
*/
for(iCell=0; iCell<nCell; iCell++){
- int bBest = 0;
- RtreeDValue growth;
- RtreeDValue area;
+ RtreeCell cell;
nodeGetCell(pRtree, pNode, iCell, &cell);
- growth = cellGrowth(pRtree, &cell, pCell);
- area = cellArea(pRtree, &cell);
- if( iCell==0||growth<fMinGrowth||(growth==fMinGrowth && area<fMinArea) ){
- bBest = 1;
+ if( cellContains(pRtree, &cell, pCell) ){
+ RtreeDValue area = cellArea(pRtree, &cell);
+ if( bFound==0 || area<fMinArea ){
+ iBest = cell.iRowid;
+ fMinArea = area;
+ bFound = 1;
+ }
}
- if( bBest ){
- fMinGrowth = growth;
- fMinArea = area;
- iBest = cell.iRowid;
+ }
+ if( !bFound ){
+ /* No cells of pNode will completely contain pCell. So pick the
+ ** cell of pNode that grows by the least amount when pCell is added.
+ ** Break ties by selecting the smaller cell.
+ */
+ for(iCell=0; iCell<nCell; iCell++){
+ RtreeCell cell;
+ RtreeDValue growth;
+ RtreeDValue area;
+ nodeGetCell(pRtree, pNode, iCell, &cell);
+ area = cellArea(pRtree, &cell);
+ cellUnion(pRtree, &cell, pCell);
+ growth = cellArea(pRtree, &cell)-area;
+ if( iCell==0
+ || growth<fMinGrowth
+ || (growth==fMinGrowth && area<fMinArea)
+ ){
+ fMinGrowth = growth;
+ fMinArea = area;
+ iBest = cell.iRowid;
+ }
}
}
- sqlite3_free(aCell);
rc = nodeAcquire(pRtree, iBest, pNode, &pChild);
nodeRelease(pRtree, pNode);
pNode = pChild;
@@ -207421,77 +210810,6 @@ static int parentWrite(Rtree *pRtree, sqlite3_int64 iNode, sqlite3_int64 iPar){
static int rtreeInsertCell(Rtree *, RtreeNode *, RtreeCell *, int);
-/*
-** Arguments aIdx, aDistance and aSpare all point to arrays of size
-** nIdx. The aIdx array contains the set of integers from 0 to
-** (nIdx-1) in no particular order. This function sorts the values
-** in aIdx according to the indexed values in aDistance. For
-** example, assuming the inputs:
-**
-** aIdx = { 0, 1, 2, 3 }
-** aDistance = { 5.0, 2.0, 7.0, 6.0 }
-**
-** this function sets the aIdx array to contain:
-**
-** aIdx = { 0, 1, 2, 3 }
-**
-** The aSpare array is used as temporary working space by the
-** sorting algorithm.
-*/
-static void SortByDistance(
- int *aIdx,
- int nIdx,
- RtreeDValue *aDistance,
- int *aSpare
-){
- if( nIdx>1 ){
- int iLeft = 0;
- int iRight = 0;
-
- int nLeft = nIdx/2;
- int nRight = nIdx-nLeft;
- int *aLeft = aIdx;
- int *aRight = &aIdx[nLeft];
-
- SortByDistance(aLeft, nLeft, aDistance, aSpare);
- SortByDistance(aRight, nRight, aDistance, aSpare);
-
- memcpy(aSpare, aLeft, sizeof(int)*nLeft);
- aLeft = aSpare;
-
- while( iLeft<nLeft || iRight<nRight ){
- if( iLeft==nLeft ){
- aIdx[iLeft+iRight] = aRight[iRight];
- iRight++;
- }else if( iRight==nRight ){
- aIdx[iLeft+iRight] = aLeft[iLeft];
- iLeft++;
- }else{
- RtreeDValue fLeft = aDistance[aLeft[iLeft]];
- RtreeDValue fRight = aDistance[aRight[iRight]];
- if( fLeft<fRight ){
- aIdx[iLeft+iRight] = aLeft[iLeft];
- iLeft++;
- }else{
- aIdx[iLeft+iRight] = aRight[iRight];
- iRight++;
- }
- }
- }
-
-#if 0
- /* Check that the sort worked */
- {
- int jj;
- for(jj=1; jj<nIdx; jj++){
- RtreeDValue left = aDistance[aIdx[jj-1]];
- RtreeDValue right = aDistance[aIdx[jj]];
- assert( left<=right );
- }
- }
-#endif
- }
-}
/*
** Arguments aIdx, aCell and aSpare all point to arrays of size
@@ -207976,107 +211294,6 @@ static int deleteCell(Rtree *pRtree, RtreeNode *pNode, int iCell, int iHeight){
return rc;
}
-static int Reinsert(
- Rtree *pRtree,
- RtreeNode *pNode,
- RtreeCell *pCell,
- int iHeight
-){
- int *aOrder;
- int *aSpare;
- RtreeCell *aCell;
- RtreeDValue *aDistance;
- int nCell;
- RtreeDValue aCenterCoord[RTREE_MAX_DIMENSIONS];
- int iDim;
- int ii;
- int rc = SQLITE_OK;
- int n;
-
- memset(aCenterCoord, 0, sizeof(RtreeDValue)*RTREE_MAX_DIMENSIONS);
-
- nCell = NCELL(pNode)+1;
- n = (nCell+1)&(~1);
-
- /* Allocate the buffers used by this operation. The allocation is
- ** relinquished before this function returns.
- */
- aCell = (RtreeCell *)sqlite3_malloc64(n * (
- sizeof(RtreeCell) + /* aCell array */
- sizeof(int) + /* aOrder array */
- sizeof(int) + /* aSpare array */
- sizeof(RtreeDValue) /* aDistance array */
- ));
- if( !aCell ){
- return SQLITE_NOMEM;
- }
- aOrder = (int *)&aCell[n];
- aSpare = (int *)&aOrder[n];
- aDistance = (RtreeDValue *)&aSpare[n];
-
- for(ii=0; ii<nCell; ii++){
- if( ii==(nCell-1) ){
- memcpy(&aCell[ii], pCell, sizeof(RtreeCell));
- }else{
- nodeGetCell(pRtree, pNode, ii, &aCell[ii]);
- }
- aOrder[ii] = ii;
- for(iDim=0; iDim<pRtree->nDim; iDim++){
- aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2]);
- aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2+1]);
- }
- }
- for(iDim=0; iDim<pRtree->nDim; iDim++){
- aCenterCoord[iDim] = (aCenterCoord[iDim]/(nCell*(RtreeDValue)2));
- }
-
- for(ii=0; ii<nCell; ii++){
- aDistance[ii] = RTREE_ZERO;
- for(iDim=0; iDim<pRtree->nDim; iDim++){
- RtreeDValue coord = (DCOORD(aCell[ii].aCoord[iDim*2+1]) -
- DCOORD(aCell[ii].aCoord[iDim*2]));
- aDistance[ii] += (coord-aCenterCoord[iDim])*(coord-aCenterCoord[iDim]);
- }
- }
-
- SortByDistance(aOrder, nCell, aDistance, aSpare);
- nodeZero(pRtree, pNode);
-
- for(ii=0; rc==SQLITE_OK && ii<(nCell-(RTREE_MINCELLS(pRtree)+1)); ii++){
- RtreeCell *p = &aCell[aOrder[ii]];
- nodeInsertCell(pRtree, pNode, p);
- if( p->iRowid==pCell->iRowid ){
- if( iHeight==0 ){
- rc = rowidWrite(pRtree, p->iRowid, pNode->iNode);
- }else{
- rc = parentWrite(pRtree, p->iRowid, pNode->iNode);
- }
- }
- }
- if( rc==SQLITE_OK ){
- rc = fixBoundingBox(pRtree, pNode);
- }
- for(; rc==SQLITE_OK && ii<nCell; ii++){
- /* Find a node to store this cell in. pNode->iNode currently contains
- ** the height of the sub-tree headed by the cell.
- */
- RtreeNode *pInsert;
- RtreeCell *p = &aCell[aOrder[ii]];
- rc = ChooseLeaf(pRtree, p, iHeight, &pInsert);
- if( rc==SQLITE_OK ){
- int rc2;
- rc = rtreeInsertCell(pRtree, pInsert, p, iHeight);
- rc2 = nodeRelease(pRtree, pInsert);
- if( rc==SQLITE_OK ){
- rc = rc2;
- }
- }
- }
-
- sqlite3_free(aCell);
- return rc;
-}
-
/*
** Insert cell pCell into node pNode. Node pNode is the head of a
** subtree iHeight high (leaf nodes have iHeight==0).
@@ -208097,12 +211314,7 @@ static int rtreeInsertCell(
}
}
if( nodeInsertCell(pRtree, pNode, pCell) ){
- if( iHeight<=pRtree->iReinsertHeight || pNode->iNode==1){
- rc = SplitNode(pRtree, pNode, pCell, iHeight);
- }else{
- pRtree->iReinsertHeight = iHeight;
- rc = Reinsert(pRtree, pNode, pCell, iHeight);
- }
+ rc = SplitNode(pRtree, pNode, pCell, iHeight);
}else{
rc = AdjustTree(pRtree, pNode, pCell);
if( ALWAYS(rc==SQLITE_OK) ){
@@ -208445,7 +211657,6 @@ static int rtreeUpdate(
}
if( rc==SQLITE_OK ){
int rc2;
- pRtree->iReinsertHeight = -1;
rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0);
rc2 = nodeRelease(pRtree, pLeaf);
if( rc==SQLITE_OK ){
@@ -208586,8 +211797,11 @@ static int rtreeShadowName(const char *zName){
return 0;
}
+/* Forward declaration */
+static int rtreeIntegrity(sqlite3_vtab*, const char*, const char*, int, char**);
+
static sqlite3_module rtreeModule = {
- 3, /* iVersion */
+ 4, /* iVersion */
rtreeCreate, /* xCreate - create a table */
rtreeConnect, /* xConnect - connect to an existing table */
rtreeBestIndex, /* xBestIndex - Determine search strategy */
@@ -208610,7 +211824,8 @@ static sqlite3_module rtreeModule = {
rtreeSavepoint, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- rtreeShadowName /* xShadowName */
+ rtreeShadowName, /* xShadowName */
+ rtreeIntegrity /* xIntegrity */
};
static int rtreeSqlInit(
@@ -208703,7 +211918,7 @@ static int rtreeSqlInit(
}
sqlite3_free(zSql);
}
- if( pRtree->nAux ){
+ if( pRtree->nAux && rc!=SQLITE_NOMEM ){
pRtree->zReadAuxSql = sqlite3_mprintf(
"SELECT * FROM \"%w\".\"%w_rowid\" WHERE rowid=?1",
zDb, zPrefix);
@@ -208866,22 +212081,27 @@ static int rtreeInit(
}
sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
+ sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
+
/* Allocate the sqlite3_vtab structure */
nDb = (int)strlen(argv[1]);
nName = (int)strlen(argv[2]);
- pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName+2);
+ pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName*2+8);
if( !pRtree ){
return SQLITE_NOMEM;
}
- memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2);
+ memset(pRtree, 0, sizeof(Rtree)+nDb+nName*2+8);
pRtree->nBusy = 1;
pRtree->base.pModule = &rtreeModule;
pRtree->zDb = (char *)&pRtree[1];
pRtree->zName = &pRtree->zDb[nDb+1];
+ pRtree->zNodeName = &pRtree->zName[nName+1];
pRtree->eCoordType = (u8)eCoordType;
memcpy(pRtree->zDb, argv[1], nDb);
memcpy(pRtree->zName, argv[2], nName);
+ memcpy(pRtree->zNodeName, argv[2], nName);
+ memcpy(&pRtree->zNodeName[nName], "_node", 6);
/* Create/Connect to the underlying relational database schema. If
@@ -209378,7 +212598,6 @@ static int rtreeCheckTable(
){
RtreeCheck check; /* Common context for various routines */
sqlite3_stmt *pStmt = 0; /* Used to find column count of rtree table */
- int bEnd = 0; /* True if transaction should be closed */
int nAux = 0; /* Number of extra columns. */
/* Initialize the context object */
@@ -209387,24 +212606,14 @@ static int rtreeCheckTable(
check.zDb = zDb;
check.zTab = zTab;
- /* If there is not already an open transaction, open one now. This is
- ** to ensure that the queries run as part of this integrity-check operate
- ** on a consistent snapshot. */
- if( sqlite3_get_autocommit(db) ){
- check.rc = sqlite3_exec(db, "BEGIN", 0, 0, 0);
- bEnd = 1;
- }
-
/* Find the number of auxiliary columns */
- if( check.rc==SQLITE_OK ){
- pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.'%q_rowid'", zDb, zTab);
- if( pStmt ){
- nAux = sqlite3_column_count(pStmt) - 2;
- sqlite3_finalize(pStmt);
- }else
- if( check.rc!=SQLITE_NOMEM ){
- check.rc = SQLITE_OK;
- }
+ pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.'%q_rowid'", zDb, zTab);
+ if( pStmt ){
+ nAux = sqlite3_column_count(pStmt) - 2;
+ sqlite3_finalize(pStmt);
+ }else
+ if( check.rc!=SQLITE_NOMEM ){
+ check.rc = SQLITE_OK;
}
/* Find number of dimensions in the rtree table. */
@@ -209435,16 +212644,36 @@ static int rtreeCheckTable(
sqlite3_finalize(check.aCheckMapping[0]);
sqlite3_finalize(check.aCheckMapping[1]);
- /* If one was opened, close the transaction */
- if( bEnd ){
- int rc = sqlite3_exec(db, "END", 0, 0, 0);
- if( check.rc==SQLITE_OK ) check.rc = rc;
- }
*pzReport = check.zReport;
return check.rc;
}
/*
+** Implementation of the xIntegrity method for Rtree.
+*/
+static int rtreeIntegrity(
+ sqlite3_vtab *pVtab, /* The virtual table to check */
+ const char *zSchema, /* Schema in which the virtual table lives */
+ const char *zName, /* Name of the virtual table */
+ int isQuick, /* True for a quick_check */
+ char **pzErr /* Write results here */
+){
+ Rtree *pRtree = (Rtree*)pVtab;
+ int rc;
+ assert( pzErr!=0 && *pzErr==0 );
+ UNUSED_PARAMETER(zSchema);
+ UNUSED_PARAMETER(zName);
+ UNUSED_PARAMETER(isQuick);
+ rc = rtreeCheckTable(pRtree->db, pRtree->zDb, pRtree->zName, pzErr);
+ if( rc==SQLITE_OK && *pzErr ){
+ *pzErr = sqlite3_mprintf("In RTree %s.%s:\n%z",
+ pRtree->zDb, pRtree->zName, *pzErr);
+ if( (*pzErr)==0 ) rc = SQLITE_NOMEM;
+ }
+ return rc;
+}
+
+/*
** Usage:
**
** rtreecheck(<rtree-table>);
@@ -210765,24 +213994,28 @@ static int geopolyInit(
(void)pAux;
sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
+ sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
/* Allocate the sqlite3_vtab structure */
nDb = strlen(argv[1]);
nName = strlen(argv[2]);
- pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName+2);
+ pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName*2+8);
if( !pRtree ){
return SQLITE_NOMEM;
}
- memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2);
+ memset(pRtree, 0, sizeof(Rtree)+nDb+nName*2+8);
pRtree->nBusy = 1;
pRtree->base.pModule = &rtreeModule;
pRtree->zDb = (char *)&pRtree[1];
pRtree->zName = &pRtree->zDb[nDb+1];
+ pRtree->zNodeName = &pRtree->zName[nName+1];
pRtree->eCoordType = RTREE_COORD_REAL32;
pRtree->nDim = 2;
pRtree->nDim2 = 4;
memcpy(pRtree->zDb, argv[1], nDb);
memcpy(pRtree->zName, argv[2], nName);
+ memcpy(pRtree->zNodeName, argv[2], nName);
+ memcpy(&pRtree->zNodeName[nName], "_node", 6);
/* Create/Connect to the underlying relational database schema. If
@@ -211196,7 +214429,6 @@ static int geopolyUpdate(
}
if( rc==SQLITE_OK ){
int rc2;
- pRtree->iReinsertHeight = -1;
rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0);
rc2 = nodeRelease(pRtree, pLeaf);
if( rc==SQLITE_OK ){
@@ -211293,7 +214525,8 @@ static sqlite3_module geopolyModule = {
rtreeSavepoint, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- rtreeShadowName /* xShadowName */
+ rtreeShadowName, /* xShadowName */
+ rtreeIntegrity /* xIntegrity */
};
static int sqlite3_geopoly_init(sqlite3 *db){
@@ -219307,7 +222540,8 @@ SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
return sqlite3_create_module(db, "dbstat", &dbstat_module, 0);
}
@@ -219744,7 +222978,8 @@ SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0);
}
@@ -219875,6 +223110,18 @@ struct sqlite3_changeset_iter {
** The data associated with each hash-table entry is a structure containing
** a subset of the initial values that the modified row contained at the
** start of the session. Or no initial values if the row was inserted.
+**
+** pDfltStmt:
+** This is only used by the sqlite3changegroup_xxx() APIs, not by
+** regular sqlite3_session objects. It is a SELECT statement that
+** selects the default value for each table column. For example,
+** if the table is
+**
+** CREATE TABLE xx(a DEFAULT 1, b, c DEFAULT 'abc')
+**
+** then this variable is the compiled version of:
+**
+** SELECT 1, NULL, 'abc'
*/
struct SessionTable {
SessionTable *pNext;
@@ -219883,10 +223130,12 @@ struct SessionTable {
int bStat1; /* True if this is sqlite_stat1 */
int bRowid; /* True if this table uses rowid for PK */
const char **azCol; /* Column names */
+ const char **azDflt; /* Default value expressions */
u8 *abPK; /* Array of primary key flags */
int nEntry; /* Total number of entries in hash table */
int nChange; /* Size of apChange[] array */
SessionChange **apChange; /* Hash table buckets */
+ sqlite3_stmt *pDfltStmt;
};
/*
@@ -220055,6 +223304,7 @@ struct SessionTable {
struct SessionChange {
u8 op; /* One of UPDATE, DELETE, INSERT */
u8 bIndirect; /* True if this change is "indirect" */
+ u16 nRecordField; /* Number of fields in aRecord[] */
int nMaxSize; /* Max size of eventual changeset record */
int nRecord; /* Number of bytes in buffer aRecord[] */
u8 *aRecord; /* Buffer containing old.* record */
@@ -220080,7 +223330,7 @@ static int sessionVarintLen(int iVal){
** Read a varint value from aBuf[] into *piVal. Return the number of
** bytes read.
*/
-static int sessionVarintGet(u8 *aBuf, int *piVal){
+static int sessionVarintGet(const u8 *aBuf, int *piVal){
return getVarint32(aBuf, *piVal);
}
@@ -220343,9 +223593,11 @@ static int sessionPreupdateHash(
** Return the number of bytes of space occupied by the value (including
** the type byte).
*/
-static int sessionSerialLen(u8 *a){
- int e = *a;
+static int sessionSerialLen(const u8 *a){
+ int e;
int n;
+ assert( a!=0 );
+ e = *a;
if( e==0 || e==0xFF ) return 1;
if( e==SQLITE_NULL ) return 1;
if( e==SQLITE_INTEGER || e==SQLITE_FLOAT ) return 9;
@@ -220750,13 +224002,14 @@ static int sessionGrowHash(
**
** For example, if the table is declared as:
**
-** CREATE TABLE tbl1(w, x, y, z, PRIMARY KEY(w, z));
+** CREATE TABLE tbl1(w, x DEFAULT 'abc', y, z, PRIMARY KEY(w, z));
**
-** Then the four output variables are populated as follows:
+** Then the five output variables are populated as follows:
**
** *pnCol = 4
** *pzTab = "tbl1"
** *pazCol = {"w", "x", "y", "z"}
+** *pazDflt = {NULL, 'abc', NULL, NULL}
** *pabPK = {1, 0, 0, 1}
**
** All returned buffers are part of the same single allocation, which must
@@ -220770,6 +224023,7 @@ static int sessionTableInfo(
int *pnCol, /* OUT: number of columns */
const char **pzTab, /* OUT: Copy of zThis */
const char ***pazCol, /* OUT: Array of column names for table */
+ const char ***pazDflt, /* OUT: Array of default value expressions */
u8 **pabPK, /* OUT: Array of booleans - true for PK col */
int *pbRowid /* OUT: True if only PK is a rowid */
){
@@ -220782,11 +224036,18 @@ static int sessionTableInfo(
int i;
u8 *pAlloc = 0;
char **azCol = 0;
+ char **azDflt = 0;
u8 *abPK = 0;
int bRowid = 0; /* Set to true to use rowid as PK */
assert( pazCol && pabPK );
+ *pazCol = 0;
+ *pabPK = 0;
+ *pnCol = 0;
+ if( pzTab ) *pzTab = 0;
+ if( pazDflt ) *pazDflt = 0;
+
nThis = sqlite3Strlen30(zThis);
if( nThis==12 && 0==sqlite3_stricmp("sqlite_stat1", zThis) ){
rc = sqlite3_table_column_metadata(db, zDb, zThis, 0, 0, 0, 0, 0, 0);
@@ -220800,39 +224061,28 @@ static int sessionTableInfo(
}else if( rc==SQLITE_ERROR ){
zPragma = sqlite3_mprintf("");
}else{
- *pazCol = 0;
- *pabPK = 0;
- *pnCol = 0;
- if( pzTab ) *pzTab = 0;
return rc;
}
}else{
zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis);
}
if( !zPragma ){
- *pazCol = 0;
- *pabPK = 0;
- *pnCol = 0;
- if( pzTab ) *pzTab = 0;
return SQLITE_NOMEM;
}
rc = sqlite3_prepare_v2(db, zPragma, -1, &pStmt, 0);
sqlite3_free(zPragma);
if( rc!=SQLITE_OK ){
- *pazCol = 0;
- *pabPK = 0;
- *pnCol = 0;
- if( pzTab ) *pzTab = 0;
return rc;
}
nByte = nThis + 1;
bRowid = (pbRowid!=0);
while( SQLITE_ROW==sqlite3_step(pStmt) ){
- nByte += sqlite3_column_bytes(pStmt, 1);
+ nByte += sqlite3_column_bytes(pStmt, 1); /* name */
+ nByte += sqlite3_column_bytes(pStmt, 4); /* dflt_value */
nDbCol++;
- if( sqlite3_column_int(pStmt, 5) ) bRowid = 0;
+ if( sqlite3_column_int(pStmt, 5) ) bRowid = 0; /* pk */
}
if( nDbCol==0 ) bRowid = 0;
nDbCol += bRowid;
@@ -220840,15 +224090,18 @@ static int sessionTableInfo(
rc = sqlite3_reset(pStmt);
if( rc==SQLITE_OK ){
- nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1);
+ nByte += nDbCol * (sizeof(const char *)*2 + sizeof(u8) + 1 + 1);
pAlloc = sessionMalloc64(pSession, nByte);
if( pAlloc==0 ){
rc = SQLITE_NOMEM;
+ }else{
+ memset(pAlloc, 0, nByte);
}
}
if( rc==SQLITE_OK ){
azCol = (char **)pAlloc;
- pAlloc = (u8 *)&azCol[nDbCol];
+ azDflt = (char**)&azCol[nDbCol];
+ pAlloc = (u8 *)&azDflt[nDbCol];
abPK = (u8 *)pAlloc;
pAlloc = &abPK[nDbCol];
if( pzTab ){
@@ -220868,11 +224121,21 @@ static int sessionTableInfo(
}
while( SQLITE_ROW==sqlite3_step(pStmt) ){
int nName = sqlite3_column_bytes(pStmt, 1);
+ int nDflt = sqlite3_column_bytes(pStmt, 4);
const unsigned char *zName = sqlite3_column_text(pStmt, 1);
+ const unsigned char *zDflt = sqlite3_column_text(pStmt, 4);
+
if( zName==0 ) break;
memcpy(pAlloc, zName, nName+1);
azCol[i] = (char *)pAlloc;
pAlloc += nName+1;
+ if( zDflt ){
+ memcpy(pAlloc, zDflt, nDflt+1);
+ azDflt[i] = (char *)pAlloc;
+ pAlloc += nDflt+1;
+ }else{
+ azDflt[i] = 0;
+ }
abPK[i] = sqlite3_column_int(pStmt, 5);
i++;
}
@@ -220883,14 +224146,11 @@ static int sessionTableInfo(
** free any allocation made. An error code will be returned in this case.
*/
if( rc==SQLITE_OK ){
- *pazCol = (const char **)azCol;
+ *pazCol = (const char**)azCol;
+ if( pazDflt ) *pazDflt = (const char**)azDflt;
*pabPK = abPK;
*pnCol = nDbCol;
}else{
- *pazCol = 0;
- *pabPK = 0;
- *pnCol = 0;
- if( pzTab ) *pzTab = 0;
sessionFree(pSession, azCol);
}
if( pbRowid ) *pbRowid = bRowid;
@@ -220899,10 +224159,9 @@ static int sessionTableInfo(
}
/*
-** This function is only called from within a pre-update handler for a
-** write to table pTab, part of session pSession. If this is the first
-** write to this table, initalize the SessionTable.nCol, azCol[] and
-** abPK[] arrays accordingly.
+** This function is called to initialize the SessionTable.nCol, azCol[]
+** abPK[] and azDflt[] members of SessionTable object pTab. If these
+** fields are already initilialized, this function is a no-op.
**
** If an error occurs, an error code is stored in sqlite3_session.rc and
** non-zero returned. Or, if no error occurs but the table has no primary
@@ -220910,15 +224169,22 @@ static int sessionTableInfo(
** indicate that updates on this table should be ignored. SessionTable.abPK
** is set to NULL in this case.
*/
-static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
+static int sessionInitTable(
+ sqlite3_session *pSession, /* Optional session handle */
+ SessionTable *pTab, /* Table object to initialize */
+ sqlite3 *db, /* Database handle to read schema from */
+ const char *zDb /* Name of db - "main", "temp" etc. */
+){
+ int rc = SQLITE_OK;
+
if( pTab->nCol==0 ){
u8 *abPK;
assert( pTab->azCol==0 || pTab->abPK==0 );
- pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb,
- pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK,
- (pSession->bImplicitPK ? &pTab->bRowid : 0)
+ rc = sessionTableInfo(pSession, db, zDb,
+ pTab->zName, &pTab->nCol, 0, &pTab->azCol, &pTab->azDflt, &abPK,
+ ((pSession==0 || pSession->bImplicitPK) ? &pTab->bRowid : 0)
);
- if( pSession->rc==SQLITE_OK ){
+ if( rc==SQLITE_OK ){
int i;
for(i=0; i<pTab->nCol; i++){
if( abPK[i] ){
@@ -220930,14 +224196,321 @@ static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
pTab->bStat1 = 1;
}
- if( pSession->bEnableSize ){
+ if( pSession && pSession->bEnableSize ){
pSession->nMaxChangesetSize += (
1 + sessionVarintLen(pTab->nCol) + pTab->nCol + strlen(pTab->zName)+1
);
}
}
}
- return (pSession->rc || pTab->abPK==0);
+
+ if( pSession ){
+ pSession->rc = rc;
+ return (rc || pTab->abPK==0);
+ }
+ return rc;
+}
+
+/*
+** Re-initialize table object pTab.
+*/
+static int sessionReinitTable(sqlite3_session *pSession, SessionTable *pTab){
+ int nCol = 0;
+ const char **azCol = 0;
+ const char **azDflt = 0;
+ u8 *abPK = 0;
+ int bRowid = 0;
+
+ assert( pSession->rc==SQLITE_OK );
+
+ pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb,
+ pTab->zName, &nCol, 0, &azCol, &azDflt, &abPK,
+ (pSession->bImplicitPK ? &bRowid : 0)
+ );
+ if( pSession->rc==SQLITE_OK ){
+ if( pTab->nCol>nCol || pTab->bRowid!=bRowid ){
+ pSession->rc = SQLITE_SCHEMA;
+ }else{
+ int ii;
+ int nOldCol = pTab->nCol;
+ for(ii=0; ii<nCol; ii++){
+ if( ii<pTab->nCol ){
+ if( pTab->abPK[ii]!=abPK[ii] ){
+ pSession->rc = SQLITE_SCHEMA;
+ }
+ }else if( abPK[ii] ){
+ pSession->rc = SQLITE_SCHEMA;
+ }
+ }
+
+ if( pSession->rc==SQLITE_OK ){
+ const char **a = pTab->azCol;
+ pTab->azCol = azCol;
+ pTab->nCol = nCol;
+ pTab->azDflt = azDflt;
+ pTab->abPK = abPK;
+ azCol = a;
+ }
+ if( pSession->bEnableSize ){
+ pSession->nMaxChangesetSize += (nCol - nOldCol);
+ pSession->nMaxChangesetSize += sessionVarintLen(nCol);
+ pSession->nMaxChangesetSize -= sessionVarintLen(nOldCol);
+ }
+ }
+ }
+
+ sqlite3_free((char*)azCol);
+ return pSession->rc;
+}
+
+/*
+** Session-change object (*pp) contains an old.* record with fewer than
+** nCol fields. This function updates it with the default values for
+** the missing fields.
+*/
+static void sessionUpdateOneChange(
+ sqlite3_session *pSession, /* For memory accounting */
+ int *pRc, /* IN/OUT: Error code */
+ SessionChange **pp, /* IN/OUT: Change object to update */
+ int nCol, /* Number of columns now in table */
+ sqlite3_stmt *pDflt /* SELECT <default-values...> */
+){
+ SessionChange *pOld = *pp;
+
+ while( pOld->nRecordField<nCol ){
+ SessionChange *pNew = 0;
+ int nByte = 0;
+ int nIncr = 0;
+ int iField = pOld->nRecordField;
+ int eType = sqlite3_column_type(pDflt, iField);
+ switch( eType ){
+ case SQLITE_NULL:
+ nIncr = 1;
+ break;
+ case SQLITE_INTEGER:
+ case SQLITE_FLOAT:
+ nIncr = 9;
+ break;
+ default: {
+ int n = sqlite3_column_bytes(pDflt, iField);
+ nIncr = 1 + sessionVarintLen(n) + n;
+ assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
+ break;
+ }
+ }
+
+ nByte = nIncr + (sizeof(SessionChange) + pOld->nRecord);
+ pNew = sessionMalloc64(pSession, nByte);
+ if( pNew==0 ){
+ *pRc = SQLITE_NOMEM;
+ return;
+ }else{
+ memcpy(pNew, pOld, sizeof(SessionChange));
+ pNew->aRecord = (u8*)&pNew[1];
+ memcpy(pNew->aRecord, pOld->aRecord, pOld->nRecord);
+ pNew->aRecord[pNew->nRecord++] = (u8)eType;
+ switch( eType ){
+ case SQLITE_INTEGER: {
+ i64 iVal = sqlite3_column_int64(pDflt, iField);
+ sessionPutI64(&pNew->aRecord[pNew->nRecord], iVal);
+ pNew->nRecord += 8;
+ break;
+ }
+
+ case SQLITE_FLOAT: {
+ double rVal = sqlite3_column_double(pDflt, iField);
+ i64 iVal = 0;
+ memcpy(&iVal, &rVal, sizeof(rVal));
+ sessionPutI64(&pNew->aRecord[pNew->nRecord], iVal);
+ pNew->nRecord += 8;
+ break;
+ }
+
+ case SQLITE_TEXT: {
+ int n = sqlite3_column_bytes(pDflt, iField);
+ const char *z = (const char*)sqlite3_column_text(pDflt, iField);
+ pNew->nRecord += sessionVarintPut(&pNew->aRecord[pNew->nRecord], n);
+ memcpy(&pNew->aRecord[pNew->nRecord], z, n);
+ pNew->nRecord += n;
+ break;
+ }
+
+ case SQLITE_BLOB: {
+ int n = sqlite3_column_bytes(pDflt, iField);
+ const u8 *z = (const u8*)sqlite3_column_blob(pDflt, iField);
+ pNew->nRecord += sessionVarintPut(&pNew->aRecord[pNew->nRecord], n);
+ memcpy(&pNew->aRecord[pNew->nRecord], z, n);
+ pNew->nRecord += n;
+ break;
+ }
+
+ default:
+ assert( eType==SQLITE_NULL );
+ break;
+ }
+
+ sessionFree(pSession, pOld);
+ *pp = pOld = pNew;
+ pNew->nRecordField++;
+ pNew->nMaxSize += nIncr;
+ if( pSession ){
+ pSession->nMaxChangesetSize += nIncr;
+ }
+ }
+ }
+}
+
+/*
+** Ensure that there is room in the buffer to append nByte bytes of data.
+** If not, use sqlite3_realloc() to grow the buffer so that there is.
+**
+** If successful, return zero. Otherwise, if an OOM condition is encountered,
+** set *pRc to SQLITE_NOMEM and return non-zero.
+*/
+static int sessionBufferGrow(SessionBuffer *p, i64 nByte, int *pRc){
+#define SESSION_MAX_BUFFER_SZ (0x7FFFFF00 - 1)
+ i64 nReq = p->nBuf + nByte;
+ if( *pRc==SQLITE_OK && nReq>p->nAlloc ){
+ u8 *aNew;
+ i64 nNew = p->nAlloc ? p->nAlloc : 128;
+
+ do {
+ nNew = nNew*2;
+ }while( nNew<nReq );
+
+ /* The value of SESSION_MAX_BUFFER_SZ is copied from the implementation
+ ** of sqlite3_realloc64(). Allocations greater than this size in bytes
+ ** always fail. It is used here to ensure that this routine can always
+ ** allocate up to this limit - instead of up to the largest power of
+ ** two smaller than the limit. */
+ if( nNew>SESSION_MAX_BUFFER_SZ ){
+ nNew = SESSION_MAX_BUFFER_SZ;
+ if( nNew<nReq ){
+ *pRc = SQLITE_NOMEM;
+ return 1;
+ }
+ }
+
+ aNew = (u8 *)sqlite3_realloc64(p->aBuf, nNew);
+ if( 0==aNew ){
+ *pRc = SQLITE_NOMEM;
+ }else{
+ p->aBuf = aNew;
+ p->nAlloc = nNew;
+ }
+ }
+ return (*pRc!=SQLITE_OK);
+}
+
+
+/*
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. Otherwise, append a string to the buffer. All bytes in the string
+** up to (but not including) the nul-terminator are written to the buffer.
+**
+** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
+** returning.
+*/
+static void sessionAppendStr(
+ SessionBuffer *p,
+ const char *zStr,
+ int *pRc
+){
+ int nStr = sqlite3Strlen30(zStr);
+ if( 0==sessionBufferGrow(p, nStr+1, pRc) ){
+ memcpy(&p->aBuf[p->nBuf], zStr, nStr);
+ p->nBuf += nStr;
+ p->aBuf[p->nBuf] = 0x00;
+ }
+}
+
+/*
+** Format a string using printf() style formatting and then append it to the
+** buffer using sessionAppendString().
+*/
+static void sessionAppendPrintf(
+ SessionBuffer *p, /* Buffer to append to */
+ int *pRc,
+ const char *zFmt,
+ ...
+){
+ if( *pRc==SQLITE_OK ){
+ char *zApp = 0;
+ va_list ap;
+ va_start(ap, zFmt);
+ zApp = sqlite3_vmprintf(zFmt, ap);
+ if( zApp==0 ){
+ *pRc = SQLITE_NOMEM;
+ }else{
+ sessionAppendStr(p, zApp, pRc);
+ }
+ va_end(ap);
+ sqlite3_free(zApp);
+ }
+}
+
+/*
+** Prepare a statement against database handle db that SELECTs a single
+** row containing the default values for each column in table pTab. For
+** example, if pTab is declared as:
+**
+** CREATE TABLE pTab(a PRIMARY KEY, b DEFAULT 123, c DEFAULT 'abcd');
+**
+** Then this function prepares and returns the SQL statement:
+**
+** SELECT NULL, 123, 'abcd';
+*/
+static int sessionPrepareDfltStmt(
+ sqlite3 *db, /* Database handle */
+ SessionTable *pTab, /* Table to prepare statement for */
+ sqlite3_stmt **ppStmt /* OUT: Statement handle */
+){
+ SessionBuffer sql = {0,0,0};
+ int rc = SQLITE_OK;
+ const char *zSep = " ";
+ int ii = 0;
+
+ *ppStmt = 0;
+ sessionAppendPrintf(&sql, &rc, "SELECT");
+ for(ii=0; ii<pTab->nCol; ii++){
+ const char *zDflt = pTab->azDflt[ii] ? pTab->azDflt[ii] : "NULL";
+ sessionAppendPrintf(&sql, &rc, "%s%s", zSep, zDflt);
+ zSep = ", ";
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_prepare_v2(db, (const char*)sql.aBuf, -1, ppStmt, 0);
+ }
+ sqlite3_free(sql.aBuf);
+
+ return rc;
+}
+
+/*
+** Table pTab has one or more existing change-records with old.* records
+** with fewer than pTab->nCol columns. This function updates all such
+** change-records with the default values for the missing columns.
+*/
+static int sessionUpdateChanges(sqlite3_session *pSession, SessionTable *pTab){
+ sqlite3_stmt *pStmt = 0;
+ int rc = pSession->rc;
+
+ rc = sessionPrepareDfltStmt(pSession->db, pTab, &pStmt);
+ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
+ int ii = 0;
+ SessionChange **pp = 0;
+ for(ii=0; ii<pTab->nChange; ii++){
+ for(pp=&pTab->apChange[ii]; *pp; pp=&((*pp)->pNext)){
+ if( (*pp)->nRecordField!=pTab->nCol ){
+ sessionUpdateOneChange(pSession, &rc, pp, pTab->nCol, pStmt);
+ }
+ }
+ }
+ }
+
+ pSession->rc = rc;
+ rc = sqlite3_finalize(pStmt);
+ if( pSession->rc==SQLITE_OK ) pSession->rc = rc;
+ return pSession->rc;
}
/*
@@ -221100,16 +224673,22 @@ static void sessionPreupdateOneChange(
int iHash;
int bNull = 0;
int rc = SQLITE_OK;
+ int nExpect = 0;
SessionStat1Ctx stat1 = {{0,0,0,0,0},0};
if( pSession->rc ) return;
/* Load table details if required */
- if( sessionInitTable(pSession, pTab) ) return;
+ if( sessionInitTable(pSession, pTab, pSession->db, pSession->zDb) ) return;
/* Check the number of columns in this xPreUpdate call matches the
** number of columns in the table. */
- if( (pTab->nCol-pTab->bRowid)!=pSession->hook.xCount(pSession->hook.pCtx) ){
+ nExpect = pSession->hook.xCount(pSession->hook.pCtx);
+ if( (pTab->nCol-pTab->bRowid)<nExpect ){
+ if( sessionReinitTable(pSession, pTab) ) return;
+ if( sessionUpdateChanges(pSession, pTab) ) return;
+ }
+ if( (pTab->nCol-pTab->bRowid)!=nExpect ){
pSession->rc = SQLITE_SCHEMA;
return;
}
@@ -221186,7 +224765,7 @@ static void sessionPreupdateOneChange(
}
/* Allocate the change object */
- pC = (SessionChange *)sessionMalloc64(pSession, nByte);
+ pC = (SessionChange*)sessionMalloc64(pSession, nByte);
if( !pC ){
rc = SQLITE_NOMEM;
goto error_out;
@@ -221219,6 +224798,7 @@ static void sessionPreupdateOneChange(
if( pSession->bIndirect || pSession->hook.xDepth(pSession->hook.pCtx) ){
pC->bIndirect = 1;
}
+ pC->nRecordField = pTab->nCol;
pC->nRecord = nByte;
pC->op = op;
pC->pNext = pTab->apChange[iHash];
@@ -221598,7 +225178,7 @@ SQLITE_API int sqlite3session_diff(
/* Locate and if necessary initialize the target table object */
rc = sessionFindTable(pSession, zTbl, &pTo);
if( pTo==0 ) goto diff_out;
- if( sessionInitTable(pSession, pTo) ){
+ if( sessionInitTable(pSession, pTo, pSession->db, pSession->zDb) ){
rc = pSession->rc;
goto diff_out;
}
@@ -221611,7 +225191,7 @@ SQLITE_API int sqlite3session_diff(
int bRowid = 0;
u8 *abPK;
const char **azCol = 0;
- rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, &abPK,
+ rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, 0, &abPK,
pSession->bImplicitPK ? &bRowid : 0
);
if( rc==SQLITE_OK ){
@@ -221726,6 +225306,7 @@ static void sessionDeleteTable(sqlite3_session *pSession, SessionTable *pList){
sessionFree(pSession, p);
}
}
+ sqlite3_finalize(pTab->pDfltStmt);
sessionFree(pSession, (char*)pTab->azCol); /* cast works around VC++ bug */
sessionFree(pSession, pTab->apChange);
sessionFree(pSession, pTab);
@@ -221758,9 +225339,7 @@ SQLITE_API void sqlite3session_delete(sqlite3_session *pSession){
** associated hash-tables. */
sessionDeleteTable(pSession, pSession->pTable);
- /* Assert that all allocations have been freed and then free the
- ** session object itself. */
- assert( pSession->nMalloc==0 );
+ /* Free the session object. */
sqlite3_free(pSession);
}
@@ -221832,48 +225411,6 @@ SQLITE_API int sqlite3session_attach(
}
/*
-** Ensure that there is room in the buffer to append nByte bytes of data.
-** If not, use sqlite3_realloc() to grow the buffer so that there is.
-**
-** If successful, return zero. Otherwise, if an OOM condition is encountered,
-** set *pRc to SQLITE_NOMEM and return non-zero.
-*/
-static int sessionBufferGrow(SessionBuffer *p, i64 nByte, int *pRc){
-#define SESSION_MAX_BUFFER_SZ (0x7FFFFF00 - 1)
- i64 nReq = p->nBuf + nByte;
- if( *pRc==SQLITE_OK && nReq>p->nAlloc ){
- u8 *aNew;
- i64 nNew = p->nAlloc ? p->nAlloc : 128;
-
- do {
- nNew = nNew*2;
- }while( nNew<nReq );
-
- /* The value of SESSION_MAX_BUFFER_SZ is copied from the implementation
- ** of sqlite3_realloc64(). Allocations greater than this size in bytes
- ** always fail. It is used here to ensure that this routine can always
- ** allocate up to this limit - instead of up to the largest power of
- ** two smaller than the limit. */
- if( nNew>SESSION_MAX_BUFFER_SZ ){
- nNew = SESSION_MAX_BUFFER_SZ;
- if( nNew<nReq ){
- *pRc = SQLITE_NOMEM;
- return 1;
- }
- }
-
- aNew = (u8 *)sqlite3_realloc64(p->aBuf, nNew);
- if( 0==aNew ){
- *pRc = SQLITE_NOMEM;
- }else{
- p->aBuf = aNew;
- p->nAlloc = nNew;
- }
- }
- return (*pRc!=SQLITE_OK);
-}
-
-/*
** Append the value passed as the second argument to the buffer passed
** as the first.
**
@@ -221943,27 +225480,6 @@ static void sessionAppendBlob(
/*
** This function is a no-op if *pRc is other than SQLITE_OK when it is
-** called. Otherwise, append a string to the buffer. All bytes in the string
-** up to (but not including) the nul-terminator are written to the buffer.
-**
-** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
-** returning.
-*/
-static void sessionAppendStr(
- SessionBuffer *p,
- const char *zStr,
- int *pRc
-){
- int nStr = sqlite3Strlen30(zStr);
- if( 0==sessionBufferGrow(p, nStr+1, pRc) ){
- memcpy(&p->aBuf[p->nBuf], zStr, nStr);
- p->nBuf += nStr;
- p->aBuf[p->nBuf] = 0x00;
- }
-}
-
-/*
-** This function is a no-op if *pRc is other than SQLITE_OK when it is
** called. Otherwise, append the string representation of integer iVal
** to the buffer. No nul-terminator is written.
**
@@ -221980,27 +225496,6 @@ static void sessionAppendInteger(
sessionAppendStr(p, aBuf, pRc);
}
-static void sessionAppendPrintf(
- SessionBuffer *p, /* Buffer to append to */
- int *pRc,
- const char *zFmt,
- ...
-){
- if( *pRc==SQLITE_OK ){
- char *zApp = 0;
- va_list ap;
- va_start(ap, zFmt);
- zApp = sqlite3_vmprintf(zFmt, ap);
- if( zApp==0 ){
- *pRc = SQLITE_NOMEM;
- }else{
- sessionAppendStr(p, zApp, pRc);
- }
- va_end(ap);
- sqlite3_free(zApp);
- }
-}
-
/*
** This function is a no-op if *pRc is other than SQLITE_OK when it is
** called. Otherwise, append the string zStr enclosed in quotes (") and
@@ -222491,26 +225986,16 @@ static int sessionGenerateChangeset(
for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
if( pTab->nEntry ){
const char *zName = pTab->zName;
- int nCol = 0; /* Number of columns in table */
- u8 *abPK = 0; /* Primary key array */
- const char **azCol = 0; /* Table columns */
int i; /* Used to iterate through hash buckets */
sqlite3_stmt *pSel = 0; /* SELECT statement to query table pTab */
int nRewind = buf.nBuf; /* Initial size of write buffer */
int nNoop; /* Size of buffer after writing tbl header */
- int bRowid = 0;
+ int nOldCol = pTab->nCol;
/* Check the table schema is still Ok. */
- rc = sessionTableInfo(
- 0, db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK,
- (pSession->bImplicitPK ? &bRowid : 0)
- );
- if( rc==SQLITE_OK && (
- pTab->nCol!=nCol
- || pTab->bRowid!=bRowid
- || memcmp(abPK, pTab->abPK, nCol)
- )){
- rc = SQLITE_SCHEMA;
+ rc = sessionReinitTable(pSession, pTab);
+ if( rc==SQLITE_OK && pTab->nCol!=nOldCol ){
+ rc = sessionUpdateChanges(pSession, pTab);
}
/* Write a table header */
@@ -222518,8 +226003,8 @@ static int sessionGenerateChangeset(
/* Build and compile a statement to execute: */
if( rc==SQLITE_OK ){
- rc = sessionSelectStmt(
- db, 0, pSession->zDb, zName, bRowid, nCol, azCol, abPK, &pSel
+ rc = sessionSelectStmt(db, 0, pSession->zDb,
+ zName, pTab->bRowid, pTab->nCol, pTab->azCol, pTab->abPK, &pSel
);
}
@@ -222528,22 +226013,22 @@ static int sessionGenerateChangeset(
SessionChange *p; /* Used to iterate through changes */
for(p=pTab->apChange[i]; rc==SQLITE_OK && p; p=p->pNext){
- rc = sessionSelectBind(pSel, nCol, abPK, p);
+ rc = sessionSelectBind(pSel, pTab->nCol, pTab->abPK, p);
if( rc!=SQLITE_OK ) continue;
if( sqlite3_step(pSel)==SQLITE_ROW ){
if( p->op==SQLITE_INSERT ){
int iCol;
sessionAppendByte(&buf, SQLITE_INSERT, &rc);
sessionAppendByte(&buf, p->bIndirect, &rc);
- for(iCol=0; iCol<nCol; iCol++){
+ for(iCol=0; iCol<pTab->nCol; iCol++){
sessionAppendCol(&buf, pSel, iCol, &rc);
}
}else{
- assert( abPK!=0 ); /* Because sessionSelectStmt() returned ok */
- rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, abPK);
+ assert( pTab->abPK!=0 );
+ rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, pTab->abPK);
}
}else if( p->op!=SQLITE_INSERT ){
- rc = sessionAppendDelete(&buf, bPatchset, p, nCol, abPK);
+ rc = sessionAppendDelete(&buf, bPatchset, p, pTab->nCol,pTab->abPK);
}
if( rc==SQLITE_OK ){
rc = sqlite3_reset(pSel);
@@ -222568,7 +226053,6 @@ static int sessionGenerateChangeset(
if( buf.nBuf==nNoop ){
buf.nBuf = nRewind;
}
- sqlite3_free((char*)azCol); /* cast works around VC++ bug */
}
}
@@ -224697,7 +228181,7 @@ static int sessionChangesetApply(
sqlite3changeset_pk(pIter, &abPK, 0);
rc = sessionTableInfo(0, db, "main", zNew,
- &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK, &sApply.bRowid
+ &sApply.nCol, &zTab, &sApply.azCol, 0, &sApply.abPK, &sApply.bRowid
);
if( rc!=SQLITE_OK ) break;
for(i=0; i<sApply.nCol; i++){
@@ -224829,11 +228313,24 @@ SQLITE_API int sqlite3changeset_apply_v2(
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
int bInv = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset, bInv, 1);
+ u64 savedFlag = db->flags & SQLITE_FkNoAction;
+
+ if( flags & SQLITE_CHANGESETAPPLY_FKNOACTION ){
+ db->flags |= ((u64)SQLITE_FkNoAction);
+ db->aDb[0].pSchema->schema_cookie -= 32;
+ }
+
if( rc==SQLITE_OK ){
rc = sessionChangesetApply(
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
);
}
+
+ if( (flags & SQLITE_CHANGESETAPPLY_FKNOACTION) && savedFlag==0 ){
+ assert( db->flags & SQLITE_FkNoAction );
+ db->flags &= ~((u64)SQLITE_FkNoAction);
+ db->aDb[0].pSchema->schema_cookie -= 32;
+ }
return rc;
}
@@ -224921,6 +228418,9 @@ struct sqlite3_changegroup {
int rc; /* Error code */
int bPatch; /* True to accumulate patchsets */
SessionTable *pList; /* List of tables in current patch */
+
+ sqlite3 *db; /* Configured by changegroup_schema() */
+ char *zDb; /* Configured by changegroup_schema() */
};
/*
@@ -224941,6 +228441,7 @@ static int sessionChangeMerge(
){
SessionChange *pNew = 0;
int rc = SQLITE_OK;
+ assert( aRec!=0 );
if( !pExist ){
pNew = (SessionChange *)sqlite3_malloc64(sizeof(SessionChange) + nRec);
@@ -225107,6 +228608,114 @@ static int sessionChangeMerge(
}
/*
+** Check if a changeset entry with nCol columns and the PK array passed
+** as the final argument to this function is compatible with SessionTable
+** pTab. If so, return 1. Otherwise, if they are incompatible in some way,
+** return 0.
+*/
+static int sessionChangesetCheckCompat(
+ SessionTable *pTab,
+ int nCol,
+ u8 *abPK
+){
+ if( pTab->azCol && nCol<pTab->nCol ){
+ int ii;
+ for(ii=0; ii<pTab->nCol; ii++){
+ u8 bPK = (ii < nCol) ? abPK[ii] : 0;
+ if( pTab->abPK[ii]!=bPK ) return 0;
+ }
+ return 1;
+ }
+ return (pTab->nCol==nCol && 0==memcmp(abPK, pTab->abPK, nCol));
+}
+
+static int sessionChangesetExtendRecord(
+ sqlite3_changegroup *pGrp,
+ SessionTable *pTab,
+ int nCol,
+ int op,
+ const u8 *aRec,
+ int nRec,
+ SessionBuffer *pOut
+){
+ int rc = SQLITE_OK;
+ int ii = 0;
+
+ assert( pTab->azCol );
+ assert( nCol<pTab->nCol );
+
+ pOut->nBuf = 0;
+ if( op==SQLITE_INSERT || (op==SQLITE_DELETE && pGrp->bPatch==0) ){
+ /* Append the missing default column values to the record. */
+ sessionAppendBlob(pOut, aRec, nRec, &rc);
+ if( rc==SQLITE_OK && pTab->pDfltStmt==0 ){
+ rc = sessionPrepareDfltStmt(pGrp->db, pTab, &pTab->pDfltStmt);
+ }
+ for(ii=nCol; rc==SQLITE_OK && ii<pTab->nCol; ii++){
+ int eType = sqlite3_column_type(pTab->pDfltStmt, ii);
+ sessionAppendByte(pOut, eType, &rc);
+ switch( eType ){
+ case SQLITE_FLOAT:
+ case SQLITE_INTEGER: {
+ i64 iVal;
+ if( eType==SQLITE_INTEGER ){
+ iVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
+ }else{
+ double rVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
+ memcpy(&iVal, &rVal, sizeof(i64));
+ }
+ if( SQLITE_OK==sessionBufferGrow(pOut, 8, &rc) ){
+ sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal);
+ }
+ break;
+ }
+
+ case SQLITE_BLOB:
+ case SQLITE_TEXT: {
+ int n = sqlite3_column_bytes(pTab->pDfltStmt, ii);
+ sessionAppendVarint(pOut, n, &rc);
+ if( eType==SQLITE_TEXT ){
+ const u8 *z = (const u8*)sqlite3_column_text(pTab->pDfltStmt, ii);
+ sessionAppendBlob(pOut, z, n, &rc);
+ }else{
+ const u8 *z = (const u8*)sqlite3_column_blob(pTab->pDfltStmt, ii);
+ sessionAppendBlob(pOut, z, n, &rc);
+ }
+ break;
+ }
+
+ default:
+ assert( eType==SQLITE_NULL );
+ break;
+ }
+ }
+ }else if( op==SQLITE_UPDATE ){
+ /* Append missing "undefined" entries to the old.* record. And, if this
+ ** is an UPDATE, to the new.* record as well. */
+ int iOff = 0;
+ if( pGrp->bPatch==0 ){
+ for(ii=0; ii<nCol; ii++){
+ iOff += sessionSerialLen(&aRec[iOff]);
+ }
+ sessionAppendBlob(pOut, aRec, iOff, &rc);
+ for(ii=0; ii<(pTab->nCol-nCol); ii++){
+ sessionAppendByte(pOut, 0x00, &rc);
+ }
+ }
+
+ sessionAppendBlob(pOut, &aRec[iOff], nRec-iOff, &rc);
+ for(ii=0; ii<(pTab->nCol-nCol); ii++){
+ sessionAppendByte(pOut, 0x00, &rc);
+ }
+ }else{
+ assert( op==SQLITE_DELETE && pGrp->bPatch );
+ sessionAppendBlob(pOut, aRec, nRec, &rc);
+ }
+
+ return rc;
+}
+
+/*
** Add all changes in the changeset traversed by the iterator passed as
** the first argument to the changegroup hash tables.
*/
@@ -225119,6 +228728,7 @@ static int sessionChangesetToHash(
int nRec;
int rc = SQLITE_OK;
SessionTable *pTab = 0;
+ SessionBuffer rec = {0, 0, 0};
while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, 0) ){
const char *zNew;
@@ -225130,6 +228740,9 @@ static int sessionChangesetToHash(
SessionChange *pExist = 0;
SessionChange **pp;
+ /* Ensure that only changesets, or only patchsets, but not a mixture
+ ** of both, are being combined. It is an error to try to combine a
+ ** changeset and a patchset. */
if( pGrp->pList==0 ){
pGrp->bPatch = pIter->bPatchset;
}else if( pIter->bPatchset!=pGrp->bPatch ){
@@ -225162,18 +228775,38 @@ static int sessionChangesetToHash(
pTab->zName = (char*)&pTab->abPK[nCol];
memcpy(pTab->zName, zNew, nNew+1);
+ if( pGrp->db ){
+ pTab->nCol = 0;
+ rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb);
+ if( rc ){
+ assert( pTab->azCol==0 );
+ sqlite3_free(pTab);
+ break;
+ }
+ }
+
/* The new object must be linked on to the end of the list, not
** simply added to the start of it. This is to ensure that the
** tables within the output of sqlite3changegroup_output() are in
** the right order. */
for(ppTab=&pGrp->pList; *ppTab; ppTab=&(*ppTab)->pNext);
*ppTab = pTab;
- }else if( pTab->nCol!=nCol || memcmp(pTab->abPK, abPK, nCol) ){
+ }
+
+ if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){
rc = SQLITE_SCHEMA;
break;
}
}
+ if( nCol<pTab->nCol ){
+ assert( pGrp->db );
+ rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, &rec);
+ if( rc ) break;
+ aRec = rec.aBuf;
+ nRec = rec.nBuf;
+ }
+
if( sessionGrowHash(0, pIter->bPatchset, pTab) ){
rc = SQLITE_NOMEM;
break;
@@ -225211,6 +228844,7 @@ static int sessionChangesetToHash(
}
}
+ sqlite3_free(rec.aBuf);
if( rc==SQLITE_OK ) rc = pIter->rc;
return rc;
}
@@ -225298,6 +228932,31 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp){
}
/*
+** Provide a database schema to the changegroup object.
+*/
+SQLITE_API int sqlite3changegroup_schema(
+ sqlite3_changegroup *pGrp,
+ sqlite3 *db,
+ const char *zDb
+){
+ int rc = SQLITE_OK;
+
+ if( pGrp->pList || pGrp->db ){
+ /* Cannot add a schema after one or more calls to sqlite3changegroup_add(),
+ ** or after sqlite3changegroup_schema() has already been called. */
+ rc = SQLITE_MISUSE;
+ }else{
+ pGrp->zDb = sqlite3_mprintf("%s", zDb);
+ if( pGrp->zDb==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ pGrp->db = db;
+ }
+ }
+ return rc;
+}
+
+/*
** Add the changeset currently stored in buffer pData, size nData bytes,
** to changeset-group p.
*/
@@ -225360,6 +229019,7 @@ SQLITE_API int sqlite3changegroup_output_strm(
*/
SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){
if( pGrp ){
+ sqlite3_free(pGrp->zDb);
sessionDeleteTable(0, pGrp->pList);
sqlite3_free(pGrp);
}
@@ -225892,8 +229552,11 @@ struct Fts5PhraseIter {
** created with the "columnsize=0" option.
**
** xColumnText:
-** This function attempts to retrieve the text of column iCol of the
-** current document. If successful, (*pz) is set to point to a buffer
+** If parameter iCol is less than zero, or greater than or equal to the
+** number of columns in the table, SQLITE_RANGE is returned.
+**
+** Otherwise, this function attempts to retrieve the text of column iCol of
+** the current document. If successful, (*pz) is set to point to a buffer
** containing the text in utf-8 encoding, (*pn) is set to the size in bytes
** (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
** if an error occurs, an SQLite error code is returned and the final values
@@ -225903,8 +229566,10 @@ struct Fts5PhraseIter {
** Returns the number of phrases in the current query expression.
**
** xPhraseSize:
-** Returns the number of tokens in phrase iPhrase of the query. Phrases
-** are numbered starting from zero.
+** If parameter iCol is less than zero, or greater than or equal to the
+** number of phrases in the current query, as returned by xPhraseCount,
+** 0 is returned. Otherwise, this function returns the number of tokens in
+** phrase iPhrase of the query. Phrases are numbered starting from zero.
**
** xInstCount:
** Set *pnInst to the total number of occurrences of all phrases within
@@ -225920,12 +229585,13 @@ struct Fts5PhraseIter {
** Query for the details of phrase match iIdx within the current row.
** Phrase matches are numbered starting from zero, so the iIdx argument
** should be greater than or equal to zero and smaller than the value
-** output by xInstCount().
+** output by xInstCount(). If iIdx is less than zero or greater than
+** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned.
**
-** Usually, output parameter *piPhrase is set to the phrase number, *piCol
+** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol
** to the column in which it occurs and *piOff the token offset of the
-** first token of the phrase. Returns SQLITE_OK if successful, or an error
-** code (i.e. SQLITE_NOMEM) if an error occurs.
+** first token of the phrase. SQLITE_OK is returned if successful, or an
+** error code (i.e. SQLITE_NOMEM) if an error occurs.
**
** This API can be quite slow if used with an FTS5 table created with the
** "detail=none" or "detail=column" option.
@@ -225951,6 +229617,10 @@ struct Fts5PhraseIter {
** Invoking Api.xUserData() returns a copy of the pointer passed as
** the third argument to pUserData.
**
+** If parameter iPhrase is less than zero, or greater than or equal to
+** the number of phrases in the query, as returned by xPhraseCount(),
+** this function returns SQLITE_RANGE.
+**
** If the callback function returns any value other than SQLITE_OK, the
** query is abandoned and the xQueryPhrase function returns immediately.
** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
@@ -226065,9 +229735,42 @@ struct Fts5PhraseIter {
**
** xPhraseNextColumn()
** See xPhraseFirstColumn above.
+**
+** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken)
+** This is used to access token iToken of phrase iPhrase of the current
+** query. Before returning, output parameter *ppToken is set to point
+** to a buffer containing the requested token, and *pnToken to the
+** size of this buffer in bytes.
+**
+** If iPhrase or iToken are less than zero, or if iPhrase is greater than
+** or equal to the number of phrases in the query as reported by
+** xPhraseCount(), or if iToken is equal to or greater than the number of
+** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken
+ are both zeroed.
+**
+** The output text is not a copy of the query text that specified the
+** token. It is the output of the tokenizer module. For tokendata=1
+** tables, this includes any embedded 0x00 and trailing data.
+**
+** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken)
+** This is used to access token iToken of phrase hit iIdx within the
+** current row. If iIdx is less than zero or greater than or equal to the
+** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
+** output variable (*ppToken) is set to point to a buffer containing the
+** matching document token, and (*pnToken) to the size of that buffer in
+** bytes. This API is not available if the specified token matches a
+** prefix query term. In that case both output variables are always set
+** to 0.
+**
+** The output text is not a copy of the document text that was tokenized.
+** It is the output of the tokenizer module. For tokendata=1 tables, this
+** includes any embedded 0x00 and trailing data.
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option.
*/
struct Fts5ExtensionApi {
- int iVersion; /* Currently always set to 2 */
+ int iVersion; /* Currently always set to 3 */
void *(*xUserData)(Fts5Context*);
@@ -226102,6 +229805,13 @@ struct Fts5ExtensionApi {
int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
+
+ /* Below this point are iVersion>=3 only */
+ int (*xQueryToken)(Fts5Context*,
+ int iPhrase, int iToken,
+ const char **ppToken, int *pnToken
+ );
+ int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*);
};
/*
@@ -226576,6 +230286,7 @@ struct Fts5Config {
char *zContent; /* content table */
char *zContentRowid; /* "content_rowid=" option value */
int bColumnsize; /* "columnsize=" option value (dflt==1) */
+ int bTokendata; /* "tokendata=" option value (dflt==0) */
int eDetail; /* FTS5_DETAIL_XXX value */
char *zContentExprlist;
Fts5Tokenizer *pTok;
@@ -226764,17 +230475,19 @@ struct Fts5IndexIter {
/*
** Values used as part of the flags argument passed to IndexQuery().
*/
-#define FTS5INDEX_QUERY_PREFIX 0x0001 /* Prefix query */
-#define FTS5INDEX_QUERY_DESC 0x0002 /* Docs in descending rowid order */
-#define FTS5INDEX_QUERY_TEST_NOIDX 0x0004 /* Do not use prefix index */
-#define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */
+#define FTS5INDEX_QUERY_PREFIX 0x0001 /* Prefix query */
+#define FTS5INDEX_QUERY_DESC 0x0002 /* Docs in descending rowid order */
+#define FTS5INDEX_QUERY_TEST_NOIDX 0x0004 /* Do not use prefix index */
+#define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */
/* The following are used internally by the fts5_index.c module. They are
** defined here only to make it easier to avoid clashes with the flags
** above. */
-#define FTS5INDEX_QUERY_SKIPEMPTY 0x0010
-#define FTS5INDEX_QUERY_NOOUTPUT 0x0020
-#define FTS5INDEX_QUERY_SKIPHASH 0x0040
+#define FTS5INDEX_QUERY_SKIPEMPTY 0x0010
+#define FTS5INDEX_QUERY_NOOUTPUT 0x0020
+#define FTS5INDEX_QUERY_SKIPHASH 0x0040
+#define FTS5INDEX_QUERY_NOTOKENDATA 0x0080
+#define FTS5INDEX_QUERY_SCANONETERM 0x0100
/*
** Create/destroy an Fts5Index object.
@@ -226843,6 +230556,10 @@ static void *sqlite3Fts5StructureRef(Fts5Index*);
static void sqlite3Fts5StructureRelease(void*);
static int sqlite3Fts5StructureTest(Fts5Index*, void*);
+/*
+** Used by xInstToken():
+*/
+static int sqlite3Fts5IterToken(Fts5IndexIter*, i64, int, int, const char**, int*);
/*
** Insert or remove data to or from the index. Each time a document is
@@ -226920,6 +230637,13 @@ static int sqlite3Fts5IndexLoadConfig(Fts5Index *p);
static int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin);
static int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid);
+static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter*);
+
+/* Used to populate hash tables for xInstToken in detail=none/column mode. */
+static int sqlite3Fts5IndexIterWriteTokendata(
+ Fts5IndexIter*, const char*, int, i64 iRowid, int iCol, int iOff
+);
+
/*
** End of interface to code in fts5_index.c.
**************************************************************************/
@@ -227025,6 +230749,7 @@ static void sqlite3Fts5HashScanNext(Fts5Hash*);
static int sqlite3Fts5HashScanEof(Fts5Hash*);
static void sqlite3Fts5HashScanEntry(Fts5Hash *,
const char **pzTerm, /* OUT: term (nul-terminated) */
+ int *pnTerm, /* OUT: Size of term in bytes */
const u8 **ppDoclist, /* OUT: pointer to doclist */
int *pnDoclist /* OUT: size of doclist in bytes */
);
@@ -227151,6 +230876,10 @@ static int sqlite3Fts5ExprClonePhrase(Fts5Expr*, int, Fts5Expr**);
static int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *);
+static int sqlite3Fts5ExprQueryToken(Fts5Expr*, int, int, const char**, int*);
+static int sqlite3Fts5ExprInstToken(Fts5Expr*, i64, int, int, int, int, const char**, int*);
+static void sqlite3Fts5ExprClearTokens(Fts5Expr*);
+
/*******************************************
** The fts5_expr.c API above this point is used by the other hand-written
** C code in this module. The interfaces below this point are called by
@@ -228865,15 +232594,19 @@ static int fts5CInstIterInit(
*/
typedef struct HighlightContext HighlightContext;
struct HighlightContext {
- CInstIter iter; /* Coalesced Instance Iterator */
- int iPos; /* Current token offset in zIn[] */
+ /* Constant parameters to fts5HighlightCb() */
int iRangeStart; /* First token to include */
int iRangeEnd; /* If non-zero, last token to include */
const char *zOpen; /* Opening highlight */
const char *zClose; /* Closing highlight */
const char *zIn; /* Input text */
int nIn; /* Size of input text in bytes */
- int iOff; /* Current offset within zIn[] */
+
+ /* Variables modified by fts5HighlightCb() */
+ CInstIter iter; /* Coalesced Instance Iterator */
+ int iPos; /* Current token offset in zIn[] */
+ int iOff; /* Have copied up to this offset in zIn[] */
+ int bOpen; /* True if highlight is open */
char *zOut; /* Output value */
};
@@ -228906,8 +232639,8 @@ static int fts5HighlightCb(
int tflags, /* Mask of FTS5_TOKEN_* flags */
const char *pToken, /* Buffer containing token */
int nToken, /* Size of token in bytes */
- int iStartOff, /* Start offset of token */
- int iEndOff /* End offset of token */
+ int iStartOff, /* Start byte offset of token */
+ int iEndOff /* End byte offset of token */
){
HighlightContext *p = (HighlightContext*)pContext;
int rc = SQLITE_OK;
@@ -228923,30 +232656,55 @@ static int fts5HighlightCb(
if( p->iRangeStart && iPos==p->iRangeStart ) p->iOff = iStartOff;
}
- if( iPos==p->iter.iStart ){
+ /* If the parenthesis is open, and this token is not part of the current
+ ** phrase, and the starting byte offset of this token is past the point
+ ** that has currently been copied into the output buffer, close the
+ ** parenthesis. */
+ if( p->bOpen
+ && (iPos<=p->iter.iStart || p->iter.iStart<0)
+ && iStartOff>p->iOff
+ ){
+ fts5HighlightAppend(&rc, p, p->zClose, -1);
+ p->bOpen = 0;
+ }
+
+ /* If this is the start of a new phrase, and the highlight is not open:
+ **
+ ** * copy text from the input up to the start of the phrase, and
+ ** * open the highlight.
+ */
+ if( iPos==p->iter.iStart && p->bOpen==0 ){
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iStartOff - p->iOff);
fts5HighlightAppend(&rc, p, p->zOpen, -1);
p->iOff = iStartOff;
+ p->bOpen = 1;
}
if( iPos==p->iter.iEnd ){
- if( p->iRangeEnd>=0 && p->iter.iStart<p->iRangeStart ){
+ if( p->bOpen==0 ){
+ assert( p->iRangeEnd>=0 );
fts5HighlightAppend(&rc, p, p->zOpen, -1);
+ p->bOpen = 1;
}
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
- fts5HighlightAppend(&rc, p, p->zClose, -1);
p->iOff = iEndOff;
+
if( rc==SQLITE_OK ){
rc = fts5CInstIterNext(&p->iter);
}
}
- if( p->iRangeEnd>=0 && iPos==p->iRangeEnd ){
- fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
- p->iOff = iEndOff;
- if( iPos>=p->iter.iStart && iPos<p->iter.iEnd ){
+ if( iPos==p->iRangeEnd ){
+ if( p->bOpen ){
+ if( p->iter.iStart>=0 && iPos>=p->iter.iStart ){
+ fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
+ p->iOff = iEndOff;
+ }
fts5HighlightAppend(&rc, p, p->zClose, -1);
+ p->bOpen = 0;
}
+ fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
+ p->iOff = iEndOff;
}
return rc;
@@ -228978,8 +232736,10 @@ static void fts5HighlightFunction(
ctx.zClose = (const char*)sqlite3_value_text(apVal[2]);
ctx.iRangeEnd = -1;
rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn);
-
- if( ctx.zIn ){
+ if( rc==SQLITE_RANGE ){
+ sqlite3_result_text(pCtx, "", -1, SQLITE_STATIC);
+ rc = SQLITE_OK;
+ }else if( ctx.zIn ){
if( rc==SQLITE_OK ){
rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter);
}
@@ -228987,6 +232747,9 @@ static void fts5HighlightFunction(
if( rc==SQLITE_OK ){
rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
}
+ if( ctx.bOpen ){
+ fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1);
+ }
fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff);
if( rc==SQLITE_OK ){
@@ -229265,6 +233028,9 @@ static void fts5SnippetFunction(
if( rc==SQLITE_OK ){
rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
}
+ if( ctx.bOpen ){
+ fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1);
+ }
if( ctx.iRangeEnd>=(nColSize-1) ){
fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff);
}else{
@@ -229540,6 +233306,7 @@ static void sqlite3Fts5BufferAppendBlob(
){
if( nData ){
if( fts5BufferGrow(pRc, pBuf, nData) ) return;
+ assert( pBuf->p!=0 );
memcpy(&pBuf->p[pBuf->n], pData, nData);
pBuf->n += nData;
}
@@ -229641,6 +233408,7 @@ static int sqlite3Fts5PoslistNext64(
i64 *piOff /* IN/OUT: Current offset */
){
int i = *pi;
+ assert( a!=0 || i==0 );
if( i>=n ){
/* EOF */
*piOff = -1;
@@ -229648,6 +233416,7 @@ static int sqlite3Fts5PoslistNext64(
}else{
i64 iOff = *piOff;
u32 iVal;
+ assert( a!=0 );
fts5FastGetVarint32(a, i, iVal);
if( iVal<=1 ){
if( iVal==0 ){
@@ -230279,6 +234048,16 @@ static int fts5ConfigParseSpecial(
return rc;
}
+ if( sqlite3_strnicmp("tokendata", zCmd, nCmd)==0 ){
+ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){
+ *pzErr = sqlite3_mprintf("malformed tokendata=... directive");
+ rc = SQLITE_ERROR;
+ }else{
+ pConfig->bTokendata = (zArg[0]=='1');
+ }
+ return rc;
+ }
+
*pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd);
return SQLITE_ERROR;
}
@@ -231012,7 +234791,9 @@ struct Fts5ExprNode {
struct Fts5ExprTerm {
u8 bPrefix; /* True for a prefix term */
u8 bFirst; /* True if token must be first in column */
- char *zTerm; /* nul-terminated term */
+ char *pTerm; /* Term data */
+ int nQueryTerm; /* Effective size of term in bytes */
+ int nFullTerm; /* Size of term in bytes incl. tokendata */
Fts5IndexIter *pIter; /* Iterator for this term */
Fts5ExprTerm *pSynonym; /* Pointer to first in list of synonyms */
};
@@ -231879,7 +235660,7 @@ static int fts5ExprNearInitAll(
p->pIter = 0;
}
rc = sqlite3Fts5IndexQuery(
- pExpr->pIndex, p->zTerm, (int)strlen(p->zTerm),
+ pExpr->pIndex, p->pTerm, p->nQueryTerm,
(pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) |
(pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0),
pNear->pColset,
@@ -232516,7 +236297,7 @@ static void fts5ExprPhraseFree(Fts5ExprPhrase *pPhrase){
Fts5ExprTerm *pSyn;
Fts5ExprTerm *pNext;
Fts5ExprTerm *pTerm = &pPhrase->aTerm[i];
- sqlite3_free(pTerm->zTerm);
+ sqlite3_free(pTerm->pTerm);
sqlite3Fts5IterClose(pTerm->pIter);
for(pSyn=pTerm->pSynonym; pSyn; pSyn=pNext){
pNext = pSyn->pSynonym;
@@ -232614,6 +236395,7 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset(
typedef struct TokenCtx TokenCtx;
struct TokenCtx {
Fts5ExprPhrase *pPhrase;
+ Fts5Config *pConfig;
int rc;
};
@@ -232647,8 +236429,12 @@ static int fts5ParseTokenize(
rc = SQLITE_NOMEM;
}else{
memset(pSyn, 0, (size_t)nByte);
- pSyn->zTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer);
- memcpy(pSyn->zTerm, pToken, nToken);
+ pSyn->pTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer);
+ pSyn->nFullTerm = pSyn->nQueryTerm = nToken;
+ if( pCtx->pConfig->bTokendata ){
+ pSyn->nQueryTerm = (int)strlen(pSyn->pTerm);
+ }
+ memcpy(pSyn->pTerm, pToken, nToken);
pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym;
pPhrase->aTerm[pPhrase->nTerm-1].pSynonym = pSyn;
}
@@ -232673,7 +236459,11 @@ static int fts5ParseTokenize(
if( rc==SQLITE_OK ){
pTerm = &pPhrase->aTerm[pPhrase->nTerm++];
memset(pTerm, 0, sizeof(Fts5ExprTerm));
- pTerm->zTerm = sqlite3Fts5Strndup(&rc, pToken, nToken);
+ pTerm->pTerm = sqlite3Fts5Strndup(&rc, pToken, nToken);
+ pTerm->nFullTerm = pTerm->nQueryTerm = nToken;
+ if( pCtx->pConfig->bTokendata && rc==SQLITE_OK ){
+ pTerm->nQueryTerm = (int)strlen(pTerm->pTerm);
+ }
}
}
@@ -232740,6 +236530,7 @@ static Fts5ExprPhrase *sqlite3Fts5ParseTerm(
memset(&sCtx, 0, sizeof(TokenCtx));
sCtx.pPhrase = pAppend;
+ sCtx.pConfig = pConfig;
rc = fts5ParseStringFromToken(pToken, &z);
if( rc==SQLITE_OK ){
@@ -232787,12 +236578,15 @@ static int sqlite3Fts5ExprClonePhrase(
Fts5Expr **ppNew
){
int rc = SQLITE_OK; /* Return code */
- Fts5ExprPhrase *pOrig; /* The phrase extracted from pExpr */
+ Fts5ExprPhrase *pOrig = 0; /* The phrase extracted from pExpr */
Fts5Expr *pNew = 0; /* Expression to return via *ppNew */
- TokenCtx sCtx = {0,0}; /* Context object for fts5ParseTokenize */
-
- pOrig = pExpr->apExprPhrase[iPhrase];
- pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr));
+ TokenCtx sCtx = {0,0,0}; /* Context object for fts5ParseTokenize */
+ if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){
+ rc = SQLITE_RANGE;
+ }else{
+ pOrig = pExpr->apExprPhrase[iPhrase];
+ pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr));
+ }
if( rc==SQLITE_OK ){
pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc,
sizeof(Fts5ExprPhrase*));
@@ -232805,7 +236599,7 @@ static int sqlite3Fts5ExprClonePhrase(
pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc,
sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*));
}
- if( rc==SQLITE_OK ){
+ if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){
Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset;
if( pColsetOrig ){
sqlite3_int64 nByte;
@@ -232819,26 +236613,27 @@ static int sqlite3Fts5ExprClonePhrase(
}
}
- if( pOrig->nTerm ){
- int i; /* Used to iterate through phrase terms */
- for(i=0; rc==SQLITE_OK && i<pOrig->nTerm; i++){
- int tflags = 0;
- Fts5ExprTerm *p;
- for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){
- const char *zTerm = p->zTerm;
- rc = fts5ParseTokenize((void*)&sCtx, tflags, zTerm, (int)strlen(zTerm),
- 0, 0);
- tflags = FTS5_TOKEN_COLOCATED;
- }
- if( rc==SQLITE_OK ){
- sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
- sCtx.pPhrase->aTerm[i].bFirst = pOrig->aTerm[i].bFirst;
+ if( rc==SQLITE_OK ){
+ if( pOrig->nTerm ){
+ int i; /* Used to iterate through phrase terms */
+ sCtx.pConfig = pExpr->pConfig;
+ for(i=0; rc==SQLITE_OK && i<pOrig->nTerm; i++){
+ int tflags = 0;
+ Fts5ExprTerm *p;
+ for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){
+ rc = fts5ParseTokenize((void*)&sCtx,tflags,p->pTerm,p->nFullTerm,0,0);
+ tflags = FTS5_TOKEN_COLOCATED;
+ }
+ if( rc==SQLITE_OK ){
+ sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
+ sCtx.pPhrase->aTerm[i].bFirst = pOrig->aTerm[i].bFirst;
+ }
}
+ }else{
+ /* This happens when parsing a token or quoted phrase that contains
+ ** no token characters at all. (e.g ... MATCH '""'). */
+ sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase));
}
- }else{
- /* This happens when parsing a token or quoted phrase that contains
- ** no token characters at all. (e.g ... MATCH '""'). */
- sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase));
}
if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){
@@ -233208,11 +237003,13 @@ static Fts5ExprNode *fts5ParsePhraseToAnd(
if( parseGrowPhraseArray(pParse) ){
fts5ExprPhraseFree(pPhrase);
}else{
+ Fts5ExprTerm *p = &pNear->apPhrase[0]->aTerm[ii];
+ Fts5ExprTerm *pTo = &pPhrase->aTerm[0];
pParse->apPhrase[pParse->nPhrase++] = pPhrase;
pPhrase->nTerm = 1;
- pPhrase->aTerm[0].zTerm = sqlite3Fts5Strndup(
- &pParse->rc, pNear->apPhrase[0]->aTerm[ii].zTerm, -1
- );
+ pTo->pTerm = sqlite3Fts5Strndup(&pParse->rc, p->pTerm, p->nFullTerm);
+ pTo->nQueryTerm = p->nQueryTerm;
+ pTo->nFullTerm = p->nFullTerm;
pRet->apChild[ii] = sqlite3Fts5ParseNode(pParse, FTS5_STRING,
0, 0, sqlite3Fts5ParseNearset(pParse, 0, pPhrase)
);
@@ -233397,16 +237194,17 @@ static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){
/* Determine the maximum amount of space required. */
for(p=pTerm; p; p=p->pSynonym){
- nByte += (int)strlen(pTerm->zTerm) * 2 + 3 + 2;
+ nByte += pTerm->nQueryTerm * 2 + 3 + 2;
}
zQuoted = sqlite3_malloc64(nByte);
if( zQuoted ){
int i = 0;
for(p=pTerm; p; p=p->pSynonym){
- char *zIn = p->zTerm;
+ char *zIn = p->pTerm;
+ char *zEnd = &zIn[p->nQueryTerm];
zQuoted[i++] = '"';
- while( *zIn ){
+ while( zIn<zEnd ){
if( *zIn=='"' ) zQuoted[i++] = '"';
zQuoted[i++] = *zIn++;
}
@@ -233484,8 +237282,10 @@ static char *fts5ExprPrintTcl(
zRet = fts5PrintfAppend(zRet, " {");
for(iTerm=0; zRet && iTerm<pPhrase->nTerm; iTerm++){
- char *zTerm = pPhrase->aTerm[iTerm].zTerm;
- zRet = fts5PrintfAppend(zRet, "%s%s", iTerm==0?"":" ", zTerm);
+ Fts5ExprTerm *p = &pPhrase->aTerm[iTerm];
+ zRet = fts5PrintfAppend(zRet, "%s%.*s", iTerm==0?"":" ",
+ p->nQueryTerm, p->pTerm
+ );
if( pPhrase->aTerm[iTerm].bPrefix ){
zRet = fts5PrintfAppend(zRet, "*");
}
@@ -233886,6 +237686,17 @@ static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){
return 0;
}
+/*
+** pToken is a buffer nToken bytes in size that may or may not contain
+** an embedded 0x00 byte. If it does, return the number of bytes in
+** the buffer before the 0x00. If it does not, return nToken.
+*/
+static int fts5QueryTerm(const char *pToken, int nToken){
+ int ii;
+ for(ii=0; ii<nToken && pToken[ii]; ii++){}
+ return ii;
+}
+
static int fts5ExprPopulatePoslistsCb(
void *pCtx, /* Copy of 2nd argument to xTokenize() */
int tflags, /* Mask of FTS5_TOKEN_* flags */
@@ -233897,22 +237708,33 @@ static int fts5ExprPopulatePoslistsCb(
Fts5ExprCtx *p = (Fts5ExprCtx*)pCtx;
Fts5Expr *pExpr = p->pExpr;
int i;
+ int nQuery = nToken;
+ i64 iRowid = pExpr->pRoot->iRowid;
UNUSED_PARAM2(iUnused1, iUnused2);
- if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE;
+ if( nQuery>FTS5_MAX_TOKEN_SIZE ) nQuery = FTS5_MAX_TOKEN_SIZE;
+ if( pExpr->pConfig->bTokendata ){
+ nQuery = fts5QueryTerm(pToken, nQuery);
+ }
if( (tflags & FTS5_TOKEN_COLOCATED)==0 ) p->iOff++;
for(i=0; i<pExpr->nPhrase; i++){
- Fts5ExprTerm *pTerm;
+ Fts5ExprTerm *pT;
if( p->aPopulator[i].bOk==0 ) continue;
- for(pTerm=&pExpr->apExprPhrase[i]->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){
- int nTerm = (int)strlen(pTerm->zTerm);
- if( (nTerm==nToken || (nTerm<nToken && pTerm->bPrefix))
- && memcmp(pTerm->zTerm, pToken, nTerm)==0
+ for(pT=&pExpr->apExprPhrase[i]->aTerm[0]; pT; pT=pT->pSynonym){
+ if( (pT->nQueryTerm==nQuery || (pT->nQueryTerm<nQuery && pT->bPrefix))
+ && memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0
){
int rc = sqlite3Fts5PoslistWriterAppend(
&pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff
);
+ if( rc==SQLITE_OK && pExpr->pConfig->bTokendata && !pT->bPrefix ){
+ int iCol = p->iOff>>32;
+ int iTokOff = p->iOff & 0x7FFFFFFF;
+ rc = sqlite3Fts5IndexIterWriteTokendata(
+ pT->pIter, pToken, nToken, iRowid, iCol, iTokOff
+ );
+ }
if( rc ) return rc;
break;
}
@@ -234049,6 +237871,83 @@ static int sqlite3Fts5ExprPhraseCollist(
}
/*
+** Does the work of the fts5_api.xQueryToken() API method.
+*/
+static int sqlite3Fts5ExprQueryToken(
+ Fts5Expr *pExpr,
+ int iPhrase,
+ int iToken,
+ const char **ppOut,
+ int *pnOut
+){
+ Fts5ExprPhrase *pPhrase = 0;
+
+ if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){
+ return SQLITE_RANGE;
+ }
+ pPhrase = pExpr->apExprPhrase[iPhrase];
+ if( iToken<0 || iToken>=pPhrase->nTerm ){
+ return SQLITE_RANGE;
+ }
+
+ *ppOut = pPhrase->aTerm[iToken].pTerm;
+ *pnOut = pPhrase->aTerm[iToken].nFullTerm;
+ return SQLITE_OK;
+}
+
+/*
+** Does the work of the fts5_api.xInstToken() API method.
+*/
+static int sqlite3Fts5ExprInstToken(
+ Fts5Expr *pExpr,
+ i64 iRowid,
+ int iPhrase,
+ int iCol,
+ int iOff,
+ int iToken,
+ const char **ppOut,
+ int *pnOut
+){
+ Fts5ExprPhrase *pPhrase = 0;
+ Fts5ExprTerm *pTerm = 0;
+ int rc = SQLITE_OK;
+
+ if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){
+ return SQLITE_RANGE;
+ }
+ pPhrase = pExpr->apExprPhrase[iPhrase];
+ if( iToken<0 || iToken>=pPhrase->nTerm ){
+ return SQLITE_RANGE;
+ }
+ pTerm = &pPhrase->aTerm[iToken];
+ if( pTerm->bPrefix==0 ){
+ if( pExpr->pConfig->bTokendata ){
+ rc = sqlite3Fts5IterToken(
+ pTerm->pIter, iRowid, iCol, iOff+iToken, ppOut, pnOut
+ );
+ }else{
+ *ppOut = pTerm->pTerm;
+ *pnOut = pTerm->nFullTerm;
+ }
+ }
+ return rc;
+}
+
+/*
+** Clear the token mappings for all Fts5IndexIter objects mannaged by
+** the expression passed as the only argument.
+*/
+static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){
+ int ii;
+ for(ii=0; ii<pExpr->nPhrase; ii++){
+ Fts5ExprTerm *pT;
+ for(pT=&pExpr->apExprPhrase[ii]->aTerm[0]; pT; pT=pT->pSynonym){
+ sqlite3Fts5IndexIterClearTokendata(pT->pIter);
+ }
+ }
+}
+
+/*
** 2014 August 11
**
** The author disclaims copyright to this source code. In place of
@@ -234086,10 +237985,15 @@ struct Fts5Hash {
/*
** Each entry in the hash table is represented by an object of the
-** following type. Each object, its key (a nul-terminated string) and
-** its current data are stored in a single memory allocation. The
-** key immediately follows the object in memory. The position list
-** data immediately follows the key data in memory.
+** following type. Each object, its key, and its current data are stored
+** in a single memory allocation. The key immediately follows the object
+** in memory. The position list data immediately follows the key data
+** in memory.
+**
+** The key is Fts5HashEntry.nKey bytes in size. It consists of a single
+** byte identifying the index (either the main term index or a prefix-index),
+** followed by the term data. For example: "0token". There is no
+** nul-terminator - in this case nKey=6.
**
** The data that follows the key is in a similar, but not identical format
** to the doclist data stored in the database. It is:
@@ -234224,8 +238128,7 @@ static int fts5HashResize(Fts5Hash *pHash){
unsigned int iHash;
Fts5HashEntry *p = apOld[i];
apOld[i] = p->pHashNext;
- iHash = fts5HashKey(nNew, (u8*)fts5EntryKey(p),
- (int)strlen(fts5EntryKey(p)));
+ iHash = fts5HashKey(nNew, (u8*)fts5EntryKey(p), p->nKey);
p->pHashNext = apNew[iHash];
apNew[iHash] = p;
}
@@ -234309,7 +238212,7 @@ static int sqlite3Fts5HashWrite(
for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){
char *zKey = fts5EntryKey(p);
if( zKey[0]==bByte
- && p->nKey==nToken
+ && p->nKey==nToken+1
&& memcmp(&zKey[1], pToken, nToken)==0
){
break;
@@ -234339,9 +238242,9 @@ static int sqlite3Fts5HashWrite(
zKey[0] = bByte;
memcpy(&zKey[1], pToken, nToken);
assert( iHash==fts5HashKey(pHash->nSlot, (u8*)zKey, nToken+1) );
- p->nKey = nToken;
+ p->nKey = nToken+1;
zKey[nToken+1] = '\0';
- p->nData = nToken+1 + 1 + sizeof(Fts5HashEntry);
+ p->nData = nToken+1 + sizeof(Fts5HashEntry);
p->pHashNext = pHash->aSlot[iHash];
pHash->aSlot[iHash] = p;
pHash->nEntry++;
@@ -234458,12 +238361,17 @@ static Fts5HashEntry *fts5HashEntryMerge(
*ppOut = p1;
p1 = 0;
}else{
- int i = 0;
char *zKey1 = fts5EntryKey(p1);
char *zKey2 = fts5EntryKey(p2);
- while( zKey1[i]==zKey2[i] ) i++;
+ int nMin = MIN(p1->nKey, p2->nKey);
- if( ((u8)zKey1[i])>((u8)zKey2[i]) ){
+ int cmp = memcmp(zKey1, zKey2, nMin);
+ if( cmp==0 ){
+ cmp = p1->nKey - p2->nKey;
+ }
+ assert( cmp!=0 );
+
+ if( cmp>0 ){
/* p2 is smaller */
*ppOut = p2;
ppOut = &p2->pScanNext;
@@ -234482,10 +238390,8 @@ static Fts5HashEntry *fts5HashEntryMerge(
}
/*
-** Extract all tokens from hash table iHash and link them into a list
-** in sorted order. The hash table is cleared before returning. It is
-** the responsibility of the caller to free the elements of the returned
-** list.
+** Link all tokens from hash table iHash into a list in sorted order. The
+** tokens are not removed from the hash table.
*/
static int fts5HashEntrySort(
Fts5Hash *pHash,
@@ -234507,7 +238413,7 @@ static int fts5HashEntrySort(
Fts5HashEntry *pIter;
for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){
if( pTerm==0
- || (pIter->nKey+1>=nTerm && 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm))
+ || (pIter->nKey>=nTerm && 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm))
){
Fts5HashEntry *pEntry = pIter;
pEntry->pScanNext = 0;
@@ -234546,12 +238452,11 @@ static int sqlite3Fts5HashQuery(
for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){
zKey = fts5EntryKey(p);
- assert( p->nKey+1==(int)strlen(zKey) );
- if( nTerm==p->nKey+1 && memcmp(zKey, pTerm, nTerm)==0 ) break;
+ if( nTerm==p->nKey && memcmp(zKey, pTerm, nTerm)==0 ) break;
}
if( p ){
- int nHashPre = sizeof(Fts5HashEntry) + nTerm + 1;
+ int nHashPre = sizeof(Fts5HashEntry) + nTerm;
int nList = p->nData - nHashPre;
u8 *pRet = (u8*)(*ppOut = sqlite3_malloc64(nPre + nList + 10));
if( pRet ){
@@ -234612,19 +238517,22 @@ static int sqlite3Fts5HashScanEof(Fts5Hash *p){
static void sqlite3Fts5HashScanEntry(
Fts5Hash *pHash,
const char **pzTerm, /* OUT: term (nul-terminated) */
+ int *pnTerm, /* OUT: Size of term in bytes */
const u8 **ppDoclist, /* OUT: pointer to doclist */
int *pnDoclist /* OUT: size of doclist in bytes */
){
Fts5HashEntry *p;
if( (p = pHash->pScan) ){
char *zKey = fts5EntryKey(p);
- int nTerm = (int)strlen(zKey);
+ int nTerm = p->nKey;
fts5HashAddPoslistSize(pHash, p, 0);
*pzTerm = zKey;
- *ppDoclist = (const u8*)&zKey[nTerm+1];
- *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1);
+ *pnTerm = nTerm;
+ *ppDoclist = (const u8*)&zKey[nTerm];
+ *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm);
}else{
*pzTerm = 0;
+ *pnTerm = 0;
*ppDoclist = 0;
*pnDoclist = 0;
}
@@ -234955,6 +238863,9 @@ typedef struct Fts5SegWriter Fts5SegWriter;
typedef struct Fts5Structure Fts5Structure;
typedef struct Fts5StructureLevel Fts5StructureLevel;
typedef struct Fts5StructureSegment Fts5StructureSegment;
+typedef struct Fts5TokenDataIter Fts5TokenDataIter;
+typedef struct Fts5TokenDataMap Fts5TokenDataMap;
+typedef struct Fts5TombstoneArray Fts5TombstoneArray;
struct Fts5Data {
u8 *p; /* Pointer to buffer containing record */
@@ -234989,6 +238900,7 @@ struct Fts5Index {
/* Error state. */
int rc; /* Current error code */
+ int flushRc;
/* State used by the fts5DataXXX() functions. */
sqlite3_blob *pReader; /* RO incr-blob open on %_data table */
@@ -234997,6 +238909,7 @@ struct Fts5Index {
sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */
sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */
sqlite3_stmt *pIdxSelect;
+ sqlite3_stmt *pIdxNextSelect;
int nRead; /* Total number of blocks read */
sqlite3_stmt *pDeleteFromIdx;
@@ -235150,8 +239063,7 @@ struct Fts5SegIter {
Fts5Data *pLeaf; /* Current leaf data */
Fts5Data *pNextLeaf; /* Leaf page (iLeafPgno+1) */
i64 iLeafOffset; /* Byte offset within current leaf */
- Fts5Data **apTombstone; /* Array of tombstone pages */
- int nTombstone;
+ Fts5TombstoneArray *pTombArray; /* Array of tombstone pages */
/* Next method */
void (*xNext)(Fts5Index*, Fts5SegIter*, int*);
@@ -235179,6 +239091,15 @@ struct Fts5SegIter {
};
/*
+** Array of tombstone pages. Reference counted.
+*/
+struct Fts5TombstoneArray {
+ int nRef; /* Number of pointers to this object */
+ int nTombstone;
+ Fts5Data *apTombstone[1]; /* Array of tombstone pages */
+};
+
+/*
** Argument is a pointer to an Fts5Data structure that contains a
** leaf page.
*/
@@ -235222,9 +239143,16 @@ struct Fts5SegIter {
** poslist:
** Used by sqlite3Fts5IterPoslist() when the poslist needs to be buffered.
** There is no way to tell if this is populated or not.
+**
+** pColset:
+** If not NULL, points to an object containing a set of column indices.
+** Only matches that occur in one of these columns will be returned.
+** The Fts5Iter does not own the Fts5Colset object, and so it is not
+** freed when the iterator is closed - it is owned by the upper layer.
*/
struct Fts5Iter {
Fts5IndexIter base; /* Base class containing output vars */
+ Fts5TokenDataIter *pTokenDataIter;
Fts5Index *pIndex; /* Index that owns this iterator */
Fts5Buffer poslist; /* Buffer containing current poslist */
@@ -235242,7 +239170,6 @@ struct Fts5Iter {
Fts5SegIter aSeg[1]; /* Array of segment iterators */
};
-
/*
** An instance of the following type is used to iterate through the contents
** of a doclist-index record.
@@ -236160,9 +240087,9 @@ static int fts5DlidxLvlNext(Fts5DlidxLvl *pLvl){
}
if( iOff<pData->nn ){
- i64 iVal;
+ u64 iVal;
pLvl->iLeafPgno += (iOff - pLvl->iOff) + 1;
- iOff += fts5GetVarint(&pData->p[iOff], (u64*)&iVal);
+ iOff += fts5GetVarint(&pData->p[iOff], &iVal);
pLvl->iRowid += iVal;
pLvl->iOff = iOff;
}else{
@@ -236541,18 +240468,20 @@ static void fts5SegIterSetNext(Fts5Index *p, Fts5SegIter *pIter){
}
/*
-** Allocate a tombstone hash page array (pIter->apTombstone) for the
-** iterator passed as the second argument. If an OOM error occurs, leave
-** an error in the Fts5Index object.
+** Allocate a tombstone hash page array object (pIter->pTombArray) for
+** the iterator passed as the second argument. If an OOM error occurs,
+** leave an error in the Fts5Index object.
*/
static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){
const int nTomb = pIter->pSeg->nPgTombstone;
if( nTomb>0 ){
- Fts5Data **apTomb = 0;
- apTomb = (Fts5Data**)sqlite3Fts5MallocZero(&p->rc, sizeof(Fts5Data)*nTomb);
- if( apTomb ){
- pIter->apTombstone = apTomb;
- pIter->nTombstone = nTomb;
+ int nByte = nTomb * sizeof(Fts5Data*) + sizeof(Fts5TombstoneArray);
+ Fts5TombstoneArray *pNew;
+ pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte);
+ if( pNew ){
+ pNew->nTombstone = nTomb;
+ pNew->nRef = 1;
+ pIter->pTombArray = pNew;
}
}
}
@@ -236809,15 +240738,16 @@ static void fts5SegIterNext_None(
}else{
const u8 *pList = 0;
const char *zTerm = 0;
+ int nTerm = 0;
int nList;
sqlite3Fts5HashScanNext(p->pHash);
- sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList);
+ sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList);
if( pList==0 ) goto next_none_eof;
pIter->pLeaf->p = (u8*)pList;
pIter->pLeaf->nn = nList;
pIter->pLeaf->szLeaf = nList;
pIter->iEndofDoclist = nList;
- sqlite3Fts5BufferSet(&p->rc,&pIter->term, (int)strlen(zTerm), (u8*)zTerm);
+ sqlite3Fts5BufferSet(&p->rc,&pIter->term, nTerm, (u8*)zTerm);
pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid);
}
@@ -236883,11 +240813,12 @@ static void fts5SegIterNext(
}else if( pIter->pSeg==0 ){
const u8 *pList = 0;
const char *zTerm = 0;
+ int nTerm = 0;
int nList = 0;
assert( (pIter->flags & FTS5_SEGITER_ONETERM) || pbNewTerm );
if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){
sqlite3Fts5HashScanNext(p->pHash);
- sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList);
+ sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList);
}
if( pList==0 ){
fts5DataRelease(pIter->pLeaf);
@@ -236897,8 +240828,7 @@ static void fts5SegIterNext(
pIter->pLeaf->nn = nList;
pIter->pLeaf->szLeaf = nList;
pIter->iEndofDoclist = nList+1;
- sqlite3Fts5BufferSet(&p->rc, &pIter->term, (int)strlen(zTerm),
- (u8*)zTerm);
+ sqlite3Fts5BufferSet(&p->rc, &pIter->term, nTerm, (u8*)zTerm);
pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid);
*pbNewTerm = 1;
}
@@ -237284,7 +241214,7 @@ static void fts5SegIterSeekInit(
fts5LeafSeek(p, bGe, pIter, pTerm, nTerm);
}
- if( p->rc==SQLITE_OK && bGe==0 ){
+ if( p->rc==SQLITE_OK && (bGe==0 || (flags & FTS5INDEX_QUERY_SCANONETERM)) ){
pIter->flags |= FTS5_SEGITER_ONETERM;
if( pIter->pLeaf ){
if( flags & FTS5INDEX_QUERY_DESC ){
@@ -237300,7 +241230,9 @@ static void fts5SegIterSeekInit(
}
fts5SegIterSetNext(p, pIter);
- fts5SegIterAllocTombstone(p, pIter);
+ if( 0==(flags & FTS5INDEX_QUERY_SCANONETERM) ){
+ fts5SegIterAllocTombstone(p, pIter);
+ }
/* Either:
**
@@ -237317,6 +241249,79 @@ static void fts5SegIterSeekInit(
);
}
+
+/*
+** SQL used by fts5SegIterNextInit() to find the page to open.
+*/
+static sqlite3_stmt *fts5IdxNextStmt(Fts5Index *p){
+ if( p->pIdxNextSelect==0 ){
+ Fts5Config *pConfig = p->pConfig;
+ fts5IndexPrepareStmt(p, &p->pIdxNextSelect, sqlite3_mprintf(
+ "SELECT pgno FROM '%q'.'%q_idx' WHERE "
+ "segid=? AND term>? ORDER BY term ASC LIMIT 1",
+ pConfig->zDb, pConfig->zName
+ ));
+
+ }
+ return p->pIdxNextSelect;
+}
+
+/*
+** This is similar to fts5SegIterSeekInit(), except that it initializes
+** the segment iterator to point to the first term following the page
+** with pToken/nToken on it.
+*/
+static void fts5SegIterNextInit(
+ Fts5Index *p,
+ const char *pTerm, int nTerm,
+ Fts5StructureSegment *pSeg, /* Description of segment */
+ Fts5SegIter *pIter /* Object to populate */
+){
+ int iPg = -1; /* Page of segment to open */
+ int bDlidx = 0;
+ sqlite3_stmt *pSel = 0; /* SELECT to find iPg */
+
+ pSel = fts5IdxNextStmt(p);
+ if( pSel ){
+ assert( p->rc==SQLITE_OK );
+ sqlite3_bind_int(pSel, 1, pSeg->iSegid);
+ sqlite3_bind_blob(pSel, 2, pTerm, nTerm, SQLITE_STATIC);
+
+ if( sqlite3_step(pSel)==SQLITE_ROW ){
+ i64 val = sqlite3_column_int64(pSel, 0);
+ iPg = (int)(val>>1);
+ bDlidx = (val & 0x0001);
+ }
+ p->rc = sqlite3_reset(pSel);
+ sqlite3_bind_null(pSel, 2);
+ if( p->rc ) return;
+ }
+
+ memset(pIter, 0, sizeof(*pIter));
+ pIter->pSeg = pSeg;
+ pIter->flags |= FTS5_SEGITER_ONETERM;
+ if( iPg>=0 ){
+ pIter->iLeafPgno = iPg - 1;
+ fts5SegIterNextPage(p, pIter);
+ fts5SegIterSetNext(p, pIter);
+ }
+ if( pIter->pLeaf ){
+ const u8 *a = pIter->pLeaf->p;
+ int iTermOff = 0;
+
+ pIter->iPgidxOff = pIter->pLeaf->szLeaf;
+ pIter->iPgidxOff += fts5GetVarint32(&a[pIter->iPgidxOff], iTermOff);
+ pIter->iLeafOffset = iTermOff;
+ fts5SegIterLoadTerm(p, pIter, 0);
+ fts5SegIterLoadNPos(p, pIter);
+ if( bDlidx ) fts5SegIterLoadDlidx(p, pIter);
+
+ assert( p->rc!=SQLITE_OK ||
+ fts5BufferCompareBlob(&pIter->term, (const u8*)pTerm, nTerm)>0
+ );
+ }
+}
+
/*
** Initialize the object pIter to point to term pTerm/nTerm within the
** in-memory hash table. If there is no such term in the hash-table, the
@@ -237343,14 +241348,21 @@ static void fts5SegIterHashInit(
const u8 *pList = 0;
p->rc = sqlite3Fts5HashScanInit(p->pHash, (const char*)pTerm, nTerm);
- sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &pList, &nList);
- n = (z ? (int)strlen((const char*)z) : 0);
+ sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &n, &pList, &nList);
if( pList ){
pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data));
if( pLeaf ){
pLeaf->p = (u8*)pList;
}
}
+
+ /* The call to sqlite3Fts5HashScanInit() causes the hash table to
+ ** fill the size field of all existing position lists. This means they
+ ** can no longer be appended to. Since the only scenario in which they
+ ** can be appended to is if the previous operation on this table was
+ ** a DELETE, by clearing the Fts5Index.bDelete flag we can avoid this
+ ** possibility altogether. */
+ p->bDelete = 0;
}else{
p->rc = sqlite3Fts5HashQuery(p->pHash, sizeof(Fts5Data),
(const char*)pTerm, nTerm, (void**)&pLeaf, &nList
@@ -237396,13 +241408,30 @@ static void fts5IndexFreeArray(Fts5Data **ap, int n){
}
/*
+** Decrement the ref-count of the object passed as the only argument. If it
+** reaches 0, free it and its contents.
+*/
+static void fts5TombstoneArrayDelete(Fts5TombstoneArray *p){
+ if( p ){
+ p->nRef--;
+ if( p->nRef<=0 ){
+ int ii;
+ for(ii=0; ii<p->nTombstone; ii++){
+ fts5DataRelease(p->apTombstone[ii]);
+ }
+ sqlite3_free(p);
+ }
+ }
+}
+
+/*
** Zero the iterator passed as the only argument.
*/
static void fts5SegIterClear(Fts5SegIter *pIter){
fts5BufferFree(&pIter->term);
fts5DataRelease(pIter->pLeaf);
fts5DataRelease(pIter->pNextLeaf);
- fts5IndexFreeArray(pIter->apTombstone, pIter->nTombstone);
+ fts5TombstoneArrayDelete(pIter->pTombArray);
fts5DlidxIterFree(pIter->pDlidx);
sqlite3_free(pIter->aRowidOffset);
memset(pIter, 0, sizeof(Fts5SegIter));
@@ -237536,7 +241565,6 @@ static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){
assert_nc( i2!=0 );
pRes->bTermEq = 1;
if( p1->iRowid==p2->iRowid ){
- p1->bDel = p2->bDel;
return i2;
}
res = ((p1->iRowid > p2->iRowid)==pIter->bRev) ? -1 : +1;
@@ -237648,7 +241676,6 @@ static void fts5SegIterNextFrom(
}while( p->rc==SQLITE_OK );
}
-
/*
** Free the iterator object passed as the second argument.
*/
@@ -237793,24 +241820,25 @@ static int fts5IndexTombstoneQuery(
static int fts5MultiIterIsDeleted(Fts5Iter *pIter){
int iFirst = pIter->aFirst[1].iFirst;
Fts5SegIter *pSeg = &pIter->aSeg[iFirst];
+ Fts5TombstoneArray *pArray = pSeg->pTombArray;
- if( pSeg->pLeaf && pSeg->nTombstone ){
+ if( pSeg->pLeaf && pArray ){
/* Figure out which page the rowid might be present on. */
- int iPg = ((u64)pSeg->iRowid) % pSeg->nTombstone;
+ int iPg = ((u64)pSeg->iRowid) % pArray->nTombstone;
assert( iPg>=0 );
/* If tombstone hash page iPg has not yet been loaded from the
** database, load it now. */
- if( pSeg->apTombstone[iPg]==0 ){
- pSeg->apTombstone[iPg] = fts5DataRead(pIter->pIndex,
+ if( pArray->apTombstone[iPg]==0 ){
+ pArray->apTombstone[iPg] = fts5DataRead(pIter->pIndex,
FTS5_TOMBSTONE_ROWID(pSeg->pSeg->iSegid, iPg)
);
- if( pSeg->apTombstone[iPg]==0 ) return 0;
+ if( pArray->apTombstone[iPg]==0 ) return 0;
}
return fts5IndexTombstoneQuery(
- pSeg->apTombstone[iPg],
- pSeg->nTombstone,
+ pArray->apTombstone[iPg],
+ pArray->nTombstone,
pSeg->iRowid
);
}
@@ -237904,7 +241932,7 @@ static Fts5Iter *fts5MultiIterAlloc(
int nSeg
){
Fts5Iter *pNew;
- int nSlot; /* Power of two >= nSeg */
+ i64 nSlot; /* Power of two >= nSeg */
for(nSlot=2; nSlot<nSeg; nSlot=nSlot*2);
pNew = fts5IdxMalloc(p,
@@ -238349,6 +242377,32 @@ static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){
}
}
+/*
+** All the component segment-iterators of pIter have been set up. This
+** functions finishes setup for iterator pIter itself.
+*/
+static void fts5MultiIterFinishSetup(Fts5Index *p, Fts5Iter *pIter){
+ int iIter;
+ for(iIter=pIter->nSeg-1; iIter>0; iIter--){
+ int iEq;
+ if( (iEq = fts5MultiIterDoCompare(pIter, iIter)) ){
+ Fts5SegIter *pSeg = &pIter->aSeg[iEq];
+ if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0);
+ fts5MultiIterAdvanced(p, pIter, iEq, iIter);
+ }
+ }
+ fts5MultiIterSetEof(pIter);
+ fts5AssertMultiIterSetup(p, pIter);
+
+ if( (pIter->bSkipEmpty && fts5MultiIterIsEmpty(p, pIter))
+ || fts5MultiIterIsDeleted(pIter)
+ ){
+ fts5MultiIterNext(p, pIter, 0, 0);
+ }else if( pIter->base.bEof==0 ){
+ Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst];
+ pIter->xSetOutputs(pIter, pSeg);
+ }
+}
/*
** Allocate a new Fts5Iter object.
@@ -238430,31 +242484,12 @@ static void fts5MultiIterNew(
assert( iIter==nSeg );
}
- /* If the above was successful, each component iterators now points
+ /* If the above was successful, each component iterator now points
** to the first entry in its segment. In this case initialize the
** aFirst[] array. Or, if an error has occurred, free the iterator
** object and set the output variable to NULL. */
if( p->rc==SQLITE_OK ){
- for(iIter=pNew->nSeg-1; iIter>0; iIter--){
- int iEq;
- if( (iEq = fts5MultiIterDoCompare(pNew, iIter)) ){
- Fts5SegIter *pSeg = &pNew->aSeg[iEq];
- if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0);
- fts5MultiIterAdvanced(p, pNew, iEq, iIter);
- }
- }
- fts5MultiIterSetEof(pNew);
- fts5AssertMultiIterSetup(p, pNew);
-
- if( (pNew->bSkipEmpty && fts5MultiIterIsEmpty(p, pNew))
- || fts5MultiIterIsDeleted(pNew)
- ){
- fts5MultiIterNext(p, pNew, 0, 0);
- }else if( pNew->base.bEof==0 ){
- Fts5SegIter *pSeg = &pNew->aSeg[pNew->aFirst[1].iFirst];
- pNew->xSetOutputs(pNew, pSeg);
- }
-
+ fts5MultiIterFinishSetup(p, pNew);
}else{
fts5MultiIterFree(pNew);
*ppOut = 0;
@@ -238479,7 +242514,6 @@ static void fts5MultiIterNew2(
pNew = fts5MultiIterAlloc(p, 2);
if( pNew ){
Fts5SegIter *pIter = &pNew->aSeg[1];
-
pIter->flags = FTS5_SEGITER_ONETERM;
if( pData->szLeaf>0 ){
pIter->pLeaf = pData;
@@ -238627,6 +242661,7 @@ static void fts5IndexDiscardData(Fts5Index *p){
sqlite3Fts5HashClear(p->pHash);
p->nPendingData = 0;
p->nPendingRow = 0;
+ p->flushRc = SQLITE_OK;
}
p->nContentlessDelete = 0;
}
@@ -238842,7 +242877,7 @@ static void fts5WriteDlidxAppend(
}
if( pDlidx->bPrevValid ){
- iVal = iRowid - pDlidx->iPrev;
+ iVal = (u64)iRowid - (u64)pDlidx->iPrev;
}else{
i64 iPgno = (i==0 ? pWriter->writer.pgno : pDlidx[-1].pgno);
assert( pDlidx->buf.n==0 );
@@ -239029,7 +243064,7 @@ static void fts5WriteAppendPoslistData(
const u8 *a = aData;
int n = nData;
- assert( p->pConfig->pgsz>0 );
+ assert( p->pConfig->pgsz>0 || p->rc!=SQLITE_OK );
while( p->rc==SQLITE_OK
&& (pPage->buf.n + pPage->pgidx.n + n)>=p->pConfig->pgsz
){
@@ -239680,7 +243715,6 @@ static void fts5DoSecureDelete(
int iPgIdx = pSeg->pLeaf->szLeaf;
u64 iDelta = 0;
- u64 iNextDelta = 0;
int iNextOff = 0;
int iOff = 0;
int nIdx = 0;
@@ -239688,8 +243722,6 @@ static void fts5DoSecureDelete(
int bLastInDoclist = 0;
int iIdx = 0;
int iStart = 0;
- int iKeyOff = 0;
- int iPrevKeyOff = 0;
int iDelKeyOff = 0; /* Offset of deleted key, if any */
nIdx = nPg-iPgIdx;
@@ -239714,10 +243746,21 @@ static void fts5DoSecureDelete(
** This block sets the following variables:
**
** iStart:
+ ** The offset of the first byte of the rowid or delta-rowid
+ ** value for the doclist entry being removed.
+ **
** iDelta:
+ ** The value of the rowid or delta-rowid value for the doclist
+ ** entry being removed.
+ **
+ ** iNextOff:
+ ** The offset of the next entry following the position list
+ ** for the one being removed. If the position list for this
+ ** entry overflows onto the next leaf page, this value will be
+ ** greater than pLeaf->szLeaf.
*/
{
- int iSOP;
+ int iSOP; /* Start-Of-Position-list */
if( pSeg->iLeafPgno==pSeg->iTermLeafPgno ){
iStart = pSeg->iTermLeafOffset;
}else{
@@ -239753,47 +243796,81 @@ static void fts5DoSecureDelete(
}
iOff = iStart;
+
+ /* If the position-list for the entry being removed flows over past
+ ** the end of this page, delete the portion of the position-list on the
+ ** next page and beyond.
+ **
+ ** Set variable bLastInDoclist to true if this entry happens
+ ** to be the last rowid in the doclist for its term. */
if( iNextOff>=iPgIdx ){
int pgno = pSeg->iLeafPgno+1;
fts5SecureDeleteOverflow(p, pSeg->pSeg, pgno, &bLastInDoclist);
iNextOff = iPgIdx;
- }else{
- /* Set bLastInDoclist to true if the entry being removed is the last
- ** in its doclist. */
- for(iIdx=0, iKeyOff=0; iIdx<nIdx; /* no-op */){
- u32 iVal = 0;
- iIdx += fts5GetVarint32(&aIdx[iIdx], iVal);
- iKeyOff += iVal;
- if( iKeyOff==iNextOff ){
- bLastInDoclist = 1;
+ }
+
+ if( pSeg->bDel==0 ){
+ if( iNextOff!=iPgIdx ){
+ /* Loop through the page-footer. If iNextOff (offset of the
+ ** entry following the one we are removing) is equal to the
+ ** offset of a key on this page, then the entry is the last
+ ** in its doclist. */
+ int iKeyOff = 0;
+ for(iIdx=0; iIdx<nIdx; /* no-op */){
+ u32 iVal = 0;
+ iIdx += fts5GetVarint32(&aIdx[iIdx], iVal);
+ iKeyOff += iVal;
+ if( iKeyOff==iNextOff ){
+ bLastInDoclist = 1;
+ }
}
}
- }
- if( fts5GetU16(&aPg[0])==iStart && (bLastInDoclist||iNextOff==iPgIdx) ){
- fts5PutU16(&aPg[0], 0);
+ /* If this is (a) the first rowid on a page and (b) is not followed by
+ ** another position list on the same page, set the "first-rowid" field
+ ** of the header to 0. */
+ if( fts5GetU16(&aPg[0])==iStart && (bLastInDoclist || iNextOff==iPgIdx) ){
+ fts5PutU16(&aPg[0], 0);
+ }
}
- if( bLastInDoclist==0 ){
+ if( pSeg->bDel ){
+ iOff += sqlite3Fts5PutVarint(&aPg[iOff], iDelta);
+ aPg[iOff++] = 0x01;
+ }else if( bLastInDoclist==0 ){
if( iNextOff!=iPgIdx ){
+ u64 iNextDelta = 0;
iNextOff += fts5GetVarint(&aPg[iNextOff], &iNextDelta);
iOff += sqlite3Fts5PutVarint(&aPg[iOff], iDelta + iNextDelta);
}
}else if(
- iStart==pSeg->iTermLeafOffset && pSeg->iLeafPgno==pSeg->iTermLeafPgno
+ pSeg->iLeafPgno==pSeg->iTermLeafPgno
+ && iStart==pSeg->iTermLeafOffset
){
/* The entry being removed was the only position list in its
** doclist. Therefore the term needs to be removed as well. */
int iKey = 0;
- for(iIdx=0, iKeyOff=0; iIdx<nIdx; iKey++){
+ int iKeyOff = 0;
+
+ /* Set iKeyOff to the offset of the term that will be removed - the
+ ** last offset in the footer that is not greater than iStart. */
+ for(iIdx=0; iIdx<nIdx; iKey++){
u32 iVal = 0;
iIdx += fts5GetVarint32(&aIdx[iIdx], iVal);
if( (iKeyOff+iVal)>(u32)iStart ) break;
iKeyOff += iVal;
}
+ assert_nc( iKey>=1 );
+ /* Set iDelKeyOff to the value of the footer entry to remove from
+ ** the page. */
iDelKeyOff = iOff = iKeyOff;
+
if( iNextOff!=iPgIdx ){
+ /* This is the only position-list associated with the term, and there
+ ** is another term following it on this page. So the subsequent term
+ ** needs to be moved to replace the term associated with the entry
+ ** being removed. */
int nPrefix = 0;
int nSuffix = 0;
int nPrefix2 = 0;
@@ -239872,6 +243949,15 @@ static void fts5DoSecureDelete(
}
}
+ /* Assuming no error has occurred, this block does final edits to the
+ ** leaf page before writing it back to disk. Input variables are:
+ **
+ ** nPg: Total initial size of leaf page.
+ ** iPgIdx: Initial offset of page footer.
+ **
+ ** iOff: Offset to move data to
+ ** iNextOff: Offset to move data from
+ */
if( p->rc==SQLITE_OK ){
const int nMove = nPg - iNextOff; /* Number of bytes to move */
int nShift = iNextOff - iOff; /* Distance to move them */
@@ -239914,10 +244000,10 @@ static void fts5FlushSecureDelete(
Fts5Index *p,
Fts5Structure *pStruct,
const char *zTerm,
+ int nTerm,
i64 iRowid
){
const int f = FTS5INDEX_QUERY_SKIPHASH;
- int nTerm = (int)strlen(zTerm);
Fts5Iter *pIter = 0; /* Used to find term instance */
fts5MultiIterNew(p, pStruct, f, 0, (const u8*)zTerm, nTerm, -1, 0, &pIter);
@@ -239991,8 +244077,7 @@ static void fts5FlushOneHash(Fts5Index *p){
int nDoclist; /* Size of doclist in bytes */
/* Get the term and doclist for this entry. */
- sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist);
- nTerm = (int)strlen(zTerm);
+ sqlite3Fts5HashScanEntry(pHash, &zTerm, &nTerm, &pDoclist, &nDoclist);
if( bSecureDelete==0 ){
fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm);
if( p->rc!=SQLITE_OK ) break;
@@ -240022,7 +244107,7 @@ static void fts5FlushOneHash(Fts5Index *p){
if( bSecureDelete ){
if( eDetail==FTS5_DETAIL_NONE ){
if( iOff<nDoclist && pDoclist[iOff]==0x00 ){
- fts5FlushSecureDelete(p, pStruct, zTerm, iRowid);
+ fts5FlushSecureDelete(p, pStruct, zTerm, nTerm, iRowid);
iOff++;
if( iOff<nDoclist && pDoclist[iOff]==0x00 ){
iOff++;
@@ -240032,7 +244117,7 @@ static void fts5FlushOneHash(Fts5Index *p){
}
}
}else if( (pDoclist[iOff] & 0x01) ){
- fts5FlushSecureDelete(p, pStruct, zTerm, iRowid);
+ fts5FlushSecureDelete(p, pStruct, zTerm, nTerm, iRowid);
if( p->rc!=SQLITE_OK || pDoclist[iOff]==0x01 ){
iOff++;
continue;
@@ -240072,10 +244157,16 @@ static void fts5FlushOneHash(Fts5Index *p){
fts5WriteFlushLeaf(p, &writer);
}
}else{
- int bDummy;
- int nPos;
- int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDummy);
- nCopy += nPos;
+ int bDel = 0;
+ int nPos = 0;
+ int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDel);
+ if( bDel && bSecureDelete ){
+ fts5BufferAppendVarint(&p->rc, pBuf, nPos*2);
+ iOff += nCopy;
+ nCopy = nPos;
+ }else{
+ nCopy += nPos;
+ }
if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){
/* The entire poslist will fit on the current leaf. So copy
** it in one go. */
@@ -240113,7 +244204,6 @@ static void fts5FlushOneHash(Fts5Index *p){
assert( pBuf->n<=pBuf->nSpace );
if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash);
}
- sqlite3Fts5HashClear(pHash);
fts5WriteFinish(p, &writer, &pgnoLast);
assert( p->rc!=SQLITE_OK || bSecureDelete || pgnoLast>0 );
@@ -240146,7 +244236,6 @@ static void fts5FlushOneHash(Fts5Index *p){
fts5IndexCrisismerge(p, &pStruct);
fts5StructureWrite(p, pStruct);
fts5StructureRelease(pStruct);
- p->nContentlessDelete = 0;
}
/*
@@ -240154,11 +244243,21 @@ static void fts5FlushOneHash(Fts5Index *p){
*/
static void fts5IndexFlush(Fts5Index *p){
/* Unless it is empty, flush the hash table to disk */
+ if( p->flushRc ){
+ p->rc = p->flushRc;
+ return;
+ }
if( p->nPendingData || p->nContentlessDelete ){
assert( p->pHash );
fts5FlushOneHash(p);
- p->nPendingData = 0;
- p->nPendingRow = 0;
+ if( p->rc==SQLITE_OK ){
+ sqlite3Fts5HashClear(p->pHash);
+ p->nPendingData = 0;
+ p->nPendingRow = 0;
+ p->nContentlessDelete = 0;
+ }else if( p->nPendingData || p->nContentlessDelete ){
+ p->flushRc = p->rc;
+ }
}
}
@@ -240236,8 +244335,9 @@ static int sqlite3Fts5IndexOptimize(Fts5Index *p){
assert( p->rc==SQLITE_OK );
fts5IndexFlush(p);
- assert( p->nContentlessDelete==0 );
+ assert( p->rc!=SQLITE_OK || p->nContentlessDelete==0 );
pStruct = fts5StructureRead(p);
+ assert( p->rc!=SQLITE_OK || pStruct!=0 );
fts5StructureInvalidate(p);
if( pStruct ){
@@ -240643,7 +244743,7 @@ static void fts5SetupPrefixIter(
u8 *pToken, /* Buffer containing prefix to match */
int nToken, /* Size of buffer pToken in bytes */
Fts5Colset *pColset, /* Restrict matches to these columns */
- Fts5Iter **ppIter /* OUT: New iterator */
+ Fts5Iter **ppIter /* OUT: New iterator */
){
Fts5Structure *pStruct;
Fts5Buffer *aBuf;
@@ -240664,8 +244764,9 @@ static void fts5SetupPrefixIter(
aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf);
pStruct = fts5StructureRead(p);
+ assert( p->rc!=SQLITE_OK || (aBuf && pStruct) );
- if( aBuf && pStruct ){
+ if( p->rc==SQLITE_OK ){
const int flags = FTS5INDEX_QUERY_SCAN
| FTS5INDEX_QUERY_SKIPEMPTY
| FTS5INDEX_QUERY_NOOUTPUT;
@@ -240677,6 +244778,12 @@ static void fts5SetupPrefixIter(
int bNewTerm = 1;
memset(&doclist, 0, sizeof(doclist));
+
+ /* If iIdx is non-zero, then it is the number of a prefix-index for
+ ** prefixes 1 character longer than the prefix being queried for. That
+ ** index contains all the doclists required, except for the one
+ ** corresponding to the prefix itself. That one is extracted from the
+ ** main term index here. */
if( iIdx!=0 ){
int dummy = 0;
const int f2 = FTS5INDEX_QUERY_SKIPEMPTY|FTS5INDEX_QUERY_NOOUTPUT;
@@ -240700,6 +244807,7 @@ static void fts5SetupPrefixIter(
pToken[0] = FTS5_MAIN_PREFIX + iIdx;
fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1);
fts5IterSetOutputCb(&p->rc, p1);
+
for( /* no-op */ ;
fts5MultiIterEof(p, p1)==0;
fts5MultiIterNext2(p, p1, &bNewTerm)
@@ -240715,7 +244823,6 @@ static void fts5SetupPrefixIter(
}
if( p1->base.nData==0 ) continue;
-
if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){
for(i=0; p->rc==SQLITE_OK && doclist.n; i++){
int i1 = i*nMerge;
@@ -240754,7 +244861,7 @@ static void fts5SetupPrefixIter(
}
fts5MultiIterFree(p1);
- pData = fts5IdxMalloc(p, sizeof(Fts5Data)+doclist.n+FTS5_DATA_ZERO_PADDING);
+ pData = fts5IdxMalloc(p, sizeof(*pData)+doclist.n+FTS5_DATA_ZERO_PADDING);
if( pData ){
pData->p = (u8*)&pData[1];
pData->nn = pData->szLeaf = doclist.n;
@@ -240897,6 +245004,7 @@ static int sqlite3Fts5IndexClose(Fts5Index *p){
sqlite3_finalize(p->pIdxWriter);
sqlite3_finalize(p->pIdxDeleter);
sqlite3_finalize(p->pIdxSelect);
+ sqlite3_finalize(p->pIdxNextSelect);
sqlite3_finalize(p->pDataVersion);
sqlite3_finalize(p->pDeleteFromIdx);
sqlite3Fts5HashFree(p->pHash);
@@ -240993,6 +245101,454 @@ static int sqlite3Fts5IndexWrite(
}
/*
+** pToken points to a buffer of size nToken bytes containing a search
+** term, including the index number at the start, used on a tokendata=1
+** table. This function returns true if the term in buffer pBuf matches
+** token pToken/nToken.
+*/
+static int fts5IsTokendataPrefix(
+ Fts5Buffer *pBuf,
+ const u8 *pToken,
+ int nToken
+){
+ return (
+ pBuf->n>=nToken
+ && 0==memcmp(pBuf->p, pToken, nToken)
+ && (pBuf->n==nToken || pBuf->p[nToken]==0x00)
+ );
+}
+
+/*
+** Ensure the segment-iterator passed as the only argument points to EOF.
+*/
+static void fts5SegIterSetEOF(Fts5SegIter *pSeg){
+ fts5DataRelease(pSeg->pLeaf);
+ pSeg->pLeaf = 0;
+}
+
+/*
+** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an
+** array of these for each row it visits. Or, for an iterator used by an
+** "ORDER BY rank" query, it accumulates an array of these for the entire
+** query.
+**
+** Each instance in the array indicates the iterator (and therefore term)
+** associated with position iPos of rowid iRowid. This is used by the
+** xInstToken() API.
+*/
+struct Fts5TokenDataMap {
+ i64 iRowid; /* Row this token is located in */
+ i64 iPos; /* Position of token */
+ int iIter; /* Iterator token was read from */
+};
+
+/*
+** An object used to supplement Fts5Iter for tokendata=1 iterators.
+*/
+struct Fts5TokenDataIter {
+ int nIter;
+ int nIterAlloc;
+
+ int nMap;
+ int nMapAlloc;
+ Fts5TokenDataMap *aMap;
+
+ Fts5PoslistReader *aPoslistReader;
+ int *aPoslistToIter;
+ Fts5Iter *apIter[1];
+};
+
+/*
+** This function appends iterator pAppend to Fts5TokenDataIter pIn and
+** returns the result.
+*/
+static Fts5TokenDataIter *fts5AppendTokendataIter(
+ Fts5Index *p, /* Index object (for error code) */
+ Fts5TokenDataIter *pIn, /* Current Fts5TokenDataIter struct */
+ Fts5Iter *pAppend /* Append this iterator */
+){
+ Fts5TokenDataIter *pRet = pIn;
+
+ if( p->rc==SQLITE_OK ){
+ if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){
+ int nAlloc = pIn ? pIn->nIterAlloc*2 : 16;
+ int nByte = nAlloc * sizeof(Fts5Iter*) + sizeof(Fts5TokenDataIter);
+ Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte);
+
+ if( pNew==0 ){
+ p->rc = SQLITE_NOMEM;
+ }else{
+ if( pIn==0 ) memset(pNew, 0, nByte);
+ pRet = pNew;
+ pNew->nIterAlloc = nAlloc;
+ }
+ }
+ }
+ if( p->rc ){
+ sqlite3Fts5IterClose((Fts5IndexIter*)pAppend);
+ }else{
+ pRet->apIter[pRet->nIter++] = pAppend;
+ }
+ assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc );
+
+ return pRet;
+}
+
+/*
+** Delete an Fts5TokenDataIter structure and its contents.
+*/
+static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){
+ if( pSet ){
+ int ii;
+ for(ii=0; ii<pSet->nIter; ii++){
+ fts5MultiIterFree(pSet->apIter[ii]);
+ }
+ sqlite3_free(pSet->aPoslistReader);
+ sqlite3_free(pSet->aMap);
+ sqlite3_free(pSet);
+ }
+}
+
+/*
+** Append a mapping to the token-map belonging to object pT.
+*/
+static void fts5TokendataIterAppendMap(
+ Fts5Index *p,
+ Fts5TokenDataIter *pT,
+ int iIter,
+ i64 iRowid,
+ i64 iPos
+){
+ if( p->rc==SQLITE_OK ){
+ if( pT->nMap==pT->nMapAlloc ){
+ int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
+ int nByte = nNew * sizeof(Fts5TokenDataMap);
+ Fts5TokenDataMap *aNew;
+
+ aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nByte);
+ if( aNew==0 ){
+ p->rc = SQLITE_NOMEM;
+ return;
+ }
+
+ pT->aMap = aNew;
+ pT->nMapAlloc = nNew;
+ }
+
+ pT->aMap[pT->nMap].iRowid = iRowid;
+ pT->aMap[pT->nMap].iPos = iPos;
+ pT->aMap[pT->nMap].iIter = iIter;
+ pT->nMap++;
+ }
+}
+
+/*
+** The iterator passed as the only argument must be a tokendata=1 iterator
+** (pIter->pTokenDataIter!=0). This function sets the iterator output
+** variables (pIter->base.*) according to the contents of the current
+** row.
+*/
+static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){
+ int ii;
+ int nHit = 0;
+ i64 iRowid = SMALLEST_INT64;
+ int iMin = 0;
+
+ Fts5TokenDataIter *pT = pIter->pTokenDataIter;
+
+ pIter->base.nData = 0;
+ pIter->base.pData = 0;
+
+ for(ii=0; ii<pT->nIter; ii++){
+ Fts5Iter *p = pT->apIter[ii];
+ if( p->base.bEof==0 ){
+ if( nHit==0 || p->base.iRowid<iRowid ){
+ iRowid = p->base.iRowid;
+ nHit = 1;
+ pIter->base.pData = p->base.pData;
+ pIter->base.nData = p->base.nData;
+ iMin = ii;
+ }else if( p->base.iRowid==iRowid ){
+ nHit++;
+ }
+ }
+ }
+
+ if( nHit==0 ){
+ pIter->base.bEof = 1;
+ }else{
+ int eDetail = pIter->pIndex->pConfig->eDetail;
+ pIter->base.bEof = 0;
+ pIter->base.iRowid = iRowid;
+
+ if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){
+ fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, iRowid, -1);
+ }else
+ if( nHit>1 && eDetail!=FTS5_DETAIL_NONE ){
+ int nReader = 0;
+ int nByte = 0;
+ i64 iPrev = 0;
+
+ /* Allocate array of iterators if they are not already allocated. */
+ if( pT->aPoslistReader==0 ){
+ pT->aPoslistReader = (Fts5PoslistReader*)sqlite3Fts5MallocZero(
+ &pIter->pIndex->rc,
+ pT->nIter * (sizeof(Fts5PoslistReader) + sizeof(int))
+ );
+ if( pT->aPoslistReader==0 ) return;
+ pT->aPoslistToIter = (int*)&pT->aPoslistReader[pT->nIter];
+ }
+
+ /* Populate an iterator for each poslist that will be merged */
+ for(ii=0; ii<pT->nIter; ii++){
+ Fts5Iter *p = pT->apIter[ii];
+ if( iRowid==p->base.iRowid ){
+ pT->aPoslistToIter[nReader] = ii;
+ sqlite3Fts5PoslistReaderInit(
+ p->base.pData, p->base.nData, &pT->aPoslistReader[nReader++]
+ );
+ nByte += p->base.nData;
+ }
+ }
+
+ /* Ensure the output buffer is large enough */
+ if( fts5BufferGrow(&pIter->pIndex->rc, &pIter->poslist, nByte+nHit*10) ){
+ return;
+ }
+
+ /* Ensure the token-mapping is large enough */
+ if( eDetail==FTS5_DETAIL_FULL && pT->nMapAlloc<(pT->nMap + nByte) ){
+ int nNew = (pT->nMapAlloc + nByte) * 2;
+ Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc(
+ pT->aMap, nNew*sizeof(Fts5TokenDataMap)
+ );
+ if( aNew==0 ){
+ pIter->pIndex->rc = SQLITE_NOMEM;
+ return;
+ }
+ pT->aMap = aNew;
+ pT->nMapAlloc = nNew;
+ }
+
+ pIter->poslist.n = 0;
+
+ while( 1 ){
+ i64 iMinPos = LARGEST_INT64;
+
+ /* Find smallest position */
+ iMin = 0;
+ for(ii=0; ii<nReader; ii++){
+ Fts5PoslistReader *pReader = &pT->aPoslistReader[ii];
+ if( pReader->bEof==0 ){
+ if( pReader->iPos<iMinPos ){
+ iMinPos = pReader->iPos;
+ iMin = ii;
+ }
+ }
+ }
+
+ /* If all readers were at EOF, break out of the loop. */
+ if( iMinPos==LARGEST_INT64 ) break;
+
+ sqlite3Fts5PoslistSafeAppend(&pIter->poslist, &iPrev, iMinPos);
+ sqlite3Fts5PoslistReaderNext(&pT->aPoslistReader[iMin]);
+
+ if( eDetail==FTS5_DETAIL_FULL ){
+ pT->aMap[pT->nMap].iPos = iMinPos;
+ pT->aMap[pT->nMap].iIter = pT->aPoslistToIter[iMin];
+ pT->aMap[pT->nMap].iRowid = iRowid;
+ pT->nMap++;
+ }
+ }
+
+ pIter->base.pData = pIter->poslist.p;
+ pIter->base.nData = pIter->poslist.n;
+ }
+ }
+}
+
+/*
+** The iterator passed as the only argument must be a tokendata=1 iterator
+** (pIter->pTokenDataIter!=0). This function advances the iterator. If
+** argument bFrom is false, then the iterator is advanced to the next
+** entry. Or, if bFrom is true, it is advanced to the first entry with
+** a rowid of iFrom or greater.
+*/
+static void fts5TokendataIterNext(Fts5Iter *pIter, int bFrom, i64 iFrom){
+ int ii;
+ Fts5TokenDataIter *pT = pIter->pTokenDataIter;
+
+ for(ii=0; ii<pT->nIter; ii++){
+ Fts5Iter *p = pT->apIter[ii];
+ if( p->base.bEof==0
+ && (p->base.iRowid==pIter->base.iRowid || (bFrom && p->base.iRowid<iFrom))
+ ){
+ fts5MultiIterNext(p->pIndex, p, bFrom, iFrom);
+ while( bFrom && p->base.bEof==0
+ && p->base.iRowid<iFrom
+ && p->pIndex->rc==SQLITE_OK
+ ){
+ fts5MultiIterNext(p->pIndex, p, 0, 0);
+ }
+ }
+ }
+
+ fts5IterSetOutputsTokendata(pIter);
+}
+
+/*
+** If the segment-iterator passed as the first argument is at EOF, then
+** set pIter->term to a copy of buffer pTerm.
+*/
+static void fts5TokendataSetTermIfEof(Fts5Iter *pIter, Fts5Buffer *pTerm){
+ if( pIter && pIter->aSeg[0].pLeaf==0 ){
+ fts5BufferSet(&pIter->pIndex->rc, &pIter->aSeg[0].term, pTerm->n, pTerm->p);
+ }
+}
+
+/*
+** This function sets up an iterator to use for a non-prefix query on a
+** tokendata=1 table.
+*/
+static Fts5Iter *fts5SetupTokendataIter(
+ Fts5Index *p, /* FTS index to query */
+ const u8 *pToken, /* Buffer containing query term */
+ int nToken, /* Size of buffer pToken in bytes */
+ Fts5Colset *pColset /* Colset to filter on */
+){
+ Fts5Iter *pRet = 0;
+ Fts5TokenDataIter *pSet = 0;
+ Fts5Structure *pStruct = 0;
+ const int flags = FTS5INDEX_QUERY_SCANONETERM | FTS5INDEX_QUERY_SCAN;
+
+ Fts5Buffer bSeek = {0, 0, 0};
+ Fts5Buffer *pSmall = 0;
+
+ fts5IndexFlush(p);
+ pStruct = fts5StructureRead(p);
+
+ while( p->rc==SQLITE_OK ){
+ Fts5Iter *pPrev = pSet ? pSet->apIter[pSet->nIter-1] : 0;
+ Fts5Iter *pNew = 0;
+ Fts5SegIter *pNewIter = 0;
+ Fts5SegIter *pPrevIter = 0;
+
+ int iLvl, iSeg, ii;
+
+ pNew = fts5MultiIterAlloc(p, pStruct->nSegment);
+ if( pSmall ){
+ fts5BufferSet(&p->rc, &bSeek, pSmall->n, pSmall->p);
+ fts5BufferAppendBlob(&p->rc, &bSeek, 1, (const u8*)"\0");
+ }else{
+ fts5BufferSet(&p->rc, &bSeek, nToken, pToken);
+ }
+ if( p->rc ){
+ sqlite3Fts5IterClose((Fts5IndexIter*)pNew);
+ break;
+ }
+
+ pNewIter = &pNew->aSeg[0];
+ pPrevIter = (pPrev ? &pPrev->aSeg[0] : 0);
+ for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
+ for(iSeg=pStruct->aLevel[iLvl].nSeg-1; iSeg>=0; iSeg--){
+ Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg];
+ int bDone = 0;
+
+ if( pPrevIter ){
+ if( fts5BufferCompare(pSmall, &pPrevIter->term) ){
+ memcpy(pNewIter, pPrevIter, sizeof(Fts5SegIter));
+ memset(pPrevIter, 0, sizeof(Fts5SegIter));
+ bDone = 1;
+ }else if( pPrevIter->iEndofDoclist>pPrevIter->pLeaf->szLeaf ){
+ fts5SegIterNextInit(p,(const char*)bSeek.p,bSeek.n-1,pSeg,pNewIter);
+ bDone = 1;
+ }
+ }
+
+ if( bDone==0 ){
+ fts5SegIterSeekInit(p, bSeek.p, bSeek.n, flags, pSeg, pNewIter);
+ }
+
+ if( pPrevIter ){
+ if( pPrevIter->pTombArray ){
+ pNewIter->pTombArray = pPrevIter->pTombArray;
+ pNewIter->pTombArray->nRef++;
+ }
+ }else{
+ fts5SegIterAllocTombstone(p, pNewIter);
+ }
+
+ pNewIter++;
+ if( pPrevIter ) pPrevIter++;
+ if( p->rc ) break;
+ }
+ }
+ fts5TokendataSetTermIfEof(pPrev, pSmall);
+
+ pNew->bSkipEmpty = 1;
+ pNew->pColset = pColset;
+ fts5IterSetOutputCb(&p->rc, pNew);
+
+ /* Loop through all segments in the new iterator. Find the smallest
+ ** term that any segment-iterator points to. Iterator pNew will be
+ ** used for this term. Also, set any iterator that points to a term that
+ ** does not match pToken/nToken to point to EOF */
+ pSmall = 0;
+ for(ii=0; ii<pNew->nSeg; ii++){
+ Fts5SegIter *pII = &pNew->aSeg[ii];
+ if( 0==fts5IsTokendataPrefix(&pII->term, pToken, nToken) ){
+ fts5SegIterSetEOF(pII);
+ }
+ if( pII->pLeaf && (!pSmall || fts5BufferCompare(pSmall, &pII->term)>0) ){
+ pSmall = &pII->term;
+ }
+ }
+
+ /* If pSmall is still NULL at this point, then the new iterator does
+ ** not point to any terms that match the query. So delete it and break
+ ** out of the loop - all required iterators have been collected. */
+ if( pSmall==0 ){
+ sqlite3Fts5IterClose((Fts5IndexIter*)pNew);
+ break;
+ }
+
+ /* Append this iterator to the set and continue. */
+ pSet = fts5AppendTokendataIter(p, pSet, pNew);
+ }
+
+ if( p->rc==SQLITE_OK && pSet ){
+ int ii;
+ for(ii=0; ii<pSet->nIter; ii++){
+ Fts5Iter *pIter = pSet->apIter[ii];
+ int iSeg;
+ for(iSeg=0; iSeg<pIter->nSeg; iSeg++){
+ pIter->aSeg[iSeg].flags |= FTS5_SEGITER_ONETERM;
+ }
+ fts5MultiIterFinishSetup(p, pIter);
+ }
+ }
+
+ if( p->rc==SQLITE_OK ){
+ pRet = fts5MultiIterAlloc(p, 0);
+ }
+ if( pRet ){
+ pRet->pTokenDataIter = pSet;
+ if( pSet ){
+ fts5IterSetOutputsTokendata(pRet);
+ }else{
+ pRet->base.bEof = 1;
+ }
+ }else{
+ fts5TokendataIterDelete(pSet);
+ }
+
+ fts5StructureRelease(pStruct);
+ fts5BufferFree(&bSeek);
+ return pRet;
+}
+
+
+/*
** Open a new iterator to iterate though all rowid that match the
** specified token or token prefix.
*/
@@ -241013,8 +245569,13 @@ static int sqlite3Fts5IndexQuery(
if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){
int iIdx = 0; /* Index to search */
int iPrefixIdx = 0; /* +1 prefix index */
+ int bTokendata = pConfig->bTokendata;
if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken);
+ if( flags & (FTS5INDEX_QUERY_NOTOKENDATA|FTS5INDEX_QUERY_SCAN) ){
+ bTokendata = 0;
+ }
+
/* Figure out which index to search and set iIdx accordingly. If this
** is a prefix query for which there is no prefix index, set iIdx to
** greater than pConfig->nPrefix to indicate that the query will be
@@ -241040,7 +245601,10 @@ static int sqlite3Fts5IndexQuery(
}
}
- if( iIdx<=pConfig->nPrefix ){
+ if( bTokendata && iIdx==0 ){
+ buf.p[0] = '0';
+ pRet = fts5SetupTokendataIter(p, buf.p, nToken+1, pColset);
+ }else if( iIdx<=pConfig->nPrefix ){
/* Straight index lookup */
Fts5Structure *pStruct = fts5StructureRead(p);
buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx);
@@ -241087,7 +245651,11 @@ static int sqlite3Fts5IndexQuery(
static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
assert( pIter->pIndex->rc==SQLITE_OK );
- fts5MultiIterNext(pIter->pIndex, pIter, 0, 0);
+ if( pIter->pTokenDataIter ){
+ fts5TokendataIterNext(pIter, 0, 0);
+ }else{
+ fts5MultiIterNext(pIter->pIndex, pIter, 0, 0);
+ }
return fts5IndexReturn(pIter->pIndex);
}
@@ -241120,7 +245688,11 @@ static int sqlite3Fts5IterNextScan(Fts5IndexIter *pIndexIter){
*/
static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
- fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch);
+ if( pIter->pTokenDataIter ){
+ fts5TokendataIterNext(pIter, 1, iMatch);
+ }else{
+ fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch);
+ }
return fts5IndexReturn(pIter->pIndex);
}
@@ -241136,12 +245708,106 @@ static const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){
}
/*
+** This is used by xInstToken() to access the token at offset iOff, column
+** iCol of row iRowid. The token is returned via output variables *ppOut
+** and *pnOut. The iterator passed as the first argument must be a tokendata=1
+** iterator (pIter->pTokenDataIter!=0).
+*/
+static int sqlite3Fts5IterToken(
+ Fts5IndexIter *pIndexIter,
+ i64 iRowid,
+ int iCol,
+ int iOff,
+ const char **ppOut, int *pnOut
+){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
+ Fts5TokenDataIter *pT = pIter->pTokenDataIter;
+ Fts5TokenDataMap *aMap = pT->aMap;
+ i64 iPos = (((i64)iCol)<<32) + iOff;
+
+ int i1 = 0;
+ int i2 = pT->nMap;
+ int iTest = 0;
+
+ while( i2>i1 ){
+ iTest = (i1 + i2) / 2;
+
+ if( aMap[iTest].iRowid<iRowid ){
+ i1 = iTest+1;
+ }else if( aMap[iTest].iRowid>iRowid ){
+ i2 = iTest;
+ }else{
+ if( aMap[iTest].iPos<iPos ){
+ if( aMap[iTest].iPos<0 ){
+ break;
+ }
+ i1 = iTest+1;
+ }else if( aMap[iTest].iPos>iPos ){
+ i2 = iTest;
+ }else{
+ break;
+ }
+ }
+ }
+
+ if( i2>i1 ){
+ Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter];
+ *ppOut = (const char*)pMap->aSeg[0].term.p+1;
+ *pnOut = pMap->aSeg[0].term.n-1;
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** Clear any existing entries from the token-map associated with the
+** iterator passed as the only argument.
+*/
+static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
+ if( pIter && pIter->pTokenDataIter ){
+ pIter->pTokenDataIter->nMap = 0;
+ }
+}
+
+/*
+** Set a token-mapping for the iterator passed as the first argument. This
+** is used in detail=column or detail=none mode when a token is requested
+** using the xInstToken() API. In this case the caller tokenizers the
+** current row and configures the token-mapping via multiple calls to this
+** function.
+*/
+static int sqlite3Fts5IndexIterWriteTokendata(
+ Fts5IndexIter *pIndexIter,
+ const char *pToken, int nToken,
+ i64 iRowid, int iCol, int iOff
+){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
+ Fts5TokenDataIter *pT = pIter->pTokenDataIter;
+ Fts5Index *p = pIter->pIndex;
+ int ii;
+
+ assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL );
+ assert( pIter->pTokenDataIter );
+
+ for(ii=0; ii<pT->nIter; ii++){
+ Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term;
+ if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break;
+ }
+ if( ii<pT->nIter ){
+ fts5TokendataIterAppendMap(p, pT, ii, iRowid, (((i64)iCol)<<32) + iOff);
+ }
+ return fts5IndexReturn(p);
+}
+
+/*
** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery().
*/
static void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){
if( pIndexIter ){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
Fts5Index *pIndex = pIter->pIndex;
+ fts5TokendataIterDelete(pIter->pTokenDataIter);
fts5MultiIterFree(pIter);
sqlite3Fts5IndexCloseReader(pIndex);
}
@@ -241649,7 +246315,9 @@ static int fts5QueryCksum(
int eDetail = p->pConfig->eDetail;
u64 cksum = *pCksum;
Fts5IndexIter *pIter = 0;
- int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIter);
+ int rc = sqlite3Fts5IndexQuery(
+ p, z, n, (flags | FTS5INDEX_QUERY_NOTOKENDATA), 0, &pIter
+ );
while( rc==SQLITE_OK && ALWAYS(pIter!=0) && 0==sqlite3Fts5IterEof(pIter) ){
i64 rowid = pIter->iRowid;
@@ -241816,7 +246484,7 @@ static void fts5IndexIntegrityCheckEmpty(
}
static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
- int iTermOff = 0;
+ i64 iTermOff = 0;
int ii;
Fts5Buffer buf1 = {0,0,0};
@@ -241825,7 +246493,7 @@ static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
ii = pLeaf->szLeaf;
while( ii<pLeaf->nn && p->rc==SQLITE_OK ){
int res;
- int iOff;
+ i64 iOff;
int nIncr;
ii += fts5GetVarint32(&pLeaf->p[ii], nIncr);
@@ -242348,6 +247016,24 @@ static void fts5DecodeRowidList(
#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
+static void fts5BufferAppendTerm(int *pRc, Fts5Buffer *pBuf, Fts5Buffer *pTerm){
+ int ii;
+ fts5BufferGrow(pRc, pBuf, pTerm->n*2 + 1);
+ if( *pRc==SQLITE_OK ){
+ for(ii=0; ii<pTerm->n; ii++){
+ if( pTerm->p[ii]==0x00 ){
+ pBuf->p[pBuf->n++] = '\\';
+ pBuf->p[pBuf->n++] = '0';
+ }else{
+ pBuf->p[pBuf->n++] = pTerm->p[ii];
+ }
+ }
+ pBuf->p[pBuf->n] = 0x00;
+ }
+}
+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
+
+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
/*
** The implementation of user-defined scalar function fts5_decode().
*/
@@ -242454,9 +247140,8 @@ static void fts5DecodeFunction(
iOff += fts5GetVarint32(&a[iOff], nAppend);
term.n = nKeep;
fts5BufferAppendBlob(&rc, &term, nAppend, &a[iOff]);
- sqlite3Fts5BufferAppendPrintf(
- &rc, &s, " term=%.*s", term.n, (const char*)term.p
- );
+ sqlite3Fts5BufferAppendPrintf(&rc, &s, " term=");
+ fts5BufferAppendTerm(&rc, &s, &term);
iOff += nAppend;
/* Figure out where the doclist for this term ends */
@@ -242564,9 +247249,8 @@ static void fts5DecodeFunction(
fts5BufferAppendBlob(&rc, &term, nByte, &a[iOff]);
iOff += nByte;
- sqlite3Fts5BufferAppendPrintf(
- &rc, &s, " term=%.*s", term.n, (const char*)term.p
- );
+ sqlite3Fts5BufferAppendPrintf(&rc, &s, " term=");
+ fts5BufferAppendTerm(&rc, &s, &term);
iOff += fts5DecodeDoclist(&rc, &s, &a[iOff], iEnd-iOff);
}
@@ -242900,7 +247584,8 @@ static int sqlite3Fts5IndexInit(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
rc = sqlite3_create_module(db, "fts5_structure", &fts5structure_module, 0);
}
@@ -243039,6 +247724,8 @@ struct Fts5FullTable {
Fts5Storage *pStorage; /* Document store */
Fts5Global *pGlobal; /* Global (connection wide) data */
Fts5Cursor *pSortCsr; /* Sort data from this cursor */
+ int iSavepoint; /* Successful xSavepoint()+1 */
+
#ifdef SQLITE_DEBUG
struct Fts5TransactionState ts;
#endif
@@ -243327,6 +248014,13 @@ static int fts5InitVtab(
pConfig->pzErrmsg = 0;
}
+ if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ rc = sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, (int)1);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
+ }
+
if( rc!=SQLITE_OK ){
fts5FreeVtab(pTab);
pTab = 0;
@@ -243569,12 +248263,15 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
}
idxStr[iIdxStr] = '\0';
- /* Set idxFlags flags for the ORDER BY clause */
+ /* Set idxFlags flags for the ORDER BY clause
+ **
+ ** Note that tokendata=1 tables cannot currently handle "ORDER BY rowid DESC".
+ */
if( pInfo->nOrderBy==1 ){
int iSort = pInfo->aOrderBy[0].iColumn;
if( iSort==(pConfig->nCol+1) && bSeenMatch ){
idxFlags |= FTS5_BI_ORDER_RANK;
- }else if( iSort==-1 ){
+ }else if( iSort==-1 && (!pInfo->aOrderBy[0].desc || !pConfig->bTokendata) ){
idxFlags |= FTS5_BI_ORDER_ROWID;
}
if( BitFlagTest(idxFlags, FTS5_BI_ORDER_RANK|FTS5_BI_ORDER_ROWID) ){
@@ -243826,6 +248523,16 @@ static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){
);
assert( !CsrFlagTest(pCsr, FTS5CSR_EOF) );
+ /* If this cursor uses FTS5_PLAN_MATCH and this is a tokendata=1 table,
+ ** clear any token mappings accumulated at the fts5_index.c level. In
+ ** other cases, specifically FTS5_PLAN_SOURCE and FTS5_PLAN_SORTED_MATCH,
+ ** we need to retain the mappings for the entire query. */
+ if( pCsr->ePlan==FTS5_PLAN_MATCH
+ && ((Fts5Table*)pCursor->pVtab)->pConfig->bTokendata
+ ){
+ sqlite3Fts5ExprClearTokens(pCsr->pExpr);
+ }
+
if( pCsr->ePlan<3 ){
int bSkip = 0;
if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc;
@@ -244251,6 +248958,9 @@ static int fts5FilterMethod(
pCsr->iFirstRowid = fts5GetRowidLimit(pRowidGe, SMALLEST_INT64);
}
+ rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
+ if( rc!=SQLITE_OK ) goto filter_out;
+
if( pTab->pSortCsr ){
/* If pSortCsr is non-NULL, then this call is being made as part of
** processing for a "... MATCH <expr> ORDER BY rank" query (ePlan is
@@ -244273,6 +248983,7 @@ static int fts5FilterMethod(
pCsr->pExpr = pTab->pSortCsr->pExpr;
rc = fts5CursorFirst(pTab, pCsr, bDesc);
}else if( pCsr->pExpr ){
+ assert( rc==SQLITE_OK );
rc = fts5CursorParseRank(pConfig, pCsr, pRank);
if( rc==SQLITE_OK ){
if( bOrderByRank ){
@@ -244444,6 +249155,7 @@ static int fts5SpecialInsert(
Fts5Config *pConfig = pTab->p.pConfig;
int rc = SQLITE_OK;
int bError = 0;
+ int bLoadConfig = 0;
if( 0==sqlite3_stricmp("delete-all", zCmd) ){
if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
@@ -244455,6 +249167,7 @@ static int fts5SpecialInsert(
}else{
rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage);
}
+ bLoadConfig = 1;
}else if( 0==sqlite3_stricmp("rebuild", zCmd) ){
if( pConfig->eContent==FTS5_CONTENT_NONE ){
fts5SetVtabError(pTab,
@@ -244464,6 +249177,7 @@ static int fts5SpecialInsert(
}else{
rc = sqlite3Fts5StorageRebuild(pTab->pStorage);
}
+ bLoadConfig = 1;
}else if( 0==sqlite3_stricmp("optimize", zCmd) ){
rc = sqlite3Fts5StorageOptimize(pTab->pStorage);
}else if( 0==sqlite3_stricmp("merge", zCmd) ){
@@ -244476,8 +249190,13 @@ static int fts5SpecialInsert(
}else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){
pConfig->bPrefixIndex = sqlite3_value_int(pVal);
#endif
+ }else if( 0==sqlite3_stricmp("flush", zCmd) ){
+ rc = sqlite3Fts5FlushToDisk(&pTab->p);
}else{
- rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
+ rc = sqlite3Fts5FlushToDisk(&pTab->p);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
+ }
if( rc==SQLITE_OK ){
rc = sqlite3Fts5ConfigSetValue(pTab->p.pConfig, zCmd, pVal, &bError);
}
@@ -244489,6 +249208,12 @@ static int fts5SpecialInsert(
}
}
}
+
+ if( rc==SQLITE_OK && bLoadConfig ){
+ pTab->p.pConfig->iCookie--;
+ rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
+ }
+
return rc;
}
@@ -244607,7 +249332,7 @@ static int fts5UpdateMethod(
assert( nArg!=1 || eType0==SQLITE_INTEGER );
/* Filter out attempts to run UPDATE or DELETE on contentless tables.
- ** This is not suported. Except - DELETE is supported if the CREATE
+ ** This is not suported. Except - they are both supported if the CREATE
** VIRTUAL TABLE statement contained "contentless_delete=1". */
if( eType0==SQLITE_INTEGER
&& pConfig->eContent==FTS5_CONTENT_NONE
@@ -244636,7 +249361,8 @@ static int fts5UpdateMethod(
}
else if( eType0!=SQLITE_INTEGER ){
- /* If this is a REPLACE, first remove the current entry (if any) */
+ /* An INSERT statement. If the conflict-mode is REPLACE, first remove
+ ** the current entry (if any). */
if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){
i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0);
@@ -244795,7 +249521,10 @@ static int fts5ApiColumnText(
){
int rc = SQLITE_OK;
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab))
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ if( iCol<0 || iCol>=pTab->pConfig->nCol ){
+ rc = SQLITE_RANGE;
+ }else if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab))
|| pCsr->ePlan==FTS5_PLAN_SPECIAL
){
*pz = 0;
@@ -244820,8 +249549,9 @@ static int fts5CsrPoslist(
int rc = SQLITE_OK;
int bLive = (pCsr->pSorter==0);
- if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){
-
+ if( iPhrase<0 || iPhrase>=sqlite3Fts5ExprPhraseCount(pCsr->pExpr) ){
+ rc = SQLITE_RANGE;
+ }else if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){
if( pConfig->eDetail!=FTS5_DETAIL_FULL ){
Fts5PoslistPopulator *aPopulator;
int i;
@@ -244845,15 +249575,21 @@ static int fts5CsrPoslist(
CsrFlagClear(pCsr, FTS5CSR_REQUIRE_POSLIST);
}
- if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){
- Fts5Sorter *pSorter = pCsr->pSorter;
- int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]);
- *pn = pSorter->aIdx[iPhrase] - i1;
- *pa = &pSorter->aPoslist[i1];
+ if( rc==SQLITE_OK ){
+ if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){
+ Fts5Sorter *pSorter = pCsr->pSorter;
+ int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]);
+ *pn = pSorter->aIdx[iPhrase] - i1;
+ *pa = &pSorter->aPoslist[i1];
+ }else{
+ *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa);
+ }
}else{
- *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa);
+ *pa = 0;
+ *pn = 0;
}
+
return rc;
}
@@ -244960,12 +249696,6 @@ static int fts5ApiInst(
){
if( iIdx<0 || iIdx>=pCsr->nInstCount ){
rc = SQLITE_RANGE;
-#if 0
- }else if( fts5IsOffsetless((Fts5Table*)pCsr->base.pVtab) ){
- *piPhrase = pCsr->aInst[iIdx*3];
- *piCol = pCsr->aInst[iIdx*3 + 2];
- *piOff = -1;
-#endif
}else{
*piPhrase = pCsr->aInst[iIdx*3];
*piCol = pCsr->aInst[iIdx*3 + 1];
@@ -245220,13 +249950,56 @@ static int fts5ApiPhraseFirstColumn(
return rc;
}
+/*
+** xQueryToken() API implemenetation.
+*/
+static int fts5ApiQueryToken(
+ Fts5Context* pCtx,
+ int iPhrase,
+ int iToken,
+ const char **ppOut,
+ int *pnOut
+){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ return sqlite3Fts5ExprQueryToken(pCsr->pExpr, iPhrase, iToken, ppOut, pnOut);
+}
+
+/*
+** xInstToken() API implemenetation.
+*/
+static int fts5ApiInstToken(
+ Fts5Context *pCtx,
+ int iIdx,
+ int iToken,
+ const char **ppOut, int *pnOut
+){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ int rc = SQLITE_OK;
+ if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0
+ || SQLITE_OK==(rc = fts5CacheInstArray(pCsr))
+ ){
+ if( iIdx<0 || iIdx>=pCsr->nInstCount ){
+ rc = SQLITE_RANGE;
+ }else{
+ int iPhrase = pCsr->aInst[iIdx*3];
+ int iCol = pCsr->aInst[iIdx*3 + 1];
+ int iOff = pCsr->aInst[iIdx*3 + 2];
+ i64 iRowid = fts5CursorRowid(pCsr);
+ rc = sqlite3Fts5ExprInstToken(
+ pCsr->pExpr, iRowid, iPhrase, iCol, iOff, iToken, ppOut, pnOut
+ );
+ }
+ }
+ return rc;
+}
+
static int fts5ApiQueryPhrase(Fts5Context*, int, void*,
int(*)(const Fts5ExtensionApi*, Fts5Context*, void*)
);
static const Fts5ExtensionApi sFts5Api = {
- 2, /* iVersion */
+ 3, /* iVersion */
fts5ApiUserData,
fts5ApiColumnCount,
fts5ApiRowCount,
@@ -245246,6 +250019,8 @@ static const Fts5ExtensionApi sFts5Api = {
fts5ApiPhraseNext,
fts5ApiPhraseFirstColumn,
fts5ApiPhraseNextColumn,
+ fts5ApiQueryToken,
+ fts5ApiInstToken
};
/*
@@ -245510,8 +250285,10 @@ static int fts5RenameMethod(
sqlite3_vtab *pVtab, /* Virtual table handle */
const char *zName /* New name of table */
){
+ int rc;
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
- return sqlite3Fts5StorageRename(pTab->pStorage, zName);
+ rc = sqlite3Fts5StorageRename(pTab->pStorage, zName);
+ return rc;
}
static int sqlite3Fts5FlushToDisk(Fts5Table *pTab){
@@ -245525,9 +250302,15 @@ static int sqlite3Fts5FlushToDisk(Fts5Table *pTab){
** Flush the contents of the pending-terms table to disk.
*/
static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
- UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
- fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_SAVEPOINT, iSavepoint);
- return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab);
+ Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
+ int rc = SQLITE_OK;
+
+ fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint);
+ rc = sqlite3Fts5FlushToDisk((Fts5Table*)pVtab);
+ if( rc==SQLITE_OK ){
+ pTab->iSavepoint = iSavepoint+1;
+ }
+ return rc;
}
/*
@@ -245536,9 +250319,16 @@ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
** This is a no-op.
*/
static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
- UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
- fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_RELEASE, iSavepoint);
- return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab);
+ Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
+ int rc = SQLITE_OK;
+ fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint);
+ if( (iSavepoint+1)<pTab->iSavepoint ){
+ rc = sqlite3Fts5FlushToDisk(&pTab->p);
+ if( rc==SQLITE_OK ){
+ pTab->iSavepoint = iSavepoint;
+ }
+ }
+ return rc;
}
/*
@@ -245548,11 +250338,14 @@ static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
*/
static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
- UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
+ int rc = SQLITE_OK;
fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint);
fts5TripCursors(pTab);
- pTab->p.pConfig->pgsz = 0;
- return sqlite3Fts5StorageRollback(pTab->pStorage);
+ if( (iSavepoint+1)<=pTab->iSavepoint ){
+ pTab->p.pConfig->pgsz = 0;
+ rc = sqlite3Fts5StorageRollback(pTab->pStorage);
+ }
+ return rc;
}
/*
@@ -245754,7 +250547,7 @@ static void fts5SourceIdFunc(
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
- sqlite3_result_text(pCtx, "fts5: 2023-09-11 12:01:27 2d3a40c05c49e1a49264912b1a05bc2143ac0e7c3df588276ce80a4cbc9bd1b0", -1, SQLITE_TRANSIENT);
+ sqlite3_result_text(pCtx, "fts5: 2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a", -1, SQLITE_TRANSIENT);
}
/*
@@ -245772,9 +250565,40 @@ static int fts5ShadowName(const char *zName){
return 0;
}
+/*
+** Run an integrity check on the FTS5 data structures. Return a string
+** if anything is found amiss. Return a NULL pointer if everything is
+** OK.
+*/
+static int fts5IntegrityMethod(
+ sqlite3_vtab *pVtab, /* the FTS5 virtual table to check */
+ const char *zSchema, /* Name of schema in which this table lives */
+ const char *zTabname, /* Name of the table itself */
+ int isQuick, /* True if this is a quick-check */
+ char **pzErr /* Write error message here */
+){
+ Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
+ int rc;
+
+ assert( pzErr!=0 && *pzErr==0 );
+ UNUSED_PARAM(isQuick);
+ rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, 0);
+ if( (rc&0xff)==SQLITE_CORRUPT ){
+ *pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s",
+ zSchema, zTabname);
+ }else if( rc!=SQLITE_OK ){
+ *pzErr = sqlite3_mprintf("unable to validate the inverted index for"
+ " FTS5 table %s.%s: %s",
+ zSchema, zTabname, sqlite3_errstr(rc));
+ }
+ sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
+
+ return SQLITE_OK;
+}
+
static int fts5Init(sqlite3 *db){
static const sqlite3_module fts5Mod = {
- /* iVersion */ 3,
+ /* iVersion */ 4,
/* xCreate */ fts5CreateMethod,
/* xConnect */ fts5ConnectMethod,
/* xBestIndex */ fts5BestIndexMethod,
@@ -245797,7 +250621,8 @@ static int fts5Init(sqlite3 *db){
/* xSavepoint */ fts5SavepointMethod,
/* xRelease */ fts5ReleaseMethod,
/* xRollbackTo */ fts5RollbackToMethod,
- /* xShadowName */ fts5ShadowName
+ /* xShadowName */ fts5ShadowName,
+ /* xIntegrity */ fts5IntegrityMethod
};
int rc;
@@ -246563,7 +251388,7 @@ static int sqlite3Fts5StorageRebuild(Fts5Storage *p){
}
if( rc==SQLITE_OK ){
- rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
+ rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, pConfig->pzErrmsg);
}
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pScan) ){
@@ -247074,7 +251899,9 @@ static int sqlite3Fts5StorageSync(Fts5Storage *p){
i64 iLastRowid = sqlite3_last_insert_rowid(p->pConfig->db);
if( p->bTotalsValid ){
rc = fts5StorageSaveTotals(p);
- p->bTotalsValid = 0;
+ if( rc==SQLITE_OK ){
+ p->bTotalsValid = 0;
+ }
}
if( rc==SQLITE_OK ){
rc = sqlite3Fts5IndexSync(p->pIndex);
@@ -247348,6 +252175,12 @@ static const unsigned char sqlite3Utf8Trans1[] = {
#endif /* ifndef SQLITE_AMALGAMATION */
+#define FTS5_SKIP_UTF8(zIn) { \
+ if( ((unsigned char)(*(zIn++)))>=0xc0 ){ \
+ while( (((unsigned char)*zIn) & 0xc0)==0x80 ){ zIn++; } \
+ } \
+}
+
typedef struct Unicode61Tokenizer Unicode61Tokenizer;
struct Unicode61Tokenizer {
unsigned char aTokenChar[128]; /* ASCII range token characters */
@@ -248383,6 +253216,7 @@ static int fts5PorterTokenize(
typedef struct TrigramTokenizer TrigramTokenizer;
struct TrigramTokenizer {
int bFold; /* True to fold to lower-case */
+ int iFoldParam; /* Parameter to pass to Fts5UnicodeFold() */
};
/*
@@ -248409,6 +253243,7 @@ static int fts5TriCreate(
}else{
int i;
pNew->bFold = 1;
+ pNew->iFoldParam = 0;
for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
const char *zArg = azArg[i+1];
if( 0==sqlite3_stricmp(azArg[i], "case_sensitive") ){
@@ -248417,10 +253252,21 @@ static int fts5TriCreate(
}else{
pNew->bFold = (zArg[0]=='0');
}
+ }else if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){
+ if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){
+ rc = SQLITE_ERROR;
+ }else{
+ pNew->iFoldParam = (zArg[0]!='0') ? 2 : 0;
+ }
}else{
rc = SQLITE_ERROR;
}
}
+
+ if( pNew->iFoldParam!=0 && pNew->bFold==0 ){
+ rc = SQLITE_ERROR;
+ }
+
if( rc!=SQLITE_OK ){
fts5TriDelete((Fts5Tokenizer*)pNew);
pNew = 0;
@@ -248443,40 +253289,62 @@ static int fts5TriTokenize(
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
int rc = SQLITE_OK;
char aBuf[32];
+ char *zOut = aBuf;
+ int ii;
const unsigned char *zIn = (const unsigned char*)pText;
const unsigned char *zEof = &zIn[nText];
u32 iCode;
+ int aStart[3]; /* Input offset of each character in aBuf[] */
UNUSED_PARAM(unusedFlags);
- while( 1 ){
- char *zOut = aBuf;
- int iStart = zIn - (const unsigned char*)pText;
- const unsigned char *zNext;
-
- READ_UTF8(zIn, zEof, iCode);
- if( iCode==0 ) break;
- zNext = zIn;
- if( zIn<zEof ){
- if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
- WRITE_UTF8(zOut, iCode);
+
+ /* Populate aBuf[] with the characters for the first trigram. */
+ for(ii=0; ii<3; ii++){
+ do {
+ aStart[ii] = zIn - (const unsigned char*)pText;
READ_UTF8(zIn, zEof, iCode);
- if( iCode==0 ) break;
- }else{
- break;
- }
- if( zIn<zEof ){
- if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
- WRITE_UTF8(zOut, iCode);
+ if( iCode==0 ) return SQLITE_OK;
+ if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
+ }while( iCode==0 );
+ WRITE_UTF8(zOut, iCode);
+ }
+
+ /* At the start of each iteration of this loop:
+ **
+ ** aBuf: Contains 3 characters. The 3 characters of the next trigram.
+ ** zOut: Points to the byte following the last character in aBuf.
+ ** aStart[3]: Contains the byte offset in the input text corresponding
+ ** to the start of each of the three characters in the buffer.
+ */
+ assert( zIn<=zEof );
+ while( 1 ){
+ int iNext; /* Start of character following current tri */
+ const char *z1;
+
+ /* Read characters from the input up until the first non-diacritic */
+ do {
+ iNext = zIn - (const unsigned char*)pText;
READ_UTF8(zIn, zEof, iCode);
if( iCode==0 ) break;
- if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
- WRITE_UTF8(zOut, iCode);
- }else{
- break;
- }
- rc = xToken(pCtx, 0, aBuf, zOut-aBuf, iStart, iStart + zOut-aBuf);
- if( rc!=SQLITE_OK ) break;
- zIn = zNext;
+ if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
+ }while( iCode==0 );
+
+ /* Pass the current trigram back to fts5 */
+ rc = xToken(pCtx, 0, aBuf, zOut-aBuf, aStart[0], iNext);
+ if( iCode==0 || rc!=SQLITE_OK ) break;
+
+ /* Remove the first character from buffer aBuf[]. Append the character
+ ** with codepoint iCode. */
+ z1 = aBuf;
+ FTS5_SKIP_UTF8(z1);
+ memmove(aBuf, z1, zOut - z1);
+ zOut -= (z1 - aBuf);
+ WRITE_UTF8(zOut, iCode);
+
+ /* Update the aStart[] array */
+ aStart[0] = aStart[1];
+ aStart[1] = aStart[2];
+ aStart[2] = iNext;
}
return rc;
@@ -248499,7 +253367,9 @@ static int sqlite3Fts5TokenizerPattern(
){
if( xCreate==fts5TriCreate ){
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
- return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB;
+ if( p->iFoldParam==0 ){
+ return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB;
+ }
}
return FTS5_PATTERN_NONE;
}
@@ -250288,7 +255158,7 @@ static int fts5VocabFilterMethod(
if( pEq ){
zTerm = (const char *)sqlite3_value_text(pEq);
nTerm = sqlite3_value_bytes(pEq);
- f = 0;
+ f = FTS5INDEX_QUERY_NOTOKENDATA;
}else{
if( pGe ){
zTerm = (const char *)sqlite3_value_text(pGe);
@@ -250442,7 +255312,8 @@ static int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
- /* xShadowName */ 0
+ /* xShadowName */ 0,
+ /* xIntegrity */ 0
};
void *p = (void*)pGlobal;
@@ -250771,6 +255642,7 @@ static sqlite3_module stmtModule = {
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
+ 0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
diff --git a/src/libs/3rdparty/sqlite/sqlite3.h b/src/libs/3rdparty/sqlite/sqlite3.h
index b9d0692988..4fdfde004e 100644
--- a/src/libs/3rdparty/sqlite/sqlite3.h
+++ b/src/libs/3rdparty/sqlite/sqlite3.h
@@ -146,9 +146,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.43.1"
-#define SQLITE_VERSION_NUMBER 3043001
-#define SQLITE_SOURCE_ID "2023-09-11 12:01:27 2d3a40c05c49e1a49264912b1a05bc2143ac0e7c3df588276ce80a4cbc9bd1b0"
+#define SQLITE_VERSION "3.45.1"
+#define SQLITE_VERSION_NUMBER 3045001
+#define SQLITE_SOURCE_ID "2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -2127,7 +2127,7 @@ struct sqlite3_mem_methods {
** is stored in each sorted record and the required column values loaded
** from the database as records are returned in sorted order. The default
** value for this option is to never use this optimization. Specifying a
-** negative value for this option restores the default behaviour.
+** negative value for this option restores the default behavior.
** This option is only available if SQLite is compiled with the
** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option.
**
@@ -2302,7 +2302,7 @@ struct sqlite3_mem_methods {
** database handle, SQLite checks if this will mean that there are now no
** connections at all to the database. If so, it performs a checkpoint
** operation before closing the connection. This option may be used to
-** override this behaviour. The first parameter passed to this operation
+** override this behavior. The first parameter passed to this operation
** is an integer - positive to disable checkpoints-on-close, or zero (the
** default) to enable them, and negative to leave the setting unchanged.
** The second parameter is a pointer to an integer
@@ -3954,14 +3954,17 @@ SQLITE_API void sqlite3_free_filename(sqlite3_filename);
** </ul>
**
** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
-** text that describes the error, as either UTF-8 or UTF-16 respectively.
+** text that describes the error, as either UTF-8 or UTF-16 respectively,
+** or NULL if no error message is available.
+** (See how SQLite handles [invalid UTF] for exceptions to this rule.)
** ^(Memory to hold the error message string is managed internally.
** The application does not need to worry about freeing the result.
** However, the error string might be overwritten or deallocated by
** subsequent calls to other SQLite interface functions.)^
**
-** ^The sqlite3_errstr() interface returns the English-language text
-** that describes the [result code], as UTF-8.
+** ^The sqlite3_errstr(E) interface returns the English-language text
+** that describes the [result code] E, as UTF-8, or NULL if E is not an
+** result code for which a text error message is available.
** ^(Memory to hold the error message string is managed internally
** and must not be freed by the application)^.
**
@@ -5325,6 +5328,7 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt);
*/
SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
+
/*
** CAPI3REF: Create Or Redefine SQL Functions
** KEYWORDS: {function creation routines}
@@ -5571,13 +5575,27 @@ SQLITE_API int sqlite3_create_window_function(
** </dd>
**
** [[SQLITE_SUBTYPE]] <dt>SQLITE_SUBTYPE</dt><dd>
-** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call
+** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call
** [sqlite3_value_subtype()] to inspect the sub-types of its arguments.
-** Specifying this flag makes no difference for scalar or aggregate user
-** functions. However, if it is not specified for a user-defined window
-** function, then any sub-types belonging to arguments passed to the window
-** function may be discarded before the window function is called (i.e.
-** sqlite3_value_subtype() will always return 0).
+** This flag instructs SQLite to omit some corner-case optimizations that
+** might disrupt the operation of the [sqlite3_value_subtype()] function,
+** causing it to return zero rather than the correct subtype().
+** SQL functions that invokes [sqlite3_value_subtype()] should have this
+** property. If the SQLITE_SUBTYPE property is omitted, then the return
+** value from [sqlite3_value_subtype()] might sometimes be zero even though
+** a non-zero subtype was specified by the function argument expression.
+**
+** [[SQLITE_RESULT_SUBTYPE]] <dt>SQLITE_RESULT_SUBTYPE</dt><dd>
+** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call
+** [sqlite3_result_subtype()] to cause a sub-type to be associated with its
+** result.
+** Every function that invokes [sqlite3_result_subtype()] should have this
+** property. If it does not, then the call to [sqlite3_result_subtype()]
+** might become a no-op if the function is used as term in an
+** [expression index]. On the other hand, SQL functions that never invoke
+** [sqlite3_result_subtype()] should avoid setting this property, as the
+** purpose of this property is to disable certain optimizations that are
+** incompatible with subtypes.
** </dd>
** </dl>
*/
@@ -5585,6 +5603,7 @@ SQLITE_API int sqlite3_create_window_function(
#define SQLITE_DIRECTONLY 0x000080000
#define SQLITE_SUBTYPE 0x000100000
#define SQLITE_INNOCUOUS 0x000200000
+#define SQLITE_RESULT_SUBTYPE 0x001000000
/*
** CAPI3REF: Deprecated Functions
@@ -5781,6 +5800,12 @@ SQLITE_API int sqlite3_value_encoding(sqlite3_value*);
** information can be used to pass a limited amount of context from
** one SQL function to another. Use the [sqlite3_result_subtype()]
** routine to set the subtype for the return value of an SQL function.
+**
+** Every [application-defined SQL function] that invoke this interface
+** should include the [SQLITE_SUBTYPE] property in the text
+** encoding argument when the function is [sqlite3_create_function|registered].
+** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype()
+** might return zero instead of the upstream subtype in some corner cases.
*/
SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*);
@@ -5879,48 +5904,56 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
** METHOD: sqlite3_context
**
** These functions may be used by (non-aggregate) SQL functions to
-** associate metadata with argument values. If the same value is passed to
-** multiple invocations of the same SQL function during query execution, under
-** some circumstances the associated metadata may be preserved. An example
-** of where this might be useful is in a regular-expression matching
-** function. The compiled version of the regular expression can be stored as
-** metadata associated with the pattern string.
+** associate auxiliary data with argument values. If the same argument
+** value is passed to multiple invocations of the same SQL function during
+** query execution, under some circumstances the associated auxiliary data
+** might be preserved. An example of where this might be useful is in a
+** regular-expression matching function. The compiled version of the regular
+** expression can be stored as auxiliary data associated with the pattern string.
** Then as long as the pattern string remains the same,
** the compiled regular expression can be reused on multiple
** invocations of the same function.
**
-** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the metadata
+** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary data
** associated by the sqlite3_set_auxdata(C,N,P,X) function with the Nth argument
** value to the application-defined function. ^N is zero for the left-most
-** function argument. ^If there is no metadata
+** function argument. ^If there is no auxiliary data
** associated with the function argument, the sqlite3_get_auxdata(C,N) interface
** returns a NULL pointer.
**
-** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th
-** argument of the application-defined function. ^Subsequent
+** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as auxiliary data for the
+** N-th argument of the application-defined function. ^Subsequent
** calls to sqlite3_get_auxdata(C,N) return P from the most recent
-** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or
-** NULL if the metadata has been discarded.
+** sqlite3_set_auxdata(C,N,P,X) call if the auxiliary data is still valid or
+** NULL if the auxiliary data has been discarded.
** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL,
** SQLite will invoke the destructor function X with parameter P exactly
-** once, when the metadata is discarded.
-** SQLite is free to discard the metadata at any time, including: <ul>
+** once, when the auxiliary data is discarded.
+** SQLite is free to discard the auxiliary data 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>
+** allocation error occurs.)^
+** <li> ^(during the original sqlite3_set_auxdata() call if the function
+** is evaluated during query planning instead of during query execution,
+** as sometimes happens with [SQLITE_ENABLE_STAT4].)^ </ul>
**
-** Note the last bullet in particular. The destructor X in
+** Note the last two bullets in particular. The destructor X in
** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the
** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata()
** should be called near the end of the function implementation and the
** function implementation should not make any use of P after
-** sqlite3_set_auxdata() has been called.
-**
-** ^(In practice, metadata is preserved between function calls for
+** sqlite3_set_auxdata() has been called. Furthermore, a call to
+** sqlite3_get_auxdata() that occurs immediately after a corresponding call
+** to sqlite3_set_auxdata() might still return NULL if an out-of-memory
+** condition occurred during the sqlite3_set_auxdata() call or if the
+** function is being evaluated during query planning rather than during
+** query execution.
+**
+** ^(In practice, auxiliary data is preserved between function calls for
** function parameters that are compile-time constants, including literal
** values and [parameters] and expressions composed from the same.)^
**
@@ -5930,10 +5963,67 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
**
** These routines must be called from the same thread in which
** the SQL function is running.
+**
+** See also: [sqlite3_get_clientdata()] and [sqlite3_set_clientdata()].
*/
SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N);
SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*));
+/*
+** CAPI3REF: Database Connection Client Data
+** METHOD: sqlite3
+**
+** These functions are used to associate one or more named pointers
+** with a [database connection].
+** A call to sqlite3_set_clientdata(D,N,P,X) causes the pointer P
+** to be attached to [database connection] D using name N. Subsequent
+** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P
+** or a NULL pointer if there were no prior calls to
+** sqlite3_set_clientdata() with the same values of D and N.
+** Names are compared using strcmp() and are thus case sensitive.
+**
+** If P and X are both non-NULL, then the destructor X is invoked with
+** argument P on the first of the following occurrences:
+** <ul>
+** <li> An out-of-memory error occurs during the call to
+** sqlite3_set_clientdata() which attempts to register pointer P.
+** <li> A subsequent call to sqlite3_set_clientdata(D,N,P,X) is made
+** with the same D and N parameters.
+** <li> The database connection closes. SQLite does not make any guarantees
+** about the order in which destructors are called, only that all
+** destructors will be called exactly once at some point during the
+** database connection closing process.
+** </ul>
+**
+** SQLite does not do anything with client data other than invoke
+** destructors on the client data at the appropriate time. The intended
+** use for client data is to provide a mechanism for wrapper libraries
+** to store additional information about an SQLite database connection.
+**
+** There is no limit (other than available memory) on the number of different
+** client data pointers (with different names) that can be attached to a
+** single database connection. However, the implementation is optimized
+** for the case of having only one or two different client data names.
+** Applications and wrapper libraries are discouraged from using more than
+** one client data name each.
+**
+** There is no way to enumerate the client data pointers
+** associated with a database connection. The N parameter can be thought
+** of as a secret key such that only code that knows the secret key is able
+** to access the associated data.
+**
+** Security Warning: These interfaces should not be exposed in scripting
+** languages or in other circumstances where it might be possible for an
+** an attacker to invoke them. Any agent that can invoke these interfaces
+** can probably also take control of the process.
+**
+** Database connection client data is only available for SQLite
+** version 3.44.0 ([dateof:3.44.0]) and later.
+**
+** See also: [sqlite3_set_auxdata()] and [sqlite3_get_auxdata()].
+*/
+SQLITE_API void *sqlite3_get_clientdata(sqlite3*,const char*);
+SQLITE_API int sqlite3_set_clientdata(sqlite3*, const char*, void*, void(*)(void*));
/*
** CAPI3REF: Constants Defining Special Destructor Behavior
@@ -6135,6 +6225,20 @@ SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n);
** higher order bits are discarded.
** The number of subtype bytes preserved by SQLite might increase
** in future releases of SQLite.
+**
+** Every [application-defined SQL function] that invokes this interface
+** should include the [SQLITE_RESULT_SUBTYPE] property in its
+** text encoding argument when the SQL function is
+** [sqlite3_create_function|registered]. If the [SQLITE_RESULT_SUBTYPE]
+** property is omitted from the function that invokes sqlite3_result_subtype(),
+** then in some cases the sqlite3_result_subtype() might fail to set
+** the result subtype.
+**
+** If SQLite is compiled with -DSQLITE_STRICT_SUBTYPE=1, then any
+** SQL function that invokes the sqlite3_result_subtype() interface
+** and that does not have the SQLITE_RESULT_SUBTYPE property will raise
+** an error. Future versions of SQLite might enable -DSQLITE_STRICT_SUBTYPE=1
+** by default.
*/
SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int);
@@ -6566,7 +6670,7 @@ SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName);
SQLITE_API int sqlite3_txn_state(sqlite3*,const char *zSchema);
/*
-** CAPI3REF: Allowed return values from [sqlite3_txn_state()]
+** CAPI3REF: Allowed return values from sqlite3_txn_state()
** KEYWORDS: {transaction state}
**
** These constants define the current transaction state of a database file.
@@ -6698,7 +6802,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
** ^Each call to the sqlite3_autovacuum_pages() interface overrides all
** previous invocations for that database connection. ^If the callback
** argument (C) to sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer,
-** then the autovacuum steps callback is cancelled. The return value
+** then the autovacuum steps callback is canceled. The return value
** from sqlite3_autovacuum_pages() is normally SQLITE_OK, but might
** be some other error code if something goes wrong. The current
** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other
@@ -7217,6 +7321,10 @@ struct sqlite3_module {
/* The methods above are in versions 1 and 2 of the sqlite_module object.
** Those below are for version 3 and greater. */
int (*xShadowName)(const char*);
+ /* The methods above are in versions 1 through 3 of the sqlite_module object.
+ ** Those below are for version 4 and greater. */
+ int (*xIntegrity)(sqlite3_vtab *pVTab, const char *zSchema,
+ const char *zTabName, int mFlags, char **pzErr);
};
/*
@@ -7704,7 +7812,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
** code is returned and the transaction rolled back.
**
** Calling this function with an argument that is not a NULL pointer or an
-** open blob handle results in undefined behaviour. ^Calling this routine
+** open blob handle results in undefined behavior. ^Calling this routine
** with a null pointer (such as would be returned by a failed call to
** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function
** is passed a valid open blob handle, the values returned by the
@@ -7931,9 +8039,11 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
**
** ^(Some systems (for example, Windows 95) do not support the operation
** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try()
-** will always return SQLITE_BUSY. The SQLite core only ever uses
-** sqlite3_mutex_try() as an optimization so this is acceptable
-** behavior.)^
+** will always return SQLITE_BUSY. In most cases the SQLite core only uses
+** sqlite3_mutex_try() as an optimization, so this is acceptable
+** behavior. The exceptions are unix builds that set the
+** SQLITE_ENABLE_SETLK_TIMEOUT build option. In that case a working
+** sqlite3_mutex_try() is required.)^
**
** ^The sqlite3_mutex_leave() routine exits a mutex that was
** previously entered by the same thread. The behavior
@@ -8184,6 +8294,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_PRNG_SAVE 5
#define SQLITE_TESTCTRL_PRNG_RESTORE 6
#define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
+#define SQLITE_TESTCTRL_FK_NO_ACTION 7
#define SQLITE_TESTCTRL_BITVEC_TEST 8
#define SQLITE_TESTCTRL_FAULT_INSTALL 9
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
@@ -8191,6 +8302,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_ASSERT 12
#define SQLITE_TESTCTRL_ALWAYS 13
#define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */
+#define SQLITE_TESTCTRL_JSON_SELFCHECK 14
#define SQLITE_TESTCTRL_OPTIMIZATIONS 15
#define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */
@@ -9245,8 +9357,8 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p);
** blocked connection already has a registered unlock-notify callback,
** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is
** called with a NULL pointer as its second argument, then any existing
-** unlock-notify callback is cancelled. ^The blocked connections
-** unlock-notify callback may also be cancelled by closing the blocked
+** unlock-notify callback is canceled. ^The blocked connections
+** unlock-notify callback may also be canceled by closing the blocked
** connection using [sqlite3_close()].
**
** The unlock-notify callback is not reentrant. If an application invokes
@@ -10549,6 +10661,13 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c
** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy
** of the database exists.
**
+** After the call, if the SQLITE_SERIALIZE_NOCOPY bit had been set,
+** the returned buffer content will remain accessible and unchanged
+** until either the next write operation on the connection or when
+** the connection is closed, and applications must not modify the
+** buffer. If the bit had been clear, the returned buffer will not
+** be accessed by SQLite after the call.
+**
** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the
** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory
** allocation error occurs.
@@ -10597,6 +10716,9 @@ SQLITE_API unsigned char *sqlite3_serialize(
** SQLite will try to increase the buffer size using sqlite3_realloc64()
** if writes on the database cause it to grow larger than M bytes.
**
+** Applications must not modify the buffer P or invalidate it before
+** the database connection D is closed.
+**
** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the
** database is currently in a read transaction or is involved in a backup
** operation.
@@ -10605,6 +10727,13 @@ SQLITE_API unsigned char *sqlite3_serialize(
** S argument to sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the
** function returns SQLITE_ERROR.
**
+** The deserialized database should not be in [WAL mode]. If the database
+** is in WAL mode, then any attempt to use the database file will result
+** in an [SQLITE_CANTOPEN] error. The application can set the
+** [file format version numbers] (bytes 18 and 19) of the input database P
+** to 0x01 prior to invoking sqlite3_deserialize(D,S,P,N,M,F) to force the
+** database file into rollback mode and work around this limitation.
+**
** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the
** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then
** [sqlite3_free()] is invoked on argument P prior to returning.
@@ -11678,6 +11807,18 @@ SQLITE_API int sqlite3changeset_concat(
/*
+** CAPI3REF: Upgrade the Schema of a Changeset/Patchset
+*/
+SQLITE_API int sqlite3changeset_upgrade(
+ sqlite3 *db,
+ const char *zDb,
+ int nIn, const void *pIn, /* Input changeset */
+ int *pnOut, void **ppOut /* OUT: Inverse of input */
+);
+
+
+
+/*
** CAPI3REF: Changegroup Handle
**
** A changegroup is an object used to combine two or more
@@ -11724,6 +11865,38 @@ typedef struct sqlite3_changegroup sqlite3_changegroup;
SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp);
/*
+** CAPI3REF: Add a Schema to a Changegroup
+** METHOD: sqlite3_changegroup_schema
+**
+** This method may be used to optionally enforce the rule that the changesets
+** added to the changegroup handle must match the schema of database zDb
+** ("main", "temp", or the name of an attached database). If
+** sqlite3changegroup_add() is called to add a changeset that is not compatible
+** with the configured schema, SQLITE_SCHEMA is returned and the changegroup
+** object is left in an undefined state.
+**
+** A changeset schema is considered compatible with the database schema in
+** the same way as for sqlite3changeset_apply(). Specifically, for each
+** table in the changeset, there exists a database table with:
+**
+** <ul>
+** <li> The name identified by the changeset, and
+** <li> at least as many columns as recorded in the changeset, and
+** <li> the primary key columns in the same position as recorded in
+** the changeset.
+** </ul>
+**
+** The output of the changegroup object always has the same schema as the
+** database nominated using this function. In cases where changesets passed
+** to sqlite3changegroup_add() have fewer columns than the corresponding table
+** in the database schema, these are filled in using the default column
+** values from the database schema. This makes it possible to combined
+** changesets that have different numbers of columns for a single table
+** within a changegroup, provided that they are otherwise compatible.
+*/
+SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb);
+
+/*
** CAPI3REF: Add A Changeset To A Changegroup
** METHOD: sqlite3_changegroup
**
@@ -11791,13 +11964,18 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp);
** If the new changeset contains changes to a table that is already present
** in the changegroup, then the number of columns and the position of the
** primary key columns for the table must be consistent. If this is not the
-** case, this function fails with SQLITE_SCHEMA. If the input changeset
-** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is
-** returned. Or, if an out-of-memory condition occurs during processing, this
-** function returns SQLITE_NOMEM. In all cases, if an error occurs the state
-** of the final contents of the changegroup is undefined.
+** case, this function fails with SQLITE_SCHEMA. Except, if the changegroup
+** object has been configured with a database schema using the
+** sqlite3changegroup_schema() API, then it is possible to combine changesets
+** with different numbers of columns for a single table, provided that
+** they are otherwise compatible.
+**
+** If the input changeset appears to be corrupt and the corruption is
+** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition
+** occurs during processing, this function returns SQLITE_NOMEM.
**
-** If no error occurs, SQLITE_OK is returned.
+** In all cases, if an error occurs the state of the final contents of the
+** changegroup is undefined. If no error occurs, SQLITE_OK is returned.
*/
SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
@@ -12062,10 +12240,17 @@ SQLITE_API int sqlite3changeset_apply_v2(
** <li>an insert change if all fields of the conflicting row match
** the row being inserted.
** </ul>
+**
+** <dt>SQLITE_CHANGESETAPPLY_FKNOACTION <dd>
+** If this flag it set, then all foreign key constraints in the target
+** database behave as if they were declared with "ON UPDATE NO ACTION ON
+** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL
+** or SET DEFAULT.
*/
#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001
#define SQLITE_CHANGESETAPPLY_INVERT 0x0002
#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004
+#define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008
/*
** CAPI3REF: Constants Passed To The Conflict Handler
@@ -12631,8 +12816,11 @@ struct Fts5PhraseIter {
** created with the "columnsize=0" option.
**
** xColumnText:
-** This function attempts to retrieve the text of column iCol of the
-** current document. If successful, (*pz) is set to point to a buffer
+** If parameter iCol is less than zero, or greater than or equal to the
+** number of columns in the table, SQLITE_RANGE is returned.
+**
+** Otherwise, this function attempts to retrieve the text of column iCol of
+** the current document. If successful, (*pz) is set to point to a buffer
** containing the text in utf-8 encoding, (*pn) is set to the size in bytes
** (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
** if an error occurs, an SQLite error code is returned and the final values
@@ -12642,8 +12830,10 @@ struct Fts5PhraseIter {
** Returns the number of phrases in the current query expression.
**
** xPhraseSize:
-** Returns the number of tokens in phrase iPhrase of the query. Phrases
-** are numbered starting from zero.
+** If parameter iCol is less than zero, or greater than or equal to the
+** number of phrases in the current query, as returned by xPhraseCount,
+** 0 is returned. Otherwise, this function returns the number of tokens in
+** phrase iPhrase of the query. Phrases are numbered starting from zero.
**
** xInstCount:
** Set *pnInst to the total number of occurrences of all phrases within
@@ -12659,12 +12849,13 @@ struct Fts5PhraseIter {
** Query for the details of phrase match iIdx within the current row.
** Phrase matches are numbered starting from zero, so the iIdx argument
** should be greater than or equal to zero and smaller than the value
-** output by xInstCount().
+** output by xInstCount(). If iIdx is less than zero or greater than
+** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned.
**
-** Usually, output parameter *piPhrase is set to the phrase number, *piCol
+** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol
** to the column in which it occurs and *piOff the token offset of the
-** first token of the phrase. Returns SQLITE_OK if successful, or an error
-** code (i.e. SQLITE_NOMEM) if an error occurs.
+** first token of the phrase. SQLITE_OK is returned if successful, or an
+** error code (i.e. SQLITE_NOMEM) if an error occurs.
**
** This API can be quite slow if used with an FTS5 table created with the
** "detail=none" or "detail=column" option.
@@ -12690,6 +12881,10 @@ struct Fts5PhraseIter {
** Invoking Api.xUserData() returns a copy of the pointer passed as
** the third argument to pUserData.
**
+** If parameter iPhrase is less than zero, or greater than or equal to
+** the number of phrases in the query, as returned by xPhraseCount(),
+** this function returns SQLITE_RANGE.
+**
** If the callback function returns any value other than SQLITE_OK, the
** query is abandoned and the xQueryPhrase function returns immediately.
** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
@@ -12804,9 +12999,42 @@ struct Fts5PhraseIter {
**
** xPhraseNextColumn()
** See xPhraseFirstColumn above.
+**
+** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken)
+** This is used to access token iToken of phrase iPhrase of the current
+** query. Before returning, output parameter *ppToken is set to point
+** to a buffer containing the requested token, and *pnToken to the
+** size of this buffer in bytes.
+**
+** If iPhrase or iToken are less than zero, or if iPhrase is greater than
+** or equal to the number of phrases in the query as reported by
+** xPhraseCount(), or if iToken is equal to or greater than the number of
+** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken
+ are both zeroed.
+**
+** The output text is not a copy of the query text that specified the
+** token. It is the output of the tokenizer module. For tokendata=1
+** tables, this includes any embedded 0x00 and trailing data.
+**
+** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken)
+** This is used to access token iToken of phrase hit iIdx within the
+** current row. If iIdx is less than zero or greater than or equal to the
+** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
+** output variable (*ppToken) is set to point to a buffer containing the
+** matching document token, and (*pnToken) to the size of that buffer in
+** bytes. This API is not available if the specified token matches a
+** prefix query term. In that case both output variables are always set
+** to 0.
+**
+** The output text is not a copy of the document text that was tokenized.
+** It is the output of the tokenizer module. For tokendata=1 tables, this
+** includes any embedded 0x00 and trailing data.
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option.
*/
struct Fts5ExtensionApi {
- int iVersion; /* Currently always set to 2 */
+ int iVersion; /* Currently always set to 3 */
void *(*xUserData)(Fts5Context*);
@@ -12841,6 +13069,13 @@ struct Fts5ExtensionApi {
int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
+
+ /* Below this point are iVersion>=3 only */
+ int (*xQueryToken)(Fts5Context*,
+ int iPhrase, int iToken,
+ const char **ppToken, int *pnToken
+ );
+ int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*);
};
/*
diff --git a/src/libs/3rdparty/sqlite/sqlite3ext.h b/src/libs/3rdparty/sqlite/sqlite3ext.h
index 7116380992..ae0949baf7 100644
--- a/src/libs/3rdparty/sqlite/sqlite3ext.h
+++ b/src/libs/3rdparty/sqlite/sqlite3ext.h
@@ -363,6 +363,9 @@ struct sqlite3_api_routines {
int (*is_interrupted)(sqlite3*);
/* Version 3.43.0 and later */
int (*stmt_explain)(sqlite3_stmt*,int);
+ /* Version 3.44.0 and later */
+ void *(*get_clientdata)(sqlite3*,const char*);
+ int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
};
/*
@@ -693,6 +696,9 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_is_interrupted sqlite3_api->is_interrupted
/* Version 3.43.0 and later */
#define sqlite3_stmt_explain sqlite3_api->stmt_explain
+/* Version 3.44.0 and later */
+#define sqlite3_get_clientdata sqlite3_api->get_clientdata
+#define sqlite3_set_clientdata sqlite3_api->set_clientdata
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
diff --git a/src/libs/extensionsystem/pluginmanager.cpp b/src/libs/extensionsystem/pluginmanager.cpp
index fcb3062faa..4254fdfc0b 100644
--- a/src/libs/extensionsystem/pluginmanager.cpp
+++ b/src/libs/extensionsystem/pluginmanager.cpp
@@ -18,7 +18,7 @@
#include <utils/futuresynchronizer.h>
#include <utils/hostosinfo.h>
#include <utils/mimeutils.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/qtcsettings.h>
#include <utils/threadutils.h>
diff --git a/src/libs/nanotrace/CMakeLists.txt b/src/libs/nanotrace/CMakeLists.txt
index 8652a81798..50693644ea 100644
--- a/src/libs/nanotrace/CMakeLists.txt
+++ b/src/libs/nanotrace/CMakeLists.txt
@@ -3,21 +3,21 @@ add_qtc_library(Nanotrace
SOURCES
nanotraceglobals.h
nanotrace.cpp nanotrace.h
+ nanotracehr.cpp nanotracehr.h
+ staticstring.h
PUBLIC_DEPENDS Qt::Core Qt::Gui
PROPERTIES
CXX_VISIBILITY_PRESET default
VISIBILITY_INLINES_HIDDEN OFF
)
+if(TARGET Nanotrace)
+ target_compile_options(Nanotrace PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,MSVC>:/wd5030>)
+endif()
+
option(DESIGNSTUDIO_USE_NANOTRACE
"Enables collecting performance data with nanotrace for Design Studio" OFF)
extend_qtc_library(Nanotrace
CONDITION DESIGN_STUDIO_USE_NANOTRACE
PUBLIC_DEFINES NANOTRACE_DESIGNSTUDIO_ENABLED
)
-
-option(NANOTRACEHR_ENABLED "Enables collecting high resolution performance data" OFF)
-extend_qtc_library(Nanotrace
- SOURCES
- nanotracehr.cpp nanotracehr.h
-)
diff --git a/src/libs/nanotrace/nanotraceglobals.h b/src/libs/nanotrace/nanotraceglobals.h
index 64af31aab9..145b8ad003 100644
--- a/src/libs/nanotrace/nanotraceglobals.h
+++ b/src/libs/nanotrace/nanotraceglobals.h
@@ -7,10 +7,24 @@
#if defined(NANOTRACE_LIBRARY)
#define NANOTRACE_EXPORT Q_DECL_EXPORT
+# ifdef Q_OS_WINDOWS
+# define NANOTRACE_EXPORT_EXTERN_TEMPLATE
+# else
+# define NANOTRACE_EXPORT_EXTERN_TEMPLATE Q_DECL_EXPORT
+# endif
+# ifdef Q_OS_WINDOWS
+# define NANOTRACE_EXPORT_TEMPLATE Q_DECL_EXPORT
+# else
+# define NANOTRACE_EXPORT_TEMPLATE
+# endif
#elif defined(NANOTRACE_STATIC_LIBRARY)
-#define NANOTRACE_EXPORT
+# define NANOTRACE_EXPORT
+# define NANOTRACE_EXPORT_EXTERN_TEMPLATE
+# define NANOTRACE_EXPORT_TEMPLATE
#else
-#define NANOTRACE_EXPORT Q_DECL_IMPORT
+# define NANOTRACE_EXPORT Q_DECL_IMPORT
+# define NANOTRACE_EXPORT_EXTERN_TEMPLATE
+# define NANOTRACE_EXPORT_TEMPLATE
#endif
#define NANOTRACESHARED_EXPORT NANOTRACE_EXPORT
diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp
index ada5bf65f5..0006227434 100644
--- a/src/libs/nanotrace/nanotracehr.cpp
+++ b/src/libs/nanotrace/nanotracehr.cpp
@@ -45,6 +45,18 @@ unsigned int getUnsignedIntegerHash(std::thread::id id)
return static_cast<unsigned int>(std::hash<std::thread::id>{}(id) & 0xFFFFFFFF);
}
+template<std::size_t capacity>
+constexpr bool isArgumentValid(const StaticString<capacity> &string)
+{
+ return string.isValid() && string.size();
+}
+
+template<typename String>
+constexpr bool isArgumentValid(const String &string)
+{
+ return string.size();
+}
+
template<typename TraceEvent>
void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, std::thread::id threadId)
{
@@ -67,23 +79,24 @@ void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, st
out << R"(,"flow_in":true)";
}
- if (event.arguments.size())
+ if (isArgumentValid(event.arguments)) {
out << R"(,"args":)" << event.arguments;
+ }
out << "}";
}
-void writeMetaEvent(TraceFile<Tracing::IsEnabled> *file, std::string_view key, std::string_view value)
+void writeMetaEvent(TraceFile<Tracing::IsEnabled> &file, std::string_view key, std::string_view value)
{
- std::lock_guard lock{file->fileMutex};
- auto &out = file->out;
+ std::lock_guard lock{file.fileMutex};
+ auto &out = file.out;
if (out.is_open()) {
- file->out << R"({"name":")" << key << R"(","ph":"M", "pid":)"
- << getUnsignedIntegerHash(QCoreApplication::applicationPid()) << R"(,"tid":)"
- << getUnsignedIntegerHash(std::this_thread::get_id()) << R"(,"args":{"name":")"
- << value << R"("}})"
- << ",\n";
+ file.out << R"({"name":")" << key << R"(","ph":"M", "pid":)"
+ << getUnsignedIntegerHash(QCoreApplication::applicationPid()) << R"(,"tid":)"
+ << getUnsignedIntegerHash(std::this_thread::get_id()) << R"(,"args":{"name":")"
+ << value << R"("}})"
+ << ",\n";
}
}
@@ -103,7 +116,6 @@ std::string getThreadName()
} // namespace
-namespace Internal {
template<typename String>
void convertToString(String &string, const QImage &image)
{
@@ -132,14 +144,11 @@ void convertToString(String &string, const QImage &image)
return "alpha premultiplied"sv;
}))));
- Internal::convertToString(string, dict);
+ convertToString(string, dict);
}
-template NANOTRACE_EXPORT void convertToString(std::string &string, const QImage &image);
template NANOTRACE_EXPORT void convertToString(ArgumentsString &string, const QImage &image);
-} // namespace Internal
-
template<typename TraceEvent>
void flushEvents(const Utils::span<TraceEvent> events,
std::thread::id threadId,
@@ -148,8 +157,8 @@ void flushEvents(const Utils::span<TraceEvent> events,
if (events.empty())
return;
- std::lock_guard lock{eventQueue.file->fileMutex};
- auto &out = eventQueue.file->out;
+ std::lock_guard lock{eventQueue.file.fileMutex};
+ auto &out = eventQueue.file.out;
if (out.is_open()) {
auto processId = QCoreApplication::applicationPid();
@@ -200,17 +209,17 @@ void finalizeFile(EnabledTraceFile &file)
template<typename TraceEvent>
void flushInThread(EnabledEventQueue<TraceEvent> &eventQueue)
{
- if (eventQueue.file->processing.valid())
- eventQueue.file->processing.wait();
+ if (eventQueue.file.processing.valid())
+ eventQueue.file.processing.wait();
auto flush = [&](const Utils::span<TraceEvent> &events, std::thread::id threadId) {
flushEvents(events, threadId, eventQueue);
};
- eventQueue.file->processing = std::async(std::launch::async,
- flush,
- eventQueue.currentEvents.subspan(0, eventQueue.eventsIndex),
- eventQueue.threadId);
+ eventQueue.file.processing = std::async(std::launch::async,
+ flush,
+ eventQueue.currentEvents.subspan(0, eventQueue.eventsIndex),
+ eventQueue.threadId);
eventQueue.currentEvents = eventQueue.currentEvents.data() == eventQueue.eventsOne.data()
? eventQueue.eventsTwo
: eventQueue.eventsOne;
@@ -222,28 +231,12 @@ template NANOTRACE_EXPORT void flushInThread(EnabledEventQueue<StringTraceEvent>
template NANOTRACE_EXPORT void flushInThread(
EnabledEventQueue<StringViewWithStringArgumentsTraceEvent> &eventQueue);
-namespace {
-TraceFile<tracingStatus()> globalTraceFile{"global.json"};
-thread_local EventQueueData<StringTraceEvent, 1000, tracingStatus()> globalEventQueueData{
- globalTraceFile};
-thread_local EventQueue s_globalEventQueue = globalEventQueueData.createEventQueue();
-} // namespace
-
-EventQueue<StringTraceEvent, tracingStatus()> &globalEventQueue()
-{
- return s_globalEventQueue;
-}
-
template<typename TraceEvent>
-EventQueue<TraceEvent, Tracing::IsEnabled>::EventQueue(EnabledTraceFile *file,
- TraceEventsSpan eventsOne,
- TraceEventsSpan eventsTwo)
+EventQueue<TraceEvent, Tracing::IsEnabled>::EventQueue(EnabledTraceFile &file)
: file{file}
- , eventsOne{eventsOne}
- , eventsTwo{eventsTwo}
- , currentEvents{eventsOne}
, threadId{std::this_thread::get_id()}
{
+ setEventsSpans(*eventArrayOne.get(), *eventArrayTwo.get());
Internal::EventQueueTracker<TraceEvent>::get().addQueue(this);
if (auto thread = QThread::currentThread()) {
auto name = getThreadName();
@@ -262,6 +255,15 @@ EventQueue<TraceEvent, Tracing::IsEnabled>::~EventQueue()
}
template<typename TraceEvent>
+void EventQueue<TraceEvent, Tracing::IsEnabled>::setEventsSpans(TraceEventsSpan eventsSpanOne,
+ TraceEventsSpan eventsSpanTwo)
+{
+ eventsOne = eventsSpanOne;
+ eventsTwo = eventsSpanTwo;
+ currentEvents = eventsSpanOne;
+}
+
+template<typename TraceEvent>
void EventQueue<TraceEvent, Tracing::IsEnabled>::flush()
{
std::lock_guard lock{mutex};
@@ -271,8 +273,9 @@ void EventQueue<TraceEvent, Tracing::IsEnabled>::flush()
}
}
-template class EventQueue<StringViewTraceEvent, Tracing::IsEnabled>;
-template class EventQueue<StringTraceEvent, Tracing::IsEnabled>;
-template class EventQueue<StringViewWithStringArgumentsTraceEvent, Tracing::IsEnabled>;
+template class NANOTRACE_EXPORT_TEMPLATE EventQueue<StringViewTraceEvent, Tracing::IsEnabled>;
+template class NANOTRACE_EXPORT_TEMPLATE EventQueue<StringTraceEvent, Tracing::IsEnabled>;
+template class NANOTRACE_EXPORT_TEMPLATE
+ EventQueue<StringViewWithStringArgumentsTraceEvent, Tracing::IsEnabled>;
} // namespace NanotraceHR
diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h
index 62ec7dc03c..810e4e3c5d 100644
--- a/src/libs/nanotrace/nanotracehr.h
+++ b/src/libs/nanotrace/nanotracehr.h
@@ -5,15 +5,22 @@
#include "nanotraceglobals.h"
+#include "staticstring.h"
+
#include <utils/smallstring.h>
#include <utils/span.h>
#include <QByteArrayView>
+#include <QList>
+#include <QMap>
#include <QStringView>
+#include <QVarLengthArray>
+#include <QVariant>
#include <array>
#include <atomic>
#include <chrono>
+#include <exception>
#include <fstream>
#include <future>
#include <mutex>
@@ -32,15 +39,6 @@ static_assert(std::is_same_v<Clock::duration, std::chrono::nanoseconds>,
enum class Tracing { IsDisabled, IsEnabled };
-constexpr Tracing tracingStatus()
-{
-#ifdef NANOTRACEHR_ENABLED
- return Tracing::IsEnabled;
-#else
- return Tracing::IsDisabled;
-#endif
-}
-
#if __cplusplus >= 202002L && __has_cpp_attribute(msvc::no_unique_address)
# define NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
#elif __cplusplus >= 202002L && __has_cpp_attribute(no_unique_address) >= 201803L
@@ -49,7 +47,7 @@ constexpr Tracing tracingStatus()
# define NO_UNIQUE_ADDRESS
#endif
-using ArgumentsString = Utils::BasicSmallString<510>;
+using ArgumentsString = StaticString<3700>;
namespace Literals {
struct TracerLiteral
@@ -60,6 +58,8 @@ struct TracerLiteral
operator std::string() const { return std::string{text}; }
+ operator Utils::SmallString() const { return text; }
+
private:
constexpr TracerLiteral(std::string_view text)
: text{text}
@@ -86,44 +86,41 @@ struct IsDictonary
inline constexpr IsDictonary isDictonary;
-namespace Internal {
-
template<typename String>
void convertToString(String &string, std::string_view text)
{
- string.append(R"(")");
+ string.append('\"');
string.append(text);
- string.append(R"(")");
+ string.append('\"');
}
template<typename String>
void convertToString(String &string, const QImage &image);
-extern template NANOTRACE_EXPORT void convertToString(std::string &string, const QImage &image);
extern template NANOTRACE_EXPORT void convertToString(ArgumentsString &string, const QImage &image);
template<typename String, std::size_t size>
void convertToString(String &string, const char (&text)[size])
{
- string.append(R"(")");
- string.append(text);
- string.append(R"(")");
+ string.append('\"');
+ string.append(std::string_view{text, size - 1});
+ string.append('\"');
}
template<typename String>
void convertToString(String &string, QStringView text)
{
- string.append(R"(")");
+ string.append('\"');
string.append(Utils::PathString{text});
- string.append(R"(")");
+ string.append('\"');
}
template<typename String>
void convertToString(String &string, const QByteArray &text)
{
- string.append(R"(")");
+ string.append('\"');
string.append(std::string_view(text.data(), Utils::usize(text)));
- string.append(R"(")");
+ string.append('\"');
}
template<typename String>
@@ -135,38 +132,51 @@ void convertToString(String &string, bool isTrue)
string.append("false");
}
-template<typename String, typename Callable, typename = std::enable_if_t<std::is_invocable_v<Callable>>>
+template<typename String, typename Callable, typename std::enable_if_t<std::is_invocable_v<Callable>, bool> = true>
void convertToString(String &string, Callable &&callable)
{
convertToString(string, callable());
}
-template<typename String>
-void convertToString(String &string, int number)
+template<typename String, typename Number, typename std::enable_if_t<std::is_arithmetic_v<Number>, bool> = true>
+void convertToString(String &string, Number number)
{
- string.append(Utils::SmallString::number(number));
+ string.append(number);
}
-template<typename String>
-void convertToString(String &string, long long number)
+template<typename Enumeration>
+constexpr std::underlying_type_t<Enumeration> to_underlying(Enumeration enumeration) noexcept
{
- string.append(Utils::SmallString::number(number));
+ static_assert(std::is_enum_v<Enumeration>, "to_underlying expect an enumeration");
+ return static_cast<std::underlying_type_t<Enumeration>>(enumeration);
}
-template<typename String>
-void convertToString(String &string, double number)
+template<typename String, typename Enumeration, typename std::enable_if_t<std::is_enum_v<Enumeration>, bool> = true>
+void convertToString(String &string, Enumeration enumeration)
{
- string.append(Utils::SmallString::number(number));
+ string.append(to_underlying(enumeration));
}
-template<typename String, typename... Arguments>
-void convertToString(String &string, const std::tuple<const IsDictonary &, Arguments...> &dictonary);
+template<typename String>
+void convertToString(String &string, const QString &text)
+{
+ convertToString(string, QStringView{text});
+}
-template<typename String, typename... Arguments>
-void convertToString(String &string, const std::tuple<const IsArray &, Arguments...> &list);
+template<typename String>
+void convertToString(String &string, const QVariant &value)
+{
+ convertToString(string, value.toString());
+}
-template<typename String, template<typename...> typename Container, typename... Arguments>
-void convertToString(String &string, const Container<Arguments...> &container);
+template<typename String, typename Type>
+void convertToString(String &string, const std::optional<Type> &value)
+{
+ if (value)
+ convertToString(string, *value);
+ else
+ convertToString(string, "empty optional");
+}
template<typename String, typename Value>
void convertArrayEntryToString(String &string, const Value &value)
@@ -178,15 +188,15 @@ void convertArrayEntryToString(String &string, const Value &value)
template<typename String, typename... Entries>
void convertArrayToString(String &string, const IsArray &, Entries &...entries)
{
- string.append(R"([)");
+ string.append('[');
(convertArrayEntryToString(string, entries), ...);
if (sizeof...(entries))
string.pop_back();
- string.append("]");
+ string.append(']');
}
template<typename String, typename... Arguments>
-void convertToString(String &string, const std::tuple<const IsArray &, Arguments...> &list)
+void convertToString(String &string, const std::tuple<IsArray, Arguments...> &list)
{
std::apply([&](auto &&...entries) { convertArrayToString(string, entries...); }, list);
}
@@ -196,59 +206,107 @@ void convertDictonaryEntryToString(String &string, const std::tuple<Key, Value>
{
const auto &[key, value] = argument;
convertToString(string, key);
- string.append(":");
+ string.append(':');
convertToString(string, value);
- string.append(",");
+ string.append(',');
}
template<typename String, typename... Entries>
void convertDictonaryToString(String &string, const IsDictonary &, Entries &...entries)
{
- string.append(R"({)");
+ string.append('{');
(convertDictonaryEntryToString(string, entries), ...);
if (sizeof...(entries))
string.pop_back();
- string.append("}");
+ string.append('}');
}
template<typename String, typename... Arguments>
-void convertToString(String &string, const std::tuple<const IsDictonary &, Arguments...> &dictonary)
+void convertToString(String &string, const std::tuple<IsDictonary, Arguments...> &dictonary)
{
std::apply([&](auto &&...entries) { convertDictonaryToString(string, entries...); }, dictonary);
}
-template<typename String, template<typename...> typename Container, typename... Arguments>
-void convertToString(String &string, const Container<Arguments...> &container)
+template<typename T>
+struct is_container : std::false_type
+{};
+
+template<typename... Arguments>
+struct is_container<std::vector<Arguments...>> : std::true_type
+{};
+
+template<typename... Arguments>
+struct is_container<QList<Arguments...>> : std::true_type
+{};
+
+template<typename T, qsizetype Prealloc>
+struct is_container<QVarLengthArray<T, Prealloc>> : std::true_type
+{};
+
+template<typename String, typename Container, typename std::enable_if_t<is_container<Container>::value, bool> = true>
+void convertToString(String &string, const Container &values)
+{
+ string.append('[');
+
+ for (const auto &value : values) {
+ convertToString(string, value);
+ string.append(',');
+ }
+
+ if (values.size())
+ string.pop_back();
+
+ string.append(']');
+}
+
+template<typename T>
+struct is_map : std::false_type
+{};
+
+template<typename... Arguments>
+struct is_map<QtPrivate::QKeyValueRange<Arguments...>> : std::true_type
+{};
+
+template<typename... Arguments>
+struct is_map<std::map<Arguments...>> : std::true_type
+{};
+
+template<typename String, typename Map, typename std::enable_if_t<is_map<Map>::value, bool> = true>
+void convertToString(String &string, const Map &map)
{
- string.append("[");
- for (const auto &entry : container) {
- convertToString(string, entry);
- string.append(",");
+ string.append('{');
+
+ for (const auto &[key, value] : map) {
+ convertToString(string, key);
+ string.append(':');
+ convertToString(string, value);
+ string.append(',');
}
- if (container.size())
+ if (map.begin() != map.end())
string.pop_back();
- string.append("]");
+ string.append('}');
}
+template<typename String, typename Key, typename Value>
+void convertToString(String &string, const QMap<Key, Value> &dictonary)
+{
+ convertToString(string, dictonary.asKeyValueRange());
+}
+
+namespace Internal {
+
template<typename String, typename... Arguments>
-String toArguments(Arguments &&...arguments)
+void toArguments(String &text, Arguments &&...arguments)
{
- if constexpr (tracingStatus() == Tracing::IsEnabled) {
- String text;
- constexpr auto argumentCount = sizeof...(Arguments);
- text.append("{");
- (convertDictonaryEntryToString(text, arguments), ...);
- if (argumentCount)
- text.pop_back();
-
- text.append("}");
-
- return text;
- } else {
- return {};
- }
+ constexpr auto argumentCount = sizeof...(Arguments);
+ text.append('{');
+ (convertDictonaryEntryToString(text, arguments), ...);
+ if (argumentCount)
+ text.pop_back();
+
+ text.append('}');
}
inline std::string_view toArguments(std::string_view arguments)
@@ -257,48 +315,58 @@ inline std::string_view toArguments(std::string_view arguments)
}
template<typename String>
-void appendArguments(String &eventArguments)
+void setArguments(String &eventArguments)
{
- eventArguments = {};
+ if constexpr (std::is_same_v<String, std::string_view>)
+ eventArguments = {};
+ else
+ eventArguments.clear();
}
template<typename String>
-void appendArguments(String &eventArguments, TracerLiteral arguments)
+void setArguments(String &eventArguments, TracerLiteral arguments)
{
eventArguments = arguments;
}
template<typename String, typename... Arguments>
-[[maybe_unused]] void appendArguments(String &eventArguments, Arguments &&...arguments)
+[[maybe_unused]] void setArguments(String &eventArguments, Arguments &&...arguments)
{
static_assert(
!std::is_same_v<String, std::string_view>,
R"(The arguments type of the tracing event queue is a string view. You can only provide trace token arguments as TracerLiteral (""_t).)");
- eventArguments = Internal::toArguments<String>(std::forward<Arguments>(arguments)...);
+ if constexpr (std::is_same_v<String, std::string_view>)
+ eventArguments = {};
+ else
+ eventArguments.clear();
+ Internal::toArguments(eventArguments, std::forward<Arguments>(arguments)...);
}
} // namespace Internal
template<typename Key, typename Value>
-auto keyValue(Key &&key, Value &&value)
+auto keyValue(const Key &key, Value &&value)
{
- return std::forward_as_tuple(std::forward<Key>(key), std::forward<Value>(value));
+ if constexpr (std::is_lvalue_reference_v<Value>)
+ return std::tuple<const Key &, const Value &>(key, value);
+ else
+ return std::tuple<const Key &, std::decay_t<Value>>(key, value);
}
template<typename... Entries>
auto dictonary(Entries &&...entries)
{
- return std::forward_as_tuple(isDictonary, std::forward<Entries>(entries)...);
+ return std::make_tuple(isDictonary, std::forward<Entries>(entries)...);
}
template<typename... Entries>
auto array(Entries &&...entries)
{
- return std::forward_as_tuple(isArray, std::forward<Entries>(entries)...);
+ return std::make_tuple(isArray, std::forward<Entries>(entries)...);
}
-enum class IsFlow : std::size_t { No = 0, Out = 1 << 0, In = 1 << 1, InOut = In | Out };
+enum class IsFlow : char { No = 0, Out = 1 << 0, In = 1 << 1, InOut = In | Out };
inline bool operator&(IsFlow first, IsFlow second)
{
@@ -307,7 +375,7 @@ inline bool operator&(IsFlow first, IsFlow second)
}
template<typename String, typename ArgumentsString>
-struct TraceEvent
+struct alignas(4096) TraceEvent
{
using StringType = String;
using ArgumentType = std::conditional_t<std::is_same_v<String, std::string_view>, TracerLiteral, String>;
@@ -322,18 +390,18 @@ struct TraceEvent
String name;
String category;
- ArgumentsString arguments;
TimePoint time;
Duration duration;
std::size_t id = 0;
- std::size_t bindId : 62;
- IsFlow flow : 2;
+ std::size_t bindId = 0;
+ IsFlow flow = IsFlow::No;
char type = ' ';
+ ArgumentsString arguments;
};
using StringViewTraceEvent = TraceEvent<std::string_view, std::string_view>;
using StringViewWithStringArgumentsTraceEvent = TraceEvent<std::string_view, ArgumentsString>;
-using StringTraceEvent = TraceEvent<std::string, std::string>;
+using StringTraceEvent = TraceEvent<Utils::SmallString, ArgumentsString>;
enum class IsEnabled { No, Yes };
@@ -411,6 +479,8 @@ class EventQueue
{
public:
using IsActive = std::false_type;
+
+ template<typename TraceFile> EventQueue(TraceFile &) {}
};
namespace Internal {
@@ -421,7 +491,13 @@ class EventQueueTracker
using Queue = EventQueue<TraceEvent, Tracing::IsEnabled>;
public:
- EventQueueTracker() = default;
+ EventQueueTracker()
+ {
+ terminateHandler = std::get_terminate();
+
+ std::set_terminate([]() { EventQueueTracker::get().terminate(); });
+ }
+
EventQueueTracker(const EventQueueTracker &) = delete;
EventQueueTracker(EventQueueTracker &&) = delete;
EventQueueTracker &operator=(const EventQueueTracker &) = delete;
@@ -455,8 +531,25 @@ public:
}
private:
+ void terminate()
+ {
+ flushAll();
+ if (terminateHandler)
+ terminateHandler();
+ }
+
+ void flushAll()
+ {
+ std::lock_guard lock{mutex};
+
+ for (auto queue : queues)
+ queue->flush();
+ }
+
+private:
std::mutex mutex;
std::vector<Queue *> queues;
+ std::terminate_handler terminateHandler = nullptr;
};
} // namespace Internal
@@ -464,14 +557,17 @@ template<typename TraceEvent>
class EventQueue<TraceEvent, Tracing::IsEnabled>
{
using TraceEventsSpan = Utils::span<TraceEvent>;
+ using TraceEvents = std::array<TraceEvent, 1000>;
public:
using IsActive = std::true_type;
- EventQueue(EnabledTraceFile *file, TraceEventsSpan eventsOne, TraceEventsSpan eventsTwo);
+ EventQueue(EnabledTraceFile &file);
~EventQueue();
+ void setEventsSpans(TraceEventsSpan eventsOne, TraceEventsSpan eventsTwo);
+
void flush();
EventQueue(const EventQueue &) = delete;
@@ -479,7 +575,9 @@ public:
EventQueue &operator=(const EventQueue &) = delete;
EventQueue &operator=(EventQueue &&) = delete;
- EnabledTraceFile *file = nullptr;
+ EnabledTraceFile &file;
+ std::unique_ptr<TraceEvents> eventArrayOne = std::make_unique<TraceEvents>();
+ std::unique_ptr<TraceEvents> eventArrayTwo = std::make_unique<TraceEvents>();
TraceEventsSpan eventsOne;
TraceEventsSpan eventsTwo;
TraceEventsSpan currentEvents;
@@ -496,44 +594,10 @@ using StringViewWithStringArgumentsEventQueue = EventQueue<StringViewWithStringA
template<Tracing isEnabled>
using StringEventQueue = EventQueue<StringTraceEvent, isEnabled>;
-extern template class NANOTRACE_EXPORT EventQueue<StringViewTraceEvent, Tracing::IsEnabled>;
-extern template class NANOTRACE_EXPORT EventQueue<StringTraceEvent, Tracing::IsEnabled>;
-extern template class NANOTRACE_EXPORT EventQueue<StringViewWithStringArgumentsTraceEvent, Tracing::IsEnabled>;
-
-template<typename TraceEvent, std::size_t eventCount, Tracing isEnabled>
-class EventQueueData
-{
-public:
- using IsActive = std::true_type;
-
- EventQueueData(TraceFile<Tracing::IsDisabled> &) {}
-
- EventQueue<TraceEvent, Tracing::IsDisabled> createEventQueue() { return {}; }
-};
-
-template<typename TraceEvent, std::size_t eventCount>
-class EventQueueData<TraceEvent, eventCount, Tracing::IsEnabled>
-{
- using TraceEvents = std::array<TraceEvent, eventCount>;
-
-public:
- using IsActive = std::true_type;
-
- EventQueueData(EnabledTraceFile &file)
- : file{file}
- {}
-
- EventQueue<TraceEvent, Tracing::IsEnabled> createEventQueue()
- {
- return {&file, eventsOne, eventsTwo};
- }
-
- EnabledTraceFile &file;
- TraceEvents eventsOne;
- TraceEvents eventsTwo;
-};
-
-NANOTRACE_EXPORT EventQueue<StringTraceEvent, tracingStatus()> &globalEventQueue();
+extern template class NANOTRACE_EXPORT_EXTERN_TEMPLATE EventQueue<StringViewTraceEvent, Tracing::IsEnabled>;
+extern template class NANOTRACE_EXPORT_EXTERN_TEMPLATE EventQueue<StringTraceEvent, Tracing::IsEnabled>;
+extern template class NANOTRACE_EXPORT_EXTERN_TEMPLATE
+ EventQueue<StringViewWithStringArgumentsTraceEvent, Tracing::IsEnabled>;
template<typename TraceEvent>
TraceEvent &getTraceEvent(EnabledEventQueue<TraceEvent> &eventQueue)
@@ -574,6 +638,7 @@ public:
BasicEnabledToken &operator=(const BasicEnabledToken &) = default;
BasicEnabledToken(BasicEnabledToken &&other) noexcept = default;
BasicEnabledToken &operator=(BasicEnabledToken &&other) noexcept = default;
+
~BasicEnabledToken() {}
constexpr explicit operator bool() const { return false; }
@@ -672,7 +737,7 @@ public:
std::size_t bindId = 0;
if (m_id) {
- auto category = m_category();
+ auto &category = m_category();
bindId = category.createBindId();
category.begin('b', m_id, name, bindId, IsFlow::Out, std::forward<Arguments>(arguments)...);
}
@@ -727,101 +792,6 @@ using StringViewWithStringArgumentsCategory = Category<StringViewWithStringArgum
using DisabledToken = Token<StringViewCategory<Tracing::IsDisabled>, Tracing::IsDisabled>;
template<typename Category, Tracing isEnabled>
-class ObjectToken : public BasicDisabledToken
-{
-public:
- using ArgumentType = typename Category::ArgumentType;
-
- ObjectToken() = default;
- ObjectToken(const ObjectToken &) = delete;
- ObjectToken &operator=(const ObjectToken &) = delete;
- ObjectToken(ObjectToken &&other) noexcept = default;
- ObjectToken &operator=(ObjectToken &&other) noexcept = default;
- ~ObjectToken() = default;
-
- template<typename... Arguments>
- void change(ArgumentType, Arguments &&...)
- {}
-};
-
-template<typename Category>
-class ObjectToken<Category, Tracing::IsEnabled> : public BasicEnabledToken
-{
- using CategoryFunctionPointer = typename Category::CategoryFunctionPointer;
-
- ObjectToken(std::string_view name, std::size_t id, CategoryFunctionPointer category)
- : m_name{name}
- , m_id{id}
- , m_category{category}
- {}
-
-public:
- using StringType = typename Category::StringType;
- using ArgumentType = typename Category::ArgumentType;
-
- friend Category;
-
- ObjectToken(const ObjectToken &other)
- : m_name{other.m_name}
- , m_category{other.m_category}
- {
- if (other.m_id)
- m_id = m_category().beginObject(m_name).m_id;
- }
-
- ObjectToken &operator=(const ObjectToken &other)
- {
- if (this != &other) {
- ~ObjectToken();
- if (other.m_id) {
- m_category = other.m_category;
- m_name = other.m_name;
- m_id = other.m_category->beginObject(other.m_name).m_id;
- }
- }
- }
-
- ObjectToken(ObjectToken &&other) noexcept
- : m_name{std::move(other.m_name)}
- , m_id{std::exchange(other.m_id, 0)}
- , m_category{std::exchange(other.m_category, nullptr)}
- {}
-
- ObjectToken &operator=(ObjectToken &&other) noexcept
- {
- if (&other != this) {
- m_name = std::move(other.m_name);
- m_id = std::exchange(other.m_id, 0);
- m_category = std::exchange(other.m_category, nullptr);
- }
-
- return *this;
- }
-
- ~ObjectToken()
- {
- if (m_id)
- m_category().end('e', m_id, std::move(m_name));
-
- m_id = 0;
- }
-
- template<typename... Arguments>
- void change(ArgumentType name, Arguments &&...arguments)
- {
- if (m_id) {
- m_category().tick(
- 'n', m_id, std::move(name), 0, IsFlow::No, std::forward<Arguments>(arguments)...);
- }
- }
-
-private:
- StringType m_name;
- std::size_t m_id = 0;
- CategoryFunctionPointer m_category = nullptr;
-};
-
-template<typename Category, Tracing isEnabled>
class AsynchronousToken : public BasicDisabledToken
{
public:
@@ -847,6 +817,12 @@ public:
}
template<typename... Arguments>
+ [[nodiscard]] AsynchronousToken begin(const FlowTokenType &, ArgumentType, Arguments &&...)
+ {
+ return {};
+ }
+
+ template<typename... Arguments>
[[nodiscard]] std::pair<AsynchronousToken, FlowTokenType> beginWithFlow(ArgumentType,
Arguments &&...)
{
@@ -858,6 +834,10 @@ public:
{}
template<typename... Arguments>
+ void tick(const FlowTokenType &, ArgumentType, Arguments &&...)
+ {}
+
+ template<typename... Arguments>
FlowTokenType tickWithFlow(ArgumentType, Arguments &&...)
{
return {};
@@ -866,8 +846,9 @@ public:
template<typename... Arguments>
void end(Arguments &&...)
{}
-};
+ static constexpr bool categoryIsActive() { return Category::isActive(); }
+};
using DisabledAsynchronousToken = AsynchronousToken<StringViewCategory<Tracing::IsDisabled>,
Tracing::IsDisabled>;
@@ -938,19 +919,35 @@ public:
}
template<typename... Arguments>
+ [[nodiscard]] AsynchronousToken begin(const FlowTokenType &flowToken,
+ ArgumentType name,
+ Arguments &&...arguments)
+ {
+ if (m_id)
+ m_category().begin('b',
+ m_id,
+ name,
+ flowToken.bindId(),
+ IsFlow::In,
+ std::forward<Arguments>(arguments)...);
+
+ return AsynchronousToken{std::move(name), m_id, m_category};
+ }
+
+ template<typename... Arguments>
[[nodiscard]] std::pair<AsynchronousToken, FlowTokenType> beginWithFlow(ArgumentType name,
Arguments &&...arguments)
{
std::size_t bindId = 0;
if (m_id) {
- auto category = m_category();
+ auto &category = m_category();
bindId = category.createBindId();
category.begin('b', m_id, name, bindId, IsFlow::Out, std::forward<Arguments>(arguments)...);
}
return {std::piecewise_construct,
- std::forward_as_tuple(std::move(name), m_id, m_category),
+ std::forward_as_tuple(PrivateTag{}, std::move(name), m_id, m_category),
std::forward_as_tuple(PrivateTag{}, std::move(name), bindId, m_category)};
}
@@ -964,17 +961,30 @@ public:
}
template<typename... Arguments>
+ void tick(const FlowTokenType &flowToken, ArgumentType name, Arguments &&...arguments)
+ {
+ if (m_id) {
+ m_category().tick('n',
+ m_id,
+ std::move(name),
+ flowToken.bindId(),
+ IsFlow::In,
+ std::forward<Arguments>(arguments)...);
+ }
+ }
+
+ template<typename... Arguments>
FlowTokenType tickWithFlow(ArgumentType name, Arguments &&...arguments)
{
std::size_t bindId = 0;
if (m_id) {
- auto category = m_category();
+ auto &category = m_category();
bindId = category.createBindId();
category.tick('n', m_id, name, bindId, IsFlow::Out, std::forward<Arguments>(arguments)...);
}
- return {std::move(name), bindId, m_category};
+ return {PrivateTag{}, std::move(name), bindId, m_category};
}
template<typename... Arguments>
@@ -986,6 +996,8 @@ public:
m_id = 0;
}
+ static constexpr bool categoryIsActive() { return Category::isActive(); }
+
private:
StringType m_name;
std::size_t m_id = 0;
@@ -1087,9 +1099,9 @@ public:
std::size_t id = 0;
if (m_bindId) {
- auto category = m_category();
+ auto &category = m_category();
id = category.createId();
- category->begin('b', id, name, m_bindId, IsFlow::In, std::forward<Arguments>(arguments)...);
+ category.begin('b', id, name, m_bindId, IsFlow::In, std::forward<Arguments>(arguments)...);
}
return {std::move(name), id, m_category};
@@ -1103,10 +1115,10 @@ public:
std::size_t bindId = 0;
if (m_bindId) {
- auto category = m_category();
+ auto &category = m_category();
id = category.createId();
bindId = category.createBindId();
- category->begin('b', id, name, bindId, IsFlow::InOut, std::forward<Arguments>(arguments)...);
+ category.begin('b', id, name, bindId, IsFlow::InOut, std::forward<Arguments>(arguments)...);
}
return {std::piecewise_construct,
@@ -1153,6 +1165,8 @@ public:
m_category().tick('i', 0, name, m_bindId, IsFlow::In, std::forward<Arguments>(arguments)...);
}
+ std::size_t bindId() const { return m_bindId; }
+
private:
StringType m_name;
std::size_t m_bindId = 0;
@@ -1167,7 +1181,6 @@ public:
using ArgumentType = typename TraceEvent::ArgumentType;
using ArgumentsStringType = typename TraceEvent::ArgumentsStringType;
using AsynchronousTokenType = AsynchronousToken<Category, Tracing::IsDisabled>;
- using ObjectTokenType = ObjectToken<Category, Tracing::IsDisabled>;
using FlowTokenType = FlowToken<Category, Tracing::IsDisabled>;
using TracerType = Tracer<Category, std::false_type>;
using TokenType = Token<Category, Tracing::IsDisabled>;
@@ -1190,12 +1203,6 @@ public:
{}
template<typename... Arguments>
- [[nodiscard]] ObjectTokenType beginObject(ArgumentType, Arguments &&...)
- {
- return {};
- }
-
- template<typename... Arguments>
[[nodiscard]] TracerType beginDuration(ArgumentType, Arguments &&...)
{
return {};
@@ -1208,6 +1215,10 @@ public:
return std::pair<TracerType, FlowTokenType>();
}
+ template<typename... Arguments>
+ void threadEvent(ArgumentType, Arguments &&...)
+ {}
+
static constexpr bool isActive() { return false; }
};
@@ -1223,14 +1234,12 @@ public:
using ArgumentsStringType = typename TraceEvent::ArgumentsStringType;
using StringType = typename TraceEvent::StringType;
using AsynchronousTokenType = AsynchronousToken<Category, Tracing::IsEnabled>;
- using ObjectTokenType = ObjectToken<Category, Tracing::IsEnabled>;
using FlowTokenType = FlowToken<Category, Tracing::IsEnabled>;
using TracerType = Tracer<Category, std::true_type>;
using TokenType = Token<Category, Tracing::IsEnabled>;
using CategoryFunctionPointer = Category &(*) ();
friend AsynchronousTokenType;
- friend ObjectTokenType;
friend TokenType;
friend FlowTokenType;
friend TracerType;
@@ -1247,6 +1256,9 @@ public:
m_bindIdCounter = m_globalBindIdCounter += 1ULL << 32;
}
+ Category(const Category &) = delete;
+ Category &operator=(const Category &) = delete;
+
template<typename... Arguments>
[[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType traceName,
Arguments &&...arguments)
@@ -1273,16 +1285,6 @@ public:
}
template<typename... Arguments>
- [[nodiscard]] ObjectTokenType beginObject(ArgumentType traceName, Arguments &&...arguments)
- {
- std::size_t id = createId();
-
- begin('b', id, std::move(traceName), 0, IsFlow::No, std::forward<Arguments>(arguments)...);
-
- return {traceName, id, m_self};
- }
-
- template<typename... Arguments>
[[nodiscard]] TracerType beginDuration(ArgumentType traceName, Arguments &&...arguments)
{
return {traceName, m_self, std::forward<Arguments>(arguments)...};
@@ -1304,6 +1306,24 @@ public:
std::forward_as_tuple(PrivateTag{}, traceName, bindId, m_self)};
}
+ template<typename... Arguments>
+ void threadEvent(ArgumentType traceName, Arguments &&...arguments)
+ {
+ if (isEnabled == IsEnabled::No)
+ return;
+
+ auto &traceEvent = getTraceEvent(m_eventQueue);
+
+ traceEvent.time = Clock::now();
+ traceEvent.name = std::move(traceName);
+ traceEvent.category = traceName;
+ traceEvent.type = 'i';
+ traceEvent.id = 0;
+ traceEvent.bindId = 0;
+ traceEvent.flow = IsFlow::No;
+ Internal::setArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
+ }
+
EnabledEventQueue<TraceEvent> &eventQueue() const { return m_eventQueue; }
std::string_view name() const { return m_name; }
@@ -1337,7 +1357,7 @@ private:
traceEvent.id = id;
traceEvent.bindId = bindId;
traceEvent.flow = flow;
- Internal::appendArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
+ Internal::setArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
traceEvent.time = Clock::now();
}
@@ -1363,7 +1383,7 @@ private:
traceEvent.id = id;
traceEvent.bindId = bindId;
traceEvent.flow = flow;
- Internal::appendArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
+ Internal::setArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
}
template<typename... Arguments>
@@ -1383,9 +1403,11 @@ private:
traceEvent.id = id;
traceEvent.bindId = 0;
traceEvent.flow = IsFlow::No;
- Internal::appendArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
+ Internal::setArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
}
+ CategoryFunctionPointer self() { return m_self; }
+
private:
StringType m_name;
EnabledEventQueue<TraceEvent> &m_eventQueue;
@@ -1470,11 +1492,8 @@ class Tracer<Category, std::true_type>
, flow{flow}
, m_category{category}
{
- if (category().isEnabled == IsEnabled::Yes) {
- Internal::appendArguments<ArgumentsStringType>(m_arguments,
- std::forward<Arguments>(arguments)...);
- m_start = Clock::now();
- }
+ if (category().isEnabled == IsEnabled::Yes)
+ sendBeginTrace(std::forward<Arguments>(arguments)...);
}
public:
@@ -1493,13 +1512,15 @@ public:
: m_name{name}
, m_category{category}
{
- if (category().isEnabled == IsEnabled::Yes) {
- Internal::appendArguments<ArgumentsStringType>(m_arguments,
- std::forward<Arguments>(arguments)...);
- m_start = Clock::now();
- }
+ if (category().isEnabled == IsEnabled::Yes)
+ sendBeginTrace(std::forward<Arguments>(arguments)...);
}
+ template<typename... Arguments>
+ [[nodiscard]] Tracer(ArgumentType name, Category &category, Arguments &&...arguments)
+ : Tracer(std::move(name), category.self(), std::forward<Arguments>(arguments)...)
+ {}
+
Tracer(const Tracer &) = delete;
Tracer &operator=(const Tracer &) = delete;
Tracer(Tracer &&other) noexcept = delete;
@@ -1507,7 +1528,7 @@ public:
TokenType createToken() { return {0, m_category}; }
- ~Tracer() { sendTrace(); }
+ ~Tracer() { sendEndTrace(); }
template<typename... Arguments>
Tracer beginDuration(ArgumentType name, Arguments &&...arguments)
@@ -1524,44 +1545,52 @@ public:
template<typename... Arguments>
void end(Arguments &&...arguments)
{
- sendTrace(std::forward<Arguments>(arguments)...);
+ sendEndTrace(std::forward<Arguments>(arguments)...);
m_name = {};
}
private:
template<typename... Arguments>
- void sendTrace(Arguments &&...arguments)
+ void sendBeginTrace(Arguments &&...arguments)
+ {
+ auto &category = m_category();
+ if (category.isEnabled == IsEnabled::Yes) {
+ auto &traceEvent = getTraceEvent(category.eventQueue());
+ traceEvent.name = m_name;
+ traceEvent.category = category.name();
+ traceEvent.bindId = m_bindId;
+ traceEvent.flow = flow;
+ traceEvent.type = 'B';
+ Internal::setArguments<ArgumentsStringType>(traceEvent.arguments,
+ std::forward<Arguments>(arguments)...);
+ traceEvent.time = Clock::now();
+ }
+ }
+
+ template<typename... Arguments>
+ void sendEndTrace(Arguments &&...arguments)
{
if (m_name.size()) {
- auto category = m_category();
+ auto &category = m_category();
if (category.isEnabled == IsEnabled::Yes) {
- auto duration = Clock::now() - m_start;
+ auto end = Clock::now();
auto &traceEvent = getTraceEvent(category.eventQueue());
- traceEvent.name = m_name;
+ traceEvent.name = std::move(m_name);
traceEvent.category = category.name();
- traceEvent.time = m_start;
- traceEvent.duration = duration;
+ traceEvent.time = end;
traceEvent.bindId = m_bindId;
traceEvent.flow = flow;
- traceEvent.type = 'X';
- if (sizeof...(arguments)) {
- m_arguments.clear();
- Internal::appendArguments<ArgumentsStringType>(traceEvent.arguments,
- std::forward<Arguments>(
- arguments)...);
- } else {
- traceEvent.arguments = m_arguments;
- }
+ traceEvent.type = 'E';
+ Internal::setArguments<ArgumentsStringType>(traceEvent.arguments,
+ std::forward<Arguments>(arguments)...);
}
}
}
private:
- TimePoint m_start;
StringType m_name;
- ArgumentsStringType m_arguments;
- std::size_t m_bindId;
- IsFlow flow;
+ std::size_t m_bindId = 0;
+ IsFlow flow = IsFlow::No;
CategoryFunctionPointer m_category;
};
@@ -1569,51 +1598,4 @@ template<typename Category, typename... Arguments>
Tracer(typename Category::ArgumentType name, Category &category, Arguments &&...)
-> Tracer<Category, typename Category::IsActive>;
-#ifdef NANOTRACEHR_ENABLED
-class GlobalTracer
-{
-public:
- template<typename... Arguments>
- [[nodiscard]] GlobalTracer(std::string name, std::string category, Arguments &&...arguments)
- : m_name{std::move(name)}
- , m_category{std::move(category)}
- {
- if (globalEventQueue().isEnabled == IsEnabled::Yes) {
- Internal::appendArguments(m_arguments, std::forward<Arguments>(arguments)...);
- m_start = Clock::now();
- }
- }
-
- ~GlobalTracer()
- {
- if (globalEventQueue().isEnabled == IsEnabled::Yes) {
- auto duration = Clock::now() - m_start;
- auto &traceEvent = getTraceEvent(globalEventQueue());
- traceEvent.name = std::move(m_name);
- traceEvent.category = std::move(m_category);
- traceEvent.arguments = std::move(m_arguments);
- traceEvent.time = std::move(m_start);
- traceEvent.duration = std::move(duration);
- traceEvent.type = 'X';
- }
- }
-
-private:
- TimePoint m_start;
- std::string m_name;
- std::string m_category;
- std::string m_arguments;
-};
-#else
-class GlobalTracer
-{
-public:
- GlobalTracer(std::string_view, std::string_view, std::string_view) {}
-
- GlobalTracer(std::string_view, std::string_view) {}
-
- ~GlobalTracer() {}
-};
-#endif
-
} // namespace NanotraceHR
diff --git a/src/libs/nanotrace/staticstring.h b/src/libs/nanotrace/staticstring.h
new file mode 100644
index 0000000000..d787bd2fe3
--- /dev/null
+++ b/src/libs/nanotrace/staticstring.h
@@ -0,0 +1,116 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include <utils/smallstringview.h>
+
+#if !(defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L))
+# include <QLocale>
+#endif
+
+#include <array>
+#include <charconv>
+#include <limits>
+
+namespace NanotraceHR {
+
+template<std::size_t Capacity>
+class StaticString
+{
+public:
+ StaticString() = default;
+ StaticString(const StaticString &) = delete;
+ StaticString &operator=(const StaticString &) = delete;
+
+ char *data() { return m_data.data(); }
+
+ const char *data() const { return m_data.data(); }
+
+ void append(Utils::SmallStringView string) noexcept
+ {
+ auto newSize = m_size + string.size();
+
+ if (newSize <= Capacity) {
+ std::char_traits<char>::copy(std::next(data(), static_cast<std::ptrdiff_t>(m_size)),
+ string.data(),
+ string.size());
+ m_size = newSize;
+ } else {
+ m_size = Capacity + 1;
+ }
+ }
+
+ void append(char character) noexcept
+ {
+ auto newSize = m_size + 1;
+
+ if (newSize <= Capacity) {
+ auto current = std::next(data(), static_cast<std::ptrdiff_t>(m_size));
+ *current = character;
+
+ m_size = newSize;
+ } else {
+ m_size = Capacity + 1;
+ }
+ }
+
+ template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>, bool> = true>
+ void append(Type number)
+ {
+#if !(defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L))
+ if constexpr (std::is_floating_point_v<Type>) {
+ QLocale locale{QLocale::Language::C};
+ append(locale.toString(number).toStdString());
+ return;
+ }
+#endif
+ // 2 bytes for the sign and because digits10 returns the floor
+ char buffer[std::numeric_limits<Type>::digits10 + 2];
+ auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
+ auto endOfConversionString = result.ptr;
+
+ append({buffer, endOfConversionString});
+ }
+
+ void pop_back() { --m_size; }
+
+ StaticString &operator+=(Utils::SmallStringView string) noexcept
+ {
+ append(string);
+
+ return *this;
+ }
+
+ StaticString &operator+=(char character) noexcept
+ {
+ append(character);
+
+ return *this;
+ }
+
+ template<typename Type, typename = std::enable_if_t<std::is_arithmetic_v<Type>>>
+ StaticString &operator+=(Type number) noexcept
+ {
+ append(number);
+
+ return *this;
+ }
+
+ bool isValid() const { return m_size <= Capacity; }
+
+ std::size_t size() const { return m_size; }
+
+ friend std::ostream &operator<<(std::ostream &out, const StaticString &text)
+ {
+ return out << std::string_view{text.data(), text.size()};
+ }
+
+ void clear() { m_size = 0; }
+
+private:
+ std::array<char, Capacity> m_data;
+ std::size_t m_size = 0;
+};
+
+} // namespace NanotraceHR
diff --git a/src/libs/qmljs/CMakeLists.txt b/src/libs/qmljs/CMakeLists.txt
index 4ca15cf7cc..a4966a8b4a 100644
--- a/src/libs/qmljs/CMakeLists.txt
+++ b/src/libs/qmljs/CMakeLists.txt
@@ -33,7 +33,6 @@ add_qtc_library(QmlJS
qmljsfindexportedcpptypes.cpp qmljsfindexportedcpptypes.h
qmljsicons.cpp qmljsicons.h
qmljsimportdependencies.cpp qmljsimportdependencies.h
- qmljsindenter.cpp qmljsindenter.h
qmljsinterpreter.cpp qmljsinterpreter.h
qmljslineinfo.cpp qmljslineinfo.h
qmljslink.cpp qmljslink.h
diff --git a/src/libs/qmljs/qmljs.qbs b/src/libs/qmljs/qmljs.qbs
index 18434c83ba..d5cc21faf1 100644
--- a/src/libs/qmljs/qmljs.qbs
+++ b/src/libs/qmljs/qmljs.qbs
@@ -28,7 +28,6 @@ QtcLibrary {
"qmljsfindexportedcpptypes.cpp", "qmljsfindexportedcpptypes.h",
"qmljsicons.cpp", "qmljsicons.h",
"qmljsimportdependencies.cpp", "qmljsimportdependencies.h",
- "qmljsindenter.cpp", "qmljsindenter.h",
"qmljsinterpreter.cpp", "qmljsinterpreter.h",
"qmljsdialect.cpp", "qmljsdialect.h",
"qmljslineinfo.cpp", "qmljslineinfo.h",
diff --git a/src/libs/qmljs/qmljsindenter.cpp b/src/libs/qmljs/qmljsindenter.cpp
deleted file mode 100644
index 8055847d90..0000000000
--- a/src/libs/qmljs/qmljsindenter.cpp
+++ /dev/null
@@ -1,603 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-/*
- This file is a self-contained interactive indenter for Qt Script.
-
- The general problem of indenting a program is ill posed. On
- the one hand, an indenter has to analyze programs written in a
- free-form formal language that is best described in terms of
- tokens, not characters, not lines. On the other hand, indentation
- applies to lines and white space characters matter, and otherwise
- the programs to indent are formally invalid in general, as they
- are begin edited.
-
- The approach taken here works line by line. We receive a program
- consisting of N lines or more, and we want to compute the
- indentation appropriate for the Nth line. Lines beyond the Nth
- lines are of no concern to us, so for simplicity we pretend the
- program has exactly N lines and we call the Nth line the "bottom
- line". Typically, we have to indent the bottom line when it's
- still empty, so we concentrate our analysis on the N - 1 lines
- that precede.
-
- By inspecting the (N - 1)-th line, the (N - 2)-th line, ...
- backwards, we determine the kind of the bottom line and indent it
- accordingly.
-
- * The bottom line is a comment line. See
- bottomLineStartsInCComment() and
- indentWhenBottomLineStartsInCComment().
- * The bottom line is a continuation line. See isContinuationLine()
- and indentForContinuationLine().
- * The bottom line is a standalone line. See
- indentForStandaloneLine().
-
- Certain tokens that influence the indentation, notably braces,
- are looked for in the lines. This is done by simple string
- comparison, without a real tokenizer. Confusing constructs such
- as comments and string literals are removed beforehand.
-*/
-
-#include <qmljs/qmljsindenter.h>
-#include <qmljs/qmljsscanner.h>
-
-#include <QTextBlock>
-
-using namespace QmlJS;
-
-/*
- Saves and restores the state of the global linizer. This enables
- backtracking.
-
- Identical to the defines in qmljslineinfo.cpp
-*/
-#define YY_SAVE() LinizerState savedState = yyLinizerState
-#define YY_RESTORE() yyLinizerState = savedState
-
-
-QmlJSIndenter::QmlJSIndenter()
- : caseOrDefault(QRegularExpression(QLatin1String(
- "^\\s*(?:"
- "case\\b[^:]+|"
- "default)"
- "\\s*:.*$")))
-
-{
-
- /*
- The indenter supports a few parameters:
-
- * ppHardwareTabSize is the size of a '\t' in your favorite editor.
- * ppIndentSize is the size of an indentation, or software tab
- size.
- * ppContinuationIndentSize is the extra indent for a continuation
- line, when there is nothing to align against on the previous
- line.
- * ppCommentOffset is the indentation within a C-style comment,
- when it cannot be picked up.
- */
-
- ppHardwareTabSize = 8;
- ppIndentSize = 4;
- ppContinuationIndentSize = 8;
- ppCommentOffset = 2;
-}
-
-QmlJSIndenter::~QmlJSIndenter()
-{
-}
-
-void QmlJSIndenter::setTabSize(int size)
-{
- ppHardwareTabSize = size;
-}
-
-void QmlJSIndenter::setIndentSize(int size)
-{
- ppIndentSize = size;
- ppContinuationIndentSize = 2 * size;
-}
-
-/*
- Returns true if string t is made only of white space; otherwise
- returns false.
-*/
-bool QmlJSIndenter::isOnlyWhiteSpace(const QString &t) const
-{
- return firstNonWhiteSpace(t).isNull();
-}
-
-/*
- Assuming string t is a line, returns the column number of a given
- index. Column numbers and index are identical for strings that don't
- contain '\t's.
-*/
-int QmlJSIndenter::columnForIndex(const QString &t, int index) const
-{
- int col = 0;
- if (index > t.length())
- index = t.length();
-
- for (int i = 0; i < index; i++) {
- if (t.at(i) == QLatin1Char('\t'))
- col = ((col / ppHardwareTabSize) + 1) * ppHardwareTabSize;
- else
- col++;
- }
- return col;
-}
-
-/*
- Returns the indentation size of string t.
-*/
-int QmlJSIndenter::indentOfLine(const QString &t) const
-{
- return columnForIndex(t, t.indexOf(firstNonWhiteSpace(t)));
-}
-
-/*
- Replaces t[k] by ch, unless t[k] is '\t'. Tab characters are better
- left alone since they break the "index equals column" rule. No
- provisions are taken against '\n' or '\r', which shouldn't occur in
- t anyway.
-*/
-void QmlJSIndenter::eraseChar(QString &t, int k, QChar ch) const
-{
- if (t.at(k) != QLatin1Char('\t'))
- t[k] = ch;
-}
-
-/*
- Returns '(' if the last parenthesis is opening, ')' if it is
- closing, and QChar() if there are no parentheses in t.
-*/
-QChar QmlJSIndenter::lastParen() const
-{
- for (int index = yyLinizerState.tokens.size() - 1; index != -1; --index) {
- const Token &token = yyLinizerState.tokens.at(index);
-
- if (token.is(Token::LeftParenthesis))
- return QLatin1Char('(');
-
- else if (token.is(Token::RightParenthesis))
- return QLatin1Char(')');
- }
-
- return QChar();
-}
-
-/*
- Returns true if typedIn the same as okayCh or is null; otherwise
- returns false.
-*/
-bool QmlJSIndenter::okay(QChar typedIn, QChar okayCh) const
-{
- return typedIn == QChar() || typedIn == okayCh;
-}
-
-/*
- Returns the recommended indent for the bottom line of yyProgram
- assuming that it starts in a C-style comment, a condition that is
- tested elsewhere.
-
- Essentially, we're trying to align against some text on the
- previous line.
-*/
-int QmlJSIndenter::indentWhenBottomLineStartsInMultiLineComment()
-{
- QTextBlock block = yyProgram.lastBlock().previous();
- QString blockText;
-
- for (; block.isValid(); block = block.previous()) {
- blockText = block.text();
-
- if (! isOnlyWhiteSpace(blockText))
- break;
- }
-
- return indentOfLine(blockText);
-}
-
-/*
- Returns the recommended indent for the bottom line of yyProgram,
- assuming it's a continuation line.
-
- We're trying to align the continuation line against some parenthesis
- or other bracked left opened on a previous line, or some interesting
- operator such as '='.
-*/
-int QmlJSIndenter::indentForContinuationLine()
-{
- int braceDepth = 0;
- int delimDepth = 0;
-
- bool leftBraceFollowed = *yyLeftBraceFollows;
-
- for (int i = 0; i < SmallRoof; i++) {
- int hook = -1;
-
- int j = yyLine->length();
- while (j > 0 && hook < 0) {
- j--;
- QChar ch = yyLine->at(j);
-
- switch (ch.unicode()) {
- case ')':
- delimDepth++;
- break;
- case ']':
- braceDepth++;
- break;
- case '}':
- braceDepth++;
- break;
- case '(':
- delimDepth--;
- /*
- An unclosed delimiter is a good place to align at,
- at least for some styles (including Qt's).
- */
- if (delimDepth == -1)
- hook = j;
- break;
-
- case '[':
- braceDepth--;
- /*
- An unclosed delimiter is a good place to align at,
- at least for some styles (including Qt's).
- */
- if (braceDepth == -1)
- hook = j;
- break;
- case '{':
- braceDepth--;
- /*
- A left brace followed by other stuff on the same
- line is typically for an enum or an initializer.
- Such a brace must be treated just like the other
- delimiters.
- */
- if (braceDepth == -1) {
- if (j < yyLine->length() - 1)
- hook = j;
- else
- return 0; // shouldn't happen
- }
- break;
- case '=':
- /*
- An equal sign is a very natural alignment hook
- because it's usually the operator with the lowest
- precedence in statements it appears in. Case in
- point:
-
- int x = 1 +
- 2;
-
- However, we have to beware of constructs such as
- default arguments and explicit enum constant
- values:
-
- void foo(int x = 0,
- int y = 0);
-
- And not
-
- void foo(int x = 0,
- int y = 0);
-
- These constructs are caracterized by a ',' at the
- end of the unfinished lines or by unbalanced
- parentheses.
- */
- Q_ASSERT(j - 1 >= 0);
-
- if (QString::fromLatin1("!=<>").indexOf(yyLine->at(j - 1)) == -1 &&
- j + 1 < yyLine->length() && yyLine->at(j + 1) != QLatin1Char('=')) {
- if (braceDepth == 0 && delimDepth == 0 &&
- j < yyLine->length() - 1 &&
- !yyLine->endsWith(QLatin1Char(',')) &&
- (yyLine->contains(QLatin1Char('(')) == yyLine->contains(QLatin1Char(')'))))
- hook = j;
- }
- }
- }
-
- if (hook >= 0) {
- /*
- Yes, we have a delimiter or an operator to align
- against! We don't really align against it, but rather
- against the following token, if any. In this example,
- the following token is "11":
-
- int x = (11 +
- 2);
-
- If there is no such token, we use a continuation indent:
-
- static QRegExp foo(QString(
- "foo foo foo foo foo foo foo foo foo"));
- */
- hook++;
- while (hook < yyLine->length()) {
- if (!yyLine->at(hook).isSpace())
- return columnForIndex(*yyLine, hook);
- hook++;
- }
- return indentOfLine(*yyLine) + ppContinuationIndentSize;
- }
-
- if (braceDepth != 0)
- break;
-
- /*
- The line's delimiters are balanced. It looks like a
- continuation line or something.
- */
- if (delimDepth == 0) {
- if (leftBraceFollowed) {
- /*
- We have
-
- int main()
- {
-
- or
-
- Bar::Bar()
- : Foo(x)
- {
-
- The "{" should be flush left.
- */
- if (!isContinuationLine())
- return indentOfLine(*yyLine);
- } else if (isContinuationLine() || yyLine->endsWith(QLatin1Char(','))) {
- /*
- We have
-
- x = a +
- b +
- c;
-
- or
-
- int t[] = {
- 1, 2, 3,
- 4, 5, 6
-
- The "c;" should fall right under the "b +", and the
- "4, 5, 6" right under the "1, 2, 3,".
- */
- return indentOfLine(*yyLine);
- } else {
- /*
- We have
-
- stream << 1 +
- 2;
-
- We could, but we don't, try to analyze which
- operator has precedence over which and so on, to
- obtain the excellent result
-
- stream << 1 +
- 2;
-
- We do have a special trick above for the assignment
- operator above, though.
- */
- return indentOfLine(*yyLine) + ppContinuationIndentSize;
- }
- }
-
- if (!readLine())
- break;
- }
- return 0;
-}
-
-/*
- Returns the recommended indent for the bottom line of yyProgram if
- that line is standalone (or should be indented likewise).
-
- Indenting a standalone line is tricky, mostly because of braceless
- control statements. Grossly, we are looking backwards for a special
- line, a "hook line", that we can use as a starting point to indent,
- and then modify the indentation level according to the braces met
- along the way to that hook.
-
- Let's consider a few examples. In all cases, we want to indent the
- bottom line.
-
- Example 1:
-
- x = 1;
- y = 2;
-
- The hook line is "x = 1;". We met 0 opening braces and 0 closing
- braces. Therefore, "y = 2;" inherits the indent of "x = 1;".
-
- Example 2:
-
- if (x) {
- y;
-
- The hook line is "if (x) {". No matter what precedes it, "y;" has
- to be indented one level deeper than the hook line, since we met one
- opening brace along the way.
-
- Example 3:
-
- if (a)
- while (b) {
- c;
- }
- d;
-
- To indent "d;" correctly, we have to go as far as the "if (a)".
- Compare with
-
- if (a) {
- while (b) {
- c;
- }
- d;
-
- Still, we're striving to go back as little as possible to
- accommodate people with irregular indentation schemes. A hook line
- near at hand is much more reliable than a remote one.
-*/
-int QmlJSIndenter::indentForStandaloneLine()
-{
- for (int i = 0; i < SmallRoof; i++) {
- if (!*yyLeftBraceFollows) {
- YY_SAVE();
-
- if (matchBracelessControlStatement()) {
- /*
- The situation is this, and we want to indent "z;":
-
- if (x &&
- y)
- z;
-
- yyLine is "if (x &&".
- */
- return indentOfLine(*yyLine) + ppIndentSize;
- }
- YY_RESTORE();
- }
-
- if (yyLine->endsWith(QLatin1Char(';')) || yyLine->contains(QLatin1Char('{'))) {
- /*
- The situation is possibly this, and we want to indent
- "z;":
-
- while (x)
- y;
- z;
-
- We return the indent of "while (x)". In place of "y;",
- any arbitrarily complex compound statement can appear.
- */
-
- if (*yyBraceDepth > 0) {
- do {
- if (!readLine())
- break;
- } while (*yyBraceDepth > 0);
- }
-
- LinizerState hookState;
-
- while (isContinuationLine())
- readLine();
- hookState = yyLinizerState;
-
- readLine();
- if (*yyBraceDepth <= 0) {
- do {
- if (!matchBracelessControlStatement())
- break;
- hookState = yyLinizerState;
- } while (readLine());
- }
-
- yyLinizerState = hookState;
-
- while (isContinuationLine())
- readLine();
-
- int indentChange = - *yyBraceDepth;
- if (caseOrDefault.match(*yyLine).hasMatch())
- ++indentChange;
-
- /*
- Never trust lines containing only '{' or '}', as some
- people (Richard M. Stallman) format them weirdly.
- */
- if (yyLine->trimmed().length() > 1)
- return indentOfLine(*yyLine) + indentChange * ppIndentSize;
- }
-
- if (!readLine())
- return -*yyBraceDepth * ppIndentSize;
- }
- return 0;
-}
-
-/*
- Returns the recommended indent for the bottom line of program.
- Unless null, typedIn stores the character of yyProgram that
- triggered reindentation.
-
- This function works better if typedIn is set properly; it is
- slightly more conservative if typedIn is completely wild, and
- slighly more liberal if typedIn is always null. The user might be
- annoyed by the liberal behavior.
-*/
-int QmlJSIndenter::indentForBottomLine(QTextBlock begin, QTextBlock end, QChar typedIn)
-{
- if (begin == end)
- return 0;
-
- const QTextBlock last = end.previous();
-
- initialize(begin, last);
-
- QString bottomLine = last.text();
- QChar firstCh = firstNonWhiteSpace(bottomLine);
- int indent = 0;
-
- if (bottomLineStartsInMultilineComment()) {
- /*
- The bottom line starts in a C-style comment. Indent it
- smartly, unless the user has already played around with it,
- in which case it's better to leave her stuff alone.
- */
- if (isOnlyWhiteSpace(bottomLine))
- indent = indentWhenBottomLineStartsInMultiLineComment();
- else
- indent = indentOfLine(bottomLine);
- } else {
- if (isUnfinishedLine())
- indent = indentForContinuationLine();
- else
- indent = indentForStandaloneLine();
-
- if ((okay(typedIn, QLatin1Char('}')) && firstCh == QLatin1Char('}'))
- || (okay(typedIn, QLatin1Char(']')) && firstCh == QLatin1Char(']'))) {
- /*
- A closing brace is one level more to the left than the
- code it follows.
- */
- indent -= ppIndentSize;
- } else if (okay(typedIn, QLatin1Char(':'))) {
- if (caseOrDefault.match(bottomLine).hasMatch()) {
- /*
- Move a case label (or the ':' in front of a
- constructor initialization list) one level to the
- left, but only if the user did not play around with
- it yet. Some users have exotic tastes in the
- matter, and most users probably are not patient
- enough to wait for the final ':' to format their
- code properly.
-
- We don't attempt the same for goto labels, as the
- user is probably the middle of "foo::bar". (Who
- uses goto, anyway?)
- */
- if (indentOfLine(bottomLine) <= indent)
- indent -= ppIndentSize;
- else
- indent = indentOfLine(bottomLine);
- }
- }
- }
-
- return qMax(0, indent);
-}
-
diff --git a/src/libs/qmljs/qmljsindenter.h b/src/libs/qmljs/qmljsindenter.h
deleted file mode 100644
index 55141c1b3b..0000000000
--- a/src/libs/qmljs/qmljsindenter.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <qmljs/qmljs_global.h>
-#include <qmljs/qmljslineinfo.h>
-
-#include <QRegularExpression>
-
-QT_FORWARD_DECLARE_CLASS(QTextBlock)
-
-namespace QmlJS {
-
-class QMLJS_EXPORT QmlJSIndenter : public LineInfo
-{
- Q_DISABLE_COPY(QmlJSIndenter)
-
-public:
- QmlJSIndenter();
- ~QmlJSIndenter();
-
- void setTabSize(int size);
- void setIndentSize(int size);
-
- int indentForBottomLine(QTextBlock firstBlock, QTextBlock lastBlock, QChar typedIn);
-
-private:
- bool isOnlyWhiteSpace(const QString &t) const;
- int columnForIndex(const QString &t, int index) const;
- int indentOfLine(const QString &t) const;
-
- void eraseChar(QString &t, int k, QChar ch) const;
- QChar lastParen() const;
- bool okay(QChar typedIn, QChar okayCh) const;
-
- int indentWhenBottomLineStartsInMultiLineComment();
- int indentForContinuationLine();
- int indentForStandaloneLine();
-
-private:
- int ppHardwareTabSize;
- int ppIndentSize;
- int ppContinuationIndentSize;
- int ppCommentOffset;
-
-private:
- QRegularExpression caseOrDefault;
-};
-
-} // namespace QmlJS
diff --git a/src/libs/qmljs/qmljsplugindumper.cpp b/src/libs/qmljs/qmljsplugindumper.cpp
index 36f2cd802d..653fa8a701 100644
--- a/src/libs/qmljs/qmljsplugindumper.cpp
+++ b/src/libs/qmljs/qmljsplugindumper.cpp
@@ -13,7 +13,7 @@
#include <utils/filesystemwatcher.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QDir>
#include <QDirIterator>
diff --git a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h
index d5a715f1d8..a3012c99f5 100644
--- a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h
+++ b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h
@@ -46,10 +46,15 @@ enum class View3DActionType {
ParticlesSeek,
SyncEnvBackground,
GetNodeAtPos,
+ GetNodeAtMainScenePos,
SetBakeLightsView3D,
SplitViewToggle,
MaterialOverride,
- ShowWireframe
+ ShowWireframe,
+ FlyModeToggle,
+ EditCameraRotation,
+ EditCameraMove,
+ EditCameraStopAllMoves
};
constexpr bool isNanotraceEnabled()
diff --git a/src/libs/qtcreatorcdbext/CMakeLists.txt b/src/libs/qtcreatorcdbext/CMakeLists.txt
index 4359d79d54..47eca2cd16 100644
--- a/src/libs/qtcreatorcdbext/CMakeLists.txt
+++ b/src/libs/qtcreatorcdbext/CMakeLists.txt
@@ -4,7 +4,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake")
project(qtcreatorcdbext)
-set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
diff --git a/src/libs/sqlite/CMakeLists.txt b/src/libs/sqlite/CMakeLists.txt
index 2c7e1ebbf3..f86f31871a 100644
--- a/src/libs/sqlite/CMakeLists.txt
+++ b/src/libs/sqlite/CMakeLists.txt
@@ -31,7 +31,7 @@ endif()
add_qtc_library(Sqlite
PROPERTIES AUTOMOC OFF AUTOUIC OFF
- DEPENDS Qt::Core Threads::Threads ${CMAKE_DL_LIBS} SqliteInternal
+ DEPENDS Qt::Core Threads::Threads ${CMAKE_DL_LIBS} SqliteInternal Nanotrace
INCLUDES
../3rdparty/sqlite
PUBLIC_INCLUDES
@@ -58,7 +58,7 @@ add_qtc_library(Sqlite
sqlitesessionchangeset.cpp sqlitesessionchangeset.h
sqlitesessions.cpp sqlitesessions.h
sqlitetable.h
- sqlitetransaction.h
+ sqlitetracing.cpp sqlitetracing.h
sqlitetransaction.h
sqlitevalue.h
sqlitewritestatement.h
diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp
index 406d49d248..8557bf6ad2 100644
--- a/src/libs/sqlite/sqlitebasestatement.cpp
+++ b/src/libs/sqlite/sqlitebasestatement.cpp
@@ -26,33 +26,7 @@ extern "C" int sqlite3_carray_bind(
namespace Sqlite {
-namespace {
-using TraceFile = NanotraceHR::TraceFile<sqliteTracingStatus()>;
-
-TraceFile traceFile{"sqlite.json"};
-
-thread_local NanotraceHR::EventQueueData<NanotraceHR::StringViewTraceEvent, 10000, sqliteTracingStatus()>
- eventQueueData(traceFile);
-thread_local NanotraceHR::EventQueue eventQueue = eventQueueData.createEventQueue();
-
-NanotraceHR::StringViewCategory<sqliteTracingStatus()> &sqliteLowLevelCategory();
-
-thread_local NanotraceHR::StringViewCategory<sqliteTracingStatus()> sqliteLowLevelCategory_{
- "sqlite low level"_t, eventQueue, sqliteLowLevelCategory};
-
-NanotraceHR::StringViewCategory<sqliteTracingStatus()> &sqliteLowLevelCategory()
-{
- return sqliteLowLevelCategory_;
-}
-
-thread_local NanotraceHR::StringViewCategory<sqliteTracingStatus()> sqliteHighLevelCategory_{
- "sqlite high level"_t, eventQueue, sqliteHighLevelCategory};
-} // namespace
-
-NanotraceHR::StringViewCategory<sqliteTracingStatus()> &sqliteHighLevelCategory()
-{
- return sqliteHighLevelCategory_;
-}
+using NanotraceHR::keyValue;
BaseStatement::BaseStatement(Utils::SmallStringView sqlStatement, Database &database)
: m_database(database)
@@ -108,14 +82,18 @@ void BaseStatement::waitForUnlockNotify() const
void BaseStatement::reset() const noexcept
{
- NanotraceHR::Tracer tracer{"reset"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"reset"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle())};
sqlite3_reset(m_compiledStatement.get());
}
bool BaseStatement::next() const
{
- NanotraceHR::Tracer tracer{"next"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"next"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle())};
int resultCode;
do {
@@ -142,7 +120,10 @@ void BaseStatement::step() const
void BaseStatement::bindNull(int index)
{
- NanotraceHR::Tracer tracer{"bind null"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind null"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index)};
int resultCode = sqlite3_bind_null(m_compiledStatement.get(), index);
if (resultCode != SQLITE_OK)
@@ -156,7 +137,11 @@ void BaseStatement::bind(int index, NullValue)
void BaseStatement::bind(int index, int value)
{
- NanotraceHR::Tracer tracer{"bind int"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind int"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("value", value)};
int resultCode = sqlite3_bind_int(m_compiledStatement.get(), index, value);
if (resultCode != SQLITE_OK)
@@ -165,7 +150,11 @@ void BaseStatement::bind(int index, int value)
void BaseStatement::bind(int index, long long value)
{
- NanotraceHR::Tracer tracer{"bind long long"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind long long"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("value", value)};
int resultCode = sqlite3_bind_int64(m_compiledStatement.get(), index, value);
if (resultCode != SQLITE_OK)
@@ -174,7 +163,11 @@ void BaseStatement::bind(int index, long long value)
void BaseStatement::bind(int index, double value)
{
- NanotraceHR::Tracer tracer{"bind double"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind double"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("value", value)};
int resultCode = sqlite3_bind_double(m_compiledStatement.get(), index, value);
if (resultCode != SQLITE_OK)
@@ -183,7 +176,11 @@ void BaseStatement::bind(int index, double value)
void BaseStatement::bind(int index, void *pointer)
{
- NanotraceHR::Tracer tracer{"bind pointer"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind pointer"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(pointer))};
int resultCode = sqlite3_bind_pointer(m_compiledStatement.get(), index, pointer, "carray", nullptr);
if (resultCode != SQLITE_OK)
@@ -192,7 +189,12 @@ void BaseStatement::bind(int index, void *pointer)
void BaseStatement::bind(int index, Utils::span<const int> values)
{
- NanotraceHR::Tracer tracer{"bind int span"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind int span"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(values.data())),
+ keyValue("size", values.size())};
int resultCode = sqlite3_carray_bind(m_compiledStatement.get(),
index,
@@ -206,7 +208,12 @@ void BaseStatement::bind(int index, Utils::span<const int> values)
void BaseStatement::bind(int index, Utils::span<const long long> values)
{
- NanotraceHR::Tracer tracer{"bind long long span"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind long long span"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(values.data())),
+ keyValue("size", values.size())};
int resultCode = sqlite3_carray_bind(m_compiledStatement.get(),
index,
@@ -220,7 +227,12 @@ void BaseStatement::bind(int index, Utils::span<const long long> values)
void BaseStatement::bind(int index, Utils::span<const double> values)
{
- NanotraceHR::Tracer tracer{"bind double span"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind double span"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(values.data())),
+ keyValue("size", values.size())};
int resultCode = sqlite3_carray_bind(m_compiledStatement.get(),
index,
@@ -234,7 +246,12 @@ void BaseStatement::bind(int index, Utils::span<const double> values)
void BaseStatement::bind(int index, Utils::span<const char *> values)
{
- NanotraceHR::Tracer tracer{"bind const char* span"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind const char* span"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(values.data())),
+ keyValue("size", values.size())};
int resultCode = sqlite3_carray_bind(m_compiledStatement.get(),
index,
@@ -248,7 +265,11 @@ void BaseStatement::bind(int index, Utils::span<const char *> values)
void BaseStatement::bind(int index, Utils::SmallStringView text)
{
- NanotraceHR::Tracer tracer{"bind string"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind string"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("text", text)};
int resultCode = sqlite3_bind_text(m_compiledStatement.get(),
index,
@@ -261,7 +282,12 @@ void BaseStatement::bind(int index, Utils::SmallStringView text)
void BaseStatement::bind(int index, BlobView blobView)
{
- NanotraceHR::Tracer tracer{"bind blob"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind blob"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(blobView.data())),
+ keyValue("size", blobView.size())};
int resultCode = SQLITE_OK;
@@ -281,7 +307,11 @@ void BaseStatement::bind(int index, BlobView blobView)
void BaseStatement::bind(int index, const Value &value)
{
- NanotraceHR::Tracer tracer{"bind value"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{
+ "bind value"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ };
switch (value.type()) {
case ValueType::Integer:
@@ -304,7 +334,11 @@ void BaseStatement::bind(int index, const Value &value)
void BaseStatement::bind(int index, ValueView value)
{
- NanotraceHR::Tracer tracer{"bind value"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{
+ "bind value"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ };
switch (value.type()) {
case ValueType::Integer:
@@ -327,7 +361,9 @@ void BaseStatement::bind(int index, ValueView value)
void BaseStatement::prepare(Utils::SmallStringView sqlStatement)
{
- NanotraceHR::Tracer tracer{"prepare"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"prepare"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sql statement", sqlStatement)};
if (!m_database.isLocked())
throw DatabaseIsNotLocked{};
@@ -343,14 +379,18 @@ void BaseStatement::prepare(Utils::SmallStringView sqlStatement)
nullptr);
m_compiledStatement.reset(sqliteStatement);
- if (resultCode == SQLITE_LOCKED)
+ if (resultCode == SQLITE_LOCKED) {
+ tracer.tick("wait for unlock"_t);
waitForUnlockNotify();
+ }
} while (resultCode == SQLITE_LOCKED);
if (resultCode != SQLITE_OK)
Sqlite::throwError(resultCode, sqliteDatabaseHandle());
+
+ tracer.end(keyValue("sqlite statement", handle()));
}
sqlite3 *BaseStatement::sqliteDatabaseHandle() const
@@ -428,7 +468,10 @@ StringType convertToTextForColumn(sqlite3_stmt *sqlStatment, int column)
Type BaseStatement::fetchType(int column) const
{
- NanotraceHR::Tracer tracer{"fetch type"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"fetch type"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
auto dataType = sqlite3_column_type(m_compiledStatement.get(), column);
@@ -450,9 +493,16 @@ Type BaseStatement::fetchType(int column) const
int BaseStatement::fetchIntValue(int column) const
{
- NanotraceHR::Tracer tracer{"fetch int"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"fetch int"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
+ auto value = sqlite3_column_int(m_compiledStatement.get(), column);
- return sqlite3_column_int(m_compiledStatement.get(), column);
+ tracer.end(keyValue("value", value));
+
+ return value;
}
template<>
@@ -474,9 +524,16 @@ long BaseStatement::fetchValue<long>(int column) const
long long BaseStatement::fetchLongLongValue(int column) const
{
- NanotraceHR::Tracer tracer{"fetch long long"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"fetch long long"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
+ auto value = sqlite3_column_int64(m_compiledStatement.get(), column);
- return sqlite3_column_int64(m_compiledStatement.get(), column);
+ tracer.end(keyValue("value", value));
+
+ return value;
}
template<>
@@ -487,14 +544,24 @@ long long BaseStatement::fetchValue<long long>(int column) const
double BaseStatement::fetchDoubleValue(int column) const
{
- NanotraceHR::Tracer tracer{"fetch double"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"fetch double"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
+ auto value = sqlite3_column_double(m_compiledStatement.get(), column);
+
+ tracer.end(keyValue("value", value));
- return sqlite3_column_double(m_compiledStatement.get(), column);
+ return value;
}
BlobView BaseStatement::fetchBlobValue(int column) const
{
- NanotraceHR::Tracer tracer{"fetch blob"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"fetch blob"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
return convertToBlobForColumn(m_compiledStatement.get(), column);
}
@@ -508,7 +575,16 @@ double BaseStatement::fetchValue<double>(int column) const
template<typename StringType>
StringType BaseStatement::fetchValue(int column) const
{
- return convertToTextForColumn<StringType>(m_compiledStatement.get(), column);
+ NanotraceHR::Tracer tracer{"fetch string value"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
+ auto text = convertToTextForColumn<StringType>(m_compiledStatement.get(), column);
+
+ tracer.end(keyValue("text", text));
+
+ return text;
}
template SQLITE_EXPORT Utils::SmallStringView BaseStatement::fetchValue<Utils::SmallStringView>(
@@ -520,11 +596,25 @@ template SQLITE_EXPORT Utils::PathString BaseStatement::fetchValue<Utils::PathSt
Utils::SmallStringView BaseStatement::fetchSmallStringViewValue(int column) const
{
- return fetchValue<Utils::SmallStringView>(column);
+ NanotraceHR::Tracer tracer{"fetch string view"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
+ auto text = fetchValue<Utils::SmallStringView>(column);
+
+ tracer.end(keyValue("text", text));
+
+ return text;
}
ValueView BaseStatement::fetchValueView(int column) const
{
+ NanotraceHR::Tracer tracer{"fetch value view"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
int dataType = sqlite3_column_type(m_compiledStatement.get(), column);
switch (dataType) {
case SQLITE_NULL:
@@ -544,7 +634,11 @@ ValueView BaseStatement::fetchValueView(int column) const
void BaseStatement::Deleter::operator()(sqlite3_stmt *statement)
{
- NanotraceHR::Tracer tracer{"finalize"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{
+ "finalize"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", reinterpret_cast<std::uintptr_t>(statement)),
+ };
sqlite3_finalize(statement);
}
diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h
index d2306c67a5..81b31473f8 100644
--- a/src/libs/sqlite/sqlitebasestatement.h
+++ b/src/libs/sqlite/sqlitebasestatement.h
@@ -9,6 +9,7 @@
#include "sqliteblob.h"
#include "sqliteexception.h"
#include "sqliteids.h"
+#include "sqlitetracing.h"
#include "sqlitetransaction.h"
#include "sqlitevalue.h"
@@ -30,8 +31,6 @@ using std::int64_t;
namespace Sqlite {
-using namespace NanotraceHR::Literals;
-
class Database;
class DatabaseBackend;
@@ -44,17 +43,6 @@ constexpr static std::underlying_type_t<Enumeration> to_underlying(Enumeration e
return static_cast<std::underlying_type_t<Enumeration>>(enumeration);
}
-constexpr NanotraceHR::Tracing sqliteTracingStatus()
-{
-#ifdef ENABLE_SQLITE_TRACING
- return NanotraceHR::tracingStatus();
-#else
- return NanotraceHR::Tracing::IsDisabled;
-#endif
-}
-
-SQLITE_EXPORT NanotraceHR::StringViewCategory<sqliteTracingStatus()> &sqliteHighLevelCategory();
-
class SQLITE_EXPORT BaseStatement
{
public:
@@ -136,6 +124,11 @@ public:
protected:
~BaseStatement() = default;
+ std::uintptr_t handle() const
+ {
+ return reinterpret_cast<std::uintptr_t>(m_compiledStatement.get());
+ }
+
private:
struct Deleter
{
@@ -166,7 +159,12 @@ public:
void execute()
{
- NanotraceHR::Tracer tracer{"execute"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{
+ "execute"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle()),
+ };
Resetter resetter{this};
BaseStatement::next();
@@ -175,7 +173,10 @@ public:
template<typename... ValueType>
void bindValues(const ValueType &...values)
{
- NanotraceHR::Tracer tracer{"bind"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"bind"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
static_assert(BindParameterCount == sizeof...(values), "Wrong binding parameter count!");
@@ -186,7 +187,10 @@ public:
template<typename... ValueType>
void write(const ValueType&... values)
{
- NanotraceHR::Tracer tracer{"write"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"write"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
bindValues(values...);
@@ -212,7 +216,10 @@ public:
typename... QueryTypes>
auto values(const QueryTypes &...queryValues)
{
- NanotraceHR::Tracer tracer{"values"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"values"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
Container resultValues;
@@ -241,7 +248,10 @@ public:
template<typename ResultType, typename... QueryTypes>
auto value(const QueryTypes &...queryValues)
{
- NanotraceHR::Tracer tracer{"value"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"value"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
ResultType resultValue{};
@@ -257,7 +267,10 @@ public:
template<typename ResultType, typename... QueryTypes>
auto optionalValue(const QueryTypes &...queryValues)
{
- NanotraceHR::Tracer tracer{"optionalValue"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"optionalValue"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
std::optional<ResultType> resultValue;
@@ -273,6 +286,7 @@ public:
template<typename Type>
static auto toValue(Utils::SmallStringView sqlStatement, Database &database)
{
+ using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"toValue"_t, sqliteHighLevelCategory()};
StatementImplementation statement(sqlStatement, database);
@@ -287,7 +301,10 @@ public:
template<typename Callable, typename... QueryTypes>
void readCallback(Callable &&callable, const QueryTypes &...queryValues)
{
- NanotraceHR::Tracer tracer{"readCallback"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"readCallback"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
@@ -304,7 +321,10 @@ public:
template<typename Container, typename... QueryTypes>
void readTo(Container &container, const QueryTypes &...queryValues)
{
- NanotraceHR::Tracer tracer{"readTo"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"readTo"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
@@ -402,9 +422,11 @@ public:
private:
using TracerCategory = std::decay_t<decltype(sqliteHighLevelCategory())>;
- NanotraceHR::Tracer<TracerCategory, typename TracerCategory::IsActive> tracer{
- "range"_t, sqliteHighLevelCategory()};
StatementImplementation &m_statement;
+ NanotraceHR::Tracer<TracerCategory, typename TracerCategory::IsActive> tracer{
+ "range"_t,
+ sqliteHighLevelCategory(),
+ NanotraceHR::keyValue("sqlite statement", m_statement.handle())};
};
template<typename ResultType>
diff --git a/src/libs/sqlite/sqliteexception.cpp b/src/libs/sqlite/sqliteexception.cpp
index b5f581ad68..bb4a474adb 100644
--- a/src/libs/sqlite/sqliteexception.cpp
+++ b/src/libs/sqlite/sqliteexception.cpp
@@ -3,14 +3,20 @@
#include "sqliteexception.h"
+#include "sqlitetracing.h"
+
#include <utils/smallstringio.h>
+#include <nanotrace/nanotracehr.h>
+
#include <sqlite.h>
#include <QDebug>
namespace Sqlite {
+using NanotraceHR::keyValue;
+
const char *Exception::what() const noexcept
{
return "Sqlite::Exception";
@@ -18,7 +24,10 @@ const char *Exception::what() const noexcept
const char *ExceptionWithMessage::what() const noexcept
{
- return "Sqlite::ExceptionWithMessage";
+ static Utils::SmallString text = Utils::SmallString::join(
+ {"Sqlite::ExceptionWithMessage", m_sqliteErrorMessage});
+
+ return text.data();
}
void ExceptionWithMessage::printWarning() const
@@ -26,6 +35,13 @@ void ExceptionWithMessage::printWarning() const
qWarning() << what() << m_sqliteErrorMessage;
}
+StatementIsBusy::StatementIsBusy(Utils::SmallString &&sqliteErrorMessage)
+ : ExceptionWithMessage{std::move(sqliteErrorMessage)}
+{
+ sqliteHighLevelCategory().threadEvent("StatementIsBusy"_t,
+ keyValue("error message", std::string_view{what()}));
+}
+
const char *StatementIsBusy::what() const noexcept
{
return "Sqlite::StatementIsBusy";
@@ -36,9 +52,19 @@ const char *DatabaseIsBusy::what() const noexcept
return "Sqlite::DatabaseIsBusy";
}
+StatementHasError::StatementHasError(Utils::SmallString &&sqliteErrorMessage)
+ : ExceptionWithMessage{std::move(sqliteErrorMessage)}
+{
+ sqliteHighLevelCategory().threadEvent("StatementHasError"_t,
+ keyValue("error message", std::string_view{what()}));
+}
+
const char *StatementHasError::what() const noexcept
{
- return "Sqlite::StatementHasError";
+ static Utils::SmallString text = Utils::SmallString::join(
+ {"Sqlite::StatementHasError: ", message()});
+
+ return text.data();
}
const char *StatementIsMisused::what() const noexcept
diff --git a/src/libs/sqlite/sqliteexception.h b/src/libs/sqlite/sqliteexception.h
index f0cadfc748..17a0639e19 100644
--- a/src/libs/sqlite/sqliteexception.h
+++ b/src/libs/sqlite/sqliteexception.h
@@ -23,13 +23,15 @@ public:
class SQLITE_EXPORT ExceptionWithMessage : public Exception
{
public:
- ExceptionWithMessage(Utils::SmallString &&sqliteErrorMessage = Utils::SmallString{})
+ ExceptionWithMessage(Utils::SmallString &&sqliteErrorMessage = {})
: m_sqliteErrorMessage(std::move(sqliteErrorMessage))
{}
const char *what() const noexcept override;
void printWarning() const;
+ std::string_view message() const noexcept { return m_sqliteErrorMessage; }
+
private:
Utils::SmallString m_sqliteErrorMessage;
};
@@ -37,7 +39,7 @@ private:
class SQLITE_EXPORT StatementIsBusy : public ExceptionWithMessage
{
public:
- using ExceptionWithMessage::ExceptionWithMessage;
+ StatementIsBusy(Utils::SmallString &&sqliteErrorMessage);
const char *what() const noexcept override;
};
@@ -90,7 +92,8 @@ public:
class SQLITE_EXPORT StatementHasError : public ExceptionWithMessage
{
public:
- using ExceptionWithMessage::ExceptionWithMessage;
+ StatementHasError(Utils::SmallString &&sqliteErrorMessage);
+
const char *what() const noexcept override;
};
diff --git a/src/libs/sqlite/sqliteids.h b/src/libs/sqlite/sqliteids.h
index d64e4d9645..1ffd546d9f 100644
--- a/src/libs/sqlite/sqliteids.h
+++ b/src/libs/sqlite/sqliteids.h
@@ -5,6 +5,7 @@
#include <utils/span.h>
+#include <nanotrace/nanotracehr.h>
#include <type_traits>
#include <vector>
@@ -64,6 +65,15 @@ public:
[[noreturn, deprecated]] InternalIntegerType operator&() const { throw std::exception{}; }
+ template<typename String>
+ friend void convertToString(String &string, BasicId id)
+ {
+ if (id.isValid())
+ NanotraceHR::convertToString(string, id.internalId());
+ else
+ NanotraceHR::convertToString(string, "invalid");
+ }
+
private:
InternalIntegerType id = 0;
};
@@ -88,4 +98,5 @@ struct hash<Sqlite::BasicId<Type, InternalIntegerType>>
return std::hash<InternalIntegerType>{}(id.internalId());
}
};
+
} // namespace std
diff --git a/src/libs/sqlite/sqlitetracing.cpp b/src/libs/sqlite/sqlitetracing.cpp
new file mode 100644
index 0000000000..700546f146
--- /dev/null
+++ b/src/libs/sqlite/sqlitetracing.cpp
@@ -0,0 +1,38 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "sqlitetracing.h"
+
+namespace Sqlite {
+
+TraceFile &traceFile()
+{
+ static TraceFile traceFile{"tracing.json"};
+
+ return traceFile;
+}
+
+namespace {
+
+thread_local NanotraceHR::EventQueue<NanotraceHR::StringViewWithStringArgumentsTraceEvent,
+ sqliteTracingStatus()>
+ eventQueue(traceFile());
+
+} // namespace
+
+NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()> &sqliteLowLevelCategory()
+{
+ thread_local NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()>
+ sqliteLowLevelCategory_{"sqlite low level"_t, eventQueue, sqliteLowLevelCategory};
+ return sqliteLowLevelCategory_;
+}
+
+NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()> &sqliteHighLevelCategory()
+{
+ thread_local NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()>
+ sqliteHighLevelCategory_{"sqlite high level"_t, eventQueue, sqliteHighLevelCategory};
+
+ return sqliteHighLevelCategory_;
+}
+
+} // namespace Sqlite
diff --git a/src/libs/sqlite/sqlitetracing.h b/src/libs/sqlite/sqlitetracing.h
new file mode 100644
index 0000000000..8dadc6de0d
--- /dev/null
+++ b/src/libs/sqlite/sqlitetracing.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "sqliteglobal.h"
+
+#include <nanotrace/nanotracehr.h>
+
+namespace Sqlite {
+using namespace NanotraceHR::Literals;
+
+constexpr NanotraceHR::Tracing sqliteTracingStatus()
+{
+#ifdef ENABLE_SQLITE_TRACING
+ return NanotraceHR::Tracing::IsEnabled;
+#else
+ return NanotraceHR::Tracing::IsDisabled;
+#endif
+}
+
+using TraceFile = NanotraceHR::TraceFile<sqliteTracingStatus()>;
+
+SQLITE_EXPORT TraceFile &traceFile();
+
+NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()> &sqliteLowLevelCategory();
+
+SQLITE_EXPORT NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()> &
+sqliteHighLevelCategory();
+
+} // namespace Sqlite
diff --git a/src/libs/sqlite/sqlitevalue.h b/src/libs/sqlite/sqlitevalue.h
index fe576f3fec..ec4120139a 100644
--- a/src/libs/sqlite/sqlitevalue.h
+++ b/src/libs/sqlite/sqlitevalue.h
@@ -6,6 +6,7 @@
#include "sqliteblob.h"
#include "sqliteexception.h"
+#include <nanotrace/nanotracehr.h>
#include <utils/smallstring.h>
#include <QVariant>
@@ -386,4 +387,27 @@ private:
};
using Values = std::vector<Value>;
+
+template<typename String>
+void convertToString(String &string, const Value &value)
+{
+ switch (value.type()) {
+ case ValueType::Null:
+ convertToString(string, "null");
+ break;
+ case ValueType::Integer:
+ convertToString(string, value.toInteger());
+ break;
+ case ValueType::Float:
+ convertToString(string, value.toFloat());
+ break;
+ case ValueType::String:
+ convertToString(string, value.toStringView());
+ break;
+ case ValueType::Blob:
+ convertToString(string, "blob");
+ break;
+ }
+}
+
} // namespace Sqlite
diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt
index 18c8abda49..a9912b3575 100644
--- a/src/libs/utils/CMakeLists.txt
+++ b/src/libs/utils/CMakeLists.txt
@@ -122,7 +122,6 @@ add_qtc_library(Utils
port.cpp port.h
portlist.cpp portlist.h
predicates.h
- process.cpp process.h
processenums.h
processhandle.cpp processhandle.h
processhelper.cpp processhelper.h
@@ -135,6 +134,7 @@ add_qtc_library(Utils
qrcparser.cpp qrcparser.h
qtcassert.cpp qtcassert.h
qtcolorbutton.cpp qtcolorbutton.h
+ qtcprocess.cpp qtcprocess.h
qtcsettings.cpp qtcsettings.h
ranges.h
reloadpromptutils.cpp reloadpromptutils.h
diff --git a/src/libs/utils/buildablehelperlibrary.cpp b/src/libs/utils/buildablehelperlibrary.cpp
index 3b54d11412..2be40b6c9b 100644
--- a/src/libs/utils/buildablehelperlibrary.cpp
+++ b/src/libs/utils/buildablehelperlibrary.cpp
@@ -4,7 +4,7 @@
#include "buildablehelperlibrary.h"
#include "environment.h"
#include "hostosinfo.h"
-#include "process.h"
+#include "qtcprocess.h"
#include <QDebug>
#include <QRegularExpression>
diff --git a/src/libs/utils/clangutils.cpp b/src/libs/utils/clangutils.cpp
index 6cf265820e..4c8c7d801d 100644
--- a/src/libs/utils/clangutils.cpp
+++ b/src/libs/utils/clangutils.cpp
@@ -4,7 +4,7 @@
#include "clangutils.h"
#include "filepath.h"
-#include "process.h"
+#include "qtcprocess.h"
#include "utilstr.h"
#include <QVersionNumber>
diff --git a/src/libs/utils/devicefileaccess.cpp b/src/libs/utils/devicefileaccess.cpp
index a38a098d38..2769f59281 100644
--- a/src/libs/utils/devicefileaccess.cpp
+++ b/src/libs/utils/devicefileaccess.cpp
@@ -13,7 +13,7 @@
#include "utilstr.h"
#ifndef UTILS_STATIC_LIBRARY
-#include "process.h"
+#include "qtcprocess.h"
#endif
#include <QCoreApplication>
diff --git a/src/libs/utils/deviceshell.cpp b/src/libs/utils/deviceshell.cpp
index abccaa1b56..69abc5ce4a 100644
--- a/src/libs/utils/deviceshell.cpp
+++ b/src/libs/utils/deviceshell.cpp
@@ -3,7 +3,7 @@
#include "deviceshell.h"
-#include "process.h"
+#include "qtcprocess.h"
#include "processinterface.h"
#include "qtcassert.h"
#include "utilstr.h"
diff --git a/src/libs/utils/externalterminalprocessimpl.cpp b/src/libs/utils/externalterminalprocessimpl.cpp
index ca178d7bf9..41b7259b8f 100644
--- a/src/libs/utils/externalterminalprocessimpl.cpp
+++ b/src/libs/utils/externalterminalprocessimpl.cpp
@@ -4,7 +4,7 @@
#include "externalterminalprocessimpl.h"
#include "algorithm.h"
-#include "process.h"
+#include "qtcprocess.h"
#include "terminalcommand.h"
#include "utilstr.h"
diff --git a/src/libs/utils/filestreamer.cpp b/src/libs/utils/filestreamer.cpp
index 2fd4007924..663f505b88 100644
--- a/src/libs/utils/filestreamer.cpp
+++ b/src/libs/utils/filestreamer.cpp
@@ -4,7 +4,7 @@
#include "filestreamer.h"
#include "async.h"
-#include "process.h"
+#include "qtcprocess.h"
#include <solutions/tasking/barrier.h>
#include <solutions/tasking/tasktreerunner.h>
diff --git a/src/libs/utils/pathchooser.cpp b/src/libs/utils/pathchooser.cpp
index 437b94bb5c..a4d2a06088 100644
--- a/src/libs/utils/pathchooser.cpp
+++ b/src/libs/utils/pathchooser.cpp
@@ -11,7 +11,7 @@
#include "hostosinfo.h"
#include "macroexpander.h"
#include "optionpushbutton.h"
-#include "process.h"
+#include "qtcprocess.h"
#include "qtcassert.h"
#include "utilstr.h"
diff --git a/src/libs/utils/processinfo.cpp b/src/libs/utils/processinfo.cpp
index 499745473e..0717bddbe9 100644
--- a/src/libs/utils/processinfo.cpp
+++ b/src/libs/utils/processinfo.cpp
@@ -4,7 +4,7 @@
#include "processinfo.h"
#include "algorithm.h"
-#include "process.h"
+#include "qtcprocess.h"
#include <QDir>
#include <QRegularExpression>
diff --git a/src/libs/utils/process.cpp b/src/libs/utils/qtcprocess.cpp
index 802ae632f1..b399d86b36 100644
--- a/src/libs/utils/process.cpp
+++ b/src/libs/utils/qtcprocess.cpp
@@ -1,7 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "process.h"
+#include "qtcprocess.h"
#include "algorithm.h"
#include "environment.h"
@@ -2176,4 +2176,4 @@ void ProcessTaskAdapter::start()
} // namespace Utils
-#include "process.moc"
+#include "qtcprocess.moc"
diff --git a/src/libs/utils/process.h b/src/libs/utils/qtcprocess.h
index 694c1a12a8..a7e00882a3 100644
--- a/src/libs/utils/process.h
+++ b/src/libs/utils/qtcprocess.h
@@ -1,11 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#if defined(Q_CC_MINGW) && defined(WIN_PTHREADS_H) && !defined(_INC_PROCESS)
- // Arrived here via <pthread.h> which wants to include <process.h>
- #include_next <process.h>
-#elif !defined(UTILS_PROCESS_H)
-#define UTILS_PROCESS_H
+#pragma once
#include "utils_global.h"
@@ -223,5 +219,3 @@ public:
using ProcessTask = Tasking::CustomTask<ProcessTaskAdapter>;
} // namespace Utils
-
-#endif // UTILS_PROCESS_H
diff --git a/src/libs/utils/ranges.h b/src/libs/utils/ranges.h
index 8b574e08d6..5a3c3507c1 100644
--- a/src/libs/utils/ranges.h
+++ b/src/libs/utils/ranges.h
@@ -3,8 +3,8 @@
#pragma once
-#if __cplusplus >= 202002L
-#include <ranges>
+#if __cpp_lib_ranges >= 202002L
+# include <ranges>
namespace Utils {
diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h
index c424e9e1ab..a8869b4ccf 100644
--- a/src/libs/utils/smallstring.h
+++ b/src/libs/utils/smallstring.h
@@ -93,7 +93,7 @@ public:
static_cast<std::size_t>(std::distance(begin, end))}
{}
- template<typename Type, typename = std::enable_if_t<std::is_pointer<Type>::value>>
+ template<typename Type, typename std::enable_if_t<std::is_pointer<Type>::value, bool> = true>
BasicSmallString(Type characterPointer) noexcept
: BasicSmallString(characterPointer, std::char_traits<char>::length(characterPointer))
{
@@ -118,7 +118,7 @@ public:
template<typename BeginIterator,
typename EndIterator,
- typename = std::enable_if_t<std::is_same<BeginIterator, EndIterator>::value>>
+ typename std::enable_if_t<std::is_same<BeginIterator, EndIterator>::value, bool> = true>
BasicSmallString(BeginIterator begin, EndIterator end) noexcept
: BasicSmallString(&(*begin), size_type(end - begin))
{}
@@ -425,13 +425,55 @@ public:
size_type oldSize = size();
size_type newSize = oldSize + string.size();
- reserve(optimalCapacity(newSize));
+ if (fitsNotInCapacity(newSize))
+ reserve(optimalCapacity(newSize));
+
std::char_traits<char>::copy(std::next(data(), static_cast<std::ptrdiff_t>(oldSize)),
string.data(),
string.size());
setSize(newSize);
}
+ void append(char character) noexcept
+ {
+ size_type oldSize = size();
+ size_type newSize = oldSize + 1;
+
+ if (fitsNotInCapacity(newSize))
+ reserve(optimalCapacity(newSize));
+
+ auto current = std::next(data(), static_cast<std::ptrdiff_t>(oldSize));
+ *current = character;
+ setSize(newSize);
+ }
+
+ template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>, bool> = true>
+ void append(Type number)
+ {
+#if defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L)
+ // 2 bytes for the sign and because digits10 returns the floor
+ char buffer[std::numeric_limits<Type>::digits10 + 2];
+ auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
+ auto endOfConversionString = result.ptr;
+
+ append({buffer, endOfConversionString});
+#else
+ if constexpr (std::is_floating_point_v<Type>) {
+ QLocale locale{QLocale::Language::C};
+ append(locale.toString(number));
+ return;
+ } else {
+ // 2 bytes for the sign and because digits10 returns the floor
+ char buffer[std::numeric_limits<Type>::digits10 + 2];
+ auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
+ auto endOfConversionString = result.ptr;
+
+ append({buffer, endOfConversionString});
+ }
+
+#endif
+ }
+
void append(QStringView string) noexcept
{
QStringEncoder encoder{QStringEncoder::Utf8};
@@ -471,6 +513,13 @@ public:
return *this;
}
+ BasicSmallString &operator+=(char character) noexcept
+ {
+ append(character);
+
+ return *this;
+ }
+
BasicSmallString &operator+=(QStringView string) noexcept
{
append(string);
@@ -478,6 +527,14 @@ public:
return *this;
}
+ template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>, bool> = true>
+ BasicSmallString &operator+=(Type number) noexcept
+ {
+ append(number);
+
+ return *this;
+ }
+
BasicSmallString &operator+=(std::initializer_list<SmallStringView> list) noexcept
{
appendInitializerList(list, size());
@@ -548,52 +605,46 @@ public:
return m_data.control.shortStringSize();
}
- static BasicSmallString join(std::initializer_list<SmallStringView> list) noexcept
+ static BasicSmallString join(std::initializer_list<SmallStringView> list,
+ Utils::SmallStringView separator) noexcept
{
size_type totalSize = 0;
for (SmallStringView string : list)
- totalSize += string.size();
+ totalSize += string.size() + separator.size();
BasicSmallString joinedString;
joinedString.reserve(totalSize);
- for (SmallStringView string : list)
- joinedString.append(string);
+ for (auto it = list.begin(); it != list.end(); ++it) {
+ joinedString.append(*it);
+ if (std::next(it) != list.end())
+ joinedString.append(separator);
+ }
return joinedString;
}
- static
- BasicSmallString number(int number)
+ static BasicSmallString join(std::initializer_list<SmallStringView> list) noexcept
{
- // 2 bytes for the sign and because digits10 returns the floor
- char buffer[std::numeric_limits<int>::digits10 + 2];
- auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
- auto endOfConversionString = result.ptr;
- return BasicSmallString(buffer, endOfConversionString);
- }
+ size_type totalSize = 0;
+ for (SmallStringView string : list)
+ totalSize += string.size();
- static BasicSmallString number(long long int number) noexcept
- {
- // 2 bytes for the sign and because digits10 returns the floor
- char buffer[std::numeric_limits<long long int>::digits10 + 2];
- auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
- auto endOfConversionString = result.ptr;
- return BasicSmallString(buffer, endOfConversionString);
+ BasicSmallString joinedString;
+ joinedString.reserve(totalSize);
+
+ for (SmallStringView string : list)
+ joinedString.append(string);
+
+ return joinedString;
}
- static BasicSmallString number(double number) noexcept
+ template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>, bool> = true>
+ static BasicSmallString number(Type number)
{
-#if defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L)
- // 2 bytes for the sign and because digits10 returns the floor
- char buffer[std::numeric_limits<double>::digits10 + 2];
- auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
- auto endOfConversionString = result.ptr;
- return BasicSmallString(buffer, endOfConversionString);
-#else
- QLocale locale{QLocale::Language::C};
- return BasicSmallString{locale.toString(number)};
-#endif
+ BasicSmallString string;
+ string.append(number);
+ return string;
}
char &operator[](std::size_t index) noexcept { return *(data() + index); }
@@ -638,7 +689,6 @@ public:
friend BasicSmallString operator+(const BasicSmallString &first,
const char (&second)[ArraySize]) noexcept
{
-
return operator+(first, SmallStringView(second));
}
@@ -670,8 +720,10 @@ unittest_public:
bool fitsNotInCapacity(size_type capacity) const noexcept
{
- return (isShortString() && capacity > shortStringCapacity())
- || (!isShortString() && capacity > m_data.reference.capacity);
+ if (isShortString())
+ return capacity > shortStringCapacity();
+
+ return capacity > m_data.reference.capacity;
}
static size_type optimalHeapCapacity(const size_type size) noexcept
diff --git a/src/libs/utils/span.h b/src/libs/utils/span.h
index b1fba81447..d21bb9ab00 100644
--- a/src/libs/utils/span.h
+++ b/src/libs/utils/span.h
@@ -5,7 +5,9 @@
#include <qcompilerdetection.h>
-#if __cplusplus >= 202002L
+// The (Apple) Clang implementation of span is incomplete until LLVM 15 / Xcode 14.3 / macOS 13
+#if __cplusplus >= 202002L \
+ && !(defined(__apple_build_version__) && __apple_build_version__ < 14030022)
#include <span>
namespace Utils {
@@ -22,6 +24,11 @@ QT_WARNING_PUSH
#elif defined(Q_CC_GNU) || defined(Q_CC_CLANG)
#pragma GCC system_header
#endif
+
+// disable automatic usage of std::span in span-lite
+// since we make that decision ourselves at the top of this header
+#define span_CONFIG_SELECT_SPAN span_SPAN_NONSTD
+
#include <3rdparty/span/span.hpp>
namespace Utils {
using namespace nonstd;
diff --git a/src/libs/utils/terminalhooks.cpp b/src/libs/utils/terminalhooks.cpp
index 52bcc05629..3e5151e6f3 100644
--- a/src/libs/utils/terminalhooks.cpp
+++ b/src/libs/utils/terminalhooks.cpp
@@ -5,7 +5,7 @@
#include "externalterminalprocessimpl.h"
#include "filepath.h"
-#include "process.h"
+#include "qtcprocess.h"
#include "utilstr.h"
#include <QMutex>
diff --git a/src/libs/utils/unarchiver.h b/src/libs/utils/unarchiver.h
index b255cd4990..61818318bb 100644
--- a/src/libs/utils/unarchiver.h
+++ b/src/libs/utils/unarchiver.h
@@ -6,7 +6,7 @@
#include "utils_global.h"
#include "commandline.h"
-#include "process.h"
+#include "qtcprocess.h"
#include <solutions/tasking/tasktree.h>
diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs
index 20e3a707bc..4b7caf4805 100644
--- a/src/libs/utils/utils.qbs
+++ b/src/libs/utils/utils.qbs
@@ -230,8 +230,8 @@ QtcLibrary {
"portlist.cpp",
"portlist.h",
"predicates.h",
- "process.cpp",
- "process.h",
+ "qtcprocess.cpp",
+ "qtcprocess.h",
"processenums.h",
"processhandle.cpp",
"processhandle.h",
diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
index 85de56b428..04466fed25 100644
--- a/src/plugins/CMakeLists.txt
+++ b/src/plugins/CMakeLists.txt
@@ -109,6 +109,7 @@ if (WITH_QMLDESIGNER)
set(qmldesigner_builddir ${PROJECT_BINARY_DIR}/qmldsgnr)
endif()
add_subdirectory(qmldesigner ${qmldesigner_builddir})
+ add_subdirectory(qmldesignerlite)
add_subdirectory(effectcomposer)
add_subdirectory(studiowelcome)
add_subdirectory(insight)
diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp
index a939fb5910..c37daf43b8 100644
--- a/src/plugins/android/androidavdmanager.cpp
+++ b/src/plugins/android/androidavdmanager.cpp
@@ -11,7 +11,7 @@
#include <utils/algorithm.h>
#include <utils/async.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QLoggingCategory>
diff --git a/src/plugins/android/androidbuildapkstep.cpp b/src/plugins/android/androidbuildapkstep.cpp
index c57d008705..940b373298 100644
--- a/src/plugins/android/androidbuildapkstep.cpp
+++ b/src/plugins/android/androidbuildapkstep.cpp
@@ -36,7 +36,7 @@
#include <utils/infolabel.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QCheckBox>
#include <QComboBox>
diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp
index 8edba77605..28edfdf747 100644
--- a/src/plugins/android/androidconfigurations.cpp
+++ b/src/plugins/android/androidconfigurations.cpp
@@ -33,7 +33,7 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/persistentsettings.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/qtcsettings.h>
#include <utils/stringutils.h>
diff --git a/src/plugins/android/androidcreatekeystorecertificate.cpp b/src/plugins/android/androidcreatekeystorecertificate.cpp
index 7a6fd7cf59..77aed2a6ad 100644
--- a/src/plugins/android/androidcreatekeystorecertificate.cpp
+++ b/src/plugins/android/androidcreatekeystorecertificate.cpp
@@ -7,7 +7,7 @@
#include <utils/infolabel.h>
#include <utils/layoutbuilder.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QCheckBox>
#include <QDialog>
diff --git a/src/plugins/android/androiddebugsupport.cpp b/src/plugins/android/androiddebugsupport.cpp
index 2f016eb23a..cea404ef6f 100644
--- a/src/plugins/android/androiddebugsupport.cpp
+++ b/src/plugins/android/androiddebugsupport.cpp
@@ -21,7 +21,7 @@
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QHostAddress>
#include <QJsonDocument>
diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp
index fcf5b9703a..6e520d3721 100644
--- a/src/plugins/android/androiddeployqtstep.cpp
+++ b/src/plugins/android/androiddeployqtstep.cpp
@@ -38,7 +38,7 @@
#include <utils/environment.h>
#include <utils/futuresynchronizer.h>
#include <utils/layoutbuilder.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QCheckBox>
diff --git a/src/plugins/android/androiddevice.cpp b/src/plugins/android/androiddevice.cpp
index b3f9915924..1d87fa1ddd 100644
--- a/src/plugins/android/androiddevice.cpp
+++ b/src/plugins/android/androiddevice.cpp
@@ -22,7 +22,7 @@
#include <projectexplorer/target.h>
#include <utils/async.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/url.h>
diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp
index b95b6bb4dd..3666762be6 100644
--- a/src/plugins/android/androidmanager.cpp
+++ b/src/plugins/android/androidmanager.cpp
@@ -23,7 +23,7 @@
#include <qtsupport/qtkitaspect.h>
#include <utils/algorithm.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QDomDocument>
diff --git a/src/plugins/android/androidpackageinstallationstep.cpp b/src/plugins/android/androidpackageinstallationstep.cpp
index 0fea1e283c..8f1d4b01d5 100644
--- a/src/plugins/android/androidpackageinstallationstep.cpp
+++ b/src/plugins/android/androidpackageinstallationstep.cpp
@@ -22,7 +22,7 @@
#include <qtsupport/qtkitaspect.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QDir>
#include <QLoggingCategory>
diff --git a/src/plugins/android/androidqmlpreviewworker.cpp b/src/plugins/android/androidqmlpreviewworker.cpp
index 06a14edc06..f194c6e979 100644
--- a/src/plugins/android/androidqmlpreviewworker.cpp
+++ b/src/plugins/android/androidqmlpreviewworker.cpp
@@ -28,7 +28,7 @@
#include <qtsupport/qtkitaspect.h>
#include <utils/async.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QDateTime>
#include <QDeadlineTimer>
diff --git a/src/plugins/android/androidrunconfiguration.cpp b/src/plugins/android/androidrunconfiguration.cpp
index 8a057a3faa..323e5d45b4 100644
--- a/src/plugins/android/androidrunconfiguration.cpp
+++ b/src/plugins/android/androidrunconfiguration.cpp
@@ -16,7 +16,7 @@
#include <qtsupport/qtkitaspect.h>
#include <utils/detailswidget.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp
index 3f8ff433b7..c9a2ea929d 100644
--- a/src/plugins/android/androidrunnerworker.cpp
+++ b/src/plugins/android/androidrunnerworker.cpp
@@ -23,7 +23,7 @@
#include <utils/async.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/temporaryfile.h>
#include <utils/url.h>
diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp
index 029cfd8352..7ae9ce05aa 100644
--- a/src/plugins/android/androidsdkmanager.cpp
+++ b/src/plugins/android/androidsdkmanager.cpp
@@ -9,7 +9,7 @@
#include <utils/algorithm.h>
#include <utils/async.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp
index cb67d9fbeb..24fa0875c4 100644
--- a/src/plugins/android/androidsettingswidget.cpp
+++ b/src/plugins/android/androidsettingswidget.cpp
@@ -20,8 +20,8 @@
#include <utils/infolabel.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
#include <utils/progressindicator.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
diff --git a/src/plugins/android/androidsignaloperation.cpp b/src/plugins/android/androidsignaloperation.cpp
index 9b71a07a9b..5016b2573e 100644
--- a/src/plugins/android/androidsignaloperation.cpp
+++ b/src/plugins/android/androidsignaloperation.cpp
@@ -4,7 +4,7 @@
#include "androidconfigurations.h"
#include "androidsignaloperation.h"
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
using namespace Utils;
diff --git a/src/plugins/autotest/boost/boosttestoutputreader.cpp b/src/plugins/autotest/boost/boosttestoutputreader.cpp
index 7b58d74f52..c069de9701 100644
--- a/src/plugins/autotest/boost/boosttestoutputreader.cpp
+++ b/src/plugins/autotest/boost/boosttestoutputreader.cpp
@@ -9,7 +9,7 @@
#include "../autotesttr.h"
#include "../testtreeitem.h"
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QLoggingCategory>
diff --git a/src/plugins/autotest/gtest/gtestoutputreader.cpp b/src/plugins/autotest/gtest/gtestoutputreader.cpp
index b24291caeb..c0c08196ad 100644
--- a/src/plugins/autotest/gtest/gtestoutputreader.cpp
+++ b/src/plugins/autotest/gtest/gtestoutputreader.cpp
@@ -8,7 +8,7 @@
#include "../autotesttr.h"
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QRegularExpression>
diff --git a/src/plugins/autotest/testoutputreader.cpp b/src/plugins/autotest/testoutputreader.cpp
index cef36c90e7..727feeac7d 100644
--- a/src/plugins/autotest/testoutputreader.cpp
+++ b/src/plugins/autotest/testoutputreader.cpp
@@ -6,7 +6,7 @@
#include "autotesttr.h"
#include "testtreeitem.h"
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QRegularExpression>
diff --git a/src/plugins/autotest/testrunner.cpp b/src/plugins/autotest/testrunner.cpp
index 3631459fec..29b7898835 100644
--- a/src/plugins/autotest/testrunner.cpp
+++ b/src/plugins/autotest/testrunner.cpp
@@ -33,7 +33,7 @@
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
#include <utils/outputformat.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QCheckBox>
#include <QComboBox>
diff --git a/src/plugins/autotoolsprojectmanager/autogenstep.cpp b/src/plugins/autotoolsprojectmanager/autogenstep.cpp
index 020a9ad96c..f1dde9c921 100644
--- a/src/plugins/autotoolsprojectmanager/autogenstep.cpp
+++ b/src/plugins/autotoolsprojectmanager/autogenstep.cpp
@@ -14,7 +14,7 @@
#include <projectexplorer/target.h>
#include <utils/aspects.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QDateTime>
diff --git a/src/plugins/autotoolsprojectmanager/autoreconfstep.cpp b/src/plugins/autotoolsprojectmanager/autoreconfstep.cpp
index 810272ecad..8ad689f130 100644
--- a/src/plugins/autotoolsprojectmanager/autoreconfstep.cpp
+++ b/src/plugins/autotoolsprojectmanager/autoreconfstep.cpp
@@ -13,7 +13,7 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <utils/aspects.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
using namespace ProjectExplorer;
using namespace Utils;
diff --git a/src/plugins/autotoolsprojectmanager/configurestep.cpp b/src/plugins/autotoolsprojectmanager/configurestep.cpp
index 97fdf3576e..4303b298f7 100644
--- a/src/plugins/autotoolsprojectmanager/configurestep.cpp
+++ b/src/plugins/autotoolsprojectmanager/configurestep.cpp
@@ -13,7 +13,7 @@
#include <projectexplorer/target.h>
#include <utils/aspects.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QDateTime>
diff --git a/src/plugins/autotoolsprojectmanager/makefileparser.cpp b/src/plugins/autotoolsprojectmanager/makefileparser.cpp
index 8a2f684f87..d4468daa8a 100644
--- a/src/plugins/autotoolsprojectmanager/makefileparser.cpp
+++ b/src/plugins/autotoolsprojectmanager/makefileparser.cpp
@@ -5,7 +5,7 @@
#include "autotoolsprojectmanagertr.h"
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
diff --git a/src/plugins/baremetal/baremetaldebugsupport.cpp b/src/plugins/baremetal/baremetaldebugsupport.cpp
index e3d32b3d5c..521289650f 100644
--- a/src/plugins/baremetal/baremetaldebugsupport.cpp
+++ b/src/plugins/baremetal/baremetaldebugsupport.cpp
@@ -24,7 +24,7 @@
#include <projectexplorer/toolchain.h>
#include <utils/portlist.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
using namespace Debugger;
diff --git a/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp
index 56495e32c6..8d8a968c09 100644
--- a/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp
+++ b/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp
@@ -9,7 +9,7 @@
#include <utils/fileutils.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/variablechooser.h>
diff --git a/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp
index ff4ae311af..2ee3b266d7 100644
--- a/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp
+++ b/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp
@@ -9,7 +9,7 @@
#include <utils/fileutils.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/variablechooser.h>
diff --git a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h
index 2cce62f6c1..61bbe83f81 100644
--- a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h
+++ b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h
@@ -10,7 +10,7 @@
#include <projectexplorer/runcontrol.h> // for RunWorker
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
namespace Utils { class PathChooser; }
diff --git a/src/plugins/baremetal/iarewtoolchain.cpp b/src/plugins/baremetal/iarewtoolchain.cpp
index 4fa7dfe2e5..ef804b0a8a 100644
--- a/src/plugins/baremetal/iarewtoolchain.cpp
+++ b/src/plugins/baremetal/iarewtoolchain.cpp
@@ -16,7 +16,7 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QDebug>
diff --git a/src/plugins/baremetal/keiltoolchain.cpp b/src/plugins/baremetal/keiltoolchain.cpp
index 50ae98178d..0ca7495c94 100644
--- a/src/plugins/baremetal/keiltoolchain.cpp
+++ b/src/plugins/baremetal/keiltoolchain.cpp
@@ -16,7 +16,7 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QDebug>
diff --git a/src/plugins/baremetal/sdcctoolchain.cpp b/src/plugins/baremetal/sdcctoolchain.cpp
index 17130e861b..971fd9f13a 100644
--- a/src/plugins/baremetal/sdcctoolchain.cpp
+++ b/src/plugins/baremetal/sdcctoolchain.cpp
@@ -16,7 +16,7 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QDebug>
diff --git a/src/plugins/beautifier/artisticstyle/artisticstyle.cpp b/src/plugins/beautifier/artisticstyle/artisticstyle.cpp
index feb532d147..2982ac267b 100644
--- a/src/plugins/beautifier/artisticstyle/artisticstyle.cpp
+++ b/src/plugins/beautifier/artisticstyle/artisticstyle.cpp
@@ -29,7 +29,7 @@
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <QAction>
diff --git a/src/plugins/beautifier/beautifiertool.cpp b/src/plugins/beautifier/beautifiertool.cpp
index 72f5a72620..cfcc117667 100644
--- a/src/plugins/beautifier/beautifiertool.cpp
+++ b/src/plugins/beautifier/beautifiertool.cpp
@@ -14,7 +14,7 @@
#include <utils/fileutils.h>
#include <utils/genericconstants.h>
#include <utils/mimeutils.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QFile>
#include <QRegularExpression>
diff --git a/src/plugins/beautifier/uncrustify/uncrustify.cpp b/src/plugins/beautifier/uncrustify/uncrustify.cpp
index e806b9026c..75bd5252ed 100644
--- a/src/plugins/beautifier/uncrustify/uncrustify.cpp
+++ b/src/plugins/beautifier/uncrustify/uncrustify.cpp
@@ -29,7 +29,7 @@
#include <utils/aspects.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QAction>
#include <QCheckBox>
diff --git a/src/plugins/boot2qt/device-detection/qdbwatcher.cpp b/src/plugins/boot2qt/device-detection/qdbwatcher.cpp
index 869b2d84fc..8a18a3b5ac 100644
--- a/src/plugins/boot2qt/device-detection/qdbwatcher.cpp
+++ b/src/plugins/boot2qt/device-detection/qdbwatcher.cpp
@@ -8,7 +8,7 @@
#include "../qdbutils.h"
#include <utils/filepath.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QFile>
#include <QTimer>
diff --git a/src/plugins/boot2qt/qdbdevice.cpp b/src/plugins/boot2qt/qdbdevice.cpp
index 9cc4852831..1ede480417 100644
--- a/src/plugins/boot2qt/qdbdevice.cpp
+++ b/src/plugins/boot2qt/qdbdevice.cpp
@@ -15,7 +15,7 @@
#include <remotelinux/linuxprocessinterface.h>
#include <utils/portlist.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/theme/theme.h>
diff --git a/src/plugins/boot2qt/qdbdevicedebugsupport.cpp b/src/plugins/boot2qt/qdbdevicedebugsupport.cpp
index b3129adf22..d9f3d64738 100644
--- a/src/plugins/boot2qt/qdbdevicedebugsupport.cpp
+++ b/src/plugins/boot2qt/qdbdevicedebugsupport.cpp
@@ -18,7 +18,7 @@
#include <debugger/debuggerruncontrol.h>
#include <utils/algorithm.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/url.h>
using namespace Debugger;
diff --git a/src/plugins/boot2qt/qdbmakedefaultappstep.cpp b/src/plugins/boot2qt/qdbmakedefaultappstep.cpp
index 3a777ddcb8..ca15acf7d4 100644
--- a/src/plugins/boot2qt/qdbmakedefaultappstep.cpp
+++ b/src/plugins/boot2qt/qdbmakedefaultappstep.cpp
@@ -14,7 +14,7 @@
#include <remotelinux/abstractremotelinuxdeploystep.h>
#include <utils/commandline.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
using namespace ProjectExplorer;
using namespace Tasking;
diff --git a/src/plugins/boot2qt/qdbplugin.cpp b/src/plugins/boot2qt/qdbplugin.cpp
index fbcd2667f6..c0a5bb25ca 100644
--- a/src/plugins/boot2qt/qdbplugin.cpp
+++ b/src/plugins/boot2qt/qdbplugin.cpp
@@ -29,7 +29,7 @@
#include <remotelinux/remotelinux_constants.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
using namespace Core;
using namespace ProjectExplorer;
diff --git a/src/plugins/boot2qt/qdbstopapplicationstep.cpp b/src/plugins/boot2qt/qdbstopapplicationstep.cpp
index ce437993f3..341691c368 100644
--- a/src/plugins/boot2qt/qdbstopapplicationstep.cpp
+++ b/src/plugins/boot2qt/qdbstopapplicationstep.cpp
@@ -13,7 +13,7 @@
#include <remotelinux/abstractremotelinuxdeploystep.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
using namespace ProjectExplorer;
using namespace Tasking;
diff --git a/src/plugins/clangtools/clangtoolrunner.cpp b/src/plugins/clangtools/clangtoolrunner.cpp
index bd97eac2ea..46fdf7c273 100644
--- a/src/plugins/clangtools/clangtoolrunner.cpp
+++ b/src/plugins/clangtools/clangtoolrunner.cpp
@@ -16,7 +16,7 @@
#include <extensionsystem/pluginmanager.h>
#include <utils/async.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/temporaryfile.h>
diff --git a/src/plugins/clangtools/clangtoolsutils.cpp b/src/plugins/clangtools/clangtoolsutils.cpp
index 029a879ce1..6a650e9c55 100644
--- a/src/plugins/clangtools/clangtoolsutils.cpp
+++ b/src/plugins/clangtools/clangtoolsutils.cpp
@@ -18,7 +18,7 @@
#include <utils/environment.h>
#include <utils/filepath.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <cppeditor/clangdiagnosticconfigsmodel.h>
diff --git a/src/plugins/clangtools/executableinfo.cpp b/src/plugins/clangtools/executableinfo.cpp
index 22898b5734..0b5d649cf5 100644
--- a/src/plugins/clangtools/executableinfo.cpp
+++ b/src/plugins/clangtools/executableinfo.cpp
@@ -7,7 +7,7 @@
#include <coreplugin/messagemanager.h>
#include <utils/environment.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QDir>
#include <QFileInfo>
diff --git a/src/plugins/clearcase/clearcaseplugin.cpp b/src/plugins/clearcase/clearcaseplugin.cpp
index f566b8223d..bc4cc82958 100644
--- a/src/plugins/clearcase/clearcaseplugin.cpp
+++ b/src/plugins/clearcase/clearcaseplugin.cpp
@@ -35,7 +35,7 @@
#include <utils/hostosinfo.h>
#include <utils/infobar.h>
#include <utils/layoutbuilder.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/temporarydirectory.h>
diff --git a/src/plugins/clearcase/clearcasesync.cpp b/src/plugins/clearcase/clearcasesync.cpp
index 71682126a6..a92ab7da7b 100644
--- a/src/plugins/clearcase/clearcasesync.cpp
+++ b/src/plugins/clearcase/clearcasesync.cpp
@@ -9,7 +9,7 @@
#include <QRegularExpression>
#include <QStringList>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QPromise>
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
index f93f41e3e8..0ba54b60a8 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
@@ -47,7 +47,7 @@
#include <utils/checkablemessagebox.h>
#include <utils/macroexpander.h>
#include <utils/mimeconstants.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QClipboard>
diff --git a/src/plugins/cmakeprojectmanager/cmakeprocess.cpp b/src/plugins/cmakeprojectmanager/cmakeprocess.cpp
index ef120914de..40cc41d64a 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprocess.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeprocess.cpp
@@ -20,7 +20,7 @@
#include <extensionsystem/pluginmanager.h>
#include <utils/algorithm.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinfo.h>
#include <utils/processinterface.h>
#include <utils/stringutils.h>
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp
index 33c8d66355..6df1e7452d 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp
@@ -23,7 +23,7 @@
#include <qtsupport/qtkitaspect.h>
#include <utils/algorithm.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
#include <utils/temporarydirectory.h>
diff --git a/src/plugins/cmakeprojectmanager/cmaketool.cpp b/src/plugins/cmakeprojectmanager/cmaketool.cpp
index af6637ee9b..4cb638659a 100644
--- a/src/plugins/cmakeprojectmanager/cmaketool.cpp
+++ b/src/plugins/cmakeprojectmanager/cmaketool.cpp
@@ -11,7 +11,7 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/temporarydirectory.h>
diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
index 99d37e6c83..a0bd4d426e 100644
--- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
+++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
@@ -18,7 +18,7 @@
#include <utils/icon.h>
#include <utils/mimeconstants.h>
#include <utils/mimeutils.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QLoggingCategory>
diff --git a/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp b/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp
index fe724a893a..76ff935975 100644
--- a/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp
+++ b/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp
@@ -18,7 +18,7 @@
#include <utils/layoutbuilder.h>
#include <utils/macroexpander.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/variablechooser.h>
diff --git a/src/plugins/coreplugin/externaltool.cpp b/src/plugins/coreplugin/externaltool.cpp
index ea13e50c50..e0b60d4c55 100644
--- a/src/plugins/coreplugin/externaltool.cpp
+++ b/src/plugins/coreplugin/externaltool.cpp
@@ -14,7 +14,7 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/macroexpander.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QCoreApplication>
diff --git a/src/plugins/coreplugin/fileutils.cpp b/src/plugins/coreplugin/fileutils.cpp
index e432543604..5712c9c8b5 100644
--- a/src/plugins/coreplugin/fileutils.cpp
+++ b/src/plugins/coreplugin/fileutils.cpp
@@ -17,7 +17,7 @@
#include <utils/commandline.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/terminalcommand.h>
#include <utils/terminalhooks.h>
#include <utils/textfileformat.h>
diff --git a/src/plugins/coreplugin/locator/executefilter.cpp b/src/plugins/coreplugin/locator/executefilter.cpp
index a2cd1de91c..629099207e 100644
--- a/src/plugins/coreplugin/locator/executefilter.cpp
+++ b/src/plugins/coreplugin/locator/executefilter.cpp
@@ -11,7 +11,7 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/macroexpander.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QJsonArray>
diff --git a/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp b/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp
index 7ecd298a8c..b360ad595d 100644
--- a/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp
+++ b/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp
@@ -16,7 +16,7 @@
#include <utils/link.h>
#include <utils/macroexpander.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
#include <utils/variablechooser.h>
diff --git a/src/plugins/coreplugin/patchtool.cpp b/src/plugins/coreplugin/patchtool.cpp
index c17db9ba92..fc463be49e 100644
--- a/src/plugins/coreplugin/patchtool.cpp
+++ b/src/plugins/coreplugin/patchtool.cpp
@@ -9,7 +9,7 @@
#include "systemsettings.h"
#include <utils/environment.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QMessageBox>
diff --git a/src/plugins/coreplugin/plugininstallwizard.cpp b/src/plugins/coreplugin/plugininstallwizard.cpp
index 5b342bf6c6..0ff62b13ff 100644
--- a/src/plugins/coreplugin/plugininstallwizard.cpp
+++ b/src/plugins/coreplugin/plugininstallwizard.cpp
@@ -18,7 +18,7 @@
#include <utils/infolabel.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/temporarydirectory.h>
#include <utils/unarchiver.h>
diff --git a/src/plugins/coreplugin/progressmanager/processprogress.cpp b/src/plugins/coreplugin/progressmanager/processprogress.cpp
index e9348fce41..e3769f54ca 100644
--- a/src/plugins/coreplugin/progressmanager/processprogress.cpp
+++ b/src/plugins/coreplugin/progressmanager/processprogress.cpp
@@ -6,7 +6,7 @@
#include "progressmanager.h"
#include "../coreplugintr.h"
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QFutureWatcher>
diff --git a/src/plugins/coreplugin/systemsettings.cpp b/src/plugins/coreplugin/systemsettings.cpp
index 9a0a40d02e..d06d14f8da 100644
--- a/src/plugins/coreplugin/systemsettings.cpp
+++ b/src/plugins/coreplugin/systemsettings.cpp
@@ -276,13 +276,7 @@ public:
connect(helpCrashReportingButton, &QAbstractButton::clicked, this, [this] {
showHelpDialog(Tr::tr("Crash Reporting"), CorePlugin::msgCrashpadInformation());
});
- connect(&s.enableCrashReporting, &BaseAspect::changed, this, [this] {
- const QString restartText = Tr::tr("The change will take effect after restart.");
- Core::RestartDialog restartDialog(Core::ICore::dialogParent(), restartText);
- restartDialog.exec();
- if (restartDialog.result() == QDialog::Accepted)
- apply();
- });
+ connect(&s.enableCrashReporting, &BaseAspect::changed, this, &SystemSettingsWidget::apply);
updateClearCrashWidgets();
connect(m_clearCrashReportsButton, &QPushButton::clicked, this, [&] {
diff --git a/src/plugins/coreplugin/themechooser.cpp b/src/plugins/coreplugin/themechooser.cpp
index e59c6d83a3..2903e573ca 100644
--- a/src/plugins/coreplugin/themechooser.cpp
+++ b/src/plugins/coreplugin/themechooser.cpp
@@ -176,7 +176,11 @@ static void addThemesFromPath(const QString &path, QList<ThemeEntry> *themes)
const QStringList themeList = themeDir.entryList();
for (const QString &fileName : std::as_const(themeList)) {
QString id = QFileInfo(fileName).completeBaseName();
- themes->append(ThemeEntry(Id::fromString(id), themeDir.absoluteFilePath(fileName)));
+ bool addTheme = true;
+ if (Core::ICore::isQtDesignStudio())
+ addTheme = id.startsWith("design");
+ if (addTheme)
+ themes->append(ThemeEntry(Id::fromString(id), themeDir.absoluteFilePath(fileName)));
}
}
diff --git a/src/plugins/cppcheck/cppcheckrunner.h b/src/plugins/cppcheck/cppcheckrunner.h
index 686a6c9eb9..0ec49aceb5 100644
--- a/src/plugins/cppcheck/cppcheckrunner.h
+++ b/src/plugins/cppcheck/cppcheckrunner.h
@@ -4,7 +4,7 @@
#pragma once
#include <utils/filepath.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QHash>
#include <QTimer>
diff --git a/src/plugins/cppeditor/cppcodemodelsettings.cpp b/src/plugins/cppeditor/cppcodemodelsettings.cpp
index 82cca5752b..ae34e5ddbb 100644
--- a/src/plugins/cppeditor/cppcodemodelsettings.cpp
+++ b/src/plugins/cppeditor/cppcodemodelsettings.cpp
@@ -15,7 +15,8 @@
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/macroexpander.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QDateTime>
diff --git a/src/plugins/cppeditor/cppmodelmanager.cpp b/src/plugins/cppeditor/cppmodelmanager.cpp
index 27aab615b7..bd5100b1b5 100644
--- a/src/plugins/cppeditor/cppmodelmanager.cpp
+++ b/src/plugins/cppeditor/cppmodelmanager.cpp
@@ -66,7 +66,7 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/savefile.h>
#include <utils/synchronizedvalue.h>
diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp
index e8d3978b73..44999e5561 100644
--- a/src/plugins/debugger/cdb/cdbengine.cpp
+++ b/src/plugins/debugger/cdb/cdbengine.cpp
@@ -43,7 +43,7 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h
index 2b644c68ba..e5c50b8e41 100644
--- a/src/plugins/debugger/cdb/cdbengine.h
+++ b/src/plugins/debugger/cdb/cdbengine.h
@@ -10,7 +10,7 @@
#include <projectexplorer/devicesupport/idevice.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QElapsedTimer>
diff --git a/src/plugins/debugger/dap/dapclient.cpp b/src/plugins/debugger/dap/dapclient.cpp
index 327d62da7a..00fe60da02 100644
--- a/src/plugins/debugger/dap/dapclient.cpp
+++ b/src/plugins/debugger/dap/dapclient.cpp
@@ -4,7 +4,7 @@
#include "dapclient.h"
#include "qjsonarray.h"
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QDebug>
#include <QJsonDocument>
diff --git a/src/plugins/debugger/dap/dapclient.h b/src/plugins/debugger/dap/dapclient.h
index 887b32eebb..92acb0ddb1 100644
--- a/src/plugins/debugger/dap/dapclient.h
+++ b/src/plugins/debugger/dap/dapclient.h
@@ -5,7 +5,7 @@
#include <debugger/debuggerengine.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QLoggingCategory>
diff --git a/src/plugins/debugger/dap/dapengine.cpp b/src/plugins/debugger/dap/dapengine.cpp
index 7f4f515e80..d2f94b9fee 100644
--- a/src/plugins/debugger/dap/dapengine.cpp
+++ b/src/plugins/debugger/dap/dapengine.cpp
@@ -31,7 +31,7 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/temporarydirectory.h>
diff --git a/src/plugins/debugger/dap/dapengine.h b/src/plugins/debugger/dap/dapengine.h
index 19edd25598..b5b72eec07 100644
--- a/src/plugins/debugger/dap/dapengine.h
+++ b/src/plugins/debugger/dap/dapengine.h
@@ -5,7 +5,7 @@
#include <debugger/debuggerengine.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QLoggingCategory>
#include <QVariant>
diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp
index fde7704c5b..1aa77ae3ea 100644
--- a/src/plugins/debugger/debuggerengine.cpp
+++ b/src/plugins/debugger/debuggerengine.cpp
@@ -54,7 +54,7 @@
#include <utils/basetreeview.h>
#include <utils/checkablemessagebox.h>
#include <utils/macroexpander.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processhandle.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
diff --git a/src/plugins/debugger/debuggeritem.cpp b/src/plugins/debugger/debuggeritem.cpp
index 591b4bd38b..c5b1bc7f6b 100644
--- a/src/plugins/debugger/debuggeritem.cpp
+++ b/src/plugins/debugger/debuggeritem.cpp
@@ -14,7 +14,7 @@
#include <utils/filepath.h>
#include <utils/hostosinfo.h>
#include <utils/macroexpander.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
#include <utils/utilsicons.h>
diff --git a/src/plugins/debugger/debuggeritemmanager.cpp b/src/plugins/debugger/debuggeritemmanager.cpp
index b4fb2af2de..b547ddab01 100644
--- a/src/plugins/debugger/debuggeritemmanager.cpp
+++ b/src/plugins/debugger/debuggeritemmanager.cpp
@@ -26,7 +26,7 @@
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
#include <utils/persistentsettings.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/treemodel.h>
#include <utils/winutils.h>
diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp
index 8ef76e7a75..e26f5f85b8 100644
--- a/src/plugins/debugger/debuggerruncontrol.cpp
+++ b/src/plugins/debugger/debuggerruncontrol.cpp
@@ -35,7 +35,7 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/portlist.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/temporarydirectory.h>
#include <utils/temporaryfile.h>
diff --git a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp
index 1cb7c6402c..be983cf584 100644
--- a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp
+++ b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp
@@ -12,7 +12,7 @@
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/variablechooser.h>
diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp
index 0f4fe9d772..08c572ae70 100644
--- a/src/plugins/debugger/gdb/gdbengine.cpp
+++ b/src/plugins/debugger/gdb/gdbengine.cpp
@@ -36,7 +36,7 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h
index a396ad5507..33eec793a4 100644
--- a/src/plugins/debugger/gdb/gdbengine.h
+++ b/src/plugins/debugger/gdb/gdbengine.h
@@ -15,7 +15,7 @@
#include <debugger/outputcollector.h>
#include <utils/id.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QProcess>
#include <QTextCodec>
diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp
index 2934f1db44..0e53afad35 100644
--- a/src/plugins/debugger/lldb/lldbengine.cpp
+++ b/src/plugins/debugger/lldb/lldbengine.cpp
@@ -31,7 +31,7 @@
#include <projectexplorer/runcontrol.h>
#include <utils/environment.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
diff --git a/src/plugins/debugger/lldb/lldbengine.h b/src/plugins/debugger/lldb/lldbengine.h
index 57cbd1e7c7..faf2cc3f6f 100644
--- a/src/plugins/debugger/lldb/lldbengine.h
+++ b/src/plugins/debugger/lldb/lldbengine.h
@@ -11,7 +11,7 @@
#include <debugger/debuggertooltipmanager.h>
#include <debugger/debuggerprotocol.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QPointer>
#include <QProcess>
diff --git a/src/plugins/debugger/moduleshandler.cpp b/src/plugins/debugger/moduleshandler.cpp
index 9072d02463..3d72f70457 100644
--- a/src/plugins/debugger/moduleshandler.cpp
+++ b/src/plugins/debugger/moduleshandler.cpp
@@ -11,7 +11,7 @@
#include <utils/basetreeview.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/treemodel.h>
diff --git a/src/plugins/debugger/pdb/pdbengine.cpp b/src/plugins/debugger/pdb/pdbengine.cpp
index 96a06c7b81..e0bfa126f5 100644
--- a/src/plugins/debugger/pdb/pdbengine.cpp
+++ b/src/plugins/debugger/pdb/pdbengine.cpp
@@ -21,7 +21,7 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <coreplugin/idocument.h>
diff --git a/src/plugins/debugger/pdb/pdbengine.h b/src/plugins/debugger/pdb/pdbengine.h
index 1e365b36a7..49dceb2441 100644
--- a/src/plugins/debugger/pdb/pdbengine.h
+++ b/src/plugins/debugger/pdb/pdbengine.h
@@ -4,7 +4,7 @@
#pragma once
#include <debugger/debuggerengine.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QVariant>
diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp
index ee578c3af3..dda927f0f1 100644
--- a/src/plugins/debugger/qml/qmlengine.cpp
+++ b/src/plugins/debugger/qml/qmlengine.cpp
@@ -38,7 +38,7 @@
#include <utils/basetreeview.h>
#include <utils/fileinprojectfinder.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/treemodel.h>
diff --git a/src/plugins/debugger/terminal.cpp b/src/plugins/debugger/terminal.cpp
index 18092f132f..e96e8b072d 100644
--- a/src/plugins/debugger/terminal.cpp
+++ b/src/plugins/debugger/terminal.cpp
@@ -12,7 +12,7 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
diff --git a/src/plugins/docker/dockerapi.cpp b/src/plugins/docker/dockerapi.cpp
index 900e5bb18b..537b6c66f3 100644
--- a/src/plugins/docker/dockerapi.cpp
+++ b/src/plugins/docker/dockerapi.cpp
@@ -7,7 +7,7 @@
#include <coreplugin/progressmanager/progressmanager.h>
#include <utils/async.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QLoggingCategory>
diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp
index f0a8aeb12c..c54bd000eb 100644
--- a/src/plugins/docker/dockerdevice.cpp
+++ b/src/plugins/docker/dockerdevice.cpp
@@ -46,7 +46,7 @@
#include <utils/overridecursor.h>
#include <utils/pathlisteditor.h>
#include <utils/port.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
#include <utils/sortfiltermodel.h>
diff --git a/src/plugins/effectcomposer/compositionnode.cpp b/src/plugins/effectcomposer/compositionnode.cpp
index b63258e0af..d939e2283a 100644
--- a/src/plugins/effectcomposer/compositionnode.cpp
+++ b/src/plugins/effectcomposer/compositionnode.cpp
@@ -113,6 +113,9 @@ void CompositionNode::parse(const QString &effectName, const QString &qenPath, c
m_fragmentCode = EffectUtils::codeFromJsonArray(json.value("fragmentCode").toArray());
m_vertexCode = EffectUtils::codeFromJsonArray(json.value("vertexCode").toArray());
+ if (json.contains("extraMargin"))
+ m_extraMargin = json.value("extraMargin").toInt();
+
if (json.contains("enabled"))
m_isEnabled = json["enabled"].toBool();
@@ -130,6 +133,10 @@ void CompositionNode::parse(const QString &effectName, const QString &qenPath, c
m_unifomrsModel.addUniform(uniform);
m_uniforms.append(uniform);
g_propertyData.insert(uniform->name(), uniform->value());
+ if (uniform->type() == Uniform::Type::Define) {
+ // Changing defines requires rebaking the shaders
+ connect(uniform, &Uniform::uniformValueChanged, this, &CompositionNode::rebakeRequested);
+ }
}
// Seek through code to get tags
diff --git a/src/plugins/effectcomposer/compositionnode.h b/src/plugins/effectcomposer/compositionnode.h
index 589954ec87..433468688a 100644
--- a/src/plugins/effectcomposer/compositionnode.h
+++ b/src/plugins/effectcomposer/compositionnode.h
@@ -52,10 +52,13 @@ public:
int decRefCount();
void setRefCount(int count);
+ int extraMargin() const { return m_extraMargin; }
+
signals:
void uniformsModelChanged();
void isEnabledChanged();
void isDepencyChanged();
+ void rebakeRequested();
private:
void parse(const QString &effectName, const QString &qenPath, const QJsonObject &json);
@@ -69,6 +72,7 @@ private:
QString m_id;
bool m_isEnabled = true;
int m_refCount = 0;
+ int m_extraMargin = 0;
QList<Uniform *> m_uniforms;
diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp
index 3f376d0633..394b85aa55 100644
--- a/src/plugins/effectcomposer/effectcomposermodel.cpp
+++ b/src/plugins/effectcomposer/effectcomposermodel.cpp
@@ -9,16 +9,13 @@
#include "syntaxhighlighterdata.h"
#include "uniform.h"
-#include <qmlprojectmanager/qmlproject.h>
-
-#include <projectexplorer/project.h>
#include <projectexplorer/projecttree.h>
#include <projectexplorer/target.h>
#include <qtsupport/qtkitaspect.h>
#include <utils/qtcassert.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <modelnodeoperations.h>
@@ -52,6 +49,8 @@ static bool writeToFile(const QByteArray &buf, const QString &filename, FileType
EffectComposerModel::EffectComposerModel(QObject *parent)
: QAbstractListModel{parent}
{
+ m_rebakeTimer.setSingleShot(true);
+ connect(&m_rebakeTimer, &QTimer::timeout, this, &EffectComposerModel::bakeShaders);
}
QHash<int, QByteArray> EffectComposerModel::roleNames() const
@@ -110,10 +109,7 @@ void EffectComposerModel::addNode(const QString &nodeQenPath)
{
beginResetModel();
auto *node = new CompositionNode({}, nodeQenPath);
- connect(qobject_cast<EffectComposerUniformsModel *>(node->uniformsModel()),
- &EffectComposerUniformsModel::dataChanged, this, [this] {
- setHasUnsavedChanges(true);
- });
+ connectCompositionNode(node);
const QList<QString> requiredNodes = node->requiredNodes();
if (requiredNodes.size() > 0) {
@@ -125,10 +121,7 @@ void EffectComposerModel::addNode(const QString &nodeQenPath)
const QString path = EffectUtils::nodesSourcesPath() + "/common/" + requiredId + ".qen";
auto requiredNode = new CompositionNode({}, path);
- connect(qobject_cast<EffectComposerUniformsModel *>(requiredNode->uniformsModel()),
- &EffectComposerUniformsModel::dataChanged, this, [this] {
- setHasUnsavedChanges(true);
- });
+ connectCompositionNode(requiredNode);
requiredNode->setRefCount(1);
m_nodes.prepend(requiredNode);
}
@@ -170,6 +163,7 @@ void EffectComposerModel::moveNode(int fromIdx, int toIdx)
void EffectComposerModel::removeNode(int idx)
{
beginResetModel();
+ m_rebakeTimer.stop();
CompositionNode *node = m_nodes.takeAt(idx);
const QStringList reqNodes = node->requiredNodes();
@@ -196,6 +190,7 @@ void EffectComposerModel::removeNode(int idx)
void EffectComposerModel::clear(bool clearName)
{
beginResetModel();
+ m_rebakeTimer.stop();
qDeleteAll(m_nodes);
m_nodes.clear();
endResetModel();
@@ -255,7 +250,7 @@ void EffectComposerModel::setVertexShader(const QString &newVertexShader)
m_vertexShader = newVertexShader;
}
-const QString &EffectComposerModel::qmlComponentString() const
+QString EffectComposerModel::qmlComponentString() const
{
return m_qmlComponentString;
}
@@ -421,7 +416,7 @@ void EffectComposerModel::setEffectError(const QString &errorMessage, int type,
Q_EMIT effectErrorChanged();
}
-QString variantAsDataString(const Uniform::Type type, const QVariant &variant)
+QString variantAsDataString(const Uniform::Type type, const Uniform::Type controlType, const QVariant &variant)
{
QString s;
switch (type) {
@@ -429,6 +424,7 @@ QString variantAsDataString(const Uniform::Type type, const QVariant &variant)
s = variant.toBool() ? QString("true") : QString("false");
break;
case Uniform::Type::Int:
+ case Uniform::Type::Channel:
s = QString::number(variant.toInt());
break;
case Uniform::Type::Float:
@@ -473,7 +469,12 @@ QString variantAsDataString(const Uniform::Type type, const QVariant &variant)
}
case Uniform::Type::Sampler:
case Uniform::Type::Define: {
- s = variant.toString();
+ if (controlType == Uniform::Type::Int)
+ s = QString::number(variant.toInt());
+ else if (controlType == Uniform::Type::Bool)
+ s = variant.toBool() ? QString("true") : QString("false");
+ else
+ s = variant.toString();
break;
}
}
@@ -489,6 +490,8 @@ QJsonObject nodeToJson(const CompositionNode &node)
nodeObject.insert("enabled", node.isEnabled());
nodeObject.insert("version", 1);
nodeObject.insert("id", node.id());
+ if (node.extraMargin())
+ nodeObject.insert("extraMargin", node.extraMargin());
// Add properties
QJsonArray propertiesArray;
@@ -497,17 +500,26 @@ QJsonObject nodeToJson(const CompositionNode &node)
QJsonObject uniformObject;
uniformObject.insert("name", QString(uniform->name()));
QString type = Uniform::stringFromType(uniform->type());
+
uniformObject.insert("type", type);
+ if (uniform->type() == Uniform::Type::Define || uniform->type() == Uniform::Type::Channel) {
+ QString controlType = Uniform::stringFromType(uniform->controlType());
+ if (controlType != type)
+ uniformObject.insert("controlType", controlType);
+ }
+
if (!uniform->displayName().isEmpty())
uniformObject.insert("displayName", QString(uniform->displayName()));
- QString value = variantAsDataString(uniform->type(), uniform->value());
+ QString value = variantAsDataString(uniform->type(), uniform->controlType(),
+ uniform->value());
if (uniform->type() == Uniform::Type::Sampler)
value = QFileInfo(value).fileName();
uniformObject.insert("value", value);
- QString defaultValue = variantAsDataString(uniform->type(), uniform->defaultValue());
+ QString defaultValue = variantAsDataString(uniform->type(), uniform->controlType(),
+ uniform->defaultValue());
if (uniform->type() == Uniform::Type::Sampler) {
defaultValue = QFileInfo(value).fileName();
if (uniform->enableMipmap())
@@ -520,9 +532,14 @@ QJsonObject nodeToJson(const CompositionNode &node)
|| uniform->type() == Uniform::Type::Int
|| uniform->type() == Uniform::Type::Vec2
|| uniform->type() == Uniform::Type::Vec3
- || uniform->type() == Uniform::Type::Vec4) {
- uniformObject.insert("minValue", variantAsDataString(uniform->type(), uniform->minValue()));
- uniformObject.insert("maxValue", variantAsDataString(uniform->type(), uniform->maxValue()));
+ || uniform->type() == Uniform::Type::Vec4
+ || uniform->controlType() == Uniform::Type::Int) {
+ uniformObject.insert("minValue", variantAsDataString(uniform->type(),
+ uniform->controlType(),
+ uniform->minValue()));
+ uniformObject.insert("maxValue", variantAsDataString(uniform->type(),
+ uniform->controlType(),
+ uniform->maxValue()));
}
if (!uniform->customValue().isEmpty())
uniformObject.insert("customValue", uniform->customValue());
@@ -557,32 +574,235 @@ QJsonObject nodeToJson(const CompositionNode &node)
return nodeObject;
}
-QString EffectComposerModel::getQmlEffectString()
+QString EffectComposerModel::getGeneratedMessage() const
{
QString s;
- // _isEffectItem is type var to hide it from property view
- QString header{
+ QString header {
R"(
// Created with Qt Design Studio (version %1), %2
+// Do not manually edit this file, it will be overwritten if effect is modified in Qt Design Studio.
+)"
+ };
+
+ s += header.arg(qApp->applicationVersion(), QDateTime::currentDateTime().toString());
+ return s;
+}
+
+QString EffectComposerModel::getDesignerSpecifics() const
+{
+ QString s;
+
+ s += getGeneratedMessage();
+
+ s +=
+R"(
+import QtQuick
+import QtQuick.Layouts
+import HelperWidgets
+import StudioTheme as StudioTheme
+
+Column {
+)";
+
+ if (m_shaderFeatures.enabled(ShaderFeatures::Time)
+ || m_shaderFeatures.enabled(ShaderFeatures::Frame)) {
+ QString animSec =
+R"(
+ Section {
+ caption: "%1"
+ width: parent.width
+
+ SectionLayout {
+ PropertyLabel {
+ text: "%2"
+ tooltip: "%3"
+ }
+
+ SecondColumnLayout {
+ CheckBox {
+ text: backendValues.timeRunning.valueToString
+ backendValue: backendValues.timeRunning
+ implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+ ExpandingSpacer {}
+ }
+)";
+ s += animSec.arg(tr("Animation"), tr("Running"), tr("Set this property to animate the effect."));
+
+ if (m_shaderFeatures.enabled(ShaderFeatures::Time)) {
+ QString timeProp =
+R"(
+ PropertyLabel {
+ text: "%1"
+ tooltip: "%2"
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: 0
+ maximumValue: 9999999
+ decimals: 2
+ stepSize: .01
+ backendValue: backendValues.animatedTime
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+ ExpandingSpacer {}
+ }
+)";
+ s += timeProp.arg(tr("Time"), tr("This property allows explicit control of current animation time."));
+ }
+ if (m_shaderFeatures.enabled(ShaderFeatures::Frame)) {
+ QString frameProp =
+R"(
+ PropertyLabel {
+ text: "%1"
+ tooltip: "%2"
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: 0
+ maximumValue: 99999999
+ decimals: 0
+ stepSize: 1
+ backendValue: backendValues.animatedFrame
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+ ExpandingSpacer {}
+ }
+)";
+ s += frameProp.arg(tr("Frame"), tr("This property allows explicit control of current animation frame."));
+ }
+
+ s += " }\n";
+ s += " }\n";
+ }
+
+ if (m_shaderFeatures.enabled(ShaderFeatures::Source) && m_extraMargin) {
+ QString generalSection =
+ R"(
+ Section {
+ caption: "%1"
+ width: parent.width
+
+ SectionLayout {
+ PropertyLabel {
+ text: "%2"
+ tooltip: "%3"
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: 0
+ maximumValue: 1000
+ decimals: 0
+ stepSize: 1
+ sliderIndicatorVisible: true
+ backendValue: backendValues.extraMargin
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+ ExpandingSpacer {}
+ }
+ }
+ }
+)";
+ s += generalSection.arg(tr("General"), tr("Extra Margin"),
+ tr("This property specifies how much of extra space is reserved for the effect outside the parent geometry."));
+ }
+
+ for (const auto &node : std::as_const(m_nodes)) {
+ const QList<Uniform *> uniforms = static_cast<EffectComposerUniformsModel *>(
+ node->uniformsModel())->uniforms();
+ QString secStr =
+R"(
+ Section {
+ caption: "%1"
+ width: parent.width
+
+ SectionLayout {
+)";
+ secStr = secStr.arg(node->name());
+
+ const QString oldSecStr = secStr;
+
+ for (Uniform *uniform : uniforms)
+ secStr += uniform->getDesignerSpecifics();
+
+ // Only add the section if it has actual content
+ if (oldSecStr != secStr) {
+ secStr += " }\n";
+ secStr += " }\n";
+ s += secStr;
+ }
+ }
+
+ s += "}\n";
+
+ return s;
+}
+
+QString EffectComposerModel::getQmlEffectString()
+{
+ QString s;
+
+ s += getGeneratedMessage();
+
+ // _isEffectItem is type var to hide it from property view
+ QString header {
+R"(
import QtQuick
Item {
id: rootItem
- // This is an internal property used by tooling to identify effect items
- property var _isEffectItem
+ // Use visible property to show and hide the effect.
+ visible: true
+ // This is an internal property used by tooling to identify effect items. Do not modify.
+ property bool _isEffectItem
+
+ // This is an internal property used to manage the effect. Do not modify.
property Item _oldParent: null
)"
};
- s += header.arg(qApp->applicationVersion(), QDateTime::currentDateTime().toString());
+ s += header;
if (m_shaderFeatures.enabled(ShaderFeatures::Source)) {
- s += " // This is the main source for the effect\n";
- s += " property Item source: null\n";
+ QString sourceStr{
+R"(
+ // This is the main source for the effect. Set internally to the current parent item. Do not modify.
+ property Item source: null
+)"
+ };
+
+ QString extraMarginStr{
+R"(
+ // This property specifies how much of extra space is reserved for the effect outside the parent geometry.
+ // It should be sufficient for most use cases but if the application uses extreme values it may be necessary to
+ // increase this value.
+ property int extraMargin: %1
+
+ onExtraMarginChanged: setupSourceRect()
+
+ function setupSourceRect() {
+ if (rootItem.source) {
+ var width = source.width + extraMargin * 2
+ var height = source.height + extraMargin * 2
+ source.layer.sourceRect = Qt.rect(-extraMargin, -extraMargin, width, height)
+ }
+ }
+)"
+ };
+ s += sourceStr;
+ if (m_extraMargin)
+ s += extraMarginStr.arg(m_extraMargin);
}
if (m_shaderFeatures.enabled(ShaderFeatures::Time)
|| m_shaderFeatures.enabled(ShaderFeatures::Frame)) {
@@ -610,10 +830,27 @@ R"(
}
if (parent) {
_oldParent = parent
+ if (visible) {
+ parent.layer.enabled = true
+ parent.layer.effect = effectComponent
+ }
+ %1
+ %3
+ }
+ }
+
+ onVisibleChanged: {
+ if (visible) {
parent.layer.enabled = true
parent.layer.effect = effectComponent
- %1
+ source = parent
+ %3
+ } else {
+ parent.layer.enabled = false
+ parent.layer.effect = null
+ source = null
}
+ parent.update()
}
)"
};
@@ -632,10 +869,13 @@ R"(
parentChanged = parentChanged.arg(mipmap1, mipmap2);
}
- parentChanged = parentChanged.arg(m_shaderFeatures.enabled(ShaderFeatures::Source)
- ? QString("source = parent") : QString(),
- m_shaderFeatures.enabled(ShaderFeatures::Source)
- ? QString("source = null") : QString());
+ if (m_shaderFeatures.enabled(ShaderFeatures::Source)) {
+ parentChanged = parentChanged.arg(QString("source = parent"),
+ QString("source = null"),
+ m_extraMargin ? QString("setupSourceRect()") : QString());
+ } else {
+ parentChanged = parentChanged.arg(QString(), QString(), QString());
+ }
s += parentChanged;
// Custom properties
@@ -666,6 +906,12 @@ R"(
void EffectComposerModel::saveComposition(const QString &name)
{
+ if (name.isEmpty() || name.size() < 3 || name[0].isLower()) {
+ QString error = QString("Error: Couldn't save composition '%1', name is invalid").arg(name);
+ qWarning() << error;
+ return;
+ }
+
const QString effectsAssetsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory();
const QString path = effectsAssetsDir + QDir::separator() + name + ".qep";
auto saveFile = QFile(path);
@@ -675,6 +921,8 @@ void EffectComposerModel::saveComposition(const QString &name)
return;
}
+ updateExtraMargin();
+
QJsonObject json;
// File format version
json.insert("version", 1);
@@ -696,9 +944,9 @@ void EffectComposerModel::saveComposition(const QString &name)
saveFile.write(jsonDoc.toJson());
saveFile.close();
setCurrentComposition(name);
- setHasUnsavedChanges(false);
saveResources(name);
+ setHasUnsavedChanges(false);
}
void EffectComposerModel::openComposition(const QString &path)
@@ -757,10 +1005,7 @@ void EffectComposerModel::openComposition(const QString &path)
for (const auto &nodeElement : nodesArray) {
auto *node = new CompositionNode(effectName, {}, nodeElement.toObject());
- connect(qobject_cast<EffectComposerUniformsModel *>(node->uniformsModel()),
- &EffectComposerUniformsModel::dataChanged, this, [this] {
- setHasUnsavedChanges(true);
- });
+ connectCompositionNode(node);
m_nodes.append(node);
const QStringList reqIds = node->requiredNodes();
for (const QString &reqId : reqIds)
@@ -799,24 +1044,29 @@ void EffectComposerModel::saveResources(const QString &name)
// Get effects dir
const Utils::FilePath effectsResDir = QmlDesigner::ModelNodeOperations::getEffectsImportDirectory();
const QString effectsResPath = effectsResDir.pathAppended(name).toString() + QDir::separator();
+ Utils::FilePath effectPath = Utils::FilePath::fromString(effectsResPath);
// Create the qmldir for effects
- Utils::FilePath qmldirPath = effectsResDir.resolvePath(QStringLiteral("qmldir"));
+ QString qmldirFileName("qmldir");
+ Utils::FilePath qmldirPath = effectsResDir.resolvePath(qmldirFileName);
QString qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray()));
if (qmldirContent.isEmpty()) {
qmldirContent.append("module Effects\n");
qmldirPath.writeFileContents(qmldirContent.toUtf8());
}
+ Utils::FilePaths oldFiles;
+ QStringList newFileNames;
+
// Create effect folder if not created
- Utils::FilePath effectPath = Utils::FilePath::fromString(effectsResPath);
- if (!effectPath.exists()) {
- QDir effectDir(effectsResDir.toString());
- effectDir.mkdir(name);
- }
+ if (!effectPath.exists())
+ effectPath.createDir();
+ else
+ oldFiles = effectPath.dirEntries(QDir::Files);
// Create effect qmldir
- qmldirPath = effectPath.resolvePath(QStringLiteral("qmldir"));
+ newFileNames.append(qmldirFileName);
+ qmldirPath = effectPath.resolvePath(qmldirFileName);
qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray()));
if (qmldirContent.isEmpty()) {
qmldirContent.append("module Effects.");
@@ -829,6 +1079,19 @@ void EffectComposerModel::saveResources(const QString &name)
qmldirPath.writeFileContents(qmldirContent.toUtf8());
}
+ // Create designer folder if not created
+ const Utils::FilePath designerPath = effectPath.pathAppended("designer");
+ if (!designerPath.exists())
+ designerPath.createDir();
+
+ // Create designer property sheet
+ // Since this is in subdir, no need to add it to newFileNames
+ QString specContent = getDesignerSpecifics();
+ QString specFileName("%1SpecificsDynamic.qml");
+ specFileName = specFileName.arg(name);
+ Utils::FilePath specPath = designerPath.resolvePath(specFileName);
+ specPath.writeFileContents(specContent.toUtf8());
+
// Create the qml file
QString qmlComponentString = getQmlEffectString();
QStringList qmlStringList = qmlComponentString.split('\n');
@@ -848,13 +1111,16 @@ void EffectComposerModel::saveResources(const QString &name)
const QString qmlString = qmlStringList.join('\n');
QString qmlFilePath = effectsResPath + qmlFilename;
writeToFile(qmlString.toUtf8(), qmlFilePath, FileType::Text);
+ newFileNames.append(qmlFilename);
// Save shaders and images
QStringList sources = {m_vertexShaderFilename, m_fragmentShaderFilename};
QStringList dests = {vsFilename, fsFilename};
+ QHash<QString, Uniform *> fileNameToUniformHash;
const QList<Uniform *> uniforms = allUniforms();
- for (const Uniform *uniform : uniforms) {
+ bool hasSampler = false;
+ for (Uniform *uniform : uniforms) {
if (uniform->type() == Uniform::Type::Sampler && !uniform->value().toString().isEmpty()) {
QString imagePath = uniform->value().toString();
QFileInfo fi(imagePath);
@@ -865,6 +1131,8 @@ void EffectComposerModel::saveResources(const QString &name)
}
sources.append(imagePath);
dests.append(imageFilename);
+ fileNameToUniformHash.insert(imageFilename, uniform);
+ hasSampler = true;
}
}
@@ -884,11 +1152,31 @@ void EffectComposerModel::saveResources(const QString &name)
for (int i = 0; i < sources.count(); ++i) {
Utils::FilePath source = Utils::FilePath::fromString(sources[i]);
Utils::FilePath target = Utils::FilePath::fromString(effectsResPath + dests[i]);
+ newFileNames.append(target.fileName());
if (target.exists() && source.fileName() != target.fileName())
target.removeFile(); // Remove existing file for update
-
if (!source.copyFile(target))
qWarning() << __FUNCTION__ << " Failed to copy file: " << source;
+
+ if (fileNameToUniformHash.contains(dests[i])) {
+ Uniform *uniform = fileNameToUniformHash[dests[i]];
+ const QVariant newValue = target.toString();
+ uniform->setDefaultValue(newValue);
+ uniform->setValue(newValue);
+ }
+ }
+
+ // Delete old content that was not overwritten
+ // We ignore subdirectories, as currently subdirs only contain fixed content
+ for (const Utils::FilePath &oldFile : oldFiles) {
+ if (!newFileNames.contains(oldFile.fileName()))
+ oldFile.removeFile();
+ }
+
+ // Refresh UI to update sampler UrlChoosers
+ if (hasSampler) {
+ beginResetModel();
+ endResetModel();
}
emit resourcesSaved(QString("Effects.%1.%1").arg(name).toUtf8(), effectPath);
@@ -924,7 +1212,13 @@ QString EffectComposerModel::valueAsString(const Uniform &uniform)
return getImageElementName(uniform, true);
} else if (uniform.type() == Uniform::Type::Color) {
return QString("\"%1\"").arg(uniform.value().toString());
+ } else if (uniform.type() == Uniform::Type::Channel) {
+ return QString::number(uniform.value().toInt());
} else if (uniform.type() == Uniform::Type::Define) {
+ if (uniform.controlType() == Uniform::Type::Int)
+ return QString::number(uniform.value().toInt());
+ else if (uniform.controlType() == Uniform::Type::Bool)
+ return uniform.value().toBool() ? QString("1") : QString("0");
return uniform.value().toString();
} else {
qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type())).toLatin1();
@@ -939,6 +1233,7 @@ QString EffectComposerModel::valueAsBinding(const Uniform &uniform)
|| uniform.type() == Uniform::Type::Int
|| uniform.type() == Uniform::Type::Float
|| uniform.type() == Uniform::Type::Color
+ || uniform.type() == Uniform::Type::Channel
|| uniform.type() == Uniform::Type::Define) {
return "g_propertyData." + uniform.name();
} else if (uniform.type() == Uniform::Type::Vec2) {
@@ -985,6 +1280,8 @@ QString EffectComposerModel::valueAsVariable(const Uniform &uniform)
} else if (uniform.type() == Uniform::Type::Color) {
QColor c = uniform.value().value<QColor>();
return QString("vec4(%1, %2, %3, %4)").arg(c.redF(), c.greenF(), c.blueF(), c.alphaF());
+ } else if (uniform.type() == Uniform::Type::Channel) {
+ return QString::number(uniform.value().toInt());
} else {
qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type())).toLatin1();
return QString();
@@ -1023,10 +1320,8 @@ const QString EffectComposerModel::getDefineProperties()
QString s;
for (Uniform *uniform : uniforms) {
// TODO: Check if uniform is already added.
- if (uniform->type() == Uniform::Type::Define) {
- QString defineValue = uniform->value().toString();
- s += QString("#define %1 %2\n").arg(uniform->name(), defineValue);
- }
+ if (uniform->type() == Uniform::Type::Define)
+ s += QString("#define %1 %2\n").arg(uniform->name(), valueAsString(*uniform));
}
if (!s.isEmpty())
s += '\n';
@@ -1363,13 +1658,13 @@ void EffectComposerModel::updateCustomUniforms()
void EffectComposerModel::createFiles()
{
- if (QFileInfo(m_vertexShaderFilename).exists())
+ if (QFileInfo::exists(m_vertexShaderFilename))
QFile(m_vertexShaderFilename).remove();
- if (QFileInfo(m_fragmentShaderFilename).exists())
+ if (QFileInfo::exists(m_fragmentShaderFilename))
QFile(m_fragmentShaderFilename).remove();
- if (QFileInfo(m_vertexShaderPreviewFilename).exists())
+ if (QFileInfo::exists(m_vertexShaderPreviewFilename))
QFile(m_vertexShaderPreviewFilename).remove();
- if (QFileInfo(m_fragmentShaderPreviewFilename).exists())
+ if (QFileInfo::exists(m_fragmentShaderPreviewFilename))
QFile(m_fragmentShaderPreviewFilename).remove();
auto vertexShaderFile = QTemporaryFile(QDir::tempPath() + "/dsem_XXXXXX.vert.qsb");
@@ -1503,14 +1798,17 @@ void EffectComposerModel::setIsEnabled(bool enabled)
emit isEnabledChanged();
}
-// Returns name for image mipmap property.
-// e.g. "myImage" -> "myImageMipmap".
-QString EffectComposerModel::mipmapPropertyName(const QString &name) const
+bool EffectComposerModel::hasValidTarget() const
{
- QString simplifiedName = name.simplified();
- simplifiedName = simplifiedName.remove(' ');
- simplifiedName += "Mipmap";
- return simplifiedName;
+ return m_hasValidTarget;
+}
+
+void EffectComposerModel::setHasValidTarget(bool validTarget)
+{
+ if (m_hasValidTarget == validTarget)
+ return;
+ m_hasValidTarget = validTarget;
+ emit hasValidTargetChanged();
}
QString EffectComposerModel::getQmlImagesString(bool localFiles)
@@ -1522,24 +1820,26 @@ QString EffectComposerModel::getQmlImagesString(bool localFiles)
QString imagePath = uniform->value().toString();
// For preview, generate image element even if path is empty, as changing uniform values
// will not trigger qml code regeneration
- if (localFiles && imagePath.isEmpty())
- continue;
+ if (localFiles) {
+ if (imagePath.isEmpty())
+ continue;
+ QFileInfo fi(imagePath);
+ imagePath = fi.fileName();
+ imagesString += QString(" property url %1Url: \"%2\"\n")
+ .arg(uniform->name(), imagePath);
+ }
imagesString += " Image {\n";
QString simplifiedName = getImageElementName(*uniform, localFiles);
imagesString += QString(" id: %1\n").arg(simplifiedName);
imagesString += " anchors.fill: parent\n";
// File paths are absolute, return as local when requested
if (localFiles) {
- QFileInfo fi(imagePath);
- imagePath = fi.fileName();
- imagesString += QString(" source: \"%1\"\n").arg(imagePath);
+ imagesString += QString(" source: rootItem.%1Url\n").arg(uniform->name());
} else {
imagesString += QString(" source: g_propertyData.%1\n").arg(uniform->name());
if (uniform->enableMipmap())
imagesString += " mipmap: true\n";
- else
- QString mipmapProperty = mipmapPropertyName(uniform->name());
}
imagesString += " visible: false\n";
@@ -1613,6 +1913,12 @@ QString EffectComposerModel::getQmlComponentString(bool localFiles)
s += l2 + "vertexShader: 'file:///" + vertFile + "'\n";
s += l2 + "fragmentShader: 'file:///" + fragFile + "'\n";
s += l2 + "anchors.fill: " + (localFiles ? "rootItem.source" : "parent") + "\n";
+ if (localFiles) {
+ if (m_extraMargin)
+ s += l2 + "anchors.margins: -rootItem.extraMargin\n";
+ } else {
+ s += l2 + "anchors.margins: -root.extraMargin\n";
+ }
if (m_shaderFeatures.enabled(ShaderFeatures::GridMesh)) {
QString gridSize = QString("%1, %2").arg(m_shaderFeatures.gridMeshWidth())
.arg(m_shaderFeatures.gridMeshHeight());
@@ -1636,6 +1942,25 @@ QString EffectComposerModel::getQmlComponentString(bool localFiles)
return s;
}
+void EffectComposerModel::connectCompositionNode(CompositionNode *node)
+{
+ connect(qobject_cast<EffectComposerUniformsModel *>(node->uniformsModel()),
+ &EffectComposerUniformsModel::dataChanged, this, [this] {
+ setHasUnsavedChanges(true);
+ });
+ connect(node, &CompositionNode::rebakeRequested, this, [this] {
+ // This can come multiple times in a row in response to property changes, so let's buffer it
+ m_rebakeTimer.start(200);
+ });
+}
+
+void EffectComposerModel::updateExtraMargin()
+{
+ m_extraMargin = 0;
+ for (CompositionNode *node : std::as_const(m_nodes))
+ m_extraMargin = qMax(node->extraMargin(), m_extraMargin);
+}
+
QString EffectComposerModel::currentComposition() const
{
return m_currentComposition;
diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h
index 69afc503ea..e92213d12a 100644
--- a/src/plugins/effectcomposer/effectcomposermodel.h
+++ b/src/plugins/effectcomposer/effectcomposermodel.h
@@ -12,6 +12,7 @@
#include <QMap>
#include <QRegularExpression>
#include <QTemporaryFile>
+#include <QTimer>
namespace ProjectExplorer {
class Target;
@@ -47,7 +48,7 @@ class EffectComposerModel : public QAbstractListModel
Q_PROPERTY(bool hasUnsavedChanges MEMBER m_hasUnsavedChanges WRITE setHasUnsavedChanges NOTIFY hasUnsavedChangesChanged)
Q_PROPERTY(bool shadersUpToDate READ shadersUpToDate WRITE setShadersUpToDate NOTIFY shadersUpToDateChanged)
Q_PROPERTY(bool isEnabled READ isEnabled WRITE setIsEnabled NOTIFY isEnabledChanged)
- Q_PROPERTY(QString qmlComponentString READ qmlComponentString)
+ Q_PROPERTY(bool hasValidTarget READ hasValidTarget WRITE setHasValidTarget NOTIFY hasValidTargetChanged)
Q_PROPERTY(QString currentComposition READ currentComposition WRITE setCurrentComposition NOTIFY currentCompositionChanged)
public:
@@ -77,13 +78,16 @@ public:
bool isEnabled() const;
void setIsEnabled(bool enabled);
+ bool hasValidTarget() const;
+ void setHasValidTarget(bool validTarget);
+
QString fragmentShader() const;
void setFragmentShader(const QString &newFragmentShader);
QString vertexShader() const;
void setVertexShader(const QString &newVertexShader);
- const QString &qmlComponentString() const;
+ Q_INVOKABLE QString qmlComponentString() const;
Q_INVOKABLE void updateQmlComponent();
@@ -110,6 +114,7 @@ signals:
void effectErrorChanged();
void shadersUpToDateChanged();
void isEnabledChanged();
+ void hasValidTargetChanged();
void shadersBaked();
void currentCompositionChanged();
void nodesChanged();
@@ -170,14 +175,18 @@ private:
void bakeShaders();
void saveResources(const QString &name);
- QString mipmapPropertyName(const QString &name) const;
QString getQmlImagesString(bool localFiles);
QString getQmlComponentString(bool localFiles);
+ QString getGeneratedMessage() const;
+ QString getDesignerSpecifics() const;
+
+ void connectCompositionNode(CompositionNode *node);
+ void updateExtraMargin();
QList<CompositionNode *> m_nodes;
int m_selectedIndex = -1;
- bool m_isEmpty = true;
+ bool m_isEmpty = false; // Init to false to force initial bake after setup
bool m_hasUnsavedChanges = false;
// True when shaders haven't changed since last baking
bool m_shadersUpToDate = true;
@@ -207,7 +216,10 @@ private:
QString m_qmlComponentString;
bool m_loadComponentImages = true;
bool m_isEnabled = true;
+ bool m_hasValidTarget = false;
QString m_currentComposition;
+ QTimer m_rebakeTimer;
+ int m_extraMargin = 0;
const QRegularExpression m_spaceReg = QRegularExpression("\\s+");
};
diff --git a/src/plugins/effectcomposer/effectcomposernodesmodel.cpp b/src/plugins/effectcomposer/effectcomposernodesmodel.cpp
index 28fc943446..1d1d405e81 100644
--- a/src/plugins/effectcomposer/effectcomposernodesmodel.cpp
+++ b/src/plugins/effectcomposer/effectcomposernodesmodel.cpp
@@ -68,7 +68,10 @@ void EffectComposerNodesModel::loadModel()
QDirIterator itEffects(categoryPath.toString(), {"*.qen"}, QDir::Files);
while (itEffects.hasNext()) {
itEffects.next();
- effects.push_back(new EffectNode(itEffects.filePath()));
+ auto node = new EffectNode(itEffects.filePath());
+ if (!node->defaultImagesHash().isEmpty())
+ m_defaultImagesHash.insert(node->name(), node->defaultImagesHash());
+ effects.push_back(node);
}
catName[0] = catName[0].toUpper(); // capitalize first letter
@@ -108,4 +111,9 @@ void EffectComposerNodesModel::updateCanBeAdded(const QStringList &uniforms)
}
}
+QHash<QString, QString> EffectComposerNodesModel::defaultImagesForNode(const QString &name) const
+{
+ return m_defaultImagesHash.value(name);
+}
+
} // namespace EffectComposer
diff --git a/src/plugins/effectcomposer/effectcomposernodesmodel.h b/src/plugins/effectcomposer/effectcomposernodesmodel.h
index 7edabcd9f9..ce914a5e6d 100644
--- a/src/plugins/effectcomposer/effectcomposernodesmodel.h
+++ b/src/plugins/effectcomposer/effectcomposernodesmodel.h
@@ -32,12 +32,15 @@ public:
void updateCanBeAdded(const QStringList &uniforms);
+ QHash<QString, QString> defaultImagesForNode(const QString &name) const;
+
private:
QString nodesSourcesPath() const;
QList<EffectNodesCategory *> m_categories;
bool m_probeNodesDir = false;
bool m_modelLoaded = false;
+ QHash<QString, QHash<QString, QString>> m_defaultImagesHash;
};
} // namespace EffectComposer
diff --git a/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp b/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp
index e547883b9d..c2c162a87e 100644
--- a/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp
+++ b/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp
@@ -19,6 +19,7 @@ QHash<int, QByteArray> EffectComposerUniformsModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[NameRole] = "uniformName";
+ roles[DisplayNameRole] = "uniformDisplayName";
roles[DescriptionRole] = "uniformDescription";
roles[ValueRole] = "uniformValue";
roles[BackendValueRole] = "uniformBackendValue";
@@ -26,6 +27,7 @@ QHash<int, QByteArray> EffectComposerUniformsModel::roleNames() const
roles[MinValueRole] = "uniformMinValue";
roles[MaxValueRole] = "uniformMaxValue";
roles[TypeRole] = "uniformType";
+ roles[ControlTypeRole] = "uniformControlType";
roles[UseCustomValueRole] = "uniformUseCustomValue";
return roles;
}
@@ -57,7 +59,9 @@ bool EffectComposerUniformsModel::setData(const QModelIndex &index, const QVaria
int idx = value.toString().indexOf("file:");
QString path = idx > 0 ? updatedValue.right(updatedValue.size() - idx - 5) : updatedValue;
- updatedValue = QUrl::fromLocalFile(path).toString();
+
+ if (idx == -1)
+ updatedValue = QUrl::fromLocalFile(path).toString();
uniform->setValue(updatedValue);
g_propertyData.insert(uniform->name(), updatedValue);
@@ -71,6 +75,14 @@ bool EffectComposerUniformsModel::setData(const QModelIndex &index, const QVaria
return true;
}
+bool EffectComposerUniformsModel::resetData(int row)
+{
+ QModelIndex idx = index(row, 0);
+ QTC_ASSERT(idx.isValid(), return false);
+
+ return setData(idx, idx.data(DefaultValueRole), ValueRole);
+}
+
void EffectComposerUniformsModel::resetModel()
{
beginResetModel();
diff --git a/src/plugins/effectcomposer/effectcomposeruniformsmodel.h b/src/plugins/effectcomposer/effectcomposeruniformsmodel.h
index 3e2d44c626..fc82194fdf 100644
--- a/src/plugins/effectcomposer/effectcomposeruniformsmodel.h
+++ b/src/plugins/effectcomposer/effectcomposeruniformsmodel.h
@@ -20,6 +20,7 @@ public:
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
+ Q_INVOKABLE bool resetData(int row);
void resetModel();
@@ -30,6 +31,7 @@ public:
private:
enum Roles {
NameRole = Qt::UserRole + 1,
+ DisplayNameRole,
DescriptionRole,
ValueRole,
BackendValueRole,
@@ -37,6 +39,7 @@ private:
MaxValueRole,
MinValueRole,
TypeRole,
+ ControlTypeRole,
UseCustomValueRole
};
diff --git a/src/plugins/effectcomposer/effectcomposerview.cpp b/src/plugins/effectcomposer/effectcomposerview.cpp
index 3c1901a3d4..ed50c389a8 100644
--- a/src/plugins/effectcomposer/effectcomposerview.cpp
+++ b/src/plugins/effectcomposer/effectcomposerview.cpp
@@ -69,9 +69,15 @@ void EffectComposerView::customNotification([[maybe_unused]] const AbstractView
[[maybe_unused]] const QList<QmlDesigner::ModelNode> &nodeList,
const QList<QVariant> &data)
{
- if (identifier == "open_effectcomposer_composition" && data.count() > 0) {
+ if (data.size() < 1)
+ return;
+
+ if (identifier == "open_effectcomposer_composition") {
const QString compositionPath = data[0].toString();
m_widget->openComposition(compositionPath);
+ } else if (identifier == "effectcomposer_effects_deleted") {
+ if (data[0].toStringList().contains(m_widget->effectComposerModel()->currentComposition()))
+ m_widget->effectComposerModel()->clear(true);
}
}
@@ -79,19 +85,19 @@ void EffectComposerView::modelAttached(QmlDesigner::Model *model)
{
AbstractView::modelAttached(model);
- m_widget->effectComposerNodesModel()->loadModel();
QString currProjectPath = QmlDesigner::DocumentManager::currentProjectDirPath().toString();
if (m_currProjectPath != currProjectPath) { // starting a new project
+ m_widget->effectComposerNodesModel()->loadModel();
m_widget->effectComposerModel()->clear(true);
m_widget->effectComposerModel()->setIsEnabled(
!QmlDesigner::DesignerMcuManager::instance().isMCUProject());
+ m_widget->initView();
}
m_currProjectPath = currProjectPath;
- m_widget->initView();
}
void EffectComposerView::modelAboutToBeDetached(QmlDesigner::Model *model)
@@ -99,4 +105,19 @@ void EffectComposerView::modelAboutToBeDetached(QmlDesigner::Model *model)
AbstractView::modelAboutToBeDetached(model);
}
+void EffectComposerView::selectedNodesChanged(const QList<QmlDesigner::ModelNode> & selectedNodeList,
+ const QList<QmlDesigner::ModelNode> & /*lastSelectedNodeList*/)
+{
+ bool hasValidTarget = false;
+
+ for (const QmlDesigner::ModelNode &node : selectedNodeList) {
+ if (node.metaInfo().isQtQuickItem()) {
+ hasValidTarget = true;
+ break;
+ }
+ }
+
+ m_widget->effectComposerModel()->setHasValidTarget(hasValidTarget);
+}
+
} // namespace EffectComposer
diff --git a/src/plugins/effectcomposer/effectcomposerview.h b/src/plugins/effectcomposer/effectcomposerview.h
index c7a381cb7d..49a7b32621 100644
--- a/src/plugins/effectcomposer/effectcomposerview.h
+++ b/src/plugins/effectcomposer/effectcomposerview.h
@@ -4,6 +4,7 @@
#pragma once
#include "abstractview.h"
+#include "modelnode.h"
#include <coreplugin/icontext.h>
@@ -35,6 +36,8 @@ public:
// AbstractView
void modelAttached(QmlDesigner::Model *model) override;
void modelAboutToBeDetached(QmlDesigner::Model *model) override;
+ void selectedNodesChanged(const QList<QmlDesigner::ModelNode> &selectedNodeList,
+ const QList<QmlDesigner::ModelNode> &lastSelectedNodeList) override;
private:
void customNotification(const AbstractView *view, const QString &identifier,
diff --git a/src/plugins/effectcomposer/effectcomposerwidget.cpp b/src/plugins/effectcomposer/effectcomposerwidget.cpp
index 5cad29baa1..dd1d5e926d 100644
--- a/src/plugins/effectcomposer/effectcomposerwidget.cpp
+++ b/src/plugins/effectcomposer/effectcomposerwidget.cpp
@@ -117,6 +117,11 @@ EffectComposerWidget::EffectComposerWidget(EffectComposerView *view)
}
});
+ connect(m_effectComposerModel.data(), &EffectComposerModel::modelAboutToBeReset,
+ this, [this] {
+ QMetaObject::invokeMethod(quickWidget()->rootObject(), "storeExpandStates");
+ });
+
connect(Core::EditorManager::instance(), &Core::EditorManager::aboutToSave, this, [this] {
if (m_effectComposerModel->hasUnsavedChanges()) {
QString compName = m_effectComposerModel->currentComposition();
@@ -187,6 +192,16 @@ QPoint EffectComposerWidget::globalPos(const QPoint &point) const
return point;
}
+QString EffectComposerWidget::uniformDefaultImage(const QString &nodeName, const QString &uniformName) const
+{
+ return m_effectComposerNodesModel->defaultImagesForNode(nodeName).value(uniformName);
+}
+
+QString EffectComposerWidget::imagesPath() const
+{
+ return Core::ICore::resourcePath("qmldesigner/effectComposerNodes/images").toString();
+}
+
QSize EffectComposerWidget::sizeHint() const
{
return {420, 420};
diff --git a/src/plugins/effectcomposer/effectcomposerwidget.h b/src/plugins/effectcomposer/effectcomposerwidget.h
index c7349f3033..aa9cb750d8 100644
--- a/src/plugins/effectcomposer/effectcomposerwidget.h
+++ b/src/plugins/effectcomposer/effectcomposerwidget.h
@@ -50,6 +50,9 @@ public:
Q_INVOKABLE void doOpenComposition();
Q_INVOKABLE QRect screenRect() const;
Q_INVOKABLE QPoint globalPos(const QPoint &point) const;
+ Q_INVOKABLE QString uniformDefaultImage(const QString &nodeName,
+ const QString &uniformName) const;
+ Q_INVOKABLE QString imagesPath() const;
QSize sizeHint() const override;
diff --git a/src/plugins/effectcomposer/effectnode.cpp b/src/plugins/effectcomposer/effectnode.cpp
index 73b0068154..6128e3a196 100644
--- a/src/plugins/effectcomposer/effectnode.cpp
+++ b/src/plugins/effectcomposer/effectnode.cpp
@@ -31,8 +31,13 @@ EffectNode::EffectNode(const QString &qenPath)
m_description = node.description();
const QList<Uniform *> uniforms = node.uniforms();
- for (const Uniform *uniform : uniforms)
+ for (const Uniform *uniform : uniforms) {
m_uniformNames.insert(uniform->name());
+ if (uniform->type() == Uniform::Type::Sampler) {
+ m_defaultImagesHash.insert(
+ uniform->name(), uniform->defaultValue().toString());
+ }
+ }
}
QString EffectNode::name() const
diff --git a/src/plugins/effectcomposer/effectnode.h b/src/plugins/effectcomposer/effectnode.h
index 6322ff29f8..dff553a2de 100644
--- a/src/plugins/effectcomposer/effectnode.h
+++ b/src/plugins/effectcomposer/effectnode.h
@@ -25,6 +25,7 @@ public:
QString name() const;
QString description() const;
QString qenPath() const;
+ QHash<QString, QString> defaultImagesHash() const { return m_defaultImagesHash; }
void setCanBeAdded(bool enabled);
@@ -40,6 +41,7 @@ private:
QUrl m_iconPath;
bool m_canBeAdded = true;
QSet<QString> m_uniformNames;
+ QHash<QString, QString> m_defaultImagesHash;
};
} // namespace EffectComposer
diff --git a/src/plugins/effectcomposer/uniform.cpp b/src/plugins/effectcomposer/uniform.cpp
index e8fd77d631..590b38b423 100644
--- a/src/plugins/effectcomposer/uniform.cpp
+++ b/src/plugins/effectcomposer/uniform.cpp
@@ -44,6 +44,9 @@ Uniform::Uniform(const QString &effectName, const QJsonObject &propObj, const QS
g_propertyData[mipmapProperty] = m_enableMipmap;
}
+ QString controlType = propObj.value("controlType").toString();
+ m_controlType = controlType.isEmpty() ? m_type : Uniform::typeFromString(controlType);
+
m_customValue = propObj.value("customValue").toString();
m_useCustomValue = getBoolValue(propObj.value("useCustomValue"), false);
@@ -61,12 +64,22 @@ Uniform::Type Uniform::type() const
return m_type;
}
+Uniform::Type Uniform::controlType() const
+{
+ return m_controlType;
+}
+
// String representation of the type for qml
QString Uniform::typeName() const
{
return Uniform::stringFromType(m_type);
}
+QString Uniform::controlTypeName() const
+{
+ return Uniform::stringFromType(m_controlType);
+}
+
QVariant Uniform::value() const
{
return m_value;
@@ -81,7 +94,21 @@ void Uniform::setValue(const QVariant &newValue)
{
if (m_value != newValue) {
m_value = newValue;
+
emit uniformValueChanged();
+
+ if (m_type == Type::Sampler) {
+ m_backendValue->setValue(newValue);
+ emit uniformBackendValueChanged();
+ }
+ }
+}
+
+void Uniform::setDefaultValue(const QVariant &newValue)
+{
+ if (m_defaultValue != newValue) {
+ m_defaultValue = newValue;
+ emit uniformDefaultValueChanged();
}
}
@@ -145,6 +172,191 @@ bool Uniform::enableMipmap() const
return m_enableMipmap;
}
+QString Uniform::getDesignerSpecifics() const
+{
+ QString specs;
+
+ // Uniforms with custom values or define type do not result in exported properties
+ if (!m_customValue.isEmpty() || m_type == Type::Define)
+ return specs;
+
+ auto appendVectorSpinbox = [this, &specs](const QString subProp, const QString &label,
+ float minVal, float maxVal, bool firstCol) {
+ QString vecSpec =
+R"(
+ SpinBox {
+ minimumValue: %4
+ maximumValue: %5
+ decimals: 2
+ stepSize: .01
+ backendValue: backendValues.%1_%2
+ implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
+
+ ControlLabel {
+ text: "%3"
+ }
+)";
+ specs += vecSpec.arg(m_name).arg(subProp).arg(label).arg(minVal).arg(maxVal);
+ if (firstCol)
+ specs += " Spacer { implicitWidth: StudioTheme.Values.controlGap }\n";
+ };
+
+ auto appendVectorSeparator = [&specs]() {
+ specs +=
+R"(
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {}
+
+ SecondColumnLayout {
+)";
+
+ };
+
+ specs +=
+R"(
+ PropertyLabel {
+ text: "%1"
+ tooltip: "%2"
+ }
+
+ SecondColumnLayout {
+)";
+ QString desc = m_description;
+ desc.replace("\n", "\\n");
+ desc.replace("\"", "\\\"");
+ specs = specs.arg(m_displayName, desc);
+
+ switch (m_type) {
+ case Type::Bool: {
+ QString typeSpec =
+R"(
+ CheckBox {
+ text: backendValues.%1.valueToString
+ backendValue: backendValues.%1
+ implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+)";
+ specs += typeSpec.arg(m_name);
+ break;
+ }
+ case Type::Int: {
+ if (m_controlType == Uniform::Type::Int) {
+ QString typeSpec =
+ R"(
+ SpinBox {
+ minimumValue: %1
+ maximumValue: %2
+ decimals: 0
+ stepSize: 1
+ sliderIndicatorVisible: true
+ backendValue: backendValues.%3
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+ )";
+ specs += typeSpec.arg(m_minValue.toString(), m_maxValue.toString(), m_name);
+ } else if (m_controlType == Uniform::Type::Channel) {
+ QString typeSpec =
+ R"(
+ ComboBox {
+ model: ["R", "G", "B", "A"]
+ backendValue: backendValues.%1
+ manualMapping: true
+
+ onCompressedActivated: backendValue.value = currentIndex
+ onValueFromBackendChanged: currentIndex = backendValue.value
+ Component.onCompleted: currentIndex = backendValue.value
+ }
+ )";
+ specs += typeSpec.arg(m_name);
+ }
+ }
+ break;
+ case Type::Float: {
+ QString typeSpec =
+R"(
+ SpinBox {
+ minimumValue: %1
+ maximumValue: %2
+ decimals: 2
+ stepSize: .01
+ sliderIndicatorVisible: true
+ backendValue: backendValues.%3
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+)";
+ specs += typeSpec.arg(m_minValue.toString(), m_maxValue.toString(), m_name);
+ break;
+ }
+ case Type::Vec2: {
+ QVector2D minVal = m_minValue.value<QVector2D>();
+ QVector2D maxVal = m_maxValue.value<QVector2D>();
+ appendVectorSpinbox("x", tr("X"), minVal.x(), maxVal.x(), true);
+ appendVectorSpinbox("y", tr("Y"), minVal.y(), maxVal.y(), false);
+ break;
+ }
+ case Type::Vec3: {
+ QVector3D minVal = m_minValue.value<QVector3D>();
+ QVector3D maxVal = m_maxValue.value<QVector3D>();
+ appendVectorSpinbox("x", tr("X"), minVal.x(), maxVal.x(), true);
+ appendVectorSpinbox("y", tr("Y"), minVal.y(), maxVal.y(), false);
+ appendVectorSeparator();
+ appendVectorSpinbox("z", tr("Z"), minVal.z(), maxVal.z(), true);
+ break;
+ }
+ case Type::Vec4: {
+ QVector4D minVal = m_minValue.value<QVector4D>();
+ QVector4D maxVal = m_maxValue.value<QVector4D>();
+ appendVectorSpinbox("x", tr("X"), minVal.x(), maxVal.x(), true);
+ appendVectorSpinbox("y", tr("Y"), minVal.y(), maxVal.y(), false);
+ appendVectorSeparator();
+ appendVectorSpinbox("z", tr("Z"), minVal.z(), maxVal.z(), true);
+ appendVectorSpinbox("w", tr("W"), minVal.w(), maxVal.w(), false);
+ break;
+ }
+ case Type::Color: {
+ QString typeSpec =
+R"(
+ ColorEditor {
+ backendValue: backendValues.%1
+ supportGradient: false
+ }
+)";
+ specs += typeSpec.arg(m_name);
+ break;
+ }
+ case Type::Sampler: {
+ QString typeSpec =
+R"(
+ UrlChooser {
+ backendValue: backendValues.%1
+ }
+)";
+ specs += typeSpec.arg(m_name + "Url");
+ break;
+ }
+ case Type::Define:
+ default:
+ break;
+ }
+
+ specs +=
+R"(
+ ExpandingSpacer {}
+ }
+)";
+
+ return specs;
+}
+
// Returns name for image mipmap property.
// e.g. "myImage" -> "myImageMipmap".
QString Uniform::mipmapPropertyName(const QString &name) const
@@ -216,6 +428,15 @@ QVariant Uniform::getInitializedVariant(bool maxValue)
return maxValue ? QVector4D(1.0, 1.0, 1.0, 1.0) : QVector4D(0.0, 0.0, 0.0, 0.0);
case Uniform::Type::Color:
return maxValue ? QColor::fromRgbF(1.0f, 1.0f, 1.0f, 1.0f) : QColor::fromRgbF(0.0f, 0.0f, 0.0f, 0.0f);
+ case Uniform::Type::Channel:
+ return 3; // sets default channel to alpha
+ case Uniform::Type::Define:
+ if (m_controlType == Uniform::Type::Bool)
+ return maxValue ? true : false;
+ else if (m_controlType == Uniform::Type::Int)
+ return maxValue ? 100 : 0;
+ else
+ return QVariant();
default:
return QVariant();
}
@@ -259,10 +480,14 @@ QVariant Uniform::valueStringToVariant(const QString &value)
}
break;
case Type::Sampler:
+ case Type::Channel:
variant = value;
break;
case Uniform::Type::Define:
- variant = value;
+ if (m_controlType == Uniform::Type::Bool)
+ variant = (value == "true");
+ else
+ variant = value;
break;
}
@@ -287,6 +512,8 @@ QString Uniform::stringFromType(Uniform::Type type, bool isShader)
return isShader ? QString("vec4") : QString("color");
else if (type == Type::Sampler)
return "sampler2D";
+ else if (type == Type::Channel)
+ return "channel";
else if (type == Type::Define)
return "define";
@@ -312,6 +539,8 @@ Uniform::Type Uniform::typeFromString(const QString &typeString)
return Uniform::Type::Color;
else if (typeString == "sampler2D" || typeString == "image") //TODO: change image to sample2D in all QENs
return Uniform::Type::Sampler;
+ else if (typeString == "channel")
+ return Uniform::Type::Channel;
else if (typeString == "define")
return Uniform::Type::Define;
@@ -335,6 +564,8 @@ QString Uniform::typeToProperty(Uniform::Type type)
return "vector4d";
else if (type == Uniform::Type::Color)
return "color";
+ else if (type == Uniform::Type::Channel)
+ return "channel";
else if (type == Uniform::Type::Sampler || type == Uniform::Type::Define)
return "var";
diff --git a/src/plugins/effectcomposer/uniform.h b/src/plugins/effectcomposer/uniform.h
index 83f15224ae..d3a3900651 100644
--- a/src/plugins/effectcomposer/uniform.h
+++ b/src/plugins/effectcomposer/uniform.h
@@ -18,14 +18,16 @@ class Uniform : public QObject
{
Q_OBJECT
- Q_PROPERTY(QString uniformName MEMBER m_displayName CONSTANT)
+ Q_PROPERTY(QString uniformName MEMBER m_name CONSTANT)
+ Q_PROPERTY(QString uniformDisplayName MEMBER m_displayName CONSTANT)
Q_PROPERTY(QString uniformType READ typeName CONSTANT)
+ Q_PROPERTY(QString uniformControlType READ controlTypeName CONSTANT)
Q_PROPERTY(QString uniformDescription READ description CONSTANT)
Q_PROPERTY(QVariant uniformValue READ value WRITE setValue NOTIFY uniformValueChanged)
Q_PROPERTY(QVariant uniformBackendValue READ backendValue NOTIFY uniformBackendValueChanged)
Q_PROPERTY(QVariant uniformMinValue MEMBER m_minValue CONSTANT)
Q_PROPERTY(QVariant uniformMaxValue MEMBER m_maxValue CONSTANT)
- Q_PROPERTY(QVariant uniformDefaultValue MEMBER m_defaultValue CONSTANT)
+ Q_PROPERTY(QVariant uniformDefaultValue MEMBER m_defaultValue NOTIFY uniformDefaultValueChanged)
Q_PROPERTY(QVariant uniformUseCustomValue MEMBER m_useCustomValue CONSTANT)
public:
@@ -39,16 +41,20 @@ public:
Vec4,
Color,
Sampler,
+ Channel,
Define
};
Uniform(const QString &effectName, const QJsonObject &props, const QString &qenPath);
Type type() const;
+ Type controlType() const;
QString typeName() const;
+ QString controlTypeName() const;
QVariant value() const;
void setValue(const QVariant &newValue);
+ void setDefaultValue(const QVariant &newValue);
QVariant backendValue() const;
@@ -69,6 +75,7 @@ public:
void setEnabled(bool newEnabled);
bool enableMipmap() const;
+ QString getDesignerSpecifics() const;
static QString stringFromType(Uniform::Type type, bool isShader = false);
static Uniform::Type typeFromString(const QString &typeString);
@@ -77,6 +84,7 @@ public:
signals:
void uniformValueChanged();
void uniformBackendValueChanged();
+ void uniformDefaultValueChanged();
private:
QString mipmapPropertyName(const QString &name) const;
@@ -90,6 +98,7 @@ private:
QString m_qenPath;
Type m_type;
+ Type m_controlType;
QVariant m_value;
QVariant m_defaultValue;
QVariant m_minValue;
diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp
index 07b5ec3b74..7a7554da9e 100644
--- a/src/plugins/fakevim/fakevimplugin.cpp
+++ b/src/plugins/fakevim/fakevimplugin.cpp
@@ -51,7 +51,7 @@
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/stylehelper.h>
diff --git a/src/plugins/fossil/fossilclient.cpp b/src/plugins/fossil/fossilclient.cpp
index d24935595a..662785930d 100644
--- a/src/plugins/fossil/fossilclient.cpp
+++ b/src/plugins/fossil/fossilclient.cpp
@@ -16,7 +16,7 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processenums.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
diff --git a/src/plugins/genericprojectmanager/genericproject.cpp b/src/plugins/genericprojectmanager/genericproject.cpp
index b201b2e1fe..17af30f9ea 100644
--- a/src/plugins/genericprojectmanager/genericproject.cpp
+++ b/src/plugins/genericprojectmanager/genericproject.cpp
@@ -37,7 +37,7 @@
#include <utils/algorithm.h>
#include <utils/filesystemwatcher.h>
#include <utils/fileutils.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QDir>
diff --git a/src/plugins/git/branchmodel.cpp b/src/plugins/git/branchmodel.cpp
index 99e6e01005..aca355f2cb 100644
--- a/src/plugins/git/branchmodel.cpp
+++ b/src/plugins/git/branchmodel.cpp
@@ -10,7 +10,7 @@
#include <solutions/tasking/tasktreerunner.h>
#include <utils/environment.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
diff --git a/src/plugins/git/branchview.cpp b/src/plugins/git/branchview.cpp
index c99789a100..555db9b01b 100644
--- a/src/plugins/git/branchview.cpp
+++ b/src/plugins/git/branchview.cpp
@@ -21,7 +21,7 @@
#include <utils/elidinglabel.h>
#include <utils/fancylineedit.h>
#include <utils/navigationtreeview.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
diff --git a/src/plugins/git/changeselectiondialog.cpp b/src/plugins/git/changeselectiondialog.cpp
index 12cc91eddb..24daa93829 100644
--- a/src/plugins/git/changeselectiondialog.cpp
+++ b/src/plugins/git/changeselectiondialog.cpp
@@ -12,7 +12,7 @@
#include <utils/completinglineedit.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/theme/theme.h>
#include <QCompleter>
diff --git a/src/plugins/git/gerrit/gerritmodel.cpp b/src/plugins/git/gerrit/gerritmodel.cpp
index 8af786ac5e..f0c3d0cadf 100644
--- a/src/plugins/git/gerrit/gerritmodel.cpp
+++ b/src/plugins/git/gerrit/gerritmodel.cpp
@@ -10,7 +10,7 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinterface.h>
#include <QApplication>
diff --git a/src/plugins/git/gerrit/gerritplugin.cpp b/src/plugins/git/gerrit/gerritplugin.cpp
index 3ef90358b9..749084c20a 100644
--- a/src/plugins/git/gerrit/gerritplugin.cpp
+++ b/src/plugins/git/gerrit/gerritplugin.cpp
@@ -22,7 +22,7 @@
#include <coreplugin/vcsmanager.h>
#include <utils/environment.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinterface.h>
#include <vcsbase/vcsoutputwindow.h>
diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp
index eb857c4685..296b790e2f 100644
--- a/src/plugins/git/gitclient.cpp
+++ b/src/plugins/git/gitclient.cpp
@@ -27,7 +27,7 @@
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <utils/mimeutils.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/temporaryfile.h>
#include <utils/theme/theme.h>
diff --git a/src/plugins/git/gitgrep.cpp b/src/plugins/git/gitgrep.cpp
index 5675fe0f48..73feffd68f 100644
--- a/src/plugins/git/gitgrep.cpp
+++ b/src/plugins/git/gitgrep.cpp
@@ -17,7 +17,7 @@
#include <utils/environment.h>
#include <utils/fancylineedit.h>
#include <utils/filesearch.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QCheckBox>
diff --git a/src/plugins/git/mergetool.h b/src/plugins/git/mergetool.h
index 727f9a85df..1fd32e3a4f 100644
--- a/src/plugins/git/mergetool.h
+++ b/src/plugins/git/mergetool.h
@@ -3,7 +3,7 @@
#pragma once
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QObject>
#include <QStringList>
diff --git a/src/plugins/gitlab/gitlabclonedialog.cpp b/src/plugins/gitlab/gitlabclonedialog.cpp
index abd442de08..ae5390e941 100644
--- a/src/plugins/gitlab/gitlabclonedialog.cpp
+++ b/src/plugins/gitlab/gitlabclonedialog.cpp
@@ -23,7 +23,7 @@
#include <utils/infolabel.h>
#include <utils/mimeutils.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <vcsbase/vcsbaseplugin.h>
diff --git a/src/plugins/gitlab/queryrunner.h b/src/plugins/gitlab/queryrunner.h
index 08afc56e1b..bdb5e06034 100644
--- a/src/plugins/gitlab/queryrunner.h
+++ b/src/plugins/gitlab/queryrunner.h
@@ -4,7 +4,7 @@
#pragma once
#include <utils/id.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QObject>
diff --git a/src/plugins/haskell/haskellmanager.cpp b/src/plugins/haskell/haskellmanager.cpp
index b57b4177ff..e62fa04bb2 100644
--- a/src/plugins/haskell/haskellmanager.cpp
+++ b/src/plugins/haskell/haskellmanager.cpp
@@ -13,7 +13,7 @@
#include <utils/algorithm.h>
#include <utils/commandline.h>
#include <utils/mimeutils.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QDir>
#include <QFileInfo>
diff --git a/src/plugins/incredibuild/cmakecommandbuilder.cpp b/src/plugins/incredibuild/cmakecommandbuilder.cpp
index 2f0dd751ae..8732601cbc 100644
--- a/src/plugins/incredibuild/cmakecommandbuilder.cpp
+++ b/src/plugins/incredibuild/cmakecommandbuilder.cpp
@@ -8,7 +8,7 @@
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildsteplist.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <cmakeprojectmanager/cmakeprojectconstants.h> // Compile-time only
diff --git a/src/plugins/ios/iosbuildstep.cpp b/src/plugins/ios/iosbuildstep.cpp
index 8edfb3dbc2..c9d396ddd4 100644
--- a/src/plugins/ios/iosbuildstep.cpp
+++ b/src/plugins/ios/iosbuildstep.cpp
@@ -20,7 +20,7 @@
#include <projectexplorer/toolchain.h>
#include <utils/filepath.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QGridLayout>
diff --git a/src/plugins/ios/iosconfigurations.cpp b/src/plugins/ios/iosconfigurations.cpp
index d576202fbd..ff995c6c2c 100644
--- a/src/plugins/ios/iosconfigurations.cpp
+++ b/src/plugins/ios/iosconfigurations.cpp
@@ -34,7 +34,7 @@
#include <utils/algorithm.h>
#include <utils/futuresynchronizer.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QDir>
diff --git a/src/plugins/ios/iosdeploystep.cpp b/src/plugins/ios/iosdeploystep.cpp
index 4d0117f627..596dc84d6b 100644
--- a/src/plugins/ios/iosdeploystep.cpp
+++ b/src/plugins/ios/iosdeploystep.cpp
@@ -21,7 +21,7 @@
#include <solutions/tasking/tasktree.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/temporaryfile.h>
#include <QFile>
diff --git a/src/plugins/ios/iosdevice.cpp b/src/plugins/ios/iosdevice.cpp
index a99f9a338d..e81c263672 100644
--- a/src/plugins/ios/iosdevice.cpp
+++ b/src/plugins/ios/iosdevice.cpp
@@ -20,7 +20,7 @@
#include <utils/layoutbuilder.h>
#include <utils/portlist.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <solutions/tasking/tasktree.h>
diff --git a/src/plugins/ios/iosdsymbuildstep.cpp b/src/plugins/ios/iosdsymbuildstep.cpp
index 0c07ff4182..c0aa4a06ee 100644
--- a/src/plugins/ios/iosdsymbuildstep.cpp
+++ b/src/plugins/ios/iosdsymbuildstep.cpp
@@ -22,7 +22,7 @@
#include <qtsupport/qtkitaspect.h>
#include <qtsupport/qtparser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
diff --git a/src/plugins/ios/iosprobe.cpp b/src/plugins/ios/iosprobe.cpp
index e934ea01f9..883263ae72 100644
--- a/src/plugins/ios/iosprobe.cpp
+++ b/src/plugins/ios/iosprobe.cpp
@@ -4,7 +4,7 @@
#include "iosprobe.h"
#include <utils/algorithm.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QFileInfo>
#include <QLoggingCategory>
diff --git a/src/plugins/ios/iosrunconfiguration.cpp b/src/plugins/ios/iosrunconfiguration.cpp
index 6f5384cc9b..676fb68724 100644
--- a/src/plugins/ios/iosrunconfiguration.cpp
+++ b/src/plugins/ios/iosrunconfiguration.cpp
@@ -23,7 +23,7 @@
#include <utils/algorithm.h>
#include <utils/filepath.h>
#include <utils/layoutbuilder.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QAction>
diff --git a/src/plugins/ios/iosrunner.cpp b/src/plugins/ios/iosrunner.cpp
index 02d47e914f..9e2174c80b 100644
--- a/src/plugins/ios/iosrunner.cpp
+++ b/src/plugins/ios/iosrunner.cpp
@@ -27,7 +27,7 @@
#include <qmldebug/qmloutputparser.h>
#include <utils/fileutils.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/url.h>
#include <utils/utilsicons.h>
diff --git a/src/plugins/ios/iossimulator.cpp b/src/plugins/ios/iossimulator.cpp
index e030fe911e..ed9a5abf66 100644
--- a/src/plugins/ios/iossimulator.cpp
+++ b/src/plugins/ios/iossimulator.cpp
@@ -8,7 +8,7 @@
#include <projectexplorer/kitaspects.h>
#include <utils/port.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QMapIterator>
diff --git a/src/plugins/ios/iostoolhandler.cpp b/src/plugins/ios/iostoolhandler.cpp
index 811404b2da..2f3fe4f288 100644
--- a/src/plugins/ios/iostoolhandler.cpp
+++ b/src/plugins/ios/iostoolhandler.cpp
@@ -14,7 +14,7 @@
#include <utils/async.h>
#include <utils/futuresynchronizer.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/temporarydirectory.h>
diff --git a/src/plugins/ios/simulatorcontrol.cpp b/src/plugins/ios/simulatorcontrol.cpp
index f696810760..4def4c0e47 100644
--- a/src/plugins/ios/simulatorcontrol.cpp
+++ b/src/plugins/ios/simulatorcontrol.cpp
@@ -7,7 +7,7 @@
#include <utils/algorithm.h>
#include <utils/async.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#ifdef Q_OS_MAC
diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp
index 96ba042b84..1b16e9dfb0 100644
--- a/src/plugins/languageclient/client.cpp
+++ b/src/plugins/languageclient/client.cpp
@@ -54,7 +54,7 @@
#include <utils/appinfo.h>
#include <utils/mimeutils.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QDebug>
#include <QGuiApplication>
diff --git a/src/plugins/languageclient/languageclientinterface.h b/src/plugins/languageclient/languageclientinterface.h
index a6dee5e4ee..1a9d56bfa9 100644
--- a/src/plugins/languageclient/languageclientinterface.h
+++ b/src/plugins/languageclient/languageclientinterface.h
@@ -8,7 +8,7 @@
#include <languageserverprotocol/jsonrpcmessages.h>
#include <utils/environment.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/temporaryfile.h>
#include <QBuffer>
diff --git a/src/plugins/languageclient/languageclientutils.cpp b/src/plugins/languageclient/languageclientutils.cpp
index 8b9962b6c9..0428fd6a84 100644
--- a/src/plugins/languageclient/languageclientutils.cpp
+++ b/src/plugins/languageclient/languageclientutils.cpp
@@ -22,7 +22,7 @@
#include <utils/environment.h>
#include <utils/infobar.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/textutils.h>
#include <utils/treeviewcombobox.h>
#include <utils/utilsicons.h>
diff --git a/src/plugins/mcusupport/mcuqmlprojectnode.h b/src/plugins/mcusupport/mcuqmlprojectnode.h
index bf40a696f6..2a06e5cde5 100644
--- a/src/plugins/mcusupport/mcuqmlprojectnode.h
+++ b/src/plugins/mcusupport/mcuqmlprojectnode.h
@@ -9,7 +9,7 @@
#include <utils/filepath.h>
#include <utils/osspecificaspects.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectnodes.h>
diff --git a/src/plugins/mcusupport/mcusupportversiondetection.cpp b/src/plugins/mcusupport/mcusupportversiondetection.cpp
index 56fe961569..79c12cc9b3 100644
--- a/src/plugins/mcusupport/mcusupportversiondetection.cpp
+++ b/src/plugins/mcusupport/mcusupportversiondetection.cpp
@@ -4,7 +4,7 @@
#include "mcusupportversiondetection.h"
#include "mcuhelpers.h"
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QDir>
#include <QRegularExpression>
diff --git a/src/plugins/mercurial/mercurialclient.cpp b/src/plugins/mercurial/mercurialclient.cpp
index fd568c9741..6140706d00 100644
--- a/src/plugins/mercurial/mercurialclient.cpp
+++ b/src/plugins/mercurial/mercurialclient.cpp
@@ -12,7 +12,7 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <vcsbase/vcsbasediffeditorcontroller.h>
diff --git a/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp b/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp
index 93ce63ad56..38d525502c 100644
--- a/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp
+++ b/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp
@@ -28,7 +28,7 @@
#include <utils/headerviewstretcher.h>
#include <utils/itemviews.h>
#include <utils/layoutbuilder.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/progressindicator.h>
#include <utils/utilsicons.h>
diff --git a/src/plugins/mesonprojectmanager/mesonwrapper.h b/src/plugins/mesonprojectmanager/mesonwrapper.h
index 9b2e2bdbeb..c38927fc9f 100644
--- a/src/plugins/mesonprojectmanager/mesonwrapper.h
+++ b/src/plugins/mesonprojectmanager/mesonwrapper.h
@@ -9,7 +9,7 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/id.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QFile>
#include <QFileInfo>
diff --git a/src/plugins/mesonprojectmanager/toolwrapper.cpp b/src/plugins/mesonprojectmanager/toolwrapper.cpp
index 632465c219..c0da501ec4 100644
--- a/src/plugins/mesonprojectmanager/toolwrapper.cpp
+++ b/src/plugins/mesonprojectmanager/toolwrapper.cpp
@@ -3,7 +3,7 @@
#include "toolwrapper.h"
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QUuid>
diff --git a/src/plugins/nim/project/nimblebuildsystem.cpp b/src/plugins/nim/project/nimblebuildsystem.cpp
index baed02b525..91f4eb2f20 100644
--- a/src/plugins/nim/project/nimblebuildsystem.cpp
+++ b/src/plugins/nim/project/nimblebuildsystem.cpp
@@ -10,7 +10,7 @@
#include <projectexplorer/taskhub.h>
#include <utils/algorithm.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
using namespace ProjectExplorer;
diff --git a/src/plugins/nim/project/nimcompilerbuildstep.cpp b/src/plugins/nim/project/nimcompilerbuildstep.cpp
index af730b4c85..aaf6ff4296 100644
--- a/src/plugins/nim/project/nimcompilerbuildstep.cpp
+++ b/src/plugins/nim/project/nimcompilerbuildstep.cpp
@@ -16,7 +16,7 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/toolchain.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QComboBox>
diff --git a/src/plugins/nim/project/nimtoolchain.cpp b/src/plugins/nim/project/nimtoolchain.cpp
index 61362314d7..8003659264 100644
--- a/src/plugins/nim/project/nimtoolchain.cpp
+++ b/src/plugins/nim/project/nimtoolchain.cpp
@@ -16,7 +16,7 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QFormLayout>
diff --git a/src/plugins/nim/suggest/server.h b/src/plugins/nim/suggest/server.h
index 73e28096e2..15fd2b04ad 100644
--- a/src/plugins/nim/suggest/server.h
+++ b/src/plugins/nim/suggest/server.h
@@ -3,7 +3,7 @@
#pragma once
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
namespace Nim::Suggest {
diff --git a/src/plugins/perforce/perforcechecker.h b/src/plugins/perforce/perforcechecker.h
index d0f608de70..5f7d2b1673 100644
--- a/src/plugins/perforce/perforcechecker.h
+++ b/src/plugins/perforce/perforcechecker.h
@@ -4,7 +4,7 @@
#pragma once
#include <utils/filepath.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
namespace Perforce::Internal {
diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp
index 7e8dca8265..a9c14748ea 100644
--- a/src/plugins/perforce/perforceplugin.cpp
+++ b/src/plugins/perforce/perforceplugin.cpp
@@ -29,7 +29,7 @@
#include <utils/action.h>
#include <utils/environment.h>
#include <utils/fileutils.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/temporarydirectory.h>
diff --git a/src/plugins/perfprofiler/perfprofilerruncontrol.cpp b/src/plugins/perfprofiler/perfprofilerruncontrol.cpp
index 56e32c9a2f..4c51b82b3f 100644
--- a/src/plugins/perfprofiler/perfprofilerruncontrol.cpp
+++ b/src/plugins/perfprofiler/perfprofilerruncontrol.cpp
@@ -16,7 +16,7 @@
#include <projectexplorer/runcontrol.h>
#include <projectexplorer/target.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QAction>
#include <QMessageBox>
diff --git a/src/plugins/perfprofiler/perfsettings.cpp b/src/plugins/perfprofiler/perfsettings.cpp
index ab4ad75cc3..2943547cbc 100644
--- a/src/plugins/perfprofiler/perfsettings.cpp
+++ b/src/plugins/perfprofiler/perfsettings.cpp
@@ -21,7 +21,7 @@
#include <utils/aspects.h>
#include <utils/layoutbuilder.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QComboBox>
diff --git a/src/plugins/perfprofiler/perftracepointdialog.cpp b/src/plugins/perfprofiler/perftracepointdialog.cpp
index d5cb2e16e3..d726aa7d86 100644
--- a/src/plugins/perfprofiler/perftracepointdialog.cpp
+++ b/src/plugins/perfprofiler/perftracepointdialog.cpp
@@ -12,7 +12,7 @@
#include <projectexplorer/target.h>
#include <utils/layoutbuilder.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QComboBox>
diff --git a/src/plugins/projectexplorer/abstractprocessstep.cpp b/src/plugins/projectexplorer/abstractprocessstep.cpp
index 547599bc9d..5cfb66e811 100644
--- a/src/plugins/projectexplorer/abstractprocessstep.cpp
+++ b/src/plugins/projectexplorer/abstractprocessstep.cpp
@@ -9,7 +9,7 @@
#include "projectexplorertr.h"
#include <utils/outputformatter.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QTextDecoder>
diff --git a/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp b/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp
index fa1f455af4..686c766838 100644
--- a/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp
+++ b/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp
@@ -8,7 +8,7 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/temporarydirectory.h>
#include <QFileInfo>
diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp
index 08880aff3b..2dae1a9b68 100644
--- a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp
+++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp
@@ -14,7 +14,7 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/portlist.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/terminalcommand.h>
#include <utils/terminalhooks.h>
diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
index c8b95084de..015b215fef 100644
--- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
@@ -14,7 +14,7 @@
#include <utils/environment.h>
#include <utils/fsengine/fsengine.h>
#include <utils/persistentsettings.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
#include <utils/terminalhooks.h>
diff --git a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp
index 5312026f73..920d7f69c5 100644
--- a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp
+++ b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp
@@ -9,7 +9,7 @@
#include <utils/port.h>
#include <utils/portlist.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
#include <utils/url.h>
diff --git a/src/plugins/projectexplorer/devicesupport/sshparameters.cpp b/src/plugins/projectexplorer/devicesupport/sshparameters.cpp
index 1467f958b9..2b2ebdabc0 100644
--- a/src/plugins/projectexplorer/devicesupport/sshparameters.cpp
+++ b/src/plugins/projectexplorer/devicesupport/sshparameters.cpp
@@ -8,7 +8,7 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QDebug>
diff --git a/src/plugins/projectexplorer/extracompiler.cpp b/src/plugins/projectexplorer/extracompiler.cpp
index 45d1c0e734..0d28129733 100644
--- a/src/plugins/projectexplorer/extracompiler.cpp
+++ b/src/plugins/projectexplorer/extracompiler.cpp
@@ -18,7 +18,7 @@
#include <utils/async.h>
#include <utils/expected.h>
#include <utils/guard.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QDateTime>
#include <QLoggingCategory>
diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp
index 7daa47c081..28792276a6 100644
--- a/src/plugins/projectexplorer/gcctoolchain.cpp
+++ b/src/plugins/projectexplorer/gcctoolchain.cpp
@@ -21,7 +21,7 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/scopedtimer.h>
diff --git a/src/plugins/projectexplorer/makestep.cpp b/src/plugins/projectexplorer/makestep.cpp
index fe3796bb30..730215d81c 100644
--- a/src/plugins/projectexplorer/makestep.cpp
+++ b/src/plugins/projectexplorer/makestep.cpp
@@ -19,7 +19,7 @@
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/utilsicons.h>
#include <utils/variablechooser.h>
diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp
index 67d3f32781..65c4432feb 100644
--- a/src/plugins/projectexplorer/msvctoolchain.cpp
+++ b/src/plugins/projectexplorer/msvctoolchain.cpp
@@ -20,7 +20,7 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/temporarydirectory.h>
#include <utils/winutils.h>
diff --git a/src/plugins/projectexplorer/processparameters.cpp b/src/plugins/projectexplorer/processparameters.cpp
index d9338ad03a..a60f105947 100644
--- a/src/plugins/projectexplorer/processparameters.cpp
+++ b/src/plugins/projectexplorer/processparameters.cpp
@@ -5,7 +5,7 @@
#include <utils/fileutils.h>
#include <utils/macroexpander.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/theme/theme.h>
#include <utils/utilstr.h>
diff --git a/src/plugins/projectexplorer/projectmodels.cpp b/src/plugins/projectexplorer/projectmodels.cpp
index dcbd3a89c8..d84df808ee 100644
--- a/src/plugins/projectexplorer/projectmodels.cpp
+++ b/src/plugins/projectexplorer/projectmodels.cpp
@@ -23,7 +23,7 @@
#include <utils/dropsupport.h>
#include <utils/fsengine/fileiconprovider.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
#include <utils/theme/theme.h>
diff --git a/src/plugins/projectexplorer/runconfigurationaspects.cpp b/src/plugins/projectexplorer/runconfigurationaspects.cpp
index 96e4bc1e1f..7b6c7f4f5d 100644
--- a/src/plugins/projectexplorer/runconfigurationaspects.cpp
+++ b/src/plugins/projectexplorer/runconfigurationaspects.cpp
@@ -19,7 +19,7 @@
#include <utils/fancylineedit.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp
index d2481f2ac6..37ed40f3c2 100644
--- a/src/plugins/projectexplorer/runcontrol.cpp
+++ b/src/plugins/projectexplorer/runcontrol.cpp
@@ -28,7 +28,7 @@
#include <utils/checkablemessagebox.h>
#include <utils/fileinprojectfinder.h>
#include <utils/outputformatter.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
#include <utils/terminalinterface.h>
diff --git a/src/plugins/projectexplorer/targetsetuppage.cpp b/src/plugins/projectexplorer/targetsetuppage.cpp
index 72e77fd664..5b752071fd 100644
--- a/src/plugins/projectexplorer/targetsetuppage.cpp
+++ b/src/plugins/projectexplorer/targetsetuppage.cpp
@@ -20,7 +20,7 @@
#include <utils/algorithm.h>
#include <utils/fancylineedit.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/wizard.h>
diff --git a/src/plugins/projectexplorer/taskfile.cpp b/src/plugins/projectexplorer/taskfile.cpp
index b6a95c0325..1c95254e2e 100644
--- a/src/plugins/projectexplorer/taskfile.cpp
+++ b/src/plugins/projectexplorer/taskfile.cpp
@@ -15,7 +15,7 @@
#include <utils/algorithm.h>
#include <utils/filepath.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QAction>
diff --git a/src/plugins/projectexplorer/toolchainconfigwidget.cpp b/src/plugins/projectexplorer/toolchainconfigwidget.cpp
index c7ce623b50..60b1479e7d 100644
--- a/src/plugins/projectexplorer/toolchainconfigwidget.cpp
+++ b/src/plugins/projectexplorer/toolchainconfigwidget.cpp
@@ -7,7 +7,7 @@
#include "projectexplorertr.h"
#include <utils/detailswidget.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QFormLayout>
diff --git a/src/plugins/projectexplorer/userfileaccessor.cpp b/src/plugins/projectexplorer/userfileaccessor.cpp
index 6a1059bcd1..a370d80ff6 100644
--- a/src/plugins/projectexplorer/userfileaccessor.cpp
+++ b/src/plugins/projectexplorer/userfileaccessor.cpp
@@ -19,7 +19,7 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/persistentsettings.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QGuiApplication>
diff --git a/src/plugins/python/pipsupport.cpp b/src/plugins/python/pipsupport.cpp
index db58b3869c..8723ca7510 100644
--- a/src/plugins/python/pipsupport.cpp
+++ b/src/plugins/python/pipsupport.cpp
@@ -16,7 +16,7 @@
#include <utils/algorithm.h>
#include <utils/async.h>
#include <utils/mimeutils.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
using namespace Utils;
diff --git a/src/plugins/python/pipsupport.h b/src/plugins/python/pipsupport.h
index 7d7b9e135f..da217282eb 100644
--- a/src/plugins/python/pipsupport.h
+++ b/src/plugins/python/pipsupport.h
@@ -4,7 +4,7 @@
#pragma once
#include <utils/filepath.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QFutureWatcher>
#include <QTimer>
diff --git a/src/plugins/python/pyside.cpp b/src/plugins/python/pyside.cpp
index 5df87ff1bb..79ffb8b951 100644
--- a/src/plugins/python/pyside.cpp
+++ b/src/plugins/python/pyside.cpp
@@ -19,7 +19,7 @@
#include <utils/algorithm.h>
#include <utils/async.h>
#include <utils/infobar.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QBoxLayout>
diff --git a/src/plugins/python/pysideuicextracompiler.cpp b/src/plugins/python/pysideuicextracompiler.cpp
index 45fb68066f..c195aeaa1f 100644
--- a/src/plugins/python/pysideuicextracompiler.cpp
+++ b/src/plugins/python/pysideuicextracompiler.cpp
@@ -3,7 +3,7 @@
#include "pysideuicextracompiler.h"
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
using namespace ProjectExplorer;
using namespace Utils;
diff --git a/src/plugins/python/pythonbuildconfiguration.cpp b/src/plugins/python/pythonbuildconfiguration.cpp
index 9e1192bcd8..d13e8cbc6a 100644
--- a/src/plugins/python/pythonbuildconfiguration.cpp
+++ b/src/plugins/python/pythonbuildconfiguration.cpp
@@ -37,7 +37,7 @@
#include <utils/detailswidget.h>
#include <utils/futuresynchronizer.h>
#include <utils/layoutbuilder.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
using namespace ProjectExplorer;
using namespace Utils;
diff --git a/src/plugins/python/pythonkitaspect.cpp b/src/plugins/python/pythonkitaspect.cpp
index 254a720d7c..0be12cead0 100644
--- a/src/plugins/python/pythonkitaspect.cpp
+++ b/src/plugins/python/pythonkitaspect.cpp
@@ -12,7 +12,7 @@
#include <utils/guard.h>
#include <utils/layoutbuilder.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QComboBox>
diff --git a/src/plugins/python/pythonlanguageclient.cpp b/src/plugins/python/pythonlanguageclient.cpp
index dbe6c434de..86cebde0e9 100644
--- a/src/plugins/python/pythonlanguageclient.cpp
+++ b/src/plugins/python/pythonlanguageclient.cpp
@@ -32,7 +32,7 @@
#include <utils/async.h>
#include <utils/infobar.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QFutureWatcher>
#include <QJsonDocument>
diff --git a/src/plugins/python/pythonsettings.cpp b/src/plugins/python/pythonsettings.cpp
index 98020fc7bc..8d30659f31 100644
--- a/src/plugins/python/pythonsettings.cpp
+++ b/src/plugins/python/pythonsettings.cpp
@@ -31,7 +31,7 @@
#include <utils/layoutbuilder.h>
#include <utils/listmodel.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/treemodel.h>
#include <utils/utilsicons.h>
diff --git a/src/plugins/python/pythonutils.cpp b/src/plugins/python/pythonutils.cpp
index 0e0d66f638..bf97831cad 100644
--- a/src/plugins/python/pythonutils.cpp
+++ b/src/plugins/python/pythonutils.cpp
@@ -19,7 +19,7 @@
#include <utils/algorithm.h>
#include <utils/mimeutils.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QReadLocker>
diff --git a/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp b/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp
index 34fa905745..6cf4cc7e31 100644
--- a/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp
+++ b/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp
@@ -26,7 +26,7 @@
#include <qtsupport/qtkitaspect.h>
#include <utils/mimeconstants.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QCoreApplication>
diff --git a/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp b/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp
index cf9f663bc7..3cfd68d5ce 100644
--- a/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp
+++ b/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp
@@ -18,7 +18,7 @@
#include <qmljstools/qmljstoolsconstants.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitaspect.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QCryptographicHash>
diff --git a/src/plugins/qbsprojectmanager/qbssession.cpp b/src/plugins/qbsprojectmanager/qbssession.cpp
index 4bbdbb6ebc..b2801acfa2 100644
--- a/src/plugins/qbsprojectmanager/qbssession.cpp
+++ b/src/plugins/qbsprojectmanager/qbssession.cpp
@@ -15,7 +15,7 @@
#include <projectexplorer/taskhub.h>
#include <utils/algorithm.h>
#include <utils/environment.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QDir>
diff --git a/src/plugins/qbsprojectmanager/qbssettings.cpp b/src/plugins/qbsprojectmanager/qbssettings.cpp
index 79299bbb32..ea89d65985 100644
--- a/src/plugins/qbsprojectmanager/qbssettings.cpp
+++ b/src/plugins/qbsprojectmanager/qbssettings.cpp
@@ -11,7 +11,7 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcsettings.h>
#include <QCheckBox>
diff --git a/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp b/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp
index 800d7350c1..c4652d2e35 100644
--- a/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp
+++ b/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp
@@ -13,7 +13,7 @@
#include <projectexplorer/target.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QCheckBox>
diff --git a/src/plugins/qmakeprojectmanager/makefileparse.cpp b/src/plugins/qmakeprojectmanager/makefileparse.cpp
index 9d2bda465f..bfa504ed60 100644
--- a/src/plugins/qmakeprojectmanager/makefileparse.cpp
+++ b/src/plugins/qmakeprojectmanager/makefileparse.cpp
@@ -5,7 +5,7 @@
#include <qtsupport/qtversionmanager.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QDebug>
#include <QDir>
diff --git a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
index 496ddb76c1..541e848c1f 100644
--- a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
@@ -36,7 +36,7 @@
#include <qtsupport/qtkitaspect.h>
#include <qtsupport/qtversionmanager.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/mimeconstants.h>
#include <utils/qtcassert.h>
diff --git a/src/plugins/qmakeprojectmanager/qmakemakestep.cpp b/src/plugins/qmakeprojectmanager/qmakemakestep.cpp
index 901cf5653c..ae8b370b77 100644
--- a/src/plugins/qmakeprojectmanager/qmakemakestep.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakemakestep.cpp
@@ -22,7 +22,7 @@
#include <projectexplorer/toolchain.h>
#include <projectexplorer/xcodebuildparser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/variablechooser.h>
#include <QDir>
diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
index 9faa6e6c53..9c6ca5caa4 100644
--- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
@@ -32,7 +32,7 @@
#include <utils/filesystemwatcher.h>
#include <utils/mimeconstants.h>
#include <utils/mimeutils.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
#include <utils/temporarydirectory.h>
diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp
index 6575366a44..c118ba1920 100644
--- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp
@@ -51,7 +51,7 @@
#include <utils/algorithm.h>
#include <utils/async.h>
#include <utils/mimeconstants.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QDebug>
#include <QDir>
diff --git a/src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp b/src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp
index 20cebc3ac7..200de04db7 100644
--- a/src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp
@@ -23,7 +23,7 @@
#include <qtsupport/qtversionmanager.h>
#include <utils/algorithm.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QDir>
diff --git a/src/plugins/qmakeprojectmanager/qmakestep.cpp b/src/plugins/qmakeprojectmanager/qmakestep.cpp
index 1d07e4e13a..e012499b54 100644
--- a/src/plugins/qmakeprojectmanager/qmakestep.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakestep.cpp
@@ -36,7 +36,7 @@
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/utilsicons.h>
#include <utils/variablechooser.h>
diff --git a/src/plugins/qmldesigner/.clang-format b/src/plugins/qmldesigner/.clang-format
index 87648a2057..366f82f76f 100644
--- a/src/plugins/qmldesigner/.clang-format
+++ b/src/plugins/qmldesigner/.clang-format
@@ -1,92 +1,32 @@
Language: Cpp
AccessModifierOffset: -4
-AlignAfterOpenBracket: Align
-AlignConsecutiveAssignments: false
-AlignConsecutiveDeclarations: false
AlignEscapedNewlines: DontAlign
-AlignOperands: true
-AlignTrailingComments: true
-AllowAllArgumentsOnNextLine: true
-AllowAllParametersOfDeclarationOnNextLine: true
-AllowShortBlocksOnASingleLine: false
-AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
-AllowShortLambdasOnASingleLine: All
-AllowShortIfStatementsOnASingleLine: false
-AllowShortLoopsOnASingleLine: false
-AlwaysBreakAfterReturnType: None
-AlwaysBreakBeforeMultilineStrings: false
-AlwaysBreakTemplateDeclarations: true
+AlwaysBreakTemplateDeclarations: true # use with clang 19
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterClass: true
- AfterControlStatement: false
- AfterEnum: false
AfterFunction: true
- AfterNamespace: false
- AfterObjCDeclaration: false
AfterStruct: true
- AfterUnion: false
- BeforeCatch: false
- BeforeElse: false
- IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
-BreakAfterAttributes: Never
BreakBeforeBinaryOperators: All
BreakBeforeBraces: Custom
-BreakBeforeConceptDeclarations: Always
-BreakBeforeInheritanceComma: false
-BreakBeforeTernaryOperators: true
-BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
-BreakAfterJavaFieldAnnotations: false
BreakInheritanceList: AfterComma
-BreakStringLiterals: true
+# BreakTemplateDeclarations: Yes # use with clang 19
ColumnLimit: 100
-CommentPragmas: '^ IWYU pragma:'
-CompactNamespaces: false
-ConstructorInitializerAllOnOneLineOrOnePerLine: false
-ConstructorInitializerIndentWidth: 4
-ContinuationIndentWidth: 4
-Cpp11BracedListStyle: true
-DerivePointerAlignment: false
-DisableFormat: false
-EmptyLineAfterAccessModifier: Never
-EmptyLineBeforeAccessModifier: LogicalBlock
-ExperimentalAutoDetectBinPacking: false
-FixNamespaceComments: true
-ForEachMacros:
- - forever # avoids { wrapped to next line
- - foreach
- - Q_FOREACH
- - BOOST_FOREACH
-#IncludeBlocks: Regroup
IncludeCategories:
- - Regex: '^<Q.*'
- Priority: 200
-IncludeIsMainRegex: '(Test)?$'
-IndentCaseBlocks: false
-IndentCaseLabels: false
+ - Regex: 'Q.*'
+ Priority: 8
+ CaseSensitive: true
IndentPPDirectives: AfterHash
-IndentRequiresClause: true
IndentWidth: 4
-IndentWrappedFunctionNames: false
-InsertBraces : false
-JavaScriptQuotes: Leave
-JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
# Do not add QT_BEGIN_NAMESPACE/QT_END_NAMESPACE as this will indent lines in between.
-LambdaBodyIndentation: Signature
-MacroBlockBegin: ""
-MacroBlockEnd: ""
-MaxEmptyLinesToKeep: 1
-NamespaceIndentation: None
ObjCBlockIndentWidth: 4
-ObjCSpaceAfterProperty: false
-ObjCSpaceBeforeProtocolList: true
PPIndentWidth: 2
PackConstructorInitializers: Never
PenaltyBreakAssignment: 500
@@ -96,40 +36,15 @@ PenaltyBreakFirstLessLess: 400
PenaltyBreakString: 600
PenaltyExcessCharacter: 7
PenaltyReturnTypeOnItsOwnLine: 300
-PointerAlignment: Right
QualifierAlignment: Custom
QualifierOrder: ['friend', 'inline', 'static', 'constexpr', 'const', 'type']
-ReflowComments: false
ReferenceAlignment: Right
-RequiresClausePosition: OwnLine
+ReflowComments: false
SeparateDefinitionBlocks: Always
-ShortNamespaceLines: 1
-SortIncludes: CaseInsensitive
SortUsingDeclarations: Lexicographic
SpaceAfterCStyleCast: true
-SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
-SpaceBeforeAssignmentOperators: true
-SpaceInEmptyParentheses: false
-SpaceBeforeCpp11BracedList: false
-SpaceBeforeCtorInitializerColon: true
-SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatementsExceptControlMacros
-SpaceBeforeRangeBasedForLoopColon: true
-SpaceBeforeSquareBrackets: false
-SpaceInEmptyBlock: false
-SpacesBeforeTrailingComments: 1
-SpacesInAngles: Never
-SpacesInConditionalStatement: false
SpacesInContainerLiterals: false
-SpacesInLineCommentPrefix:
- Minimum: 1
- Maximum: -1
-SpacesInCStyleCastParentheses: false
-SpacesInParentheses: false
-SpacesInSquareBrackets: false
-Standard: Latest
StatementAttributeLikeMacros: [emit]
-StatementMacros: [Q_UNUSED]
TabWidth: 4
-UseTab: Never
diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt
index 2e29b8dda8..01f03cc271 100644
--- a/src/plugins/qmldesigner/CMakeLists.txt
+++ b/src/plugins/qmldesigner/CMakeLists.txt
@@ -8,12 +8,12 @@ if (APPLE)
set(QmlDesignerPluginInstallPrefix "${IDE_PLUGIN_PATH}/QmlDesigner")
endif()
-add_compile_options("$<$<COMPILE_LANG_AND_ID:CXX,Clang>:-Wno-error=maybe-uninitialized>")
-add_compile_options("$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-Wno-error=maybe-uninitialized>")
-env_with_default("QDS_USE_PROJECTSTORAGE" ENV_QDS_USE_PROJECTSTORAGE OFF)
-option(USE_PROJECTSTORAGE "Use ProjectStorage" ${ENV_QDS_USE_PROJECTSTORAGE})
-add_feature_info("ProjectStorage" ${USE_PROJECTSTORAGE} "")
+set(BUILD_NOT_DESIGNSTUDIO NOT ${BUILD_NOT_DESIGNSTUDIO})
+option(QTC_USE_QML_DESIGNER_LITE "Use Qml Designer Lite" ${BUILD_NOT_DESIGNSTUDIO})
+add_feature_info("Qml Designer Lite" ${QTC_USE_QML_DESIGNER_LITE} "")
+
+option(USE_PROJECTSTORAGE "Use ProjectStorage" ${QTC_USE_QML_DESIGNER_LITE})
env_with_default("QTC_ENABLE_PROJECT_STORAGE_TRACING" ENV_QTC_ENABLE_PROJECT_STORAGE_TRACING OFF)
option(ENABLE_PROJECT_STORAGE_TRACING "Enable sqlite tracing" ${ENV_QTC_ENABLE_PROJECT_STORAGE_TRACING})
@@ -27,11 +27,9 @@ env_with_default("QTC_ENABLE_MODEL_TRACING" ENV_QTC_ENABLE_MODEL_TRACING OFF)
option(ENABLE_MODEL_TRACING "Enable model tracing" ${ENV_QTC_ENABLE_MODEL_TRACING})
add_feature_info("Model tracing" ${ENABLE_MODEL_TRACING} "")
-
-
add_qtc_library(QmlDesignerUtils STATIC
DEPENDS
- Qt::Gui Utils Qt::QmlPrivate Core
+ Qt::Gui Utils Qt::QmlPrivate
PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/utils
SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/utils
@@ -47,6 +45,10 @@ add_qtc_library(QmlDesignerUtils STATIC
qmldesignerutils_global.h
)
+
+target_compile_options(QmlDesignerUtils PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,Clang,GNU>:-Wno-error=maybe-uninitialized>)
+target_compile_options(QmlDesignerUtils PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,Clang>:-Wno-unneeded-internal-declaration>)
+
extend_qtc_library(QmlDesignerUtils
CONDITION ENABLE_COMPILE_WARNING_AS_ERROR
PROPERTIES COMPILE_WARNING_AS_ERROR ON
@@ -82,6 +84,7 @@ add_qtc_library(QmlDesignerCore STATIC
Sqlite
DEFINES
$<$<BOOL:${USE_PROJECTSTORAGE}>:QDS_USE_PROJECTSTORAGE>
+ $<$<BOOL:${QTC_USE_QML_DESIGNER_LITE}>:QTC_USE_QML_DESIGNER_LITE>
INCLUDES
${CMAKE_CURRENT_LIST_DIR}
PUBLIC_INCLUDES
@@ -94,7 +97,7 @@ add_qtc_library(QmlDesignerCore STATIC
)
extend_qtc_library(QmlDesignerCore
- CONDITION TARGET Nanotrace
+ CONDITION ENABLE_PROJECT_STORAGE_TRACING OR ENABLE_IMAGE_CACHE_TRACING OR ENABLE_MODEL_TRACING
PUBLIC_DEPENDS Nanotrace
PUBLIC_DEFINES
ENABLE_QMLDESIGNER_TRACING
@@ -488,6 +491,7 @@ add_qtc_plugin(QmlDesigner
IDE_LIBRARY_BASENAME=\"${IDE_LIBRARY_BASE_PATH}\"
SHARE_QML_PATH="${CMAKE_CURRENT_SOURCE_DIR}/../../../share/qtcreator/qmldesigner"
$<$<BOOL:${USE_PROJECTSTORAGE}>:QDS_USE_PROJECTSTORAGE>
+ $<$<BOOL:${QTC_USE_QML_DESIGNER_LITE}>:QTC_USE_QML_DESIGNER_LITE>
INCLUDES
${CMAKE_CURRENT_LIST_DIR}/components
${CMAKE_CURRENT_LIST_DIR}/components/assetslibrary
@@ -514,7 +518,6 @@ add_qtc_plugin(QmlDesigner
documentmanager.cpp documentmanager.h
documentwarningwidget.cpp documentwarningwidget.h
dynamiclicensecheck.h
- generateresource.cpp generateresource.h
openuiqmlfiledialog.cpp openuiqmlfiledialog.h
puppetenvironmentbuilder.cpp puppetenvironmentbuilder.h
qmldesigner_global.h
@@ -623,12 +626,15 @@ extend_qtc_plugin(QmlDesigner
propertycomponentgeneratorinterface.h
qmldesignericonprovider.cpp qmldesignericonprovider.h
qmleditormenu.cpp qmleditormenu.h
+ resourcegenerator.cpp resourcegenerator.h
selectioncontext.cpp selectioncontext.h
theme.cpp theme.h
zoomaction.cpp zoomaction.h
anchoraction.cpp anchoraction.h
svgpasteaction.cpp svgpasteaction.h
viewmanager.cpp viewmanager.h
+ utils3d.cpp utils3d.h
+ dialogutils.cpp dialogutils.h
)
extend_qtc_plugin(QmlDesigner
@@ -650,6 +656,7 @@ extend_qtc_plugin(QmlDesigner
backgroundcolorselection.cpp backgroundcolorselection.h
bakelights.cpp bakelights.h
snapconfiguration.cpp snapconfiguration.h
+ cameraspeedconfiguration.cpp cameraspeedconfiguration.h
bakelightsdatamodel.cpp bakelightsdatamodel.h
bakelightsconnectionmanager.cpp bakelightsconnectionmanager.h
edit3d.qrc
@@ -666,7 +673,6 @@ extend_qtc_plugin(QmlDesigner
backgroundaction.cpp backgroundaction.h
bindingindicator.cpp bindingindicator.h
bindingindicatorgraphicsitem.cpp bindingindicatorgraphicsitem.h
- contentnoteditableindicator.cpp contentnoteditableindicator.h
controlelement.cpp controlelement.h
dragtool.cpp dragtool.h
formeditor.qrc
@@ -706,6 +712,7 @@ extend_qtc_plugin(QmlDesigner
snappinglinecreator.cpp snappinglinecreator.h
toolbox.cpp toolbox.h
transitiontool.cpp transitiontool.h
+ view3dtool.cpp view3dtool.h
)
extend_qtc_plugin(QmlDesigner
@@ -835,13 +842,13 @@ extend_qtc_plugin(QmlDesigner
extend_qtc_plugin(QmlDesigner
SOURCES_PREFIX components/collectioneditor
SOURCES
+ collectiondatatypemodel.cpp collectiondatatypemodel.h
collectiondetails.cpp collectiondetails.h
collectiondetailsmodel.cpp collectiondetailsmodel.h
collectiondetailssortfiltermodel.cpp collectiondetailssortfiltermodel.h
collectioneditorconstants.h
collectioneditorutils.cpp collectioneditorutils.h
collectionlistmodel.cpp collectionlistmodel.h
- collectionsourcemodel.cpp collectionsourcemodel.h
collectionview.cpp collectionview.h
collectionwidget.cpp collectionwidget.h
datastoremodelnode.cpp datastoremodelnode.h
@@ -914,7 +921,6 @@ extend_qtc_plugin(QmlDesigner
include/propertybinding.h
include/qml3dnode.h
include/qmlvisualnode.h
-
)
extend_qtc_plugin(QmlDesigner
@@ -1245,3 +1251,16 @@ extend_qtc_plugin(qtquickplugin
qtquickplugin.cpp qtquickplugin.h
qtquickplugin.qrc
)
+
+if (BUILD_DESIGNSTUDIO)
+ configure_file(
+ "${QtCreator_SOURCE_DIR}/dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini"
+ "${PROJECT_BINARY_DIR}/${IDE_DATA_PATH}/QtProject/${IDE_CASED_ID}.ini"
+ COPYONLY
+ )
+ install(FILES
+ ${QtCreator_SOURCE_DIR}/dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini
+ DESTINATION ${IDE_DATA_PATH}/QtProject
+ RENAME ${IDE_CASED_ID}.ini
+ )
+endif ()
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp
index 5f33036b40..3b98eb6baf 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp
@@ -9,11 +9,12 @@
#include "assetslibraryview.h"
#include "designeractionmanager.h"
#include "import.h"
-#include "nodemetainfo.h"
#include "modelnodeoperations.h"
+#include "nodemetainfo.h"
#include "qmldesignerconstants.h"
#include "qmldesignerplugin.h"
#include "theme.h"
+#include <utils3d.h>
#include <studioquickwidget.h>
@@ -229,25 +230,28 @@ int AssetsLibraryWidget::qtVersion() const
void AssetsLibraryWidget::addTextures(const QStringList &filePaths)
{
m_assetsView->executeInTransaction(__FUNCTION__, [&] {
- m_createTextures.execute(filePaths, AddTextureMode::Texture,
- m_assetsView->model()->active3DSceneId());
+ m_createTextures.execute(filePaths,
+ AddTextureMode::Texture,
+ Utils3D::active3DSceneId(m_assetsView->model()));
});
}
void AssetsLibraryWidget::addLightProbe(const QString &filePath)
{
m_assetsView->executeInTransaction(__FUNCTION__, [&] {
- m_createTextures.execute({filePath}, AddTextureMode::LightProbe,
- m_assetsView->model()->active3DSceneId());
+ m_createTextures.execute({filePath},
+ AddTextureMode::LightProbe,
+ Utils3D::active3DSceneId(m_assetsView->model()));
});
}
void AssetsLibraryWidget::updateContextMenuActionsEnableState()
{
- setHasMaterialLibrary(m_assetsView->materialLibraryNode().isValid()
+ setHasMaterialLibrary(Utils3D::materialLibraryNode(m_assetsView).isValid()
&& m_assetsView->model()->hasImport("QtQuick3D"));
- ModelNode activeSceneEnv = m_createTextures.resolveSceneEnv(m_assetsView->model()->active3DSceneId());
+ ModelNode activeSceneEnv = m_createTextures.resolveSceneEnv(
+ Utils3D::active3DSceneId(m_assetsView->model()));
setHasSceneEnv(activeSceneEnv.isValid());
}
@@ -269,8 +273,11 @@ void AssetsLibraryWidget::setHasSceneEnv(bool b)
emit hasSceneEnvChanged();
}
-void AssetsLibraryWidget::handleDeleteEffects(const QStringList &effectNames)
+void AssetsLibraryWidget::handleDeleteEffects([[maybe_unused]] const QStringList &effectNames)
{
+#ifdef QDS_USE_PROJECTSTORAGE
+// That code has to rewritten with modules. Seem try to find all effects nodes.
+#else
DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument();
if (!document)
return;
@@ -334,6 +341,9 @@ void AssetsLibraryWidget::handleDeleteEffects(const QStringList &effectNames)
// contain only unworkable states.
if (clearStacks)
document->clearUndoRedoStacks();
+
+ m_assetsView->emitCustomNotification("effectcomposer_effects_deleted", {}, {effectNames});
+#endif
}
void AssetsLibraryWidget::invalidateThumbnail(const QString &id)
diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp
index 256bfac1e9..bd63742d5e 100644
--- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp
+++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp
@@ -49,12 +49,10 @@ void ActionEditor::prepareDialog()
s_lastActionEditor = this;
- m_dialog = new ActionEditorDialog(Core::ICore::dialogParent());
+ m_dialog = Utils::makeUniqueObjectPtr<ActionEditorDialog>(Core::ICore::dialogParent());
- QObject::connect(m_dialog, &AbstractEditorDialog::accepted,
- this, &ActionEditor::accepted);
- QObject::connect(m_dialog, &AbstractEditorDialog::rejected,
- this, &ActionEditor::rejected);
+ QObject::connect(m_dialog.get(), &AbstractEditorDialog::accepted, this, &ActionEditor::accepted);
+ QObject::connect(m_dialog.get(), &AbstractEditorDialog::rejected, this, &ActionEditor::rejected);
m_dialog->setAttribute(Qt::WA_DeleteOnClose);
}
@@ -231,6 +229,7 @@ void ActionEditor::prepareConnections()
QList<ActionEditorDialog::SingletonOption> singletons;
QStringList states;
+ [[maybe_unused]] auto model = m_modelNode.model();
const QList<QmlDesigner::ModelNode> allNodes = m_modelNode.view()->allModelNodes();
for (const auto &modelNode : allNodes) {
// Skip nodes without specified id
@@ -242,11 +241,19 @@ void ActionEditor::prepareConnections()
for (const auto &property : modelNode.metaInfo().properties()) {
if (isSkippedType(property.propertyType()))
continue;
-
+#ifdef QDS_USE_PROJECTSTORAGE
+ auto exportedTypeName = model->exportedTypeNameForMetaInfo(property.propertyType())
+ .name.toQByteArray();
+ connection.properties.append(
+ ActionEditorDialog::PropertyOption(QString::fromUtf8(property.name()),
+ exportedTypeName,
+ property.isWritable()));
+#else
connection.properties.append(
ActionEditorDialog::PropertyOption(QString::fromUtf8(property.name()),
skipCpp(property.propertyType().typeName()),
property.isWritable()));
+#endif
}
for (const VariantProperty &variantProperty : modelNode.variantProperties()) {
@@ -307,11 +314,21 @@ void ActionEditor::prepareConnections()
for (const auto &property : metaInfo.properties()) {
if (isSkippedType(property.propertyType()))
continue;
-
+#ifdef QDS_USE_PROJECTSTORAGE
+ auto exportedTypeName = model
+ ->exportedTypeNameForMetaInfo(
+ property.propertyType())
+ .name.toQByteArray();
+ singelton.properties.append(
+ ActionEditorDialog::PropertyOption(QString::fromUtf8(property.name()),
+ exportedTypeName,
+ property.isWritable()));
+#else
singelton.properties.append(ActionEditorDialog::PropertyOption(
QString::fromUtf8(property.name()),
skipCpp(property.propertyType().typeName()),
property.isWritable()));
+#endif
}
if (!singelton.properties.isEmpty()) {
@@ -327,13 +344,13 @@ void ActionEditor::prepareConnections()
for (const QmlModelState &state : QmlItemNode(m_modelNode.view()->rootModelNode()).states().allStates())
states.append(state.name());
- if (!connections.isEmpty() && !m_dialog.isNull())
+ if (!connections.isEmpty() && m_dialog)
m_dialog->setAllConnections(connections, singletons, states);
}
void ActionEditor::updateWindowName(const QString &targetName)
{
- if (!m_dialog.isNull()) {
+ if (m_dialog) {
if (targetName.isEmpty())
m_dialog->setWindowTitle(m_dialog->defaultTitle());
else
diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h
index 9a14ad2117..5cdee3e428 100644
--- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h
+++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h
@@ -9,6 +9,8 @@
#include <modelnode.h>
#include <signalhandlerproperty.h>
+#include <utils/uniqueobjectptr.h>
+
#include <QtQml>
#include <QObject>
#include <QPointer>
@@ -63,7 +65,7 @@ private:
void prepareDialog();
private:
- QPointer<ActionEditorDialog> m_dialog;
+ Utils::UniqueObjectPtr<ActionEditorDialog> m_dialog;
QModelIndex m_index;
QmlDesigner::ModelNode m_modelNode;
};
diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp
index b605a77191..6a77b6235e 100644
--- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp
+++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp
@@ -37,12 +37,10 @@ void BindingEditor::prepareDialog()
{
QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_BINDINGEDITOR_OPENED);
- m_dialog = new BindingEditorDialog(Core::ICore::dialogParent());
+ m_dialog = Utils::makeUniqueObjectPtr<BindingEditorDialog>(Core::ICore::dialogParent());
- QObject::connect(m_dialog, &AbstractEditorDialog::accepted,
- this, &BindingEditor::accepted);
- QObject::connect(m_dialog, &AbstractEditorDialog::rejected,
- this, &BindingEditor::rejected);
+ QObject::connect(m_dialog.get(), &AbstractEditorDialog::accepted, this, &BindingEditor::accepted);
+ QObject::connect(m_dialog.get(), &AbstractEditorDialog::rejected, this, &BindingEditor::rejected);
m_dialog->setAttribute(Qt::WA_DeleteOnClose);
}
@@ -178,22 +176,22 @@ bool isType(const TypeName &first, const TypeName &second, const Tuple &...types
bool compareTypes(const NodeMetaInfo &sourceType, const NodeMetaInfo &targetType)
{
- if constexpr (useProjectStorage()) {
- return targetType.isVariant() || sourceType.isVariant() || targetType == sourceType
- || (targetType.isNumber() && sourceType.isNumber())
- || (targetType.isColor() && sourceType.isColor())
- || (targetType.isString() && sourceType.isString());
- } else {
- const TypeName source = sourceType.simplifiedTypeName();
- const TypeName target = targetType.simplifiedTypeName();
-
- static constexpr auto variantTypes = std::make_tuple("alias", "unknown", "variant", "var");
-
- return isType(variantTypes, target) || isType(variantTypes, source) || target == source
- || targetType == sourceType || isType(target, source, "double", "real", "int")
- || isType(target, source, "QColor", "color")
- || isType(target, source, "QString", "string");
- }
+#ifdef QDS_USE_PROJECTSTORAGE
+ return targetType.isVariant() || sourceType.isVariant() || targetType == sourceType
+ || (targetType.isNumber() && sourceType.isNumber())
+ || (targetType.isColor() && sourceType.isColor())
+ || (targetType.isString() && sourceType.isString());
+#else
+ const TypeName source = sourceType.simplifiedTypeName();
+ const TypeName target = targetType.simplifiedTypeName();
+
+ static constexpr auto variantTypes = std::make_tuple("alias", "unknown", "variant", "var");
+
+ return isType(variantTypes, target) || isType(variantTypes, source) || target == source
+ || targetType == sourceType || isType(target, source, "double", "real", "int")
+ || isType(target, source, "QColor", "color")
+ || isType(target, source, "QString", "string");
+#endif
}
} // namespace
@@ -273,13 +271,13 @@ void BindingEditor::prepareBindings()
}
}
- if (!bindings.isEmpty() && !m_dialog.isNull())
+ if (!bindings.isEmpty() && m_dialog)
m_dialog->setAllBindings(bindings, m_backendValueType);
}
void BindingEditor::updateWindowName()
{
- if (!m_dialog.isNull() && m_backendValueType) {
+ if (m_dialog && m_backendValueType) {
QString targetString;
if constexpr (useProjectStorage()) {
auto exportedTypeNames = m_backendValueType.exportedTypeNamesForSourceId(
@@ -289,8 +287,13 @@ void BindingEditor::updateWindowName()
+ exportedTypeNames.front().name.toQString() + "]";
}
} else {
+#ifdef QDS_USE_PROJECTSTORAGE
+ targetString = " [" + (m_targetName.isEmpty() ? QString() : (m_targetName + ": "))
+ + QString::fromUtf8(m_backendValueType.displayName()) + "]";
+#else
targetString = " [" + (m_targetName.isEmpty() ? QString() : (m_targetName + ": "))
+ QString::fromUtf8(m_backendValueType.simplifiedTypeName()) + "]";
+#endif
}
m_dialog->setWindowTitle(m_dialog->defaultTitle() + targetString);
diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h
index 52aa0884f6..8ed49b2eb6 100644
--- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h
+++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h
@@ -8,6 +8,8 @@
#include <qmldesignercorelib_global.h>
#include <modelnode.h>
+#include <utils/uniqueobjectptr.h>
+
#include <QtQml>
#include <QObject>
#include <QPointer>
@@ -72,7 +74,7 @@ private:
void prepareDialog();
private:
- QPointer<BindingEditorDialog> m_dialog;
+ Utils::UniqueObjectPtr<BindingEditorDialog> m_dialog;
QVariant m_backendValue;
QVariant m_modelNodeBackend;
QVariant m_stateModelNode;
diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp
index aa3b883b9a..ff2361aa36 100644
--- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp
+++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp
@@ -132,14 +132,14 @@ void BindingDocument::applyFontSettings()
{
TextDocument::applyFontSettings();
m_semanticHighlighter->updateFontSettings(fontSettings());
- if (!isSemanticInfoOutdated())
+ if (!isSemanticInfoOutdated() && semanticInfo().isValid())
m_semanticHighlighter->rerun(semanticInfo());
}
void BindingDocument::triggerPendingUpdates()
{
TextDocument::triggerPendingUpdates(); // calls applyFontSettings if necessary
- if (!isSemanticInfoOutdated())
+ if (!isSemanticInfoOutdated() && semanticInfo().isValid())
m_semanticHighlighter->rerun(semanticInfo());
}
diff --git a/src/plugins/qmldesigner/components/bindingeditor/signallist.cpp b/src/plugins/qmldesigner/components/bindingeditor/signallist.cpp
index a4538b56c3..12a605e58a 100644
--- a/src/plugins/qmldesigner/components/bindingeditor/signallist.cpp
+++ b/src/plugins/qmldesigner/components/bindingeditor/signallist.cpp
@@ -54,12 +54,8 @@ bool SignalListFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &s
|| sourceModel()->data(signalIndex).toString().contains(filterRegularExpression()));
}
-
-
-
SignalList::SignalList(QObject *)
- : m_dialog(QPointer<SignalListDialog>())
- , m_model(new SignalListModel(this))
+ : m_model(Utils::makeUniqueObjectPtr<SignalListModel>(this))
, m_modelNode()
{
}
@@ -71,9 +67,9 @@ SignalList::~SignalList()
void SignalList::prepareDialog()
{
- m_dialog = new SignalListDialog(Core::ICore::dialogParent());
+ m_dialog = Utils::makeUniqueObjectPtr<SignalListDialog>(Core::ICore::dialogParent());
m_dialog->setAttribute(Qt::WA_DeleteOnClose);
- m_dialog->initialize(m_model);
+ m_dialog->initialize(m_model.get());
m_dialog->setWindowTitle(::QmlDesigner::SignalList::tr("Signal List for %1")
.arg(m_modelNode.validId()));
@@ -98,13 +94,14 @@ void SignalList::hideWidget()
SignalList* SignalList::showWidget(const ModelNode &modelNode)
{
- auto signalList = new SignalList();
+ auto signalList = new SignalList;
signalList->setModelNode(modelNode);
signalList->prepareSignals();
signalList->showWidget();
- connect(signalList->m_dialog, &QDialog::destroyed,
- [signalList]() { signalList->deleteLater(); } );
+ connect(signalList->m_dialog.get(), &QDialog::destroyed, [signalList]() {
+ signalList->deleteLater();
+ });
return signalList;
}
@@ -221,14 +218,18 @@ void SignalList::addConnection(const QModelIndex &modelIndex)
const ModelNode rootModelNode = view->rootModelNode();
if (rootModelNode.isValid() && rootModelNode.metaInfo().isValid()) {
- NodeMetaInfo nodeMetaInfo = view->model()->qtQuickConnectionsMetaInfo();
+#ifndef QDS_USE_PROJECTSTORAGE
+ NodeMetaInfo nodeMetaInfo = view->model()->qtQmlConnectionsMetaInfo();
if (nodeMetaInfo.isValid()) {
- view->executeInTransaction("ConnectionModel::addConnection",
- [this, view, nodeMetaInfo, targetModelIndex, modelIndex,
- buttonModelIndex, signalName, &rootModelNode] {
+#endif
+ view->executeInTransaction("ConnectionModel::addConnection", [&] {
+#ifdef QDS_USE_PROJECTSTORAGE
+ ModelNode newNode = view->createModelNode("Connections");
+#else
ModelNode newNode = view->createModelNode("QtQuick.Connections",
nodeMetaInfo.majorVersion(),
nodeMetaInfo.minorVersion());
+#endif
const QString source = m_modelNode.validId() + ".trigger()";
if (QmlItemNode::isValidQmlItemNode(m_modelNode))
@@ -243,7 +244,9 @@ void SignalList::addConnection(const QModelIndex &modelIndex)
m_model->setConnected(modelIndex.row(), true);
m_model->setData(buttonModelIndex, newNode.internalId(), SignalListModel::ConnectionsInternalIdRole);
});
+#ifndef QDS_USE_PROJECTSTORAGE
}
+#endif
}
}
@@ -265,20 +268,24 @@ void SignalList::removeConnection(const QModelIndex &modelIndex)
ModelNode node = targetSignal.parentModelNode();
if (node.isValid()) {
- view->executeInTransaction("ConnectionModel::removeConnection",
- [this, modelIndex, buttonModelIndex, targetSignal, &node] {
- QList<SignalHandlerProperty> allSignals = node.signalProperties();
- if (allSignals.size() > 1) {
- const auto targetSignalWithPrefix = SignalHandlerProperty::prefixAdded(targetSignal.name());
- for (const SignalHandlerProperty &signal : allSignals)
- if (signal.name() == targetSignalWithPrefix)
- node.removeProperty(targetSignalWithPrefix);
- } else {
- node.destroy();
- }
- m_model->setConnected(modelIndex.row(), false);
- m_model->setData(buttonModelIndex, QVariant(), SignalListModel::ConnectionsInternalIdRole);
- });
+ view->executeInTransaction(
+ "ConnectionModel::removeConnection",
+ [this, modelIndex, buttonModelIndex, targetSignal, &node] {
+ const QList<SignalHandlerProperty> allSignals = node.signalProperties();
+ if (allSignals.size() > 1) {
+ const auto targetSignalWithPrefix = SignalHandlerProperty::prefixAdded(
+ targetSignal.name());
+ for (const SignalHandlerProperty &signal : allSignals)
+ if (signal.name() == targetSignalWithPrefix)
+ node.removeProperty(targetSignalWithPrefix);
+ } else {
+ node.destroy();
+ }
+ m_model->setConnected(modelIndex.row(), false);
+ m_model->setData(buttonModelIndex,
+ QVariant(),
+ SignalListModel::ConnectionsInternalIdRole);
+ });
}
}
diff --git a/src/plugins/qmldesigner/components/bindingeditor/signallist.h b/src/plugins/qmldesigner/components/bindingeditor/signallist.h
index 228cb0e85a..5842d445bb 100644
--- a/src/plugins/qmldesigner/components/bindingeditor/signallist.h
+++ b/src/plugins/qmldesigner/components/bindingeditor/signallist.h
@@ -8,6 +8,8 @@
#include <modelnode.h>
#include <qmlconnections.h>
+#include <utils/uniqueobjectptr.h>
+
#include <QtQml>
#include <QObject>
#include <QPointer>
@@ -74,8 +76,8 @@ private:
void removeConnection(const QModelIndex &modelIndex);
private:
- QPointer<SignalListDialog> m_dialog;
- SignalListModel *m_model;
+ Utils::UniqueObjectPtr<SignalListDialog> m_dialog;
+ Utils::UniqueObjectPtr<SignalListModel> m_model;
ModelNode m_modelNode;
};
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp
new file mode 100644
index 0000000000..cb306c58cb
--- /dev/null
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp
@@ -0,0 +1,87 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "collectiondatatypemodel.h"
+
+#include <QHash>
+#include <QtQml/QmlTypeAndRevisionsRegistration>
+
+namespace QmlDesigner {
+
+struct CollectionDataTypeModel::Details
+{
+ CollectionDetails::DataType type;
+ QString name;
+ QString description;
+};
+
+const QList<CollectionDataTypeModel::Details> CollectionDataTypeModel::m_orderedDetails{
+ {DataType::String, "String", "Text"},
+ {DataType::Integer, "Integer", "Whole number that can be positive, negative, or zero"},
+ {DataType::Real, "Real", "Number with a decimal"},
+ {DataType::Image, "Image", "Image resource"},
+ {DataType::Color, "Color", "HEX value"},
+ {DataType::Url, "Url", "Resource locator"},
+ {DataType::Boolean, "Boolean", "True/false"},
+ {DataType::Unknown, "Unknown", "Unknown data type"},
+};
+
+CollectionDataTypeModel::CollectionDataTypeModel(QObject *parent)
+ : QAbstractListModel(parent)
+{
+}
+
+int CollectionDataTypeModel::rowCount([[maybe_unused]] const QModelIndex &parent) const
+{
+ return m_orderedDetails.size();
+}
+
+QVariant CollectionDataTypeModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return {};
+
+ if (role == Qt::DisplayRole)
+ return m_orderedDetails.at(index.row()).name;
+ if (role == Qt::ToolTipRole)
+ return m_orderedDetails.at(index.row()).description;
+
+ return {};
+}
+
+QString CollectionDataTypeModel::dataTypeToString(DataType dataType)
+{
+ static const QHash<DataType, QString> dataTypeHash = []() -> QHash<DataType, QString> {
+ QHash<DataType, QString> result;
+ for (const Details &details : m_orderedDetails)
+ result.insert(details.type, details.name);
+ return result;
+ }();
+
+ if (dataTypeHash.contains(dataType))
+ return dataTypeHash.value(dataType);
+
+ return "Unknown";
+}
+
+CollectionDetails::DataType CollectionDataTypeModel::dataTypeFromString(const QString &dataType)
+{
+ static const QHash<QString, DataType> stringTypeHash = []() -> QHash<QString, DataType> {
+ QHash<QString, DataType> result;
+ for (const Details &details : m_orderedDetails)
+ result.insert(details.name, details.type);
+ return result;
+ }();
+
+ if (stringTypeHash.contains(dataType))
+ return stringTypeHash.value(dataType);
+
+ return DataType::Unknown;
+}
+
+void CollectionDataTypeModel::registerDeclarativeType()
+{
+ qmlRegisterType<CollectionDataTypeModel>("CollectionDetails", 1, 0, "CollectionDataTypeModel");
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h
new file mode 100644
index 0000000000..1f91aecbff
--- /dev/null
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "collectiondetails.h"
+
+#include <QAbstractListModel>
+#include <QList>
+
+namespace QmlDesigner {
+
+class CollectionDataTypeModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ using DataType = CollectionDetails::DataType;
+ CollectionDataTypeModel(QObject *parent = nullptr);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+ static Q_INVOKABLE QString dataTypeToString(DataType dataType);
+ static Q_INVOKABLE DataType dataTypeFromString(const QString &dataType);
+
+ static void registerDeclarativeType();
+
+private:
+ struct Details;
+ static const QList<Details> m_orderedDetails;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp
index f35e9f2ed2..9005882813 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp
@@ -3,16 +3,30 @@
#include "collectiondetails.h"
+#include "collectiondatatypemodel.h"
+#include "collectioneditorutils.h"
+
#include <utils/span.h>
+#include <qmljs/parser/qmljsast_p.h>
+#include <qmljs/parser/qmljsastvisitor_p.h>
+#include <qmljs/qmljsdocument.h>
#include <qqml.h>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
+#include <QRegularExpression>
+#include <QTextStream>
#include <QUrl>
#include <QVariant>
namespace QmlDesigner {
+#define COLLERR_OK QT_TRANSLATE_NOOP("CollectioParseError", "no error occurred")
+#define COLLERR_MAINOBJECT QT_TRANSLATE_NOOP("CollectioParseError", "Document object not found")
+#define COLLERR_COLLECTIONNAME QT_TRANSLATE_NOOP("CollectioParseError", "Model name not found")
+#define COLLERR_COLLECTIONOBJ QT_TRANSLATE_NOOP("CollectioParseError", "Model is not an object")
+#define COLLERR_COLUMNARRAY QT_TRANSLATE_NOOP("CollectioParseError", "Column is not an array")
+#define COLLERR_UNKNOWN QT_TRANSLATE_NOOP("CollectioParseError", "Unknown error")
struct CollectionProperty
{
@@ -28,32 +42,17 @@ const QMap<DataTypeWarning::Warning, QString> DataTypeWarning::dataTypeWarnings
class CollectionDetails::Private
{
- using SourceFormat = CollectionEditorConstants::SourceFormat;
-
public:
QList<CollectionProperty> properties;
- QList<QJsonObject> elements;
- SourceFormat sourceFormat = SourceFormat::Unknown;
+ QList<QJsonArray> dataRecords;
CollectionReference reference;
bool isChanged = false;
bool isValidColumnId(int column) const { return column > -1 && column < properties.size(); }
- bool isValidRowId(int row) const { return row > -1 && row < elements.size(); }
+ bool isValidRowId(int row) const { return row > -1 && row < dataRecords.size(); }
};
-inline static bool isValidColorName(const QString &colorName)
-{
-#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
- return QColor::isValidColorName(colorName);
-#else
- constexpr QStringView colorPattern(
- u"(?<color>^(?:#(?:(?:[0-9a-fA-F]{2}){3,4}|(?:[0-9a-fA-F]){3,4}))$)");
- static const QRegularExpression colorRegex(colorPattern.toString());
- return colorRegex.match(colorName).hasMatch();
-#endif // >= Qt 6.4
-}
-
/**
* @brief getCustomUrl
* MimeType = <MainType/SubType>
@@ -68,26 +67,26 @@ inline static bool isValidColorName(const QString &colorName)
* will be stored in this parameter, otherwise it will be empty.
* @return true if the result is either url or image
*/
-inline static bool getCustomUrl(const QString &value,
- CollectionDetails::DataType &dataType,
- QUrl *urlResult = nullptr,
- QString *subType = nullptr)
-{
- constexpr QStringView urlPattern(
- u"^(?<MimeType>"
- u"(?<MainType>image)\\/"
- u"(?<SubType>apng|avif|gif|jpeg|png|(?:svg\\+xml)|webp|xyz)\\:)?" // end of MimeType
- u"(?<Address>"
- u"(?<Url>https?:\\/\\/"
- u"(?:www\\.|(?!www))[A-z0-9][A-z0-9-]+[A-z0-9]\\.[^\\s]{2,}|www\\.[A-z0-9][A-z0-9-]+"
- u"[A-z0-9]\\.[^\\s]{2,}|https?:\\/\\/"
- u"(?:www\\.|(?!www))[A-z0-9]+\\.[^\\s]{2,}|www\\.[A-z0-9]+\\.[^\\s]{2,})|" // end of Url
- u"(?<LocalFile>("
- u"?:(?:[A-z]:)|(?:(?:\\\\|\\/){1,2}\\w+)\\$?)(?:(?:\\\\|\\/)(?:\\w[\\w ]*.*))+)" // end of LocalFile
- u"){1}$"); // end of Address
- static const QRegularExpression urlRegex(urlPattern.toString());
-
- const QRegularExpressionMatch match = urlRegex.match(value);
+static bool getCustomUrl(const QString &value,
+ CollectionDetails::DataType &dataType,
+ QUrl *urlResult = nullptr,
+ QString *subType = nullptr)
+{
+ static const QRegularExpression urlRegex{
+ "^(?<MimeType>"
+ "(?<MainType>image)\\/"
+ "(?<SubType>apng|avif|gif|jpeg|png|(?:svg\\+xml)|webp|xyz)\\:)?" // end of MimeType
+ "(?<Address>"
+ "(?<Url>https?:\\/\\/"
+ "(?:www\\.|(?!www))[A-z0-9][A-z0-9-]+[A-z0-9]\\.[^\\s]{2,}|www\\.[A-z0-9][A-z0-9-]+"
+ "[A-z0-9]\\.[^\\s]{2,}|https?:\\/\\/"
+ "(?:www\\.|(?!www))[A-z0-9]+\\.[^\\s]{2,}|www\\.[A-z0-9]+\\.[^\\s]{2,})|" // end of Url
+ "(?<LocalFile>("
+ "?:(?:[A-z]:)|(?:(?:\\\\|\\/){1,2}\\w+)\\$?)(?:(?:\\\\|\\/)(?:\\w[\\w ]*.*))+)" // end of LocalFile
+ "){1}$" // end of Address
+ };
+
+ const QRegularExpressionMatch match = urlRegex.match(value.trimmed());
if (match.hasMatch()) {
if (match.hasCaptured("Address")) {
if (match.hasCaptured("MimeType") && match.captured("MainType") == "image")
@@ -115,7 +114,61 @@ inline static bool getCustomUrl(const QString &value,
return false;
}
-static CollectionProperty::DataType collectionDataTypeFromJsonValue(const QJsonValue &value)
+/**
+ * @brief dataTypeFromString
+ * @param value The string value to be evaluated
+ * @return Unknown if the string is empty, But returns Bool, Color, Integer,
+ * Real, Url, Image if these types are detected within the non-empty string,
+ * Otherwise it returns String.
+ * If the value is integer, but it's out of the int range, it will be
+ * considered as a Real.
+ */
+static CollectionDetails::DataType dataTypeFromString(const QString &value)
+{
+ using DataType = CollectionDetails::DataType;
+ static const QRegularExpression validator{
+ "(?<boolean>^(?:true|false)$)|"
+ "(?<color>^(?:#(?:(?:[0-9a-fA-F]{2}){3,4}|(?:[0-9a-fA-F]){3,4}))$)|"
+ "(?<integer>^\\d+$)|"
+ "(?<real>^(?:-?(?:0|[1-9]\\d*)?(?:\\.\\d*)?(?<=\\d|\\.)"
+ "(?:e-?(?:0|[1-9]\\d*))?|0x[0-9a-f]+)$)"};
+ static const int boolIndex = validator.namedCaptureGroups().indexOf("boolean");
+ static const int colorIndex = validator.namedCaptureGroups().indexOf("color");
+ static const int integerIndex = validator.namedCaptureGroups().indexOf("integer");
+ static const int realIndex = validator.namedCaptureGroups().indexOf("real");
+
+ [[maybe_unused]] static const bool allIndexesFound =
+ [](const std::initializer_list<int> &captureIndexes) {
+ QTC_ASSERT(Utils::allOf(captureIndexes, [](int val) { return val > -1; }), return false);
+ return true;
+ }({boolIndex, colorIndex, integerIndex, realIndex});
+
+ if (value.isEmpty())
+ return DataType::Unknown;
+
+ const QString trimmedValue = value.trimmed();
+ QRegularExpressionMatch match = validator.match(trimmedValue);
+
+ if (match.hasCaptured(boolIndex))
+ return DataType::Boolean;
+ if (match.hasCaptured(colorIndex))
+ return DataType::Color;
+ if (match.hasCaptured(integerIndex)) {
+ bool isInt = false;
+ trimmedValue.toInt(&isInt);
+ return isInt ? DataType::Integer : DataType::Real;
+ }
+ if (match.hasCaptured(realIndex))
+ return DataType::Real;
+
+ DataType urlType;
+ if (getCustomUrl(trimmedValue, urlType))
+ return urlType;
+
+ return DataType::String;
+}
+
+static CollectionProperty::DataType dataTypeFromJsonValue(const QJsonValue &value)
{
using DataType = CollectionDetails::DataType;
using JsonType = QJsonValue::Type;
@@ -131,22 +184,49 @@ static CollectionProperty::DataType collectionDataTypeFromJsonValue(const QJsonV
return DataType::Integer;
return DataType::Real;
}
- case JsonType::String: {
- const QString stringValue = value.toString();
- if (isValidColorName(stringValue))
- return DataType::Color;
-
- DataType urlType;
- if (getCustomUrl(stringValue, urlType))
- return urlType;
-
- return DataType::String;
- } break;
+ case JsonType::String:
+ return dataTypeFromString(value.toString());
default:
return DataType::Unknown;
}
}
+static QList<CollectionProperty> getColumnsFromImportedJsonArray(const QJsonArray &importedArray)
+{
+ using DataType = CollectionDetails::DataType;
+
+ QHash<QString, int> resultSet;
+ QList<CollectionProperty> result;
+
+ for (const QJsonValue &value : importedArray) {
+ if (value.isObject()) {
+ const QJsonObject object = value.toObject();
+ QJsonObject::ConstIterator element = object.constBegin();
+ const QJsonObject::ConstIterator stopItem = object.constEnd();
+
+ while (element != stopItem) {
+ const QString propertyName = element.key();
+ if (resultSet.contains(propertyName)) {
+ CollectionProperty &property = result[resultSet.value(propertyName)];
+ if (property.type == DataType::Unknown) {
+ property.type = dataTypeFromJsonValue(element.value());
+ } else if (property.type == DataType::Integer) {
+ const DataType currentCellDataType = dataTypeFromJsonValue(element.value());
+ if (currentCellDataType == DataType::Real)
+ property.type = currentCellDataType;
+ }
+ } else {
+ result.append({propertyName, dataTypeFromJsonValue(element.value())});
+ resultSet.insert(propertyName, resultSet.size());
+ }
+ ++element;
+ }
+ }
+ }
+
+ return result;
+}
+
static QVariant valueToVariant(const QJsonValue &value, CollectionDetails::DataType type)
{
using DataType = CollectionDetails::DataType;
@@ -214,6 +294,93 @@ static QJsonValue variantToJsonValue(
}
}
+inline static bool isEmptyJsonValue(const QJsonValue &value)
+{
+ return value.isNull() || value.isUndefined() || (value.isString() && value.toString().isEmpty());
+}
+
+QStringList csvReadLine(const QString &line)
+{
+ static const QRegularExpression lineRegex{
+ "(?:,\\\"|^\\\")(?<value>\\\"\\\"|[\\w\\W]*?)(?=\\\",|\\\"$)"
+ "|(?:,(?!\\\")|^(?!\\\"))(?<quote>[^,]*?)(?=$|,)|(\\\\r\\\\n|\\\\n)"};
+ static const int valueIndex = lineRegex.namedCaptureGroups().indexOf("value");
+ static const int quoteIndex = lineRegex.namedCaptureGroups().indexOf("quote");
+ Q_ASSERT(valueIndex > 0 && quoteIndex > 0);
+
+ QStringList result;
+ QRegularExpressionMatchIterator iterator = lineRegex.globalMatch(line, 0);
+ while (iterator.hasNext()) {
+ const QRegularExpressionMatch match = iterator.next();
+
+ if (match.hasCaptured(valueIndex))
+ result.append(match.captured(valueIndex));
+ else if (match.hasCaptured(quoteIndex))
+ result.append(match.captured(quoteIndex));
+ }
+ return result;
+}
+
+class PropertyOrderFinder : public QmlJS::AST::Visitor
+{
+public:
+ static QStringList parse(const QString &jsonContent)
+ {
+ PropertyOrderFinder finder;
+ QmlJS::Document::MutablePtr jsonDoc = QmlJS::Document::create(Utils::FilePath::fromString(
+ "<expression>"),
+ QmlJS::Dialect::Json);
+
+ jsonDoc->setSource(jsonContent);
+ jsonDoc->parseJavaScript();
+
+ if (!jsonDoc->isParsedCorrectly())
+ return {};
+
+ jsonDoc->ast()->accept(&finder);
+ return finder.m_orderedList;
+ }
+
+protected:
+ bool visit(QmlJS::AST::PatternProperty *patternProperty) override
+ {
+ const QString propertyName = patternProperty->name->asString();
+ if (!m_propertySet.contains(propertyName)) {
+ m_propertySet.insert(propertyName);
+ m_orderedList.append(propertyName);
+ }
+ return true;
+ }
+
+ void throwRecursionDepthError() override
+ {
+ qWarning() << Q_FUNC_INFO << __LINE__ << "Recursion depth error";
+ };
+
+private:
+ QSet<QString> m_propertySet;
+ QStringList m_orderedList;
+};
+
+QString CollectionParseError::errorString() const
+{
+ switch (errorNo) {
+ case NoError:
+ return COLLERR_OK;
+ case MainObjectMissing:
+ return COLLERR_MAINOBJECT;
+ case CollectionNameNotFound:
+ return COLLERR_COLLECTIONNAME;
+ case CollectionIsNotObject:
+ return COLLERR_COLLECTIONOBJ;
+ case ColumnsBlockIsNotArray:
+ return COLLERR_COLUMNARRAY;
+ case UnknownError:
+ default:
+ return COLLERR_UNKNOWN;
+ }
+}
+
CollectionDetails::CollectionDetails()
: d(new Private())
{}
@@ -224,134 +391,102 @@ CollectionDetails::CollectionDetails(const CollectionReference &reference)
d->reference = reference;
}
-CollectionDetails::CollectionDetails(const CollectionDetails &other) = default;
-
-CollectionDetails::~CollectionDetails() = default;
-
-void CollectionDetails::resetDetails(const QStringList &propertyNames,
- const QList<QJsonObject> &elements,
- CollectionEditorConstants::SourceFormat format)
+void CollectionDetails::resetData(const QJsonDocument &localDocument,
+ const QString &collectionToImport,
+ CollectionParseError *error)
{
- if (!isValid())
- return;
-
- d->properties = Utils::transform(propertyNames, [](const QString &name) -> CollectionProperty {
- return {name, DataType::Unknown};
- });
+ CollectionDetails importedCollection = fromLocalJson(localDocument, collectionToImport, error);
+ d->properties.swap(importedCollection.d->properties);
+ d->dataRecords.swap(importedCollection.d->dataRecords);
+}
- d->elements = elements;
- d->sourceFormat = format;
+CollectionDetails::CollectionDetails(const CollectionDetails &other) = default;
- resetPropertyTypes();
- markSaved();
-}
+CollectionDetails::~CollectionDetails() = default;
void CollectionDetails::insertColumn(const QString &propertyName,
int colIdx,
const QVariant &defaultValue,
DataType type)
{
- if (!isValid())
- return;
-
if (containsPropertyName(propertyName))
return;
CollectionProperty property = {propertyName, type};
- if (d->isValidColumnId(colIdx))
+ if (d->isValidColumnId(colIdx)) {
d->properties.insert(colIdx, property);
- else
+ } else {
+ colIdx = d->properties.size();
d->properties.append(property);
+ }
- QJsonValue defaultJsonValue = QJsonValue::fromVariant(defaultValue);
- for (QJsonObject &element : d->elements)
- element.insert(propertyName, defaultJsonValue);
+ const QJsonValue defaultJsonValue = QJsonValue::fromVariant(defaultValue);
+ for (QJsonArray &record : d->dataRecords)
+ record.insert(colIdx, defaultJsonValue);
markChanged();
}
bool CollectionDetails::removeColumns(int colIdx, int count)
{
- if (count < 1 || !isValid() || !d->isValidColumnId(colIdx))
+ if (!d->isValidColumnId(colIdx))
return false;
int maxCount = d->properties.count() - colIdx;
count = std::min(maxCount, count);
- const QList<CollectionProperty> removedProperties = d->properties.mid(colIdx, count);
- d->properties.remove(colIdx, count);
+ if (count < 1)
+ return false;
- for (const CollectionProperty &property : removedProperties) {
- for (QJsonObject &element : d->elements)
- element.remove(property.name);
- }
+ d->properties.remove(colIdx, count);
- markChanged();
+ for (QJsonArray &record : d->dataRecords) {
+ QJsonArray newElement;
- return true;
-}
+ auto elementItr = record.constBegin();
+ auto elementEnd = elementItr + colIdx;
+ while (elementItr != elementEnd)
+ newElement.append(*(elementItr++));
-void CollectionDetails::insertElementAt(std::optional<QJsonObject> object, int row)
-{
- if (!isValid())
- return;
+ elementItr += count;
+ elementEnd = record.constEnd();
- auto insertJson = [this, row](const QJsonObject &jsonObject) {
- if (d->isValidRowId(row))
- d->elements.insert(row, jsonObject);
- else
- d->elements.append(jsonObject);
- };
+ while (elementItr != elementEnd)
+ newElement.append(*(elementItr++));
- if (object.has_value()) {
- insertJson(object.value());
- } else {
- QJsonObject defaultObject;
- for (const CollectionProperty &property : std::as_const(d->properties))
- defaultObject.insert(property.name, {});
- insertJson(defaultObject);
+ record = newElement;
}
markChanged();
+
+ return true;
}
-void CollectionDetails::insertEmptyElements(int row, int count)
+void CollectionDetails::insertEmptyRows(int row, int count)
{
- if (!isValid())
- return;
-
if (count < 1)
return;
row = qBound(0, row, rows());
- d->elements.insert(row, count, {});
+
+ insertRecords({}, row, count);
markChanged();
}
-bool CollectionDetails::removeElements(int row, int count)
+bool CollectionDetails::removeRows(int row, int count)
{
- if (count < 1 || !isValid() || !d->isValidRowId(row))
+ if (!d->isValidRowId(row))
return false;
- int maxCount = d->elements.count() - row;
+ int maxCount = d->dataRecords.count() - row;
count = std::min(maxCount, count);
- QSet<QString> removedProperties;
- Utils::span elementsSpan{std::as_const(d->elements)};
- for (const QJsonObject &element : elementsSpan.subspan(row, count)) {
- const QStringList elementPropertyNames = element.keys();
- for (const QString &removedProperty : elementPropertyNames)
- removedProperties.insert(removedProperty);
- }
-
- d->elements.remove(row, count);
-
- for (const QString &removedProperty : removedProperties)
- resetPropertyType(removedProperty);
+ if (count < 1)
+ return false;
+ d->dataRecords.remove(row, count);
markChanged();
-
return true;
}
@@ -360,13 +495,12 @@ bool CollectionDetails::setPropertyValue(int row, int column, const QVariant &va
if (!d->isValidRowId(row) || !d->isValidColumnId(column))
return false;
- QJsonObject &element = d->elements[row];
QVariant currentValue = data(row, column);
-
if (value == currentValue)
return false;
- element.insert(d->properties.at(column).name, variantToJsonValue(value, typeAt(column)));
+ QJsonArray &record = d->dataRecords[row];
+ record.replace(column, variantToJsonValue(value, typeAt(column)));
markChanged();
return true;
}
@@ -377,17 +511,10 @@ bool CollectionDetails::setPropertyName(int column, const QString &value)
return false;
const CollectionProperty &oldProperty = d->properties.at(column);
- const QString oldColumnName = oldProperty.name;
- if (oldColumnName == value)
+ if (oldProperty.name == value)
return false;
d->properties.replace(column, {value, oldProperty.type});
- for (QJsonObject &element : d->elements) {
- if (element.contains(oldColumnName)) {
- element.insert(value, element.value(oldColumnName));
- element.remove(oldColumnName);
- }
- }
markChanged();
return true;
@@ -395,7 +522,7 @@ bool CollectionDetails::setPropertyName(int column, const QString &value)
bool CollectionDetails::setPropertyType(int column, DataType type)
{
- if (!isValid() || !d->isValidColumnId(column))
+ if (!d->isValidColumnId(column))
return false;
bool changed = false;
@@ -406,12 +533,12 @@ bool CollectionDetails::setPropertyType(int column, DataType type)
const DataType formerType = property.type;
property.type = type;
- for (QJsonObject &element : d->elements) {
- if (element.contains(property.name)) {
- const QJsonValue value = element.value(property.name);
+ for (QJsonArray &rowData : d->dataRecords) {
+ if (column < rowData.size()) {
+ const QJsonValue value = rowData.at(column);
const QVariant properTypedValue = valueToVariant(value, formerType);
const QJsonValue properTypedJsonValue = variantToJsonValue(properTypedValue, type);
- element.insert(property.name, properTypedJsonValue);
+ rowData.replace(column, properTypedJsonValue);
changed = true;
}
}
@@ -427,29 +554,15 @@ CollectionReference CollectionDetails::reference() const
return d->reference;
}
-CollectionEditorConstants::SourceFormat CollectionDetails::sourceFormat() const
-{
- return d->sourceFormat;
-}
-
QVariant CollectionDetails::data(int row, int column) const
{
- if (!isValid())
- return {};
-
if (!d->isValidRowId(row))
return {};
if (!d->isValidColumnId(column))
return {};
- const QString &propertyName = d->properties.at(column).name;
- const QJsonObject &elementNode = d->elements.at(row);
-
- if (!elementNode.contains(propertyName))
- return {};
-
- const QJsonValue cellValue = elementNode.value(propertyName);
+ const QJsonValue cellValue = d->dataRecords.at(row).at(column);
if (typeAt(column) == DataType::Image) {
const QUrl imageUrl = valueToVariant(cellValue, DataType::Image).toUrl();
@@ -482,48 +595,37 @@ CollectionDetails::DataType CollectionDetails::typeAt(int row, int column) const
if (!d->isValidRowId(row) || !d->isValidColumnId(column))
return {};
- const QString &propertyName = d->properties.at(column).name;
- const QJsonObject &element = d->elements.at(row);
-
- if (element.contains(propertyName))
- return collectionDataTypeFromJsonValue(element.value(propertyName));
-
- return {};
+ const QJsonValue cellData = d->dataRecords.at(row).at(column);
+ return dataTypeFromJsonValue(cellData);
}
DataTypeWarning::Warning CollectionDetails::cellWarningCheck(int row, int column) const
{
- const QString &propertyName = d->properties.at(column).name;
- const QJsonObject &element = d->elements.at(row);
+ const QJsonValue cellValue = d->dataRecords.at(row).at(column);
const DataType columnType = typeAt(column);
const DataType cellType = typeAt(row, column);
- if (columnType == DataType::Unknown || element.isEmpty()
- || data(row, column) == QVariant::fromValue(nullptr)) {
+
+ if (columnType == DataType::Unknown || isEmptyJsonValue(cellValue))
return DataTypeWarning::Warning::None;
- }
- if (element.contains(propertyName)) {
- if (columnType == DataType::Real && cellType == DataType::Integer)
- return DataTypeWarning::Warning::None;
- else if (columnType != cellType)
- return DataTypeWarning::Warning::CellDataTypeMismatch;
- }
+ if (columnType == DataType::Real && cellType == DataType::Integer)
+ return DataTypeWarning::Warning::None;
+
+ if (columnType != cellType)
+ return DataTypeWarning::Warning::CellDataTypeMismatch;
return DataTypeWarning::Warning::None;
}
-bool CollectionDetails::containsPropertyName(const QString &propertyName)
+bool CollectionDetails::containsPropertyName(const QString &propertyName) const
{
- if (!isValid())
- return false;
-
return Utils::anyOf(d->properties, [&propertyName](const CollectionProperty &property) {
return property.name == propertyName;
});
}
-bool CollectionDetails::isValid() const
+bool CollectionDetails::hasValidReference() const
{
return d->reference.node.isValid() && d->reference.name.size();
}
@@ -540,7 +642,7 @@ int CollectionDetails::columns() const
int CollectionDetails::rows() const
{
- return d->elements.size();
+ return d->dataRecords.size();
}
bool CollectionDetails::markSaved()
@@ -565,6 +667,94 @@ void CollectionDetails::resetReference(const CollectionReference &reference)
}
}
+QString CollectionDetails::toJson() const
+{
+ QJsonArray exportedArray;
+ const int propertyCount = d->properties.count();
+
+ for (const QJsonArray &record : std::as_const(d->dataRecords)) {
+ const int valueCount = std::min(int(record.count()), propertyCount);
+
+ QJsonObject exportedElement;
+ for (int i = 0; i < valueCount; ++i) {
+ const QJsonValue &value = record.at(i);
+ if (isEmptyJsonValue(value))
+ exportedElement.insert(d->properties.at(i).name, QJsonValue::Null);
+ else
+ exportedElement.insert(d->properties.at(i).name, value);
+ }
+
+ exportedArray.append(exportedElement);
+ }
+
+ return QString::fromUtf8(QJsonDocument(exportedArray).toJson());
+}
+
+QString CollectionDetails::toCsv() const
+{
+ QString content;
+
+ auto gotoNextLine = [&content]() {
+ if (content.size() && content.back() == ',')
+ content.back() = '\n';
+ else
+ content += "\n";
+ };
+
+ const int propertyCount = d->properties.count();
+ if (propertyCount <= 0)
+ return "";
+
+ for (const CollectionProperty &property : std::as_const(d->properties))
+ content += property.name + ',';
+
+ gotoNextLine();
+
+ for (const QJsonArray &record : std::as_const(d->dataRecords)) {
+ const int valueCount = std::min(int(record.count()), propertyCount);
+ int i = 0;
+ for (; i < valueCount; ++i) {
+ const QJsonValue &value = record.at(i);
+
+ if (value.isDouble())
+ content += QString::number(value.toDouble()) + ',';
+ else if (value.isBool())
+ content += value.toBool() ? "true," : "false,";
+ else
+ content += value.toString() + ',';
+ }
+
+ for (; i < propertyCount; ++i)
+ content += ',';
+
+ gotoNextLine();
+ }
+
+ return content;
+}
+
+QJsonObject CollectionDetails::toLocalJson() const
+{
+ QJsonObject collectionObject;
+ QJsonArray columnsArray;
+ QJsonArray dataArray;
+
+ for (const CollectionProperty &property : std::as_const(d->properties)) {
+ QJsonObject columnObject;
+ columnObject.insert("name", property.name);
+ columnObject.insert("type", CollectionDataTypeModel::dataTypeToString(property.type));
+ columnsArray.append(columnObject);
+ }
+
+ for (const QJsonArray &record : std::as_const(d->dataRecords))
+ dataArray.append(record);
+
+ collectionObject.insert("columns", columnsArray);
+ collectionObject.insert("data", dataArray);
+
+ return collectionObject;
+}
+
void CollectionDetails::registerDeclarativeType()
{
typedef CollectionDetails::DataType DataType;
@@ -575,93 +765,242 @@ void CollectionDetails::registerDeclarativeType()
qmlRegisterUncreatableType<DataTypeWarning>("CollectionDetails", 1, 0, "Warning", "Enum type");
}
-CollectionDetails &CollectionDetails::operator=(const CollectionDetails &other)
+CollectionDetails CollectionDetails::fromImportedCsv(const QByteArray &document,
+ const bool &firstRowIsHeader)
{
- CollectionDetails value(other);
- swap(value);
- return *this;
-}
+ QStringList headers;
+ QJsonArray importedArray;
-void CollectionDetails::markChanged()
-{
- d->isChanged = true;
+ QTextStream stream(document);
+ stream.setEncoding(QStringConverter::Latin1);
+
+ if (firstRowIsHeader && !stream.atEnd()) {
+ headers = Utils::transform(csvReadLine(stream.readLine()),
+ [](const QString &value) -> QString { return value.trimmed(); });
+ }
+
+ while (!stream.atEnd()) {
+ const QStringList recordDataList = csvReadLine(stream.readLine());
+ int column = -1;
+ QJsonObject recordData;
+ for (const QString &cellData : recordDataList) {
+ if (++column == headers.size()) {
+ QString proposalName;
+ int proposalId = column;
+ do
+ proposalName = QString("Column %1").arg(++proposalId);
+ while (headers.contains(proposalName));
+ headers.append(proposalName);
+ }
+ recordData.insert(headers.at(column), cellData);
+ }
+ importedArray.append(recordData);
+ }
+
+ return fromImportedJson(importedArray, headers);
}
-void CollectionDetails::resetPropertyType(const QString &propertyName)
+CollectionDetails CollectionDetails::fromImportedJson(const QByteArray &json, QJsonParseError *error)
{
- for (CollectionProperty &property : d->properties) {
- if (property.name == propertyName)
- resetPropertyType(property);
+ QJsonArray importedCollection;
+ auto refineJsonArray = [](const QJsonArray &array) -> QJsonArray {
+ QJsonArray resultArray;
+ for (const QJsonValue &collectionData : array) {
+ if (collectionData.isObject()) {
+ QJsonObject rowObject = collectionData.toObject();
+ const QStringList rowKeys = rowObject.keys();
+ for (const QString &key : rowKeys) {
+ const QJsonValue cellValue = rowObject.value(key);
+ if (cellValue.isArray())
+ rowObject.remove(key);
+ }
+ resultArray.push_back(rowObject);
+ }
+ }
+ return resultArray;
+ };
+
+ QJsonParseError parseError;
+ QJsonDocument document = QJsonDocument::fromJson(json, &parseError);
+ if (error)
+ *error = parseError;
+
+ if (parseError.error != QJsonParseError::NoError)
+ return CollectionDetails{};
+
+ if (document.isArray()) {
+ importedCollection = refineJsonArray(document.array());
+ } else if (document.isObject()) {
+ QJsonObject documentObject = document.object();
+ const QStringList mainKeys = documentObject.keys();
+
+ bool arrayFound = false;
+ for (const QString &key : mainKeys) {
+ const QJsonValue value = documentObject.value(key);
+ if (value.isArray()) {
+ arrayFound = true;
+ importedCollection = refineJsonArray(value.toArray());
+ break;
+ }
+ }
+
+ if (!arrayFound) {
+ QJsonObject singleObject;
+ for (const QString &key : mainKeys) {
+ const QJsonValue value = documentObject.value(key);
+
+ if (!value.isObject())
+ singleObject.insert(key, value);
+ }
+ importedCollection.push_back(singleObject);
+ }
}
+
+ return fromImportedJson(importedCollection, PropertyOrderFinder::parse(QLatin1String(json)));
}
-void CollectionDetails::resetPropertyType(CollectionProperty &property)
+CollectionDetails CollectionDetails::fromLocalJson(const QJsonDocument &document,
+ const QString &collectionName,
+ CollectionParseError *error)
{
- const QString &propertyName = property.name;
- DataType columnType = DataType::Unknown;
- for (const QJsonObject &element : std::as_const(d->elements)) {
- if (element.contains(propertyName)) {
- const DataType cellType = collectionDataTypeFromJsonValue(element.value(propertyName));
- if (cellType != DataType::Unknown) {
- if (columnType == DataType::Integer && cellType != DataType::Real)
- continue;
+ auto setError = [&error](CollectionParseError::ParseError parseError) {
+ if (error)
+ error->errorNo = parseError;
+ };
- columnType = cellType;
- if (columnType == DataType::Integer)
- continue;
+ setError(CollectionParseError::NoError);
- break;
- }
+ if (document.isObject()) {
+ QJsonObject collectionMap = document.object();
+ if (collectionMap.contains(collectionName)) {
+ QJsonValue collectionValue = collectionMap.value(collectionName);
+ if (collectionValue.isObject())
+ return fromLocalCollection(collectionValue.toObject());
+ else
+ setError(CollectionParseError::CollectionIsNotObject);
+ } else {
+ setError(CollectionParseError::CollectionNameNotFound);
}
+ } else {
+ setError(CollectionParseError::MainObjectMissing);
}
- property.type = columnType;
+
+ return CollectionDetails{};
+}
+
+CollectionDetails &CollectionDetails::operator=(const CollectionDetails &other)
+{
+ CollectionDetails value(other);
+ swap(value);
+ return *this;
}
-void CollectionDetails::resetPropertyTypes()
+void CollectionDetails::markChanged()
{
- for (CollectionProperty &property : d->properties)
- resetPropertyType(property);
+ d->isChanged = true;
}
-QJsonArray CollectionDetails::getCollectionAsJsonArray() const
+void CollectionDetails::insertRecords(const QJsonArray &record, int idx, int count)
{
- QJsonArray collectionArray;
+ if (count < 1)
+ return;
- for (const QJsonObject &element : std::as_const(d->elements))
- collectionArray.push_back(element);
+ QJsonArray localRecord;
+ const int columnsCount = columns();
+ for (int i = 0; i < columnsCount; i++) {
+ const QJsonValue originalCellData = record.at(i);
+ if (originalCellData.isArray())
+ localRecord.append({});
+ else
+ localRecord.append(originalCellData);
+ }
+
+ if (idx > d->dataRecords.size() || idx < 0)
+ idx = d->dataRecords.size();
- return collectionArray;
+ d->dataRecords.insert(idx, count, localRecord);
}
-QString CollectionDetails::getCollectionAsJsonString() const
+CollectionDetails CollectionDetails::fromImportedJson(const QJsonArray &importedArray,
+ const QStringList &propertyPriority)
{
- return QString::fromUtf8(QJsonDocument(getCollectionAsJsonArray()).toJson());
+ QList<CollectionProperty> columnData = getColumnsFromImportedJsonArray(importedArray);
+ if (!propertyPriority.isEmpty()) {
+ QMap<QString, int> priorityMap;
+ for (const QString &propertyName : propertyPriority) {
+ if (!priorityMap.contains(propertyName))
+ priorityMap.insert(propertyName, priorityMap.size());
+ }
+ const int lowestPriority = priorityMap.size();
+
+ Utils::sort(columnData, [&](const CollectionProperty &a, const CollectionProperty &b) {
+ return priorityMap.value(a.name, lowestPriority)
+ < priorityMap.value(b.name, lowestPriority);
+ });
+ }
+
+ QList<QJsonArray> localJsonArray;
+ for (const QJsonValue &importedRowValue : importedArray) {
+ QJsonObject importedRowObject = importedRowValue.toObject();
+ QJsonArray localRow;
+ for (const CollectionProperty &property : columnData)
+ localRow.append(importedRowObject.value(property.name));
+ localJsonArray.append(localRow);
+ }
+ CollectionDetails result;
+ result.d->properties = columnData;
+ result.d->dataRecords = localJsonArray;
+ result.markSaved();
+
+ return result;
}
-QString CollectionDetails::getCollectionAsCsvString() const
+CollectionDetails CollectionDetails::fromLocalCollection(const QJsonObject &localCollection,
+ CollectionParseError *error)
{
- QString content;
- if (d->properties.count() <= 0)
- return "";
-
- for (const CollectionProperty &property : std::as_const(d->properties))
- content += property.name + ',';
+ auto setError = [&error](CollectionParseError::ParseError parseError) {
+ if (error)
+ error->errorNo = parseError;
+ };
- content.back() = '\n';
+ CollectionDetails result;
+ setError(CollectionParseError::NoError);
+
+ if (localCollection.contains("columns")) {
+ const QJsonValue columnsValue = localCollection.value("columns");
+ if (columnsValue.isArray()) {
+ const QJsonArray columns = columnsValue.toArray();
+ for (const QJsonValue &columnValue : columns) {
+ if (columnValue.isObject()) {
+ const QJsonObject column = columnValue.toObject();
+ const QString columnName = column.value("name").toString();
+ if (!columnName.isEmpty()) {
+ result.insertColumn(columnName,
+ -1,
+ {},
+ CollectionDataTypeModel::dataTypeFromString(
+ column.value("type").toString()));
+ }
+ }
+ }
- for (const QJsonObject &elementsRow : std::as_const(d->elements)) {
- for (const CollectionProperty &property : std::as_const(d->properties)) {
- const QJsonValue &value = elementsRow.value(property.name);
+ if (int columnsCount = result.columns()) {
+ const QJsonArray dataRecords = localCollection.value("data").toArray();
+ for (const QJsonValue &dataRecordValue : dataRecords) {
+ QJsonArray dataRecord = dataRecordValue.toArray();
+ while (dataRecord.count() > columnsCount)
+ dataRecord.removeLast();
- if (value.isDouble())
- content += QString::number(value.toDouble()) + ',';
- else
- content += value.toString() + ',';
+ result.insertRecords(dataRecord);
+ }
+ }
+ } else {
+ setError(CollectionParseError::ColumnsBlockIsNotArray);
+ return result;
}
- content.back() = '\n';
}
- return content;
+ return result;
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h
index a18c557c52..b84c214570 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h
@@ -3,13 +3,13 @@
#pragma once
-#include "collectioneditorconstants.h"
#include "modelnode.h"
#include <QSharedPointer>
QT_BEGIN_NAMESPACE
class QJsonObject;
+struct QJsonParseError;
class QVariant;
QT_END_NAMESPACE
@@ -36,8 +36,6 @@ struct CollectionReference
struct CollectionProperty;
struct DataTypeWarning {
- Q_GADGET
-
public:
enum Warning { None, CellDataTypeMismatch };
Q_ENUM(Warning)
@@ -47,14 +45,31 @@ public:
: warning(warning)
{}
- static QString getDataTypeWarningString(Warning warning) {
+ static QString getDataTypeWarningString(Warning warning)
+ {
return dataTypeWarnings.value(warning);
}
private:
+ Q_GADGET
static const QMap<Warning, QString> dataTypeWarnings;
};
+struct CollectionParseError
+{
+ enum ParseError {
+ NoError,
+ MainObjectMissing,
+ CollectionNameNotFound,
+ CollectionIsNotObject,
+ ColumnsBlockIsNotArray,
+ UnknownError
+ };
+
+ ParseError errorNo = ParseError::NoError;
+ QString errorString() const;
+};
+
class CollectionDetails
{
Q_GADGET
@@ -68,33 +83,32 @@ public:
CollectionDetails(const CollectionDetails &other);
~CollectionDetails();
- void resetDetails(const QStringList &propertyNames,
- const QList<QJsonObject> &elements,
- CollectionEditorConstants::SourceFormat format);
+ void resetData(const QJsonDocument &localDocument,
+ const QString &collectionToImport,
+ CollectionParseError *error = nullptr);
+
void insertColumn(const QString &propertyName,
int colIdx = -1,
const QVariant &defaultValue = {},
DataType type = DataType::Unknown);
bool removeColumns(int colIdx, int count = 1);
- void insertElementAt(std::optional<QJsonObject> object, int row = -1);
- void insertEmptyElements(int row = 0, int count = 1);
- bool removeElements(int row, int count = 1);
+ void insertEmptyRows(int row = 0, int count = 1);
+ bool removeRows(int row, int count = 1);
bool setPropertyValue(int row, int column, const QVariant &value);
bool setPropertyName(int column, const QString &value);
bool setPropertyType(int column, DataType type);
CollectionReference reference() const;
- CollectionEditorConstants::SourceFormat sourceFormat() const;
QVariant data(int row, int column) const;
QString propertyAt(int column) const;
DataType typeAt(int column) const;
DataType typeAt(int row, int column) const;
DataTypeWarning::Warning cellWarningCheck(int row, int column) const;
- bool containsPropertyName(const QString &propertyName);
+ bool containsPropertyName(const QString &propertyName) const;
- bool isValid() const;
+ bool hasValidReference() const;
bool isChanged() const;
int columns() const;
@@ -104,23 +118,35 @@ public:
void swap(CollectionDetails &other);
void resetReference(const CollectionReference &reference);
- QString getCollectionAsJsonString() const;
- QString getCollectionAsCsvString() const;
- QJsonArray getCollectionAsJsonArray() const;
+ QString toJson() const;
+ QString toCsv() const;
+ QJsonObject toLocalJson() const;
static void registerDeclarativeType();
+ static CollectionDetails fromImportedCsv(const QByteArray &document,
+ const bool &firstRowIsHeader = true);
+ static CollectionDetails fromImportedJson(const QByteArray &json,
+ QJsonParseError *error = nullptr);
+ static CollectionDetails fromLocalJson(const QJsonDocument &document,
+ const QString &collectionName,
+ CollectionParseError *error = nullptr);
+
CollectionDetails &operator=(const CollectionDetails &other);
private:
void markChanged();
- void resetPropertyType(const QString &propertyName);
- void resetPropertyType(CollectionProperty &property);
- void resetPropertyTypes();
+ void insertRecords(const QJsonArray &record, int idx = -1, int count = 1);
+
+ static CollectionDetails fromImportedJson(const QJsonArray &importedArray,
+ const QStringList &propertyPriority = {});
+ static CollectionDetails fromLocalCollection(const QJsonObject &localCollection,
+ CollectionParseError *error = nullptr);
// The private data is supposed to be shared between the copies
class Private;
QSharedPointer<Private> d;
};
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp
index 51e3be9ad6..1643dfc23e 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp
@@ -3,7 +3,7 @@
#include "collectiondetailsmodel.h"
-#include "collectioneditorconstants.h"
+#include "collectiondatatypemodel.h"
#include "collectioneditorutils.h"
#include "modelnode.h"
@@ -13,116 +13,11 @@
#include <utils/qtcassert.h>
#include <utils/textfileformat.h>
-#include <QFile>
-#include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
-namespace {
-
-QStringList getJsonHeaders(const QJsonArray &collectionArray)
-{
- QSet<QString> resultSet;
- QList<QString> result;
-
- for (const QJsonValue &value : collectionArray) {
- if (value.isObject()) {
- const QJsonObject object = value.toObject();
- QJsonObject::ConstIterator element = object.constBegin();
- const QJsonObject::ConstIterator stopItem = object.constEnd();
-
- while (element != stopItem) {
- const QString property = element.key();
- if (!resultSet.contains(property)) {
- result.append(property);
- resultSet.insert(property);
- }
- ++element;
- }
- }
- }
-
- return result;
-}
-
-class CollectionDataTypeHelper
-{
-public:
- using DataType = QmlDesigner::CollectionDetails::DataType;
-
- static QString typeToString(DataType dataType)
- {
- static const QHash<DataType, QString> typeStringHash = typeToStringHash();
- return typeStringHash.value(dataType);
- }
-
- static DataType typeFromString(const QString &dataType)
- {
- static const QHash<QString, DataType> stringTypeHash = stringToTypeHash();
- return stringTypeHash.value(dataType, DataType::Unknown);
- }
-
- static QStringList typesStringList()
- {
- static const QStringList typesList = orderedTypeNames();
- return typesList;
- }
-
-private:
- CollectionDataTypeHelper() = delete;
-
- static QHash<DataType, QString> typeToStringHash()
- {
- return {
- {DataType::Unknown, "Unknown"},
- {DataType::String, "String"},
- {DataType::Url, "Url"},
- {DataType::Real, "Real"},
- {DataType::Integer, "Integer"},
- {DataType::Boolean, "Boolean"},
- {DataType::Image, "Image"},
- {DataType::Color, "Color"},
- };
- }
-
- static QHash<QString, DataType> stringToTypeHash()
- {
- QHash<QString, DataType> stringTypeHash;
- const QHash<DataType, QString> typeStringHash = typeToStringHash();
- for (const auto &transferItem : typeStringHash.asKeyValueRange())
- stringTypeHash.insert(transferItem.second, transferItem.first);
-
- return stringTypeHash;
- }
-
- static QStringList orderedTypeNames()
- {
- const QList<DataType> orderedtypes{
- DataType::String,
- DataType::Integer,
- DataType::Real,
- DataType::Image,
- DataType::Color,
- DataType::Url,
- DataType::Boolean,
- DataType::Unknown,
- };
-
- QStringList orderedNames;
- QHash<DataType, QString> typeStringHash = typeToStringHash();
-
- for (const DataType &type : orderedtypes)
- orderedNames.append(typeStringHash.take(type));
-
- Q_ASSERT(typeStringHash.isEmpty());
- return orderedNames;
- }
-};
-
-} // namespace
-
namespace QmlDesigner {
CollectionDetailsModel::CollectionDetailsModel(QObject *parent)
@@ -161,6 +56,8 @@ QVariant CollectionDetailsModel::data(const QModelIndex &index, int role) const
if (!index.isValid())
return {};
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return {});
+
if (role == SelectedRole)
return (index.column() == m_selectedColumn || index.row() == m_selectedRow);
@@ -181,6 +78,8 @@ QVariant CollectionDetailsModel::data(const QModelIndex &index, int role) const
bool CollectionDetailsModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
+
if (!index.isValid())
return {};
@@ -208,6 +107,8 @@ bool CollectionDetailsModel::setHeaderData(int section,
const QVariant &value,
[[maybe_unused]] int role)
{
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
+
if (orientation == Qt::Vertical)
return false;
@@ -220,21 +121,24 @@ bool CollectionDetailsModel::setHeaderData(int section,
bool CollectionDetailsModel::insertRows(int row, int count, [[maybe_unused]] const QModelIndex &parent)
{
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
+
if (count < 1)
return false;
row = qBound(0, row, rowCount());
- beginResetModel();
- m_currentCollection.insertEmptyElements(row, count);
- endResetModel();
+ beginInsertRows({}, row, row + count - 1);
+ m_currentCollection.insertEmptyRows(row, count);
+ endInsertRows();
- selectRow(row);
return true;
}
bool CollectionDetailsModel::removeColumns(int column, int count, const QModelIndex &parent)
{
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
+
if (column < 0 || column >= columnCount(parent) || count < 1)
return false;
@@ -246,24 +150,20 @@ bool CollectionDetailsModel::removeColumns(int column, int count, const QModelIn
if (!columnCount(parent))
removeRows(0, rowCount(parent), parent);
- int nextColumn = column - 1;
- if (nextColumn < 0 && columnCount(parent) > 0)
- nextColumn = 0;
-
- selectColumn(nextColumn);
-
ensureSingleCell();
return columnsRemoved;
}
bool CollectionDetailsModel::removeRows(int row, int count, const QModelIndex &parent)
{
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
+
if (row < 0 || row >= rowCount(parent) || count < 1)
return false;
count = std::min(count, rowCount(parent) - row);
beginRemoveRows(parent, row, row + count - 1);
- bool rowsRemoved = m_currentCollection.removeElements(row, count);
+ bool rowsRemoved = m_currentCollection.removeRows(row, count);
endRemoveRows();
ensureSingleCell();
@@ -282,7 +182,7 @@ QVariant CollectionDetailsModel::headerData(int section, Qt::Orientation orienta
{
if (orientation == Qt::Horizontal) {
if (role == DataTypeRole)
- return CollectionDataTypeHelper::typeToString(m_currentCollection.typeAt(section));
+ return CollectionDataTypeModel::dataTypeToString(m_currentCollection.typeAt(section));
else
return m_currentCollection.propertyAt(section);
}
@@ -295,6 +195,8 @@ QVariant CollectionDetailsModel::headerData(int section, Qt::Orientation orienta
CollectionDetails::DataType CollectionDetailsModel::propertyDataType(int column) const
{
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return CollectionDetails::DataType::Unknown);
+
return m_currentCollection.typeAt(column);
}
@@ -310,21 +212,29 @@ int CollectionDetailsModel::selectedRow() const
QString CollectionDetailsModel::propertyName(int column) const
{
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return {});
+
return m_currentCollection.propertyAt(column);
}
QString CollectionDetailsModel::propertyType(int column) const
{
- return CollectionDataTypeHelper::typeToString(m_currentCollection.typeAt(column));
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return {});
+
+ return CollectionDataTypeModel::dataTypeToString(m_currentCollection.typeAt(column));
}
bool CollectionDetailsModel::isPropertyAvailable(const QString &name)
{
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
+
return m_currentCollection.containsPropertyName(name);
}
bool CollectionDetailsModel::addColumn(int column, const QString &name, const QString &propertyType)
{
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
+
if (m_currentCollection.containsPropertyName(name))
return false;
@@ -335,7 +245,7 @@ bool CollectionDetailsModel::addColumn(int column, const QString &name, const QS
m_currentCollection.insertColumn(name,
column,
{},
- CollectionDataTypeHelper::typeFromString(propertyType));
+ CollectionDataTypeModel::dataTypeFromString(propertyType));
endInsertColumns();
return m_currentCollection.containsPropertyName(name);
}
@@ -379,8 +289,10 @@ bool CollectionDetailsModel::renameColumn(int section, const QString &newValue)
bool CollectionDetailsModel::setPropertyType(int column, const QString &newValue)
{
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
+
bool changed = m_currentCollection.setPropertyType(column,
- CollectionDataTypeHelper::typeFromString(
+ CollectionDataTypeModel::dataTypeFromString(
newValue));
if (changed) {
emit headerDataChanged(Qt::Horizontal, column, column);
@@ -428,11 +340,6 @@ void CollectionDetailsModel::deselectAll()
selectRow(-1);
}
-QStringList CollectionDetailsModel::typesList()
-{
- return CollectionDataTypeHelper::typesStringList();
-}
-
void CollectionDetailsModel::loadCollection(const ModelNode &sourceNode, const QString &collection)
{
QString fileName = CollectionEditorUtils::getSourceCollectionPath(sourceNode);
@@ -451,10 +358,7 @@ void CollectionDetailsModel::loadCollection(const ModelNode &sourceNode, const Q
} else {
deselectAll();
switchToCollection(newReference);
- if (sourceNode.type() == CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME)
- loadJsonCollection(fileName, collection);
- else if (sourceNode.type() == CollectionEditorConstants::CSVCOLLECTIONMODEL_TYPENAME)
- loadCsvCollection(fileName, collection);
+ loadJsonCollection(fileName, collection);
}
}
@@ -499,7 +403,6 @@ bool CollectionDetailsModel::saveDataStoreCollections()
const ModelNode node = m_currentCollection.reference().node;
const Utils::FilePath path = CollectionEditorUtils::dataStoreJsonFilePath();
Utils::FileReader fileData;
- Utils::FileSaver sourceFile(path);
if (!fileData.fetch(path)) {
qWarning() << Q_FUNC_INFO << "Cannot read the json file:" << fileData.errorString();
@@ -516,16 +419,14 @@ bool CollectionDetailsModel::saveDataStoreCollections()
for (CollectionDetails &openedCollection : m_openedCollections) {
const CollectionReference reference = openedCollection.reference();
if (reference.node == node) {
- obj.insert(reference.name, openedCollection.getCollectionAsJsonArray());
+ obj.insert(reference.name, openedCollection.toLocalJson());
collectionsToBeSaved << openedCollection;
}
}
document.setObject(obj);
- bool saved = sourceFile.write(document.toJson());
- saved &= sourceFile.finalize();
- if (saved) {
+ if (CollectionEditorUtils::writeToJsonDocument(path, document)) {
const CollectionReference currentReference = m_currentCollection.reference();
for (CollectionDetails &collection : collectionsToBeSaved) {
collection.markSaved();
@@ -545,14 +446,14 @@ bool CollectionDetailsModel::exportCollection(const QUrl &url)
using Utils::FilePath;
using Utils::TextFileFormat;
- QTC_ASSERT(m_currentCollection.isValid(), return false);
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
bool saved = false;
const FilePath filePath = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile()
: url.toString());
const QString saveFormat = filePath.toFileInfo().suffix().toLower();
- const QString content = saveFormat == "csv" ? m_currentCollection.getCollectionAsCsvString()
- : m_currentCollection.getCollectionAsJsonString();
+ const QString content = saveFormat == "csv" ? m_currentCollection.toCsv()
+ : m_currentCollection.toJson();
TextFileFormat textFileFormat;
textFileFormat.codec = EditorManager::defaultTextCodec();
@@ -566,6 +467,49 @@ bool CollectionDetailsModel::exportCollection(const QUrl &url)
return saved;
}
+const CollectionDetails CollectionDetailsModel::upToDateConstCollection(
+ const CollectionReference &reference) const
+{
+ using Utils::FilePath;
+ using Utils::FileReader;
+ CollectionDetails collection;
+
+ if (m_openedCollections.contains(reference)) {
+ collection = m_openedCollections.value(reference);
+ } else {
+ QUrl url = CollectionEditorUtils::getSourceCollectionPath(reference.node);
+ FilePath path = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile()
+ : url.toString());
+ FileReader file;
+
+ if (!file.fetch(path))
+ return collection;
+
+ QJsonParseError jpe;
+ QJsonDocument document = QJsonDocument::fromJson(file.data(), &jpe);
+
+ if (jpe.error != QJsonParseError::NoError)
+ return collection;
+
+ collection = CollectionDetails::fromLocalJson(document, reference.name);
+ collection.resetReference(reference);
+ }
+ return collection;
+}
+
+bool CollectionDetailsModel::collectionHasColumn(const CollectionReference &reference,
+ const QString &columnName) const
+{
+ const CollectionDetails collection = upToDateConstCollection(reference);
+ return collection.containsPropertyName(columnName);
+}
+
+QString CollectionDetailsModel::getFirstColumnName(const CollectionReference &reference) const
+{
+ const CollectionDetails collection = upToDateConstCollection(reference);
+ return collection.propertyAt(0);
+}
+
void CollectionDetailsModel::updateEmpty()
{
bool isEmptyNow = rowCount() == 0;
@@ -603,113 +547,25 @@ void CollectionDetailsModel::closeCollectionIfSaved(const CollectionReference &c
void CollectionDetailsModel::closeCurrentCollectionIfSaved()
{
- if (m_currentCollection.isValid()) {
+ if (m_currentCollection.hasValidReference()) {
closeCollectionIfSaved(m_currentCollection.reference());
m_currentCollection = CollectionDetails{};
}
}
-void CollectionDetailsModel::loadJsonCollection(const QString &source, const QString &collection)
-{
- using CollectionEditorConstants::SourceFormat;
-
- QFile sourceFile(source);
- QJsonArray collectionNodes;
- bool jsonFileIsOk = false;
- if (sourceFile.open(QFile::ReadOnly)) {
- QJsonParseError jpe;
- QJsonDocument document = QJsonDocument::fromJson(sourceFile.readAll(), &jpe);
- if (jpe.error == QJsonParseError::NoError) {
- jsonFileIsOk = true;
- if (document.isObject()) {
- QJsonObject collectionMap = document.object();
- if (collectionMap.contains(collection)) {
- QJsonValue collectionVal = collectionMap.value(collection);
- if (collectionVal.isArray())
- collectionNodes = collectionVal.toArray();
- else
- collectionNodes.append(collectionVal);
- }
- }
- }
- }
-
- if (collectionNodes.isEmpty()) {
- endResetModel();
- return;
- };
-
- QList<QJsonObject> elements;
- for (const QJsonValue &value : std::as_const(collectionNodes)) {
- if (value.isObject()) {
- QJsonObject object = value.toObject();
- elements.append(object);
- }
- }
-
- SourceFormat sourceFormat = jsonFileIsOk ? SourceFormat::Json : SourceFormat::Unknown;
- beginResetModel();
- m_currentCollection.resetDetails(getJsonHeaders(collectionNodes), elements, sourceFormat);
- ensureSingleCell();
- endResetModel();
-}
-
-void CollectionDetailsModel::loadCsvCollection(const QString &source,
- [[maybe_unused]] const QString &collectionName)
+void CollectionDetailsModel::loadJsonCollection(const QString &filePath, const QString &collection)
{
- using CollectionEditorConstants::SourceFormat;
-
- QFile sourceFile(source);
- QStringList headers;
- QList<QJsonObject> elements;
- bool csvFileIsOk = false;
-
- if (sourceFile.open(QFile::ReadOnly)) {
- QTextStream stream(&sourceFile);
-
- if (!stream.atEnd())
- headers = stream.readLine().split(',');
-
- if (!headers.isEmpty()) {
- while (!stream.atEnd()) {
- const QStringList recordDataList = stream.readLine().split(',');
- int column = -1;
- QJsonObject recordData;
- for (const QString &cellData : recordDataList) {
- if (++column == headers.size())
- break;
- recordData.insert(headers.at(column), cellData);
- }
- if (recordData.count())
- elements.append(recordData);
- }
- csvFileIsOk = true;
- }
- }
+ QJsonDocument document = readJsonFile(filePath);
- for (const QString &header : std::as_const(headers)) {
- for (QJsonObject &element: elements) {
- QVariant variantValue;
- if (element.contains(header)) {
- variantValue = variantFromString(element.value(header).toString());
- element[header] = variantValue.toJsonValue();
-
- if (variantValue.isValid())
- break;
- }
- }
- }
-
- SourceFormat sourceFormat = csvFileIsOk ? SourceFormat::Csv : SourceFormat::Unknown;
beginResetModel();
- m_currentCollection.resetDetails(headers, elements, sourceFormat);
+ m_currentCollection.resetData(document, collection);
ensureSingleCell();
endResetModel();
}
void CollectionDetailsModel::ensureSingleCell()
{
- if (!m_currentCollection.isValid())
+ if (!m_currentCollection.hasValidReference())
return;
if (!columnCount())
@@ -721,31 +577,25 @@ void CollectionDetailsModel::ensureSingleCell()
updateEmpty();
}
-QVariant CollectionDetailsModel::variantFromString(const QString &value)
+QJsonDocument CollectionDetailsModel::readJsonFile(const QUrl &url)
{
- constexpr QStringView typesPattern{u"(?<boolean>^(?:true|false)$)|"
- u"(?<number>^(?:-?(?:0|[1-9]\\d*)?(?:\\.\\d*)?(?<=\\d|\\.)"
- u"(?:e-?(?:0|[1-9]\\d*))?|0x[0-9a-f]+)$)|"
- u"(?<color>^(?:#(?:(?:[0-9a-fA-F]{2}){3,4}|"
- u"(?:[0-9a-fA-F]){3,4}))$)|"
- u"(?<string>[A-Za-z][A-Za-z0-9_ -]*)"};
- static QRegularExpression validator(typesPattern.toString());
- const QString trimmedValue = value.trimmed();
- QRegularExpressionMatch match = validator.match(trimmedValue);
- QVariant variantValue = value;
+ using Utils::FilePath;
+ using Utils::FileReader;
+ FilePath path = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() : url.toString());
+ FileReader file;
- if (value.isEmpty())
- return QVariant();
- if (!match.captured(u"boolean").isEmpty())
- return variantValue.toBool();
- if (!match.captured(u"number").isEmpty())
- return variantValue.toDouble();
- if (!match.captured(u"color").isEmpty())
- return variantValue.value<QColor>();
- if (!match.captured(u"string").isEmpty())
- return variantValue.toString();
+ if (!file.fetch(path)) {
+ emit warning(tr("File reading problem"), file.errorString());
+ return {};
+ }
+
+ QJsonParseError jpe;
+ QJsonDocument document = QJsonDocument::fromJson(file.data(), &jpe);
+
+ if (jpe.error != QJsonParseError::NoError)
+ emit warning(tr("Json parse error"), jpe.errorString());
- return QVariant::fromValue(value);
+ return document;
}
void CollectionDetailsModel::setCollectionName(const QString &newCollectionName)
@@ -761,5 +611,4 @@ QString CollectionDetailsModel::warningToString(DataTypeWarning::Warning warning
return DataTypeWarning::getDataTypeWarningString(warning);
}
-
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h
index cef942b044..24a040cce6 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h
@@ -58,7 +58,6 @@ public:
Q_INVOKABLE bool selectRow(int row);
Q_INVOKABLE void deselectAll();
Q_INVOKABLE QString warningToString(DataTypeWarning::Warning warning) const;
- static Q_INVOKABLE QStringList typesList();
void loadCollection(const ModelNode &sourceNode, const QString &collection);
void removeCollection(const ModelNode &sourceNode, const QString &collection);
@@ -68,11 +67,16 @@ public:
Q_INVOKABLE bool saveDataStoreCollections();
Q_INVOKABLE bool exportCollection(const QUrl &url);
+ const CollectionDetails upToDateConstCollection(const CollectionReference &reference) const;
+ bool collectionHasColumn(const CollectionReference &reference, const QString &columnName) const;
+ QString getFirstColumnName(const CollectionReference &reference) const;
+
signals:
void collectionNameChanged(const QString &collectionName);
void selectedColumnChanged(int);
void selectedRowChanged(int);
void isEmptyChanged(bool);
+ void warning(const QString &title, const QString &body);
private slots:
void updateEmpty();
@@ -82,10 +86,9 @@ private:
void closeCollectionIfSaved(const CollectionReference &collection);
void closeCurrentCollectionIfSaved();
void setCollectionName(const QString &newCollectionName);
- void loadJsonCollection(const QString &source, const QString &collection);
- void loadCsvCollection(const QString &source, const QString &collectionName);
+ void loadJsonCollection(const QString &filePath, const QString &collection);
void ensureSingleCell();
- QVariant variantFromString(const QString &value);
+ QJsonDocument readJsonFile(const QUrl &url);
QHash<CollectionReference, CollectionDetails> m_openedCollections;
CollectionDetails m_currentCollection;
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h
index a591719d87..76524762ed 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h
@@ -5,7 +5,7 @@
namespace QmlDesigner::CollectionEditorConstants {
-enum class SourceFormat { Unknown, Json, Csv };
+enum class SourceFormat { Unknown, Json };
inline constexpr char SOURCEFILE_PROPERTY[] = "source";
inline constexpr char ALLMODELS_PROPERTY[] = "allModels";
@@ -13,7 +13,6 @@ inline constexpr char JSONCHILDMODELNAME_PROPERTY[] = "modelName";
inline constexpr char COLLECTIONMODEL_IMPORT[] = "QtQuick.Studio.Utils";
inline constexpr char JSONCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Utils.JsonListModel";
-inline constexpr char CSVCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Utils.CsvTableModel";
inline constexpr char JSONCOLLECTIONCHILDMODEL_TYPENAME[] = "QtQuick.Studio.Utils.ChildListModel";
inline constexpr char JSONBACKEND_TYPENAME[] = "JsonData";
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp
index 4b779c52fa..4725987f12 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp
@@ -7,6 +7,7 @@
#include "nodemetainfo.h"
#include "propertymetainfo.h"
+#include <coreplugin/documentmanager.h>
#include <coreplugin/icore.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
@@ -24,6 +25,8 @@
#include <QJsonValue>
#include <QUrl>
+using DataType = QmlDesigner::CollectionDetails::DataType;
+
namespace {
using CollectionDataVariant = std::variant<QString, bool, double, int, QUrl, QColor>;
@@ -33,10 +36,8 @@ inline bool operator<(const QColor &a, const QColor &b)
return a.name(QColor::HexArgb) < b.name(QColor::HexArgb);
}
-inline CollectionDataVariant valueToVariant(const QVariant &value,
- QmlDesigner::CollectionDetails::DataType type)
+inline CollectionDataVariant valueToVariant(const QVariant &value, DataType type)
{
- using DataType = QmlDesigner::CollectionDetails::DataType;
switch (type) {
case DataType::String:
return value.toString();
@@ -112,32 +113,17 @@ inline Utils::FilePath qmlDirFilePath()
namespace QmlDesigner::CollectionEditorUtils {
-bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type)
+bool variantIslessThan(const QVariant &a, const QVariant &b, DataType type)
{
return std::visit(LessThanVisitor{}, valueToVariant(a, type), valueToVariant(b, type));
}
-CollectionEditorConstants::SourceFormat getSourceCollectionFormat(const ModelNode &node)
-{
- using namespace QmlDesigner;
- if (node.type() == CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME)
- return CollectionEditorConstants::SourceFormat::Json;
-
- if (node.type() == CollectionEditorConstants::CSVCOLLECTIONMODEL_TYPENAME)
- return CollectionEditorConstants::SourceFormat::Csv;
-
- return CollectionEditorConstants::SourceFormat::Unknown;
-}
-
QString getSourceCollectionType(const ModelNode &node)
{
using namespace QmlDesigner;
if (node.type() == CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME)
return "json";
- if (node.type() == CollectionEditorConstants::CSVCOLLECTIONMODEL_TYPENAME)
- return "csv";
-
return {};
}
@@ -210,16 +196,6 @@ bool isDataStoreNode(const ModelNode &dataStoreNode)
return modelPath.isSameFile(expectedFile);
}
-QJsonArray defaultCollectionArray()
-{
- QJsonObject initialObject;
- QJsonArray initialCollection;
-
- initialObject.insert("Column1", "");
- initialCollection.append(initialObject);
- return initialCollection;
-}
-
bool ensureDataStoreExists(bool &justCreated)
{
using Utils::FilePath;
@@ -240,8 +216,13 @@ bool ensureDataStoreExists(bool &justCreated)
return false;
}
- templatePath.copyFile(filePath);
- if (filePath.exists()) {
+ if (!filePath.parentDir().ensureWritableDir()) {
+ qWarning() << Q_FUNC_INFO << __LINE__ << "Cannot create directory"
+ << filePath.parentDir();
+ return false;
+ }
+
+ if (templatePath.copyFile(filePath)) {
justCreated = true;
return true;
}
@@ -293,12 +274,6 @@ bool ensureDataStoreExists(bool &justCreated)
if (qmlDirSaver.finalize()) {
justCreated = true;
-
- // Force code model reset to notice changes to existing module
- auto modelManager = QmlJS::ModelManagerInterface::instance();
- if (modelManager)
- modelManager->resetCodeModel();
-
return true;
}
@@ -306,178 +281,60 @@ bool ensureDataStoreExists(bool &justCreated)
return false;
}
-QJsonArray loadAsSingleJsonCollection(const QUrl &url)
+QJsonObject defaultCollection()
{
- QFile file(url.isLocalFile() ? url.toLocalFile() : url.toString());
- QJsonArray collection;
- QByteArray jsonData;
- if (file.open(QFile::ReadOnly))
- jsonData = file.readAll();
-
- file.close();
- if (jsonData.isEmpty())
- return {};
+ QJsonObject collectionObject;
- QJsonParseError parseError;
- QJsonDocument document = QJsonDocument::fromJson(jsonData, &parseError);
- if (parseError.error != QJsonParseError::NoError)
- return {};
-
- auto refineJsonArray = [](const QJsonArray &array) -> QJsonArray {
- QJsonArray resultArray;
- for (const QJsonValue &collectionData : array) {
- if (collectionData.isObject()) {
- QJsonObject rowObject = collectionData.toObject();
- const QStringList rowKeys = rowObject.keys();
- for (const QString &key : rowKeys) {
- QJsonValue cellValue = rowObject.value(key);
- if (cellValue.isArray())
- rowObject.remove(key);
- }
- resultArray.push_back(rowObject);
- }
- }
- return resultArray;
- };
+ QJsonArray columns;
+ QJsonObject defaultColumn;
+ defaultColumn.insert("name", "Column 1");
+ defaultColumn.insert("type", "string");
+ columns.append(defaultColumn);
- if (document.isArray()) {
- collection = refineJsonArray(document.array());
- } else if (document.isObject()) {
- QJsonObject documentObject = document.object();
- const QStringList mainKeys = documentObject.keys();
-
- bool arrayFound = false;
- for (const QString &key : mainKeys) {
- const QJsonValue &value = documentObject.value(key);
- if (value.isArray()) {
- arrayFound = true;
- collection = refineJsonArray(value.toArray());
- break;
- }
- }
+ QJsonArray collectionData;
+ QJsonArray cellData;
+ cellData.append(QString{});
+ collectionData.append(cellData);
- if (!arrayFound) {
- QJsonObject singleObject;
- for (const QString &key : mainKeys) {
- const QJsonValue value = documentObject.value(key);
+ collectionObject.insert("columns", columns);
+ collectionObject.insert("data", collectionData);
- if (!value.isObject())
- singleObject.insert(key, value);
- }
- collection.push_back(singleObject);
- }
- }
- return collection;
-}
-
-QJsonArray loadAsCsvCollection(const QUrl &url)
-{
- QFile sourceFile(url.isLocalFile() ? url.toLocalFile() : url.toString());
- QStringList headers;
- QJsonArray elements;
-
- if (sourceFile.open(QFile::ReadOnly)) {
- QTextStream stream(&sourceFile);
-
- if (!stream.atEnd())
- headers = stream.readLine().split(',');
-
- for (QString &header : headers)
- header = header.trimmed();
-
- if (!headers.isEmpty()) {
- while (!stream.atEnd()) {
- const QStringList recordDataList = stream.readLine().split(',');
- int column = -1;
- QJsonObject recordData;
- for (const QString &cellData : recordDataList) {
- if (++column == headers.size())
- break;
- recordData.insert(headers.at(column), cellData);
- }
- elements.append(recordData);
- }
- }
- }
-
- return elements;
+ return collectionObject;
}
-QString getFirstColumnName(const QString &collectionName)
+QJsonObject defaultColorCollection()
{
- Utils::FilePath dataStorePath = CollectionEditorUtils::dataStoreJsonFilePath();
+ using Utils::FilePath;
+ using Utils::FileReader;
+ const FilePath templatePath = findFile(Core::ICore::resourcePath(), "Colors.json.tpl");
- if (!dataStorePath.exists())
+ FileReader fileReader;
+ if (!fileReader.fetch(templatePath)) {
+ qWarning() << Q_FUNC_INFO << __LINE__ << "Can't read the content of the file" << templatePath;
return {};
+ }
- Utils::FileReader dataStoreFile;
- if (!dataStoreFile.fetch(dataStorePath))
+ QJsonParseError parseError;
+ const CollectionDetails collection = CollectionDetails::fromImportedJson(fileReader.data(),
+ &parseError);
+ if (parseError.error != QJsonParseError::NoError) {
+ qWarning() << Q_FUNC_INFO << __LINE__ << "Error in template file" << parseError.errorString();
return {};
-
- QJsonParseError jsonError;
- QJsonDocument dataStoreDocument = QJsonDocument::fromJson(dataStoreFile.data(), &jsonError);
- if (jsonError.error == QJsonParseError::NoError) {
- QJsonObject rootObject = dataStoreDocument.object();
- if (rootObject.contains(collectionName)) {
- QJsonArray collectionArray = rootObject.value(collectionName).toArray();
- for (const QJsonValue &elementValue : std::as_const(collectionArray)) {
- const QJsonObject elementObject = elementValue.toObject();
- QJsonObject::ConstIterator element = elementObject.constBegin();
- if (element != elementObject.constEnd())
- return element.key();
- }
- } else {
- qWarning() << Q_FUNC_INFO << __LINE__
- << QString("Collection \"%1\" not found.").arg(collectionName);
- }
- } else {
- qWarning() << Q_FUNC_INFO << __LINE__ << "Problem in reading json file."
- << jsonError.errorString();
}
- return {};
+ return collection.toLocalJson();
}
-bool collectionHasColumn(const QString &collectionName, const QString &columnName)
+bool writeToJsonDocument(const Utils::FilePath &path, const QJsonDocument &document, QString *errorString)
{
- Utils::FilePath dataStorePath = CollectionEditorUtils::dataStoreJsonFilePath();
-
- if (!dataStorePath.exists())
- return false;
-
- Utils::FileReader dataStoreFile;
- if (!dataStoreFile.fetch(dataStorePath))
- return false;
-
- QJsonParseError jsonError;
- QJsonDocument dataStoreDocument = QJsonDocument::fromJson(dataStoreFile.data(), &jsonError);
- if (jsonError.error == QJsonParseError::NoError) {
- QJsonObject rootObject = dataStoreDocument.object();
- if (rootObject.contains(collectionName)) {
- QJsonArray collectionArray = rootObject.value(collectionName).toArray();
- for (const QJsonValue &elementValue : std::as_const(collectionArray)) {
- const QJsonObject elementObject = elementValue.toObject();
- QJsonObject::ConstIterator element = elementObject.constBegin();
- const QJsonObject::ConstIterator stopItem = elementObject.constEnd();
-
- while (element != stopItem) {
- const QString keyName = element.key();
- ++element;
-
- if (columnName == keyName)
- return true;
- }
- }
- } else {
- qWarning() << Q_FUNC_INFO << __LINE__
- << QString("Collection \"%1\" not found.").arg(collectionName);
- }
- } else {
- qWarning() << Q_FUNC_INFO << __LINE__ << "Problem in reading json file."
- << jsonError.errorString();
- }
-
- return false;
+ Core::FileChangeBlocker fileBlocker(path);
+ Utils::FileSaver jsonFile(path);
+ if (jsonFile.write(document.toJson()))
+ jsonFile.finalize();
+ if (errorString)
+ *errorString = jsonFile.errorString();
+
+ return !jsonFile.hasError();
}
} // namespace QmlDesigner::CollectionEditorUtils
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h
index 46429f04b6..355addf59b 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h
@@ -8,6 +8,7 @@
QT_BEGIN_NAMESPACE
class QJsonArray;
+class QJsonObject;
QT_END_NAMESPACE
namespace Utils {
@@ -18,8 +19,6 @@ namespace QmlDesigner::CollectionEditorUtils {
bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type);
-CollectionEditorConstants::SourceFormat getSourceCollectionFormat(const QmlDesigner::ModelNode &node);
-
QString getSourceCollectionType(const QmlDesigner::ModelNode &node);
QString getSourceCollectionPath(const QmlDesigner::ModelNode &dataStoreNode);
@@ -28,6 +27,10 @@ Utils::FilePath dataStoreJsonFilePath();
Utils::FilePath dataStoreQmlFilePath();
+bool writeToJsonDocument(const Utils::FilePath &path,
+ const QJsonDocument &document,
+ QString *errorString = nullptr);
+
bool isDataStoreNode(const ModelNode &dataStoreNode);
bool ensureDataStoreExists(bool &justCreated);
@@ -36,14 +39,8 @@ bool canAcceptCollectionAsModel(const ModelNode &node);
bool hasTextRoleProperty(const ModelNode &node);
-QJsonArray defaultCollectionArray();
-
-QJsonArray loadAsSingleJsonCollection(const QUrl &url);
-
-QJsonArray loadAsCsvCollection(const QUrl &url);
-
-QString getFirstColumnName(const QString &collectionName);
+QJsonObject defaultCollection();
-bool collectionHasColumn(const QString &collectionName, const QString &columnName);
+QJsonObject defaultColorCollection();
} // namespace QmlDesigner::CollectionEditorUtils
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp
index 214ece078a..d27a077d2a 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp
@@ -3,13 +3,16 @@
#include "collectionlistmodel.h"
-#include "collectioneditorconstants.h"
#include "collectioneditorutils.h"
-#include "variantproperty.h"
#include <utils/algorithm.h>
+#include <utils/fileutils.h>
#include <utils/qtcassert.h>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonParseError>
+
namespace {
template<typename ValueType>
@@ -22,14 +25,23 @@ bool containsItem(const std::initializer_list<ValueType> &container, const Value
return it != end;
}
+bool sameCollectionNames(QStringList a, QStringList b)
+{
+ if (a.size() != b.size())
+ return false;
+
+ a.sort(Qt::CaseSensitive);
+ b.sort(Qt::CaseSensitive);
+
+ return a == b;
+}
+
} // namespace
namespace QmlDesigner {
-CollectionListModel::CollectionListModel(const ModelNode &sourceModel)
- : QStringListModel()
- , m_sourceNode(sourceModel)
- , m_sourceType(CollectionEditorUtils::getSourceCollectionType(sourceModel))
+CollectionListModel::CollectionListModel()
+ : QAbstractListModel()
{
connect(this, &CollectionListModel::modelReset, this, &CollectionListModel::updateEmpty);
connect(this, &CollectionListModel::rowsRemoved, this, &CollectionListModel::updateEmpty);
@@ -50,22 +62,37 @@ QHash<int, QByteArray> CollectionListModel::roleNames() const
return roles;
}
+int CollectionListModel::rowCount([[maybe_unused]] const QModelIndex &parent) const
+{
+ return m_data.count();
+}
+
bool CollectionListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid())
return false;
if (containsItem<int>({Qt::EditRole, Qt::DisplayRole, NameRole}, role)) {
- if (contains(value.toString()))
+ if (collectionExists(value.toString()))
return false;
QString oldName = collectionNameAt(index.row());
- bool nameChanged = Super::setData(index, value);
+ bool nameChanged = value != data(index);
if (nameChanged) {
- QString newName = collectionNameAt(index.row());
- emit this->collectionNameChanged(oldName, newName);
+ QString newName = value.toString();
+ QString errorString;
+ if (renameCollectionInDataStore(oldName, newName, errorString)) {
+ m_data.replace(index.row(), newName);
+ emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, NameRole});
+ emit this->collectionNameChanged(oldName, newName);
+ if (m_selectedCollectionName == oldName)
+ updateSelectedCollectionName();
+ return true;
+ } else {
+ emit warning("Rename Model", errorString);
+ return false;
+ }
}
- return nameChanged;
} else if (role == SelectedRole) {
if (value.toBool() != index.data(SelectedRole).toBool()) {
setSelectedIndex(value.toBool() ? index.row() : -1);
@@ -78,22 +105,36 @@ bool CollectionListModel::setData(const QModelIndex &index, const QVariant &valu
bool CollectionListModel::removeRows(int row, int count, const QModelIndex &parent)
{
const int rows = rowCount(parent);
- if (count < 1 || row >= rows)
+ if (row >= rows)
return false;
row = qBound(0, row, rows - 1);
- count = qBound(1, count, rows - row);
+ count = qBound(0, count, rows - row);
- QStringList removedCollections = stringList().mid(row, count);
+ if (count < 1)
+ return false;
+
+ QString errorString;
+ QStringList removedCollections = m_data.mid(row, count);
+ if (removeCollectionsFromDataStore(removedCollections, errorString)) {
+ beginRemoveRows(parent, row, row + count - 1);
+ m_data.remove(row, count);
+ endRemoveRows();
- bool itemsRemoved = Super::removeRows(row, count, parent);
- if (itemsRemoved) {
emit collectionsRemoved(removedCollections);
- if (m_selectedIndex >= row)
- selectCollectionIndex(m_selectedIndex - count, true);
- }
+ if (m_selectedIndex >= row) {
+ int preferredIndex = m_selectedIndex - count;
+ if (preferredIndex < 0) // If the selected item is deleted, reset selection
+ selectCollectionIndex(-1);
+ selectCollectionIndex(preferredIndex, true);
+ }
- return itemsRemoved;
+ updateSelectedCollectionName();
+ return true;
+ } else {
+ emit warning("Remove Model", errorString);
+ return false;
+ }
}
QVariant CollectionListModel::data(const QModelIndex &index, int role) const
@@ -103,13 +144,18 @@ QVariant CollectionListModel::data(const QModelIndex &index, int role) const
switch (role) {
case IdRole:
return index.row();
- case NameRole:
- return Super::data(index);
case SelectedRole:
return index.row() == m_selectedIndex;
+ case NameRole:
+ default:
+ return m_data.at(index.row());
}
+}
- return Super::data(index, role);
+void CollectionListModel::setDataStoreNode(const ModelNode &dataStoreNode)
+{
+ m_dataStoreNode = dataStoreNode;
+ update();
}
int CollectionListModel::selectedIndex() const
@@ -119,22 +165,35 @@ int CollectionListModel::selectedIndex() const
ModelNode CollectionListModel::sourceNode() const
{
- return m_sourceNode;
+ return m_dataStoreNode;
+}
+
+bool CollectionListModel::collectionExists(const QString &collectionName) const
+{
+ return m_data.contains(collectionName);
}
-QString CollectionListModel::sourceAddress() const
+QStringList CollectionListModel::collections() const
{
- return CollectionEditorUtils::getSourceCollectionPath(m_sourceNode);
+ return m_data;
}
-bool CollectionListModel::contains(const QString &collectionName) const
+QString CollectionListModel::getUniqueCollectionName(const QString &baseName) const
{
- return stringList().contains(collectionName);
+ QString name = baseName.isEmpty() ? "Model" : baseName;
+ QString nameTemplate = name + "%1";
+
+ int num = 0;
+
+ while (collectionExists(name))
+ name = nameTemplate.arg(++num, 2, 10, QChar('0'));
+
+ return name;
}
void CollectionListModel::selectCollectionIndex(int idx, bool selectAtLeastOne)
{
- int collectionCount = stringList().size();
+ int collectionCount = m_data.size();
int preferredIndex = -1;
if (collectionCount) {
if (selectAtLeastOne)
@@ -146,11 +205,20 @@ void CollectionListModel::selectCollectionIndex(int idx, bool selectAtLeastOne)
setSelectedIndex(preferredIndex);
}
-void CollectionListModel::selectCollectionName(const QString &collectionName)
+void CollectionListModel::selectCollectionName(QString collectionName, bool selectAtLeastOne)
{
- int idx = stringList().indexOf(collectionName);
+ int idx = m_data.indexOf(collectionName);
if (idx > -1)
selectCollectionIndex(idx);
+ else
+ selectCollectionIndex(selectedIndex(), selectAtLeastOne);
+
+ collectionName = collectionNameAt(selectedIndex());
+ if (m_selectedCollectionName == collectionName)
+ return;
+
+ m_selectedCollectionName = collectionName;
+ emit selectedCollectionNameChanged(m_selectedCollectionName);
}
QString CollectionListModel::collectionNameAt(int idx) const
@@ -158,6 +226,78 @@ QString CollectionListModel::collectionNameAt(int idx) const
return index(idx).data(NameRole).toString();
}
+QString CollectionListModel::selectedCollectionName() const
+{
+ return m_selectedCollectionName;
+}
+
+void CollectionListModel::update()
+{
+ using Utils::FilePath;
+ using Utils::FileReader;
+
+ FileReader sourceFile;
+ QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode);
+ FilePath path = FilePath::fromUserInput(sourceFileAddress);
+ bool fileRead = false;
+ if (path.exists()) {
+ fileRead = sourceFile.fetch(path);
+ if (!fileRead)
+ emit this->warning(tr("Model Editor"),
+ tr("Cannot read the dataStore file\n%1").arg(sourceFile.errorString()));
+ }
+
+ QStringList collectionNames;
+ if (fileRead) {
+ QJsonParseError parseError;
+ QJsonDocument document = QJsonDocument::fromJson(sourceFile.data(), &parseError);
+ if (parseError.error != QJsonParseError::NoError) {
+ emit this->warning(tr("Model Editor"),
+ tr("There is an error in the JSON file.\n%1")
+ .arg(parseError.errorString()));
+ } else {
+ if (document.isObject())
+ collectionNames = document.object().toVariantMap().keys();
+ else
+ emit this->warning(tr("Model Editor"), tr("The JSON document be an object."));
+ }
+ }
+
+ if (!sameCollectionNames(m_data, collectionNames)) {
+ QString prevSelectedCollection = selectedIndex() > -1 ? m_data.at(selectedIndex())
+ : QString();
+ beginResetModel();
+ m_data = collectionNames;
+ endResetModel();
+ emit this->collectionNamesChanged(collections());
+ selectCollectionName(prevSelectedCollection, true);
+ }
+}
+
+bool CollectionListModel::addCollection(const QString &collectionName,
+ const QJsonObject &localCollection)
+{
+ if (collectionExists(collectionName)) {
+ emit warning(tr("Add Model"), tr("Model \"%1\" already exists.").arg(collectionName));
+ return false;
+ }
+
+ QString errorMessage;
+ if (addCollectionToDataStore(collectionName, localCollection, errorMessage)) {
+ int row = rowCount();
+ beginInsertRows({}, row, row);
+ m_data.append(collectionName);
+ endInsertRows();
+
+ selectCollectionName(collectionName);
+ emit collectionAdded(collectionName);
+ return true;
+ } else {
+ emit warning(tr("Add Collection"), errorMessage);
+ }
+ return false;
+}
+
void CollectionListModel::setSelectedIndex(int idx)
{
idx = (idx > -1 && idx < rowCount()) ? idx : -1;
@@ -175,12 +315,193 @@ void CollectionListModel::setSelectedIndex(int idx)
emit dataChanged(newIndex, newIndex, {SelectedRole});
emit selectedIndexChanged(idx);
+ updateSelectedCollectionName();
+ }
+}
+
+bool CollectionListModel::removeCollectionsFromDataStore(const QStringList &removedCollections,
+ QString &error) const
+{
+ using Utils::FilePath;
+ using Utils::FileReader;
+ auto setErrorAndReturn = [&error](const QString &msg) -> bool {
+ error = msg;
+ return false;
+ };
+
+ if (m_dataStoreNode.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME)
+ return setErrorAndReturn(tr("Invalid node type"));
+
+ QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode);
+
+ QFileInfo sourceFileInfo(sourceFileAddress);
+ if (!sourceFileInfo.isFile())
+ return setErrorAndReturn(tr("The selected node has an invalid source address"));
+
+ FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress);
+ FileReader jsonFile;
+ if (!jsonFile.fetch(jsonPath)) {
+ return setErrorAndReturn(tr("Can't read file \"%1\".\n%2")
+ .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString()));
+ }
+
+ QJsonParseError parseError;
+ QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError);
+ if (parseError.error != QJsonParseError::NoError) {
+ return setErrorAndReturn(tr("\"%1\" is corrupted.\n%2")
+ .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString()));
+ }
+
+ if (document.isObject()) {
+ QJsonObject rootObject = document.object();
+
+ for (const QString &collectionName : removedCollections) {
+ bool sourceContainsCollection = rootObject.contains(collectionName);
+ if (sourceContainsCollection) {
+ rootObject.remove(collectionName);
+ } else {
+ setErrorAndReturn(tr("The model group doesn't contain the model name (%1).")
+ .arg(sourceContainsCollection));
+ }
+ }
+
+ document.setObject(rootObject);
+
+ if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) {
+ error.clear();
+ return true;
+ } else {
+ return setErrorAndReturn(
+ tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
+ }
+ } else {
+ return setErrorAndReturn(tr("Local Json Document should be an object"));
+ }
+
+ return false;
+}
+
+bool CollectionListModel::renameCollectionInDataStore(const QString &oldName,
+ const QString &newName,
+ QString &error)
+{
+ using Utils::FilePath;
+ using Utils::FileReader;
+ using Utils::FileSaver;
+
+ auto setErrorAndReturn = [&error](const QString &msg) -> bool {
+ error = msg;
+ return false;
+ };
+
+ if (m_dataStoreNode.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME)
+ return setErrorAndReturn(tr("Invalid node type"));
+
+ QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode);
+
+ QFileInfo sourceFileInfo(sourceFileAddress);
+ if (!sourceFileInfo.isFile())
+ return setErrorAndReturn(tr("Selected node must have a valid source file address"));
+
+ FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress);
+ FileReader jsonFile;
+ if (!jsonFile.fetch(jsonPath)) {
+ return setErrorAndReturn(
+ tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString()));
+ }
+
+ QJsonParseError parseError;
+ QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError);
+ if (parseError.error != QJsonParseError::NoError) {
+ return setErrorAndReturn(tr("\"%1\" is corrupted.\n%2")
+ .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString()));
+ }
+
+ if (document.isObject()) {
+ QJsonObject rootObject = document.object();
+
+ bool collectionContainsOldName = rootObject.contains(oldName);
+ bool collectionContainsNewName = rootObject.contains(newName);
+
+ if (!collectionContainsOldName) {
+ return setErrorAndReturn(
+ tr("The model group doesn't contain the old model name (%1).").arg(oldName));
+ }
+
+ if (collectionContainsNewName) {
+ return setErrorAndReturn(
+ tr("The model name \"%1\" already exists in the model group.").arg(newName));
+ }
+
+ QJsonValue oldValue = rootObject.value(oldName);
+ rootObject.insert(newName, oldValue);
+ rootObject.remove(oldName);
+
+ document.setObject(rootObject);
+
+ if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) {
+ error.clear();
+ return true;
+ } else {
+ return setErrorAndReturn(
+ tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
+ }
+ } else {
+ return setErrorAndReturn(tr("Local Json Document should be an object"));
+ }
+ return false;
+}
+
+bool CollectionListModel::addCollectionToDataStore(const QString &collectionName,
+ const QJsonObject &localCollection,
+ QString &errorString) const
+{
+ using Utils::FilePath;
+ using Utils::FileReader;
+ auto returnError = [&errorString](const QString &msg) -> bool {
+ errorString = msg;
+ return false;
+ };
+
+ if (collectionExists(collectionName))
+ return returnError(tr("A model with the identical name already exists."));
+
+ QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode);
+
+ QFileInfo sourceFileInfo(sourceFileAddress);
+ if (!sourceFileInfo.isFile())
+ return returnError(tr("Selected node must have a valid source file address"));
+
+ FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress);
+ FileReader jsonFile;
+ if (!jsonFile.fetch(jsonPath)) {
+ return returnError(
+ tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString()));
+ }
+
+ QJsonParseError parseError;
+ QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError);
+ if (parseError.error != QJsonParseError::NoError)
+ return returnError(tr("\"%1\" is corrupted.\n%2")
+ .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString()));
+
+ if (document.isObject()) {
+ QJsonObject sourceObject = document.object();
+ sourceObject.insert(collectionName, localCollection);
+ document.setObject(sourceObject);
+
+ if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document))
+ return true;
+ else
+ return returnError(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
+ } else {
+ return returnError(tr("JSON document type should be an object containing models."));
}
}
void CollectionListModel::updateEmpty()
{
- bool isEmptyNow = stringList().isEmpty();
+ bool isEmptyNow = m_data.isEmpty();
if (m_isEmpty != isEmptyNow) {
m_isEmpty = isEmptyNow;
emit isEmptyChanged(m_isEmpty);
@@ -190,4 +511,11 @@ void CollectionListModel::updateEmpty()
}
}
+void CollectionListModel::updateSelectedCollectionName()
+{
+ QString selectedCollectionByIndex = collectionNameAt(selectedIndex());
+ if (selectedCollectionByIndex != selectedCollectionName())
+ selectCollectionName(selectedCollectionByIndex);
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h
index c65af750d8..7902fd5909 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h
@@ -3,56 +3,77 @@
#pragma once
+#include <QAbstractListModel>
#include <QHash>
-#include <QStringListModel>
#include "modelnode.h"
namespace QmlDesigner {
-class CollectionListModel : public QStringListModel
+class CollectionListModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged)
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
- Q_PROPERTY(QString sourceType MEMBER m_sourceType CONSTANT)
+ Q_PROPERTY(QString selectedCollectionName
+ READ selectedCollectionName
+ WRITE selectCollectionName
+ NOTIFY selectedCollectionNameChanged)
public:
- enum Roles { IdRole = Qt::UserRole + 1, NameRole, SourceRole, SelectedRole, CollectionsRole };
+ enum Roles { IdRole = Qt::UserRole + 1, NameRole, SelectedRole };
- explicit CollectionListModel(const ModelNode &sourceModel);
- virtual QHash<int, QByteArray> roleNames() const override;
+ explicit CollectionListModel();
+ QHash<int, QByteArray> roleNames() const override;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
bool removeRows(int row, int count, const QModelIndex &parent = {}) override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ void setDataStoreNode(const ModelNode &dataStoreNode = {});
Q_INVOKABLE int selectedIndex() const;
Q_INVOKABLE ModelNode sourceNode() const;
- Q_INVOKABLE QString sourceAddress() const;
- Q_INVOKABLE bool contains(const QString &collectionName) const;
+ Q_INVOKABLE bool collectionExists(const QString &collectionName) const;
+ Q_INVOKABLE QStringList collections() const;
+ Q_INVOKABLE QString getUniqueCollectionName(const QString &baseName = {}) const;
void selectCollectionIndex(int idx, bool selectAtLeastOne = false);
- void selectCollectionName(const QString &collectionName);
+ void selectCollectionName(QString collectionName, bool selectAtLeastOne = false);
QString collectionNameAt(int idx) const;
+ QString selectedCollectionName() const;
+
+ void update();
+ bool addCollection(const QString &collectionName, const QJsonObject &localCollection);
signals:
void selectedIndexChanged(int idx);
void isEmptyChanged(bool);
void collectionNameChanged(const QString &oldName, const QString &newName);
+ void collectionNamesChanged(const QStringList &collectionNames);
void collectionsRemoved(const QStringList &names);
+ void collectionAdded(const QString &name);
+ void selectedCollectionNameChanged(const QString &selectedCollectionName);
+ void warning(const QString &title, const QString &body);
private:
void setSelectedIndex(int idx);
+ bool removeCollectionsFromDataStore(const QStringList &removedCollections, QString &error) const;
+ bool renameCollectionInDataStore(const QString &oldName, const QString &newName, QString &error);
+ bool addCollectionToDataStore(const QString &collectionName,
+ const QJsonObject &localCollection,
+ QString &errorString) const;
void updateEmpty();
+ void updateSelectedCollectionName();
- using Super = QStringListModel;
+ using Super = QAbstractListModel;
int m_selectedIndex = -1;
bool m_isEmpty = false;
- const ModelNode m_sourceNode;
- const QString m_sourceType;
+ ModelNode m_dataStoreNode;
+ QString m_selectedCollectionName;
+ QStringList m_data;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp
deleted file mode 100644
index 1d27f20548..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp
+++ /dev/null
@@ -1,725 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "collectionsourcemodel.h"
-
-#include "abstractview.h"
-#include "collectioneditorconstants.h"
-#include "collectioneditorutils.h"
-#include "collectionlistmodel.h"
-#include "variantproperty.h"
-
-#include <utils/qtcassert.h>
-#include <qqml.h>
-
-#include <QFile>
-#include <QFileInfo>
-#include <QJsonArray>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonParseError>
-
-namespace {
-
-QSharedPointer<QmlDesigner::CollectionListModel> loadCollection(
- const QmlDesigner::ModelNode &sourceNode,
- QSharedPointer<QmlDesigner::CollectionListModel> initialCollection = {})
-{
- using namespace QmlDesigner::CollectionEditorConstants;
- using namespace QmlDesigner::CollectionEditorUtils;
- QString sourceFileAddress = getSourceCollectionPath(sourceNode);
-
- QSharedPointer<QmlDesigner::CollectionListModel> collectionsList;
- auto setupCollectionList = [&sourceNode, &initialCollection, &collectionsList]() {
- if (initialCollection.isNull())
- collectionsList.reset(new QmlDesigner::CollectionListModel(sourceNode));
- else if (initialCollection->sourceNode() == sourceNode)
- collectionsList = initialCollection;
- else
- collectionsList.reset(new QmlDesigner::CollectionListModel(sourceNode));
- };
-
- if (sourceNode.type() == JSONCOLLECTIONMODEL_TYPENAME) {
- QFile sourceFile(sourceFileAddress);
- if (!sourceFile.open(QFile::ReadOnly))
- return {};
-
- QJsonParseError parseError;
- QJsonDocument document = QJsonDocument::fromJson(sourceFile.readAll(), &parseError);
- if (parseError.error != QJsonParseError::NoError)
- return {};
-
- setupCollectionList();
-
- if (document.isObject()) {
- const QJsonObject sourceObject = document.object();
- collectionsList->setStringList(sourceObject.toVariantMap().keys());
- }
- } else if (sourceNode.type() == CSVCOLLECTIONMODEL_TYPENAME) {
- QmlDesigner::VariantProperty collectionNameProperty = sourceNode.variantProperty(
- "objectName");
- setupCollectionList();
- collectionsList->setStringList({collectionNameProperty.value().toString()});
- }
- return collectionsList;
-}
-
-} // namespace
-
-namespace QmlDesigner {
-
-CollectionSourceModel::CollectionSourceModel(QObject *parent)
- : Super(parent)
-{}
-
-int CollectionSourceModel::rowCount(const QModelIndex &) const
-{
- return m_collectionSources.size();
-}
-
-QVariant CollectionSourceModel::data(const QModelIndex &index, int role) const
-{
- QTC_ASSERT(index.isValid(), return {});
-
- const ModelNode *collectionSource = &m_collectionSources.at(index.row());
-
- switch (role) {
- case NameRole: // Not used, to be removed
- return collectionSource->variantProperty("objectName").value().toString();
- case NodeRole:
- return QVariant::fromValue(*collectionSource);
- case CollectionTypeRole:
- return CollectionEditorUtils::getSourceCollectionType(*collectionSource);
- case SourceRole:
- return collectionSource->variantProperty(CollectionEditorConstants::SOURCEFILE_PROPERTY).value();
- case SelectedRole:
- return index.row() == m_selectedIndex;
- case CollectionsRole:
- return QVariant::fromValue(m_collectionList.at(index.row()).data());
- }
-
- return {};
-}
-
-bool CollectionSourceModel::setData(const QModelIndex &index, const QVariant &value, int role)
-{
- if (!index.isValid())
- return false;
-
- ModelNode collectionSource = m_collectionSources.at(index.row());
- switch (role) {
- case Qt::DisplayRole:
- case NameRole: {
- auto collectionName = collectionSource.variantProperty("objectName");
- if (collectionName.value() == value)
- return false;
-
- collectionName.setValue(value.toString());
- } break;
- case SourceRole: {
- auto sourceAddress = collectionSource.variantProperty(
- CollectionEditorConstants::SOURCEFILE_PROPERTY);
- if (sourceAddress.value() == value)
- return false;
-
- sourceAddress.setValue(value.toString());
- } break;
- case SelectedRole: {
- if (value.toBool() != index.data(SelectedRole).toBool())
- setSelectedIndex(value.toBool() ? index.row() : -1);
- else
- return false;
- } break;
- default:
- return false;
- }
-
- return true;
-}
-
-bool CollectionSourceModel::removeRows(int row, int count, [[maybe_unused]] const QModelIndex &parent)
-{
- const int rowMax = std::min(row + count, rowCount());
-
- if (row >= rowMax || row < 0)
- return false;
-
- AbstractView *view = m_collectionSources.at(row).view();
- if (!view)
- return false;
-
- count = rowMax - row;
-
- bool selectionUpdateNeeded = m_selectedIndex >= row && m_selectedIndex < rowMax;
-
- // It's better to remove the group of nodes here because of the performance issue for the list,
- // and update issue for the view
- beginRemoveRows({}, row, rowMax - 1);
-
- view->executeInTransaction(Q_FUNC_INFO, [row, count, this]() {
- for (ModelNode node : Utils::span<const ModelNode>(m_collectionSources).subspan(row, count)) {
- m_sourceIndexHash.remove(node.internalId());
- node.destroy();
- }
- m_collectionSources.remove(row, count);
- m_collectionList.remove(row, count);
- });
-
- int idx = row;
- for (const ModelNode &node : Utils::span<const ModelNode>(m_collectionSources).subspan(row))
- m_sourceIndexHash.insert(node.internalId(), ++idx);
-
- endRemoveRows();
-
- if (selectionUpdateNeeded)
- updateSelectedSource();
-
- updateEmpty();
- return true;
-}
-
-QHash<int, QByteArray> CollectionSourceModel::roleNames() const
-{
- static QHash<int, QByteArray> roles;
- if (roles.isEmpty()) {
- roles.insert(Super::roleNames());
- roles.insert({{NameRole, "sourceName"},
- {NodeRole, "sourceNode"},
- {CollectionTypeRole, "sourceCollectionType"},
- {SelectedRole, "sourceIsSelected"},
- {SourceRole, "sourceAddress"},
- {CollectionsRole, "internalModels"}});
- }
- return roles;
-}
-
-void CollectionSourceModel::setSources(const ModelNodes &sources)
-{
- beginResetModel();
- m_collectionSources = sources;
- m_sourceIndexHash.clear();
- m_collectionList.clear();
- int i = -1;
- for (const ModelNode &collectionSource : sources) {
- m_sourceIndexHash.insert(collectionSource.internalId(), ++i);
-
- auto loadedCollection = loadCollection(collectionSource);
- m_collectionList.append(loadedCollection);
-
- registerCollection(loadedCollection);
- }
-
- updateEmpty();
- endResetModel();
-
- updateSelectedSource(true);
-}
-
-void CollectionSourceModel::removeSource(const ModelNode &node)
-{
- int nodePlace = m_sourceIndexHash.value(node.internalId(), -1);
- if (nodePlace < 0)
- return;
-
- removeRow(nodePlace);
-}
-
-int CollectionSourceModel::sourceIndex(const ModelNode &node) const
-{
- return m_sourceIndexHash.value(node.internalId(), -1);
-}
-
-void CollectionSourceModel::addSource(const ModelNode &node)
-{
- int newRowId = m_collectionSources.count();
- beginInsertRows({}, newRowId, newRowId);
- m_collectionSources.append(node);
- m_sourceIndexHash.insert(node.internalId(), newRowId);
-
- auto loadedCollection = loadCollection(node);
- m_collectionList.append(loadedCollection);
-
- registerCollection(loadedCollection);
-
- updateEmpty();
- endInsertRows();
- updateSelectedSource(true);
-}
-
-void CollectionSourceModel::selectSource(const ModelNode &node)
-{
- int nodePlace = m_sourceIndexHash.value(node.internalId(), -1);
- if (nodePlace < 0)
- return;
-
- selectSourceIndex(nodePlace, true);
-}
-
-bool CollectionSourceModel::collectionExists(const ModelNode &node, const QString &collectionName) const
-{
- int idx = sourceIndex(node);
- if (idx < 0)
- return false;
-
- auto collections = m_collectionList.at(idx);
- if (collections.isNull())
- return false;
-
- return collections->contains(collectionName);
-}
-
-bool CollectionSourceModel::addCollectionToSource(const ModelNode &node,
- const QString &collectionName,
- const QJsonArray &newCollectionData,
- QString *errorString)
-{
- auto returnError = [errorString](const QString &msg) -> bool {
- if (errorString)
- *errorString = msg;
- return false;
- };
-
- int idx = sourceIndex(node);
- if (idx < 0)
- return returnError(tr("Node is not indexed in the models."));
-
- if (node.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME)
- return returnError(tr("Node should be a JSON model."));
-
- if (collectionExists(node, collectionName))
- return returnError(tr("A model with the identical name already exists."));
-
- QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(node);
-
- QFileInfo sourceFileInfo(sourceFileAddress);
- if (!sourceFileInfo.isFile())
- return returnError(tr("Selected node must have a valid source file address"));
-
- QFile jsonFile(sourceFileAddress);
- if (!jsonFile.open(QFile::ReadWrite))
- return returnError(tr("Can't read or write \"%1\".\n%2")
- .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString()));
-
- QJsonParseError parseError;
- QJsonDocument document = QJsonDocument::fromJson(jsonFile.readAll(), &parseError);
- if (parseError.error != QJsonParseError::NoError)
- return returnError(tr("\"%1\" is corrupted.\n%2")
- .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString()));
-
- if (document.isObject()) {
- QJsonObject sourceObject = document.object();
- sourceObject.insert(collectionName, newCollectionData);
- document.setObject(sourceObject);
- if (!jsonFile.resize(0))
- return returnError(tr("Can't clean \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
-
- QByteArray jsonData = document.toJson();
- auto writtenBytes = jsonFile.write(jsonData);
- jsonFile.close();
-
- if (writtenBytes != jsonData.size())
- return returnError(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
-
- updateCollectionList(index(idx));
-
- auto collections = m_collectionList.at(idx);
- if (collections.isNull())
- return returnError(tr("No model is available for the JSON model group."));
-
- collections->selectCollectionName(collectionName);
- setSelectedCollectionName(collectionName);
- return true;
- } else {
- return returnError(tr("JSON document type should be an object containing models."));
- }
-}
-
-QmlDesigner::ModelNode CollectionSourceModel::sourceNodeAt(int idx)
-{
- QModelIndex data = index(idx);
- if (!data.isValid())
- return {};
-
- return m_collectionSources.at(idx);
-}
-
-CollectionListModel *CollectionSourceModel::selectedCollectionList()
-{
- QModelIndex idx = index(m_selectedIndex);
- if (!idx.isValid())
- return {};
-
- return idx.data(CollectionsRole).value<CollectionListModel *>();
-}
-
-void CollectionSourceModel::selectSourceIndex(int idx, bool selectAtLeastOne)
-{
- int collectionCount = m_collectionSources.size();
- int preferredIndex = -1;
- if (collectionCount) {
- if (selectAtLeastOne)
- preferredIndex = std::max(0, std::min(idx, collectionCount - 1));
- else if (idx > -1 && idx < collectionCount)
- preferredIndex = idx;
- }
-
- setSelectedIndex(preferredIndex);
-}
-
-void CollectionSourceModel::deselect()
-{
- setSelectedIndex(-1);
-}
-
-void CollectionSourceModel::updateSelectedSource(bool selectAtLeastOne)
-{
- int idx = m_selectedIndex;
- m_selectedIndex = -1;
- selectSourceIndex(idx, selectAtLeastOne);
-}
-
-bool CollectionSourceModel::collectionExists(const QVariant &node, const QString &collectionName) const
-{
- return collectionExists(node.value<ModelNode>(), collectionName);
-}
-
-void CollectionSourceModel::updateNodeName(const ModelNode &node)
-{
- QModelIndex index = indexOfNode(node);
- emit dataChanged(index, index, {NameRole, Qt::DisplayRole});
- updateCollectionList(index);
-}
-
-void CollectionSourceModel::updateNodeSource(const ModelNode &node)
-{
- QModelIndex index = indexOfNode(node);
- emit dataChanged(index, index, {SourceRole});
- updateCollectionList(index);
-}
-
-void CollectionSourceModel::onSelectedCollectionChanged(CollectionListModel *collectionList,
- int collectionIndex)
-{
- if (collectionIndex > -1) {
- if (m_previousSelectedList && m_previousSelectedList != collectionList)
- m_previousSelectedList->selectCollectionIndex(-1);
-
- m_previousSelectedList = collectionList;
-
- setSelectedCollectionName(collectionList->collectionNameAt(collectionIndex));
-
- selectSourceIndex(sourceIndex(collectionList->sourceNode()));
- } else {
- setSelectedCollectionName({});
- }
-}
-
-void CollectionSourceModel::onCollectionNameChanged(CollectionListModel *collectionList,
- const QString &oldName, const QString &newName)
-{
- auto emitRenameWarning = [this](const QString &msg) -> void {
- emit warning(tr("Rename Model"), msg);
- };
-
- const ModelNode node = collectionList->sourceNode();
- const QModelIndex nodeIndex = indexOfNode(node);
-
- if (!nodeIndex.isValid()) {
- emitRenameWarning(tr("Invalid node"));
- return;
- }
-
- if (node.type() == CollectionEditorConstants::CSVCOLLECTIONMODEL_TYPENAME) {
- if (!setData(nodeIndex, newName, NameRole))
- emitRenameWarning(tr("Can't rename the node"));
- return;
- } else if (node.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) {
- emitRenameWarning(tr("Invalid node type"));
- return;
- }
-
- QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(node);
-
- QFileInfo sourceFileInfo(sourceFileAddress);
- if (!sourceFileInfo.isFile()) {
- emitRenameWarning(tr("Selected node must have a valid source file address"));
- return;
- }
-
- QFile jsonFile(sourceFileAddress);
- if (!jsonFile.open(QFile::ReadWrite)) {
- emitRenameWarning(tr("Can't read or write \"%1\".\n%2")
- .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString()));
- return;
- }
-
- QJsonParseError parseError;
- QJsonDocument document = QJsonDocument::fromJson(jsonFile.readAll(), &parseError);
- if (parseError.error != QJsonParseError::NoError) {
- emitRenameWarning(tr("\"%1\" is corrupted.\n%2")
- .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString()));
- return;
- }
-
- if (document.isObject()) {
- QJsonObject rootObject = document.object();
-
- bool collectionContainsOldName = rootObject.contains(oldName);
- bool collectionContainsNewName = rootObject.contains(newName);
-
- if (!collectionContainsOldName) {
- emitRenameWarning(
- tr("The model group doesn't contain the old model name (%1).").arg(oldName));
- return;
- }
-
- if (collectionContainsNewName) {
- emitRenameWarning(
- tr("The model name \"%1\" already exists in the model group.").arg(newName));
- return;
- }
-
- QJsonValue oldValue = rootObject.value(oldName);
- rootObject.insert(newName, oldValue);
- rootObject.remove(oldName);
-
- document.setObject(rootObject);
- if (!jsonFile.resize(0)) {
- emitRenameWarning(tr("Can't clean \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
- return;
- }
-
- QByteArray jsonData = document.toJson();
- auto writtenBytes = jsonFile.write(jsonData);
- jsonFile.close();
-
- if (writtenBytes != jsonData.size()) {
- emitRenameWarning(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
- return;
- }
-
- CollectionListModel *list = m_collectionList.at(nodeIndex.row()).data();
- bool updateSelectedNames = list && list == m_previousSelectedList.data();
- emit collectionRenamed(oldName, newName);
- updateCollectionList(nodeIndex);
-
- if (updateSelectedNames) {
- list = m_collectionList.at(nodeIndex.row()).data();
- if (m_selectedCollectionName == oldName) {
- list->selectCollectionName(newName);
- setSelectedCollectionName(newName);
- } else {
- // reselect to update ID if it's changed due to renaming and order changes
- list->selectCollectionName(m_selectedCollectionName);
- }
- }
- }
-}
-
-void CollectionSourceModel::onCollectionsRemoved(CollectionListModel *collectionList,
- const QStringList &removedCollections)
-{
- auto emitDeleteWarning = [this](const QString &msg) -> void {
- emit warning(tr("Delete Model"), msg);
- };
-
- const ModelNode node = collectionList->sourceNode();
- const QModelIndex nodeIndex = indexOfNode(node);
-
- if (!nodeIndex.isValid()) {
- emitDeleteWarning(tr("Invalid node"));
- return;
- }
-
- if (node.type() == CollectionEditorConstants::CSVCOLLECTIONMODEL_TYPENAME) {
- removeSource(node);
- return;
- } else if (node.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) {
- emitDeleteWarning(tr("Invalid node type"));
- return;
- }
-
- QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(node);
-
- QFileInfo sourceFileInfo(sourceFileAddress);
- if (!sourceFileInfo.isFile()) {
- emitDeleteWarning(tr("The selected node has an invalid source address"));
- return;
- }
-
- QFile jsonFile(sourceFileAddress);
- if (!jsonFile.open(QFile::ReadWrite)) {
- emitDeleteWarning(tr("Can't read or write \"%1\".\n%2")
- .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString()));
- return;
- }
-
- QJsonParseError parseError;
- QJsonDocument document = QJsonDocument::fromJson(jsonFile.readAll(), &parseError);
- if (parseError.error != QJsonParseError::NoError) {
- emitDeleteWarning(tr("\"%1\" is corrupted.\n%2")
- .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString()));
- return;
- }
-
- if (document.isObject()) {
- QJsonObject rootObject = document.object();
-
- QStringList collectionsRemovedFromDocument;
- for (const QString &collectionName : removedCollections) {
- bool sourceContainsCollection = rootObject.contains(collectionName);
- if (sourceContainsCollection) {
- rootObject.remove(collectionName);
- collectionsRemovedFromDocument << collectionName;
- } else {
- emitDeleteWarning(tr("The model group doesn't contain the model name (%1).")
- .arg(sourceContainsCollection));
- }
- }
-
- document.setObject(rootObject);
- if (!jsonFile.resize(0)) {
- emitDeleteWarning(tr("Can't clean \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
- return;
- }
-
- QByteArray jsonData = document.toJson();
- auto writtenBytes = jsonFile.write(jsonData);
- jsonFile.close();
-
- if (writtenBytes != jsonData.size()) {
- emitDeleteWarning(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
- return;
- }
-
- for (const QString &collectionName : std::as_const(collectionsRemovedFromDocument))
- emit collectionRemoved(collectionName);
-
- updateCollectionList(nodeIndex);
- if (m_previousSelectedList == collectionList)
- onSelectedCollectionChanged(collectionList, collectionList->selectedIndex());
- }
-}
-
-void CollectionSourceModel::setSelectedIndex(int idx)
-{
- idx = (idx > -1 && idx < m_collectionSources.count()) ? idx : -1;
-
- if (m_selectedIndex != idx) {
- QModelIndex previousIndex = index(m_selectedIndex);
- QModelIndex newIndex = index(idx);
-
- m_selectedIndex = idx;
-
- if (previousIndex.isValid())
- emit dataChanged(previousIndex, previousIndex, {SelectedRole});
-
- if (newIndex.isValid())
- emit dataChanged(newIndex, newIndex, {SelectedRole});
-
- emit selectedIndexChanged(idx);
-
- if (idx > -1) {
- QPointer<CollectionListModel> relatedCollectionList = m_collectionList.at(idx).data();
- if (relatedCollectionList) {
- if (relatedCollectionList->selectedIndex() < 0)
- relatedCollectionList->selectCollectionIndex(0, true);
- } else if (m_previousSelectedList) {
- m_previousSelectedList->selectCollectionIndex(-1);
- m_previousSelectedList = {};
- setSelectedCollectionName({});
- }
- }
- }
-}
-
-void CollectionSourceModel::setSelectedCollectionName(const QString &collectionName)
-{
- if (m_selectedCollectionName != collectionName) {
- m_selectedCollectionName = collectionName;
- emit collectionSelected(m_selectedCollectionName);
- }
-}
-
-void CollectionSourceModel::updateEmpty()
-{
- bool isEmptyNow = m_collectionSources.isEmpty();
- if (m_isEmpty != isEmptyNow) {
- m_isEmpty = isEmptyNow;
- emit isEmptyChanged(m_isEmpty);
-
- if (m_isEmpty)
- setSelectedIndex(-1);
- }
-}
-
-void CollectionSourceModel::updateCollectionList(QModelIndex index)
-{
- if (!index.isValid())
- return;
-
- ModelNode sourceNode = sourceNodeAt(index.row());
- QSharedPointer<CollectionListModel> oldList = m_collectionList.at(index.row());
- QSharedPointer<CollectionListModel> newList = loadCollection(sourceNode, oldList);
- if (oldList != newList) {
- m_collectionList.replace(index.row(), newList);
- emit dataChanged(index, index, {CollectionsRole});
- registerCollection(newList);
- }
-}
-
-void CollectionSourceModel::registerCollection(const QSharedPointer<CollectionListModel> &collection)
-{
- CollectionListModel *collectionList = collection.data();
- if (collectionList == nullptr)
- return;
-
- if (!collectionList->property("_is_registered_in_sourceModel").toBool()) {
- collectionList->setProperty("_is_registered_in_sourceModel", true);
-
- connect(collectionList,
- &CollectionListModel::selectedIndexChanged,
- this,
- [this, collectionList](int idx) { onSelectedCollectionChanged(collectionList, idx); });
-
- connect(collectionList,
- &CollectionListModel::collectionNameChanged,
- this,
- [this, collectionList](const QString &oldName, const QString &newName) {
- onCollectionNameChanged(collectionList, oldName, newName);
- });
-
- connect(collectionList,
- &CollectionListModel::collectionsRemoved,
- this,
- [this, collectionList](const QStringList &removedCollections) {
- onCollectionsRemoved(collectionList, removedCollections);
- });
- }
-
- if (collectionList->sourceNode().isValid())
- emit collectionNamesInitialized(collection->stringList());
-}
-
-QModelIndex CollectionSourceModel::indexOfNode(const ModelNode &node) const
-{
- return index(m_sourceIndexHash.value(node.internalId(), -1));
-}
-
-void CollectionJsonSourceFilterModel::registerDeclarativeType()
-{
- qmlRegisterType<CollectionJsonSourceFilterModel>("CollectionEditor",
- 1,
- 0,
- "CollectionJsonSourceFilterModel");
-}
-
-bool CollectionJsonSourceFilterModel::filterAcceptsRow(int source_row, const QModelIndex &) const
-{
- if (!sourceModel())
- return false;
- QModelIndex sourceItem = sourceModel()->index(source_row, 0, {});
- return sourceItem.data(CollectionSourceModel::Roles::CollectionTypeRole).toString() == "json";
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h
deleted file mode 100644
index 5ab77f2a98..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "modelnode.h"
-
-#include <QAbstractListModel>
-#include <QHash>
-#include <QSortFilterProxyModel>
-
-namespace QmlDesigner {
-
-class CollectionJsonSourceFilterModel;
-class CollectionListModel;
-
-class CollectionSourceModel : public QAbstractListModel
-{
- Q_OBJECT
-
- Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged)
- Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
-
-public:
- enum Roles {
- NameRole = Qt::UserRole + 1,
- NodeRole,
- CollectionTypeRole,
- SourceRole,
- SelectedRole,
- CollectionsRole
- };
-
- explicit CollectionSourceModel(QObject *parent = nullptr);
-
- virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
- virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
- virtual bool setData(const QModelIndex &index,
- const QVariant &value,
- int role = Qt::EditRole) override;
-
- Q_INVOKABLE virtual bool removeRows(int row,
- int count = 1,
- const QModelIndex &parent = QModelIndex()) override;
-
- virtual QHash<int, QByteArray> roleNames() const override;
-
- void setSources(const ModelNodes &sources);
- void removeSource(const ModelNode &node);
- int sourceIndex(const ModelNode &node) const;
- void addSource(const ModelNode &node);
- void selectSource(const ModelNode &node);
-
- bool collectionExists(const ModelNode &node, const QString &collectionName) const;
- bool addCollectionToSource(const ModelNode &node,
- const QString &collectionName,
- const QJsonArray &newCollectionData,
- QString *errorString = nullptr);
-
- ModelNode sourceNodeAt(int idx);
- CollectionListModel *selectedCollectionList();
-
- Q_INVOKABLE void selectSourceIndex(int idx, bool selectAtLeastOne = false);
- Q_INVOKABLE void deselect();
- Q_INVOKABLE void updateSelectedSource(bool selectAtLeastOne = false);
- Q_INVOKABLE bool collectionExists(const QVariant &node, const QString &collectionName) const;
-
- void updateNodeName(const ModelNode &node);
- void updateNodeSource(const ModelNode &node);
-
-signals:
- void selectedIndexChanged(int idx);
- void collectionSelected(const QString &collectionName);
- void collectionNamesInitialized(const QStringList &initialList);
- void collectionRenamed(const QString &oldname, const QString &newName);
- void collectionRemoved(const QString &collectionName);
-
- void isEmptyChanged(bool);
- void warning(const QString &title, const QString &body);
-
-private slots:
- void onSelectedCollectionChanged(CollectionListModel *collectionList, int collectionIndex);
- void onCollectionNameChanged(CollectionListModel *collectionList, const QString &oldName,
- const QString &newName);
- void onCollectionsRemoved(CollectionListModel *collectionList,
- const QStringList &removedCollections);
-
-private:
- void setSelectedIndex(int idx);
- void setSelectedCollectionName(const QString &collectionName);
- void updateEmpty();
- void updateCollectionList(QModelIndex index);
- void registerCollection(const QSharedPointer<CollectionListModel> &collection);
- QModelIndex indexOfNode(const ModelNode &node) const;
-
- using Super = QAbstractListModel;
-
- ModelNodes m_collectionSources;
- QHash<qint32, int> m_sourceIndexHash; // internalId -> index
- QList<QSharedPointer<CollectionListModel>> m_collectionList;
- QPointer<CollectionListModel> m_previousSelectedList;
- QString m_selectedCollectionName;
- int m_selectedIndex = -1;
- bool m_isEmpty = true;
-};
-
-class CollectionJsonSourceFilterModel : public QSortFilterProxyModel
-{
- Q_OBJECT
-
-public:
- static void registerDeclarativeType();
-
-protected:
- bool filterAcceptsRow(int source_row, const QModelIndex &) const override;
-};
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp
index 9a9084d8f0..b47fb6a51f 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp
@@ -3,37 +3,52 @@
#include "collectionview.h"
+#include "collectiondatatypemodel.h"
#include "collectiondetailsmodel.h"
#include "collectioneditorconstants.h"
#include "collectioneditorutils.h"
-#include "collectionsourcemodel.h"
+#include "collectionlistmodel.h"
#include "collectionwidget.h"
#include "datastoremodelnode.h"
#include "designmodecontext.h"
#include "nodeabstractproperty.h"
#include "nodemetainfo.h"
+#include "nodeproperty.h"
#include "qmldesignerplugin.h"
#include "variantproperty.h"
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectmanager.h>
-
-#include <QJsonArray>
-#include <QJsonDocument>
-#include <QJsonObject>
+#include <qmljs/qmljsmodelmanagerinterface.h>
#include <coreplugin/icore.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
+#include <QTimer>
+
namespace {
-inline bool isStudioCollectionModel(const QmlDesigner::ModelNode &node)
+bool isStudioCollectionModel(const QmlDesigner::ModelNode &node)
+{
+ return node.metaInfo().isQtQuickStudioUtilsJsonListModel();
+}
+
+inline void setVariantPropertyValue(const QmlDesigner::ModelNode &node,
+ const QmlDesigner::PropertyName &propertyName,
+ const QVariant &value)
+{
+ QmlDesigner::VariantProperty property = node.variantProperty(propertyName);
+ property.setValue(value);
+}
+
+inline void setBindingPropertyExpression(const QmlDesigner::ModelNode &node,
+ const QmlDesigner::PropertyName &propertyName,
+ const QString &expression)
{
- using namespace QmlDesigner::CollectionEditorConstants;
- return node.metaInfo().typeName() == JSONCOLLECTIONMODEL_TYPENAME
- || node.metaInfo().typeName() == CSVCOLLECTIONMODEL_TYPENAME;
+ QmlDesigner::BindingProperty property = node.bindingProperty(propertyName);
+ property.setExpression(expression);
}
} // namespace
@@ -45,15 +60,10 @@ CollectionView::CollectionView(ExternalDependenciesInterface &externalDependenci
, m_dataStore(std::make_unique<DataStoreModelNode>())
{
- connect(ProjectExplorer::ProjectManager::instance(),
- &ProjectExplorer::ProjectManager::startupProjectChanged, this, [this] {
- resetDataStoreNode();
- if (m_widget.get())
- m_widget->collectionDetailsModel()->removeAllCollections();
- });
- resetDataStoreNode();
}
+CollectionView::~CollectionView() = default;
+
bool CollectionView::hasWidget() const
{
return true;
@@ -61,35 +71,44 @@ bool CollectionView::hasWidget() const
QmlDesigner::WidgetInfo CollectionView::widgetInfo()
{
- if (m_widget.isNull()) {
- m_widget = new CollectionWidget(this);
+ if (!m_widget) {
+ m_widget = Utils::makeUniqueObjectPtr<CollectionWidget>(this);
m_widget->setMinimumSize(m_widget->minimumSizeHint());
+ connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged, m_widget.get(), [&] {
+ resetDataStoreNode();
+ m_widget->collectionDetailsModel()->removeAllCollections();
+ });
- auto collectionEditorContext = new Internal::CollectionEditorContext(m_widget.data());
+ auto collectionEditorContext = new Internal::CollectionEditorContext(m_widget.get());
Core::ICore::addContextObject(collectionEditorContext);
- CollectionSourceModel *sourceModel = m_widget->sourceModel().data();
+ CollectionListModel *listModel = m_widget->listModel().data();
- connect(sourceModel,
- &CollectionSourceModel::collectionSelected,
+ connect(listModel,
+ &CollectionListModel::selectedCollectionNameChanged,
this,
[this](const QString &collection) {
m_widget->collectionDetailsModel()->loadCollection(dataStoreNode(), collection);
});
- connect(sourceModel, &CollectionSourceModel::isEmptyChanged, this, [this](bool isEmpty) {
+ connect(listModel, &CollectionListModel::isEmptyChanged, this, [this](bool isEmpty) {
if (isEmpty)
m_widget->collectionDetailsModel()->loadCollection({}, {});
});
- connect(sourceModel,
- &CollectionSourceModel::collectionNamesInitialized,
+ connect(listModel, &CollectionListModel::modelReset, this, [this] {
+ CollectionListModel *listModel = m_widget->listModel().data();
+ if (listModel->sourceNode() == m_dataStore->modelNode())
+ m_dataStore->setCollectionNames(listModel->collections());
+ });
+
+ connect(listModel,
+ &CollectionListModel::collectionAdded,
this,
- [this](const QStringList &collectionNames) {
- m_dataStore->setCollectionNames(collectionNames);
- });
+ [this](const QString &collectionName) { m_dataStore->addCollection(collectionName); });
- connect(sourceModel,
- &CollectionSourceModel::collectionRenamed,
+ connect(listModel,
+ &CollectionListModel::collectionNameChanged,
this,
[this](const QString &oldName, const QString &newName) {
m_dataStore->renameCollection(oldName, newName);
@@ -98,21 +117,23 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo()
newName);
});
- connect(sourceModel,
- &CollectionSourceModel::collectionRemoved,
+ connect(listModel,
+ &CollectionListModel::collectionsRemoved,
this,
- [this](const QString &collectionName) {
- m_dataStore->removeCollection(collectionName);
- m_widget->collectionDetailsModel()->removeCollection(dataStoreNode(),
- collectionName);
+ [this](const QStringList &collectionNames) {
+ m_dataStore->removeCollections(collectionNames);
+ for (const QString &collectionName : collectionNames) {
+ m_widget->collectionDetailsModel()->removeCollection(dataStoreNode(),
+ collectionName);
+ }
});
}
- return createWidgetInfo(m_widget.data(),
+ return createWidgetInfo(m_widget.get(),
"CollectionEditor",
WidgetInfo::LeftPane,
0,
- tr("Model Editor"),
+ tr("Model Editor [beta]"),
tr("Model Editor view"));
}
@@ -122,51 +143,24 @@ void CollectionView::modelAttached(Model *model)
resetDataStoreNode();
}
-void CollectionView::nodeReparented(const ModelNode &node,
- [[maybe_unused]] const NodeAbstractProperty &newPropertyParent,
- [[maybe_unused]] const NodeAbstractProperty &oldPropertyParent,
- [[maybe_unused]] PropertyChangeFlags propertyChange)
-{
- if (!isStudioCollectionModel(node))
- return;
-
- refreshModel();
-
- m_widget->sourceModel()->selectSource(node);
-}
-
-void CollectionView::nodeAboutToBeRemoved(const ModelNode &removedNode)
-{
- // removing the model lib node
- if (isStudioCollectionModel(removedNode))
- m_widget->sourceModel()->removeSource(removedNode);
-}
-
-void CollectionView::nodeRemoved(const ModelNode &removedNode,
- [[maybe_unused]] const NodeAbstractProperty &parentProperty,
- [[maybe_unused]] PropertyChangeFlags propertyChange)
+void CollectionView::modelAboutToBeDetached([[maybe_unused]] Model *model)
{
- if (isStudioCollectionModel(removedNode))
- m_widget->sourceModel()->updateSelectedSource(true);
-}
-
-void CollectionView::variantPropertiesChanged(const QList<VariantProperty> &propertyList,
- [[maybe_unused]] PropertyChangeFlags propertyChange)
-{
- for (const VariantProperty &property : propertyList) {
- ModelNode node(property.parentModelNode());
- if (isStudioCollectionModel(node)) {
- if (property.name() == "objectName")
- m_widget->sourceModel()->updateNodeName(node);
- else if (property.name() == CollectionEditorConstants::SOURCEFILE_PROPERTY)
- m_widget->sourceModel()->updateNodeSource(node);
- }
- }
+ m_libraryInfoIsUpdated = false;
+ m_reloadCounter = 0;
+ m_rewriterAmended = false;
+ m_dataStoreTypeFound = false;
+ disconnect(m_documentUpdateConnection);
+ QTC_ASSERT(m_delayedTasks.isEmpty(), m_delayedTasks.clear());
+ if (m_widget)
+ m_widget->listModel()->setDataStoreNode();
}
void CollectionView::selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
[[maybe_unused]] const QList<ModelNode> &lastSelectedNodeList)
{
+ if (!m_widget)
+ return;
+
QList<ModelNode> selectedCollectionNodes = Utils::filtered(selectedNodeList,
&isStudioCollectionModel);
@@ -181,20 +175,27 @@ void CollectionView::selectedNodesChanged(const QList<ModelNode> &selectedNodeLi
}
m_widget->setTargetNodeSelected(singleSelectedHasModelProperty);
+}
- // More than one model is selected. So ignore them
- if (selectedCollectionNodes.size() > 1)
+void CollectionView::customNotification(const AbstractView *,
+ const QString &identifier,
+ const QList<ModelNode> &nodeList,
+ const QList<QVariant> &data)
+{
+ if (!m_widget)
return;
- if (selectedCollectionNodes.size() == 1) { // If exactly one model is selected
- m_widget->sourceModel()->selectSource(selectedCollectionNodes.first());
- return;
- }
+ if (identifier == QLatin1String("item_library_created_by_drop") && !nodeList.isEmpty())
+ onItemLibraryNodeCreated(nodeList.first());
+ else if (identifier == QLatin1String("open_collection_by_id") && !data.isEmpty())
+ m_widget->openCollection(collectionNameFromDataStoreChildren(data.first().toByteArray()));
+ else if (identifier == "delete_selected_collection")
+ m_widget->deleteSelectedCollection();
}
-void CollectionView::addResource(const QUrl &url, const QString &name, const QString &type)
+void CollectionView::addResource(const QUrl &url, const QString &name)
{
- executeInTransaction(Q_FUNC_INFO, [this, &url, &name, &type]() {
+ executeInTransaction(Q_FUNC_INFO, [this, &url, &name]() {
ensureStudioModelImport();
QString sourceAddress;
if (url.isLocalFile()) {
@@ -204,13 +205,14 @@ void CollectionView::addResource(const QUrl &url, const QString &name, const QSt
} else {
sourceAddress = url.toString();
}
-
- const NodeMetaInfo resourceMetaInfo = type.compare("json", Qt::CaseInsensitive) == 0
- ? jsonCollectionMetaInfo()
- : csvCollectionMetaInfo();
+#ifdef QDS_USE_PROJECTSTORAGE
+ ModelNode resourceNode = createModelNode("JsonListModel");
+#else
+ const NodeMetaInfo resourceMetaInfo = jsonCollectionMetaInfo();
ModelNode resourceNode = createModelNode(resourceMetaInfo.typeName(),
resourceMetaInfo.majorVersion(),
resourceMetaInfo.minorVersion());
+#endif
VariantProperty sourceProperty = resourceNode.variantProperty(
CollectionEditorConstants::SOURCEFILE_PROPERTY);
VariantProperty nameProperty = resourceNode.variantProperty("objectName");
@@ -221,22 +223,127 @@ void CollectionView::addResource(const QUrl &url, const QString &name, const QSt
});
}
+void CollectionView::assignCollectionToNode(const QString &collectionName, const ModelNode &node)
+{
+ if (!m_widget)
+ return;
+
+ using DataType = CollectionDetails::DataType;
+ executeInTransaction("CollectionView::assignCollectionToNode", [&]() {
+ m_dataStore->assignCollectionToNode(
+ this,
+ node,
+ collectionName,
+ [&](const QString &collectionName, const QString &columnName) -> bool {
+ const CollectionReference reference{dataStoreNode(), collectionName};
+ return m_widget->collectionDetailsModel()->collectionHasColumn(reference, columnName);
+ },
+ [&](const QString &collectionName) -> QString {
+ const CollectionReference reference{dataStoreNode(), collectionName};
+ return m_widget->collectionDetailsModel()->getFirstColumnName(reference);
+ });
+
+ // Create and assign a delegate to the list view item
+ if (node.metaInfo().isQtQuickListView()) {
+ CollectionDetails collection = m_widget->collectionDetailsModel()->upToDateConstCollection(
+ {dataStoreNode(), collectionName});
+
+ ModelNode rowItem(createModelNode("QtQuick.Row"));
+ ::setVariantPropertyValue(rowItem, "spacing", 5);
+
+ const int columnsCount = collection.columns();
+ for (int column = 0; column < columnsCount; ++column) {
+ const DataType dataType = collection.typeAt(column);
+ const QString columnName = collection.propertyAt(column);
+ ModelNode cellItem;
+ if (dataType == DataType::Color) {
+ cellItem = createModelNode("QtQuick.Rectangle");
+ ::setBindingPropertyExpression(cellItem, "color", columnName);
+ ::setVariantPropertyValue(cellItem, "height", 20);
+ } else {
+ cellItem = createModelNode("QtQuick.Text");
+ ::setBindingPropertyExpression(cellItem, "text", columnName);
+ }
+ ::setVariantPropertyValue(cellItem, "width", 100);
+ rowItem.defaultNodeAbstractProperty().reparentHere(cellItem);
+ }
+
+ NodeProperty delegateProperty = node.nodeProperty("delegate");
+ // Remove the old model node if is available
+ if (delegateProperty.modelNode())
+ delegateProperty.modelNode().destroy();
+
+ delegateProperty.setModelNode(rowItem);
+ }
+ });
+}
+
void CollectionView::assignCollectionToSelectedNode(const QString &collectionName)
{
QTC_ASSERT(dataStoreNode() && hasSingleSelectedModelNode(), return);
- m_dataStore->assignCollectionToNode(this, singleSelectedModelNode(), collectionName);
+ assignCollectionToNode(collectionName, singleSelectedModelNode());
+}
+
+void CollectionView::addNewCollection(const QString &collectionName, const QJsonObject &localCollection)
+{
+ if (!m_widget)
+ return;
+
+ addTask(QSharedPointer<CollectionTask>(
+ new AddCollectionTask(this, m_widget->listModel(), localCollection, collectionName)));
+}
+
+void CollectionView::openCollection(const QString &collectionName)
+{
+ if (!m_widget)
+ return;
+
+ m_widget->openCollection(collectionName);
}
void CollectionView::registerDeclarativeType()
{
CollectionDetails::registerDeclarativeType();
- CollectionJsonSourceFilterModel::registerDeclarativeType();
+ CollectionDataTypeModel::registerDeclarativeType();
}
void CollectionView::resetDataStoreNode()
{
+ if (!m_widget)
+ return;
+
m_dataStore->reloadModel();
- refreshModel();
+
+ ModelNode dataStore = m_dataStore->modelNode();
+ if (!dataStore || m_widget->listModel()->sourceNode() == dataStore)
+ return;
+
+ bool dataStoreSingletonFound = m_dataStoreTypeFound;
+ if (!dataStoreSingletonFound && rewriterView() && rewriterView()->isAttached()) {
+ const QList<QmlTypeData> types = rewriterView()->getQMLTypes();
+ for (const QmlTypeData &cppTypeData : types) {
+ if (cppTypeData.isSingleton && cppTypeData.typeName == "DataStore") {
+ dataStoreSingletonFound = true;
+ break;
+ }
+ }
+ if (!dataStoreSingletonFound && !m_rewriterAmended) {
+ rewriterView()->forceAmend();
+ m_rewriterAmended = true;
+ }
+ }
+
+ if (dataStoreSingletonFound) {
+ m_widget->listModel()->setDataStoreNode(dataStore);
+ m_dataStoreTypeFound = true;
+
+ while (!m_delayedTasks.isEmpty())
+ m_delayedTasks.takeFirst()->process();
+ } else if (++m_reloadCounter < 50) {
+ QTimer::singleShot(200, this, &CollectionView::resetDataStoreNode);
+ } else {
+ QTC_ASSERT(false, m_delayedTasks.clear());
+ }
}
ModelNode CollectionView::dataStoreNode() const
@@ -244,28 +351,49 @@ ModelNode CollectionView::dataStoreNode() const
return m_dataStore->modelNode();
}
-void CollectionView::refreshModel()
+void CollectionView::ensureDataStoreExists()
{
- if (!model())
- return;
-
- // Load Model Groups
- ModelNodes collectionSourceNodes;
-
- if (ModelNode dataStore = m_dataStore->modelNode())
- collectionSourceNodes << dataStore;
-
- m_widget->sourceModel()->setSources(collectionSourceNodes);
+ bool filesJustCreated = false;
+ bool filesExist = CollectionEditorUtils::ensureDataStoreExists(filesJustCreated);
+ if (filesExist) {
+ if (filesJustCreated) {
+ // Force code model reset to notice changes to existing module
+ auto modelManager = QmlJS::ModelManagerInterface::instance();
+ if (modelManager) {
+ m_libraryInfoIsUpdated = false;
+
+ m_expectedDocumentUpdates.clear();
+ m_expectedDocumentUpdates << CollectionEditorUtils::dataStoreQmlFilePath()
+ << CollectionEditorUtils::dataStoreJsonFilePath();
+
+ m_documentUpdateConnection = connect(modelManager,
+ &QmlJS::ModelManagerInterface::documentUpdated,
+ this,
+ &CollectionView::onDocumentUpdated);
+
+ modelManager->resetCodeModel();
+ }
+ resetDataStoreNode();
+ } else {
+ m_libraryInfoIsUpdated = true;
+ }
+ }
}
-NodeMetaInfo CollectionView::jsonCollectionMetaInfo() const
+QString CollectionView::collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const
{
- return model()->metaInfo(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME);
+ return dataStoreNode()
+ .nodeProperty(childPropertyName)
+ .modelNode()
+ .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)
+ .toVariantProperty()
+ .value()
+ .toString();
}
-NodeMetaInfo CollectionView::csvCollectionMetaInfo() const
+NodeMetaInfo CollectionView::jsonCollectionMetaInfo() const
{
- return model()->metaInfo(CollectionEditorConstants::CSVCOLLECTIONMODEL_TYPENAME);
+ return model()->metaInfo(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME);
}
void CollectionView::ensureStudioModelImport()
@@ -281,4 +409,80 @@ void CollectionView::ensureStudioModelImport()
});
}
+void CollectionView::onItemLibraryNodeCreated(const ModelNode &node)
+{
+ if (!m_widget)
+ return;
+
+ if (node.metaInfo().isQtQuickListView()) {
+ addTask(QSharedPointer<CollectionTask>(
+ new DropListViewTask(this, m_widget->listModel(), node)));
+ }
+}
+
+void CollectionView::onDocumentUpdated(const QSharedPointer<const QmlJS::Document> &doc)
+{
+ if (m_expectedDocumentUpdates.contains(doc->fileName()))
+ m_expectedDocumentUpdates.remove(doc->fileName());
+
+ if (m_expectedDocumentUpdates.isEmpty()) {
+ disconnect(m_documentUpdateConnection);
+ m_libraryInfoIsUpdated = true;
+ }
+}
+
+void CollectionView::addTask(QSharedPointer<CollectionTask> task)
+{
+ ensureDataStoreExists();
+ if (m_dataStoreTypeFound)
+ task->process();
+ else if (m_dataStore->modelNode())
+ m_delayedTasks << task;
+}
+
+CollectionTask::CollectionTask(CollectionView *view, CollectionListModel *listModel)
+ : m_collectionView(view)
+ , m_listModel(listModel)
+{}
+
+DropListViewTask::DropListViewTask(CollectionView *view,
+ CollectionListModel *listModel,
+ const ModelNode &node)
+ : CollectionTask(view, listModel)
+ , m_node(node)
+{}
+
+void DropListViewTask::process()
+{
+ AbstractView *view = m_node.view();
+ if (!m_node || !m_collectionView || !m_listModel || !view)
+ return;
+
+ const QString newCollectionName = m_listModel->getUniqueCollectionName("ListModel");
+ m_listModel->addCollection(newCollectionName, CollectionEditorUtils::defaultColorCollection());
+ m_collectionView->openCollection(newCollectionName);
+ m_collectionView->assignCollectionToNode(newCollectionName, m_node);
+}
+
+AddCollectionTask::AddCollectionTask(CollectionView *view,
+ CollectionListModel *listModel,
+ const QJsonObject &localJsonObject,
+ const QString &collectionName)
+ : CollectionTask(view, listModel)
+ , m_localJsonObject(localJsonObject)
+ , m_name(collectionName)
+{}
+
+void AddCollectionTask::process()
+{
+ if (!m_listModel)
+ return;
+
+ const QString newCollectionName = m_listModel->collectionExists(m_name)
+ ? m_listModel->getUniqueCollectionName(m_name)
+ : m_name;
+
+ m_listModel->addCollection(newCollectionName, m_localJsonObject);
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h
index c08368b0c3..d8be8b7055 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h
@@ -3,12 +3,24 @@
#pragma once
-#include "abstractview.h"
#include "datastoremodelnode.h"
-#include "modelnode.h"
+
+#include <abstractview.h>
+#include <modelnode.h>
+
+#include <utils/uniqueobjectptr.h>
+
+#include <QJsonObject>
+
+namespace QmlJS {
+class Document;
+}
namespace QmlDesigner {
+class CollectionDetails;
+class CollectionListModel;
+class CollectionTask;
class CollectionWidget;
class DataStoreModelNode;
@@ -18,45 +30,95 @@ class CollectionView : public AbstractView
public:
explicit CollectionView(ExternalDependenciesInterface &externalDependencies);
+ ~CollectionView();
bool hasWidget() const override;
WidgetInfo widgetInfo() override;
void modelAttached(Model *model) override;
-
- void nodeReparented(const ModelNode &node,
- const NodeAbstractProperty &newPropertyParent,
- const NodeAbstractProperty &oldPropertyParent,
- PropertyChangeFlags propertyChange) override;
-
- void nodeAboutToBeRemoved(const ModelNode &removedNode) override;
-
- void nodeRemoved(const ModelNode &removedNode,
- const NodeAbstractProperty &parentProperty,
- PropertyChangeFlags propertyChange) override;
-
- void variantPropertiesChanged(const QList<VariantProperty> &propertyList,
- PropertyChangeFlags propertyChange) override;
+ void modelAboutToBeDetached(Model *model) override;
void selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
const QList<ModelNode> &lastSelectedNodeList) override;
- void addResource(const QUrl &url, const QString &name, const QString &type);
+ void customNotification(const AbstractView *view,
+ const QString &identifier,
+ const QList<ModelNode> &nodeList,
+ const QList<QVariant> &data) override;
+ void addResource(const QUrl &url, const QString &name);
+
+ void assignCollectionToNode(const QString &collectionName, const ModelNode &node);
void assignCollectionToSelectedNode(const QString &collectionName);
+ void addNewCollection(const QString &collectionName, const QJsonObject &localCollection);
+
+ void openCollection(const QString &collectionName);
static void registerDeclarativeType();
void resetDataStoreNode();
ModelNode dataStoreNode() const;
+ void ensureDataStoreExists();
+ QString collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const;
private:
- void refreshModel();
+ friend class CollectionTask;
+
NodeMetaInfo jsonCollectionMetaInfo() const;
- NodeMetaInfo csvCollectionMetaInfo() const;
void ensureStudioModelImport();
+ void onItemLibraryNodeCreated(const ModelNode &node);
+ void onDocumentUpdated(const QSharedPointer<const QmlJS::Document> &doc);
+ void addTask(QSharedPointer<CollectionTask> task);
- QPointer<CollectionWidget> m_widget;
std::unique_ptr<DataStoreModelNode> m_dataStore;
+ Utils::UniqueObjectPtr<CollectionWidget> m_widget;
+ QSet<Utils::FilePath> m_expectedDocumentUpdates;
+ QList<QSharedPointer<CollectionTask>> m_delayedTasks;
+ QMetaObject::Connection m_documentUpdateConnection;
+ bool m_libraryInfoIsUpdated = false;
+ bool m_dataStoreTypeFound = false;
+ bool m_rewriterAmended = false;
+ int m_reloadCounter = 0;
+};
+
+class CollectionTask
+{
+public:
+ CollectionTask(CollectionView *view, CollectionListModel *listModel);
+ CollectionTask() = delete;
+ virtual ~CollectionTask() = default;
+
+ virtual void process() = 0;
+
+protected:
+ QPointer<CollectionView> m_collectionView;
+ QPointer<CollectionListModel> m_listModel;
+};
+
+class DropListViewTask : public CollectionTask
+{
+public:
+ DropListViewTask(CollectionView *view, CollectionListModel *listModel, const ModelNode &node);
+
+ void process() override;
+
+private:
+ ModelNode m_node;
};
+
+class AddCollectionTask : public CollectionTask
+{
+public:
+ AddCollectionTask(CollectionView *view,
+ CollectionListModel *listModel,
+ const QJsonObject &localJsonObject,
+ const QString &collectionName);
+
+ void process() override;
+
+private:
+ QJsonObject m_localJsonObject;
+ QString m_name;
+};
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp
index a123cf3361..7ecd54174a 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp
@@ -3,11 +3,13 @@
#include "collectionwidget.h"
+#include "collectiondetails.h"
#include "collectiondetailsmodel.h"
#include "collectiondetailssortfiltermodel.h"
#include "collectioneditorutils.h"
-#include "collectionsourcemodel.h"
+#include "collectionlistmodel.h"
#include "collectionview.h"
+#include "designmodewidget.h"
#include "qmldesignerconstants.h"
#include "qmldesignerplugin.h"
#include "theme.h"
@@ -16,7 +18,6 @@
#include <coreplugin/messagebox.h>
#include <studioquickwidget.h>
-#include <QFile>
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument>
@@ -53,9 +54,8 @@ QString getPreferredCollectionName(const QUrl &url, const QString &collectionNam
namespace QmlDesigner {
CollectionWidget::CollectionWidget(CollectionView *view)
- : QFrame()
- , m_view(view)
- , m_sourceModel(new CollectionSourceModel)
+ : m_view(view)
+ , m_listModel(new CollectionListModel)
, m_collectionDetailsModel(new CollectionDetailsModel)
, m_collectionDetailsSortFilterModel(std::make_unique<CollectionDetailsSortFilterModel>())
, m_quickWidget(new StudioQuickWidget(this))
@@ -63,12 +63,12 @@ CollectionWidget::CollectionWidget(CollectionView *view)
setWindowTitle(tr("Model Editor", "Title of model editor widget"));
Core::IContext *icontext = nullptr;
- Core::Context context(Constants::C_QMLMATERIALBROWSER);
+ Core::Context context(Constants::C_QMLCOLLECTIONEDITOR);
icontext = new Core::IContext(this);
icontext->setContext(context);
icontext->setWidget(this);
- connect(m_sourceModel, &CollectionSourceModel::warning, this, &CollectionWidget::warn);
+ connect(m_listModel, &CollectionListModel::warning, this, &CollectionWidget::warn);
m_collectionDetailsSortFilterModel->setSourceModel(m_collectionDetailsModel);
@@ -89,7 +89,7 @@ CollectionWidget::CollectionWidget(CollectionView *view)
auto map = m_quickWidget->registerPropertyMap("CollectionEditorBackend");
map->setProperties({
{"rootView", QVariant::fromValue(this)},
- {"model", QVariant::fromValue(m_sourceModel.data())},
+ {"model", QVariant::fromValue(m_listModel.data())},
{"collectionDetailsModel", QVariant::fromValue(m_collectionDetailsModel.data())},
{"collectionDetailsSortFilterModel",
QVariant::fromValue(m_collectionDetailsSortFilterModel.get())},
@@ -103,6 +103,8 @@ CollectionWidget::CollectionWidget(CollectionView *view)
QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_MODELEDITOR_TIME);
}
+CollectionWidget::~CollectionWidget() = default;
+
void CollectionWidget::contextHelp(const Core::IContext::HelpCallback &callback) const
{
if (m_view)
@@ -111,9 +113,9 @@ void CollectionWidget::contextHelp(const Core::IContext::HelpCallback &callback)
callback({});
}
-QPointer<CollectionSourceModel> CollectionWidget::sourceModel() const
+QPointer<CollectionListModel> CollectionWidget::listModel() const
{
- return m_sourceModel;
+ return m_listModel;
}
QPointer<CollectionDetailsModel> CollectionWidget::collectionDetailsModel() const
@@ -152,28 +154,28 @@ bool CollectionWidget::loadJsonFile(const QUrl &url, const QString &collectionNa
if (!isJsonFile(url))
return false;
- m_view->addResource(url, getPreferredCollectionName(url, collectionName), "json");
+ m_view->addResource(url, getPreferredCollectionName(url, collectionName));
return true;
}
bool CollectionWidget::loadCsvFile(const QUrl &url, const QString &collectionName)
{
- m_view->addResource(url, getPreferredCollectionName(url, collectionName), "csv");
+ m_view->addResource(url, getPreferredCollectionName(url, collectionName));
return true;
}
bool CollectionWidget::isJsonFile(const QUrl &url) const
{
- QString filePath = url.isLocalFile() ? url.toLocalFile() : url.toString();
- QFile file(filePath);
-
- if (!file.exists() || !file.open(QFile::ReadOnly))
+ Utils::FilePath filePath = Utils::FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile()
+ : url.toString());
+ Utils::FileReader file;
+ if (!file.fetch(filePath))
return false;
QJsonParseError error;
- QJsonDocument::fromJson(file.readAll(), &error);
+ QJsonDocument::fromJson(file.data(), &error);
if (error.error)
return false;
@@ -183,9 +185,8 @@ bool CollectionWidget::isJsonFile(const QUrl &url) const
bool CollectionWidget::isCsvFile(const QUrl &url) const
{
QString filePath = url.isLocalFile() ? url.toLocalFile() : url.toString();
- QFile file(filePath);
-
- return file.exists() && file.fileName().endsWith(".csv");
+ QFileInfo fileInfo(filePath);
+ return fileInfo.exists() && !fileInfo.suffix().compare("csv", Qt::CaseInsensitive);
}
bool CollectionWidget::isValidUrlToImport(const QUrl &url) const
@@ -202,134 +203,57 @@ bool CollectionWidget::isValidUrlToImport(const QUrl &url) const
return false;
}
-bool CollectionWidget::addCollection(const QString &collectionName,
- const QString &collectionType,
- const QUrl &sourceUrl,
- const QVariant &sourceNode)
+bool CollectionWidget::importFile(const QString &collectionName,
+ const QUrl &url,
+ const bool &firstRowIsHeader)
{
- const ModelNode node = sourceNode.value<ModelNode>();
- bool isNewCollection = !node.isValid();
-
- if (isNewCollection) {
- QString sourcePath = sourceUrl.isLocalFile() ? sourceUrl.toLocalFile() : sourceUrl.toString();
-
- if (collectionType == "json") {
- QJsonObject jsonObject;
- jsonObject.insert(collectionName, CollectionEditorUtils::defaultCollectionArray());
-
- QFile sourceFile(sourcePath);
- if (!sourceFile.open(QFile::WriteOnly)) {
- warn(tr("File error"),
- tr("Can not open the file to write.\n") + sourceFile.errorString());
- return false;
- }
-
- sourceFile.write(QJsonDocument(jsonObject).toJson());
- sourceFile.close();
-
- bool loaded = loadJsonFile(sourcePath, collectionName);
- if (!loaded)
- sourceFile.remove();
-
- return loaded;
- } else if (collectionType == "csv") {
- QFile sourceFile(sourcePath);
- if (!sourceFile.open(QFile::WriteOnly)) {
- warn(tr("File error"),
- tr("Can not open the file to write.\n") + sourceFile.errorString());
- return false;
- }
-
- sourceFile.write("Column1\n\n");
- sourceFile.close();
-
- bool loaded = loadCsvFile(sourcePath, collectionName);
- if (!loaded)
- sourceFile.remove();
-
- return loaded;
- } else if (collectionType == "existing") {
- QFileInfo fileInfo(sourcePath);
- if (fileInfo.suffix() == "json")
- return loadJsonFile(sourcePath, collectionName);
- else if (fileInfo.suffix() == "csv")
- return loadCsvFile(sourcePath, collectionName);
- }
- } else if (collectionType == "json") {
- QString errorMsg;
- bool added = m_sourceModel->addCollectionToSource(
- node, collectionName, CollectionEditorUtils::defaultCollectionArray(), &errorMsg);
- if (!added)
- warn(tr("Can not add a model to the JSON file"), errorMsg);
- return added;
- }
-
- return false;
-}
-
-bool CollectionWidget::importToJson(const QVariant &sourceNode,
- const QString &collectionName,
- const QUrl &url)
-{
- using CollectionEditorConstants::SourceFormat;
using Utils::FilePath;
- const ModelNode node = sourceNode.value<ModelNode>();
- const SourceFormat nodeFormat = CollectionEditorUtils::getSourceCollectionFormat(node);
- QTC_ASSERT(node.isValid() && nodeFormat == SourceFormat::Json, return false);
FilePath fileInfo = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile()
: url.toString());
- bool added = false;
- QString errorMsg;
- QJsonArray loadedCollection;
-
- if (fileInfo.suffix() == "json")
- loadedCollection = CollectionEditorUtils::loadAsSingleJsonCollection(url);
- else if (fileInfo.suffix() == "csv")
- loadedCollection = CollectionEditorUtils::loadAsCsvCollection(url);
+ CollectionDetails loadedCollection;
+ QByteArray fileContent;
+
+ auto loadUrlContent = [&]() -> bool {
+ Utils::FileReader file;
+ if (file.fetch(fileInfo)) {
+ fileContent = file.data();
+ return true;
+ }
- if (!loadedCollection.isEmpty()) {
- const QString newCollectionName = generateUniqueCollectionName(node, collectionName);
- added = m_sourceModel->addCollectionToSource(node, newCollectionName, loadedCollection, &errorMsg);
- } else {
- errorMsg = tr("The imported model is empty or is not supported.");
- }
+ warn(tr("Import from file"), tr("Cannot import from file \"%1\"").arg(fileInfo.fileName()));
+ return false;
+ };
- if (!added)
- warn(tr("Can not add a model to the JSON file"), errorMsg);
- return added;
-}
+ if (fileInfo.suffix() == "json") {
+ if (!loadUrlContent())
+ return false;
-bool CollectionWidget::importCollectionToDataStore(const QString &collectionName, const QUrl &url)
-{
- using Utils::FilePath;
- const ModelNode node = dataStoreNode();
- if (node.isValid())
- return importToJson(QVariant::fromValue(node), collectionName, url);
+ QJsonParseError parseError;
+ loadedCollection = CollectionDetails::fromImportedJson(fileContent, &parseError);
+ if (parseError.error != QJsonParseError::NoError) {
+ warn(tr("Json file Import error"),
+ tr("Cannot parse json content\n%1").arg(parseError.errorString()));
+ }
+ } else if (fileInfo.suffix() == "csv") {
+ if (!loadUrlContent())
+ return false;
+ loadedCollection = CollectionDetails::fromImportedCsv(fileContent, firstRowIsHeader);
+ }
- warn(tr("Can not import to the main model"), tr("The data store is not available."));
+ if (loadedCollection.columns()) {
+ m_view->addNewCollection(collectionName, loadedCollection.toLocalJson());
+ return true;
+ } else {
+ warn(tr("Can not add a model to the JSON file"),
+ tr("The imported model is empty or is not supported."));
+ }
return false;
}
-bool CollectionWidget::addCollectionToDataStore(const QString &collectionName)
+void CollectionWidget::addCollectionToDataStore(const QString &collectionName)
{
- ensureDataStoreExists();
- const ModelNode node = dataStoreNode();
- if (!node.isValid()) {
- warn(tr("Can not import to the main model"), tr("The default model node is not available."));
- return false;
- }
-
- QString errorMsg;
- bool added = m_sourceModel->addCollectionToSource(node,
- generateUniqueCollectionName(node,
- collectionName),
- CollectionEditorUtils::defaultCollectionArray(),
- &errorMsg);
- if (!added)
- warn(tr("Failed to add a model to the default model group"), errorMsg);
-
- return added;
+ m_view->addNewCollection(collectionName, CollectionEditorUtils::defaultCollection());
}
void CollectionWidget::assignCollectionToSelectedNode(const QString collectionName)
@@ -337,12 +261,10 @@ void CollectionWidget::assignCollectionToSelectedNode(const QString collectionNa
m_view->assignCollectionToSelectedNode(collectionName);
}
-void CollectionWidget::ensureDataStoreExists()
+void CollectionWidget::openCollection(const QString &collectionName)
{
- bool filesJustCreated = false;
- bool filesExist = CollectionEditorUtils::ensureDataStoreExists(filesJustCreated);
- if (filesExist && filesJustCreated)
- m_view->resetDataStoreNode();
+ m_listModel->selectCollectionName(collectionName);
+ QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("CollectionEditor", true);
}
ModelNode CollectionWidget::dataStoreNode() const
@@ -367,20 +289,9 @@ void CollectionWidget::setTargetNodeSelected(bool selected)
emit targetNodeSelectedChanged(m_targetNodeSelected);
}
-QString CollectionWidget::generateUniqueCollectionName(const ModelNode &node, const QString &name)
+void CollectionWidget::deleteSelectedCollection()
{
- if (!m_sourceModel->collectionExists(node, name))
- return name;
-
- static QRegularExpression reg("^(?<mainName>[\\w\\d\\.\\_\\-]+)\\_(?<number>\\d+)$");
- QRegularExpressionMatch match = reg.match(name);
- if (match.hasMatch()) {
- int nextNumber = match.captured("number").toInt() + 1;
- return generateUniqueCollectionName(
- node, QString("%1_%2").arg(match.captured("mainName")).arg(nextNumber));
- } else {
- return generateUniqueCollectionName(node, QString("%1_1").arg(name));
- }
+ QMetaObject::invokeMethod(m_quickWidget->quickWidget()->rootObject(), "deleteSelectedCollection");
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h
index 2be98df190..f06edd2323 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h
@@ -13,7 +13,7 @@ namespace QmlDesigner {
class CollectionDetailsModel;
class CollectionDetailsSortFilterModel;
-class CollectionSourceModel;
+class CollectionListModel;
class CollectionView;
class ModelNode;
@@ -25,9 +25,10 @@ class CollectionWidget : public QFrame
public:
CollectionWidget(CollectionView *view);
+ ~CollectionWidget();
void contextHelp(const Core::IContext::HelpCallback &callback) const;
- QPointer<CollectionSourceModel> sourceModel() const;
+ QPointer<CollectionListModel> listModel() const;
QPointer<CollectionDetailsModel> collectionDetailsModel() const;
void reloadQmlSource();
@@ -39,28 +40,21 @@ public:
Q_INVOKABLE bool isJsonFile(const QUrl &url) const;
Q_INVOKABLE bool isCsvFile(const QUrl &url) const;
Q_INVOKABLE bool isValidUrlToImport(const QUrl &url) const;
- Q_INVOKABLE bool addCollection(const QString &collectionName,
- const QString &collectionType,
- const QUrl &sourceUrl,
- const QVariant &sourceNode);
- Q_INVOKABLE bool importToJson(const QVariant &sourceNode,
- const QString &collectionName,
- const QUrl &url);
-
- Q_INVOKABLE bool importCollectionToDataStore(const QString &collectionName, const QUrl &url);
-
- Q_INVOKABLE bool addCollectionToDataStore(const QString &collectionName);
+ Q_INVOKABLE bool importFile(const QString &collectionName,
+ const QUrl &url,
+ const bool &firstRowIsHeader = true);
+ Q_INVOKABLE void addCollectionToDataStore(const QString &collectionName);
Q_INVOKABLE void assignCollectionToSelectedNode(const QString collectionName);
-
- Q_INVOKABLE void ensureDataStoreExists();
-
+ Q_INVOKABLE void openCollection(const QString &collectionName);
Q_INVOKABLE ModelNode dataStoreNode() const;
void warn(const QString &title, const QString &body);
void setTargetNodeSelected(bool selected);
+ void deleteSelectedCollection();
+
signals:
void targetNodeSelectedChanged(bool);
@@ -68,7 +62,7 @@ private:
QString generateUniqueCollectionName(const ModelNode &node, const QString &name);
QPointer<CollectionView> m_view;
- QPointer<CollectionSourceModel> m_sourceModel;
+ QPointer<CollectionListModel> m_listModel;
QPointer<CollectionDetailsModel> m_collectionDetailsModel;
std::unique_ptr<CollectionDetailsSortFilterModel> m_collectionDetailsSortFilterModel;
QScopedPointer<StudioQuickWidget> m_quickWidget;
diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp
index a1f82bbc65..5be9c20f9e 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp
+++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp
@@ -20,6 +20,7 @@
#include <qmljstools/qmljscodestylepreferences.h>
#include <qmljstools/qmljstoolssettings.h>
+#include <coreplugin/documentmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectmanager.h>
@@ -144,6 +145,12 @@ void DataStoreModelNode::reloadModel()
if (dataStoreQmlPath.exists() && dataStoreJsonPath.exists()) {
if (!m_model.get() || m_model->fileUrl() != dataStoreQmlUrl) {
+#ifdef QDS_USE_PROJECTSTORAGE
+ m_model = model()->createModel("JsonListModel");
+ forceUpdate = true;
+ Import import = Import::createLibraryImport("QtQuick.Studio.Utils");
+ m_model->changeImports({import}, {});
+#else
m_model = Model::create(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME, 1, 1);
forceUpdate = true;
Import import = Import::createLibraryImport(
@@ -154,17 +161,18 @@ void DataStoreModelNode::reloadModel()
} catch (const Exception &) {
QTC_ASSERT(false, return);
}
+#endif
}
} else {
reset();
}
- QTC_ASSERT(m_model.get(), return);
- m_model->setFileUrl(dataStoreQmlUrl);
-
- m_dataRelativePath = dataStoreJsonPath.relativePathFrom(dataStoreQmlPath).toFSPathString();
+ if (!m_model.get())
+ return;
if (forceUpdate) {
+ m_model->setFileUrl(dataStoreQmlUrl);
+ m_dataRelativePath = dataStoreJsonPath.relativePathFrom(dataStoreQmlPath).toFSPathString();
preloadFile();
update();
}
@@ -182,7 +190,8 @@ Model *DataStoreModelNode::model() const
ModelNode DataStoreModelNode::modelNode() const
{
- QTC_ASSERT(m_model.get(), return {});
+ if (!m_model.get())
+ return {};
return m_model->rootModelNode();
}
@@ -298,6 +307,7 @@ void DataStoreModelNode::updateSingletonFile()
imports += QStringLiteral("import %1\n").arg(import.toString(true));
QString content = pragmaSingleTone + imports + getModelQmlText();
+ Core::DocumentManager::expectFileChange(dataStoreQmlFilePath());
FileSaver file(dataStoreQmlFilePath());
file.write(content.toLatin1());
file.finalize();
@@ -387,6 +397,14 @@ void DataStoreModelNode::setCollectionNames(const QStringList &newCollectionName
update();
}
+void DataStoreModelNode::addCollection(const QString &collectionName)
+{
+ if (!m_collectionPropertyNames.contains(collectionName)) {
+ m_collectionPropertyNames.insert(collectionName, {});
+ update();
+ }
+}
+
void DataStoreModelNode::renameCollection(const QString &oldName, const QString &newName)
{
ModelNode dataStoreNode = modelNode();
@@ -417,17 +435,25 @@ void DataStoreModelNode::renameCollection(const QString &oldName, const QString
<< QString("There is no old collection name registered with this name \"%1\"").arg(oldName);
}
-void DataStoreModelNode::removeCollection(const QString &collectionName)
+void DataStoreModelNode::removeCollections(const QStringList &collectionNames)
{
- if (m_collectionPropertyNames.contains(collectionName)) {
- m_collectionPropertyNames.remove(collectionName);
- update();
+ bool updateRequired = false;
+ for (const QString &collectionName : collectionNames) {
+ if (m_collectionPropertyNames.contains(collectionName)) {
+ m_collectionPropertyNames.remove(collectionName);
+ updateRequired = true;
+ }
}
+
+ if (updateRequired)
+ update();
}
void DataStoreModelNode::assignCollectionToNode(AbstractView *view,
const ModelNode &targetNode,
- const QString &collectionName)
+ const QString &collectionName,
+ CollectionColumnFinder collectionHasColumn,
+ FirstColumnProvider firstColumnProvider)
{
QTC_ASSERT(targetNode.isValid(), return);
@@ -452,8 +478,16 @@ void DataStoreModelNode::assignCollectionToNode(AbstractView *view,
view->executeInTransaction("assignCollectionToNode", [&]() {
QString identifier = QString("DataStore.%1").arg(QString::fromLatin1(sourceProperty.name()));
+
+ // Remove the old model node property if exists
+ NodeProperty modelNodeProperty = targetNode.nodeProperty("model");
+ if (modelNodeProperty.modelNode())
+ modelNodeProperty.modelNode().destroy();
+
+ // Assign the collection to the node
BindingProperty modelProperty = targetNode.bindingProperty("model");
modelProperty.setExpression(identifier);
+
if (CollectionEditorUtils::hasTextRoleProperty(targetNode)) {
VariantProperty textRoleProperty = targetNode.variantProperty("textRole");
const QVariant currentTextRoleValue = textRoleProperty.value();
@@ -461,14 +495,14 @@ void DataStoreModelNode::assignCollectionToNode(AbstractView *view,
if (currentTextRoleValue.isValid() && !currentTextRoleValue.isNull()) {
if (currentTextRoleValue.type() == QVariant::String) {
const QString currentTextRole = currentTextRoleValue.toString();
- if (CollectionEditorUtils::collectionHasColumn(collectionName, currentTextRole))
+ if (collectionHasColumn(collectionName, currentTextRole))
return;
} else {
return;
}
}
- QString textRoleValue = CollectionEditorUtils::getFirstColumnName(collectionName);
+ QString textRoleValue = firstColumnProvider(collectionName);
textRoleProperty.setValue(textRoleValue);
}
});
diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h
index 1c855bca7a..6cd969edbe 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h
+++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h
@@ -18,6 +18,10 @@ class Model;
class DataStoreModelNode
{
public:
+ using CollectionColumnFinder = std::function<bool(const QString &collectionName,
+ const QString &columnName)>;
+ using FirstColumnProvider = std::function<QString(const QString &collectionName)>;
+
DataStoreModelNode();
void reloadModel();
@@ -27,12 +31,15 @@ public:
ModelNode modelNode() const;
void setCollectionNames(const QStringList &newCollectionNames);
+ void addCollection(const QString &collectionName);
void renameCollection(const QString &oldName, const QString &newName);
- void removeCollection(const QString &collectionName);
+ void removeCollections(const QStringList &collectionNames);
void assignCollectionToNode(AbstractView *view,
const ModelNode &targetNode,
- const QString &collectionName);
+ const QString &collectionName,
+ CollectionColumnFinder collectionHasColumn,
+ FirstColumnProvider firstColumnProvider);
private:
QString getModelQmlText();
diff --git a/src/plugins/qmldesigner/components/colortool/colortool.cpp b/src/plugins/qmldesigner/components/colortool/colortool.cpp
index 7c7cca6213..d7695fc6f6 100644
--- a/src/plugins/qmldesigner/components/colortool/colortool.cpp
+++ b/src/plugins/qmldesigner/components/colortool/colortool.cpp
@@ -27,16 +27,13 @@
namespace QmlDesigner {
-ColorTool::ColorTool()
-{
-}
+ColorTool::ColorTool() = default;
ColorTool::~ColorTool() = default;
void ColorTool::clear()
{
- if (m_colorDialog)
- m_colorDialog.data()->deleteLater();
+ m_colorDialog.reset();
AbstractFormEditorTool::clear();
}
@@ -87,7 +84,7 @@ void ColorTool::mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList, QGr
void ColorTool::itemsAboutToRemoved(const QList<FormEditorItem*> &removedItemList)
{
- if (m_colorDialog.isNull())
+ if (!m_colorDialog)
return;
if (removedItemList.contains(m_formEditorItem))
@@ -96,7 +93,7 @@ void ColorTool::itemsAboutToRemoved(const QList<FormEditorItem*> &removedItemLis
void ColorTool::selectedItemsChanged(const QList<FormEditorItem*> &itemList)
{
- if (m_colorDialog.data() && m_oldColor.isValid())
+ if (m_colorDialog && m_oldColor.isValid())
m_formEditorItem->qmlItemNode().setVariantProperty("color", m_oldColor);
if (!itemList.isEmpty()
@@ -108,15 +105,19 @@ void ColorTool::selectedItemsChanged(const QList<FormEditorItem*> &itemList)
else
m_oldColor = m_formEditorItem->qmlItemNode().modelValue("color").value<QColor>();
- if (m_colorDialog.isNull()) {
- m_colorDialog = new QColorDialog(view()->formEditorWidget()->parentWidget());
- m_colorDialog.data()->setCurrentColor(m_oldColor);
+ if (!m_colorDialog) {
+ m_colorDialog = Utils::makeUniqueObjectLatePtr<QColorDialog>(
+ view()->formEditorWidget()->parentWidget());
+ m_colorDialog->setCurrentColor(m_oldColor);
- connect(m_colorDialog.data(), &QDialog::accepted, this, &ColorTool::colorDialogAccepted);
- connect(m_colorDialog.data(), &QDialog::rejected, this, &ColorTool::colorDialogRejected);
- connect(m_colorDialog.data(), &QColorDialog::currentColorChanged, this, &ColorTool::currentColorChanged);
+ connect(m_colorDialog.get(), &QDialog::accepted, this, &ColorTool::colorDialogAccepted);
+ connect(m_colorDialog.get(), &QDialog::rejected, this, &ColorTool::colorDialogRejected);
+ connect(m_colorDialog.get(),
+ &QColorDialog::currentColorChanged,
+ this,
+ &ColorTool::currentColorChanged);
- m_colorDialog.data()->exec();
+ m_colorDialog->exec();
}
} else {
view()->changeToSelectionTool();
diff --git a/src/plugins/qmldesigner/components/colortool/colortool.h b/src/plugins/qmldesigner/components/colortool/colortool.h
index ff0b356c06..966bf85688 100644
--- a/src/plugins/qmldesigner/components/colortool/colortool.h
+++ b/src/plugins/qmldesigner/components/colortool/colortool.h
@@ -6,6 +6,8 @@
#include "abstractcustomtool.h"
#include "selectionindicator.h"
+#include <utils/uniqueobjectptr.h>
+
#include <QHash>
#include <QPointer>
#include <QColorDialog>
@@ -59,7 +61,7 @@ private:
void currentColorChanged(const QColor &color);
private:
- QPointer<QColorDialog> m_colorDialog;
+ Utils::UniqueObjectLatePtr<QColorDialog> m_colorDialog;
FormEditorItem *m_formEditorItem = nullptr;
QColor m_oldColor;
QString m_oldExpression;
diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
index 17ba5aa970..d992a6a5bf 100644
--- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
+++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
@@ -69,6 +69,7 @@ const char mergeTemplateCommandId[] = "MergeTemplate";
const char goToImplementationCommandId[] = "GoToImplementation";
const char makeComponentCommandId[] = "MakeComponent";
const char editMaterialCommandId[] = "EditMaterial";
+const char editCollectionCommandId[] = "EditCollection";
const char addItemToStackedContainerCommandId[] = "AddItemToStackedContainer";
const char addTabBarToStackedContainerCommandId[] = "AddTabBarToStackedContainer";
const char increaseIndexOfStackedContainerCommandId[] = "IncreaseIndexOfStackedContainer";
@@ -80,6 +81,7 @@ const char fitRootToScreenCommandId[] = "FitRootToScreen";
const char fitSelectionToScreenCommandId[] = "FitSelectionToScreen";
const char editAnnotationsCommandId[] = "EditAnnotation";
const char addMouseAreaFillCommandId[] = "AddMouseAreaFill";
+const char editIn3dViewCommandId[] = "editIn3dView";
const char openSignalDialogCommandId[] = "OpenSignalDialog";
const char update3DAssetCommandId[] = "Update3DAsset";
@@ -126,8 +128,10 @@ const char mergeTemplateDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMen
const char goToImplementationDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Go to Implementation");
const char makeComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Create Component");
const char editMaterialDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Material");
+const char editCollectionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Model");
const char editAnnotationsDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Annotations");
const char addMouseAreaFillDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Mouse Area");
+const char editIn3dViewDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit in 3D View");
const char openSignalDialogDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Open Signal Dialog");
const char update3DAssetDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Update 3D Asset");
@@ -209,6 +213,8 @@ enum PrioritiesEnum : int {
SelectionCategory,
ArrangeCategory,
EditCategory,
+ EditListModel,
+ EditCollection,
/******** Section *****************************/
PositionSection = 2000,
SnappingCategory,
diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
index 6e4b1a1efd..4e32237ee9 100644
--- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
@@ -118,6 +118,7 @@ void DesignerActionManager::polishActions() const
Core::Context qmlDesignerNavigatorContext(Constants::C_QMLNAVIGATOR);
Core::Context qmlDesignerMaterialBrowserContext(Constants::C_QMLMATERIALBROWSER);
Core::Context qmlDesignerAssetsLibraryContext(Constants::C_QMLASSETSLIBRARY);
+ Core::Context qmlDesignerCollectionEditorContext(Constants::C_QMLCOLLECTIONEDITOR);
Core::Context qmlDesignerUIContext;
qmlDesignerUIContext.add(qmlDesignerFormEditorContext);
@@ -125,6 +126,7 @@ void DesignerActionManager::polishActions() const
qmlDesignerUIContext.add(qmlDesignerNavigatorContext);
qmlDesignerUIContext.add(qmlDesignerMaterialBrowserContext);
qmlDesignerUIContext.add(qmlDesignerAssetsLibraryContext);
+ qmlDesignerUIContext.add(qmlDesignerCollectionEditorContext);
for (auto *action : actions) {
if (!action->menuId().isEmpty()) {
@@ -577,10 +579,14 @@ QList<SlotList> getSlotsLists(const ModelNode &node)
//creates connection without signalHandlerProperty
ModelNode createNewConnection(ModelNode targetNode)
{
- NodeMetaInfo connectionsMetaInfo = targetNode.view()->model()->qtQuickConnectionsMetaInfo();
+#ifdef QDS_USE_PROJECTSTORAGE
+ ModelNode newConnectionNode = targetNode.view()->createModelNode("Connections");
+#else
+ NodeMetaInfo connectionsMetaInfo = targetNode.view()->model()->qtQmlConnectionsMetaInfo();
const auto typeName = useProjectStorage() ? "Connections" : "QtQuick.Connections";
ModelNode newConnectionNode = targetNode.view()->createModelNode(
typeName, connectionsMetaInfo.majorVersion(), connectionsMetaInfo.minorVersion());
+#endif
if (QmlItemNode::isValidQmlItemNode(targetNode)) {
targetNode.nodeAbstractProperty("data").reparentHere(newConnectionNode);
} else {
@@ -840,16 +846,22 @@ public:
{},
ComponentCoreConstants::rootCategory,
QKeySequence("Alt+e"),
- 1001,
+ ComponentCoreConstants::Priorities::EditListModel,
&openDialog,
- &isListViewInBaseState,
- &isListViewInBaseState)
+ &isListViewInBaseStateAndHasListModel,
+ &isListViewInBaseStateAndHasListModel)
{}
- static bool isListViewInBaseState(const SelectionContext &selectionState)
+ static bool isListViewInBaseStateAndHasListModel(const SelectionContext &selectionState)
{
- return selectionState.isInBaseState() && selectionState.singleNodeIsSelected()
- && selectionState.currentSingleSelectedNode().metaInfo().isListOrGridView();
+ if (!selectionState.isInBaseState() || !selectionState.singleNodeIsSelected())
+ return false;
+
+ const ModelNode singleSelectedNode = selectionState.currentSingleSelectedNode();
+
+ return singleSelectedNode.metaInfo().isListOrGridView()
+ && singleSelectedNode.property("model").toNodeProperty().modelNode().type()
+ == "QtQml.Models.ListModel";
}
bool isEnabled(const SelectionContext &) const override { return true; }
@@ -866,20 +878,9 @@ public:
NodeMetaInfo modelMetaInfo = view->model()->metaInfo("ListModel");
NodeMetaInfo elementMetaInfo = view->model()->metaInfo("ListElement");
- ListModelEditorModel model{[&] {
- return view->createModelNode(useProjectStorage()
- ? "ListModel"
- : "QtQml.Models.ListModel",
- modelMetaInfo.majorVersion(),
- modelMetaInfo.minorVersion());
- },
- [&] {
- return view->createModelNode(
- useProjectStorage() ? "ListElement"
- : "QtQml.Models.ListElement",
- elementMetaInfo.majorVersion(),
- elementMetaInfo.minorVersion());
- },
+#ifdef QDS_USE_PROJECTSTORAGE
+ ListModelEditorModel model{[&] { return view->createModelNode("ListModel"); },
+ [&] { return view->createModelNode("ListElement"); },
[&](const ModelNode &node) {
bool isNowInComponent = ModelNodeOperations::goIntoComponent(
node);
@@ -898,6 +899,33 @@ public:
return node;
}};
+#else
+ ListModelEditorModel model{
+ [&] {
+ return view->createModelNode("QtQml.Models.ListModel",
+ modelMetaInfo.majorVersion(),
+ modelMetaInfo.minorVersion());
+ },
+ [&] {
+ return view->createModelNode("QtQml.Models.ListElement",
+ elementMetaInfo.majorVersion(),
+ elementMetaInfo.minorVersion());
+ },
+ [&](const ModelNode &node) {
+ bool isNowInComponent = ModelNodeOperations::goIntoComponent(node);
+
+ Model *currentModel = QmlDesignerPlugin::instance()->currentDesignDocument()->currentModel();
+
+ if (currentModel->rewriterView() && !currentModel->rewriterView()->errors().isEmpty()) {
+ throw DocumentError{};
+ }
+
+ if (isNowInComponent)
+ return view->rootModelNode();
+
+ return node;
+ }};
+#endif
model.setListView(targetNode);
@@ -1888,9 +1916,9 @@ void DesignerActionManager::createDefaultDesignerActions()
contextIcon(DesignerIcons::EnterComponentIcon),
rootCategory,
QKeySequence(Qt::Key_F2),
- Priorities::ComponentActions + 2,
+ Priorities::ComponentActions + 3,
&goIntoComponentOperation,
- &selectionIsComponent));
+ &selectionIsEditableComponent));
addDesignerAction(new ModelNodeContextMenuAction(jumpToCodeCommandId,
JumpToCodeDisplayName,
@@ -1936,12 +1964,23 @@ void DesignerActionManager::createDefaultDesignerActions()
}
addDesignerAction(new ModelNodeContextMenuAction(
+ editIn3dViewCommandId,
+ editIn3dViewDisplayName,
+ contextIcon(DesignerIcons::EditIcon),
+ rootCategory,
+ QKeySequence(),
+ Priorities::ComponentActions + 1,
+ &editIn3dView,
+ &singleSelectionView3D,
+ &singleSelectionView3D));
+
+ addDesignerAction(new ModelNodeContextMenuAction(
makeComponentCommandId,
makeComponentDisplayName,
contextIcon(DesignerIcons::MakeComponentIcon),
rootCategory,
QKeySequence(),
- Priorities::ComponentActions + 1,
+ Priorities::ComponentActions + 2,
&moveToComponent,
&singleSelection,
&singleSelection));
@@ -1977,6 +2016,16 @@ void DesignerActionManager::createDefaultDesignerActions()
addDesignerAction(new EditListModelAction);
+ addDesignerAction(new ModelNodeContextMenuAction(editCollectionCommandId,
+ editCollectionDisplayName,
+ contextIcon(DesignerIcons::EditIcon),
+ rootCategory,
+ QKeySequence("Alt+e"),
+ ComponentCoreConstants::Priorities::EditCollection,
+ &editCollection,
+ &hasCollectionAsModel,
+ &hasCollectionAsModel));
+
addDesignerAction(new ModelNodeContextMenuAction(openSignalDialogCommandId,
openSignalDialogDisplayName,
{},
diff --git a/src/plugins/qmldesigner/components/componentcore/designericons.h b/src/plugins/qmldesigner/components/componentcore/designericons.h
index 3cb5a1e973..1bce6581bd 100644
--- a/src/plugins/qmldesigner/components/componentcore/designericons.h
+++ b/src/plugins/qmldesigner/components/componentcore/designericons.h
@@ -58,6 +58,7 @@ public:
CameraIcon,
CameraOrthographicIcon,
CameraPerspectiveIcon,
+ CameraSpeedConfigIcon,
ConnectionsIcon,
CopyIcon,
CreateIcon,
diff --git a/src/plugins/qmldesigner/components/componentcore/dialogutils.cpp b/src/plugins/qmldesigner/components/componentcore/dialogutils.cpp
new file mode 100644
index 0000000000..f882ae528d
--- /dev/null
+++ b/src/plugins/qmldesigner/components/componentcore/dialogutils.cpp
@@ -0,0 +1,32 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <model.h>
+
+#include <coreplugin/messagebox.h>
+
+namespace QmlDesigner {
+
+namespace DialogUtils {
+
+void showWarningForInvalidId(const QString &id)
+{
+ constexpr char text[] = R"(
+The ID <b>'%1'</b> is invalid.
+
+Make sure the ID is:
+<ul>
+<li>Unique within the QML file.</li>
+<li>Beginning with a lowercase letter.</li>
+<li>Without any blank space or symbol.</li>
+<li>Not a reserved QML keyword. </li>
+</ul>
+)";
+
+ Core::AsynchronousMessageBox::warning(Model::tr("Invalid Id"),
+ Model::tr(text).arg(id));
+}
+
+} // namespace DialogUtils
+
+} //QmlDesigner
diff --git a/src/plugins/qmldesigner/components/componentcore/dialogutils.h b/src/plugins/qmldesigner/components/componentcore/dialogutils.h
new file mode 100644
index 0000000000..3ca98016dd
--- /dev/null
+++ b/src/plugins/qmldesigner/components/componentcore/dialogutils.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <qmldesignercomponents_global.h>
+
+#include <QString>
+
+namespace QmlDesigner {
+
+namespace DialogUtils {
+
+QMLDESIGNERCOMPONENTS_EXPORT void showWarningForInvalidId(const QString &id);
+
+} // namespace DialogUtils
+} //QmlDesigner
diff --git a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp
index a323654b6b..8d3412e0e8 100644
--- a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp
@@ -169,9 +169,12 @@ void LayoutInGridLayout::doIt()
m_selectionContext.view()->executeInTransaction("LayoutInGridLayout1",[this, &layoutNode, layoutType](){
QTC_ASSERT(m_selectionContext.view()->model()->hasNodeMetaInfo(layoutType), return);
-
+#ifdef QDS_USE_PROJECTSTORAGE
+ layoutNode = m_selectionContext.view()->createModelNode(layoutType);
+#else
NodeMetaInfo metaInfo = m_selectionContext.view()->model()->metaInfo(layoutType);
layoutNode = m_selectionContext.view()->createModelNode(layoutType, metaInfo.majorVersion(), metaInfo.minorVersion());
+#endif
reparentTo(layoutNode, m_parentNode);
});
@@ -387,10 +390,13 @@ void LayoutInGridLayout::fillEmptyCells()
if (y > 0)
yPos = m_yTopOffsets.at(y-1);
+#ifdef QDS_USE_PROJECTSTORAGE
+ ModelNode newNode = m_selectionContext.view()->createModelNode("Item");
+#else
NodeMetaInfo metaInfo = m_selectionContext.view()->model()->metaInfo("QtQuick.Item");
ModelNode newNode = m_selectionContext.view()->createModelNode("QtQuick.Item", metaInfo.majorVersion(), metaInfo.minorVersion());
-
+#endif
reparentTo(newNode, m_parentNode);
m_spacerNodes.append(newNode);
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp
index 4a119d88a5..f6e18458b2 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp
@@ -89,10 +89,10 @@ bool fileComponentExists(const ModelNode &modelNode)
return QFileInfo::exists(fileName);
}
-bool selectionIsComponent(const SelectionContext &selectionState)
+bool selectionIsEditableComponent(const SelectionContext &selectionState)
{
- return selectionState.currentSingleSelectedNode().isComponent()
- && fileComponentExists(selectionState.currentSingleSelectedNode());
+ ModelNode node = selectionState.currentSingleSelectedNode();
+ return node.isComponent() && !QmlItemNode(node).isEffectItem() && fileComponentExists(node);
}
bool selectionIsImported3DAsset(const SelectionContext &selectionState)
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h
index 6bc9c703bc..eb4915b1d0 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h
@@ -69,6 +69,18 @@ inline bool modelHasMaterial(const SelectionContext &selectionState)
return prop.exists() && (!prop.expression().isEmpty() || !prop.resolveToModelNodeList().empty());
}
+inline bool hasCollectionAsModel(const SelectionContext &selectionState)
+{
+ if (!selectionState.isInBaseState() || !selectionState.singleNodeIsSelected())
+ return false;
+
+ const ModelNode singleSelectedNode = selectionState.currentSingleSelectedNode();
+
+ return singleSelectedNode.metaInfo().isQtQuickListView()
+ && singleSelectedNode.property("model").toBindingProperty().expression().startsWith(
+ "DataStore.");
+}
+
inline bool selectionEnabled(const SelectionContext &selectionState)
{
return selectionState.showSelectionTools();
@@ -85,6 +97,12 @@ inline bool singleSelectionNotRoot(const SelectionContext &selectionState)
&& !selectionState.currentSingleSelectedNode().isRootNode();
}
+inline bool singleSelectionView3D(const SelectionContext &selectionState)
+{
+ return selectionState.singleNodeIsSelected()
+ && selectionState.currentSingleSelectedNode().metaInfo().isQtQuick3DView3D();
+}
+
inline bool selectionHasProperty(const SelectionContext &selectionState, const char *property)
{
for (const ModelNode &modelNode : selectionState.selectedModelNodes())
@@ -112,7 +130,7 @@ inline bool singleSelectedItem(const SelectionContext &selectionState)
}
bool selectionHasSameParent(const SelectionContext &selectionState);
-bool selectionIsComponent(const SelectionContext &selectionState);
+bool selectionIsEditableComponent(const SelectionContext &selectionState);
bool singleSelectionItemIsAnchored(const SelectionContext &selectionState);
bool singleSelectionItemIsNotAnchored(const SelectionContext &selectionState);
bool selectionIsImported3DAsset(const SelectionContext &selectionState);
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
index a9bbd2ecdb..cebe7d7c53 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
@@ -60,7 +60,7 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/smallstring.h>
@@ -484,11 +484,13 @@ static void layoutHelperFunction(const SelectionContext &selectionContext,
selectionContext.view()->executeInTransaction("DesignerActionManager|layoutHelperFunction",[=](){
QmlItemNode parentNode = qmlItemNode.instanceParentItem();
-
+#ifdef QDS_USE_PROJECTSTORAGE
+ const ModelNode layoutNode = selectionContext.view()->createModelNode(layoutType);
+#else
NodeMetaInfo metaInfo = selectionContext.view()->model()->metaInfo(layoutType);
const ModelNode layoutNode = selectionContext.view()->createModelNode(layoutType, metaInfo.majorVersion(), metaInfo.minorVersion());
-
+#endif
reparentTo(layoutNode, parentNode);
QList<ModelNode> sortedSelectedNodes = selectionContext.selectedModelNodes();
@@ -579,9 +581,14 @@ static void addSignal(const QString &typeName,
const QString &itemId,
const QString &signalName,
bool isRootModelNode,
- ExternalDependenciesInterface &externanDependencies)
+ ExternalDependenciesInterface &externanDependencies,
+ [[maybe_unused]] Model *otherModel)
{
+#ifdef QDS_USE_PROJECTSTORAGE
+ auto model = otherModel->createModel("Item");
+#else
auto model = Model::create("Item", 2, 0);
+#endif
RewriterView rewriterView(externanDependencies, RewriterView::Amend);
auto textEdit = qobject_cast<TextEditor::TextEditorWidget*>
@@ -707,14 +714,16 @@ void addSignalHandlerOrGotoImplementation(const SelectionContext &selectionState
itemId,
dialog->signal(),
isModelNodeRoot,
- selectionState.view()->externalDependencies());
+ selectionState.view()->externalDependencies(),
+ selectionState.view()->model());
});
addSignal(typeName,
itemId,
dialog->signal(),
isModelNodeRoot,
- selectionState.view()->externalDependencies());
+ selectionState.view()->externalDependencies(),
+ selectionState.view()->model());
//Move cursor to correct curser position
const QString filePath = Core::EditorManager::currentDocument()->filePath().toString();
@@ -833,6 +842,30 @@ void editMaterial(const SelectionContext &selectionContext)
}
}
+// Open a collection in the collection editor
+void editCollection(const SelectionContext &selectionContext)
+{
+ ModelNode modelNode = selectionContext.targetNode();
+
+ if (!modelNode)
+ modelNode = selectionContext.currentSingleSelectedNode();
+
+ if (!modelNode)
+ return;
+
+ const QString dataStoreExpression = "DataStore.";
+
+ BindingProperty prop = modelNode.bindingProperty("model");
+ if (!prop.exists() || !prop.expression().startsWith(dataStoreExpression))
+ return;
+
+ AbstractView *view = selectionContext.view();
+ const QString collectionId = prop.expression().mid(dataStoreExpression.size());
+
+ // to CollectionEditor...
+ view->emitCustomNotification("open_collection_by_id", {}, {collectionId});
+}
+
void addItemToStackedContainer(const SelectionContext &selectionContext)
{
AbstractView *view = selectionContext.view();
@@ -862,15 +895,26 @@ void addItemToStackedContainer(const SelectionContext &selectionContext)
NodeMetaInfo itemMetaInfo = view->model()->metaInfo("QtQuick.Item", -1, -1);
QTC_ASSERT(itemMetaInfo.isValid(), return);
- QTC_ASSERT(itemMetaInfo.majorVersion() == 2, return);
-
+#ifdef QDS_USE_PROJECTSTORAGE
+ QmlDesigner::ModelNode itemNode = view->createModelNode("Item");
+#else
QmlDesigner::ModelNode itemNode =
view->createModelNode("QtQuick.Item", itemMetaInfo.majorVersion(), itemMetaInfo.minorVersion());
-
+#endif
container.defaultNodeListProperty().reparentHere(itemNode);
if (potentialTabBar.isValid()) {// The stacked container is hooked up to a TabBar
- NodeMetaInfo tabButtonMetaInfo = view->model()->metaInfo("QtQuick.Controls.TabButton", -1, -1);
+#ifdef QDS_USE_PROJECTSTORAGE
+ const int buttonIndex = potentialTabBar.directSubModelNodes().size();
+ ModelNode tabButtonNode = view->createModelNode("TabButton");
+
+ tabButtonNode.variantProperty("text").setValue(
+ QString::fromLatin1("Tab %1").arg(buttonIndex));
+ potentialTabBar.defaultNodeListProperty().reparentHere(tabButtonNode);
+#else
+ NodeMetaInfo tabButtonMetaInfo = view->model()->metaInfo("QtQuick.Controls.TabButton",
+ -1,
+ -1);
if (tabButtonMetaInfo.isValid()) {
const int buttonIndex = potentialTabBar.directSubModelNodes().size();
ModelNode tabButtonNode =
@@ -882,6 +926,7 @@ void addItemToStackedContainer(const SelectionContext &selectionContext)
potentialTabBar.defaultNodeListProperty().reparentHere(tabButtonNode);
}
+#endif
}
});
}
@@ -981,6 +1026,7 @@ void addTabBarToStackedContainer(const SelectionContext &selectionContext)
QTC_ASSERT(container.isValid(), return);
QTC_ASSERT(container.metaInfo().isValid(), return);
+#ifndef QDS_USE_PROJECTSTORAGE
NodeMetaInfo tabBarMetaInfo = view->model()->metaInfo("QtQuick.Controls.TabBar", -1, -1);
QTC_ASSERT(tabBarMetaInfo.isValid(), return);
QTC_ASSERT(tabBarMetaInfo.majorVersion() == 2, return);
@@ -988,6 +1034,7 @@ void addTabBarToStackedContainer(const SelectionContext &selectionContext)
NodeMetaInfo tabButtonMetaInfo = view->model()->metaInfo("QtQuick.Controls.TabButton", -1, -1);
QTC_ASSERT(tabButtonMetaInfo.isValid(), return);
QTC_ASSERT(tabButtonMetaInfo.majorVersion() == 2, return);
+#endif
QmlItemNode containerItemNode(container);
QTC_ASSERT(containerItemNode.isValid(), return);
@@ -995,14 +1042,15 @@ void addTabBarToStackedContainer(const SelectionContext &selectionContext)
const PropertyName indexPropertyName = getIndexPropertyName(container);
QTC_ASSERT(container.metaInfo().hasProperty(indexPropertyName), return);
- view->executeInTransaction("DesignerActionManager:addItemToStackedContainer",
- [view, container, containerItemNode, tabBarMetaInfo, tabButtonMetaInfo, indexPropertyName](){
-
+ view->executeInTransaction("DesignerActionManager:addItemToStackedContainer", [&]() {
+#ifdef QDS_USE_PROJECTSTORAGE
+ ModelNode tabBarNode = view->createModelNode("TabBar");
+#else
ModelNode tabBarNode =
view->createModelNode("QtQuick.Controls.TabBar",
tabBarMetaInfo.majorVersion(),
tabBarMetaInfo.minorVersion());
-
+#endif
container.parentProperty().reparentHere(tabBarNode);
const int maxValue = container.directSubModelNodes().size();
@@ -1014,11 +1062,14 @@ void addTabBarToStackedContainer(const SelectionContext &selectionContext)
tabBarItem.anchors().setAnchor(AnchorLineBottom, containerItemNode, AnchorLineTop);
for (int i = 0; i < maxValue; ++i) {
+#ifdef QDS_USE_PROJECTSTORAGE
+ ModelNode tabButtonNode = view->createModelNode("TabButton");
+#else
ModelNode tabButtonNode =
view->createModelNode("QtQuick.Controls.TabButton",
tabButtonMetaInfo.majorVersion(),
tabButtonMetaInfo.minorVersion());
-
+#endif
tabButtonNode.variantProperty("text").setValue(QString::fromLatin1("Tab %1").arg(i));
tabBarNode.defaultNodeListProperty().reparentHere(tabButtonNode);
}
@@ -1029,7 +1080,6 @@ void addTabBarToStackedContainer(const SelectionContext &selectionContext)
const QString expression = id + "." + QString::fromLatin1(indexPropertyName);
container.bindingProperty(indexPropertyName).setExpression(expression);
});
-
}
AddFilesResult addFilesToProject(const QStringList &fileNames, const QString &defaultDir, bool showDialog)
@@ -1146,23 +1196,22 @@ void createFlowActionArea(const SelectionContext &selectionContext)
const QPointF pos = selectionContext.scenePosition().isNull() ? QPointF() : selectionContext.scenePosition() - QmlItemNode(container).flowPosition();
- view->executeInTransaction("DesignerActionManager:createFlowActionArea",
- [view, container, actionAreaMetaInfo, pos](){
-
- ModelNode flowActionNode =
- view->createModelNode("FlowView.FlowActionArea",
+ view->executeInTransaction("DesignerActionManager:createFlowActionArea", [&]() {
+#ifdef QDS_USE_PROJECTSTORAGE
+ ModelNode flowActionNode = view->createModelNode("FlowActionArea");
+#else
+ ModelNode flowActionNode = view->createModelNode("FlowView.FlowActionArea",
actionAreaMetaInfo.majorVersion(),
actionAreaMetaInfo.minorVersion());
+#endif
+ if (!pos.isNull()) {
+ flowActionNode.variantProperty("x").setValue(pos.x());
+ flowActionNode.variantProperty("y").setValue(pos.y());
+ }
- if (!pos.isNull()) {
- flowActionNode.variantProperty("x").setValue(pos.x());
- flowActionNode.variantProperty("y").setValue(pos.y());
- }
-
- container.defaultNodeListProperty().reparentHere(flowActionNode);
- view->setSelectedModelNode(flowActionNode);
- });
-
+ container.defaultNodeListProperty().reparentHere(flowActionNode);
+ view->setSelectedModelNode(flowActionNode);
+ });
}
void addTransition(const SelectionContext &selectionContext)
@@ -1197,17 +1246,18 @@ void addFlowEffect(const SelectionContext &selectionContext, const TypeName &typ
NodeMetaInfo effectMetaInfo = view->model()->metaInfo("FlowView." + typeName, -1, -1);
QTC_ASSERT(typeName == "None" || effectMetaInfo.isValid(), return);
- view->executeInTransaction("DesignerActionManager:addFlowEffect", [=]() {
+ view->executeInTransaction("DesignerActionManager:addFlowEffect", [&]() {
if (container.hasProperty("effect"))
container.removeProperty("effect");
if (effectMetaInfo.isQtObject()) {
- ModelNode effectNode = view->createModelNode(useProjectStorage()
- ? typeName
- : effectMetaInfo.typeName(),
+#ifdef QDS_USE_PROJECTSTORAGE
+ ModelNode effectNode = view->createModelNode(typeName);
+#else
+ ModelNode effectNode = view->createModelNode(effectMetaInfo.typeName(),
effectMetaInfo.majorVersion(),
effectMetaInfo.minorVersion());
-
+#endif
container.nodeProperty("effect").reparentHere(effectNode);
view->setSelectedModelNode(effectNode);
}
@@ -1275,7 +1325,6 @@ void reparentToNodeAndAdjustPosition(const ModelNode &parentModelNode,
void addToGroupItem(const SelectionContext &selectionContext)
{
- const TypeName typeName = "QtQuick.Studio.Components.GroupItem";
try {
if (!hasStudioComponentsImport(selectionContext)) {
@@ -1291,20 +1340,28 @@ void addToGroupItem(const SelectionContext &selectionContext)
if (qmlItemNode.hasInstanceParentItem()) {
ModelNode groupNode;
- selectionContext.view()->executeInTransaction("DesignerActionManager|addToGroupItem1",[=, &groupNode](){
-
- QmlItemNode parentNode = qmlItemNode.instanceParentItem();
- NodeMetaInfo metaInfo = selectionContext.view()->model()->metaInfo(typeName);
- groupNode = selectionContext.view()->createModelNode(typeName, metaInfo.majorVersion(), metaInfo.minorVersion());
- reparentTo(groupNode, parentNode);
- });
- selectionContext.view()->executeInTransaction("DesignerActionManager|addToGroupItem2",[=](){
-
- QList<ModelNode> selectedNodes = selectionContext.selectedModelNodes();
- setUpperLeftPostionToNode(groupNode, selectedNodes);
-
- reparentToNodeAndAdjustPosition(groupNode, selectedNodes);
- });
+ selectionContext.view()
+ ->executeInTransaction("DesignerActionManager|addToGroupItem1", [&]() {
+ QmlItemNode parentNode = qmlItemNode.instanceParentItem();
+#ifdef QDS_USE_PROJECTSTORAGE
+ groupNode = selectionContext.view()->createModelNode("GroupItem");
+#else
+ const TypeName typeName = "QtQuick.Studio.Components.GroupItem";
+
+ NodeMetaInfo metaInfo = selectionContext.view()->model()->metaInfo(typeName);
+ groupNode = selectionContext.view()->createModelNode(typeName,
+ metaInfo.majorVersion(),
+ metaInfo.minorVersion());
+#endif
+ reparentTo(groupNode, parentNode);
+ });
+ selectionContext.view()
+ ->executeInTransaction("DesignerActionManager|addToGroupItem2", [&]() {
+ QList<ModelNode> selectedNodes = selectionContext.selectedModelNodes();
+ setUpperLeftPostionToNode(groupNode, selectedNodes);
+
+ reparentToNodeAndAdjustPosition(groupNode, selectedNodes);
+ });
}
}
} catch (RewritingException &e) {
@@ -1363,7 +1420,6 @@ static void getTypeAndImport(const SelectionContext &selectionContext,
void addCustomFlowEffect(const SelectionContext &selectionContext)
{
-
TypeName typeName;
QString typeString;
@@ -1392,23 +1448,27 @@ void addCustomFlowEffect(const SelectionContext &selectionContext)
QTC_ASSERT(container.metaInfo().isValid(), return);
QTC_ASSERT(QmlItemNode::isFlowTransition(container), return);
+#ifndef QDS_USE_PROJECTSTORAGE
NodeMetaInfo effectMetaInfo = view->model()->metaInfo(typeName, -1, -1);
QTC_ASSERT(typeName == "None" || effectMetaInfo.isValid(), return);
-
- view->executeInTransaction("DesignerActionManager:addFlowEffect", [=]() {
+#endif
+ view->executeInTransaction("DesignerActionManager:addFlowEffect", [&]() {
if (container.hasProperty("effect"))
container.removeProperty("effect");
+#ifdef QDS_USE_PROJECTSTORAGE
+ ModelNode effectNode = view->createModelNode(typeName);
+ container.nodeProperty("effect").reparentHere(effectNode);
+ view->setSelectedModelNode(effectNode);
+#else
if (effectMetaInfo.isValid()) {
- ModelNode effectNode = view->createModelNode(useProjectStorage()
- ? typeName
- : effectMetaInfo.typeName(),
+ ModelNode effectNode = view->createModelNode(effectMetaInfo.typeName(),
effectMetaInfo.majorVersion(),
effectMetaInfo.minorVersion());
-
container.nodeProperty("effect").reparentHere(effectNode);
view->setSelectedModelNode(effectNode);
}
+#endif
});
}
@@ -1426,7 +1486,6 @@ static QString fromCamelCase(const QString &s)
QString getTemplateDialog(const Utils::FilePath &projectPath)
{
-
const Utils::FilePath templatesPath = projectPath.pathAppended("templates");
const QStringList templateFiles = QDir(templatesPath.toString()).entryList({"*.qml"});
@@ -1574,23 +1633,30 @@ void addMouseAreaFill(const SelectionContext &selectionContext)
return;
}
- selectionContext.view()->executeInTransaction("DesignerActionManager|addMouseAreaFill", [selectionContext]() {
- ModelNode modelNode = selectionContext.currentSingleSelectedNode();
- if (modelNode.isValid()) {
- NodeMetaInfo itemMetaInfo = selectionContext.view()->model()->metaInfo("QtQuick.MouseArea", -1, -1);
- QTC_ASSERT(itemMetaInfo.isValid(), return);
-
- QmlDesigner::ModelNode mouseAreaNode =
- selectionContext.view()->createModelNode("QtQuick.MouseArea", itemMetaInfo.majorVersion(), itemMetaInfo.minorVersion());
- mouseAreaNode.validId();
-
- modelNode.defaultNodeListProperty().reparentHere(mouseAreaNode);
- QmlItemNode mouseAreaItemNode(mouseAreaNode);
- if (mouseAreaItemNode.isValid()) {
- mouseAreaItemNode.anchors().fill();
+ selectionContext.view()
+ ->executeInTransaction("DesignerActionManager|addMouseAreaFill", [selectionContext]() {
+ ModelNode modelNode = selectionContext.currentSingleSelectedNode();
+ if (modelNode.isValid()) {
+#ifdef QDS_USE_PROJECTSTORAGE
+ QmlDesigner::ModelNode mouseAreaNode = selectionContext.view()->createModelNode(
+ "MouseArea");
+#else
+ NodeMetaInfo itemMetaInfo = selectionContext.view()->model()->metaInfo(
+ "QtQuick.MouseArea", -1, -1);
+ QTC_ASSERT(itemMetaInfo.isValid(), return);
+
+ QmlDesigner::ModelNode mouseAreaNode = selectionContext.view()->createModelNode(
+ "QtQuick.MouseArea", itemMetaInfo.majorVersion(), itemMetaInfo.minorVersion());
+#endif
+ mouseAreaNode.validId();
+
+ modelNode.defaultNodeListProperty().reparentHere(mouseAreaNode);
+ QmlItemNode mouseAreaItemNode(mouseAreaNode);
+ if (mouseAreaItemNode.isValid()) {
+ mouseAreaItemNode.anchors().fill();
+ }
}
- }
- });
+ });
}
QVariant previewImageDataForGenericNode(const ModelNode &modelNode)
@@ -1623,6 +1689,15 @@ void updateImported3DAsset(const SelectionContext &selectionContext)
}
}
+void editIn3dView(const SelectionContext &selectionContext)
+{
+ if (selectionContext.view() && selectionContext.hasSingleSelectedModelNode()
+ && selectionContext.currentSingleSelectedNode().metaInfo().isQtQuick3DView3D()) {
+ QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("Editor3D", true);
+ selectionContext.view()->emitView3DAction(View3DActionType::AlignViewToCamera, true);
+ }
+}
+
bool isEffectComposerActivated()
{
const QVector<ExtensionSystem::PluginSpec *> specs = ExtensionSystem::PluginManager::plugins();
@@ -1638,7 +1713,7 @@ void openEffectComposer(const QString &filePath)
if (ModelNodeOperations::isEffectComposerActivated()) {
QmlDesignerPlugin::instance()->viewManager()
.emitCustomNotification("open_effectcomposer_composition", {}, {filePath});
- QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("Effect Composer", true);
+ QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("EffectComposer", true);
} else {
ModelNodeOperations::openOldEffectMaker(filePath);
}
@@ -1654,7 +1729,8 @@ void openOldEffectMaker(const QString &filePath)
Utils::FilePath projectPath = target->project()->projectDirectory();
QString effectName = QFileInfo(filePath).baseName();
- QString effectResDir = QLatin1String(Constants::DEFAULT_ASSET_IMPORT_FOLDER) + "/Effects/" + effectName;
+ QString effectResDir = QLatin1String(Constants::DEFAULT_EFFECTS_IMPORT_FOLDER)
+ + "/" + effectName;
Utils::FilePath effectResPath = projectPath.pathAppended(effectResDir);
if (!effectResPath.exists())
QDir().mkpath(effectResPath.toString());
@@ -1693,7 +1769,7 @@ void openOldEffectMaker(const QString &filePath)
Utils::FilePath getEffectsImportDirectory()
{
- QString defaultDir = QLatin1String(Constants::DEFAULT_ASSET_IMPORT_FOLDER) + "/Effects";
+ QString defaultDir = QLatin1String(Constants::DEFAULT_EFFECTS_IMPORT_FOLDER);
Utils::FilePath projectPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath();
Utils::FilePath effectsPath = projectPath.pathAppended(defaultDir);
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h
index 196dd4922c..a67cef4942 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h
@@ -92,6 +92,7 @@ void layoutGridLayout(const SelectionContext &selectionState);
void goImplementation(const SelectionContext &selectionState);
void addNewSignalHandler(const SelectionContext &selectionState);
void editMaterial(const SelectionContext &selectionContext);
+void editCollection(const SelectionContext &selectionContext);
void addSignalHandlerOrGotoImplementation(const SelectionContext &selectionState, bool addAlwaysNewSlot);
void removeLayout(const SelectionContext &selectionContext);
void removePositioner(const SelectionContext &selectionContext);
@@ -123,6 +124,7 @@ void addMouseAreaFill(const SelectionContext &selectionContext);
void openSignalDialog(const SelectionContext &selectionContext);
void updateImported3DAsset(const SelectionContext &selectionContext);
+void editIn3dView(const SelectionContext &selectionContext);
QMLDESIGNERCOMPONENTS_EXPORT Utils::FilePath getEffectsImportDirectory();
QMLDESIGNERCOMPONENTS_EXPORT QString getEffectsDefaultDirectory(const QString &defaultDir = {});
diff --git a/src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp b/src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp
index e59fca7353..62916a44bd 100644
--- a/src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0#include "qmleditormenu.h"
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "qmleditormenu.h"
diff --git a/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp
new file mode 100644
index 0000000000..4a229564c6
--- /dev/null
+++ b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp
@@ -0,0 +1,272 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <resourcegenerator.h>
+
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/documentmanager.h>
+#include <coreplugin/messagebox.h>
+#include <coreplugin/messagemanager.h>
+
+#include <projectexplorer/projectmanager.h>
+#include <projectexplorer/target.h>
+
+#include <qmlprojectmanager/buildsystem/qmlbuildsystem.h>
+#include <qmlprojectmanager/qmlprojectconstants.h>
+
+#include <qtsupport/qtkitaspect.h>
+
+#include <utils/qtcassert.h>
+#include <utils/qtcprocess.h>
+
+#include <QMessageBox>
+#include <QProgressDialog>
+#include <QTemporaryFile>
+#include <QXmlStreamWriter>
+#include <QtConcurrent>
+
+using namespace Utils;
+
+namespace QmlDesigner::ResourceGenerator {
+
+void generateMenuEntry(QObject *parent)
+{
+ const Core::Context projectContext(QmlProjectManager::Constants::QML_PROJECT_ID);
+ // ToDo: move this to QtCreator and add tr to the string then
+ auto action = new QAction(QCoreApplication::translate("QmlDesigner::GenerateResource",
+ "Generate QRC Resource File..."),
+ parent);
+ action->setEnabled(ProjectExplorer::ProjectManager::startupProject() != nullptr);
+ // todo make it more intelligent when it gets enabled
+ QObject::connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged,
+ [action]() {
+ if (auto buildSystem = QmlProjectManager::QmlBuildSystem::getStartupBuildSystem())
+ action->setEnabled(!buildSystem->qtForMCUs());
+ });
+
+ Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.CreateResource");
+ QObject::connect(action, &QAction::triggered, []() {
+ auto project = ProjectExplorer::ProjectManager::startupProject();
+ QTC_ASSERT(project, return);
+ const FilePath projectPath = project->projectFilePath().parentDir();
+ auto qrcFilePath = Core::DocumentManager::getSaveFileNameWithExtension(
+ QCoreApplication::translate("QmlDesigner::GenerateResource", "Save Project as QRC File"),
+ projectPath.pathAppended(project->displayName() + ".qrc"),
+ QCoreApplication::translate("QmlDesigner::GenerateResource", "QML Resource File (*.qrc)"));
+
+ if (qrcFilePath.toString().isEmpty())
+ return;
+
+ createQrcFile(qrcFilePath);
+
+ Core::AsynchronousMessageBox::information(
+ QCoreApplication::translate("QmlDesigner::GenerateResource", "Success"),
+ QCoreApplication::translate("QmlDesigner::GenerateResource",
+ "Successfully generated QRC resource file\n %1")
+ .arg(qrcFilePath.toString()));
+ });
+
+ // ToDo: move this to QtCreator and add tr to the string then
+ auto rccAction = new QAction(QCoreApplication::translate("QmlDesigner::GenerateResource",
+ "Generate Deployable Package..."),
+ parent);
+ rccAction->setEnabled(ProjectExplorer::ProjectManager::startupProject() != nullptr);
+ QObject::connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged,
+ [rccAction]() {
+ rccAction->setEnabled(ProjectExplorer::ProjectManager::startupProject());
+ });
+
+ Core::Command *cmd2 = Core::ActionManager::registerAction(rccAction,
+ "QmlProject.CreateRCCResource");
+ QObject::connect(rccAction, &QAction::triggered, []() {
+ auto project = ProjectExplorer::ProjectManager::startupProject();
+ QTC_ASSERT(project, return);
+ const FilePath projectPath = project->projectFilePath().parentDir();
+ const FilePath qmlrcFilePath = Core::DocumentManager::getSaveFileNameWithExtension(
+ QCoreApplication::translate("QmlDesigner::GenerateResource", "Save Project as Resource"),
+ projectPath.pathAppended(project->displayName() + ".qmlrc"),
+ "QML Resource File (*.qmlrc);;Resource File (*.rcc)");
+
+ if (qmlrcFilePath.toString().isEmpty())
+ return;
+
+ QProgressDialog progress;
+ progress.setLabelText(
+ QCoreApplication::translate("QmlDesigner::GenerateResource",
+ "Generating deployable package. Please wait..."));
+ progress.setRange(0, 0);
+ progress.setWindowModality(Qt::WindowModal);
+ progress.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
+ progress.setCancelButton(nullptr);
+ progress.show();
+
+ QFuture<bool> future = QtConcurrent::run(
+ [qmlrcFilePath]() { return createQmlrcFile(qmlrcFilePath); });
+
+ while (!future.isFinished())
+ QCoreApplication::processEvents();
+
+ progress.close();
+
+ if (future.isCanceled()) {
+ qDebug() << "Operation canceled by user";
+ return;
+ }
+
+ if (!future.result()) {
+ Core::MessageManager::writeDisrupting(
+ QCoreApplication::translate("QmlDesigner::GenerateResource",
+ "Failed to generate deployable package!"));
+ QMessageBox msgBox;
+ msgBox.setWindowTitle(
+ QCoreApplication::translate("QmlDesigner::GenerateResource", "Error"));
+ msgBox.setText(QCoreApplication::translate(
+ "QmlDesigner::GenerateResource",
+ "Failed to generate deployable package!\n\nPlease check "
+ "the output pane for more information."));
+ msgBox.exec();
+ return;
+ }
+
+ QMessageBox msgBox;
+ msgBox.setWindowTitle(QCoreApplication::translate("QmlDesigner::GenerateResource", "Success"));
+ msgBox.setText(QCoreApplication::translate("QmlDesigner::GenerateResource",
+ "Successfully generated deployable package"));
+ msgBox.exec();
+ });
+
+ Core::ActionContainer *exportMenu = Core::ActionManager::actionContainer(
+ QmlProjectManager::Constants::EXPORT_MENU);
+ exportMenu->addAction(cmd, QmlProjectManager::Constants::G_EXPORT_GENERATE);
+ exportMenu->addAction(cmd2, QmlProjectManager::Constants::G_EXPORT_GENERATE);
+}
+
+bool skipSuffix(const QString &fileName)
+{
+ const QStringList suffixes = {".pri",
+ ".pro",
+ ".user",
+ ".qrc",
+ ".qds",
+ "CMakeLists.txt",
+ ".db",
+ ".tmp",
+ ".TMP",
+ ".metainfo",
+ ".qtds",
+ ".db-shm",
+ ".db-wal"};
+
+ for (const auto &suffix : suffixes)
+ if (fileName.endsWith(suffix))
+ return true;
+
+ return false;
+}
+
+QStringList getProjectFileList()
+{
+ const ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
+ const FilePaths paths = project->files(ProjectExplorer::Project::AllFiles);
+
+ const QDir dir(project->projectFilePath().parentDir().toString());
+ QStringList selectedFileList;
+
+ for (const FilePath &path : paths) {
+ QString relativePath = dir.relativeFilePath(path.toString());
+ if (!skipSuffix(relativePath))
+ selectedFileList.append(relativePath);
+ }
+
+ return selectedFileList;
+}
+
+bool createQrcFile(const FilePath &qrcFilePath)
+{
+ QFile qrcFile(qrcFilePath.toString());
+
+ if (!qrcFile.open(QIODeviceBase::WriteOnly | QIODevice::Truncate))
+ return false;
+
+ QXmlStreamWriter writer(&qrcFile);
+ writer.setAutoFormatting(true);
+ writer.setAutoFormattingIndent(0);
+
+ writer.writeStartElement("RCC");
+ writer.writeStartElement("qresource");
+
+ for (const QString &fileName : getProjectFileList())
+ writer.writeTextElement("file", fileName.trimmed());
+
+ writer.writeEndElement();
+ writer.writeEndElement();
+ qrcFile.close();
+
+ return true;
+}
+
+bool createQmlrcFile(const FilePath &qmlrcFilePath)
+{
+ const FilePath tempQrcFile = qmlrcFilePath.parentDir().pathAppended("temp.qrc");
+ if (!createQrcFile(tempQrcFile))
+ return false;
+
+ const ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
+ const QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(
+ project->activeTarget()->kit());
+ const FilePath rccBinary = qtVersion->rccFilePath();
+
+ Utils::Process rccProcess;
+ rccProcess.setWorkingDirectory(project->projectDirectory());
+
+ const QStringList arguments = {"--binary",
+ "--output",
+ qmlrcFilePath.toString(),
+ tempQrcFile.toString()};
+
+ rccProcess.setCommand({rccBinary, arguments});
+ rccProcess.start();
+ if (!rccProcess.waitForStarted()) {
+ Core::MessageManager::writeDisrupting(
+ QCoreApplication::translate("QmlDesigner::GenerateResource",
+ "Unable to generate resource file: %1")
+ .arg(qmlrcFilePath.toString()));
+ return false;
+ }
+
+ QByteArray stdOut;
+ QByteArray stdErr;
+ if (!rccProcess.readDataFromProcess(&stdOut, &stdErr)) {
+ rccProcess.stop();
+ Core::MessageManager::writeDisrupting(
+ QCoreApplication::translate("QmlDesigner::GenerateResource",
+ "A timeout occurred running \"%1\".")
+ .arg(rccProcess.commandLine().toUserOutput()));
+ return false;
+ }
+
+ if (!stdOut.trimmed().isEmpty())
+ Core::MessageManager::writeFlashing(QString::fromLocal8Bit(stdOut));
+
+ if (!stdErr.trimmed().isEmpty())
+ Core::MessageManager::writeFlashing(QString::fromLocal8Bit(stdErr));
+
+ if (rccProcess.exitStatus() != QProcess::NormalExit) {
+ Core::MessageManager::writeDisrupting(
+ QCoreApplication::translate("QmlDesigner::GenerateResource", "\"%1\" crashed.")
+ .arg(rccProcess.commandLine().toUserOutput()));
+ return false;
+ }
+ if (rccProcess.exitCode() != 0) {
+ Core::MessageManager::writeDisrupting(
+ QCoreApplication::translate("QmlDesigner::GenerateResource", "\"%1\" failed (exit code %2).")
+ .arg(rccProcess.commandLine().toUserOutput())
+ .arg(rccProcess.exitCode()));
+ return false;
+ }
+ return true;
+}
+
+} // namespace QmlDesigner::ResourceGenerator
diff --git a/src/plugins/qmldesigner/components/componentcore/resourcegenerator.h b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.h
new file mode 100644
index 0000000000..6784763427
--- /dev/null
+++ b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.h
@@ -0,0 +1,18 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <utils/fileutils.h>
+
+#include <qmldesignercomponents_global.h>
+
+namespace QmlDesigner::ResourceGenerator {
+
+QMLDESIGNERCOMPONENTS_EXPORT void generateMenuEntry(QObject *parent);
+QMLDESIGNERCOMPONENTS_EXPORT QStringList getProjectFileList();
+QMLDESIGNERCOMPONENTS_EXPORT bool skipSuffix(const QString &fileName);
+QMLDESIGNERCOMPONENTS_EXPORT bool createQrcFile(const Utils::FilePath &qrcFilePath);
+QMLDESIGNERCOMPONENTS_EXPORT bool createQmlrcFile(const Utils::FilePath &qmlrcFilePath);
+
+} // namespace QmlDesigner::ResourceGenerator
diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h
index 3ffc41c3c6..392f6c94f6 100644
--- a/src/plugins/qmldesigner/components/componentcore/theme.h
+++ b/src/plugins/qmldesigner/components/componentcore/theme.h
@@ -83,6 +83,8 @@ public:
binding_medium,
bounds_small,
branch_medium,
+ cameraSpeed_medium,
+ camera_medium,
camera_small,
centerHorizontal,
centerVertical,
diff --git a/src/plugins/qmldesigner/components/componentcore/utils3d.cpp b/src/plugins/qmldesigner/components/componentcore/utils3d.cpp
new file mode 100644
index 0000000000..3983dcb13c
--- /dev/null
+++ b/src/plugins/qmldesigner/components/componentcore/utils3d.cpp
@@ -0,0 +1,157 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "utils3d.h"
+
+#include <nodeabstractproperty.h>
+#include <nodelistproperty.h>
+#include <nodemetainfo.h>
+#include <qmldesignerconstants.h>
+#include <variantproperty.h>
+
+namespace QmlDesigner {
+namespace Utils3D {
+
+ModelNode active3DSceneNode(AbstractView *view)
+{
+ if (!view)
+ return {};
+
+ auto activeSceneAux = view->rootModelNode().auxiliaryData(active3dSceneProperty);
+ if (activeSceneAux) {
+ int activeScene = activeSceneAux->toInt();
+
+ if (view->hasModelNodeForInternalId(activeScene))
+ return view->modelNodeForInternalId(activeScene);
+ }
+
+ return {};
+}
+
+qint32 active3DSceneId(Model *model)
+{
+ auto sceneId = model->rootModelNode().auxiliaryData(active3dSceneProperty);
+ if (sceneId)
+ return sceneId->toInt();
+ return -1;
+}
+
+ModelNode materialLibraryNode(AbstractView *view)
+{
+ return view->modelNodeForId(Constants::MATERIAL_LIB_ID);
+}
+
+// Creates material library if it doesn't exist and moves any existing materials into it.
+void ensureMaterialLibraryNode(AbstractView *view)
+{
+ ModelNode matLib = view->modelNodeForId(Constants::MATERIAL_LIB_ID);
+ if (matLib.isValid()
+ || (!view->rootModelNode().metaInfo().isQtQuick3DNode()
+ && !view->rootModelNode().metaInfo().isQtQuickItem())) {
+ return;
+ }
+
+ view->executeInTransaction(__FUNCTION__, [&] {
+ // Create material library node
+#ifdef QDS_USE_PROJECTSTORAGE
+ TypeName nodeTypeName = view->rootModelNode().metaInfo().isQtQuick3DNode() ? "Node" : "Item";
+ matLib = view->createModelNode(nodeTypeName, -1, -1);
+#else
+ auto nodeType = view->rootModelNode().metaInfo().isQtQuick3DNode()
+ ? view->model()->qtQuick3DNodeMetaInfo()
+ : view->model()->qtQuickItemMetaInfo();
+ matLib = view->createModelNode(nodeType.typeName(), nodeType.majorVersion(), nodeType.minorVersion());
+#endif
+ matLib.setIdWithoutRefactoring(Constants::MATERIAL_LIB_ID);
+ view->rootModelNode().defaultNodeListProperty().reparentHere(matLib);
+ });
+
+ // Do the material reparentings in different transaction to work around issue QDS-8094
+ view->executeInTransaction(__FUNCTION__, [&] {
+ const QList<ModelNode> materials = view->rootModelNode().subModelNodesOfType(
+ view->model()->qtQuick3DMaterialMetaInfo());
+ if (!materials.isEmpty()) {
+ // Move all materials to under material library node
+ for (const ModelNode &node : materials) {
+ // If material has no name, set name to id
+ QString matName = node.variantProperty("objectName").value().toString();
+ if (matName.isEmpty()) {
+ VariantProperty objNameProp = node.variantProperty("objectName");
+ objNameProp.setValue(node.id());
+ }
+
+ matLib.defaultNodeListProperty().reparentHere(node);
+ }
+ }
+ });
+}
+
+bool isPartOfMaterialLibrary(const ModelNode &node)
+{
+ if (!node.isValid())
+ return false;
+
+ ModelNode matLib = materialLibraryNode(node.view());
+
+ return matLib.isValid()
+ && (node == matLib
+ || (node.hasParentProperty() && node.parentProperty().parentModelNode() == matLib));
+}
+
+ModelNode getTextureDefaultInstance(const QString &source, AbstractView *view)
+{
+ ModelNode matLib = materialLibraryNode(view);
+ if (!matLib.isValid())
+ return {};
+
+ const QList<ModelNode> matLibNodes = matLib.directSubModelNodes();
+ for (const ModelNode &tex : matLibNodes) {
+ if (tex.isValid() && tex.metaInfo().isQtQuick3DTexture()) {
+ const QList<AbstractProperty> props = tex.properties();
+ if (props.size() != 1)
+ continue;
+ const AbstractProperty &prop = props[0];
+ if (prop.name() == "source" && prop.isVariantProperty()
+ && prop.toVariantProperty().value().toString() == source) {
+ return tex;
+ }
+ }
+ }
+
+ return {};
+}
+
+ModelNode activeView3dNode(AbstractView *view)
+{
+ if (!view || !view->model())
+ return {};
+
+ ModelNode activeView3D;
+ ModelNode activeScene = Utils3D::active3DSceneNode(view);
+
+ if (activeScene.isValid()) {
+ if (activeScene.metaInfo().isQtQuick3DView3D()) {
+ activeView3D = activeScene;
+ } else {
+ ModelNode sceneParent = activeScene.parentProperty().parentModelNode();
+ if (sceneParent.metaInfo().isQtQuick3DView3D())
+ activeView3D = sceneParent;
+ }
+ return activeView3D;
+ }
+
+ return {};
+}
+
+QString activeView3dId(AbstractView *view)
+{
+ ModelNode activeView3D = activeView3dNode(view);
+
+ if (activeView3D.isValid())
+ return activeView3D.id();
+
+ return {};
+}
+
+} // namespace Utils3D
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/componentcore/utils3d.h b/src/plugins/qmldesigner/components/componentcore/utils3d.h
new file mode 100644
index 0000000000..0e582c0fb4
--- /dev/null
+++ b/src/plugins/qmldesigner/components/componentcore/utils3d.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+// please put here 3d related functions which have no clear place elsewhere
+
+#include <abstractview.h>
+
+namespace QmlDesigner {
+namespace Utils3D {
+
+inline constexpr AuxiliaryDataKeyView active3dSceneProperty{AuxiliaryDataType::Temporary,
+ "active3dScene"};
+
+ModelNode active3DSceneNode(AbstractView *view);
+qint32 active3DSceneId(Model *model);
+
+ModelNode materialLibraryNode(AbstractView *view);
+void ensureMaterialLibraryNode(AbstractView *view);
+bool isPartOfMaterialLibrary(const ModelNode &node);
+ModelNode getTextureDefaultInstance(const QString &source, AbstractView *view);
+
+ModelNode activeView3dNode(AbstractView *view);
+QString activeView3dId(AbstractView *view);
+
+} // namespace Utils3D
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp
index a5e41e8f23..97c73205b2 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp
@@ -19,9 +19,8 @@
namespace QmlDesigner {
-BackendModel::BackendModel(ConnectionView *parent) :
- QStandardItemModel(parent)
- ,m_connectionView(parent)
+BackendModel::BackendModel(ConnectionView *view)
+ : m_connectionView(view)
{
connect(this, &QStandardItemModel::dataChanged, this, &BackendModel::handleDataChanged);
}
@@ -229,16 +228,17 @@ void BackendModel::addNewBackend()
if (!dialog.isSingleton()) {
m_connectionView->executeInTransaction("BackendModel::addNewBackend",
[this, metaInfo, typeName, propertyName, &dialog] {
- int minorVersion = metaInfo.minorVersion();
- int majorVersion = metaInfo.majorVersion();
if (dialog.localDefinition()) {
- ModelNode newNode = m_connectionView->createModelNode(useProjectStorage()
- ? typeName.toUtf8()
- : metaInfo.typeName(),
+#ifdef QDS_USE_PROJECTSTORAGE
+ ModelNode newNode = m_connectionView->createModelNode(typeName.toUtf8());
+#else
+ int minorVersion = metaInfo.minorVersion();
+ int majorVersion = metaInfo.majorVersion();
+ ModelNode newNode = m_connectionView->createModelNode(metaInfo.typeName(),
majorVersion,
minorVersion);
-
+#endif
m_connectionView->rootModelNode().nodeProperty(propertyName.toUtf8()).setDynamicTypeNameAndsetModelNode(
typeName.toUtf8(), newNode);
} else {
diff --git a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.h b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.h
index c4f148aa2e..9feaff9194 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.h
+++ b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.h
@@ -21,7 +21,7 @@ public:
IsLocalColumn = 3,
};
- BackendModel(ConnectionView *parent);
+ BackendModel(ConnectionView *view);
ConnectionView *connectionView() const;
diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp
index e3c47d996c..f871bae84b 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp
@@ -20,10 +20,8 @@
namespace QmlDesigner {
-BindingModel::BindingModel(ConnectionView *parent)
- : QStandardItemModel(parent)
- , m_connectionView(parent)
- , m_delegate(new BindingModelBackendDelegate(this))
+BindingModel::BindingModel(ConnectionView *view)
+ : m_connectionView(view)
{
setHorizontalHeaderLabels(BindingModelItem::headerLabels());
}
@@ -33,9 +31,9 @@ ConnectionView *BindingModel::connectionView() const
return m_connectionView;
}
-BindingModelBackendDelegate *BindingModel::delegate() const
+BindingModelBackendDelegate *BindingModel::delegate()
{
- return m_delegate;
+ return &m_delegate;
}
int BindingModel::currentIndex() const
@@ -133,7 +131,7 @@ void BindingModel::setCurrentIndex(int i)
m_currentIndex = i;
emit currentIndexChanged();
}
- m_delegate->update(currentProperty(), m_connectionView);
+ m_delegate.update(currentProperty(), m_connectionView);
}
void BindingModel::setCurrentProperty(const AbstractProperty &property)
@@ -153,7 +151,7 @@ void BindingModel::updateItem(const BindingProperty &property)
setCurrentProperty(property);
}
}
- m_delegate->update(currentProperty(), m_connectionView);
+ m_delegate.update(currentProperty(), m_connectionView);
}
void BindingModel::removeItem(const AbstractProperty &property)
@@ -248,9 +246,8 @@ void BindingModel::addModelNode(const ModelNode &node)
appendRow(new BindingModelItem(property));
}
-BindingModelBackendDelegate::BindingModelBackendDelegate(BindingModel *parent)
- : QObject(parent)
- , m_targetNode()
+BindingModelBackendDelegate::BindingModelBackendDelegate()
+ : m_targetNode()
, m_property()
, m_sourceNode()
, m_sourceNodeProperty()
diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h
index 4f5032afc2..b57cc5c958 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h
+++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h
@@ -17,6 +17,39 @@ class BindingModelBackendDelegate;
class BindingModelItem;
class ConnectionView;
+class BindingModelBackendDelegate : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString targetNode READ targetNode NOTIFY targetNodeChanged)
+ Q_PROPERTY(StudioQmlComboBoxBackend *property READ property CONSTANT)
+ Q_PROPERTY(StudioQmlComboBoxBackend *sourceNode READ sourceNode CONSTANT)
+ Q_PROPERTY(StudioQmlComboBoxBackend *sourceProperty READ sourceProperty CONSTANT)
+
+signals:
+ void targetNodeChanged();
+
+public:
+ BindingModelBackendDelegate();
+
+ void update(const BindingProperty &property, AbstractView *view);
+
+private:
+ QString targetNode() const;
+ void sourceNodeChanged();
+ void sourcePropertyNameChanged() const;
+ void targetPropertyNameChanged() const;
+
+ StudioQmlComboBoxBackend *property();
+ StudioQmlComboBoxBackend *sourceNode();
+ StudioQmlComboBoxBackend *sourceProperty();
+
+ QString m_targetNode;
+ StudioQmlComboBoxBackend m_property;
+ StudioQmlComboBoxBackend m_sourceNode;
+ StudioQmlComboBoxBackend m_sourceNodeProperty;
+};
+
class BindingModel : public QStandardItemModel
{
Q_OBJECT
@@ -29,10 +62,10 @@ public:
Q_PROPERTY(BindingModelBackendDelegate *delegate READ delegate CONSTANT)
public:
- BindingModel(ConnectionView *parent = nullptr);
+ BindingModel(ConnectionView *view);
ConnectionView *connectionView() const;
- BindingModelBackendDelegate *delegate() const;
+ BindingModelBackendDelegate *delegate();
int currentIndex() const;
BindingProperty currentProperty() const;
@@ -63,41 +96,9 @@ private:
private:
ConnectionView *m_connectionView = nullptr;
- BindingModelBackendDelegate *m_delegate = nullptr;
+ BindingModelBackendDelegate m_delegate;
int m_currentIndex = -1;
};
-class BindingModelBackendDelegate : public QObject
-{
- Q_OBJECT
-
- Q_PROPERTY(QString targetNode READ targetNode NOTIFY targetNodeChanged)
- Q_PROPERTY(StudioQmlComboBoxBackend *property READ property CONSTANT)
- Q_PROPERTY(StudioQmlComboBoxBackend *sourceNode READ sourceNode CONSTANT)
- Q_PROPERTY(StudioQmlComboBoxBackend *sourceProperty READ sourceProperty CONSTANT)
-
-signals:
- void targetNodeChanged();
-
-public:
- BindingModelBackendDelegate(BindingModel *parent = nullptr);
-
- void update(const BindingProperty &property, AbstractView *view);
-
-private:
- QString targetNode() const;
- void sourceNodeChanged();
- void sourcePropertyNameChanged() const;
- void targetPropertyNameChanged() const;
-
- StudioQmlComboBoxBackend *property();
- StudioQmlComboBoxBackend *sourceNode();
- StudioQmlComboBoxBackend *sourceProperty();
-
- QString m_targetNode;
- StudioQmlComboBoxBackend m_property;
- StudioQmlComboBoxBackend m_sourceNode;
- StudioQmlComboBoxBackend m_sourceNodeProperty;
-};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp
index 0741b43430..2047f7625c 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp
@@ -48,16 +48,16 @@ QStringList propertyNameListToStringList(const QmlDesigner::PropertyNameList &pr
bool isConnection(const QmlDesigner::ModelNode &modelNode)
{
- return (modelNode.metaInfo().simplifiedTypeName() == "Connections");
+ return modelNode.metaInfo().isQtQmlConnections();
}
} //namespace
namespace QmlDesigner {
-ConnectionModel::ConnectionModel(ConnectionView *parent)
- : QStandardItemModel(parent), m_connectionView(parent),
- m_delegate(new ConnectionModelBackendDelegate(this))
+ConnectionModel::ConnectionModel(ConnectionView *view)
+ : m_connectionView(view)
+ , m_delegate{this}
{
connect(this, &QStandardItemModel::dataChanged, this, &ConnectionModel::handleDataChanged);
}
@@ -90,7 +90,7 @@ void ConnectionModel::resetModel()
addModelNode(modelNode);
}
endResetModel();
- m_delegate->update();
+ m_delegate.update();
}
SignalHandlerProperty ConnectionModel::signalHandlerPropertyForRow(int rowNumber) const
@@ -361,11 +361,14 @@ void ConnectionModel::addConnection(const PropertyName &signalName)
ModelNode rootModelNode = connectionView()->rootModelNode();
if (rootModelNode.isValid() && rootModelNode.metaInfo().isValid()) {
-
- NodeMetaInfo nodeMetaInfo = connectionView()->model()->qtQuickConnectionsMetaInfo();
+#ifndef QDS_USE_PROJECTSTORAGE
+ NodeMetaInfo nodeMetaInfo = connectionView()->model()->qtQmlConnectionsMetaInfo();
if (nodeMetaInfo.isValid()) {
- ModelNode selectedNode = connectionView()->selectedModelNodes().constFirst();
+#endif
+ ModelNode selectedNode = connectionView()->firstSelectedModelNode();
+ if (!selectedNode)
+ selectedNode = connectionView()->rootModelNode();
PropertyName signalHandlerName = signalName;
if (signalHandlerName.isEmpty())
@@ -374,12 +377,14 @@ void ConnectionModel::addConnection(const PropertyName &signalName)
signalHandlerName = addOnToSignalName(QString::fromUtf8(signalHandlerName)).toUtf8();
connectionView()
- ->executeInTransaction("ConnectionModel::addConnection",
- [this, nodeMetaInfo, signalHandlerName, &rootModelNode] {
- ModelNode newNode = connectionView()
- ->createModelNode("QtQuick.Connections",
- nodeMetaInfo.majorVersion(),
- nodeMetaInfo.minorVersion());
+ ->executeInTransaction("ConnectionModel::addConnection", [&] {
+#ifdef QDS_USE_PROJECTSTORAGE
+ ModelNode newNode = connectionView()->createModelNode("Connections");
+#else
+ ModelNode newNode = connectionView()->createModelNode("QtQuick.Connections",
+ nodeMetaInfo.majorVersion(),
+ nodeMetaInfo.minorVersion());
+#endif
QString source = "console.log(\"clicked\")";
if (connectionView()->selectedModelNodes().size() == 1) {
@@ -407,7 +412,9 @@ void ConnectionModel::addConnection(const PropertyName &signalName)
selectProperty(newNode.signalHandlerProperty(signalHandlerName));
});
+#ifndef QDS_USE_PROJECTSTORAGE
}
+#endif
}
}
@@ -480,7 +487,7 @@ void ConnectionModel::setCurrentIndex(int i)
m_currentIndex = i;
emit currentIndexChanged();
}
- m_delegate->setCurrentRow(i);
+ m_delegate.setCurrentRow(i);
}
int ConnectionModel::currentIndex() const
@@ -506,7 +513,7 @@ void ConnectionModel::nodeAboutToBeRemoved(const ModelNode &removedNode)
if (selectedSignal.isValid()) {
ModelNode targetNode = getTargetNodeForConnection(selectedSignal.parentModelNode());
if (targetNode == removedNode) {
- emit m_delegate->popupShouldClose();
+ emit m_delegate.popupShouldClose();
}
}
}
@@ -517,9 +524,9 @@ void ConnectionModel::handleException()
resetModel();
}
-ConnectionModelBackendDelegate *ConnectionModel::delegate() const
+ConnectionModelBackendDelegate *ConnectionModel::delegate()
{
- return m_delegate;
+ return &m_delegate;
}
void ConnectionModel::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
@@ -676,11 +683,14 @@ QHash<int, QByteArray> ConnectionModel::roleNames() const
return roleNames;
}
-ConnectionModelBackendDelegate::ConnectionModelBackendDelegate(ConnectionModel *parent)
- : QObject(parent), m_signalDelegate(parent->connectionView()), m_okStatementDelegate(parent),
- m_koStatementDelegate(parent), m_conditionListModel(parent),
- m_propertyTreeModel(parent->connectionView()), m_propertyListProxyModel(&m_propertyTreeModel)
-
+ConnectionModelBackendDelegate::ConnectionModelBackendDelegate(ConnectionModel *model)
+ : m_signalDelegate(model->connectionView())
+ , m_okStatementDelegate(model)
+ , m_koStatementDelegate(model)
+ , m_conditionListModel(model)
+ , m_propertyTreeModel(model->connectionView())
+ , m_propertyListProxyModel(&m_propertyTreeModel)
+ , m_model(model)
{
connect(&m_signalDelegate, &PropertyTreeModelDelegate::commitData, this, [this] {
handleTargetChanged();
@@ -734,7 +744,7 @@ void ConnectionModelBackendDelegate::changeActionType(ActionType actionType)
{
QTC_ASSERT(actionType != ConnectionModelStatementDelegate::Custom, return );
- ConnectionModel *model = qobject_cast<ConnectionModel *>(parent());
+ ConnectionModel *model = m_model;
QTC_ASSERT(model, return );
QTC_ASSERT(model->connectionView()->isAttached(), return );
@@ -898,7 +908,7 @@ void ConnectionModelBackendDelegate::update()
m_propertyTreeModel.resetModel();
m_propertyListProxyModel.setRowAndInternalId(0, internalRootIndex);
- ConnectionModel *model = qobject_cast<ConnectionModel *>(parent());
+ ConnectionModel *model = m_model;
QTC_ASSERT(model, return );
if (!model->connectionView()->isAttached())
@@ -937,7 +947,7 @@ void ConnectionModelBackendDelegate::update()
void ConnectionModelBackendDelegate::jumpToCode()
{
- ConnectionModel *model = qobject_cast<ConnectionModel *>(parent());
+ ConnectionModel *model = m_model;
QTC_ASSERT(model, return );
QTC_ASSERT(model->connectionView()->isAttached(), return );
@@ -1050,7 +1060,7 @@ void ConnectionModelBackendDelegate::setupCondition()
void ConnectionModelBackendDelegate::setupHandlerAndStatements()
{
- ConnectionModel *model = qobject_cast<ConnectionModel *>(parent());
+ ConnectionModel *model = m_model;
QTC_ASSERT(model, return );
SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow());
@@ -1106,7 +1116,7 @@ void ConnectionModelBackendDelegate::setupHandlerAndStatements()
void ConnectionModelBackendDelegate::handleTargetChanged()
{
- ConnectionModel *model = qobject_cast<ConnectionModel *>(parent());
+ ConnectionModel *model = m_model;
QTC_ASSERT(model, return );
@@ -1179,8 +1189,8 @@ void ConnectionModelBackendDelegate::handleKOStatementChanged()
void ConnectionModelBackendDelegate::handleConditionChanged()
{
+ ConnectionModel *model = m_model;
- ConnectionModel *model = qobject_cast<ConnectionModel *>(parent());
QTC_ASSERT(model, return );
QTC_ASSERT(model->connectionView()->isAttached(), return );
@@ -1194,7 +1204,7 @@ void ConnectionModelBackendDelegate::handleConditionChanged()
void ConnectionModelBackendDelegate::commitNewSource(const QString &source)
{
- ConnectionModel *model = qobject_cast<ConnectionModel *>(parent());
+ ConnectionModel *model = m_model;
QTC_ASSERT(model, return );
@@ -1214,10 +1224,12 @@ void ConnectionModelBackendDelegate::commitNewSource(const QString &source)
static ConnectionEditorStatements::MatchedStatement emptyStatement;
-ConnectionModelStatementDelegate::ConnectionModelStatementDelegate(ConnectionModel *parent)
- : QObject(parent), m_functionDelegate(parent->connectionView()),
- m_lhsDelegate(parent->connectionView()), m_rhsAssignmentDelegate(parent->connectionView()),
- m_statement(emptyStatement), m_model(parent)
+ConnectionModelStatementDelegate::ConnectionModelStatementDelegate(ConnectionModel *model)
+ : m_functionDelegate(model->connectionView())
+ , m_lhsDelegate(model->connectionView())
+ , m_rhsAssignmentDelegate(model->connectionView())
+ , m_statement(emptyStatement)
+ , m_model(model)
{
m_functionDelegate.setPropertyType(PropertyTreeModel::SlotType);
@@ -1657,8 +1669,9 @@ QString ConnectionModelStatementDelegate::baseStateName() const
static ConnectionEditorStatements::MatchedCondition emptyCondition;
-ConditionListModel::ConditionListModel(ConnectionModel *parent)
- : m_connectionModel(parent), m_condition(emptyCondition)
+ConditionListModel::ConditionListModel(ConnectionModel *model)
+ : m_connectionModel(model)
+ , m_condition(emptyCondition)
{}
int ConditionListModel::rowCount(const QModelIndex & /*parent*/) const
@@ -2139,12 +2152,12 @@ ConnectionEditorStatements::ComparativeStatement ConditionListModel::toStatement
void QmlDesigner::ConnectionModel::modelAboutToBeDetached()
{
- emit m_delegate->popupShouldClose();
+ emit m_delegate.popupShouldClose();
}
void ConnectionModel::showPopup()
{
- emit m_delegate->popupShouldOpen();
+ emit m_delegate.popupShouldOpen();
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h
index d45014914d..017cf67684 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h
@@ -10,6 +10,8 @@
#include <QAbstractListModel>
#include <QStandardItemModel>
+#include <memory>
+
namespace QmlDesigner {
class AbstractProperty;
@@ -19,92 +21,7 @@ class SignalHandlerProperty;
class VariantProperty;
class ConnectionView;
-class ConnectionModelBackendDelegate;
-
-class ConnectionModel : public QStandardItemModel
-{
- Q_OBJECT
-
- Q_PROPERTY(ConnectionModelBackendDelegate *delegate READ delegate CONSTANT)
-
-public:
- Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
-
-public:
- enum ColumnRoles {
- TargetModelNodeRow = 0,
- TargetPropertyNameRow = 1,
- SourceRow = 2
- };
- enum UserRoles {
- InternalIdRole = Qt::UserRole + 1,
- TargetPropertyNameRole,
- TargetNameRole,
- ActionTypeRole
- };
- ConnectionModel(ConnectionView *parent = nullptr);
-
- Qt::ItemFlags flags(const QModelIndex &modelIndex) const override;
-
- void resetModel();
- SignalHandlerProperty signalHandlerPropertyForRow(int rowNumber) const;
- ConnectionView *connectionView() const;
-
- QStringList getSignalsForRow(int row) const;
- QStringList getflowActionTriggerForRow(int row) const;
- ModelNode getTargetNodeForConnection(const ModelNode &connection) const;
-
- void addConnection(const PropertyName &signalName = {});
-
- void bindingPropertyChanged(const BindingProperty &bindingProperty);
- void variantPropertyChanged(const VariantProperty &variantProperty);
- void abstractPropertyChanged(const AbstractProperty &abstractProperty);
-
- void deleteConnectionByRow(int currentRow);
- void removeRowFromTable(const SignalHandlerProperty &property);
-
- Q_INVOKABLE void add();
- Q_INVOKABLE void remove(int row);
-
- void setCurrentIndex(int i);
- int currentIndex() const;
-
- void selectProperty(const SignalHandlerProperty &property);
-
- void nodeAboutToBeRemoved(const ModelNode &removedNode);
- void modelAboutToBeDetached();
-
- void showPopup();
-
-signals:
- void currentIndexChanged();
-
-protected:
- void addModelNode(const ModelNode &modelNode);
- void addConnection(const ModelNode &modelNode);
- void addSignalHandler(const SignalHandlerProperty &bindingProperty);
- void removeModelNode(const ModelNode &modelNode);
- void removeConnection(const ModelNode &modelNode);
- void updateSource(int row);
- void updateSignalName(int rowNumber);
- void updateTargetNode(int rowNumber);
- void updateCustomData(QStandardItem *item, const SignalHandlerProperty &signalHandlerProperty);
- QStringList getPossibleSignalsForConnection(const ModelNode &connection) const;
-
- QHash<int, QByteArray> roleNames() const override;
-
-private:
- void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight);
- void handleException();
- ConnectionModelBackendDelegate *delegate() const;
-
-private:
- ConnectionView *m_connectionView;
- bool m_lock = false;
- QString m_exceptionError;
- ConnectionModelBackendDelegate *m_delegate = nullptr;
- int m_currentIndex = -1;
-};
+class ConnectionModel;
class ConditionListModel : public QAbstractListModel
{
@@ -125,7 +42,7 @@ public:
QString value;
};
- ConditionListModel(ConnectionModel *parent = nullptr);
+ ConditionListModel(ConnectionModel *model);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
@@ -136,8 +53,7 @@ public:
void setCondition(ConnectionEditorStatements::MatchedCondition &condition);
ConnectionEditorStatements::MatchedCondition &condition();
- static ConditionToken tokenFromConditionToken(
- const ConnectionEditorStatements::ConditionToken &token);
+ static ConditionToken tokenFromConditionToken(const ConnectionEditorStatements::ConditionToken &token);
static ConditionToken tokenFromComparativeStatement(
const ConnectionEditorStatements::ComparativeStatement &token);
@@ -196,7 +112,7 @@ class ConnectionModelStatementDelegate : public QObject
Q_OBJECT
public:
- explicit ConnectionModelStatementDelegate(ConnectionModel *parent = nullptr);
+ explicit ConnectionModelStatementDelegate(ConnectionModel *model);
enum ActionType { CallFunction, Assign, ChangeState, SetProperty, PrintMessage, Custom };
@@ -277,12 +193,11 @@ class ConnectionModelBackendDelegate : public QObject
Q_PROPERTY(PropertyListProxyModel *propertyListProxyModel READ propertyListProxyModel CONSTANT)
public:
- explicit ConnectionModelBackendDelegate(ConnectionModel *parent = nullptr);
+ explicit ConnectionModelBackendDelegate(ConnectionModel *model);
using ActionType = ConnectionModelStatementDelegate::ActionType;
- Q_INVOKABLE void changeActionType(
- QmlDesigner::ConnectionModelStatementDelegate::ActionType actionType);
+ Q_INVOKABLE void changeActionType(QmlDesigner::ConnectionModelStatementDelegate::ActionType actionType);
Q_INVOKABLE void addCondition();
Q_INVOKABLE void removeCondition();
@@ -349,6 +264,93 @@ private:
PropertyTreeModel m_propertyTreeModel;
PropertyListProxyModel m_propertyListProxyModel;
bool m_blockReflection = false;
+ QPointer<ConnectionModel> m_model = nullptr;
+};
+
+class ConnectionModel : public QStandardItemModel
+{
+ Q_OBJECT
+
+ Q_PROPERTY(ConnectionModelBackendDelegate *delegate READ delegate CONSTANT)
+
+public:
+ Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
+
+public:
+ enum ColumnRoles {
+ TargetModelNodeRow = 0,
+ TargetPropertyNameRow = 1,
+ SourceRow = 2
+ };
+ enum UserRoles {
+ InternalIdRole = Qt::UserRole + 1,
+ TargetPropertyNameRole,
+ TargetNameRole,
+ ActionTypeRole
+ };
+
+ ConnectionModel(ConnectionView *view);
+
+ Qt::ItemFlags flags(const QModelIndex &modelIndex) const override;
+
+ void resetModel();
+ SignalHandlerProperty signalHandlerPropertyForRow(int rowNumber) const;
+ ConnectionView *connectionView() const;
+
+ QStringList getSignalsForRow(int row) const;
+ QStringList getflowActionTriggerForRow(int row) const;
+ ModelNode getTargetNodeForConnection(const ModelNode &connection) const;
+
+ void addConnection(const PropertyName &signalName = {});
+
+ void bindingPropertyChanged(const BindingProperty &bindingProperty);
+ void variantPropertyChanged(const VariantProperty &variantProperty);
+ void abstractPropertyChanged(const AbstractProperty &abstractProperty);
+
+ void deleteConnectionByRow(int currentRow);
+ void removeRowFromTable(const SignalHandlerProperty &property);
+
+ Q_INVOKABLE void add();
+ Q_INVOKABLE void remove(int row);
+
+ void setCurrentIndex(int i);
+ int currentIndex() const;
+
+ void selectProperty(const SignalHandlerProperty &property);
+
+ void nodeAboutToBeRemoved(const ModelNode &removedNode);
+ void modelAboutToBeDetached();
+
+ void showPopup();
+
+signals:
+ void currentIndexChanged();
+
+protected:
+ void addModelNode(const ModelNode &modelNode);
+ void addConnection(const ModelNode &modelNode);
+ void addSignalHandler(const SignalHandlerProperty &bindingProperty);
+ void removeModelNode(const ModelNode &modelNode);
+ void removeConnection(const ModelNode &modelNode);
+ void updateSource(int row);
+ void updateSignalName(int rowNumber);
+ void updateTargetNode(int rowNumber);
+ void updateCustomData(QStandardItem *item, const SignalHandlerProperty &signalHandlerProperty);
+ QStringList getPossibleSignalsForConnection(const ModelNode &connection) const;
+
+ QHash<int, QByteArray> roleNames() const override;
+
+private:
+ void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight);
+ void handleException();
+ ConnectionModelBackendDelegate *delegate();
+
+private:
+ ConnectionView *m_connectionView;
+ bool m_lock = false;
+ QString m_exceptionError;
+ ConnectionModelBackendDelegate m_delegate;
+ int m_currentIndex = -1;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp
index ab010e3f35..786fd04f1b 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp
@@ -47,7 +47,10 @@ class ConnectionViewQuickWidget : public StudioQuickWidget
// Q_OBJECT carefull
public:
- ConnectionViewQuickWidget(ConnectionView *connectionEditorView)
+ ConnectionViewQuickWidget(ConnectionView *connectionEditorView,
+ ConnectionModel *connectionModel,
+ BindingModel *bindingModel,
+ DynamicPropertiesModel *dynamicPropertiesModel)
: m_connectionEditorView(connectionEditorView)
{
@@ -70,15 +73,11 @@ public:
qmlRegisterAnonymousType<DynamicPropertiesModelBackendDelegate>(
"ConnectionsEditorEditorBackend", 1);
- map->setProperties(
- {{"connectionModel", QVariant::fromValue(m_connectionEditorView->connectionModel())}});
+ map->setProperties({{"connectionModel", QVariant::fromValue(connectionModel)}});
- map->setProperties(
- {{"bindingModel", QVariant::fromValue(m_connectionEditorView->bindingModel())}});
+ map->setProperties({{"bindingModel", QVariant::fromValue(bindingModel)}});
- map->setProperties(
- {{"dynamicPropertiesModel",
- QVariant::fromValue(m_connectionEditorView->dynamicPropertiesModel())}});
+ map->setProperties({{"dynamicPropertiesModel", QVariant::fromValue(dynamicPropertiesModel)}});
qmlRegisterType<ConnectionModelBackendDelegate>("ConnectionsEditorEditorBackend",
1,
@@ -141,75 +140,92 @@ private:
QShortcut *m_qmlSourceUpdateShortcut;
};
+struct ConnectionView::ConnectionViewData
+{
+ ConnectionViewData(ConnectionView *view)
+ : connectionModel{view}
+ , bindingModel{view}
+ , dynamicPropertiesModel{false, view}
+ , backendModel{view}
+ , propertyTreeModel{view}
+ , connectionViewQuickWidget{Utils::makeUniqueObjectPtr<ConnectionViewQuickWidget>(
+ view, &connectionModel, &bindingModel, &dynamicPropertiesModel)}
+ {}
+
+ ConnectionModel connectionModel;
+ BindingModel bindingModel;
+ DynamicPropertiesModel dynamicPropertiesModel;
+ BackendModel backendModel;
+ PropertyTreeModel propertyTreeModel;
+ int currentIndex = 0;
+
+ // Ensure that QML is deleted first to avoid calling back to C++.
+ Utils::UniqueObjectPtr<ConnectionViewQuickWidget> connectionViewQuickWidget;
+};
+
ConnectionView::ConnectionView(ExternalDependenciesInterface &externalDependencies)
: AbstractView{externalDependencies}
- , m_connectionModel(new ConnectionModel(this))
- , m_bindingModel(new BindingModel(this))
- , m_dynamicPropertiesModel(new DynamicPropertiesModel(false, this))
- , m_backendModel(new BackendModel(this))
- , m_connectionViewQuickWidget(new ConnectionViewQuickWidget(this))
+ , d{std::make_unique<ConnectionViewData>(this)}
{}
ConnectionView::~ConnectionView()
{
- // Ensure that QML is deleted first to avoid calling back to C++.
- delete m_connectionViewQuickWidget.data();
}
void ConnectionView::modelAttached(Model *model)
{
AbstractView::modelAttached(model);
- bindingModel()->reset();
- dynamicPropertiesModel()->reset();
- connectionModel()->resetModel();
- backendModel()->resetModel();
+ d->bindingModel.reset();
+ d->dynamicPropertiesModel.reset();
+ d->connectionModel.resetModel();
+ d->backendModel.resetModel();
}
void ConnectionView::modelAboutToBeDetached(Model *model)
{
AbstractView::modelAboutToBeDetached(model);
- bindingModel()->reset();
- dynamicPropertiesModel()->reset();
- connectionModel()->resetModel();
- connectionModel()->modelAboutToBeDetached();
+ d->bindingModel.reset();
+ d->dynamicPropertiesModel.reset();
+ d->connectionModel.resetModel();
+ d->connectionModel.modelAboutToBeDetached();
}
void ConnectionView::nodeCreated(const ModelNode & /*createdNode*/)
{
- connectionModel()->resetModel();
+ d->connectionModel.resetModel();
}
void ConnectionView::nodeAboutToBeRemoved(const ModelNode &removedNode)
{
- connectionModel()->nodeAboutToBeRemoved(removedNode);
+ d->connectionModel.nodeAboutToBeRemoved(removedNode);
}
void ConnectionView::nodeRemoved(const ModelNode & /*removedNode*/,
const NodeAbstractProperty & /*parentProperty*/,
AbstractView::PropertyChangeFlags /*propertyChange*/)
{
- connectionModel()->resetModel();
+ d->connectionModel.resetModel();
}
void ConnectionView::nodeReparented(const ModelNode & /*node*/, const NodeAbstractProperty & /*newPropertyParent*/,
const NodeAbstractProperty & /*oldPropertyParent*/, AbstractView::PropertyChangeFlags /*propertyChange*/)
{
- connectionModel()->resetModel();
+ d->connectionModel.resetModel();
}
void ConnectionView::nodeIdChanged(const ModelNode & /*node*/, const QString & /*newId*/, const QString & /*oldId*/)
{
- connectionModel()->resetModel();
- bindingModel()->reset();
- dynamicPropertiesModel()->reset();
+ d->connectionModel.resetModel();
+ d->bindingModel.reset();
+ d->dynamicPropertiesModel.reset();
}
void ConnectionView::propertiesRemoved(const QList<AbstractProperty> &propertyList)
{
for (const AbstractProperty &property : propertyList) {
if (property.isDefaultProperty())
- connectionModel()->resetModel();
+ d->connectionModel.resetModel();
- dynamicPropertiesModel()->dispatchPropertyChanges(property);
+ d->dynamicPropertiesModel.dispatchPropertyChanges(property);
}
}
@@ -217,12 +233,12 @@ void ConnectionView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &p
{
for (const AbstractProperty &property : propertyList) {
if (property.isBindingProperty()) {
- bindingModel()->removeItem(property);
- dynamicPropertiesModel()->removeItem(property);
+ d->bindingModel.removeItem(property);
+ d->dynamicPropertiesModel.removeItem(property);
} else if (property.isVariantProperty()) {
- dynamicPropertiesModel()->removeItem(property);
+ d->dynamicPropertiesModel.removeItem(property);
} else if (property.isSignalHandlerProperty()) {
- connectionModel()->removeRowFromTable(property.toSignalHandlerProperty());
+ d->connectionModel.removeRowFromTable(property.toSignalHandlerProperty());
}
}
}
@@ -232,13 +248,13 @@ void ConnectionView::variantPropertiesChanged(const QList<VariantProperty> &prop
{
for (const VariantProperty &variantProperty : propertyList) {
if (variantProperty.isDynamic())
- dynamicPropertiesModel()->updateItem(variantProperty);
+ d->dynamicPropertiesModel.updateItem(variantProperty);
if (variantProperty.isDynamic() && variantProperty.parentModelNode().isRootNode())
- backendModel()->resetModel();
+ d->backendModel.resetModel();
- connectionModel()->variantPropertyChanged(variantProperty);
+ d->connectionModel.variantPropertyChanged(variantProperty);
- dynamicPropertiesModel()->dispatchPropertyChanges(variantProperty);
+ d->dynamicPropertiesModel.dispatchPropertyChanges(variantProperty);
}
}
@@ -246,15 +262,15 @@ void ConnectionView::bindingPropertiesChanged(const QList<BindingProperty> &prop
AbstractView::PropertyChangeFlags /*propertyChange*/)
{
for (const BindingProperty &bindingProperty : propertyList) {
- bindingModel()->updateItem(bindingProperty);
+ d->bindingModel.updateItem(bindingProperty);
if (bindingProperty.isDynamic())
- dynamicPropertiesModel()->updateItem(bindingProperty);
+ d->dynamicPropertiesModel.updateItem(bindingProperty);
if (bindingProperty.isDynamic() && bindingProperty.parentModelNode().isRootNode())
- backendModel()->resetModel();
+ d->backendModel.resetModel();
- connectionModel()->bindingPropertyChanged(bindingProperty);
+ d->connectionModel.bindingPropertyChanged(bindingProperty);
- dynamicPropertiesModel()->dispatchPropertyChanges(bindingProperty);
+ d->dynamicPropertiesModel.dispatchPropertyChanges(bindingProperty);
}
}
@@ -262,29 +278,29 @@ void ConnectionView::signalHandlerPropertiesChanged(const QVector<SignalHandlerP
AbstractView::PropertyChangeFlags /*propertyChange*/)
{
for (const SignalHandlerProperty &signalHandlerProperty : propertyList)
- connectionModel()->abstractPropertyChanged(signalHandlerProperty);
+ d->connectionModel.abstractPropertyChanged(signalHandlerProperty);
}
void ConnectionView::selectedNodesChanged(const QList<ModelNode> & selectedNodeList,
const QList<ModelNode> & /*lastSelectedNodeList*/)
{
- bindingModel()->reset(selectedNodeList);
- dynamicPropertiesModel()->reset();
+ d->bindingModel.reset(selectedNodeList);
+ d->dynamicPropertiesModel.reset();
}
void ConnectionView::importsChanged(const Imports & /*addedImports*/, const Imports & /*removedImports*/)
{
- backendModel()->resetModel();
+ d->backendModel.resetModel();
}
void ConnectionView::currentStateChanged(const ModelNode &)
{
- dynamicPropertiesModel()->reset();
+ d->dynamicPropertiesModel.reset();
}
WidgetInfo ConnectionView::widgetInfo()
{
- return createWidgetInfo(m_connectionViewQuickWidget.data(),
+ return createWidgetInfo(d->connectionViewQuickWidget.get(),
QLatin1String("ConnectionView"),
WidgetInfo::LeftPane,
0,
@@ -303,35 +319,35 @@ bool ConnectionView::isWidgetEnabled()
ConnectionModel *ConnectionView::connectionModel() const
{
- return m_connectionModel;
+ return &d->connectionModel;
}
BindingModel *ConnectionView::bindingModel() const
{
- return m_bindingModel;
+ return &d->bindingModel;
}
DynamicPropertiesModel *ConnectionView::dynamicPropertiesModel() const
{
- return m_dynamicPropertiesModel;
+ return &d->dynamicPropertiesModel;
}
BackendModel *ConnectionView::backendModel() const
{
- return m_backendModel;
+ return &d->backendModel;
}
int ConnectionView::currentIndex() const
{
- return m_currentIndex;
+ return d->currentIndex;
}
void ConnectionView::setCurrentIndex(int i)
{
- if (m_currentIndex == i)
+ if (d->currentIndex == i)
return;
- m_currentIndex = i;
+ d->currentIndex = i;
emit currentIndexChanged();
}
@@ -362,8 +378,8 @@ void ConnectionView::customNotification(const AbstractView *,
QTC_ASSERT(data.count() == 1, return );
const PropertyName name = data.first().toString().toUtf8();
- m_connectionModel->addConnection(name);
- m_connectionModel->showPopup();
+ d->connectionModel.addConnection(name);
+ d->connectionModel.showPopup();
} else if (identifier == EditConnectionNotification) {
QTC_ASSERT(nodeList.count() == 1, return );
ModelNode modelNode = nodeList.first();
@@ -374,8 +390,8 @@ void ConnectionView::customNotification(const AbstractView *,
QTC_ASSERT(modelNode.hasSignalHandlerProperty(name), return );
- m_connectionModel->selectProperty(modelNode.signalHandlerProperty(name));
- m_connectionModel->showPopup();
+ d->connectionModel.selectProperty(modelNode.signalHandlerProperty(name));
+ d->connectionModel.showPopup();
}
}
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h
index 12a53d43a9..1cf3aa26dd 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h
@@ -6,6 +6,8 @@
#include <abstractview.h>
#include <qmlitemnode.h>
+#include <utils/uniqueobjectptr.h>
+
#include <QPointer>
QT_BEGIN_NAMESPACE
@@ -81,15 +83,10 @@ signals:
void currentIndexChanged();
private:
- ConnectionModel *m_connectionModel;
- BindingModel *m_bindingModel;
- DynamicPropertiesModel *m_dynamicPropertiesModel;
- BackendModel *m_backendModel;
- PropertyTreeModel *m_propertyTreeModel;
- PropertyListProxyModel *m_propertyListProxyModel;
- int m_currentIndex = 0;
-
- QPointer<ConnectionViewQuickWidget> m_connectionViewQuickWidget;
+ struct ConnectionViewData;
+
+private:
+ std::unique_ptr<ConnectionViewData> d;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp
index 5cf90ca6fd..9fdd3daec3 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp
@@ -23,10 +23,9 @@
namespace QmlDesigner {
-DynamicPropertiesModel::DynamicPropertiesModel(bool exSelection, AbstractView *parent)
- : QStandardItemModel(parent)
- , m_view(parent)
- , m_delegate(new DynamicPropertiesModelBackendDelegate(this))
+DynamicPropertiesModel::DynamicPropertiesModel(bool exSelection, AbstractView *view)
+ : m_view(view)
+ , m_delegate(std::make_unique<DynamicPropertiesModelBackendDelegate>())
, m_explicitSelection(exSelection)
{
setHorizontalHeaderLabels(DynamicPropertiesItem::headerLabels());
@@ -39,7 +38,7 @@ AbstractView *DynamicPropertiesModel::view() const
DynamicPropertiesModelBackendDelegate *DynamicPropertiesModel::delegate() const
{
- return m_delegate;
+ return m_delegate.get();
}
int DynamicPropertiesModel::currentIndex() const
@@ -383,9 +382,8 @@ void DynamicPropertiesModel::setSelectedNode(const ModelNode &node)
reset();
}
-DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel *parent)
- : QObject(parent)
- , m_internalNodeId(std::nullopt)
+DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate()
+ : m_internalNodeId(std::nullopt)
{
m_type.setModel({"int", "bool", "var", "real", "string", "url", "color"});
connect(&m_type, &StudioQmlComboBoxBackend::activated, this, [this] { handleTypeChanged(); });
diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h
index 8ae4abf6d5..c2beed8730 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h
+++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h
@@ -7,8 +7,11 @@
#include "nodeinstanceglobal.h"
#include "studioquickwidget.h"
+
#include <QStandardItemModel>
+#include <memory>
+
namespace QmlDesigner {
class AbstractView;
@@ -28,7 +31,7 @@ public:
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
Q_PROPERTY(DynamicPropertiesModelBackendDelegate *delegate READ delegate CONSTANT)
- DynamicPropertiesModel(bool explicitSelection, AbstractView *parent);
+ DynamicPropertiesModel(bool explicitSelection, AbstractView *view);
AbstractView *view() const;
DynamicPropertiesModelBackendDelegate *delegate() const;
@@ -76,7 +79,7 @@ public:
private:
AbstractView *m_view = nullptr;
- DynamicPropertiesModelBackendDelegate *m_delegate = nullptr;
+ std::unique_ptr<DynamicPropertiesModelBackendDelegate> m_delegate;
int m_currentIndex = -1;
// TODO: Remove.
@@ -94,7 +97,7 @@ class DynamicPropertiesModelBackendDelegate : public QObject
Q_PROPERTY(StudioQmlTextBackend *value READ value CONSTANT)
public:
- DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel *parent = nullptr);
+ DynamicPropertiesModelBackendDelegate();
void update(const AbstractProperty &property);
diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp
index 4189496316..cefaf69506 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp
@@ -163,8 +163,8 @@ std::vector<PropertyName> properityLists()
return result;
}
-PropertyTreeModel::PropertyTreeModel(ConnectionView *parent)
- : QAbstractItemModel(parent), m_connectionView(parent)
+PropertyTreeModel::PropertyTreeModel(ConnectionView *view)
+ : m_connectionView(view)
{}
void PropertyTreeModel::resetModel()
@@ -883,7 +883,8 @@ QString PropertyListProxyModel::parentName() const
return m_treeModel->data(m_parentIndex, PropertyTreeModel::UserRoles::PropertyNameRole).toString();
}
-PropertyTreeModelDelegate::PropertyTreeModelDelegate(ConnectionView *parent) : m_model(parent)
+PropertyTreeModelDelegate::PropertyTreeModelDelegate(ConnectionView *view)
+ : m_model(view)
{
connect(&m_nameCombboBox, &StudioQmlComboBoxBackend::activated, this, [this] {
handleNameChanged();
diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h
index 07e1401c4a..39af485765 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h
+++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h
@@ -49,7 +49,7 @@ public:
BoolType
};
- PropertyTreeModel(ConnectionView *parent = nullptr);
+ PropertyTreeModel(ConnectionView *view);
void resetModel();
@@ -187,7 +187,7 @@ class PropertyTreeModelDelegate : public QObject
Q_PROPERTY(StudioQmlComboBoxBackend *id READ idCombboBox CONSTANT)
public:
- explicit PropertyTreeModelDelegate(ConnectionView *parent = nullptr);
+ explicit PropertyTreeModelDelegate(ConnectionView *view);
void setPropertyType(PropertyTreeModel::PropertyTypes type);
void setup(const QString &id, const QString &name, bool *nameExists = nullptr);
void setupNameComboBox(const QString &id, const QString &name, bool *nameExists);
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp
index 81be76ed32..61ae078ea8 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp
@@ -16,6 +16,7 @@
#include "qmldesignerconstants.h"
#include "qmlobjectnode.h"
#include "variantproperty.h"
+#include <utils3d.h>
#include <coreplugin/messagebox.h>
#include <enumeration.h>
@@ -119,7 +120,7 @@ WidgetInfo ContentLibraryView::widgetInfo()
[&] (const QmlDesigner::TypeName &type) {
// delete instances of the bundle material that is about to be unimported
executeInTransaction("ContentLibraryView::widgetInfo", [&] {
- ModelNode matLib = materialLibraryNode();
+ ModelNode matLib = Utils3D::materialLibraryNode(this);
if (!matLib.isValid())
return;
@@ -143,7 +144,7 @@ WidgetInfo ContentLibraryView::widgetInfo()
QTC_ASSERT(typeName.size(), return);
if (!m_bundleEffectTarget)
- m_bundleEffectTarget = active3DSceneNode();
+ m_bundleEffectTarget = Utils3D::active3DSceneNode(this);
QTC_ASSERT(m_bundleEffectTarget, return);
@@ -168,7 +169,7 @@ WidgetInfo ContentLibraryView::widgetInfo()
QTC_ASSERT(metaInfo.isValid(), return);
if (!m_bundleEffectTarget)
- m_bundleEffectTarget = active3DSceneNode();
+ m_bundleEffectTarget = Utils3D::active3DSceneNode(this);
QTC_ASSERT(m_bundleEffectTarget, return);
@@ -221,12 +222,12 @@ void ContentLibraryView::modelAttached(Model *model)
updateBundlesQuick3DVersion();
updateBundleMaterialsImportedState();
- const bool hasLibrary = materialLibraryNode().isValid();
+ const bool hasLibrary = Utils3D::materialLibraryNode(this).isValid();
m_widget->setHasMaterialLibrary(hasLibrary);
m_widget->setHasQuick3DImport(m_hasQuick3DImport);
m_widget->setIsQt6Project(externalDependencies().isQt6Project());
- m_sceneId = model->active3DSceneId();
+ m_sceneId = Utils3D::active3DSceneId(model);
m_widget->setHasActive3DScene(m_sceneId != -1);
m_widget->clearSearchFilter();
@@ -288,7 +289,7 @@ void ContentLibraryView::customNotification(const AbstractView *view,
return;
if (identifier == "drop_bundle_material") {
- ModelNode matLib = materialLibraryNode();
+ ModelNode matLib = Utils3D::materialLibraryNode(this);
if (!matLib.isValid())
return;
@@ -310,7 +311,7 @@ void ContentLibraryView::customNotification(const AbstractView *view,
m_draggedBundleMaterial = nullptr;
} else if (identifier == "drop_bundle_texture") {
- ModelNode matLib = materialLibraryNode();
+ ModelNode matLib = Utils3D::materialLibraryNode(this);
if (!matLib.isValid())
return;
@@ -322,7 +323,7 @@ void ContentLibraryView::customNotification(const AbstractView *view,
m_bundleEffectPos = data.size() == 1 ? data.first() : QVariant();
m_widget->effectsModel()->addInstance(m_draggedBundleEffect);
- m_bundleEffectTarget = nodeList.first() ? nodeList.first() : active3DSceneNode();
+ m_bundleEffectTarget = nodeList.first() ? nodeList.first() : Utils3D::active3DSceneNode(this);
}
}
@@ -341,6 +342,14 @@ void ContentLibraryView::nodeAboutToBeRemoved(const ModelNode &removedNode)
m_widget->setHasMaterialLibrary(false);
}
+void ContentLibraryView::auxiliaryDataChanged(const ModelNode &,
+ AuxiliaryDataKeyView type,
+ const QVariant &data)
+{
+ if (type == Utils3D::active3dSceneProperty)
+ active3DSceneChanged(data.toInt());
+}
+
#ifdef QDS_USE_PROJECTSTORAGE
void ContentLibraryView::applyBundleMaterialToDropTarget(const ModelNode &bundleMat,
const TypeName &typeName)
@@ -445,7 +454,7 @@ void ContentLibraryView::applyBundleMaterialToDropTarget(const ModelNode &bundle
ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &type)
{
- ModelNode matLib = materialLibraryNode();
+ ModelNode matLib = Utils3D::materialLibraryNode(this);
if (!matLib.isValid())
return {};
@@ -471,7 +480,7 @@ ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &t
#ifdef QDS_USE_PROJECTSTORAGE
ModelNode ContentLibraryView::createMaterial(const TypeName &typeName)
{
- ModelNode matLib = materialLibraryNode();
+ ModelNode matLib = Utils3D::materialLibraryNode(this);
if (!matLib.isValid() || !typeName.size())
return {};
@@ -495,7 +504,7 @@ ModelNode ContentLibraryView::createMaterial(const TypeName &typeName)
#else
ModelNode ContentLibraryView::createMaterial(const NodeMetaInfo &metaInfo)
{
- ModelNode matLib = materialLibraryNode();
+ ModelNode matLib = Utils3D::materialLibraryNode(this);
if (!matLib.isValid() || !metaInfo.isValid())
return {};
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h
index 36b579bf75..3b57b7a4ab 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h
@@ -33,7 +33,6 @@ public:
void modelAttached(Model *model) override;
void modelAboutToBeDetached(Model *model) override;
void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
- void active3DSceneChanged(qint32 sceneId) override;
void selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
const QList<ModelNode> &lastSelectedNodeList) override;
void customNotification(const AbstractView *view, const QString &identifier,
@@ -42,8 +41,12 @@ public:
const NodeAbstractProperty &oldPropertyParent,
AbstractView::PropertyChangeFlags propertyChange) override;
void nodeAboutToBeRemoved(const ModelNode &removedNode) override;
+ void auxiliaryDataChanged(const ModelNode &node,
+ AuxiliaryDataKeyView type,
+ const QVariant &data) override;
private:
+ void active3DSceneChanged(qint32 sceneId);
void updateBundleMaterialsImportedState();
void updateBundleEffectsImportedState();
void updateBundlesQuick3DVersion();
diff --git a/src/plugins/qmldesigner/components/createtexture.cpp b/src/plugins/qmldesigner/components/createtexture.cpp
index 8ef538bd2f..56056e3fd2 100644
--- a/src/plugins/qmldesigner/components/createtexture.cpp
+++ b/src/plugins/qmldesigner/components/createtexture.cpp
@@ -12,10 +12,12 @@
#include "nodemetainfo.h"
#include "qmlobjectnode.h"
#include "variantproperty.h"
+#include <utils3d.h>
#include <coreplugin/messagebox.h>
#include <QTimer>
+#include <QUrl>
namespace QmlDesigner {
@@ -73,7 +75,7 @@ ModelNode CreateTexture::createTextureFromImage(const Utils::FilePath &assetPat
if (mode != AddTextureMode::Texture && mode != AddTextureMode::LightProbe)
return {};
- ModelNode matLib = m_view->materialLibraryNode();
+ ModelNode matLib = Utils3D::materialLibraryNode(m_view);
if (!matLib.isValid())
return {};
@@ -81,16 +83,19 @@ ModelNode CreateTexture::createTextureFromImage(const Utils::FilePath &assetPat
QString textureSource = assetPath.relativePathFrom(DocumentManager::currentFilePath()).toString();
- ModelNode newTexNode = m_view->getTextureDefaultInstance(textureSource);
+ ModelNode newTexNode = Utils3D::getTextureDefaultInstance(textureSource, m_view);
if (!newTexNode.isValid()) {
+#ifdef QDS_USE_PROJECTSTORAGE
+ newTexNode = m_view->createModelNode("Texture");
+#else
newTexNode = m_view->createModelNode("QtQuick3D.Texture",
metaInfo.majorVersion(),
metaInfo.minorVersion());
-
+#endif
newTexNode.setIdWithoutRefactoring(m_view->model()->generateNewId(assetPath.baseName()));
VariantProperty sourceProp = newTexNode.variantProperty("source");
- sourceProp.setValue(textureSource);
+ sourceProp.setValue(QUrl(textureSource));
matLib.defaultNodeListProperty().reparentHere(newTexNode);
}
@@ -117,7 +122,7 @@ ModelNode CreateTexture::resolveSceneEnv(int sceneId)
if (selectedNode.metaInfo().isQtQuick3DSceneEnvironment()) {
activeSceneEnv = selectedNode;
} else if (sceneId != -1) {
- ModelNode activeScene = m_view->active3DSceneNode();
+ ModelNode activeScene = Utils3D::active3DSceneNode(m_view);
if (activeScene.isValid()) {
QmlObjectNode view3D;
if (activeScene.metaInfo().isQtQuick3DView3D()) {
diff --git a/src/plugins/qmldesigner/components/debugview/debugview.cpp b/src/plugins/qmldesigner/components/debugview/debugview.cpp
index 64f85915f1..e05001e477 100644
--- a/src/plugins/qmldesigner/components/debugview/debugview.cpp
+++ b/src/plugins/qmldesigner/components/debugview/debugview.cpp
@@ -95,7 +95,9 @@ void DebugView::nodeCreated(const ModelNode &createdNode)
message << createdNode.nodeSource();
message << "MetaInfo " << createdNode.metaInfo().isValid() << " ";
if (auto metaInfo = createdNode.metaInfo()) {
+#ifndef QDS_USE_PROJECTSTORAGE
message << metaInfo.majorVersion() << "." << metaInfo.minorVersion();
+#endif
message << ModelUtils::componentFilePath(createdNode);
}
log("::nodeCreated:", message.readAll());
@@ -283,6 +285,7 @@ void DebugView::selectedNodesChanged(const QList<ModelNode> &selectedNodes /*sel
message << lineBreak;
if (selectedNode.metaInfo().isValid()) {
+#ifndef QDS_USE_PROJECTSTORAGE
for (const NodeMetaInfo &metaInfo : selectedNode.metaInfo().selfAndPrototypes()) {
message << metaInfo.typeName() << " " << metaInfo.majorVersion() << "."
<< metaInfo.minorVersion() << lineBreak;
@@ -290,6 +293,7 @@ void DebugView::selectedNodesChanged(const QList<ModelNode> &selectedNodes /*sel
message << lineBreak;
message << selectedNode.metaInfo().typeName();
+#endif
message << lineBreak;
message << "Node Source" << selectedNode.nodeSource();
@@ -323,10 +327,11 @@ void DebugView::selectedNodesChanged(const QList<ModelNode> &selectedNodes /*sel
message << "Is valid object node: " << QmlItemNode::isValidQmlObjectNode(selectedNode);
message << lineBreak;
+#ifndef QDS_USE_PROJECTSTORAGE
message << "version: "
<< QString::number(selectedNode.metaInfo().majorVersion()) + "."
+ QString::number(selectedNode.metaInfo().minorVersion());
-
+#endif
message << lineBreak;
QmlItemNode itemNode(selectedNode);
diff --git a/src/plugins/qmldesigner/components/edit3d/bakelights.cpp b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp
index 35d59f07ef..ab1eb9b07b 100644
--- a/src/plugins/qmldesigner/components/edit3d/bakelights.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp
@@ -16,6 +16,7 @@
#include <nodemetainfo.h>
#include <plaintexteditmodifier.h>
#include <rewriterview.h>
+#include <utils3d.h>
#include <variantproperty.h>
#include <coreplugin/icore.h>
@@ -61,7 +62,7 @@ BakeLights::BakeLights(AbstractView *view)
: QObject(view)
, m_view(view)
{
- m_view3dId = resolveView3dId(view);
+ m_view3dId = Utils3D::activeView3dId(view);
if (m_view3dId.isEmpty()) {
// It should never get here, baking controls should be disabled in this case
@@ -78,38 +79,6 @@ BakeLights::~BakeLights()
cleanup();
}
-ModelNode BakeLights::resolveView3dNode(AbstractView *view)
-{
- if (!view || !view->model())
- return {};
-
- ModelNode activeView3D;
- ModelNode activeScene = view->active3DSceneNode();
-
- if (activeScene.isValid()) {
- if (activeScene.metaInfo().isQtQuick3DView3D()) {
- activeView3D = activeScene;
- } else {
- ModelNode sceneParent = activeScene.parentProperty().parentModelNode();
- if (sceneParent.metaInfo().isQtQuick3DView3D())
- activeView3D = sceneParent;
- }
- return activeView3D;
- }
-
- return {};
-}
-
-QString BakeLights::resolveView3dId(AbstractView *view)
-{
- ModelNode activeView3D = resolveView3dNode(view);
-
- if (activeView3D.isValid())
- return activeView3D.id();
-
- return {};
-}
-
void BakeLights::raiseDialog()
{
if (m_progressDialog)
@@ -142,9 +111,12 @@ void BakeLights::bakeLights()
m_rewriterView = new RewriterView{m_view->externalDependencies(), RewriterView::Amend};
m_nodeInstanceView = new NodeInstanceView{*m_connectionManager, m_view->externalDependencies()};
+#ifdef QDS_USE_PROJECTSTORAGE
+ m_model = m_view->model()->createModel("Item");
+#else
m_model = QmlDesigner::Model::create("QtQuick/Item", 2, 1);
m_model->setFileUrl(m_view->model()->fileUrl());
-
+#endif
// Take the current unsaved state of the main model and apply it to our copy
auto textDocument = std::make_unique<QTextDocument>(
m_view->model()->rewriterView()->textModifier()->textDocument()->toRawText());
@@ -251,7 +223,11 @@ void BakeLights::exposeModelsAndLights(const QString &nodeId)
}
RewriterView rewriter{m_view->externalDependencies(), RewriterView::Amend};
+#ifdef QDS_USE_PROJECTSTORAGE
+ auto compModel = m_view->model()->createModel("Item");
+#else
ModelPointer compModel = QmlDesigner::Model::create("QtQuick/Item", 2, 1);
+#endif
const Utils::FilePath compFilePath = Utils::FilePath::fromString(componentFilePath);
QByteArray src = compFilePath.fileContents().value();
diff --git a/src/plugins/qmldesigner/components/edit3d/bakelights.h b/src/plugins/qmldesigner/components/edit3d/bakelights.h
index 65f00acc7b..2b6848bd48 100644
--- a/src/plugins/qmldesigner/components/edit3d/bakelights.h
+++ b/src/plugins/qmldesigner/components/edit3d/bakelights.h
@@ -41,9 +41,6 @@ public:
bool manualMode() const;
void setManualMode(bool enabled);
- static ModelNode resolveView3dNode(AbstractView *view);
- static QString resolveView3dId(AbstractView *view);
-
signals:
void finished();
void progress(const QString &msg);
diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp
index e812bb6d00..95a260c26f 100644
--- a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp
@@ -15,6 +15,8 @@
#include "qmlobjectnode.h"
#include "variantproperty.h"
+#include <utils3d.h>
+
#include <utils/expected.h>
#include <utils/filepath.h>
#include <utils/qtcassert.h>
@@ -140,7 +142,7 @@ bool BakeLightsDataModel::reset()
beginResetModel();
m_dataList.clear();
- m_view3dNode = BakeLights::resolveView3dNode(m_view);
+ m_view3dNode = Utils3D::activeView3dNode(m_view);
// Find all models and lights in active View3D
QList<ModelNode> nodes = m_view3dNode.allSubModelNodes();
@@ -353,10 +355,14 @@ void BakeLightsDataModel::apply()
if (node.hasBindingProperty(propName))
blmNode = node.bindingProperty(propName).resolveToModelNode();
if (!blmNode.isValid() && data.enabled) {
+#ifdef QDS_USE_PROJECTSTORAGE
+ blmNode = m_view->createModelNode("BakedLightmap");
+#else
NodeMetaInfo metaInfo = m_view->model()->qtQuick3DBakedLightmapMetaInfo();
blmNode = m_view->createModelNode("QtQuick3D.BakedLightmap",
metaInfo.majorVersion(),
metaInfo.minorVersion());
+#endif
QString idPart;
if (data.aliasProp.isEmpty())
idPart = data.id;
diff --git a/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp
new file mode 100644
index 0000000000..76560ac192
--- /dev/null
+++ b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp
@@ -0,0 +1,192 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "cameraspeedconfiguration.h"
+
+#include "designersettings.h"
+#include "edit3dview.h"
+#include "edit3dviewconfig.h"
+
+#include <coreplugin/icore.h>
+
+#include <utils/environment.h>
+
+#include <QCursor>
+#include <QGuiApplication>
+#include <QQmlContext>
+#include <QQmlEngine>
+#include <QQuickView>
+#include <QTimer>
+#include <QVariant>
+
+namespace QmlDesigner {
+
+static QString propertyEditorResourcesPath()
+{
+#ifdef SHARE_QML_PATH
+ if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
+ return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources";
+#endif
+ return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString();
+}
+
+static QString qmlSourcesPath()
+{
+#ifdef SHARE_QML_PATH
+ if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
+ return QLatin1String(SHARE_QML_PATH) + "/edit3dQmlSource";
+#endif
+ return Core::ICore::resourcePath("qmldesigner/edit3dQmlSource").toString();
+}
+
+CameraSpeedConfiguration::CameraSpeedConfiguration(Edit3DView *view)
+ : QObject(view)
+ , m_view(view)
+{
+}
+
+CameraSpeedConfiguration::~CameraSpeedConfiguration()
+{
+ delete m_configDialog;
+ restoreCursor();
+}
+
+void CameraSpeedConfiguration::apply()
+{
+ if (m_changes && !m_view.isNull())
+ m_view->setCameraSpeedAuxData(speed(), multiplier());
+ deleteLater();
+}
+
+void CameraSpeedConfiguration::resetDefaults()
+{
+ setSpeed(defaultSpeed);
+ setMultiplier(defaultMultiplier);
+}
+
+void CameraSpeedConfiguration::hideCursor()
+{
+ if (QGuiApplication::overrideCursor())
+ return;
+
+ QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
+
+ if (QWindow *w = QGuiApplication::focusWindow())
+ m_lastPos = QCursor::pos(w->screen());
+}
+
+void CameraSpeedConfiguration::restoreCursor()
+{
+ if (!QGuiApplication::overrideCursor())
+ return;
+
+ QGuiApplication::restoreOverrideCursor();
+
+ if (QWindow *w = QGuiApplication::focusWindow())
+ QCursor::setPos(w->screen(), m_lastPos);
+}
+
+void CameraSpeedConfiguration::holdCursorInPlace()
+{
+ if (!QGuiApplication::overrideCursor())
+ return;
+
+ if (QWindow *w = QGuiApplication::focusWindow())
+ QCursor::setPos(w->screen(), m_lastPos);
+}
+
+int CameraSpeedConfiguration::devicePixelRatio()
+{
+ if (QWindow *w = QGuiApplication::focusWindow())
+ return w->devicePixelRatio();
+
+ return 1;
+}
+
+void CameraSpeedConfiguration::showConfigDialog(const QPoint &pos)
+{
+ double speed, multiplier;
+ m_view->getCameraSpeedAuxData(speed, multiplier);
+
+ setSpeed(speed);
+ setMultiplier(multiplier);
+
+ m_changes = false;
+
+ if (!m_configDialog) {
+ // Show non-modal progress dialog with cancel button
+ QString path = qmlSourcesPath() + "/CameraSpeedConfigurationDialog.qml";
+
+ m_configDialog = new QQuickView;
+ m_configDialog->setResizeMode(QQuickView::SizeViewToRootObject);
+ m_configDialog->setFlags(Qt::Dialog | Qt::FramelessWindowHint);
+ m_configDialog->setModality(Qt::NonModal);
+ m_configDialog->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
+
+ m_configDialog->rootContext()->setContextObject(this);
+ m_configDialog->setSource(QUrl::fromLocalFile(path));
+ m_configDialog->installEventFilter(this);
+
+ QPoint finalPos = pos;
+ finalPos.setX(pos.x() - m_configDialog->size().width() / 2);
+ finalPos.setY(pos.y());
+ m_configDialog->setPosition(finalPos);
+ }
+
+ m_configDialog->show();
+}
+
+void CameraSpeedConfiguration::setSpeed(double value)
+{
+ if (value != m_speed) {
+ m_speed = value;
+ m_changes = true;
+ emit speedChanged();
+ emit totalSpeedChanged();
+ }
+}
+
+void CameraSpeedConfiguration::setMultiplier(double value)
+{
+ if (value != m_multiplier) {
+ m_multiplier = value;
+ m_changes = true;
+ emit multiplierChanged();
+ emit totalSpeedChanged();
+ }
+}
+
+void CameraSpeedConfiguration::asyncClose()
+{
+ QTimer::singleShot(0, this, [this] {
+ if (!m_configDialog.isNull() && m_configDialog->isVisible())
+ m_configDialog->close();
+ });
+}
+
+void CameraSpeedConfiguration::cancel()
+{
+ if (!m_configDialog.isNull() && m_configDialog->isVisible())
+ m_configDialog->close();
+
+ deleteLater();
+}
+
+bool CameraSpeedConfiguration::eventFilter(QObject *obj, QEvent *event)
+{
+ if (obj == m_configDialog) {
+ if (event->type() == QEvent::FocusOut) {
+ asyncClose();
+ } else if (event->type() == QEvent::KeyPress) {
+ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
+ if (keyEvent->key() == Qt::Key_Escape)
+ asyncClose();
+ } else if (event->type() == QEvent::Close) {
+ apply();
+ }
+ }
+
+ return QObject::eventFilter(obj, event);
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h
new file mode 100644
index 0000000000..55256890cb
--- /dev/null
+++ b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h
@@ -0,0 +1,76 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+#pragma once
+
+#include <auxiliarydata.h>
+
+#include <QObject>
+#include <QPoint>
+#include <QPointer>
+
+QT_BEGIN_NAMESPACE
+class QQuickView;
+QT_END_NAMESPACE
+
+namespace QmlDesigner {
+
+class Edit3DView;
+
+inline constexpr AuxiliaryDataKeyView edit3dCameraSpeedDocProperty{AuxiliaryDataType::Document,
+ "cameraSpeed3d"};
+inline constexpr AuxiliaryDataKeyView edit3dCameraSpeedMultiplierDocProperty{AuxiliaryDataType::Document,
+ "cameraSpeed3dMultiplier"};
+inline constexpr AuxiliaryDataKeyView edit3dCameraTotalSpeedProperty{AuxiliaryDataType::NodeInstanceAuxiliary,
+ "cameraTotalSpeed3d"};
+
+class CameraSpeedConfiguration : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(double speed READ speed WRITE setSpeed NOTIFY speedChanged)
+ Q_PROPERTY(double multiplier READ multiplier WRITE setMultiplier NOTIFY multiplierChanged)
+ Q_PROPERTY(double totalSpeed READ totalSpeed NOTIFY totalSpeedChanged)
+
+public:
+ CameraSpeedConfiguration(Edit3DView *view);
+ ~CameraSpeedConfiguration();
+
+ Q_INVOKABLE void resetDefaults();
+ Q_INVOKABLE void hideCursor();
+ Q_INVOKABLE void restoreCursor();
+ Q_INVOKABLE void holdCursorInPlace();
+ Q_INVOKABLE int devicePixelRatio();
+
+ void cancel();
+ void apply();
+
+ void showConfigDialog(const QPoint &pos);
+
+ void setSpeed(double value);
+ double speed() const { return m_speed; }
+ void setMultiplier(double value);
+ double multiplier() const { return m_multiplier; }
+ double totalSpeed() const { return m_speed * m_multiplier; }
+
+ constexpr static double defaultSpeed = 25.;
+ constexpr static double defaultMultiplier = 1.;
+
+signals:
+ void speedChanged();
+ void multiplierChanged();
+ void totalSpeedChanged();
+
+protected:
+ bool eventFilter(QObject *obj, QEvent *event) override;
+
+private:
+ void asyncClose();
+
+ QPointer<QQuickView> m_configDialog;
+ QPointer<Edit3DView> m_view;
+ double m_speed = 0.;
+ double m_multiplier = 0.;
+ bool m_changes = false;
+ QPoint m_lastPos;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp
index add61d195d..b3d140bc88 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp
@@ -9,6 +9,8 @@
#include "qmldesignerconstants.h"
#include "seekerslider.h"
+#include <utils3d.h>
+
#include <utils/algorithm.h>
namespace QmlDesigner {
@@ -149,7 +151,7 @@ bool Edit3DBakeLightsAction::isVisible(const SelectionContext &) const
bool Edit3DBakeLightsAction::isEnabled(const SelectionContext &) const
{
return m_view->isBakingLightsSupported()
- && !BakeLights::resolveView3dId(m_view).isEmpty();
+ && !Utils3D::activeView3dId(m_view).isEmpty();
}
}
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp
index 6afe4fa816..2e8ef8304f 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp
@@ -17,6 +17,8 @@
#include <qmldesignerplugin.h>
#include <qmldesignerconstants.h>
+#include <QApplication>
+#include <QDateTime>
#include <QFileInfo>
#include <QPainter>
#include <QQuickWidget>
@@ -77,10 +79,67 @@ QWidget *Edit3DCanvas::busyIndicator() const
return m_busyIndicator;
}
+void Edit3DCanvas::setFlyMode(bool enabled, const QPoint &pos)
+{
+ if (m_flyMode == enabled)
+ return;
+
+ m_flyMode = enabled;
+
+ if (enabled) {
+ m_flyModeStartTime = QDateTime::currentMSecsSinceEpoch();
+
+ // Mouse cursor will be hidden in the flight mode
+ QApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
+
+ m_flyModeStartCursorPos = pos;
+ m_flyModeFirstUpdate = true;
+
+ // Hide cursor on the middle of the active split to make the wheel work during flight mode.
+ // We can't rely on current activeSplit value, as mouse press to enter flight mode can change the
+ // active split, so hide the cursor based on its current location.
+ QPoint center = mapToGlobal(QPoint(width() / 2, height() / 2));
+ if (m_parent->view()->isSplitView()) {
+ if (pos.x() <= center.x()) {
+ if (pos.y() <= center.y())
+ m_hiddenCursorPos = mapToGlobal(QPoint(width() / 4, height() / 4));
+ else
+ m_hiddenCursorPos = mapToGlobal(QPoint(width() / 4, (height() / 4) * 3));
+ } else {
+ if (pos.y() <= center.y())
+ m_hiddenCursorPos = mapToGlobal(QPoint((width() / 4) * 3, height() / 4));
+ else
+ m_hiddenCursorPos = mapToGlobal(QPoint((width() / 4) * 3, (height() / 4) * 3));
+ }
+ } else {
+ m_hiddenCursorPos = center;
+ }
+
+ QCursor::setPos(m_hiddenCursorPos);
+ } else {
+ QCursor::setPos(m_flyModeStartCursorPos);
+
+ if (QApplication::overrideCursor())
+ QApplication::restoreOverrideCursor();
+
+ if (m_contextMenuPending && (QDateTime::currentMSecsSinceEpoch() - m_flyModeStartTime) < 500)
+ m_parent->view()->showContextMenu();
+
+ m_contextMenuPending = false;
+ m_flyModeStartTime = 0;
+ }
+
+ m_parent->view()->setFlyMode(enabled);
+}
+
void Edit3DCanvas::mousePressEvent(QMouseEvent *e)
{
- if (e->button() == Qt::RightButton && e->modifiers() == Qt::NoModifier)
+ m_contextMenuPending = false;
+ if (!m_flyMode && e->modifiers() == Qt::NoModifier && e->buttons() == Qt::RightButton) {
+ setFlyMode(true, e->globalPos());
m_parent->view()->startContextMenu(e->pos());
+ m_contextMenuPending = true;
+ }
m_parent->view()->sendInputEvent(e);
QWidget::mousePressEvent(e);
@@ -88,6 +147,8 @@ void Edit3DCanvas::mousePressEvent(QMouseEvent *e)
void Edit3DCanvas::mouseReleaseEvent(QMouseEvent *e)
{
+ if ((e->buttons() & Qt::RightButton) == Qt::NoButton)
+ setFlyMode(false);
m_parent->view()->sendInputEvent(e);
QWidget::mouseReleaseEvent(e);
}
@@ -100,8 +161,29 @@ void Edit3DCanvas::mouseDoubleClickEvent(QMouseEvent *e)
void Edit3DCanvas::mouseMoveEvent(QMouseEvent *e)
{
- m_parent->view()->sendInputEvent(e);
+ if (!m_flyMode)
+ m_parent->view()->sendInputEvent(e);
+
QWidget::mouseMoveEvent(e);
+
+ if (m_flyMode && e->globalPos() != m_hiddenCursorPos) {
+ if (!m_flyModeFirstUpdate) {
+ // We notify explicit camera rotation need for puppet rather than rely in mouse events,
+ // as mouse isn't grabbed on puppet side and can't handle fast movements that go out of
+ // edit camera mouse area. This also simplifies split view handling.
+ QPointF diff = m_hiddenCursorPos - e->globalPos();
+ if (e->buttons() == (Qt::LeftButton | Qt::RightButton)) {
+ m_parent->view()->emitView3DAction(View3DActionType::EditCameraMove,
+ QVector3D{float(-diff.x()), float(-diff.y()), 0.f});
+ } else {
+ m_parent->view()->emitView3DAction(View3DActionType::EditCameraRotation, diff / 6.);
+ }
+ } else {
+ // Skip first move to avoid undesirable jump occasionally when initiating flight mode
+ m_flyModeFirstUpdate = false;
+ }
+ QCursor::setPos(m_hiddenCursorPos);
+ }
}
void Edit3DCanvas::wheelEvent(QWheelEvent *e)
@@ -112,13 +194,15 @@ void Edit3DCanvas::wheelEvent(QWheelEvent *e)
void Edit3DCanvas::keyPressEvent(QKeyEvent *e)
{
- m_parent->view()->sendInputEvent(e);
+ if (!e->isAutoRepeat())
+ m_parent->view()->sendInputEvent(e);
QWidget::keyPressEvent(e);
}
void Edit3DCanvas::keyReleaseEvent(QKeyEvent *e)
{
- m_parent->view()->sendInputEvent(e);
+ if (!e->isAutoRepeat())
+ m_parent->view()->sendInputEvent(e);
QWidget::keyReleaseEvent(e);
}
@@ -146,6 +230,10 @@ void Edit3DCanvas::focusOutEvent(QFocusEvent *focusEvent)
{
QmlDesignerPlugin::emitUsageStatisticsTime(Constants::EVENT_3DEDITOR_TIME,
m_usageTimer.elapsed());
+
+ setFlyMode(false);
+ m_parent->view()->emitView3DAction(View3DActionType::EditCameraStopAllMoves, {});
+
QWidget::focusOutEvent(focusEvent);
}
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h
index 810e246f8a..39207554a7 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h
@@ -2,11 +2,12 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
-#include <QtWidgets/qwidget.h>
-#include <QtGui/qimage.h>
-#include <QtGui/qevent.h>
-#include <QtCore/qelapsedtimer.h>
-#include <QtCore/qpointer.h>
+#include <QEvent>
+#include <QElapsedTimer>
+#include <QImage>
+#include <QPoint>
+#include <QPointer>
+#include <QWidget>
namespace QmlDesigner {
@@ -25,6 +26,8 @@ public:
QImage renderImage() const;
void setOpacity(qreal opacity);
QWidget *busyIndicator() const;
+ void setFlyMode(bool enabled, const QPoint &pos = {});
+ bool isFlyMode() const { return m_flyMode; }
protected:
void mousePressEvent(QMouseEvent *e) override;
@@ -50,6 +53,12 @@ private:
QElapsedTimer m_usageTimer;
qreal m_opacity = 1.0;
QWidget *m_busyIndicator = nullptr;
+ bool m_flyMode = false;
+ QPoint m_flyModeStartCursorPos;
+ QPoint m_hiddenCursorPos;
+ qint64 m_flyModeStartTime = 0;
+ bool m_flyModeFirstUpdate = false;
+ bool m_contextMenuPending = false;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
index 12647f014c..911ee1fb15 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
@@ -5,6 +5,7 @@
#include "backgroundcolorselection.h"
#include "bakelights.h"
+#include "cameraspeedconfiguration.h"
#include "designeractionmanager.h"
#include "designericons.h"
#include "designersettings.h"
@@ -23,7 +24,9 @@
#include "seekerslider.h"
#include "snapconfiguration.h"
+#include <auxiliarydataproperties.h>
#include <model/modelutils.h>
+#include <utils3d.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagebox.h>
@@ -143,7 +146,7 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
if (sceneState.contains(sceneKey)) {
qint32 newActiveScene = sceneState[sceneKey].value<qint32>();
edit3DWidget()->canvas()->updateActiveScene(newActiveScene);
- model()->setActive3DSceneId(newActiveScene);
+ setActive3DSceneId(newActiveScene);
updateAlignActionStates();
}
@@ -238,7 +241,7 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
bool desiredSyncValue = false;
if (sceneState.contains(syncEnvBgKey))
desiredSyncValue = sceneState[syncEnvBgKey].toBool();
- ModelNode checkNode = active3DSceneNode();
+ ModelNode checkNode = Utils3D::active3DSceneNode(this);
const bool activeSceneValid = checkNode.isValid();
while (checkNode.isValid()) {
@@ -268,6 +271,8 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
selectionContext.setUpdateMode(SelectionContext::UpdateMode::Fast);
if (m_bakeLightsAction)
m_bakeLightsAction->currentContextChanged(selectionContext);
+
+ syncCameraSpeedToNewView();
}
void Edit3DView::modelAttached(Model *model)
@@ -385,7 +390,7 @@ void Edit3DView::updateAlignActionStates()
{
bool enabled = false;
- ModelNode activeScene = active3DSceneNode();
+ ModelNode activeScene = Utils3D::active3DSceneNode(this);
if (activeScene.isValid()) {
const QList<ModelNode> nodes = activeScene.allSubModelNodes();
enabled = ::Utils::anyOf(nodes, [](const ModelNode &node) {
@@ -397,6 +402,11 @@ void Edit3DView::updateAlignActionStates()
m_alignViewAction->action()->setEnabled(enabled);
}
+void Edit3DView::setActive3DSceneId(qint32 sceneId)
+{
+ rootModelNode().setAuxiliaryData(Utils3D::active3dSceneProperty, sceneId);
+}
+
void Edit3DView::modelAboutToBeDetached(Model *model)
{
m_isBakingLightsSupported = false;
@@ -427,8 +437,19 @@ void Edit3DView::customNotification([[maybe_unused]] const AbstractView *view,
[[maybe_unused]] const QList<ModelNode> &nodeList,
[[maybe_unused]] const QList<QVariant> &data)
{
- if (identifier == "asset_import_update")
+ if (identifier == "asset_import_update") {
resetPuppet();
+ } else if (identifier == "pick_3d_node_from_2d_scene" && data.size() == 1 && nodeList.size() == 1) {
+ // Pick via 2D view, data has pick coordinates in main scene coordinates
+ QTimer::singleShot(0, this, [=, self = QPointer{this}]() {
+ if (!self)
+ return;
+
+ self->emitView3DAction(View3DActionType::GetNodeAtMainScenePos,
+ QVariantList{data[0], nodeList[0].internalId()});
+ self->m_nodeAtPosReqType = NodeAtPosReqType::MainScenePick;
+ });
+ }
}
/**
@@ -442,11 +463,13 @@ void Edit3DView::customNotification([[maybe_unused]] const AbstractView *view,
void Edit3DView::nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos3d)
{
if (m_nodeAtPosReqType == NodeAtPosReqType::ContextMenu) {
- // Make sure right-clicked item is selected. Due to a bug in puppet side right-clicking an item
- // while the context-menu is shown doesn't select the item.
- if (modelNode.isValid() && !modelNode.isSelected())
- setSelectedModelNode(modelNode);
- m_edit3DWidget->showContextMenu(m_contextMenuPos, modelNode, pos3d);
+ m_contextMenuPos3D = pos3d;
+ if (m_edit3DWidget->canvas()->isFlyMode()) {
+ m_contextMenuPendingNode = modelNode;
+ } else {
+ m_nodeAtPosReqType = NodeAtPosReqType::None;
+ showContextMenu();
+ }
} else if (m_nodeAtPosReqType == NodeAtPosReqType::ComponentDrop) {
ModelNode createdNode;
executeInTransaction(__FUNCTION__, [&] {
@@ -474,6 +497,10 @@ void Edit3DView::nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos
bool isModel = modelNode.metaInfo().isQtQuick3DModel();
if (!m_droppedFile.isEmpty() && isModel)
emitCustomNotification("apply_asset_to_model3D", {modelNode}, {m_droppedFile}); // To MaterialBrowserView
+ } else if (m_nodeAtPosReqType == NodeAtPosReqType::MainScenePick) {
+ if (modelNode.isValid())
+ setSelectedModelNode(modelNode);
+ emitView3DAction(View3DActionType::AlignViewToCamera, true);
}
m_droppedModelNode = {};
@@ -672,6 +699,54 @@ QPoint Edit3DView::resolveToolbarPopupPos(Edit3DAction *action) const
return pos;
}
+void Edit3DView::showContextMenu()
+{
+ // If request for context menu is still pending, skip for now
+ if (m_nodeAtPosReqType == NodeAtPosReqType::ContextMenu)
+ return;
+
+ if (m_contextMenuPendingNode.isValid()) {
+ if (!m_contextMenuPendingNode.isSelected())
+ setSelectedModelNode(m_contextMenuPendingNode);
+ } else {
+ clearSelectedModelNodes();
+ }
+
+ m_edit3DWidget->showContextMenu(m_contextMenuPosMouse, m_contextMenuPendingNode, m_contextMenuPos3D);
+ m_contextMenuPendingNode = {};
+}
+
+void Edit3DView::setFlyMode(bool enabled)
+{
+ emitView3DAction(View3DActionType::FlyModeToggle, enabled);
+
+ // Disable any actions with conflicting hotkeys
+ if (enabled) {
+ m_flyModeDisabledActions.clear();
+ const QList<QKeySequence> controlKeys = { Qt::Key_W, Qt::Key_A, Qt::Key_S,
+ Qt::Key_D, Qt::Key_Q, Qt::Key_E,
+ Qt::Key_Up, Qt::Key_Down, Qt::Key_Left,
+ Qt::Key_Right, Qt::Key_PageDown, Qt::Key_PageUp};
+ for (auto i = m_edit3DActions.cbegin(), end = m_edit3DActions.cend(); i != end; ++i) {
+ for (const QKeySequence &controlKey : controlKeys) {
+ if (Core::Command *cmd = m_edit3DWidget->actionToCommandHash().value(i.value()->action())) {
+ if (cmd->keySequence().matches(controlKey) == QKeySequence::ExactMatch) {
+ if (i.value()->action()->isEnabled()) {
+ m_flyModeDisabledActions.append(i.value());
+ i.value()->action()->setEnabled(false);
+ }
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ for (Edit3DAction *action : std::as_const(m_flyModeDisabledActions))
+ action->action()->setEnabled(true);
+ m_flyModeDisabledActions.clear();
+ }
+}
+
void Edit3DView::syncSnapAuxPropsToSettings()
{
if (!model())
@@ -697,6 +772,48 @@ void Edit3DView::syncSnapAuxPropsToSettings()
Edit3DViewConfig::load(DesignerSettingsKey::EDIT3DVIEW_SNAP_SCALE_INTERVAL));
}
+void Edit3DView::setCameraSpeedAuxData(double speed, double multiplier)
+{
+ ModelNode node = Utils3D::active3DSceneNode(this);
+ node.setAuxiliaryData(edit3dCameraSpeedDocProperty, speed);
+ node.setAuxiliaryData(edit3dCameraSpeedMultiplierDocProperty, multiplier);
+ rootModelNode().setAuxiliaryData(edit3dCameraTotalSpeedProperty, (speed * multiplier));
+ m_previousCameraSpeed = speed;
+ m_previousCameraMultiplier = multiplier;
+}
+
+void Edit3DView::getCameraSpeedAuxData(double &speed, double &multiplier)
+{
+ ModelNode node = Utils3D::active3DSceneNode(this);
+ auto speedProp = node.auxiliaryData(edit3dCameraSpeedDocProperty);
+ auto multProp = node.auxiliaryData(edit3dCameraSpeedMultiplierDocProperty);
+ speed = speedProp ? speedProp->toDouble() : CameraSpeedConfiguration::defaultSpeed;
+ multiplier = multProp ? multProp->toDouble() : CameraSpeedConfiguration::defaultMultiplier;
+}
+
+void Edit3DView::syncCameraSpeedToNewView()
+{
+ // Camera speed is inherited from previous active view if explicit values have not been
+ // stored for the currently active view
+ ModelNode node = Utils3D::active3DSceneNode(this);
+ auto speedProp = node.auxiliaryData(edit3dCameraSpeedDocProperty);
+ auto multProp = node.auxiliaryData(edit3dCameraSpeedMultiplierDocProperty);
+ double speed = CameraSpeedConfiguration::defaultSpeed;
+ double multiplier = CameraSpeedConfiguration::defaultMultiplier;
+
+ if (!speedProp || !multProp) {
+ if (m_previousCameraSpeed > 0 && m_previousCameraMultiplier > 0) {
+ speed = m_previousCameraSpeed;
+ multiplier = m_previousCameraMultiplier;
+ }
+ } else {
+ speed = speedProp->toDouble();
+ multiplier = multProp->toDouble();
+ }
+
+ setCameraSpeedAuxData(speed, multiplier);
+}
+
const QList<Edit3DView::SplitToolState> &Edit3DView::splitToolStates() const
{
return m_splitToolStates;
@@ -715,6 +832,11 @@ int Edit3DView::activeSplit() const
return m_activeSplit;
}
+bool Edit3DView::isSplitView() const
+{
+ return m_splitViewAction->action()->isChecked();
+}
+
void Edit3DView::createEdit3DActions()
{
m_selectionModeAction = std::make_unique<Edit3DAction>(
@@ -843,7 +965,7 @@ void Edit3DView::createEdit3DActions()
QmlDesigner::Constants::EDIT3D_EDIT_SHOW_SELECTION_BOX,
View3DActionType::ShowSelectionBox,
QCoreApplication::translate("ShowSelectionBoxAction", "Show Selection Boxes"),
- QKeySequence(Qt::Key_S),
+ QKeySequence(Qt::Key_B),
true,
true,
QIcon(),
@@ -1084,6 +1206,30 @@ void Edit3DView::createEdit3DActions()
toolbarIcon(DesignerIcons::SplitViewIcon),
this);
+ SelectionContextOperation cameraSpeedConfigTrigger = [this](const SelectionContext &) {
+ if (!m_cameraSpeedConfiguration) {
+ m_cameraSpeedConfiguration = new CameraSpeedConfiguration(this);
+ connect(m_cameraSpeedConfiguration.data(), &CameraSpeedConfiguration::totalSpeedChanged,
+ this, [this] {
+ setCameraSpeedAuxData(m_cameraSpeedConfiguration->speed(),
+ m_cameraSpeedConfiguration->multiplier());
+ });
+ }
+ m_cameraSpeedConfiguration->showConfigDialog(resolveToolbarPopupPos(m_cameraSpeedConfigAction.get()));
+ };
+
+ m_cameraSpeedConfigAction = std::make_unique<Edit3DAction>(
+ QmlDesigner::Constants::EDIT3D_CAMERA_SPEED_CONFIG,
+ View3DActionType::Empty,
+ QCoreApplication::translate("CameraSpeedConfigAction", "Open camera speed configuration dialog"),
+ QKeySequence(),
+ false,
+ false,
+ toolbarIcon(DesignerIcons::CameraSpeedConfigIcon),
+ this,
+ cameraSpeedConfigTrigger);
+
+
m_leftActions << m_selectionModeAction.get();
m_leftActions << nullptr; // Null indicates separator
m_leftActions << nullptr; // Second null after separator indicates an exclusive group
@@ -1102,6 +1248,7 @@ void Edit3DView::createEdit3DActions()
m_leftActions << nullptr;
m_leftActions << m_alignCamerasAction.get();
m_leftActions << m_alignViewAction.get();
+ m_leftActions << m_cameraSpeedConfigAction.get();
m_leftActions << nullptr;
m_leftActions << m_visibilityTogglesAction.get();
m_leftActions << m_backgroundColorMenuAction.get();
@@ -1182,7 +1329,7 @@ void Edit3DView::addQuick3DImport()
// context menu is created when nodeAtPosReady() is received from puppet
void Edit3DView::startContextMenu(const QPoint &pos)
{
- m_contextMenuPos = pos;
+ m_contextMenuPosMouse = pos;
m_nodeAtPosReqType = NodeAtPosReqType::ContextMenu;
}
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h
index 56248d9587..781b26d8d8 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h
@@ -15,6 +15,7 @@
#include <QTimer>
#include <QVariant>
#include <QVector>
+#include <QVector3D>
QT_BEGIN_NAMESPACE
class QAction;
@@ -24,6 +25,7 @@ QT_END_NAMESPACE
namespace QmlDesigner {
class BakeLights;
+class CameraSpeedConfiguration;
class Edit3DWidget;
class SnapConfiguration;
@@ -73,6 +75,7 @@ public:
void addQuick3DImport();
void startContextMenu(const QPoint &pos);
+ void showContextMenu();
void dropMaterial(const ModelNode &matNode, const QPointF &pos);
void dropBundleMaterial(const QPointF &pos);
void dropBundleEffect(const QPointF &pos);
@@ -83,11 +86,15 @@ public:
bool isBakingLightsSupported() const;
void syncSnapAuxPropsToSettings();
+ void setCameraSpeedAuxData(double speed, double multiplier);
+ void getCameraSpeedAuxData(double &speed, double &multiplier);
const QList<SplitToolState> &splitToolStates() const;
void setSplitToolState(int splitIndex, const SplitToolState &state);
int activeSplit() const;
+ bool isSplitView() const;
+ void setFlyMode(bool enabled);
private slots:
void onEntriesChanged();
@@ -101,6 +108,7 @@ private:
TextureDrop,
ContextMenu,
AssetDrop,
+ MainScenePick,
None
};
@@ -111,12 +119,14 @@ private:
void handleEntriesChanged();
void showMaterialPropertiesView();
void updateAlignActionStates();
+ void setActive3DSceneId(qint32 sceneId);
void createSelectBackgroundColorAction(QAction *syncEnvBackgroundAction);
void createGridColorSelectionAction();
void createResetColorAction(QAction *syncEnvBackgroundAction);
void createSyncEnvBackgroundAction();
void createSeekerSliderAction();
+ void syncCameraSpeedToNewView();
QPoint resolveToolbarPopupPos(Edit3DAction *action) const;
@@ -158,6 +168,7 @@ private:
std::unique_ptr<Edit3DAction> m_backgroundColorMenuAction;
std::unique_ptr<Edit3DAction> m_snapToggleAction;
std::unique_ptr<Edit3DAction> m_snapConfigAction;
+ std::unique_ptr<Edit3DAction> m_cameraSpeedConfigAction;
std::unique_ptr<Edit3DBakeLightsAction> m_bakeLightsAction;
int particlemode;
@@ -166,14 +177,21 @@ private:
ItemLibraryEntry m_droppedEntry;
QString m_droppedFile;
NodeAtPosReqType m_nodeAtPosReqType;
- QPoint m_contextMenuPos;
+ QPoint m_contextMenuPosMouse;
+ QVector3D m_contextMenuPos3D;
QTimer m_compressionTimer;
QPointer<BakeLights> m_bakeLights;
bool m_isBakingLightsSupported = false;
QPointer<SnapConfiguration> m_snapConfiguration;
+ QPointer<CameraSpeedConfiguration> m_cameraSpeedConfiguration;
int m_activeSplit = 0;
QList<SplitToolState> m_splitToolStates;
+ QList<Edit3DAction *> m_flyModeDisabledActions;
+ ModelNode m_contextMenuPendingNode;
+
+ double m_previousCameraSpeed = -1.;
+ double m_previousCameraMultiplier = -1.;
friend class Edit3DAction;
};
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
index ff5852c631..07102ae893 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
@@ -19,6 +19,7 @@
#include "qmleditormenu.h"
#include "qmlvisualnode.h"
#include "viewmanager.h"
+#include <utils3d.h>
#include <auxiliarydataproperties.h>
#include <designeractionmanager.h>
@@ -121,6 +122,7 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view)
// Register action as creator command to make it configurable
Core::Command *command = Core::ActionManager::registerAction(
a, action->menuId().constData(), context);
+ m_actionToCommandHash.insert(a, command);
command->setDefaultKeySequence(a->shortcut());
if (proxyGroup)
proxyGroup->addAction(command->action());
@@ -479,10 +481,7 @@ void Edit3DWidget::onCreateAction(QAction *action)
if (!m_view->model()->hasImport(import, true, true))
m_view->model()->changeImports({import}, {});
- int activeScene = -1;
- auto data = m_view->rootModelNode().auxiliaryData(active3dSceneProperty);
- if (data)
- activeScene = data->toInt();
+ int activeScene = Utils3D::active3DSceneId(m_view->model());
auto modelNode = QmlVisualNode::createQml3DNode(m_view, entry,
activeScene, m_contextMenuPos3d).modelNode();
QTC_ASSERT(modelNode.isValid(), return);
@@ -688,7 +687,7 @@ void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent)
|| dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_MATERIAL)
|| dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_EFFECT)
|| dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_TEXTURE)) {
- if (m_view->active3DSceneNode().isValid())
+ if (Utils3D::active3DSceneNode(m_view).isValid())
dragEnterEvent->acceptProposedAction();
} else if (dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_ITEM_LIBRARY_INFO)) {
QByteArray data = dragEnterEvent->mimeData()->data(Constants::MIME_TYPE_ITEM_LIBRARY_INFO);
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h
index 0c9c807473..211b044d41 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h
@@ -12,6 +12,10 @@
#include <itemlibraryentry.h>
#include <modelnode.h>
+namespace Core {
+class Command;
+}
+
namespace QmlDesigner {
class Edit3DView;
@@ -52,6 +56,8 @@ public:
void showContextMenu(const QPoint &pos, const ModelNode &modelNode, const QVector3D &pos3d);
void updateCreateSubMenu(const QList<ItemLibraryDetails> &entriesList);
+ const QHash<QAction *, Core::Command *> &actionToCommandHash() { return m_actionToCommandHash; }
+
private slots:
void onCreateAction(QAction *action);
void onMatOverrideAction(QAction *action);
@@ -100,6 +106,7 @@ private:
QVector3D m_contextMenuPos3d;
QHash<QString, ItemLibraryEntry> m_nameToEntry;
ItemLibraryEntry m_draggedEntry;
+ QHash<QAction *, Core::Command *> m_actionToCommandHash;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/eventlist/eventlist.cpp b/src/plugins/qmldesigner/components/eventlist/eventlist.cpp
index 5ba29bb0df..22de112b6d 100644
--- a/src/plugins/qmldesigner/components/eventlist/eventlist.cpp
+++ b/src/plugins/qmldesigner/components/eventlist/eventlist.cpp
@@ -187,7 +187,7 @@ void EventList::initialize(EventListPluginView *parent)
QByteArray unqualifiedTypeName = "ListModel";
auto metaInfo = parent->model()->metaInfo(unqualifiedTypeName);
#ifdef QDS_USE_PROJECTSTORAGE
- m_model = Model::create(unqualifiedTypeName, -1, -1);
+ m_model = parent->model()->createModel(unqualifiedTypeName);
#else
QByteArray fullTypeName = metaInfo.typeName();
int minorVersion = metaInfo.minorVersion();
@@ -195,7 +195,6 @@ void EventList::initialize(EventListPluginView *parent)
m_model = Model::create(fullTypeName, majorVersion, minorVersion);
#endif
- m_model->setParent(parent);
}
if (!m_eventView) {
diff --git a/src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.cpp b/src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.cpp
deleted file mode 100644
index 8c40c8c358..0000000000
--- a/src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "contentnoteditableindicator.h"
-#include "nodemetainfo.h"
-
-#include <utils/algorithm.h>
-
-#include <QSet>
-#include <QPen>
-
-namespace QmlDesigner {
-
-ContentNotEditableIndicator::ContentNotEditableIndicator(LayerItem *layerItem)
- : m_layerItem(layerItem)
-{
-
-}
-
-ContentNotEditableIndicator::ContentNotEditableIndicator() = default;
-
-ContentNotEditableIndicator::~ContentNotEditableIndicator()
-{
- clear();
-}
-
-void ContentNotEditableIndicator::clear()
-{
- for (const EntryPair &entryPair : std::as_const(m_entryList)) {
- delete entryPair.second;
- entryPair.first->blurContent(false);
- }
-
- m_entryList.clear();
-}
-
-bool operator ==(const ContentNotEditableIndicator::EntryPair &firstPair, const ContentNotEditableIndicator::EntryPair &secondPair)
-{
- return firstPair.first == secondPair.first;
-}
-
-void ContentNotEditableIndicator::setItems(const QList<FormEditorItem*> &itemList)
-{
- removeEntriesWhichAreNotInTheList(itemList);
- addAddiationEntries(itemList);
-}
-
-void ContentNotEditableIndicator::updateItems(const QList<FormEditorItem *> &itemList)
-{
- QSet<FormEditorItem*> affectedFormEditorItemItems;
- affectedFormEditorItemItems.unite(Utils::toSet(itemList));
- for (FormEditorItem *formEditorItem : itemList)
- affectedFormEditorItemItems.unite(Utils::toSet(formEditorItem->offspringFormEditorItems()));
-
- for (const EntryPair &entryPair : std::as_const(m_entryList)) {
- for (FormEditorItem *formEditorItem : std::as_const(affectedFormEditorItemItems)) {
- if (formEditorItem == entryPair.first) {
- QRectF boundingRectangleInSceneSpace
- = formEditorItem->qmlItemNode().instanceSceneTransform().mapRect(
- formEditorItem->qmlItemNode().instanceBoundingRect());
- entryPair.second->setRect(boundingRectangleInSceneSpace);
- entryPair.second->update();
- }
- }
- }
-}
-
-void ContentNotEditableIndicator::addAddiationEntries(const QList<FormEditorItem *> &itemList)
-{
- for (FormEditorItem *formEditorItem : itemList) {
- const ModelNode modelNode = formEditorItem->qmlItemNode().modelNode();
- if (modelNode.metaInfo().isValid() && modelNode.metaInfo().isQtQuickLoader()) {
- if (!m_entryList.contains(EntryPair(formEditorItem, 0))) {
- auto indicatorShape = new QGraphicsRectItem(m_layerItem);
- QPen linePen;
- linePen.setCosmetic(true);
- linePen.setColor(QColor(0xa0, 0xa0, 0xa0));
- indicatorShape->setPen(linePen);
- QRectF boundingRectangleInSceneSpace = formEditorItem->qmlItemNode().instanceSceneTransform().mapRect(formEditorItem->qmlItemNode().instanceBoundingRect());
- indicatorShape->setRect(boundingRectangleInSceneSpace);
- static QBrush brush(QColor(0, 0, 0, 10), Qt::BDiagPattern);
- indicatorShape->setBrush(brush);
-
- m_entryList.append(EntryPair(formEditorItem, indicatorShape));
- }
- }
- }
-}
-
-void ContentNotEditableIndicator::removeEntriesWhichAreNotInTheList(const QList<FormEditorItem *> &itemList)
-{
- for (int i = 0; i < m_entryList.size(); ++i) {
- const EntryPair &entryPair = m_entryList.at(i);
- if (!itemList.contains(entryPair.first)) {
- delete entryPair.second;
- entryPair.first->blurContent(false);
- m_entryList.removeAt(i--);
- }
- }
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.h b/src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.h
deleted file mode 100644
index 514331e2ac..0000000000
--- a/src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#pragma once
-
-#include "layeritem.h"
-#include "formeditoritem.h"
-
-#include <QPointer>
-#include <QGraphicsRectItem>
-
-namespace QmlDesigner {
-
-class ContentNotEditableIndicator
-{
-public:
- using EntryPair = QPair<FormEditorItem*, QGraphicsRectItem *>;
-
- ContentNotEditableIndicator(LayerItem *layerItem);
- ContentNotEditableIndicator();
- ~ContentNotEditableIndicator();
-
- void clear();
-
- void setItems(const QList<FormEditorItem*> &itemList);
- void updateItems(const QList<FormEditorItem*> &itemList);
-
-protected:
- void addAddiationEntries(const QList<FormEditorItem*> &itemList);
- void removeEntriesWhichAreNotInTheList(const QList<FormEditorItem*> &itemList);
-
-private:
- QPointer<LayerItem> m_layerItem;
- QList<EntryPair> m_entryList;
-};
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp
index e363e9bb11..0b7d199b50 100644
--- a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp
@@ -274,6 +274,11 @@ void DragTool::dropEvent(const QList<QGraphicsItem *> &itemList, QGraphicsSceneD
nodeList.append(node);
}
view()->setSelectedModelNodes(nodeList);
+
+ bool itemLibraryJustCreated = hasItemLibraryInfo(event->mimeData())
+ && nodeList.size() == 1;
+ if (itemLibraryJustCreated)
+ view()->emitCustomNotification("item_library_created_by_drop", nodeList);
}
m_dragNodes.clear();
}
@@ -382,7 +387,7 @@ void DragTool::dragMoveEvent(const QList<QGraphicsItem *> &itemList, QGraphicsSc
}
}
-void DragTool::end()
+void DragTool::end()
{
m_moveManipulator.end();
clear();
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp
index 5836d25ad9..eaf935d4c9 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp
@@ -863,6 +863,7 @@ QmlItemNode findRecursiveQmlItemNode(const QmlObjectNode &firstQmlObjectNode)
void FormEditorView::instancePropertyChanged(const QList<QPair<ModelNode, PropertyName> > &propertyList)
{
QList<FormEditorItem*> changedItems;
+ bool needEffectUpdate = false;
for (auto &nodePropertyPair : propertyList) {
const QmlItemNode qmlItemNode(nodePropertyPair.first);
const PropertyName propertyName = nodePropertyPair.second;
@@ -873,10 +874,14 @@ void FormEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Proper
m_scene->synchronizeOtherProperty(item, propertyName);
changedItems.append(item);
}
+ } else if (propertyName == "visible" && qmlItemNode.isEffectItem()) {
+ needEffectUpdate = true;
}
}
}
m_currentTool->formEditorItemsChanged(changedItems);
+ if (needEffectUpdate)
+ updateHasEffects();
}
bool FormEditorView::isMoveToolAvailable() const
@@ -1011,7 +1016,7 @@ void FormEditorView::updateHasEffects()
FormEditorItem *item = m_scene->itemForQmlItemNode(qmlNode);
if (item)
item->setHasEffect(false);
- if (qmlNode.isEffectItem()) {
+ if (qmlNode.isEffectItem() && qmlNode.instanceIsVisible()) {
FormEditorItem *parentItem = m_scene->itemForQmlItemNode(qmlNode.modelParentItem());
if (parentItem)
parentItem->setHasEffect(true);
diff --git a/src/plugins/qmldesigner/components/formeditor/movetool.cpp b/src/plugins/qmldesigner/components/formeditor/movetool.cpp
index 0d486b0f4f..7aa66c2390 100644
--- a/src/plugins/qmldesigner/components/formeditor/movetool.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/movetool.cpp
@@ -21,14 +21,14 @@
namespace QmlDesigner {
MoveTool::MoveTool(FormEditorView *editorView)
- : AbstractFormEditorTool(editorView),
- m_moveManipulator(editorView->scene()->manipulatorLayerItem(), editorView),
- m_selectionIndicator(editorView->scene()->manipulatorLayerItem()),
- m_resizeIndicator(editorView->scene()->manipulatorLayerItem()),
- m_rotationIndicator(editorView->scene()->manipulatorLayerItem()),
- m_anchorIndicator(editorView->scene()->manipulatorLayerItem()),
- m_bindingIndicator(editorView->scene()->manipulatorLayerItem()),
- m_contentNotEditableIndicator(editorView->scene()->manipulatorLayerItem())
+ : AbstractFormEditorTool(editorView)
+ , m_moveManipulator(editorView->scene()->manipulatorLayerItem(), editorView)
+ , m_selectionIndicator(editorView->scene()->manipulatorLayerItem())
+ , m_resizeIndicator(editorView->scene()->manipulatorLayerItem())
+ , m_rotationIndicator(editorView->scene()->manipulatorLayerItem())
+ , m_anchorIndicator(editorView->scene()->manipulatorLayerItem())
+ , m_bindingIndicator(editorView->scene()->manipulatorLayerItem())
+
{
m_selectionIndicator.setCursor(Qt::SizeAllCursor);
}
@@ -44,7 +44,6 @@ void MoveTool::clear()
m_rotationIndicator.clear();
m_anchorIndicator.clear();
m_bindingIndicator.clear();
- m_contentNotEditableIndicator.clear();
AbstractFormEditorTool::clear();
if (view()->formEditorWidget()->graphicsView())
@@ -142,9 +141,6 @@ void MoveTool::hoverMoveEvent(const QList<QGraphicsItem*> &itemList,
view()->changeToSelectionTool();
return;
}
-
-
- m_contentNotEditableIndicator.setItems(toFormEditorItemList(itemList));
}
void MoveTool::keyPressEvent(QKeyEvent *event)
@@ -381,7 +377,6 @@ void MoveTool::formEditorItemsChanged(const QList<FormEditorItem*> &itemList)
m_rotationIndicator.updateItems(selectedItemList);
m_anchorIndicator.updateItems(selectedItemList);
m_bindingIndicator.updateItems(selectedItemList);
- m_contentNotEditableIndicator.updateItems(selectedItemList);
}
void MoveTool::focusLost()
diff --git a/src/plugins/qmldesigner/components/formeditor/movetool.h b/src/plugins/qmldesigner/components/formeditor/movetool.h
index e7176f3263..0370c9010d 100644
--- a/src/plugins/qmldesigner/components/formeditor/movetool.h
+++ b/src/plugins/qmldesigner/components/formeditor/movetool.h
@@ -9,7 +9,6 @@
#include "rotationindicator.h"
#include "anchorindicator.h"
#include "bindingindicator.h"
-#include "contentnoteditableindicator.h"
namespace QmlDesigner {
@@ -66,7 +65,6 @@ private:
RotationIndicator m_rotationIndicator;
AnchorIndicator m_anchorIndicator;
BindingIndicator m_bindingIndicator;
- ContentNotEditableIndicator m_contentNotEditableIndicator;
QList<FormEditorItem*> m_movingItems;
};
diff --git a/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp b/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp
index 282208f3b1..d30539d52b 100644
--- a/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp
@@ -31,7 +31,6 @@ SelectionTool::SelectionTool(FormEditorView *editorView)
, m_rotationIndicator(editorView->scene()->manipulatorLayerItem())
, m_anchorIndicator(editorView->scene()->manipulatorLayerItem())
, m_bindingIndicator(editorView->scene()->manipulatorLayerItem())
- , m_contentNotEditableIndicator(editorView->scene()->manipulatorLayerItem())
{
m_selectionIndicator.setCursor(Qt::ArrowCursor);
}
@@ -143,8 +142,6 @@ void SelectionTool::hoverMoveEvent(const QList<QGraphicsItem*> &itemList,
}
scene()->highlightBoundingRect(topSelectableItem);
-
- m_contentNotEditableIndicator.setItems(toFormEditorItemList(itemList));
}
void SelectionTool::mouseReleaseEvent(const QList<QGraphicsItem*> &itemList,
@@ -250,7 +247,6 @@ void SelectionTool::clear()
m_rotationIndicator.clear();
m_anchorIndicator.clear();
m_bindingIndicator.clear();
- m_contentNotEditableIndicator.clear();
AbstractFormEditorTool::clear();
}
@@ -273,7 +269,6 @@ void SelectionTool::formEditorItemsChanged(const QList<FormEditorItem*> &itemLis
m_rotationIndicator.updateItems(selectedItemList);
m_anchorIndicator.updateItems(selectedItemList);
m_bindingIndicator.updateItems(selectedItemList);
- m_contentNotEditableIndicator.updateItems(selectedItemList);
}
void SelectionTool::instancesCompleted(const QList<FormEditorItem*> &/*itemList*/)
diff --git a/src/plugins/qmldesigner/components/formeditor/selectiontool.h b/src/plugins/qmldesigner/components/formeditor/selectiontool.h
index 712de3ccf8..5453c43ac4 100644
--- a/src/plugins/qmldesigner/components/formeditor/selectiontool.h
+++ b/src/plugins/qmldesigner/components/formeditor/selectiontool.h
@@ -10,7 +10,6 @@
#include "rotationindicator.h"
#include "anchorindicator.h"
#include "bindingindicator.h"
-#include "contentnoteditableindicator.h"
#include <QElapsedTimer>
#include <QTime>
@@ -67,7 +66,6 @@ private:
RotationIndicator m_rotationIndicator;
AnchorIndicator m_anchorIndicator;
BindingIndicator m_bindingIndicator;
- ContentNotEditableIndicator m_contentNotEditableIndicator;
QElapsedTimer m_mousePressTimer;
QCursor m_cursor;
bool m_itemSelectedAndMovable = false;
diff --git a/src/plugins/qmldesigner/components/formeditor/view3dtool.cpp b/src/plugins/qmldesigner/components/formeditor/view3dtool.cpp
new file mode 100644
index 0000000000..a494d46775
--- /dev/null
+++ b/src/plugins/qmldesigner/components/formeditor/view3dtool.cpp
@@ -0,0 +1,114 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "view3dtool.h"
+
+#include "designmodewidget.h"
+#include "formeditorview.h"
+#include "qmldesignerplugin.h"
+
+#include <QGraphicsSceneMouseEvent>
+
+namespace QmlDesigner {
+
+View3DTool::View3DTool()
+ : QObject(), AbstractCustomTool()
+{
+}
+
+View3DTool::~View3DTool()
+{
+}
+
+void View3DTool::clear()
+{
+ m_view3dNode = {};
+ AbstractFormEditorTool::clear();
+}
+
+void View3DTool::mouseMoveEvent(const QList<QGraphicsItem *> &, QGraphicsSceneMouseEvent *)
+{
+}
+
+void View3DTool::hoverMoveEvent(const QList<QGraphicsItem *> &, QGraphicsSceneMouseEvent *)
+{
+}
+
+void View3DTool::keyPressEvent(QKeyEvent *)
+{
+}
+
+void View3DTool::keyReleaseEvent(QKeyEvent *)
+{
+}
+
+void View3DTool::dragLeaveEvent(const QList<QGraphicsItem *> &, QGraphicsSceneDragDropEvent *)
+{
+}
+
+void View3DTool::dragMoveEvent(const QList<QGraphicsItem *> &, QGraphicsSceneDragDropEvent *)
+{
+}
+
+void View3DTool::mouseReleaseEvent(const QList<QGraphicsItem *> &,
+ QGraphicsSceneMouseEvent *event)
+{
+ if (m_view3dNode.isValid()) {
+ QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("Editor3D", true);
+ view()->emitCustomNotification("pick_3d_node_from_2d_scene",
+ {m_view3dNode}, {event->scenePos()});
+ }
+
+ view()->changeToSelectionTool();
+}
+
+void View3DTool::itemsAboutToRemoved(const QList<FormEditorItem *> &)
+{
+}
+
+void View3DTool::instancesCompleted(const QList<FormEditorItem *> &)
+{
+}
+
+void View3DTool::instancesParentChanged(const QList<FormEditorItem *> &)
+{
+}
+
+void View3DTool::instancePropertyChange(const QList<QPair<ModelNode, PropertyName>> &)
+{
+}
+
+void View3DTool::formEditorItemsChanged(const QList<FormEditorItem *> &)
+{
+}
+
+int View3DTool::wantHandleItem(const ModelNode &modelNode) const
+{
+ if (modelNode.metaInfo().isQtQuick3DView3D())
+ return 30;
+
+ return 0;
+}
+
+
+QString View3DTool::name() const
+{
+ return tr("View3D Tool");
+}
+
+void View3DTool::selectedItemsChanged(const QList<FormEditorItem *> &itemList)
+{
+ if (itemList.size() == 1) {
+ if (itemList[0]) {
+ ModelNode node = itemList[0]->qmlItemNode().modelNode();
+ if (node.metaInfo().isQtQuick3DView3D()) {
+ m_view3dNode = node;
+ return;
+ }
+ }
+ }
+
+ view()->changeToSelectionTool();
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/formeditor/view3dtool.h b/src/plugins/qmldesigner/components/formeditor/view3dtool.h
new file mode 100644
index 0000000000..eeadd3483b
--- /dev/null
+++ b/src/plugins/qmldesigner/components/formeditor/view3dtool.h
@@ -0,0 +1,52 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#pragma once
+
+#include "abstractcustomtool.h"
+
+#include "modelnode.h"
+
+namespace QmlDesigner {
+
+class View3DTool : public QObject, public AbstractCustomTool
+{
+ Q_OBJECT
+public:
+ View3DTool();
+ ~View3DTool();
+
+ void mouseMoveEvent(const QList<QGraphicsItem *> &itemList,
+ QGraphicsSceneMouseEvent *event) override;
+ void hoverMoveEvent(const QList<QGraphicsItem *> &itemList,
+ QGraphicsSceneMouseEvent *event) override;
+ void mouseReleaseEvent(const QList<QGraphicsItem *> &itemList,
+ QGraphicsSceneMouseEvent *event) override;
+ void keyPressEvent(QKeyEvent *event) override;
+ void keyReleaseEvent(QKeyEvent *keyEvent) override;
+
+ void dragLeaveEvent(const QList<QGraphicsItem *> &itemList,
+ QGraphicsSceneDragDropEvent *event) override;
+ void dragMoveEvent(const QList<QGraphicsItem *> &itemList,
+ QGraphicsSceneDragDropEvent *event) override;
+
+ void itemsAboutToRemoved(const QList<FormEditorItem *> &itemList) override;
+
+ void instancesCompleted(const QList<FormEditorItem *> &itemList) override;
+ void instancesParentChanged(const QList<FormEditorItem *> &itemList) override;
+ void instancePropertyChange(const QList<QPair<ModelNode, PropertyName>> &propertyList) override;
+
+ void clear() override;
+
+ void formEditorItemsChanged(const QList<FormEditorItem *> &itemList) override;
+
+ int wantHandleItem(const ModelNode &modelNode) const override;
+
+ QString name() const override;
+
+ void selectedItemsChanged(const QList<FormEditorItem *> &itemList) override;
+
+private:
+ ModelNode m_view3dNode;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp
index 7b7f4a51ef..c0bebbb82b 100644
--- a/src/plugins/qmldesigner/components/integration/designdocument.cpp
+++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp
@@ -15,6 +15,7 @@
#include <nodeinstanceview.h>
#include <nodelistproperty.h>
#include <rewritingexception.h>
+#include <utils3d.h>
#include <variantproperty.h>
#include <viewmanager.h>
#include <qmldesignerplugin.h>
@@ -161,13 +162,15 @@ const AbstractView *DesignDocument::view() const
ModelPointer DesignDocument::createInFileComponentModel()
{
+#ifdef QDS_USE_PROJECTSTORAGE
+ auto model = m_documentModel->createModel("Item", std::make_unique<ModelResourceManagement>());
+#else
auto model = Model::create("QtQuick.Item",
1,
0,
nullptr,
std::make_unique<ModelResourceManagement>());
model->setFileUrl(m_documentModel->fileUrl());
-#ifndef QDS_USE_PROJECTSTORAGE
model->setMetaInfo(m_documentModel->metaInfo());
#endif
@@ -232,13 +235,12 @@ void DesignDocument::moveNodesToPosition(const QList<ModelNode> &nodes, const st
});
if (all3DNodes) {
- auto data = rootModelNode().auxiliaryData(active3dSceneProperty);
- if (data) {
- if (int activeSceneId = data->toInt(); activeSceneId != -1) {
- NodeListProperty sceneNodeProperty = QmlVisualNode::findSceneNodeProperty(
- rootModelNode().view(), activeSceneId);
- targetNode = sceneNodeProperty.parentModelNode();
- }
+ int activeSceneId = Utils3D::active3DSceneId(m_documentModel.get());
+
+ if (activeSceneId != -1) {
+ NodeListProperty sceneNodeProperty = QmlVisualNode::findSceneNodeProperty(
+ rootModelNode().view(), activeSceneId);
+ targetNode = sceneNodeProperty.parentModelNode();
}
}
}
diff --git a/src/plugins/qmldesigner/components/integration/designdocumentview.cpp b/src/plugins/qmldesigner/components/integration/designdocumentview.cpp
index 9d0deef713..6ef95bf4c4 100644
--- a/src/plugins/qmldesigner/components/integration/designdocumentview.cpp
+++ b/src/plugins/qmldesigner/components/integration/designdocumentview.cpp
@@ -97,8 +97,12 @@ static bool hasOnly3DNodes(const ModelNode &node)
QString DesignDocumentView::toText() const
{
+#ifdef QDS_USE_PROJECTSTORAGE
+ auto outputModel = model()->createModel("Rectangle");
+#else
auto outputModel = Model::create("QtQuick.Rectangle", 1, 0, model());
outputModel->setFileUrl(model()->fileUrl());
+#endif
QPlainTextEdit textEdit;
QString imports;
@@ -136,8 +140,12 @@ QString DesignDocumentView::toText() const
void DesignDocumentView::fromText(const QString &text)
{
+#ifdef QDS_USE_PROJECTSTORAGE
+ auto inputModel = model()->createModel("Rectangle");
+#else
auto inputModel = Model::create("QtQuick.Rectangle", 1, 0, model());
inputModel->setFileUrl(model()->fileUrl());
+#endif
QPlainTextEdit textEdit;
QString imports;
const auto modelImports = model()->imports();
@@ -179,12 +187,16 @@ ModelPointer DesignDocumentView::pasteToModel(ExternalDependenciesInterface &ext
QTC_ASSERT(parentModel, return nullptr);
+#ifdef QDS_USE_PROJECTSTORAGE
+ auto pasteModel = parentModel->createModel("Item");
+#else
auto pasteModel = Model::create("empty", 1, 0, parentModel);
Q_ASSERT(pasteModel);
if (!pasteModel)
return nullptr;
+#endif
pasteModel->setFileUrl(parentModel->fileUrl());
pasteModel->changeImports(parentModel->imports(), {});
@@ -204,12 +216,16 @@ void DesignDocumentView::copyModelNodes(const QList<ModelNode> &nodesToCopy,
QTC_ASSERT(parentModel, return);
+#ifdef QDS_USE_PROJECTSTORAGE
+ auto copyModel = parentModel->createModel("Rectangle");
+#else
auto copyModel = Model::create("QtQuick.Rectangle", 1, 0, parentModel);
copyModel->setFileUrl(parentModel->fileUrl());
copyModel->changeImports(parentModel->imports(), {});
Q_ASSERT(copyModel);
+#endif
QList<ModelNode> selectedNodes = nodesToCopy;
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
index eaf6f89b14..dbeacc7595 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
@@ -323,7 +323,7 @@ void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo,
QHash<QString, ItemLibraryImport *> importHash;
for (const Import &import : model->imports()) {
if (import.url() != projectName) {
- if (excludedImports.contains(import.url()))
+ if (excludedImports.contains(import.url()) || import.url().startsWith("Effects."))
continue;
bool addNew = true;
bool isQuick3DAsset = import.url().startsWith("Quick3DAssets.");
@@ -371,10 +371,13 @@ void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo,
else
metaInfo = model->metaInfo(entry.typeName());
+#ifdef QDS_USE_PROJECTSTORAGE
+ bool valid = metaInfo.isValid();
+#else
bool valid = metaInfo.isValid()
&& (metaInfo.majorVersion() >= entry.majorVersion()
|| metaInfo.majorVersion() < 0);
-
+#endif
bool isItem = valid && metaInfo.isQtQuickItem();
bool forceVisibility = valid && NodeHints::fromItemLibraryEntry(entry).visibleInLibrary();
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp
index 76ae09a453..8bd5761728 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp
@@ -16,6 +16,7 @@
#include "qmldesignerconstants.h"
#include "qmlobjectnode.h"
#include "variantproperty.h"
+#include <utils3d.h>
#include <coreplugin/icore.h>
@@ -240,7 +241,7 @@ void MaterialBrowserView::modelAttached(Model *model)
loadPropertyGroups(); // Needs the delay because it uses metaInfo
});
- m_sceneId = model->active3DSceneId();
+ m_sceneId = Utils3D::active3DSceneId(model);
}
void MaterialBrowserView::refreshModel(bool updateImages)
@@ -585,6 +586,14 @@ void MaterialBrowserView::instancePropertyChanged(const QList<QPair<ModelNode, P
}
}
+void MaterialBrowserView::auxiliaryDataChanged(const ModelNode &,
+ AuxiliaryDataKeyView type,
+ const QVariant &data)
+{
+ if (type == Utils3D::active3dSceneProperty)
+ active3DSceneChanged(data.toInt());
+}
+
void MaterialBrowserView::applyTextureToModel3D(const QmlObjectNode &model3D, const ModelNode &texture)
{
if (!texture.isValid() && m_appliedTexturePath.isEmpty())
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h
index 297487ae66..36ac908e8c 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h
@@ -49,7 +49,9 @@ public:
const QList<ModelNode> &nodeList, const QList<QVariant> &data) override;
void instancesCompleted(const QVector<ModelNode> &completedNodeList) override;
void instancePropertyChanged(const QList<QPair<ModelNode, PropertyName> > &propertyList) override;
- void active3DSceneChanged(qint32 sceneId) override;
+ void auxiliaryDataChanged(const ModelNode &node,
+ AuxiliaryDataKeyView type,
+ const QVariant &data) override;
void currentStateChanged(const ModelNode &node) override;
void applyTextureToModel3D(const QmlObjectNode &model3D, const ModelNode &texture = {});
@@ -65,6 +67,7 @@ protected:
bool eventFilter(QObject *obj, QEvent *event) override;
private:
+ void active3DSceneChanged(qint32 sceneId);
void refreshModel(bool updateImages);
void updateMaterialsPreview();
bool isMaterial(const ModelNode &node) const;
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialutils.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialutils.cpp
index 76924bedbc..1ee2e409f3 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialutils.cpp
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialutils.cpp
@@ -8,6 +8,7 @@
#include "nodemetainfo.h"
#include "qmlobjectnode.h"
#include "variantproperty.h"
+#include <utils3d.h>
#include <utils/qtcassert.h>
@@ -24,7 +25,7 @@ void MaterialUtils::assignMaterialTo3dModel(AbstractView *view, const ModelNode
{
QTC_ASSERT(modelNode.isValid() && modelNode.metaInfo().isQtQuick3DModel(), return);
- ModelNode matLib = view->materialLibraryNode();
+ ModelNode matLib = Utils3D::materialLibraryNode(view);
if (!matLib.isValid())
return;
@@ -46,10 +47,14 @@ void MaterialUtils::assignMaterialTo3dModel(AbstractView *view, const ModelNode
// if no valid material, create a new default material
if (!newMaterialNode.isValid()) {
+#ifdef QDS_USE_PROJECTSTORAGE
+ newMaterialNode = view->createModelNode("PrincipledMaterial");
+#else
NodeMetaInfo metaInfo = view->model()->qtQuick3DPrincipledMaterialMetaInfo();
newMaterialNode = view->createModelNode("QtQuick3D.PrincipledMaterial",
metaInfo.majorVersion(),
metaInfo.minorVersion());
+#endif
newMaterialNode.validId();
}
}
diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp
index 5661436a8f..664006fb7a 100644
--- a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp
+++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp
@@ -3,35 +3,30 @@
#include "materialeditorcontextobject.h"
-#include <abstractview.h>
#include <bindingproperty.h>
#include <documentmanager.h>
#include <nodemetainfo.h>
-#include <rewritingexception.h>
-#include <qmldesignerplugin.h>
-#include <qmlmodelnodeproxy.h>
#include <qmlobjectnode.h>
#include <qmltimeline.h>
#include <qmltimelinekeyframegroup.h>
#include <variantproperty.h>
-#include <coreplugin/messagebox.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <QApplication>
#include <QCursor>
#include <QMessageBox>
+#include <QQmlComponent>
#include <QQmlContext>
-#include <QWindow>
-
-#include <coreplugin/icore.h>
+#include <QQmlPropertyMap>
+#include <QQuickWidget>
namespace QmlDesigner {
-MaterialEditorContextObject::MaterialEditorContextObject(QQmlContext *context, QObject *parent)
+MaterialEditorContextObject::MaterialEditorContextObject(QQuickWidget *widget, QObject *parent)
: QObject(parent)
- , m_qmlContext(context)
+ , m_quickWidget(widget)
{
qmlRegisterUncreatableType<MaterialEditorContextObject>("MaterialToolBarAction", 1, 0, "ToolBarAction", "Enum type");
}
@@ -41,7 +36,7 @@ QQmlComponent *MaterialEditorContextObject::specificQmlComponent()
if (m_specificQmlComponent)
return m_specificQmlComponent;
- m_specificQmlComponent = new QQmlComponent(m_qmlContext->engine(), this);
+ m_specificQmlComponent = new QQmlComponent(m_quickWidget->rootContext()->engine(), this);
m_specificQmlComponent->setData(m_specificQmlData.toUtf8(), QUrl::fromLocalFile("specifics.qml"));
return m_specificQmlComponent;
@@ -543,4 +538,18 @@ void MaterialEditorContextObject::goIntoComponent()
DocumentManager::goIntoComponent(m_selectedMaterial);
}
+QRect MaterialEditorContextObject::screenRect() const
+{
+ if (m_quickWidget && m_quickWidget->screen())
+ return m_quickWidget->screen()->availableGeometry();
+ return {};
+}
+
+QPoint MaterialEditorContextObject::globalPos(const QPoint &point) const
+{
+ if (m_quickWidget)
+ return m_quickWidget->mapToGlobal(point);
+ return point;
+}
+
} // QmlDesigner
diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.h
index 8772f3e1d2..0ae8e1016f 100644
--- a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.h
+++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.h
@@ -6,13 +6,16 @@
#include <model.h>
#include <modelnode.h>
-#include <QObject>
-#include <QUrl>
-#include <QQmlPropertyMap>
-#include <QQmlComponent>
#include <QColor>
+#include <QObject>
#include <QPoint>
-#include <QMouseEvent>
+#include <QUrl>
+
+QT_BEGIN_NAMESPACE
+class QQmlComponent;
+class QQmlPropertyMap;
+class QQuickWidget;
+QT_END_NAMESPACE
namespace QmlDesigner {
@@ -44,7 +47,7 @@ class MaterialEditorContextObject : public QObject
Q_PROPERTY(QQmlPropertyMap *backendValues READ backendValues WRITE setBackendValues NOTIFY backendValuesChanged)
public:
- MaterialEditorContextObject(QQmlContext *context, QObject *parent = nullptr);
+ MaterialEditorContextObject(QQuickWidget *widget, QObject *parent = nullptr);
QUrl specificsUrl() const { return m_specificsUrl; }
QString specificQmlData() const {return m_specificQmlData; }
@@ -76,6 +79,9 @@ public:
Q_INVOKABLE bool isBlocked(const QString &propName) const;
Q_INVOKABLE void goIntoComponent();
+ Q_INVOKABLE QRect screenRect() const;
+ Q_INVOKABLE QPoint globalPos(const QPoint &point) const;
+
enum ToolBarAction {
ApplyToSelected = 0,
ApplyToSelectedAdd,
@@ -146,7 +152,7 @@ private:
QUrl m_specificsUrl;
QString m_specificQmlData;
QQmlComponent *m_specificQmlComponent = nullptr;
- QQmlContext *m_qmlContext = nullptr;
+ QQuickWidget *m_quickWidget = nullptr;
QString m_stateName;
QStringList m_allStateNames;
diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp
index f0ce34a0e6..ecc460ae51 100644
--- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp
+++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp
@@ -79,15 +79,15 @@ public:
};
MaterialEditorQmlBackend::MaterialEditorQmlBackend(MaterialEditorView *materialEditor)
- : m_view(new QQuickWidget)
+ : m_quickWidget(Utils::makeUniqueObjectPtr<QQuickWidget>())
, m_materialEditorTransaction(new MaterialEditorTransaction(materialEditor))
- , m_contextObject(new MaterialEditorContextObject(m_view->rootContext()))
+ , m_contextObject(new MaterialEditorContextObject(m_quickWidget.get()))
, m_materialEditorImageProvider(new MaterialEditorImageProvider())
{
- m_view->setObjectName(Constants::OBJECT_NAME_MATERIAL_EDITOR);
- m_view->setResizeMode(QQuickWidget::SizeRootObjectToView);
- m_view->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
- m_view->engine()->addImageProvider("materialEditor", m_materialEditorImageProvider);
+ m_quickWidget->setObjectName(Constants::OBJECT_NAME_MATERIAL_EDITOR);
+ m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
+ m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
+ m_quickWidget->engine()->addImageProvider("materialEditor", m_materialEditorImageProvider);
m_contextObject->setBackendValues(&m_backendValuesPropertyMap);
m_contextObject->setModel(materialEditor->model());
context()->setContextObject(m_contextObject.data());
@@ -188,7 +188,7 @@ void MaterialEditorQmlBackend::setValue(const QmlObjectNode &, const PropertyNam
QQmlContext *MaterialEditorQmlBackend::context() const
{
- return m_view->rootContext();
+ return m_quickWidget->rootContext();
}
MaterialEditorContextObject *MaterialEditorQmlBackend::contextObject() const
@@ -198,12 +198,12 @@ MaterialEditorContextObject *MaterialEditorQmlBackend::contextObject() const
QQuickWidget *MaterialEditorQmlBackend::widget() const
{
- return m_view;
+ return m_quickWidget.get();
}
void MaterialEditorQmlBackend::setSource(const QUrl &url)
{
- m_view->setSource(url);
+ m_quickWidget->setSource(url);
}
QmlAnchorBindingProxy &MaterialEditorQmlBackend::backendAnchorBinding()
@@ -214,7 +214,7 @@ QmlAnchorBindingProxy &MaterialEditorQmlBackend::backendAnchorBinding()
void MaterialEditorQmlBackend::updateMaterialPreview(const QPixmap &pixmap)
{
m_materialEditorImageProvider->setPixmap(pixmap);
- QMetaObject::invokeMethod(m_view->rootObject(), "refreshPreview");
+ QMetaObject::invokeMethod(m_quickWidget->rootObject(), "refreshPreview");
}
DesignerPropertyMap &MaterialEditorQmlBackend::backendValuesPropertyMap()
@@ -287,8 +287,10 @@ void MaterialEditorQmlBackend::setup(const QmlObjectNode &selectedMaterialNode,
contextObject()->setSelectionChanged(false);
+#ifndef QDS_USE_PROJECTSTORAGE
NodeMetaInfo metaInfo = selectedMaterialNode.modelNode().metaInfo();
contextObject()->setMajorVersion(metaInfo.isValid() ? metaInfo.majorVersion() : -1);
+#endif
} else {
context()->setContextProperty("hasMaterial", QVariant(false));
}
diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h
index 65649329a2..6f9d6014e9 100644
--- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h
+++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h
@@ -7,6 +7,8 @@
#include "qmlanchorbindingproxy.h"
#include "qmlmodelnodeproxy.h"
+#include <utils/uniqueobjectptr.h>
+
#include <nodemetainfo.h>
class PropertyEditorValue;
@@ -58,13 +60,13 @@ private:
MaterialEditorView *materialEditor);
PropertyName auxNamePostFix(const PropertyName &propertyName);
- QQuickWidget *m_view = nullptr;
+ Utils::UniqueObjectPtr<QQuickWidget> m_quickWidget = nullptr;
QmlAnchorBindingProxy m_backendAnchorBinding;
QmlModelNodeProxy m_backendModelNode;
DesignerPropertyMap m_backendValuesPropertyMap;
QScopedPointer<MaterialEditorTransaction> m_materialEditorTransaction;
QScopedPointer<MaterialEditorContextObject> m_contextObject;
- MaterialEditorImageProvider *m_materialEditorImageProvider = nullptr;
+ QPointer<MaterialEditorImageProvider> m_materialEditorImageProvider;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp
index 5490829bb5..e083310cdb 100644
--- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp
+++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp
@@ -25,6 +25,7 @@
#include "qmltimeline.h"
#include "variantproperty.h"
#include <itemlibraryentry.h>
+#include <utils3d.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagebox.h>
@@ -54,9 +55,10 @@ MaterialEditorView::MaterialEditorView(ExternalDependenciesInterface &externalDe
&& model()->rewriterView()->errors().isEmpty()) {
DesignDocument *doc = QmlDesignerPlugin::instance()->currentDesignDocument();
if (doc && !doc->inFileComponentModelActive())
- ensureMaterialLibraryNode();
+ Utils3D::ensureMaterialLibraryNode(this);
if (m_qmlBackEnd && m_qmlBackEnd->contextObject())
- m_qmlBackEnd->contextObject()->setHasMaterialLibrary(materialLibraryNode().isValid());
+ m_qmlBackEnd->contextObject()->setHasMaterialLibrary(
+ Utils3D::materialLibraryNode(this).isValid());
m_ensureMatLibTimer.stop();
}
});
@@ -414,14 +416,17 @@ void MaterialEditorView::handleToolBarAction(int action)
if (!model())
break;
executeInTransaction(__FUNCTION__, [&] {
- ModelNode matLib = materialLibraryNode();
+ ModelNode matLib = Utils3D::materialLibraryNode(this);
if (!matLib.isValid())
return;
-
+#ifdef QDS_USE_PROJECTSTORAGE
+ ModelNode newMatNode = createModelNode("PrincipledMaterial");
+#else
NodeMetaInfo metaInfo = model()->qtQuick3DPrincipledMaterialMetaInfo();
ModelNode newMatNode = createModelNode("QtQuick3D.PrincipledMaterial",
metaInfo.majorVersion(),
metaInfo.minorVersion());
+#endif
renameMaterial(newMatNode, "New Material");
matLib.defaultNodeListProperty().reparentHere(newMatNode);
});
@@ -524,12 +529,17 @@ void MaterialEditorView::handlePreviewModelChanged(const QString &modelStr)
void MaterialEditorView::setupQmlBackend()
{
+#ifdef QDS_USE_PROJECTSTORAGE
+// TODO unify implementation with property editor view
+#else
+
QUrl qmlPaneUrl;
QUrl qmlSpecificsUrl;
QString specificQmlData;
QString currentTypeName;
- if (m_selectedMaterial.isValid() && m_hasQuick3DImport && (materialLibraryNode().isValid() || m_hasMaterialRoot)) {
+ if (m_selectedMaterial.isValid() && m_hasQuick3DImport
+ && (Utils3D::materialLibraryNode(this).isValid() || m_hasMaterialRoot)) {
qmlPaneUrl = QUrl::fromLocalFile(materialEditorResourcesPath() + "/MaterialEditorPane.qml");
TypeName diffClassName;
@@ -582,7 +592,8 @@ void MaterialEditorView::setupQmlBackend()
currentQmlBackend->widget()->installEventFilter(this);
currentQmlBackend->contextObject()->setHasQuick3DImport(m_hasQuick3DImport);
- currentQmlBackend->contextObject()->setHasMaterialLibrary(materialLibraryNode().isValid());
+ currentQmlBackend->contextObject()->setHasMaterialLibrary(
+ Utils3D::materialLibraryNode(this).isValid());
currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData);
currentQmlBackend->contextObject()->setCurrentType(currentTypeName);
currentQmlBackend->contextObject()->setIsQt6Project(externalDependencies().isQt6Project());
@@ -599,6 +610,7 @@ void MaterialEditorView::setupQmlBackend()
m_stackedWidget->setCurrentWidget(m_qmlBackEnd->widget());
m_stackedWidget->setMinimumSize({400, 300});
+#endif
}
void MaterialEditorView::commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value)
@@ -1028,14 +1040,17 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material)
QList<AbstractProperty> dynamicProps;
executeInTransaction(__FUNCTION__, [&] {
- ModelNode matLib = materialLibraryNode();
+ ModelNode matLib = Utils3D::materialLibraryNode(this);
if (!matLib.isValid())
return;
// create the duplicate material
+#ifdef QDS_USE_PROJECTSTORAGE
+ QmlObjectNode duplicateMat = createModelNode(matType);
+#else
NodeMetaInfo metaInfo = model()->metaInfo(matType);
QmlObjectNode duplicateMat = createModelNode(matType, metaInfo.majorVersion(), metaInfo.minorVersion());
-
+#endif
duplicateMatNode = duplicateMat.modelNode();
// set name and id
diff --git a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp
index 04c2ebd9e6..58b4c42749 100644
--- a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp
+++ b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp
@@ -97,9 +97,13 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i
} else if (insertInfo.isQtQuick3DMaterial()) {
if (parentInfo.isQtQuick3DModel())
propertyList.append("materials");
+#ifdef QDS_USE_PROJECTSTORAGE
+// TODO add the types here or use the module
+#else
} else if (insertInfo.typeName().startsWith("ComponentBundles.MaterialBundle")) {
if (parentInfo.isQtQuick3DModel())
propertyList.append("materials");
+#endif
} else if (insertInfo.isQtQuick3DBakedLightmap()) {
if (parentInfo.isQtQuick3DModel())
propertyList.append("bakedLightmap");
diff --git a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp
index aacaf6dc0d..223e04fd96 100644
--- a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp
+++ b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp
@@ -9,9 +9,8 @@
#include "navigatortreeview.h"
#include "navigatorwidget.h"
#include "choosefrompropertylistdialog.h"
-#include "qproxystyle.h"
-
#include <model/modelutils.h>
+#include <dialogutils.h>
#include <modelnodecontextmenu.h>
#include <theme.h>
#include <qmldesignerconstants.h>
@@ -169,8 +168,7 @@ static void setId(const QModelIndex &index, const QString &newId)
return;
if (!ModelNode::isValidId(newId)) {
- Core::AsynchronousMessageBox::warning(NavigatorTreeView::tr("Invalid Id"),
- NavigatorTreeView::tr("%1 is an invalid id.").arg(newId));
+ DialogUtils::showWarningForInvalidId(newId);
} else if (modelNode.view()->hasId(newId)) {
Core::AsynchronousMessageBox::warning(NavigatorTreeView::tr("Invalid Id"),
NavigatorTreeView::tr("%1 already exists.").arg(newId));
diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
index 3bfe26c74d..d305753ead 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
+++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
@@ -23,10 +23,11 @@
#include <nodehints.h>
#include <nodelistproperty.h>
#include <nodeproperty.h>
-#include <rewritingexception.h>
-#include <variantproperty.h>
#include <qmldesignerconstants.h>
#include <qmlitemnode.h>
+#include <rewritingexception.h>
+#include <utils3d.h>
+#include <variantproperty.h>
#include <qmlprojectmanager/qmlproject.h>
@@ -796,8 +797,10 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in
moveNodesInteractive(targetProperty, newModelNodeList, targetRowNumber);
}
- if (newQmlObjectNode.isValid())
+ if (newQmlObjectNode.isValid()) {
m_view->setSelectedModelNode(newQmlObjectNode.modelNode());
+ m_view->emitCustomNotification("item_library_created_by_drop", {newQmlObjectNode});
+ }
}
}
}
@@ -933,11 +936,11 @@ bool NavigatorTreeModel::setData(const QModelIndex &index, const QVariant &value
if (index.column() == ColumnType::Alias && role == Qt::CheckStateRole) {
m_view->handleChangedExport(modelNode, value.toInt() != 0);
} else if (index.column() == ColumnType::Visibility && role == Qt::CheckStateRole) {
- if (m_view->isPartOfMaterialLibrary(modelNode))
+ if (Utils3D::isPartOfMaterialLibrary(modelNode) || QmlItemNode(modelNode).isEffectItem())
return false;
QmlVisualNode(modelNode).setVisibilityOverride(value.toInt() == 0);
} else if (index.column() == ColumnType::Lock && role == Qt::CheckStateRole) {
- if (m_view->isPartOfMaterialLibrary(modelNode))
+ if (Utils3D::isPartOfMaterialLibrary(modelNode))
return false;
modelNode.setLocked(value.toInt() != 0);
}
diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp
index 5cea0aab4e..090beb6471 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp
+++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp
@@ -271,14 +271,20 @@ void NavigatorView::dragStarted(QMimeData *mimeData)
} else if (mimeData->hasFormat(Constants::MIME_TYPE_TEXTURE)) {
qint32 internalId = mimeData->data(Constants::MIME_TYPE_TEXTURE).toInt();
ModelNode texNode = modelNodeForInternalId(internalId);
-
+#ifdef QDS_USE_PROJECTSTORAGE
+ m_widget->setDragType(texNode.type());
+#else
m_widget->setDragType(texNode.metaInfo().typeName());
+#endif
m_widget->update();
} else if (mimeData->hasFormat(Constants::MIME_TYPE_MATERIAL)) {
qint32 internalId = mimeData->data(Constants::MIME_TYPE_MATERIAL).toInt();
ModelNode matNode = modelNodeForInternalId(internalId);
-
+#ifdef QDS_USE_PROJECTSTORAGE
+ m_widget->setDragType(matNode.type());
+#else
m_widget->setDragType(matNode.metaInfo().typeName());
+#endif
m_widget->update();
} else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_TEXTURE)) {
m_widget->setDragType(Constants::MIME_TYPE_BUNDLE_TEXTURE);
@@ -700,7 +706,7 @@ void NavigatorView::updateItemSelection()
blockSelectionChangedSignal(blocked);
if (!selectedModelNodes().isEmpty())
- treeWidget()->scrollTo(indexForModelNode(selectedModelNodes().constFirst()));
+ treeWidget()->scrollTo(indexForModelNode(selectedModelNodes().constLast()));
// make sure selected nodes are visible
for (const QModelIndex &selectedIndex : itemSelection.indexes()) {
diff --git a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp
index 3c419a3e82..2942ca86c7 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp
@@ -114,9 +114,9 @@ QList<FileResourcesItem> FileResourcesModel::model() const
return m_model;
}
-void FileResourcesModel::openFileDialog()
+void FileResourcesModel::openFileDialog(const QString &customPath)
{
- QString resourcePath = m_path.toLocalFile();
+ QString resourcePath = customPath.isEmpty() ? m_path.toLocalFile() : customPath;
bool resourcePathChanged = m_lastResourcePath != resourcePath;
m_lastResourcePath = resourcePath;
diff --git a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h
index 4bca3f531a..d382424c8a 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h
@@ -69,7 +69,7 @@ public:
void refreshModel();
- Q_INVOKABLE void openFileDialog();
+ Q_INVOKABLE void openFileDialog(const QString &customPath = {});
Q_INVOKABLE QString resolve(const QString &relative) const;
Q_INVOKABLE bool isLocal(const QString &path) const;
diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp
index 53af36c7ae..8bedabd83a 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp
@@ -695,6 +695,10 @@ void GradientModel::resetPuppet()
QmlDesigner::ModelNode GradientModel::createGradientNode()
{
+#ifdef QDS_USE_PROJECTSTORAGE
+ QmlDesigner::TypeName typeName = m_gradientTypeName.toUtf8();
+ auto gradientNode = view()->createModelNode(typeName);
+#else
QmlDesigner::TypeName fullTypeName = m_gradientTypeName.toUtf8();
if (m_gradientTypeName == "Gradient") {
@@ -709,7 +713,7 @@ QmlDesigner::ModelNode GradientModel::createGradientNode()
int majorVersion = metaInfo.majorVersion();
auto gradientNode = view()->createModelNode(fullTypeName, majorVersion, minorVersion);
-
+#endif
setupGradientProperties(gradientNode);
return gradientNode;
@@ -717,6 +721,9 @@ QmlDesigner::ModelNode GradientModel::createGradientNode()
QmlDesigner::ModelNode GradientModel::createGradientStopNode()
{
+#ifdef QDS_USE_PROJECTSTORAGE
+ return view()->createModelNode("GradientStop");
+#else
QByteArray fullTypeName = "QtQuick.GradientStop";
auto metaInfo = model()->metaInfo(fullTypeName);
@@ -724,6 +731,7 @@ QmlDesigner::ModelNode GradientModel::createGradientStopNode()
int majorVersion = metaInfo.majorVersion();
return view()->createModelNode(fullTypeName, majorVersion, minorVersion);
+#endif
}
void GradientModel::deleteGradientNode(bool saveTransaction)
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp
index 03199d274d..591ce5a57f 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp
@@ -2,17 +2,16 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "propertyeditorcontextobject.h"
-#include "timelineeditor/easingcurvedialog.h"
-
-#include <abstractview.h>
-#include <nodemetainfo.h>
-#include <rewritingexception.h>
-#include <qmldesignerconstants.h>
-#include <qml3dnode.h>
-#include <qmldesignerplugin.h>
-#include <qmlmodelnodeproxy.h>
-#include <qmlobjectnode.h>
-#include <qmltimeline.h>
+
+#include "abstractview.h"
+#include "easingcurvedialog.h"
+#include "nodemetainfo.h"
+#include "qmldesignerconstants.h"
+#include "qml3dnode.h"
+#include "qmldesignerplugin.h"
+#include "qmlmodelnodeproxy.h"
+#include "qmlobjectnode.h"
+#include "qmltimeline.h"
#include <coreplugin/messagebox.h>
#include <utils/algorithm.h>
@@ -73,13 +72,15 @@ namespace QmlDesigner {
static Q_LOGGING_CATEGORY(urlSpecifics, "qtc.propertyeditor.specifics", QtWarningMsg)
- PropertyEditorContextObject::PropertyEditorContextObject(QObject *parent)
+ PropertyEditorContextObject::PropertyEditorContextObject(Quick2PropertyEditorView *widget,
+ QObject *parent)
: QObject(parent)
, m_isBaseState(false)
, m_selectionChanged(false)
, m_backendValues(nullptr)
, m_qmlComponent(nullptr)
, m_qmlContext(nullptr)
+ , m_quickWidget(widget)
{}
QString PropertyEditorContextObject::convertColorToString(const QVariant &color)
@@ -620,6 +621,20 @@ void PropertyEditorContextObject::verifyInsightImport()
m_model->changeImports({import}, {});
}
+QRect PropertyEditorContextObject::screenRect() const
+{
+ if (m_quickWidget && m_quickWidget->screen())
+ return m_quickWidget->screen()->availableGeometry();
+ return {};
+}
+
+QPoint PropertyEditorContextObject::globalPos(const QPoint &point) const
+{
+ if (m_quickWidget)
+ return m_quickWidget->mapToGlobal(point);
+ return point;
+}
+
void EasingCurveEditor::registerDeclarativeType()
{
qmlRegisterType<EasingCurveEditor>("HelperWidgets", 2, 0, "EasingCurveEditor");
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h
index 2f5ad4b4eb..ba5896d798 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h
@@ -3,8 +3,9 @@
#pragma once
-#include <model.h>
-#include <modelnode.h>
+#include "model.h"
+#include "modelnode.h"
+#include "quick2propertyeditorview.h"
#include <QColor>
#include <QObject>
@@ -14,8 +15,6 @@
#include <QQmlPropertyMap>
#include <QUrl>
-#include <QMouseEvent>
-
namespace QmlDesigner {
class PropertyEditorContextObject : public QObject
@@ -42,9 +41,9 @@ class PropertyEditorContextObject : public QObject
Q_PROPERTY(bool hasActiveTimeline READ hasActiveTimeline NOTIFY hasActiveTimelineChanged)
- Q_PROPERTY(QQmlPropertyMap* backendValues READ backendValues WRITE setBackendValues NOTIFY backendValuesChanged)
+ Q_PROPERTY(QQmlPropertyMap *backendValues READ backendValues WRITE setBackendValues NOTIFY backendValuesChanged)
- Q_PROPERTY(QQmlComponent* specificQmlComponent READ specificQmlComponent NOTIFY specificQmlComponentChanged)
+ Q_PROPERTY(QQmlComponent *specificQmlComponent READ specificQmlComponent NOTIFY specificQmlComponentChanged)
Q_PROPERTY(bool hasMultiSelection READ hasMultiSelection WRITE setHasMultiSelection NOTIFY
hasMultiSelectionChanged)
@@ -53,7 +52,7 @@ class PropertyEditorContextObject : public QObject
Q_PROPERTY(QStringList insightCategories MEMBER m_insightCategories NOTIFY insightCategoriesChanged)
public:
- PropertyEditorContextObject(QObject *parent = nullptr);
+ PropertyEditorContextObject(Quick2PropertyEditorView *widget, QObject *parent = nullptr);
QUrl specificsUrl() const {return m_specificsUrl; }
QString specificQmlData() const {return m_specificQmlData; }
@@ -63,7 +62,7 @@ public:
bool isBaseState() const { return m_isBaseState; }
bool selectionChanged() const { return m_selectionChanged; }
- QQmlPropertyMap* backendValues() const { return m_backendValues; }
+ QQmlPropertyMap *backendValues() const { return m_backendValues; }
Q_INVOKABLE QString convertColorToString(const QVariant &color);
Q_INVOKABLE QColor colorFromString(const QString &colorString);
@@ -92,6 +91,9 @@ public:
Q_INVOKABLE void verifyInsightImport();
+ Q_INVOKABLE QRect screenRect() const;
+ Q_INVOKABLE QPoint globalPos(const QPoint &point) const;
+
QString activeDragSuffix() const;
void setActiveDragSuffix(const QString &suffix);
@@ -154,7 +156,7 @@ public slots:
void setSelectionChanged(bool newSelectionChanged);
- void setBackendValues(QQmlPropertyMap* newBackendValues);
+ void setBackendValues(QQmlPropertyMap *newBackendValues);
void setModel(Model *model);
@@ -171,7 +173,7 @@ private:
bool m_isBaseState;
bool m_selectionChanged;
- QQmlPropertyMap* m_backendValues;
+ QQmlPropertyMap *m_backendValues;
int m_majorVersion = 1;
int m_minorVersion = 1;
@@ -179,6 +181,7 @@ private:
int m_minorQtQuickVersion = -1;
QQmlComponent *m_qmlComponent;
QQmlContext *m_qmlContext;
+ Quick2PropertyEditorView *m_quickWidget = nullptr;
QPoint m_lastPos;
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp
index 393183f57c..7f1ab00bb9 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp
@@ -10,11 +10,12 @@
#include <auxiliarydataproperties.h>
#include <bindingproperty.h>
#include <nodemetainfo.h>
-#include <variantproperty.h>
+#include <projectstorage/sourcepathcache.h>
#include <qmldesignerconstants.h>
#include <qmldesignerplugin.h>
#include <qmlobjectnode.h>
#include <qmltimeline.h>
+#include <variantproperty.h>
#include <theme.h>
@@ -84,7 +85,7 @@ PropertyEditorQmlBackend::PropertyEditorQmlBackend(PropertyEditorView *propertyE
: m_view(new Quick2PropertyEditorView(imageCache))
, m_propertyEditorTransaction(new PropertyEditorTransaction(propertyEditor))
, m_dummyPropertyEditorValue(new PropertyEditorValue())
- , m_contextObject(new PropertyEditorContextObject())
+ , m_contextObject(new PropertyEditorContextObject(m_view))
{
m_view->engine()->setOutputWarningsToStandardError(QmlDesignerPlugin::instance()
->settings().value(DesignerSettingsKey::SHOW_PROPERTYEDITOR_WARNINGS).toBool());
@@ -514,6 +515,12 @@ void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const Q
NodeMetaInfo metaInfo = qmlObjectNode.modelNode().metaInfo();
+#ifdef QDS_USE_PROJECTSTORAGE
+ contextObject()->setMajorVersion(-1);
+ contextObject()->setMinorVersion(-1);
+ contextObject()->setMajorQtQuickVersion(-1);
+ contextObject()->setMinorQtQuickVersion(-1);
+#else
if (metaInfo.isValid()) {
contextObject()->setMajorVersion(metaInfo.majorVersion());
contextObject()->setMinorVersion(metaInfo.minorVersion());
@@ -523,7 +530,7 @@ void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const Q
contextObject()->setMajorQtQuickVersion(-1);
contextObject()->setMinorQtQuickVersion(-1);
}
-
+#endif
contextObject()->setMajorQtQuickVersion(qmlObjectNode.view()->majorQtQuickVersion());
contextObject()->setMinorQtQuickVersion(qmlObjectNode.view()->minorQtQuickVersion());
@@ -619,10 +626,20 @@ inline bool dotPropertyHeuristic(const QmlObjectNode &node, const NodeMetaInfo &
return true;
}
+#ifndef QDS_USE_PROJECTSTORAGE
QString PropertyEditorQmlBackend::templateGeneration(const NodeMetaInfo &metaType,
const NodeMetaInfo &superType,
const QmlObjectNode &node)
{
+ // If we have dynamically generated specifics file for the type, prefer using it
+ QUrl dynamicUrl = PropertyEditorQmlBackend::getQmlFileUrl(
+ metaType.typeName() + "SpecificsDynamic", metaType);
+
+ if (checkIfUrlExists(dynamicUrl)) {
+ Utils::FilePath fp = Utils::FilePath::fromString(fileFromUrl(dynamicUrl));
+ return QString::fromUtf8(fp.fileContents().value_or(QByteArray()));
+ }
+
if (!templateConfiguration() || !templateConfiguration()->isValid())
return QString();
@@ -749,7 +766,8 @@ QString PropertyEditorQmlBackend::templateGeneration(const NodeMetaInfo &metaTyp
qmlTemplate += "Column {\n";
qmlTemplate += "width: parent.width\n";
- if (node.modelNode().isComponent())
+ bool isEditableComponent = node.modelNode().isComponent() && !QmlItemNode(node).isEffectItem();
+ if (isEditableComponent)
qmlTemplate += "ComponentButton {}\n";
QString qmlInnerTemplate = "";
@@ -839,6 +857,7 @@ QUrl PropertyEditorQmlBackend::getQmlFileUrl(const TypeName &relativeTypeName, c
{
return fileToUrl(locateQmlFile(info, QString::fromUtf8(fixTypeNameForPanes(relativeTypeName) + ".qml")));
}
+#endif // QDS_USE_PROJECTSTORAGE
TypeName PropertyEditorQmlBackend::fixTypeNameForPanes(const TypeName &typeName)
{
@@ -873,11 +892,13 @@ NodeMetaInfo PropertyEditorQmlBackend::findCommonAncestor(const ModelNode &node)
return node.metaInfo();
}
+#ifndef QDS_USE_PROJECTSTORAGE
TypeName PropertyEditorQmlBackend::qmlFileName(const NodeMetaInfo &nodeInfo)
{
const TypeName fixedTypeName = fixTypeNameForPanes(nodeInfo.typeName());
return fixedTypeName + "Pane.qml";
}
+#endif
QUrl PropertyEditorQmlBackend::fileToUrl(const QString &filePath) {
QUrl fileUrl;
@@ -953,6 +974,7 @@ void PropertyEditorQmlBackend::setValueforAuxiliaryProperties(const QmlObjectNod
setValue(qmlObjectNode, propertyName, qmlObjectNode.modelNode().auxiliaryDataWithDefault(key));
}
+#ifndef QDS_USE_PROJECTSTORAGE
std::tuple<QUrl, NodeMetaInfo> PropertyEditorQmlBackend::getQmlUrlForMetaInfo(const NodeMetaInfo &metaInfo)
{
QString className;
@@ -1016,7 +1038,7 @@ QString PropertyEditorQmlBackend::locateQmlFile(const NodeMetaInfo &info, const
return QFileInfo::exists(possibleFilePath);
});
}
-
+#endif // QDS_USE_PROJECTSTORAGE
} //QmlDesigner
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h
index 64cca97207..b677258488 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h
@@ -48,10 +48,13 @@ public:
PropertyEditorValue *propertyValueForName(const QString &propertyName);
static QString propertyEditorResourcesPath();
- static QString templateGeneration(const NodeMetaInfo &type, const NodeMetaInfo &superType, const QmlObjectNode &node);
-
+#ifndef QDS_USE_PROJECTSTORAGE
+ static QString templateGeneration(const NodeMetaInfo &type,
+ const NodeMetaInfo &superType,
+ const QmlObjectNode &node);
static QUrl getQmlFileUrl(const TypeName &relativeTypeName, const NodeMetaInfo &info);
static std::tuple<QUrl, NodeMetaInfo> getQmlUrlForMetaInfo(const NodeMetaInfo &modelNode);
+#endif
static bool checkIfUrlExists(const QUrl &url);
@@ -80,10 +83,12 @@ private:
PropertyEditorView *propertyEditor,
const NodeMetaInfo &type);
- static TypeName qmlFileName(const NodeMetaInfo &nodeInfo);
static QUrl fileToUrl(const QString &filePath);
static QString fileFromUrl(const QUrl &url);
+#ifndef QDS_USE_PROJECTSTORAGE
+ static TypeName qmlFileName(const NodeMetaInfo &nodeInfo);
static QString locateQmlFile(const NodeMetaInfo &info, const QString &relativePath);
+#endif
static TypeName fixTypeNameForPanes(const TypeName &typeName);
private:
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp
index f042df5241..c1d91f907a 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp
@@ -153,7 +153,8 @@ void PropertyEditorValue::setExpressionWithEmit(const QString &expression)
if (m_expression != expression) {
setExpression(expression);
m_value.clear();
- emit expressionChanged(nameAsQString()); // Note that we set the name in this case
+ emit expressionChanged(nameAsQString());
+ emit expressionChangedQml();// Note that we set the name in this case
}
}
@@ -180,7 +181,7 @@ bool PropertyEditorValue::isInSubState() const
bool PropertyEditorValue::isBound() const
{
const QmlObjectNode objectNode(modelNode());
- return objectNode.isValid() && objectNode.hasBindingProperty(name());
+ return m_forceBound || (objectNode.isValid() && objectNode.hasBindingProperty(name()));
}
bool PropertyEditorValue::isInModel() const
@@ -334,6 +335,7 @@ void PropertyEditorValue::resetValue()
m_expression = QString();
emit valueChanged(nameAsQString(), QVariant());
emit expressionChanged({});
+ emit expressionChangedQml();
}
}
@@ -425,6 +427,34 @@ QStringList PropertyEditorValue::getExpressionAsList() const
return generateStringList(expression());
}
+QVector<double> PropertyEditorValue::getExpressionAsVector() const
+{
+ const QRegularExpression rx(
+ QRegularExpression::anchoredPattern("Qt.vector(2|3|4)d\\((.*?)\\)"));
+ const QRegularExpressionMatch match = rx.match(expression());
+ if (!match.hasMatch())
+ return {};
+
+ const QStringList floats = match.captured(2).split(',', Qt::SkipEmptyParts);
+
+ bool ok;
+
+ const int num = match.captured(1).toInt();
+
+ if (num != floats.count())
+ return {};
+
+ QVector<double> ret;
+ for (const QString &number : floats) {
+ ret.append(number.toDouble(&ok));
+
+ if (!ok)
+ return {};
+ }
+
+ return ret;
+}
+
bool PropertyEditorValue::idListAdd(const QString &value)
{
const QmlObjectNode objectNode(modelNode());
@@ -507,6 +537,15 @@ void PropertyEditorValue::openMaterialEditor(int idx)
m_modelNode.view()->emitCustomNotification("select_material", {}, {idx});
}
+void PropertyEditorValue::setForceBound(bool b)
+{
+ if (m_forceBound == b)
+ return;
+ m_forceBound = b;
+
+ emit isBoundChanged();
+}
+
QStringList PropertyEditorValue::generateStringList(const QString &string) const
{
QString copy = string;
@@ -579,6 +618,15 @@ void PropertyEditorNodeWrapper::add(const QString &type)
TypeName propertyType = type.toUtf8();
if ((m_editorValue && m_editorValue->modelNode().isValid())) {
+#ifdef QDS_USE_PROJECTSTORAGE
+ if (propertyType.isEmpty()) {
+ auto node = m_editorValue->modelNode();
+ auto metaInfo = node.metaInfo().property(m_editorValue->name()).propertyType();
+ auto exportedTypeName = node.model()->exportedTypeNameForMetaInfo(metaInfo);
+ propertyType = exportedTypeName.name.toQByteArray();
+ }
+ m_modelNode = m_editorValue->modelNode().view()->createModelNode(propertyType);
+#else
if (propertyType.isEmpty()) {
propertyType = m_editorValue->modelNode()
.metaInfo()
@@ -589,6 +637,7 @@ void PropertyEditorNodeWrapper::add(const QString &type)
while (propertyType.contains('*')) // strip star
propertyType.chop(1);
m_modelNode = m_editorValue->modelNode().view()->createModelNode(propertyType, 4, 7);
+#endif
m_editorValue->modelNode().nodeAbstractProperty(m_editorValue->name()).reparentHere(m_modelNode);
if (!m_modelNode.isValid())
qWarning("PropertyEditorNodeWrapper::add failed");
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h
index 59236c4fe2..f621c9a500 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h
@@ -126,12 +126,15 @@ public:
bool isIdList() const;
Q_INVOKABLE QStringList getExpressionAsList() const;
+ Q_INVOKABLE QVector<double> getExpressionAsVector() const;
Q_INVOKABLE bool idListAdd(const QString &value);
Q_INVOKABLE bool idListRemove(int idx);
Q_INVOKABLE bool idListReplace(int idx, const QString &value);
Q_INVOKABLE void commitDrop(const QString &dropData);
Q_INVOKABLE void openMaterialEditor(int idx);
+ Q_INVOKABLE void setForceBound(bool b);
+
public slots:
void resetValue();
void setEnumeration(const QString &scope, const QString &name);
@@ -143,6 +146,8 @@ signals:
void expressionChanged(const QString &name); // HACK - We use the same notifer for the backend and frontend.
// If name is empty the signal is used for QML.
+ void expressionChangedQml();
+
void exportPropertyAsAliasRequested(const QString &name);
void removeAliasExportRequested(const QString &name);
@@ -168,6 +173,7 @@ private:
bool m_hasActiveDrag = false;
bool m_isValid = false; // if the property value belongs to a non-existing complexProperty it is invalid
PropertyEditorNodeWrapper *m_complexNode;
+ bool m_forceBound = false;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp
index 91e62bfef3..1ff098f4ea 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp
@@ -96,8 +96,9 @@ PropertyEditorView::~PropertyEditorView()
qDeleteAll(m_qmlBackendHash);
}
-void PropertyEditorView::setupPane(const TypeName &typeName)
+void PropertyEditorView::setupPane([[maybe_unused]] const TypeName &typeName)
{
+#ifndef QDS_USE_PROJECTSTORAGE
NodeMetaInfo metaInfo = model()->metaInfo(typeName);
QUrl qmlFile = PropertyEditorQmlBackend::getQmlFileUrl("Qt/ItemPane", metaInfo);
@@ -118,6 +119,7 @@ void PropertyEditorView::setupPane(const TypeName &typeName)
} else {
qmlBackend->initialSetup(typeName, qmlSpecificsFile, this);
}
+#endif // QDS_USE_PROJECTSTORAGE
}
void PropertyEditorView::changeValue(const QString &name)
@@ -448,6 +450,7 @@ void PropertyEditorView::resetView()
namespace {
+#ifndef QDS_USE_PROJECTSTORAGE
[[maybe_unused]] std::tuple<NodeMetaInfo, QUrl> diffType(const NodeMetaInfo &commonAncestor,
const NodeMetaInfo &specificsClassMetaInfo)
{
@@ -483,6 +486,7 @@ namespace {
return {};
}
+#endif // QDS_USE_PROJECTSTORAGE
PropertyEditorQmlBackend *getQmlBackend(QHash<QString, PropertyEditorQmlBackend *> &qmlBackendHash,
const QUrl &qmlFileUrl,
@@ -572,61 +576,56 @@ void setupWidget(PropertyEditorQmlBackend *currentQmlBackend,
void PropertyEditorView::setupQmlBackend()
{
- if constexpr (useProjectStorage()) {
- auto selfAndPrototypes = m_selectedNode.metaInfo().selfAndPrototypes();
- auto specificQmlData = m_propertyEditorComponentGenerator.create(selfAndPrototypes,
- m_selectedNode.isComponent());
- auto [panePath, specificsPath] = findPaneAndSpecificsPath(selfAndPrototypes,
- model()->pathCache());
- PropertyEditorQmlBackend *currentQmlBackend = getQmlBackend(m_qmlBackendHash,
- QUrl::fromLocalFile(
- QString{panePath}),
- m_imageCache,
- m_stackedWidget,
- this);
-
- setupCurrentQmlBackend(currentQmlBackend,
- m_selectedNode,
- QUrl::fromLocalFile(QString{specificsPath}),
- currentState(),
- this,
- specificQmlData);
-
- setupWidget(currentQmlBackend, this, m_stackedWidget);
-
- m_qmlBackEndForCurrentType = currentQmlBackend;
-
- setupInsight(rootModelNode(), currentQmlBackend);
- } else {
- const NodeMetaInfo commonAncestor = PropertyEditorQmlBackend::findCommonAncestor(
- m_selectedNode);
+#ifdef QDS_USE_PROJECTSTORAGE
+ auto selfAndPrototypes = m_selectedNode.metaInfo().selfAndPrototypes();
+ bool isEditableComponent = m_selectedNode.isComponent()
+ && !QmlItemNode(m_selectedNode).isEffectItem();
+ auto specificQmlData = m_propertyEditorComponentGenerator.create(selfAndPrototypes,
+ isEditableComponent);
+ auto [panePath, specificsPath] = findPaneAndSpecificsPath(selfAndPrototypes, model()->pathCache());
+ PropertyEditorQmlBackend *currentQmlBackend = getQmlBackend(m_qmlBackendHash,
+ QUrl::fromLocalFile(QString{panePath}),
+ m_imageCache,
+ m_stackedWidget,
+ this);
- const auto [qmlFileUrl, specificsClassMetaInfo] = PropertyEditorQmlBackend::getQmlUrlForMetaInfo(
- commonAncestor);
+ setupCurrentQmlBackend(currentQmlBackend,
+ m_selectedNode,
+ QUrl::fromLocalFile(QString{specificsPath}),
+ currentState(),
+ this,
+ specificQmlData);
- auto [diffClassMetaInfo, qmlSpecificsFile] = diffType(commonAncestor, specificsClassMetaInfo);
+ setupWidget(currentQmlBackend, this, m_stackedWidget);
- QString specificQmlData = getSpecificQmlData(commonAncestor, m_selectedNode, diffClassMetaInfo);
+ m_qmlBackEndForCurrentType = currentQmlBackend;
- PropertyEditorQmlBackend *currentQmlBackend = getQmlBackend(m_qmlBackendHash,
- qmlFileUrl,
- m_imageCache,
- m_stackedWidget,
- this);
+ setupInsight(rootModelNode(), currentQmlBackend);
+#else
+ const NodeMetaInfo commonAncestor = PropertyEditorQmlBackend::findCommonAncestor(m_selectedNode);
- setupCurrentQmlBackend(currentQmlBackend,
- m_selectedNode,
- qmlSpecificsFile,
- currentState(),
- this,
- specificQmlData);
+ const auto [qmlFileUrl, specificsClassMetaInfo] = PropertyEditorQmlBackend::getQmlUrlForMetaInfo(
+ commonAncestor);
- setupWidget(currentQmlBackend, this, m_stackedWidget);
+ auto [diffClassMetaInfo, qmlSpecificsFile] = diffType(commonAncestor, specificsClassMetaInfo);
- m_qmlBackEndForCurrentType = currentQmlBackend;
+ QString specificQmlData = getSpecificQmlData(commonAncestor, m_selectedNode, diffClassMetaInfo);
- setupInsight(rootModelNode(), currentQmlBackend);
- }
+ PropertyEditorQmlBackend *currentQmlBackend = getQmlBackend(m_qmlBackendHash,
+ qmlFileUrl,
+ m_imageCache,
+ m_stackedWidget,
+ this);
+
+ setupCurrentQmlBackend(
+ currentQmlBackend, m_selectedNode, qmlSpecificsFile, currentState(), this, specificQmlData);
+
+ setupWidget(currentQmlBackend, this, m_stackedWidget);
+
+ m_qmlBackEndForCurrentType = currentQmlBackend;
+
+ setupInsight(rootModelNode(), currentQmlBackend);
+#endif // QDS_USE_PROJECTSTORAGE
}
void PropertyEditorView::commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value)
diff --git a/src/plugins/qmldesigner/components/stateseditor/propertymodel.cpp b/src/plugins/qmldesigner/components/stateseditor/propertymodel.cpp
index 0a72d60ef7..db07dd2208 100644
--- a/src/plugins/qmldesigner/components/stateseditor/propertymodel.cpp
+++ b/src/plugins/qmldesigner/components/stateseditor/propertymodel.cpp
@@ -64,12 +64,19 @@ QVariant PropertyModel::data(const QModelIndex &index, int role) const
if (!propertyChanges.target().isValid())
return {};
-
+#ifdef QDS_USE_PROJECTSTORAGE
+ return propertyChanges.target()
+ .metaInfo()
+ .property(m_properties.at(index.row()).name())
+ .propertyType()
+ .displayName();
+#else
return propertyChanges.target()
.metaInfo()
.property(m_properties.at(index.row()).name())
.propertyType()
.typeName();
+#endif
}
}
return {};
diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp
index 2ff5adedbe..33351baf5e 100644
--- a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp
+++ b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp
@@ -343,6 +343,9 @@ bool StatesEditorModel::renameActiveStateGroup(const QString &name)
void StatesEditorModel::addStateGroup(const QString &name)
{
m_statesEditorView->executeInTransaction("createStateGroup", [this, name]() {
+#ifdef QDS_USE_PROJECTSTORAGE
+ auto stateGroupNode = m_statesEditorView->createModelNode("StateGroup");
+#else
const TypeName typeName = "QtQuick.StateGroup";
auto metaInfo = m_statesEditorView->model()->metaInfo(typeName);
int minorVersion = metaInfo.minorVersion();
@@ -350,6 +353,7 @@ void StatesEditorModel::addStateGroup(const QString &name)
auto stateGroupNode = m_statesEditorView->createModelNode(typeName,
majorVersion,
minorVersion);
+#endif
stateGroupNode.setIdWithoutRefactoring(m_statesEditorView->model()->generateNewId(name));
m_statesEditorView->rootModelNode().defaultNodeAbstractProperty().reparentHere(
diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp
index 0cf01cf620..80cf5693d2 100644
--- a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp
+++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp
@@ -247,8 +247,10 @@ void TextureEditorQmlBackend::setup(const QmlObjectNode &selectedTextureNode, co
contextObject()->setSelectionChanged(false);
+#ifndef QDS_USE_PROJECTSTORAGE
NodeMetaInfo metaInfo = selectedTextureNode.modelNode().metaInfo();
contextObject()->setMajorVersion(metaInfo.isValid() ? metaInfo.majorVersion() : -1);
+#endif
} else {
context()->setContextProperty("hasTexture", QVariant(false));
}
diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp
index 47d85c4dbc..5de3730c97 100644
--- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp
+++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp
@@ -34,6 +34,7 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
+#include <utils3d.h>
#include <qmldesignerplugin.h>
#include <QApplication>
@@ -65,9 +66,10 @@ TextureEditorView::TextureEditorView(AsynchronousImageCache &imageCache,
&& model()->rewriterView()->errors().isEmpty()) {
DesignDocument *doc = QmlDesignerPlugin::instance()->currentDesignDocument();
if (doc && !doc->inFileComponentModelActive())
- ensureMaterialLibraryNode();
+ Utils3D::ensureMaterialLibraryNode(this);
if (m_qmlBackEnd && m_qmlBackEnd->contextObject())
- m_qmlBackEnd->contextObject()->setHasMaterialLibrary(materialLibraryNode().isValid());
+ m_qmlBackEnd->contextObject()->setHasMaterialLibrary(
+ Utils3D::materialLibraryNode(this).isValid());
m_ensureMatLibTimer.stop();
}
});
@@ -375,13 +377,17 @@ void TextureEditorView::handleToolBarAction(int action)
if (!model())
break;
executeInTransaction("TextureEditorView:handleToolBarAction", [&] {
- ModelNode matLib = materialLibraryNode();
+ ModelNode matLib = Utils3D::materialLibraryNode(this);
if (!matLib.isValid())
return;
-
+#ifdef QDS_USE_PROJECTSTORAGE
+ ModelNode newTextureNode = createModelNode("Texture");
+#else
NodeMetaInfo metaInfo = model()->metaInfo("QtQuick3D.Texture");
- ModelNode newTextureNode = createModelNode("QtQuick3D.Texture", metaInfo.majorVersion(),
- metaInfo.minorVersion());
+ ModelNode newTextureNode = createModelNode("QtQuick3D.Texture",
+ metaInfo.majorVersion(),
+ metaInfo.minorVersion());
+#endif
newTextureNode.validId();
matLib.defaultNodeListProperty().reparentHere(newTextureNode);
});
@@ -406,11 +412,15 @@ void TextureEditorView::handleToolBarAction(int action)
void TextureEditorView::setupQmlBackend()
{
+#ifdef QDS_USE_PROJECTSTORAGE
+// This is an copy of the property editor code which is already rewritten. Please reuse that code.
+#else
QUrl qmlPaneUrl;
QUrl qmlSpecificsUrl;
QString specificQmlData;
- if (m_selectedTexture.isValid() && m_hasQuick3DImport && (materialLibraryNode().isValid() || m_hasTextureRoot)) {
+ if (m_selectedTexture.isValid() && m_hasQuick3DImport
+ && (Utils3D::materialLibraryNode(this).isValid() || m_hasTextureRoot)) {
qmlPaneUrl = QUrl::fromLocalFile(textureEditorResourcesPath() + "/TextureEditorPane.qml");
TypeName diffClassName;
@@ -457,7 +467,8 @@ void TextureEditorView::setupQmlBackend()
currentQmlBackend->widget()->installEventFilter(this);
currentQmlBackend->contextObject()->setHasQuick3DImport(m_hasQuick3DImport);
- currentQmlBackend->contextObject()->setHasMaterialLibrary(materialLibraryNode().isValid());
+ currentQmlBackend->contextObject()->setHasMaterialLibrary(
+ Utils3D::materialLibraryNode(this).isValid());
currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData);
bool hasValidSelection = QmlObjectNode(m_selectedModel).hasBindingProperty("materials");
currentQmlBackend->contextObject()->setHasSingleModelSelection(hasValidSelection);
@@ -471,6 +482,7 @@ void TextureEditorView::setupQmlBackend()
m_dynamicPropertiesModel->reset();
m_stackedWidget->setCurrentWidget(m_qmlBackEnd->widget());
+#endif
}
void TextureEditorView::commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value)
@@ -745,14 +757,17 @@ void TextureEditorView::duplicateTexture(const ModelNode &texture)
QList<AbstractProperty> dynamicProps;
executeInTransaction(__FUNCTION__, [&] {
- ModelNode matLib = materialLibraryNode();
+ ModelNode matLib = Utils3D::materialLibraryNode(this);
if (!matLib.isValid())
return;
// create the duplicate texture
+#ifdef QDS_USE_PROJECTSTORAGE
+ QmlObjectNode duplicateTex = createModelNode(matType);
+#else
NodeMetaInfo metaInfo = model()->metaInfo(matType);
QmlObjectNode duplicateTex = createModelNode(matType, metaInfo.majorVersion(), metaInfo.minorVersion());
-
+#endif
duplicateTextureNode = duplicateTex .modelNode();
duplicateTextureNode.validId();
diff --git a/src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp b/src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp
index 0f12c6ce2d..b06023242f 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp
@@ -137,7 +137,7 @@ const char settingsFileName[] = "EasingCurves.ini";
QString settingsFullFilePath(const QSettings::Scope &scope)
{
if (scope == QSettings::SystemScope)
- return Core::ICore::installerResourcePath(settingsFileName).toString();
+ return Core::ICore::resourcePath("qmldesigner/%1").toString().arg(settingsFileName);
return Core::ICore::userResourcePath(settingsFileName).toString();
}
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp
index 83551378ee..e65c05c39f 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp
@@ -13,6 +13,7 @@
#include <variantproperty.h>
#include <qmlitemnode.h>
#include <qmlobjectnode.h>
+#include <dialogutils.h>
#include <coreplugin/messagebox.h>
@@ -141,8 +142,7 @@ TimelineAnimationForm::TimelineAnimationForm(QWidget *parent)
bool error = false;
if (!ModelNode::isValidId(newId)) {
- Core::AsynchronousMessageBox::warning(tr("Invalid Id"),
- tr("%1 is an invalid id.").arg(newId));
+ DialogUtils::showWarningForInvalidId(newId);
error = true;
} else if (animation().view()->hasId(newId)) {
Core::AsynchronousMessageBox::warning(tr("Invalid Id"),
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp
index b3d408dc0d..08915b6577 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp
@@ -10,6 +10,7 @@
#include <nodemetainfo.h>
#include <rewritertransaction.h>
#include <variantproperty.h>
+#include <dialogutils.h>
#include <coreplugin/messagebox.h>
@@ -125,8 +126,7 @@ TimelineForm::TimelineForm(QWidget *parent)
bool error = false;
if (!ModelNode::isValidId(newId)) {
- Core::AsynchronousMessageBox::warning(tr("Invalid Id"),
- tr("%1 is an invalid id.").arg(newId));
+ DialogUtils::showWarningForInvalidId(newId);
error = true;
} else if (m_timeline.view()->hasId(newId)) {
Core::AsynchronousMessageBox::warning(tr("Invalid Id"),
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp
index c363f94409..8288e69316 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp
@@ -278,6 +278,7 @@ TimelineWidget *TimelineView::widget() const
return m_timelineWidget;
}
+namespace {
QList<QmlModelState> getAllStates(TimelineView* view)
{
QmlVisualNode visNode(view->rootModelNode());
@@ -315,6 +316,7 @@ void enableInCurrentState(
}
}
}
+} // namespace
const QmlTimeline TimelineView::addNewTimeline()
{
@@ -329,21 +331,24 @@ const QmlTimeline TimelineView::addNewTimeline()
} catch (const Exception &e) {
e.showException();
}
-
+#ifndef QDS_USE_PROJECTSTORAGE
NodeMetaInfo metaInfo = model()->metaInfo(timelineType);
QTC_ASSERT(metaInfo.isValid(), return QmlTimeline());
-
+#endif
ModelNode timelineNode;
- executeInTransaction("TimelineView::addNewTimeline",
- [this, timelineType, metaInfo, &timelineNode] {
+ executeInTransaction("TimelineView::addNewTimeline", [&] {
bool hasTimelines = getTimelines().isEmpty();
QString currentStateName = getStateName(this, hasTimelines);
+#ifdef QDS_USE_PROJECTSTORAGE
+ timelineNode = createModelNode("Timeline");
+#else
timelineNode = createModelNode(timelineType,
metaInfo.majorVersion(),
metaInfo.minorVersion());
+#endif
timelineNode.validId();
timelineNode.variantProperty("startFrame").setValue(0);
@@ -366,21 +371,25 @@ ModelNode TimelineView::addAnimation(QmlTimeline timeline)
QTC_ASSERT(isAttached(), return ModelNode());
+#ifndef QDS_USE_PROJECTSTORAGE
NodeMetaInfo metaInfo = model()->metaInfo(animationType);
QTC_ASSERT(metaInfo.isValid(), return ModelNode());
-
+#endif
ModelNode animationNode;
- executeInTransaction("TimelineView::addAnimation",
- [this, timeline, animationType, metaInfo, &animationNode] {
+ executeInTransaction("TimelineView::addAnimation", [&] {
bool hasAnimations = getAnimations(timeline).isEmpty();
QString currentStateName = getStateName(this, hasAnimations);
+#ifdef QDS_USE_PROJECTSTORAGE
+ animationNode = createModelNode("TimelineAnimation");
+#else
animationNode = createModelNode(animationType,
metaInfo.majorVersion(),
metaInfo.minorVersion());
animationNode.variantProperty("duration").setValue(timeline.duration());
+#endif
animationNode.validId();
animationNode.variantProperty("from").setValue(timeline.startKeyframe());
@@ -635,11 +644,12 @@ void TimelineView::registerActions()
TimelineWidget *TimelineView::createWidget()
{
- if (!m_timelineWidget)
+ if (!m_timelineWidget) {
m_timelineWidget = new TimelineWidget(this);
- auto *timelineContext = new TimelineContext(m_timelineWidget);
- Core::ICore::addContextObject(timelineContext);
+ auto *timelineContext = new TimelineContext(m_timelineWidget);
+ Core::ICore::addContextObject(timelineContext);
+ }
return m_timelineWidget;
}
diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp
index 67043f02d1..e9df928c96 100644
--- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp
+++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp
@@ -7,11 +7,12 @@
#include <crumblebar.h>
#include <designeractionmanager.h>
#include <designmodewidget.h>
-#include <viewmanager.h>
-#include <zoomaction.h>
+#include <dynamiclicensecheck.h>
#include <qmldesignerconstants.h>
#include <qmldesignerplugin.h>
#include <qmleditormenu.h>
+#include <viewmanager.h>
+#include <zoomaction.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/coreconstants.h>
@@ -734,6 +735,11 @@ bool ToolBarBackend::projectOpened() const
return ProjectExplorer::ProjectManager::instance()->startupProject();
}
+bool ToolBarBackend::isSharingEnabled()
+{
+ return QmlDesigner::checkEnterpriseLicense();
+}
+
void ToolBarBackend::launchGlobalAnnotations()
{
QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_EDIT_GLOBAL_ANNOTATION);
diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h
index eb258f9ab7..5d0b0e712a 100644
--- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h
+++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h
@@ -96,6 +96,7 @@ class ToolBarBackend : public QObject
Q_PROPERTY(bool isQt6 READ isQt6 NOTIFY isQt6Changed)
Q_PROPERTY(bool isMCUs READ isMCUs NOTIFY isMCUsChanged)
Q_PROPERTY(bool projectOpened READ projectOpened NOTIFY projectOpenedChanged)
+ Q_PROPERTY(bool isSharingEnabled READ isSharingEnabled NOTIFY isSharingEnabledChanged)
public:
ToolBarBackend(QObject *parent = nullptr);
@@ -144,6 +145,8 @@ public:
bool projectOpened() const;
+ bool isSharingEnabled();
+
static void launchGlobalAnnotations();
signals:
@@ -163,6 +166,7 @@ signals:
void isQt6Changed();
void isMCUsChanged();
void projectOpenedChanged();
+ void isSharingEnabledChanged();
private:
void setupWorkspaces();
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
index c24ebc1ce3..9f9e888823 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
@@ -225,8 +225,19 @@ ModelNode TransitionEditorView::addNewTransition()
if (!idPropertyList.isEmpty()) {
executeInTransaction(
- " TransitionEditorView::addNewTransition", [&transition, idPropertyList, root, this]() {
-
+ " TransitionEditorView::addNewTransition", [&transition, idPropertyList, root, this]() {
+
+#ifdef QDS_USE_PROJECTSTORAGE
+ transition = createModelNode("Transition",
+ {{
+ "from",
+ "*",
+ },
+ {
+ "to",
+ "*",
+ }});
+#else
const NodeMetaInfo transitionMetaInfo = model()->metaInfo("QtQuick.Transition");
transition = createModelNode("QtQuick.Transition",
transitionMetaInfo.majorVersion(),
@@ -239,28 +250,38 @@ ModelNode TransitionEditorView::addNewTransition()
"to",
"*",
}});
- transition.setAuxiliaryData(transitionDurationProperty, 2000);
- transition.validId();
- root.nodeListProperty("transitions").reparentHere(transition);
-
- for (auto it = idPropertyList.cbegin(); it != idPropertyList.cend(); ++it) {
- ModelNode parallelAnimation = createModelNode("QtQuick.ParallelAnimation");
- transition.defaultNodeAbstractProperty().reparentHere(parallelAnimation);
- for (const QString &property : it.value()) {
- ModelNode sequentialAnimation
- = createModelNode("QtQuick.SequentialAnimation");
- parallelAnimation.defaultNodeAbstractProperty().reparentHere(
- sequentialAnimation);
-
+#endif
+ transition.setAuxiliaryData(transitionDurationProperty, 2000);
+ transition.validId();
+ root.nodeListProperty("transitions").reparentHere(transition);
+
+ for (auto it = idPropertyList.cbegin(); it != idPropertyList.cend(); ++it) {
+ ModelNode parallelAnimation = createModelNode("QtQuick.ParallelAnimation");
+ transition.defaultNodeAbstractProperty().reparentHere(parallelAnimation);
+ for (const QString &property : it.value()) {
+ ModelNode sequentialAnimation = createModelNode(
+ "QtQuick.SequentialAnimation");
+ parallelAnimation.defaultNodeAbstractProperty().reparentHere(
+ sequentialAnimation);
+
+#ifdef QDS_USE_PROJECTSTORAGE
+ ModelNode pauseAnimation = createModelNode("PauseAnimation",
+ {{"duration", 50}});
+#else
const NodeMetaInfo pauseMetaInfo = model()->metaInfo("QtQuick.PauseAnimation");
ModelNode pauseAnimation = createModelNode("QtQuick.PauseAnimation",
pauseMetaInfo.majorVersion(),
pauseMetaInfo.minorVersion(),
{{"duration", 50}});
- sequentialAnimation.defaultNodeAbstractProperty().reparentHere(
- pauseAnimation);
-
+#endif
+ sequentialAnimation.defaultNodeAbstractProperty().reparentHere(pauseAnimation);
+
+#ifdef QDS_USE_PROJECTSTORAGE
+ ModelNode propertyAnimation = createModelNode("PropertyAnimation",
+ {{"property", property},
+ {"duration", 150}});
+#else
const NodeMetaInfo propertyMetaInfo = model()->metaInfo("QtQuick.PauseAnimation");
ModelNode propertyAnimation = createModelNode("QtQuick.PropertyAnimation",
@@ -268,11 +289,12 @@ ModelNode TransitionEditorView::addNewTransition()
propertyMetaInfo.minorVersion(),
{{"property", property},
{"duration", 150}});
- propertyAnimation.bindingProperty("target").setExpression(it.key());
- sequentialAnimation.defaultNodeAbstractProperty().reparentHere(
- propertyAnimation);
+#endif
+ propertyAnimation.bindingProperty("target").setExpression(it.key());
+ sequentialAnimation.defaultNodeAbstractProperty().reparentHere(
+ propertyAnimation);
+ }
}
- }
});
} else {
QString properties;
@@ -296,11 +318,12 @@ ModelNode TransitionEditorView::addNewTransition()
TransitionEditorWidget *TransitionEditorView::createWidget()
{
- if (!m_transitionEditorWidget)
+ if (!m_transitionEditorWidget) {
m_transitionEditorWidget = new TransitionEditorWidget(this);
- auto *transitionContext = new TransitionContext(m_transitionEditorWidget);
- Core::ICore::addContextObject(transitionContext);
+ auto *transitionContext = new TransitionContext(m_transitionEditorWidget);
+ Core::ICore::addContextObject(transitionContext);
+ }
return m_transitionEditorWidget;
}
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp
index 1770ba63fc..dcbb0b23b3 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp
@@ -13,6 +13,7 @@
#include <rewritertransaction.h>
#include <variantproperty.h>
#include <qmlitemnode.h>
+#include <dialogutils.h>
#include <coreplugin/messagebox.h>
@@ -45,8 +46,7 @@ TransitionForm::TransitionForm(QWidget *parent)
bool error = false;
if (!ModelNode::isValidId(newId)) {
- Core::AsynchronousMessageBox::warning(tr("Invalid ID"),
- tr("%1 is an invalid ID.").arg(newId));
+ DialogUtils::showWarningForInvalidId(newId);
error = true;
} else if (m_transition.view()->hasId(newId)) {
Core::AsynchronousMessageBox::warning(tr("Invalid ID"),
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp
index 9d32b803cb..97148e664f 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp
@@ -76,6 +76,11 @@ void ImageCacheCollector::start(Utils::SmallStringView name,
AbortCallback abortCallback,
ImageCache::TraceToken traceToken)
{
+#ifdef QDS_USE_PROJECTSTORAGE
+ if (!m_projectStorage || !m_pathCache)
+ return;
+#endif
+
using namespace NanotraceHR::Literals;
auto [collectorTraceToken, flowtoken] = traceToken.beginDurationWithFlow(
"generate image in standard collector"_t);
@@ -86,8 +91,15 @@ void ImageCacheCollector::start(Utils::SmallStringView name,
captureImageMaximumSize);
const QString filePath{name};
+#ifdef QDS_USE_PROJECTSTORAGE
+ auto model = QmlDesigner::Model::create({*m_projectStorage, *m_pathCache},
+ "Item",
+ {},
+ QUrl::fromLocalFile(filePath));
+#else
auto model = QmlDesigner::Model::create("QtQuick/Item", 2, 1);
model->setFileUrl(QUrl::fromLocalFile(filePath));
+#endif
auto textDocument = std::make_unique<QTextDocument>(fileToString(filePath));
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h
index e5230ea2b2..fec68c2894 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h
@@ -5,6 +5,8 @@
#include "imagecachecollectorinterface.h"
+#include <modelfwd.h>
+
#include <QPointer>
QT_BEGIN_NAMESPACE
@@ -62,6 +64,10 @@ private:
QSize captureImageMaximumSize;
ExternalDependenciesInterface &m_externalDependencies;
ImageCacheCollectorNullImageHandling nullImageHandling{};
+#ifdef QDS_USE_PROJECTSTORAGE
+ ProjectStorageType *m_projectStorage = nullptr;
+ PathCacheType *m_pathCache = nullptr;
+#endif
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h
index 71c675fd3c..450a51afdd 100644
--- a/src/plugins/qmldesigner/designercore/include/abstractview.h
+++ b/src/plugins/qmldesigner/designercore/include/abstractview.h
@@ -86,6 +86,7 @@ public:
ModelNode createModelNode(const TypeName &typeName);
+#
ModelNode createModelNode(const TypeName &typeName,
int majorVersion,
int minorVersion,
@@ -95,6 +96,13 @@ public:
ModelNode::NodeSourceType nodeSourceType = ModelNode::NodeWithoutSource,
const QString &behaviorPropertyName = {});
+ ModelNode createModelNode(const TypeName &typeName,
+ const PropertyListType &propertyList,
+ const AuxiliaryDatas &auxPropertyList = {},
+ const QString &nodeSource = {},
+ ModelNode::NodeSourceType nodeSourceType = ModelNode::NodeWithoutSource,
+ const QString &behaviorPropertyName = {});
+
ModelNode rootModelNode() const;
ModelNode rootModelNode();
@@ -223,19 +231,11 @@ public:
virtual void view3DAction(View3DActionType type, const QVariant &value);
- virtual void active3DSceneChanged(qint32 sceneId);
-
virtual void dragStarted(QMimeData *mimeData);
virtual void dragEnded();
void changeRootNodeType(const TypeName &type, int majorVersion, int minorVersion);
- void ensureMaterialLibraryNode();
- ModelNode materialLibraryNode();
- bool isPartOfMaterialLibrary(const ModelNode &node);
- ModelNode active3DSceneNode();
- ModelNode getTextureDefaultInstance(const QString &source);
-
const NodeInstanceView *nodeInstanceView() const;
RewriterView *rewriterView() const;
diff --git a/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h b/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h
index fe55402f17..b6bb22290a 100644
--- a/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h
+++ b/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h
@@ -95,8 +95,6 @@ inline constexpr AuxiliaryDataKeyDefaultValue insightCategoriesProperty{Auxiliar
"insightCategories",
{}};
inline constexpr AuxiliaryDataKeyView uuidProperty{AuxiliaryDataType::Document, "uuid"};
-inline constexpr AuxiliaryDataKeyView active3dSceneProperty{AuxiliaryDataType::Temporary,
- "active3dScene"};
inline constexpr AuxiliaryDataKeyView tmpProperty{AuxiliaryDataType::Temporary, "tmp"};
inline constexpr AuxiliaryDataKeyView recordProperty{AuxiliaryDataType::Temporary, "Record"};
inline constexpr AuxiliaryDataKeyView transitionDurationProperty{AuxiliaryDataType::Document,
diff --git a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h
index a96082ee0f..f946190767 100644
--- a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h
+++ b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h
@@ -20,7 +20,7 @@ namespace ImageCache {
constexpr NanotraceHR::Tracing tracingStatus()
{
#ifdef ENABLE_IMAGE_CACHE_TRACING
- return NanotraceHR::tracingStatus();
+ return NanotraceHR::Tracing::IsEnabled;
#else
return NanotraceHR::Tracing::IsDisabled;
#endif
diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h
index 9816857db6..b907e6c5d8 100644
--- a/src/plugins/qmldesigner/designercore/include/model.h
+++ b/src/plugins/qmldesigner/designercore/include/model.h
@@ -20,6 +20,13 @@
#include <import.h>
+#ifdef QDS_USE_PROJECTSTORAGE
+# define DEPRECATED_OLD_CREATE_MODELNODE \
+ [[deprecated("Use unqualified type names and no versions!")]]
+#else
+# define DEPRECATED_OLD_CREATE_MODELNODE
+#endif
+
QT_BEGIN_NAMESPACE
class QPixmap;
class QUrl;
@@ -87,11 +94,12 @@ public:
~Model();
- static ModelPointer create(const TypeName &typeName,
- int major = 1,
- int minor = 1,
- Model *metaInfoProxyModel = nullptr,
- std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {})
+ DEPRECATED_OLD_CREATE_MODELNODE static ModelPointer create(
+ const TypeName &typeName,
+ int major = 1,
+ int minor = 1,
+ Model *metaInfoProxyModel = nullptr,
+ std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {})
{
return ModelPointer(
new Model(typeName, major, minor, metaInfoProxyModel, std::move(resourceManagement)));
@@ -110,13 +118,15 @@ public:
fileUrl,
std::move(resourceManagement)));
}
- static ModelPointer create(ProjectStorageDependencies m_projectStorageDependencies,
- const TypeName &typeName,
- int major = 1,
- int minor = 1,
- std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {})
+
+ DEPRECATED_OLD_CREATE_MODELNODE static ModelPointer create(
+ ProjectStorageDependencies projectStorageDependencies,
+ const TypeName &typeName,
+ int major = 1,
+ int minor = 1,
+ std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {})
{
- return ModelPointer(new Model(m_projectStorageDependencies,
+ return ModelPointer(new Model(projectStorageDependencies,
typeName,
major,
minor,
@@ -124,6 +134,9 @@ public:
std::move(resourceManagement)));
}
+ ModelPointer createModel(const TypeName &typeName,
+ std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {});
+
QUrl fileUrl() const;
SourceId fileUrlSourceId() const;
void setFileUrl(const QUrl &url);
@@ -150,6 +163,7 @@ public:
NodeMetaInfo flowViewFlowWildcardMetaInfo() const;
NodeMetaInfo fontMetaInfo() const;
NodeMetaInfo qmlQtObjectMetaInfo() const;
+ NodeMetaInfo qtQmlConnectionsMetaInfo() const;
NodeMetaInfo qtQmlModelsListModelMetaInfo() const;
NodeMetaInfo qtQmlModelsListElementMetaInfo() const;
NodeMetaInfo qtQuick3DBakedLightmapMetaInfo() const;
@@ -164,7 +178,6 @@ public:
NodeMetaInfo qtQuick3DPrincipledMaterialMetaInfo() const;
NodeMetaInfo qtQuick3DSpotLightMetaInfo() const;
NodeMetaInfo qtQuick3DTextureMetaInfo() const;
- NodeMetaInfo qtQuickConnectionsMetaInfo() const;
NodeMetaInfo qtQuickControlsTextAreaMetaInfo() const;
NodeMetaInfo qtQuickImageMetaInfo() const;
NodeMetaInfo qtQuickItemMetaInfo() const;
@@ -216,6 +229,10 @@ public:
QStringList importPaths() const;
Import highestPossibleImport(const QString &importPath);
+ ModuleIds moduleIds() const;
+
+ Storage::Info::ExportedTypeName exportedTypeNameForMetaInfo(const NodeMetaInfo &metaInfo) const;
+
RewriterView *rewriterView() const;
void setRewriterView(RewriterView *rewriterView);
@@ -239,12 +256,14 @@ public:
std::optional<std::function<bool(const QString &)>> isDuplicate = {}) const;
QString generateIdFromName(const QString &name, const QString &fallbackId = "element") const;
- void setActive3DSceneId(qint32 sceneId);
- qint32 active3DSceneId() const;
-
void startDrag(QMimeData *mimeData, const QPixmap &icon);
void endDrag();
+ void setCurrentStateNode(const ModelNode &node);
+ ModelNode currentStateNode(AbstractView *view = nullptr);
+
+ void setCurrentTimeline(const ModelNode &timeline);
+
NotNullPointer<const ProjectStorageType> projectStorage() const;
const PathCacheType &pathCache() const;
PathCacheType &pathCache();
diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h
index 58cdeba228..53c755ddc8 100644
--- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h
+++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h
@@ -22,6 +22,16 @@ QT_BEGIN_NAMESPACE
class QDeclarativeContext;
QT_END_NAMESPACE
+#ifdef QDS_USE_PROJECTSTORAGE
+# define DEPRECATED_TYPENAME [[deprecated("Don't use string based types anymore!")]]
+# define DEPRECATED_VERSION_NUMBER \
+ [[deprecated( \
+ "In most cases you don't need them anymore because the import is setting them!")]]
+#else
+# define DEPRECATED_TYPENAME
+# define DEPRECATED_VERSION_NUMBER
+#endif
+
namespace QmlDesigner {
class MetaInfo;
@@ -92,10 +102,11 @@ public:
bool defaultPropertyIsComponent() const;
- TypeName typeName() const;
- TypeName simplifiedTypeName() const;
- int majorVersion() const;
- int minorVersion() const;
+ TypeName displayName() const;
+ DEPRECATED_TYPENAME TypeName typeName() const;
+ DEPRECATED_TYPENAME TypeName simplifiedTypeName() const;
+ DEPRECATED_VERSION_NUMBER int majorVersion() const;
+ DEPRECATED_VERSION_NUMBER int minorVersion() const;
Storage::Info::ExportedTypeNames allExportedTypeNames() const;
Storage::Info::ExportedTypeNames exportedTypeNamesForSourceId(SourceId sourceId) const;
@@ -155,6 +166,7 @@ public:
bool isQmlComponent() const;
bool isQtMultimediaSoundEffect() const;
bool isQtObject() const;
+ bool isQtQmlConnections() const;
bool isQtQuick3DBakedLightmap() const;
bool isQtQuick3DBuffer() const;
bool isQtQuick3DCamera() const;
@@ -166,6 +178,7 @@ public:
bool isQtQuick3DLight() const;
bool isQtQuickListElement() const;
bool isQtQuickListModel() const;
+ bool isQtQuickListView() const;
bool isQtQuick3DMaterial() const;
bool isQtQuick3DModel() const;
bool isQtQuick3DNode() const;
@@ -201,6 +214,7 @@ public:
bool isQtQuickState() const;
bool isQtQuickStateOperation() const;
bool isQtQuickStudioComponentsGroupItem() const;
+ bool isQtQuickStudioUtilsJsonListModel() const;
bool isQtQuickText() const;
bool isQtQuickTimelineKeyframe() const;
bool isQtQuickTimelineKeyframeGroup() const;
diff --git a/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h b/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h
index 7871b5df3a..57ef521d86 100644
--- a/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h
+++ b/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h
@@ -33,4 +33,13 @@ enum AnchorLineType {
AnchorLineAllMask = AnchorLineVerticalMask | AnchorLineHorizontalMask
};
+constexpr bool isUsingQmlDesignerLite()
+{
+#ifdef QTC_USE_QML_DESIGNER_LITE
+ return true;
+#else
+ return false;
+#endif
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h
index 5948ec7ab1..dde5515a5a 100644
--- a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h
+++ b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h
@@ -111,6 +111,7 @@ public:
QSizeF instanceSize() const;
int instancePenWidth() const;
bool instanceIsRenderPixmapNull() const;
+ bool instanceIsVisible() const;
QPixmap instanceRenderPixmap() const;
QPixmap instanceBlurredRenderPixmap() const;
diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
index f16acf380f..33a100f7b2 100644
--- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
@@ -75,7 +75,7 @@
#include <qmlprojectmanager/qmlproject.h>
#include <utils/algorithm.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/theme/theme.h>
#include <utils/threadutils.h>
@@ -848,6 +848,7 @@ void NodeInstanceView::removeInstanceNodeRelationship(const ModelNode &node)
Q_ASSERT(m_nodeInstanceHash.contains(node));
NodeInstance instance = instanceForModelNode(node);
m_nodeInstanceHash.remove(node);
+ m_statePreviewImage.remove(node);
instance.makeInvalid();
}
diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp
index 9193a1ba37..85f904666c 100644
--- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp
+++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp
@@ -26,6 +26,12 @@
#include <utils/qtcassert.h>
#include <utils/algorithm.h>
+// remove that if the old code model is removed
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
+QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
+QT_WARNING_DISABLE_MSVC(4996)
+
namespace QmlDesigner {
/*!
@@ -1924,6 +1930,11 @@ bool NodeMetaInfo::defaultPropertyIsComponent() const
return false;
}
+TypeName NodeMetaInfo::displayName() const
+{
+ return {};
+}
+
TypeName NodeMetaInfo::typeName() const
{
if (isValid())
@@ -2029,10 +2040,6 @@ QString NodeMetaInfo::componentFileName() const
if (isValid()) {
return m_privateData->componentFileName();
}
- } else {
- if (isValid()) {
- return m_privateData->componentFileName();
- }
}
return {};
@@ -2369,6 +2376,16 @@ bool NodeMetaInfo::isQtObject() const
}
}
+bool NodeMetaInfo::isQtQmlConnections() const
+{
+ if constexpr (useProjectStorage()) {
+ using namespace Storage::Info;
+ return isBasedOnCommonType<QtQml, Connections>(m_projectStorage, m_typeId);
+ } else {
+ return isValid() && simplifiedTypeName() == "Connections";
+ }
+}
+
bool NodeMetaInfo::isLayoutable() const
{
if constexpr (useProjectStorage()) {
@@ -2766,6 +2783,16 @@ bool NodeMetaInfo::isQtQuickListModel() const
}
}
+bool NodeMetaInfo::isQtQuickListView() const
+{
+ if constexpr (useProjectStorage()) {
+ using namespace Storage::Info;
+ return isBasedOnCommonType<QtQuick, ListView>(m_projectStorage, m_typeId);
+ } else {
+ return isValid() && (isSubclassOf("QtQuick.ListView"));
+ }
+}
+
bool NodeMetaInfo::isQtQuick3DInstanceList() const
{
if constexpr (useProjectStorage()) {
@@ -3008,6 +3035,17 @@ bool NodeMetaInfo::isQtQuickStudioComponentsGroupItem() const
}
}
+bool NodeMetaInfo::isQtQuickStudioUtilsJsonListModel() const
+{
+ if constexpr (useProjectStorage()) {
+ using namespace Storage::Info;
+ return isBasedOnCommonType<QtQuick_Studio_Components, JsonListModel>(m_projectStorage,
+ m_typeId);
+ } else {
+ return isValid() && isSubclassOf("QtQuick.Studio.Utils.JsonListModel");
+ }
+}
+
bool NodeMetaInfo::isQmlComponent() const
{
if constexpr (useProjectStorage()) {
@@ -3017,11 +3055,9 @@ bool NodeMetaInfo::isQmlComponent() const
if (!isValid())
return false;
- auto type = m_privateData->qualfiedTypeName();
+ auto type = simplifiedTypeName();
- return type == "Component" || type == "Qt.Component" || type == "QtQuick.Component"
- || type == "QtQml.Component" || type == "<cpp>.QQmlComponent" || type == "QQmlComponent"
- || type == "QML.Component" || type == "QtQml.Base.Component";
+ return type == "Component" || type == "QQmlComponent";
}
}
@@ -3031,7 +3067,7 @@ bool NodeMetaInfo::isFont() const
using namespace Storage::Info;
return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, font>());
} else {
- return isValid() && m_privateData->qualfiedTypeName() == "font";
+ return isValid() && simplifiedTypeName() == "font";
}
}
@@ -3044,9 +3080,9 @@ bool NodeMetaInfo::isColor() const
if (!isValid())
return false;
- auto type = m_privateData->qualfiedTypeName();
+ auto type = simplifiedTypeName();
- return type == "QColor" || type == "color" || type == "QtQuick.color";
+ return type == "QColor" || type == "color" || type == "color";
}
}
@@ -3141,7 +3177,7 @@ bool NodeMetaInfo::isUrl() const
if (!isValid())
return false;
- auto type = m_privateData->qualfiedTypeName();
+ auto type = simplifiedTypeName();
return type == "url" || type == "QUrl";
}
@@ -3445,10 +3481,10 @@ QVariant PropertyMetaInfo::castedValue(const QVariant &value) const
if constexpr (!useProjectStorage()) {
const QVariant variant = value;
QVariant copyVariant = variant;
- if (isEnumType() || variant.canConvert<Enumeration>())
- return variant;
-
const TypeName &typeName = propertyTypeName();
+ // skip casting flags and keep them as int. TODO: use flags as enums
+ if (isEnumType() || variant.canConvert<Enumeration>() || typeName.endsWith("Flags"))
+ return variant;
QVariant::Type typeId = nodeMetaInfoPrivateData()->variantTypeId(propertyName());
@@ -3685,3 +3721,5 @@ CompoundPropertyMetaInfos MetaInfoUtils::inflateValueAndReadOnlyProperties(Prope
}
} // namespace QmlDesigner
+
+QT_WARNING_POP
diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp
index 936c23fa57..403731d1c4 100644
--- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp
+++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp
@@ -344,8 +344,11 @@ void SubComponentManager::unregisterQmlFile(const QFileInfo &fileInfo, const QSt
void SubComponentManager::registerQmlFile(const QFileInfo &fileInfo, const QString &qualifier,
bool addToLibrary)
{
- if (!addToLibrary || !model() || fileInfo.path().contains(QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER)))
+ if (!addToLibrary || !model()
+ || fileInfo.path().contains(QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER))
+ || fileInfo.path().contains(QLatin1String(Constants::DEFAULT_EFFECTS_IMPORT_FOLDER))) {
return;
+ }
QString componentName = fileInfo.baseName();
const QString baseComponentName = componentName;
diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp
index ff332ee3b2..125c7195a8 100644
--- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp
+++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp
@@ -25,6 +25,9 @@
namespace QmlDesigner {
+using namespace NanotraceHR::Literals;
+using NanotraceHR::keyValue;
+
/*!
\class QmlDesigner::AbstractView
\ingroup CoreModel
@@ -65,12 +68,12 @@ RewriterTransaction AbstractView::beginRewriterTransaction(const QByteArray &ide
ModelNode AbstractView::createModelNode(const TypeName &typeName)
{
- if constexpr (useProjectStorage()) {
- return createModelNode(typeName, -1, -1);
- } else {
- const NodeMetaInfo metaInfo = model()->metaInfo(typeName);
- return createModelNode(typeName, metaInfo.majorVersion(), metaInfo.minorVersion());
- }
+#ifdef QDS_USE_PROJECTSTORAGE
+ return createModelNode(typeName, -1, -1);
+#else
+ const NodeMetaInfo metaInfo = model()->metaInfo(typeName);
+ return createModelNode(typeName, metaInfo.majorVersion(), metaInfo.minorVersion());
+#endif
}
ModelNode AbstractView::createModelNode(const TypeName &typeName,
@@ -87,6 +90,24 @@ ModelNode AbstractView::createModelNode(const TypeName &typeName,
behaviorPropertyName), model(), this);
}
+ModelNode AbstractView::createModelNode(const TypeName &typeName,
+ const QList<QPair<PropertyName, QVariant>> &propertyList,
+ const AuxiliaryDatas &auxPropertyList,
+ const QString &nodeSource,
+ ModelNode::NodeSourceType nodeSourceType,
+ const QString &behaviorPropertyName)
+{
+ return ModelNode(model()->d->createNode(typeName,
+ -1,
+ -1,
+ propertyList,
+ auxPropertyList,
+ nodeSource,
+ nodeSourceType,
+ behaviorPropertyName),
+ model(),
+ this);
+}
// Returns the constant root model node.
ModelNode AbstractView::rootModelNode() const
@@ -370,8 +391,6 @@ void AbstractView::modelNodePreviewPixmapChanged(const ModelNode &/*node*/, cons
void AbstractView::view3DAction(View3DActionType, const QVariant &) {}
-void AbstractView::active3DSceneChanged(qint32 /*sceneId*/) {}
-
void AbstractView::dragStarted(QMimeData * /*mimeData*/) {}
void AbstractView::dragEnded() {}
@@ -548,8 +567,10 @@ void AbstractView::disableWidget()
void AbstractView::enableWidget()
{
- if (hasWidget() && widgetInfo().widgetFlags == DesignerWidgetFlags::DisableOnError)
- widgetInfo().widget->setEnabled(true);
+ if (hasWidget()) {
+ if (auto info = widgetInfo(); info.widgetFlags == DesignerWidgetFlags::DisableOnError)
+ info.widget->setEnabled(true);
+ }
}
QString AbstractView::contextHelpId() const
@@ -569,8 +590,8 @@ void AbstractView::setCurrentTimeline(const ModelNode &timeline)
if (currentTimeline().isValid())
currentTimeline().toogleRecording(false);
- if (model())
- model()->d->notifyCurrentTimelineChanged(timeline);
+ if (m_model)
+ m_model->setCurrentTimeline(timeline);
}
void AbstractView::activateTimelineRecording(const ModelNode &timeline)
@@ -580,8 +601,8 @@ void AbstractView::activateTimelineRecording(const ModelNode &timeline)
Internal::WriteLocker locker(m_model.data());
- if (model())
- model()->d->notifyCurrentTimelineChanged(timeline);
+ if (m_model)
+ m_model->setCurrentTimeline(timeline);
}
void AbstractView::deactivateTimelineRecording()
@@ -590,9 +611,8 @@ void AbstractView::deactivateTimelineRecording()
currentTimeline().toogleRecording(false);
currentTimeline().resetGroupRecording();
}
-
- if (model())
- model()->d->notifyCurrentTimelineChanged(ModelNode());
+ if (m_model)
+ m_model->setCurrentTimeline({});
}
bool AbstractView::executeInTransaction(const QByteArray &identifier, const OperationBlock &lambda)
@@ -761,9 +781,8 @@ void AbstractView::emitRewriterEndTransaction()
void AbstractView::setCurrentStateNode(const ModelNode &node)
{
- Internal::WriteLocker locker(m_model.data());
- if (model())
- model()->d->notifyCurrentStateChanged(node);
+ if (m_model)
+ m_model->setCurrentStateNode(node);
}
void AbstractView::changeRootNodeType(const TypeName &type, int majorVersion, int minorVersion)
@@ -773,110 +792,10 @@ void AbstractView::changeRootNodeType(const TypeName &type, int majorVersion, in
m_model.data()->d->changeRootNodeType(type, majorVersion, minorVersion);
}
-// Creates material library if it doesn't exist and moves any existing materials into it.
-void AbstractView::ensureMaterialLibraryNode()
-{
- ModelNode matLib = modelNodeForId(Constants::MATERIAL_LIB_ID);
- if (matLib.isValid()
- || (!rootModelNode().metaInfo().isQtQuick3DNode()
- && !rootModelNode().metaInfo().isQtQuickItem())) {
- return;
- }
-
- executeInTransaction(__FUNCTION__, [&] {
- // Create material library node
-#ifdef QDS_USE_PROJECTSTORAGE
- TypeName nodeTypeName = rootModelNode().metaInfo().isQtQuick3DNode() ? "Node" : "Item";
- matLib = createModelNode(nodeTypeName, -1, -1);
-#else
- auto nodeType = rootModelNode().metaInfo().isQtQuick3DNode()
- ? model()->qtQuick3DNodeMetaInfo()
- : model()->qtQuickItemMetaInfo();
- matLib = createModelNode(nodeType.typeName(), nodeType.majorVersion(), nodeType.minorVersion());
-#endif
- matLib.setIdWithoutRefactoring(Constants::MATERIAL_LIB_ID);
- rootModelNode().defaultNodeListProperty().reparentHere(matLib);
- });
-
- // Do the material reparentings in different transaction to work around issue QDS-8094
- executeInTransaction(__FUNCTION__, [&] {
- const QList<ModelNode> materials = rootModelNode().subModelNodesOfType(
- model()->qtQuick3DMaterialMetaInfo());
- if (!materials.isEmpty()) {
- // Move all materials to under material library node
- for (const ModelNode &node : materials) {
- // If material has no name, set name to id
- QString matName = node.variantProperty("objectName").value().toString();
- if (matName.isEmpty()) {
- VariantProperty objNameProp = node.variantProperty("objectName");
- objNameProp.setValue(node.id());
- }
-
- matLib.defaultNodeListProperty().reparentHere(node);
- }
- }
- });
-}
-
-// Returns ModelNode for project's material library if it exists.
-ModelNode AbstractView::materialLibraryNode()
-{
- return modelNodeForId(Constants::MATERIAL_LIB_ID);
-}
-
-bool AbstractView::isPartOfMaterialLibrary(const ModelNode &node)
-{
- if (!node.isValid())
- return false;
-
- ModelNode matLib = materialLibraryNode();
-
- return matLib.isValid()
- && (node == matLib
- || (node.hasParentProperty() && node.parentProperty().parentModelNode() == matLib));
-}
-
-ModelNode AbstractView::active3DSceneNode()
-{
- auto activeSceneAux = rootModelNode().auxiliaryData(active3dSceneProperty);
- if (activeSceneAux) {
- int activeScene = activeSceneAux->toInt();
-
- if (hasModelNodeForInternalId(activeScene))
- return modelNodeForInternalId(activeScene);
- }
-
- return {};
-}
-
-ModelNode AbstractView::getTextureDefaultInstance(const QString &source)
-{
- ModelNode matLib = materialLibraryNode();
- if (!matLib.isValid())
- return {};
-
- const QList <ModelNode> matLibNodes = matLib.directSubModelNodes();
- for (const ModelNode &tex : matLibNodes) {
- if (tex.isValid() && tex.metaInfo().isQtQuick3DTexture()) {
- const QList<AbstractProperty> props = tex.properties();
- if (props.size() != 1)
- continue;
- const AbstractProperty &prop = props[0];
- if (prop.name() == "source" && prop.isVariantProperty()
- && prop.toVariantProperty().value().toString() == source) {
- return tex;
- }
- }
- }
-
- return {};
-}
-
-
ModelNode AbstractView::currentStateNode() const
{
- if (model())
- return ModelNode(m_model.data()->d->currentStateNode(), m_model.data(), const_cast<AbstractView*>(this));
+ if (m_model)
+ return m_model->currentStateNode(const_cast<AbstractView *>(this));
return {};
}
@@ -925,6 +844,7 @@ static int getMajorVersionFromImport(const Model *model)
return -1;
}
+#ifndef QDS_USE_PROJECTSTORAGE
static int getMajorVersionFromNode(const ModelNode &modelNode)
{
if (modelNode.metaInfo().isValid()) {
@@ -949,6 +869,7 @@ static int getMinorVersionFromNode(const ModelNode &modelNode)
return 1; // default
}
+#endif
int AbstractView::majorQtQuickVersion() const
{
@@ -956,7 +877,11 @@ int AbstractView::majorQtQuickVersion() const
if (majorVersionFromImport >= 0)
return majorVersionFromImport;
+#ifdef QDS_USE_PROJECTSTORAGE
+ return -1;
+#else
return getMajorVersionFromNode(rootModelNode());
+#endif
}
int AbstractView::minorQtQuickVersion() const
@@ -965,7 +890,11 @@ int AbstractView::minorQtQuickVersion() const
if (minorVersionFromImport >= 0)
return minorVersionFromImport;
+#ifdef QDS_USE_PROJECTSTORAGE
+ return -1;
+#else
return getMinorVersionFromNode(rootModelNode());
+#endif
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp
index 141548047e..23c17dc61a 100644
--- a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp
+++ b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp
@@ -37,10 +37,11 @@ BindingProperty::BindingProperty(const PropertyName &propertyName, const Interna
void BindingProperty::setExpression(const QString &expression)
{
- Internal::WriteLocker locker(model());
if (!isValid())
return;
+ Internal::WriteLocker locker(model());
+
if (isDynamic())
qWarning() << "Calling BindingProperty::setExpression on dynamic property.";
diff --git a/src/plugins/qmldesigner/designercore/model/internalbindingproperty.cpp b/src/plugins/qmldesigner/designercore/model/internalbindingproperty.cpp
index 6a3eee1975..2e7542c79b 100644
--- a/src/plugins/qmldesigner/designercore/model/internalbindingproperty.cpp
+++ b/src/plugins/qmldesigner/designercore/model/internalbindingproperty.cpp
@@ -21,8 +21,11 @@ QString InternalBindingProperty::expression() const
{
return m_expression;
}
+
void InternalBindingProperty::setExpression(const QString &expression)
{
+ traceToken.tick("expression"_t, keyValue("expression", expression));
+
m_expression = expression;
}
diff --git a/src/plugins/qmldesigner/designercore/model/internalnode_p.h b/src/plugins/qmldesigner/designercore/model/internalnode_p.h
index 7c0bbb9bb3..f3fe55f231 100644
--- a/src/plugins/qmldesigner/designercore/model/internalnode_p.h
+++ b/src/plugins/qmldesigner/designercore/model/internalnode_p.h
@@ -39,6 +39,10 @@ class InternalNode;
using InternalNodePointer = std::shared_ptr<InternalNode>;
using InternalPropertyPointer = std::shared_ptr<InternalProperty>;
+using NanotraceHR::dictonary;
+using NanotraceHR::keyValue;
+using namespace std::literals::string_view_literals;
+
class InternalNode : public std::enable_shared_from_this<InternalNode>
{
friend InternalProperty;
@@ -47,13 +51,19 @@ public:
using Pointer = std::shared_ptr<InternalNode>;
using WeakPointer = std::weak_ptr<InternalNode>;
- explicit InternalNode(TypeName typeName, int majorVersion, int minorVersion, qint32 internalId)
+ explicit InternalNode(TypeName typeName,
+ int majorVersion,
+ int minorVersion,
+ qint32 internalId,
+ ModelTracing::Category::FlowTokenType flowTraceToken)
: typeName(std::move(typeName))
, majorVersion(majorVersion)
, minorVersion(minorVersion)
, isValid(true)
, internalId(internalId)
- , traceToken(ModelTracing::category().beginObject("InternalNode"_t))
+ , traceToken(flowTraceToken.beginAsynchronous("InternalNode"_t,
+ keyValue("type", typeName),
+ keyValue("internal id", internalId)))
{}
InternalNodeAbstractProperty::Pointer parentProperty() const { return m_parentProperty.lock(); }
@@ -224,7 +234,7 @@ public:
ModuleId moduleId;
ImportedTypeNameId importedTypeNameId;
TypeId typeId;
- NO_UNIQUE_ADDRESS ModelTracing::ObjectTraceToken traceToken;
+ NO_UNIQUE_ADDRESS ModelTracing::AsynchronousToken traceToken;
private:
AuxiliaryDatas m_auxiliaryDatas;
diff --git a/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.cpp b/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.cpp
index c5aaa8fa94..975927c097 100644
--- a/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.cpp
+++ b/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.cpp
@@ -40,12 +40,20 @@ int InternalNodeListProperty::indexOf(const InternalNode::Pointer &node) const
void InternalNodeListProperty::add(const InternalNode::Pointer &internalNode)
{
Q_ASSERT(!m_nodeList.contains(internalNode));
+
+ auto flowToken = traceToken.tickWithFlow("add node"_t);
+ internalNode->traceToken.tick(flowToken, "node added"_t);
+
m_nodeList.append(internalNode);
}
void InternalNodeListProperty::remove(const InternalNodePointer &internalNode)
{
Q_ASSERT(m_nodeList.contains(internalNode));
+
+ auto flowToken = traceToken.tickWithFlow("remove node"_t);
+ internalNode->traceToken.tick(flowToken, "node removed"_t);
+
m_nodeList.removeAll(internalNode);
}
@@ -56,6 +64,8 @@ const QList<InternalNode::Pointer> &InternalNodeListProperty::nodeList() const
void InternalNodeListProperty::slide(int from, int to)
{
+ traceToken.tick("slide"_t, keyValue("from", from), keyValue("to", to));
+
InternalNode::Pointer internalNode = m_nodeList.takeAt(from);
m_nodeList.insert(to, internalNode);
}
diff --git a/src/plugins/qmldesigner/designercore/model/internalnodeproperty.cpp b/src/plugins/qmldesigner/designercore/model/internalnodeproperty.cpp
index df9f559372..ff9d71d6fd 100644
--- a/src/plugins/qmldesigner/designercore/model/internalnodeproperty.cpp
+++ b/src/plugins/qmldesigner/designercore/model/internalnodeproperty.cpp
@@ -42,6 +42,10 @@ bool InternalNodeProperty::isValid() const
void InternalNodeProperty::remove([[maybe_unused]] const InternalNode::Pointer &node)
{
Q_ASSERT(m_node == node);
+
+ auto flowToken = traceToken.tickWithFlow("remove node"_t);
+ node->traceToken.tick(flowToken, "node removed"_t);
+
m_node.reset();
}
@@ -49,6 +53,10 @@ void InternalNodeProperty::add(const InternalNode::Pointer &node)
{
Q_ASSERT(node);
Q_ASSERT(node->parentProperty());
+
+ auto flowToken = traceToken.tickWithFlow("add node"_t);
+ node->traceToken.tick(flowToken, "node added"_t);
+
m_node = node;
}
diff --git a/src/plugins/qmldesigner/designercore/model/internalproperty.cpp b/src/plugins/qmldesigner/designercore/model/internalproperty.cpp
index f209629041..06a1d90373 100644
--- a/src/plugins/qmldesigner/designercore/model/internalproperty.cpp
+++ b/src/plugins/qmldesigner/designercore/model/internalproperty.cpp
@@ -4,17 +4,40 @@
#include "internalproperty.h"
#include "internalnode_p.h"
-namespace QmlDesigner::Internal {
+namespace QmlDesigner {
-static auto traceTokenInitArg(const QByteArray &name)
+template<typename String> void convertToString(String &string, PropertyType type)
{
- return ModelTracing::category().beginObject("InternalProperty"_t, keyValue("name", name));
+ string.append("\"");
+
+ switch (type) {
+ case PropertyType::None:
+ string.append("None"_sv);
+ break;
+ case PropertyType::Variant:
+ string.append("Variant"_sv);
+ break;
+ case PropertyType::Node:
+ string.append("Node"_sv);
+ break;
+ case PropertyType::NodeList:
+ string.append("NodeList"_sv);
+ break;
+ case PropertyType::Binding:
+ string.append("Binding"_sv);
+ break;
+ case PropertyType::SignalHandler:
+ string.append("SignalHandler"_sv);
+ break;
+ case PropertyType::SignalDeclaration:
+ string.append("SignalDeclaration"_sv);
+ break;
+ }
+
+ string.append("\"");
}
-// Creates invalid InternalProperty
-InternalProperty::InternalProperty()
- : traceToken(traceTokenInitArg(m_name))
-{}
+namespace Internal {
InternalProperty::~InternalProperty() = default;
@@ -24,7 +47,9 @@ InternalProperty::InternalProperty(const PropertyName &name,
: m_name(name)
, m_propertyOwner(propertyOwner)
, m_propertyType(propertyType)
- , traceToken(traceTokenInitArg(m_name))
+ , traceToken(propertyOwner->traceToken.begin(name,
+ keyValue("owner", propertyOwner->internalId),
+ keyValue("type", propertyType)))
{}
bool InternalProperty::isValid() const
@@ -44,13 +69,17 @@ TypeName InternalProperty::dynamicTypeName() const
void InternalProperty::setDynamicTypeName(const TypeName &name)
{
+ traceToken.tick("dynamic type name"_t, keyValue("name", name));
+
m_dynamicType = name;
}
void InternalProperty::resetDynamicTypeName()
{
- m_dynamicType.clear();
-}
+ traceToken.tick("reset dynamic type name"_t);
-} //namespace QmlDesigner::Internal
+ m_dynamicType.clear();
+}
+} // namespace Internal
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/model/internalproperty.h b/src/plugins/qmldesigner/designercore/model/internalproperty.h
index 2fc77b2027..f5ca983969 100644
--- a/src/plugins/qmldesigner/designercore/model/internalproperty.h
+++ b/src/plugins/qmldesigner/designercore/model/internalproperty.h
@@ -193,7 +193,9 @@ private:
TypeName m_dynamicType;
std::weak_ptr<InternalNode> m_propertyOwner;
PropertyType m_propertyType = PropertyType::None;
- NO_UNIQUE_ADDRESS ModelTracing::ObjectTraceToken traceToken;
+
+protected:
+ NO_UNIQUE_ADDRESS ModelTracing::AsynchronousToken traceToken;
};
} // namespace Internal
diff --git a/src/plugins/qmldesigner/designercore/model/internalsignalhandlerproperty.cpp b/src/plugins/qmldesigner/designercore/model/internalsignalhandlerproperty.cpp
index 70aade63a6..d199ad4b8c 100644
--- a/src/plugins/qmldesigner/designercore/model/internalsignalhandlerproperty.cpp
+++ b/src/plugins/qmldesigner/designercore/model/internalsignalhandlerproperty.cpp
@@ -23,6 +23,8 @@ QString InternalSignalHandlerProperty::source() const
}
void InternalSignalHandlerProperty::setSource(const QString &source)
{
+ traceToken.tick("source"_t, keyValue("source", source));
+
m_source = source;
}
@@ -38,6 +40,8 @@ QString InternalSignalDeclarationProperty::signature() const
void InternalSignalDeclarationProperty::setSignature(const QString &signature)
{
+ traceToken.tick("signature"_t, keyValue("signature", signature));
+
m_signature = signature;
}
diff --git a/src/plugins/qmldesigner/designercore/model/internalvariantproperty.cpp b/src/plugins/qmldesigner/designercore/model/internalvariantproperty.cpp
index 53a0347c17..04ff6fff70 100644
--- a/src/plugins/qmldesigner/designercore/model/internalvariantproperty.cpp
+++ b/src/plugins/qmldesigner/designercore/model/internalvariantproperty.cpp
@@ -19,6 +19,8 @@ QVariant InternalVariantProperty::value() const
void InternalVariantProperty::setValue(const QVariant &value)
{
+ traceToken.tick("value"_t, keyValue("value", value));
+
m_value = value;
}
diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp
index 6700185575..a4cd31b2a8 100644
--- a/src/plugins/qmldesigner/designercore/model/model.cpp
+++ b/src/plugins/qmldesigner/designercore/model/model.cpp
@@ -64,6 +64,9 @@ Components that want to be informed about changes in the model can register a su
*/
namespace QmlDesigner {
+
+using NanotraceHR::keyValue;
+
namespace Internal {
ModelPrivate::ModelPrivate(Model *model,
@@ -136,12 +139,15 @@ ModelPrivate::ModelPrivate(Model *model,
ModelPrivate::~ModelPrivate()
{
+ removeNode(rootNode());
if constexpr (useProjectStorage())
projectStorage->removeObserver(this);
};
void ModelPrivate::detachAllViews()
{
+ auto tracer = traceToken.begin("detach all views"_t);
+
for (const QPointer<AbstractView> &view : std::as_const(m_viewList))
detachView(view.data(), true);
@@ -175,6 +181,8 @@ Storage::Imports createStorageImports(const Imports &imports,
void ModelPrivate::changeImports(Imports toBeAddedImports, Imports toBeRemovedImports)
{
+ auto tracer = traceToken.begin("change imports"_t);
+
std::sort(toBeAddedImports.begin(), toBeAddedImports.end());
std::sort(toBeRemovedImports.begin(), toBeRemovedImports.end());
@@ -250,6 +258,8 @@ void ModelPrivate::setDocumentMessages(const QList<DocumentMessage> &errors,
void ModelPrivate::setFileUrl(const QUrl &fileUrl)
{
+ auto tracer = traceToken.begin("file url"_t);
+
QUrl oldPath = m_fileUrl;
if (oldPath != fileUrl) {
@@ -294,7 +304,11 @@ InternalNodePointer ModelPrivate::createNode(const TypeName &typeName,
if (!isRootNode)
internalId = m_internalIdCounter++;
- auto newNode = std::make_shared<InternalNode>(typeName, majorVersion, minorVersion, internalId);
+ auto newNode = std::make_shared<InternalNode>(typeName,
+ majorVersion,
+ minorVersion,
+ internalId,
+ traceToken.tickWithFlow("create node"_t));
setTypeId(newNode.get(), typeName);
@@ -338,6 +352,7 @@ void ModelPrivate::removeNodeFromModel(const InternalNodePointer &node)
if (!node->id.isEmpty())
m_idNodeHash.remove(node->id);
node->isValid = false;
+ node->traceToken.end();
m_nodes.removeOne(node);
m_internalIdNodeHash.remove(node->internalId);
}
@@ -480,7 +495,7 @@ void ModelPrivate::changeNodeId(const InternalNodePointer &node, const QString &
const QString oldId = node->id;
node->id = id;
- node->traceToken.change("id"_t, std::forward_as_tuple("id", id));
+ node->traceToken.tick("id"_t, std::forward_as_tuple("id", id));
if (!oldId.isEmpty())
m_idNodeHash.remove(oldId);
if (!id.isEmpty())
@@ -675,7 +690,6 @@ void ModelPrivate::notifyInstancesChildrenChanged(const QVector<ModelNode> &mode
void ModelPrivate::notifyCurrentStateChanged(const ModelNode &node)
{
- m_currentStateNode = node.internalNode();
notifyNodeInstanceViewLast([&](AbstractView *view) {
view->currentStateChanged(ModelNode(node.internalNode(), m_model, view));
});
@@ -683,7 +697,6 @@ void ModelPrivate::notifyCurrentStateChanged(const ModelNode &node)
void ModelPrivate::notifyCurrentTimelineChanged(const ModelNode &node)
{
- m_currentTimelineNode = node.internalNode();
notifyNodeInstanceViewLast([&](AbstractView *view) {
view->currentTimelineChanged(ModelNode(node.internalNode(), m_model, view));
});
@@ -720,11 +733,6 @@ void ModelPrivate::notifyView3DAction(View3DActionType type, const QVariant &val
notifyNormalViewsLast([&](AbstractView *view) { view->view3DAction(type, value); });
}
-void ModelPrivate::notifyActive3DSceneIdChanged(qint32 sceneId)
-{
- notifyInstanceChanges([&](AbstractView *view) { view->active3DSceneChanged(sceneId); });
-}
-
void ModelPrivate::notifyDragStarted(QMimeData *mimeData)
{
notifyInstanceChanges([&](AbstractView *view) { view->dragStarted(mimeData); });
@@ -1102,14 +1110,38 @@ void ModelPrivate::setSelectedNodes(const QList<InternalNodePointer> &selectedNo
if (sortedSelectedList == m_selectedInternalNodeList)
return;
+ auto flowToken = traceToken.tickWithFlow("selected model nodes"_t);
+
+ if constexpr (decltype(traceToken)::categoryIsActive()) { // the compiler should optimize it away but to be sure
+ std::set_difference(sortedSelectedList.begin(),
+ sortedSelectedList.end(),
+ m_selectedInternalNodeList.begin(),
+ m_selectedInternalNodeList.end(),
+ Utils::make_iterator([&](const auto &node) {
+ node->traceToken.tick(flowToken, "select model node"_t);
+ }));
+ }
+
const QList<InternalNodePointer> lastSelectedNodeList = m_selectedInternalNodeList;
m_selectedInternalNodeList = sortedSelectedList;
+ if constexpr (decltype(traceToken)::categoryIsActive()) { // the compiler should optimize it away but to be sure
+ std::set_difference(lastSelectedNodeList.begin(),
+ lastSelectedNodeList.end(),
+ m_selectedInternalNodeList.begin(),
+ m_selectedInternalNodeList.end(),
+ Utils::make_iterator([&](const auto &node) {
+ node->traceToken.tick(flowToken, "deselect model node"_t);
+ }));
+ }
+
changeSelectedNodes(sortedSelectedList, lastSelectedNodeList);
}
void ModelPrivate::clearSelectedNodes()
{
+ auto tracer = traceToken.begin("clear selected model nodes"_t);
+
const QList<InternalNodePointer> lastSelectedNodeList = m_selectedInternalNodeList;
m_selectedInternalNodeList.clear();
changeSelectedNodes(m_selectedInternalNodeList, lastSelectedNodeList);
@@ -1486,6 +1518,8 @@ void ModelPrivate::changeRootNodeType(const TypeName &type, int majorVersion, in
{
Q_ASSERT(rootNode());
+ m_rootInternalNode->traceToken.tick("type name"_t, keyValue("type name", type));
+
m_rootInternalNode->typeName = type;
m_rootInternalNode->majorVersion = majorVersion;
m_rootInternalNode->minorVersion = minorVersion;
@@ -1495,6 +1529,8 @@ void ModelPrivate::changeRootNodeType(const TypeName &type, int majorVersion, in
void ModelPrivate::setScriptFunctions(const InternalNodePointer &node, const QStringList &scriptFunctionList)
{
+ m_rootInternalNode->traceToken.tick("script function"_t);
+
node->scriptFunctions = scriptFunctionList;
notifyScriptFunctionsChanged(node, scriptFunctionList);
@@ -1502,6 +1538,8 @@ void ModelPrivate::setScriptFunctions(const InternalNodePointer &node, const QSt
void ModelPrivate::setNodeSource(const InternalNodePointer &node, const QString &nodeSource)
{
+ m_rootInternalNode->traceToken.tick("node source"_t);
+
node->nodeSource = nodeSource;
notifyNodeSourceChanged(node, nodeSource);
}
@@ -1693,6 +1731,16 @@ Model::Model(const TypeName &typeName,
this, typeName, major, minor, metaInfoProxyModel, std::move(resourceManagement)))
{}
+ModelPointer Model::createModel(const TypeName &typeName,
+ std::unique_ptr<ModelResourceManagementInterface> resourceManagement)
+{
+ return Model::create({*d->projectStorage, *d->pathCache},
+ typeName,
+ imports(),
+ fileUrl(),
+ std::move(resourceManagement));
+}
+
Model::~Model() = default;
const Imports &Model::imports() const
@@ -1700,6 +1748,16 @@ const Imports &Model::imports() const
return d->imports();
}
+ModuleIds Model::moduleIds() const
+{
+ return {};
+}
+
+Storage::Info::ExportedTypeName Model::exportedTypeNameForMetaInfo(const NodeMetaInfo &) const
+{
+ return {};
+}
+
const Imports &Model::possibleImports() const
{
return d->m_possibleImportList;
@@ -1717,6 +1775,8 @@ void Model::changeImports(Imports importsToBeAdded, Imports importsToBeRemoved)
void Model::setPossibleImports(Imports possibleImports)
{
+ auto tracer = d->traceToken.begin("possible imports"_t);
+
std::sort(possibleImports.begin(), possibleImports.end());
if (d->m_possibleImportList != possibleImports) {
@@ -1727,6 +1787,8 @@ void Model::setPossibleImports(Imports possibleImports)
void Model::setUsedImports(Imports usedImports)
{
+ auto tracer = d->traceToken.begin("used imports"_t);
+
std::sort(usedImports.begin(), usedImports.end());
if (d->m_usedImportList != usedImports) {
@@ -1871,24 +1933,6 @@ QString Model::generateIdFromName(const QString &name, const QString &fallbackId
return newId;
}
-void Model::setActive3DSceneId(qint32 sceneId)
-{
- auto activeSceneAux = d->rootNode()->auxiliaryData(active3dSceneProperty);
- if (activeSceneAux && activeSceneAux->toInt() == sceneId)
- return;
-
- d->rootNode()->setAuxiliaryData(active3dSceneProperty, sceneId);
- d->notifyActive3DSceneIdChanged(sceneId);
-}
-
-qint32 Model::active3DSceneId() const
-{
- auto sceneId = d->rootNode()->auxiliaryData(active3dSceneProperty);
- if (sceneId)
- return sceneId->toInt();
- return -1;
-}
-
void Model::startDrag(QMimeData *mimeData, const QPixmap &icon)
{
d->notifyDragStarted(mimeData);
@@ -1907,6 +1951,25 @@ void Model::endDrag()
d->notifyDragEnded();
}
+void Model::setCurrentStateNode(const ModelNode &node)
+{
+ Internal::WriteLocker locker(this);
+ d->m_currentStateNode = node.internalNode();
+ d->notifyCurrentStateChanged(node);
+}
+
+ModelNode Model::currentStateNode(AbstractView *view)
+{
+ return ModelNode(d->currentStateNode(), this, view);
+}
+
+void Model::setCurrentTimeline(const ModelNode &timeline)
+{
+ d->m_currentTimelineNode = timeline.internalNode();
+
+ d->notifyCurrentTimelineChanged(timeline);
+}
+
NotNullPointer<const ProjectStorageType> Model::projectStorage() const
{
return d->projectStorage;
@@ -2411,13 +2474,13 @@ NodeMetaInfo Model::qtQuickTransistionMetaInfo() const
}
}
-NodeMetaInfo Model::qtQuickConnectionsMetaInfo() const
+NodeMetaInfo Model::qtQmlConnectionsMetaInfo() const
{
if constexpr (useProjectStorage()) {
using namespace Storage::Info;
- return createNodeMetaInfo<QtQuick, Connections>();
+ return createNodeMetaInfo<QtQml, Connections>();
} else {
- return metaInfo("QtQuick.Connections");
+ return metaInfo("QtQml.Connections");
}
}
@@ -2570,6 +2633,10 @@ The view is informed that it has been registered within the model by a call to A
*/
void Model::attachView(AbstractView *view)
{
+ auto traceToken = d->traceToken.begin("attachView"_t,
+ keyValue("name",
+ std::string_view{view->metaObject()->className()}));
+
// Internal::WriteLocker locker(d);
auto castedRewriterView = qobject_cast<RewriterView *>(view);
if (castedRewriterView) {
@@ -2597,6 +2664,10 @@ void Model::attachView(AbstractView *view)
*/
void Model::detachView(AbstractView *view, ViewNotification emitDetachNotify)
{
+ auto traceToken = d->traceToken.begin("detachView"_t,
+ keyValue("name",
+ std::string_view{view->metaObject()->className()}));
+
// Internal::WriteLocker locker(d);
bool emitNotify = (emitDetachNotify == NotifyView);
@@ -2662,12 +2733,12 @@ ModelNode createNode(Model *model,
ModelNode Model::createModelNode(const TypeName &typeName)
{
- if constexpr (useProjectStorage()) {
- return createNode(this, d.get(), typeName, -1, -1);
- } else {
- const NodeMetaInfo m = metaInfo(typeName);
- return createNode(this, d.get(), typeName, m.majorVersion(), m.minorVersion());
- }
+#ifdef QDS_USE_PROJECTSTORAGE
+ return createNode(this, d.get(), typeName, -1, -1);
+#else
+ const NodeMetaInfo m = metaInfo(typeName);
+ return createNode(this, d.get(), typeName, m.majorVersion(), m.minorVersion());
+#endif
}
void Model::changeRootNodeType(const TypeName &type)
diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h
index d7743b028e..a3e972f329 100644
--- a/src/plugins/qmldesigner/designercore/model/model_p.h
+++ b/src/plugins/qmldesigner/designercore/model/model_p.h
@@ -5,6 +5,8 @@
#include "qmldesignercorelib_global.h"
+#include <tracing/qmldesignertracing.h>
+
#include "abstractview.h"
#ifndef QDS_USE_PROJECTSTORAGE
# include "metainfo.h"
@@ -37,6 +39,8 @@ class NodeMetaInfoPrivate;
namespace Internal {
+using namespace NanotraceHR::Literals;
+
class InternalNode;
class InternalProperty;
class InternalBindingProperty;
@@ -133,7 +137,6 @@ public:
/*factory methods for internal use in model and rewriter*/
void removeNodeAndRelatedResources(const InternalNodePointer &node);
- void removeNode(const InternalNodePointer &node);
void changeNodeId(const InternalNodePointer &node, const QString &id);
void changeNodeType(const InternalNodePointer &node, const TypeName &typeName, int majorVersion, int minorVersion);
@@ -310,6 +313,7 @@ public:
protected:
void removedTypeIds(const TypeIds &removedTypeIds) override;
+ void removeNode(const InternalNodePointer &node);
private:
void removePropertyWithoutNotification(InternalProperty *property);
@@ -329,6 +333,7 @@ private:
public:
NotNullPointer<ProjectStorageType> projectStorage = nullptr;
NotNullPointer<PathCacheType> pathCache = nullptr;
+ ModelTracing::AsynchronousToken traceToken = ModelTracing::category().beginAsynchronous("Model"_t);
private:
Model *m_model = nullptr;
@@ -353,6 +358,7 @@ private:
QPointer<NodeInstanceView> m_nodeInstanceView;
QPointer<Model> m_metaInfoProxyModel;
QHash<TypeName, std::shared_ptr<NodeMetaInfoPrivate>> m_nodeMetaInfoCache;
+
bool m_writeLock = false;
qint32 m_internalIdCounter = 1;
};
diff --git a/src/plugins/qmldesigner/designercore/model/modelmerger.cpp b/src/plugins/qmldesigner/designercore/model/modelmerger.cpp
index a9fa1ebd19..24873bb141 100644
--- a/src/plugins/qmldesigner/designercore/model/modelmerger.cpp
+++ b/src/plugins/qmldesigner/designercore/model/modelmerger.cpp
@@ -152,9 +152,17 @@ static ModelNode createNodeFromNode(const ModelNode &modelNode,
const QHash<QString, QString> &idRenamingHash,
AbstractView *view, const MergePredicate &mergePredicate)
{
+#ifdef QDS_USE_PROJECTSTORAGE
+ ModelNode newNode = view->createModelNode(modelNode.type(),
+ {},
+ {},
+ modelNode.nodeSource(),
+ modelNode.nodeSourceType());
+#else
NodeMetaInfo nodeMetaInfo = view->model()->metaInfo(modelNode.type());
ModelNode newNode(view->createModelNode(modelNode.type(), nodeMetaInfo.majorVersion(), nodeMetaInfo.minorVersion(),
{}, {}, modelNode.nodeSource(), modelNode.nodeSourceType()));
+#endif
syncVariantProperties(newNode, modelNode);
syncAuxiliaryProperties(newNode, modelNode);
syncBindingProperties(newNode, modelNode, idRenamingHash);
diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.cpp b/src/plugins/qmldesigner/designercore/model/modelutils.cpp
index cb3f482289..3dd7fe6c32 100644
--- a/src/plugins/qmldesigner/designercore/model/modelutils.cpp
+++ b/src/plugins/qmldesigner/designercore/model/modelutils.cpp
@@ -9,6 +9,8 @@
#include <projectstorage/projectstorage.h>
#include <projectstorage/sourcepathcache.h>
+#include <coreplugin/messagebox.h>
+
#include <utils/expected.h>
#include <utils/ranges.h>
diff --git a/src/plugins/qmldesigner/designercore/model/qmlconnections.cpp b/src/plugins/qmldesigner/designercore/model/qmlconnections.cpp
index 7a63e73d62..b7d04f3f38 100644
--- a/src/plugins/qmldesigner/designercore/model/qmlconnections.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmlconnections.cpp
@@ -98,10 +98,15 @@ QList<SignalHandlerProperty> QmlConnections::signalProperties() const
ModelNode QmlConnections::createQmlConnections(AbstractView *view)
{
- NodeMetaInfo nodeMetaInfo = view->model()->qtQuickConnectionsMetaInfo();
+#ifdef QDS_USE_PROJECTSTORAGE
+ return view->createModelNode("Connections");
+#else
+ NodeMetaInfo nodeMetaInfo = view->model()->qtQmlConnectionsMetaInfo();
+
return view->createModelNode("QtQuick.Connections",
nodeMetaInfo.majorVersion(),
nodeMetaInfo.minorVersion());
+#endif
}
} // QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp
index e524e91c70..826856428b 100644
--- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp
@@ -81,12 +81,25 @@ QmlItemNode QmlItemNode::createQmlItemNodeFromImage(AbstractView *view, const QS
propertyPairList.append({PropertyName("source"), QVariant(relativeImageName)});
}
+#ifdef QDS_USE_PROJECTSTORAGE
+ TypeName type("Image");
+ QImageReader reader(imageName);
+ if (reader.supportsAnimation())
+ type = "AnimatedImage";
+
+ newQmlItemNode = QmlItemNode(view->createModelNode(type, propertyPairList));
+#else
+
TypeName type("QtQuick.Image");
QImageReader reader(imageName);
if (reader.supportsAnimation())
type = "QtQuick.AnimatedImage";
- newQmlItemNode = QmlItemNode(view->createModelNode(type, metaInfo.majorVersion(), metaInfo.minorVersion(), propertyPairList));
+ newQmlItemNode = QmlItemNode(view->createModelNode(type,
+ metaInfo.majorVersion(),
+ metaInfo.minorVersion(),
+ propertyPairList));
+#endif
parentproperty.reparentHere(newQmlItemNode);
QFileInfo fi(relativeImageName);
@@ -129,7 +142,6 @@ QmlItemNode QmlItemNode::createQmlItemNodeFromFont(AbstractView *view,
QmlItemNode newQmlItemNode;
auto doCreateQmlItemNodeFromFont = [=, &newQmlItemNode, &parentproperty]() {
- NodeMetaInfo metaInfo = view->model()->metaInfo("QtQuick.Text");
QList<QPair<PropertyName, QVariant>> propertyPairList;
if (const int intX = qRound(position.x()))
propertyPairList.append({PropertyName("x"), QVariant(intX)});
@@ -138,9 +150,13 @@ QmlItemNode QmlItemNode::createQmlItemNodeFromFont(AbstractView *view,
propertyPairList.append({PropertyName("font.family"), QVariant(fontFamily)});
propertyPairList.append({PropertyName("font.pointSize"), 20});
propertyPairList.append({PropertyName("text"), QVariant(fontFamily)});
-
+#ifdef QDS_USE_PROJECTSTORAGE
+ newQmlItemNode = QmlItemNode(view->createModelNode("Text", propertyPairList));
+#else
+ NodeMetaInfo metaInfo = view->model()->metaInfo("QtQuick.Text");
newQmlItemNode = QmlItemNode(view->createModelNode("QtQuick.Text", metaInfo.majorVersion(),
metaInfo.minorVersion(), propertyPairList));
+#endif
parentproperty.reparentHere(newQmlItemNode);
newQmlItemNode.setId(view->model()->generateNewId("text", "text"));
@@ -480,6 +496,11 @@ bool QmlItemNode::instanceIsRenderPixmapNull() const
return nodeInstance().renderPixmap().isNull();
}
+bool QmlItemNode::instanceIsVisible() const
+{
+ return nodeInstance().property("visible").toBool();
+}
+
QPixmap QmlItemNode::instanceRenderPixmap() const
{
return nodeInstance().renderPixmap();
diff --git a/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp
index 55e56512e6..8e1d130295 100644
--- a/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp
@@ -475,7 +475,7 @@ QList<ModelNode> QmlObjectNode::getAllConnections() const
if (!isValid())
return {};
- auto list = view()->allModelNodesOfType(model()->qtQuickConnectionsMetaInfo());
+ auto list = view()->allModelNodesOfType(model()->qtQmlConnectionsMetaInfo());
return Utils::filtered(list, [this](const ModelNode &connection) {
return connection.hasBindingProperty("target")
&& connection.bindingProperty("target").resolveToModelNode() == modelNode();
diff --git a/src/plugins/qmldesigner/designercore/model/qmlstate.cpp b/src/plugins/qmldesigner/designercore/model/qmlstate.cpp
index 55864745a9..60ac06f288 100644
--- a/src/plugins/qmldesigner/designercore/model/qmlstate.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmlstate.cpp
@@ -141,6 +141,9 @@ void QmlModelState::addChangeSetIfNotExists(const ModelNode &node)
if (!hasPropertyChanges(node)) {
ModelNode newChangeSet;
+#ifdef QDS_USE_PROJECTSTORAGE
+ newChangeSet = modelNode().view()->createModelNode("PropertyChanges");
+#else
const QByteArray typeName = "QtQuick.PropertyChanges";
NodeMetaInfo metaInfo = modelNode().model()->metaInfo(typeName);
@@ -148,6 +151,7 @@ void QmlModelState::addChangeSetIfNotExists(const ModelNode &node)
int minor = metaInfo.minorVersion();
newChangeSet = modelNode().view()->createModelNode(typeName, major, minor);
+#endif
modelNode().nodeListProperty("changes").reparentHere(newChangeSet);
@@ -296,6 +300,9 @@ ModelNode QmlModelState::createQmlState(AbstractView *view, const PropertyListTy
{
QTC_ASSERT(view, return {});
+#ifdef QDS_USE_PROJECTSTORAGE
+ return view->createModelNode("State", propertyList);
+#else
const QByteArray typeName = "QtQuick.State";
NodeMetaInfo metaInfo = view->model()->metaInfo(typeName);
@@ -303,6 +310,7 @@ ModelNode QmlModelState::createQmlState(AbstractView *view, const PropertyListTy
int minor = metaInfo.minorVersion();
return view->createModelNode(typeName, major, minor, propertyList);
+#endif
}
void QmlModelState::setAsDefault()
diff --git a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp
index 75db18b5a2..c84f234257 100644
--- a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp
@@ -278,7 +278,11 @@ static QmlObjectNode createQmlObjectNodeFromSource(AbstractView *view,
const QString &source,
const QmlVisualNode::Position &position)
{
+#ifdef QDS_USE_PROJECTSTORAGE
+ auto inputModel = view->model()->createModel("Item");
+#else
auto inputModel = Model::create("QtQuick.Item", 1, 0, view->model());
+#endif
inputModel->setFileUrl(view->model()->fileUrl());
QPlainTextEdit textEdit;
@@ -328,11 +332,12 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view,
NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry);
auto createNodeFunc = [=, &newQmlObjectNode, &parentProperty]() {
+#ifndef QDS_USE_PROJECTSTORAGE
NodeMetaInfo metaInfo = view->model()->metaInfo(itemLibraryEntry.typeName());
int minorVersion = metaInfo.minorVersion();
int majorVersion = metaInfo.majorVersion();
-
+#endif
using PropertyBindingEntry = QPair<PropertyName, QString>;
QList<PropertyBindingEntry> propertyBindingList;
QList<PropertyBindingEntry> propertyEnumList;
@@ -359,7 +364,18 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view,
if (itemLibraryEntry.typeName() == "QtQml.Component")
nodeSourceType = ModelNode::NodeWithComponentSource;
- newQmlObjectNode = QmlObjectNode(view->createModelNode(itemLibraryEntry.typeName(), majorVersion, minorVersion, propertyPairList, {}, {}, nodeSourceType));
+#ifdef QDS_USE_PROJECTSTORAGE
+ newQmlObjectNode = QmlObjectNode(view->createModelNode(
+ itemLibraryEntry.typeName(), propertyPairList, {}, {}, nodeSourceType));
+#else
+ newQmlObjectNode = QmlObjectNode(view->createModelNode(itemLibraryEntry.typeName(),
+ majorVersion,
+ minorVersion,
+ propertyPairList,
+ {},
+ {},
+ nodeSourceType));
+#endif
} else {
newQmlObjectNode = createQmlObjectNodeFromSource(view, itemLibraryEntry.qmlSource(), position);
}
diff --git a/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp b/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp
index c4b4eb8c09..16877b61db 100644
--- a/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp
+++ b/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp
@@ -169,6 +169,11 @@ ModelNode StylesheetMerger::createReplacementNode(const ModelNode& styleNode, Mo
continue;
propertyList.append(QPair<PropertyName, QVariant>(variantProperty.name(), variantProperty.value()));
}
+
+#ifdef QDS_USE_PROJECTSTORAGE
+ ModelNode newNode(m_templateView->createModelNode(
+ styleNode.type(), propertyList, {}, styleNode.nodeSource(), styleNode.nodeSourceType()));
+#else
ModelNode newNode(m_templateView->createModelNode(styleNode.type(),
nodeMetaInfo.majorVersion(),
nodeMetaInfo.minorVersion(),
@@ -176,6 +181,7 @@ ModelNode StylesheetMerger::createReplacementNode(const ModelNode& styleNode, Mo
{},
styleNode.nodeSource(),
styleNode.nodeSourceType()));
+#endif
syncAuxiliaryProperties(newNode, modelNode);
syncBindingProperties(newNode, modelNode);
@@ -436,10 +442,14 @@ void StylesheetMerger::syncStateNode(ModelNode &outputState, const ModelNode &in
changeSet = itr->second;
} else {
const QByteArray typeName = inputChangeset.type();
+#ifdef QDS_USE_PROJECTSTORAGE
+ changeSet = m_templateView->createModelNode(typeName);
+#else
NodeMetaInfo metaInfo = m_templateView->model()->metaInfo(typeName);
int major = metaInfo.majorVersion();
int minor = metaInfo.minorVersion();
changeSet = m_templateView->createModelNode(typeName, major, minor);
+#endif
outputState.nodeListProperty("changes").reparentHere(changeSet);
outputChangeSets.insert({key, changeSet});
}
@@ -611,7 +621,11 @@ void StylesheetMerger::styleMerge(const QString &qmlTemplateString,
QTC_ASSERT(parentModel, return );
+#ifdef QDS_USE_PROJECTSTORAGE
+ auto templateModel = model->createModel("Item");
+#else
auto templateModel(Model::create("QtQuick.Item", 2, 1, parentModel));
+#endif
Q_ASSERT(templateModel.get());
templateModel->setFileUrl(parentModel->fileUrl());
@@ -637,9 +651,12 @@ void StylesheetMerger::styleMerge(const QString &qmlTemplateString,
ModelNode templateRootNode = templateRewriterView->rootModelNode();
QTC_ASSERT(templateRootNode.isValid(), return );
+#ifdef QDS_USE_PROJECTSTORAGE
+ auto styleModel = model->createModel("Item");
+#else
auto styleModel(Model::create("QtQuick.Item", 2, 1, parentModel));
Q_ASSERT(styleModel.get());
-
+#endif
styleModel->setFileUrl(parentModel->fileUrl());
QPlainTextEdit textEditStyle;
diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
index 9ab009bd83..3820322997 100644
--- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
+++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
@@ -67,17 +67,6 @@ bool isSupportedAttachedProperties(const QString &propertyName)
|| propertyName.startsWith(QLatin1String("InsightCategory."));
}
-bool isSupportedVersion(QmlDesigner::Version version)
-{
- if (version.major == 2)
- return version.minor <= 15;
-
- if (version.major == 6)
- return version.minor <= 6;
-
- return false;
-}
-
bool isGlobalQtEnums(QStringView value)
{
static constexpr auto list = Utils::to_array<std::u16string_view>(
@@ -122,12 +111,6 @@ bool isKnownEnumScopes(QStringView value)
!= std::end(list);
}
-bool supportedQtQuickVersion(const QmlDesigner::Import &import)
-{
- auto version = import.toVersion();
- return version.isEmpty() || isSupportedVersion(version);
-}
-
QString stripQuotes(const QString &str)
{
if ((str.startsWith(QLatin1Char('"')) && str.endsWith(QLatin1Char('"')))
@@ -917,13 +900,13 @@ QList<QmlDesigner::Import> generatePossibleFileImports(const QString &path,
QmlDesigner::Imports createQt5Modules()
{
- return {QmlDesigner::Import::createLibraryImport("QtQuick", "5.15"),
- QmlDesigner::Import::createLibraryImport("QtQuick.Controls", "5.15"),
- QmlDesigner::Import::createLibraryImport("QtQuick.Window", "5.15"),
- QmlDesigner::Import::createLibraryImport("QtQuick.Layouts", "5.15"),
- QmlDesigner::Import::createLibraryImport("QtQuick.Timeline", "5.15"),
- QmlDesigner::Import::createLibraryImport("QtCharts", "5.15"),
- QmlDesigner::Import::createLibraryImport("QtDataVisulaization", "5.15"),
+ return {QmlDesigner::Import::createLibraryImport("QtQuick", "2.15"),
+ QmlDesigner::Import::createLibraryImport("QtQuick.Controls", "2.15"),
+ QmlDesigner::Import::createLibraryImport("QtQuick.Window", "2.15"),
+ QmlDesigner::Import::createLibraryImport("QtQuick.Layouts", "2.15"),
+ QmlDesigner::Import::createLibraryImport("QtQuick.Timeline", "1.0"),
+ QmlDesigner::Import::createLibraryImport("QtCharts", "2.15"),
+ QmlDesigner::Import::createLibraryImport("QtDataVisulaization", "2.15"),
QmlDesigner::Import::createLibraryImport("QtQuick.Studio.Controls", "1.0"),
QmlDesigner::Import::createLibraryImport("QtQuick.Studio.Effects", "1.0"),
QmlDesigner::Import::createLibraryImport("FlowView", "1.0"),
@@ -1192,11 +1175,11 @@ void TextToModelMerger::syncNode(ModelNode &modelNode,
int majorVersion = -1;
int minorVersion = -1;
- if constexpr (!useProjectStorage()) {
- typeName = info.typeName();
- majorVersion = info.majorVersion();
- minorVersion = info.minorVersion();
- }
+#ifndef QDS_USE_PROJECTSTORAGE
+ typeName = info.typeName();
+ majorVersion = info.majorVersion();
+ minorVersion = info.minorVersion();
+#endif
if (modelNode.isRootNode() && !m_rewriterView->allowComponentRoot() && info.isQmlComponent()) {
for (AST::UiObjectMemberList *iter = astInitializer->members; iter; iter = iter->next) {
@@ -1222,9 +1205,13 @@ void TextToModelMerger::syncNode(ModelNode &modelNode,
return;
if (!isRootNode && modelNode.majorVersion() != -1 && modelNode.minorVersion() != -1) {
- qWarning() << "Preempting Node sync. Type differs" << modelNode <<
- modelNode.majorVersion() << modelNode.minorVersion();
- return; // the difference handler will create a new node, so we're done.
+ qWarning() << "Preempting Node sync. Type differs" << modelNode
+ << modelNode.majorVersion() << modelNode.minorVersion();
+
+ // Don't return when validating. We want node offset to be calculated and aux data
+ // to be correct.
+ if (differenceHandler.isAmender())
+ return; // the difference handler will create a new node, so we're done.
}
}
@@ -1559,11 +1546,12 @@ void TextToModelMerger::syncNodeProperty(AbstractProperty &modelProperty,
int majorVersion = -1;
int minorVersion = -1;
- if constexpr (!useProjectStorage()) {
- typeName = info.typeName();
- majorVersion = info.majorVersion();
- minorVersion = info.minorVersion();
- }
+
+#ifndef QDS_USE_PROJECTSTORAGE
+ typeName = info.typeName();
+ majorVersion = info.majorVersion();
+ minorVersion = info.minorVersion();
+#endif
if (modelProperty.isNodeProperty() && dynamicPropertyType == modelProperty.dynamicTypeName()) {
ModelNode nodePropertyNode = modelProperty.toNodeProperty().modelNode();
@@ -2100,11 +2088,11 @@ ModelNode ModelAmender::listPropertyMissingModelNode(NodeListProperty &modelProp
int majorVersion = -1;
int minorVersion = -1;
- if constexpr (!useProjectStorage()) {
- typeName = info.typeName();
- majorVersion = info.majorVersion();
- minorVersion = info.minorVersion();
- }
+#ifndef QDS_USE_PROJECTSTORAGE
+ typeName = info.typeName();
+ majorVersion = info.majorVersion();
+ minorVersion = info.minorVersion();
+#endif
const bool propertyTakesComponent = propertyHasImplicitComponentType(modelProperty, info);
@@ -2227,42 +2215,31 @@ void TextToModelMerger::collectImportErrors(QList<DocumentMessage> *errors)
bool hasQtQuick = false;
for (const QmlDesigner::Import &import : m_rewriterView->model()->imports()) {
if (import.isLibraryImport() && import.url() == u"QtQuick") {
- if (supportedQtQuickVersion(import)) {
- hasQtQuick = true;
-
- auto &externalDependencies = m_rewriterView->externalDependencies();
- if (externalDependencies.hasStartupTarget()) {
- const bool qt6import = !import.hasVersion() || import.majorVersion() == 6;
-
- if (!externalDependencies.isQt6Import() && (m_hasVersionlessImport || qt6import)) {
- const QmlJS::DiagnosticMessage diagnosticMessage(
- QmlJS::Severity::Error,
- SourceLocation(0, 0, 0, 0),
- QCoreApplication::translate(
- "QmlDesigner::TextToModelMerger",
- "Qt Quick 6 is not supported with a Qt 5 kit."));
- errors->prepend(
- DocumentMessage(diagnosticMessage,
- QUrl::fromLocalFile(m_document->fileName().path())));
- }
- } else {
+ hasQtQuick = true;
+
+ auto &externalDependencies = m_rewriterView->externalDependencies();
+ if (externalDependencies.hasStartupTarget()) {
+ const bool qt6import = !import.hasVersion() || import.majorVersion() == 6;
+
+ if (!externalDependencies.isQt6Import() && (m_hasVersionlessImport || qt6import)) {
const QmlJS::DiagnosticMessage diagnosticMessage(
QmlJS::Severity::Error,
SourceLocation(0, 0, 0, 0),
- QCoreApplication::translate("QmlDesigner::TextToModelMerger",
- "The Design Mode requires a valid Qt kit."));
+ QCoreApplication::translate(
+ "QmlDesigner::TextToModelMerger",
+ "Qt Quick 6 is not supported with a Qt 5 kit."));
errors->prepend(
DocumentMessage(diagnosticMessage,
QUrl::fromLocalFile(m_document->fileName().path())));
}
} else {
- const QmlJS::DiagnosticMessage
- diagnosticMessage(QmlJS::Severity::Error,
- SourceLocation(0, 0, 0, 0),
- QCoreApplication::translate("QmlDesigner::TextToModelMerger",
- "Unsupported Qt Quick version."));
- errors->append(DocumentMessage(diagnosticMessage,
- QUrl::fromLocalFile(m_document->fileName().path())));
+ const QmlJS::DiagnosticMessage diagnosticMessage(
+ QmlJS::Severity::Error,
+ SourceLocation(0, 0, 0, 0),
+ QCoreApplication::translate("QmlDesigner::TextToModelMerger",
+ "The Design Mode requires a valid Qt kit."));
+ errors->prepend(DocumentMessage(diagnosticMessage,
+ QUrl::fromLocalFile(m_document->fileName().path())));
}
}
}
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h
index 41f894356a..03c25dfac7 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h
@@ -55,6 +55,7 @@ inline constexpr char InstanceListEntry[] = "InstanceListEntry";
inline constexpr char InstanceList[] = "InstanceList";
inline constexpr char IntType[] = "int";
inline constexpr char Item[] = "Item";
+inline constexpr char JsonListModel[] = "JsonListModel";
inline constexpr char KeyframeGroup[] = "KeyframeGroup";
inline constexpr char Keyframe[] = "Keyframe";
inline constexpr char Layout[] = "Layout";
@@ -171,11 +172,11 @@ class CommonTypeCache
CacheType<QML, var>,
CacheType<QML_cppnative, FloatType>,
CacheType<QML_cppnative, UIntType>,
+ CacheType<QtQml, Connections>,
CacheType<QtMultimedia, SoundEffect>,
CacheType<QtQml_Models, ListElement>,
CacheType<QtQml_Models, ListModel>,
CacheType<QtQuick, BorderImage>,
- CacheType<QtQuick, Connections>,
CacheType<QtQuick, GridView>,
CacheType<QtQuick, Image>,
CacheType<QtQuick, Item>,
@@ -243,6 +244,7 @@ class CommonTypeCache
CacheType<QtQuick_Extras, Picture>,
CacheType<QtQuick_Layouts, Layout>,
CacheType<QtQuick_Studio_Components, GroupItem>,
+ CacheType<QtQuick_Studio_Components, JsonListModel>,
CacheType<QtQuick_Templates, Control>,
CacheType<QtQuick_Timeline, Keyframe>,
CacheType<QtQuick_Timeline, KeyframeGroup>,
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filestatus.h b/src/plugins/qmldesigner/designercore/projectstorage/filestatus.h
index f3e275b8f3..48b3ba2700 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/filestatus.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/filestatus.h
@@ -50,6 +50,18 @@ public:
explicit operator bool() const { return isValid(); }
+ template<typename String>
+ friend void convertToString(String &string, const FileStatus &fileStatus)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("source id", fileStatus.sourceId),
+ keyValue("size", fileStatus.size),
+ keyValue("last modified", fileStatus.lastModified));
+
+ convertToString(string, dict);
+ }
+
public:
SourceId sourceId;
long long size = -1;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp
index 700a85bc11..2bef2244d7 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp
@@ -3,23 +3,6 @@
#include "projectstorage.h"
-#include <tracing/qmldesignertracing.h>
-
#include <sqlitedatabase.h>
-namespace QmlDesigner {
-
-namespace {
-
-thread_local NanotraceHR::StringViewCategory<projectStorageTracingStatus()> projectStorageCategory_{
- "project storage"_t, Tracing::eventQueue(), projectStorageCategory};
-}
-
-NanotraceHR::StringViewCategory<projectStorageTracingStatus()> &projectStorageCategory()
-{
- return projectStorageCategory_;
-}
-
-} // namespace QmlDesigner
-
template class QmlDesigner::ProjectStorage<Sqlite::Database>;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h
index 6244977ec7..45a349c22c 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h
@@ -6,9 +6,12 @@
#include "commontypecache.h"
#include "projectstorageexceptions.h"
#include "projectstorageinterface.h"
+#include "projectstoragetypes.h"
#include "sourcepathcachetypes.h"
#include "storagecache.h"
+#include <tracing/qmldesignertracing.h>
+
#include <sqlitealgorithms.h>
#include <sqlitedatabase.h>
#include <sqlitetable.h>
@@ -28,16 +31,7 @@ namespace QmlDesigner {
using namespace NanotraceHR::Literals;
-constexpr NanotraceHR::Tracing projectStorageTracingStatus()
-{
-#ifdef ENABLE_PROJECT_STORAGE_TRACING
- return NanotraceHR::tracingStatus();
-#else
- return NanotraceHR::Tracing::IsDisabled;
-#endif
-}
-
-NanotraceHR::StringViewCategory<projectStorageTracingStatus()> &projectStorageCategory();
+using ProjectStorageTracing::projectStorageCategory;
template<typename Database>
class ProjectStorage final : public ProjectStorageInterface
@@ -135,7 +129,11 @@ public:
void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override
{
- NanotraceHR::Tracer tracer{"synchronize document imports"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"synchronize document imports"_t,
+ projectStorageCategory(),
+ keyValue("imports", imports),
+ keyValue("source id", sourceId)};
Sqlite::withImmediateTransaction(database, [&] {
synchronizeDocumentImports(imports,
@@ -144,188 +142,336 @@ public:
});
}
- void addObserver(ProjectStorageObserver *observer) override { observers.push_back(observer); }
+ void addObserver(ProjectStorageObserver *observer) override
+ {
+ NanotraceHR::Tracer tracer{"add observer"_t, projectStorageCategory()};
+ observers.push_back(observer);
+ }
void removeObserver(ProjectStorageObserver *observer) override
{
+ NanotraceHR::Tracer tracer{"remove observer"_t, projectStorageCategory()};
observers.removeOne(observer);
}
ModuleId moduleId(Utils::SmallStringView moduleName) const override
{
- NanotraceHR::Tracer tracer{"get module id"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get module id"_t,
+ projectStorageCategory(),
+ keyValue("module name", moduleName)};
- return moduleCache.id(moduleName);
+ auto moduleId = moduleCache.id(moduleName);
+
+ tracer.end(keyValue("module id", moduleId));
+
+ return moduleId;
}
Utils::SmallString moduleName(ModuleId moduleId) const
{
- NanotraceHR::Tracer tracer{"get module name"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get module name"_t,
+ projectStorageCategory(),
+ keyValue("module id", moduleId)};
if (!moduleId)
throw ModuleDoesNotExists{};
- return moduleCache.value(moduleId);
+ auto moduleName = moduleCache.value(moduleId);
+
+ tracer.end(keyValue("module name", moduleName));
+
+ return moduleName;
}
TypeId typeId(ModuleId moduleId,
Utils::SmallStringView exportedTypeName,
Storage::Version version) const override
{
- NanotraceHR::Tracer tracer{"get type id by exported name"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get type id by exported name"_t,
+ projectStorageCategory(),
+ keyValue("module id", moduleId),
+ keyValue("exported type name", exportedTypeName),
+ keyValue("version", version)};
- if (version.minor)
- return selectTypeIdByModuleIdAndExportedNameAndVersionStatement
- .template valueWithTransaction<TypeId>(moduleId,
- exportedTypeName,
- version.major.value,
- version.minor.value);
+ TypeId typeId;
+
+ if (version.minor) {
+ typeId = selectTypeIdByModuleIdAndExportedNameAndVersionStatement
+ .template valueWithTransaction<TypeId>(moduleId,
+ exportedTypeName,
+ version.major.value,
+ version.minor.value);
+
+ } else if (version.major) {
+ typeId = selectTypeIdByModuleIdAndExportedNameAndMajorVersionStatement
+ .template valueWithTransaction<TypeId>(moduleId,
+ exportedTypeName,
+ version.major.value);
- if (version.major)
- return selectTypeIdByModuleIdAndExportedNameAndMajorVersionStatement
- .template valueWithTransaction<TypeId>(moduleId, exportedTypeName, version.major.value);
+ } else {
+ typeId = selectTypeIdByModuleIdAndExportedNameStatement
+ .template valueWithTransaction<TypeId>(moduleId, exportedTypeName);
+ }
- return selectTypeIdByModuleIdAndExportedNameStatement
- .template valueWithTransaction<TypeId>(moduleId, exportedTypeName);
+ tracer.end(keyValue("type id", typeId));
+
+ return typeId;
}
TypeId typeId(ImportedTypeNameId typeNameId) const override
{
- NanotraceHR::Tracer tracer{"get type id by imported type name"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get type id by imported type name"_t,
+ projectStorageCategory(),
+ keyValue("imported type name id", typeNameId)};
+
+ auto typeId = Sqlite::withDeferredTransaction(database,
+ [&] { return fetchTypeId(typeNameId); });
- return Sqlite::withDeferredTransaction(database, [&] { return fetchTypeId(typeNameId); });
+ tracer.end(keyValue("type id", typeId));
+
+ return typeId;
}
QVarLengthArray<TypeId, 256> typeIds(ModuleId moduleId) const override
{
- NanotraceHR::Tracer tracer{"get type ids by module id"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get type ids by module id"_t,
+ projectStorageCategory(),
+ keyValue("module id", moduleId)};
+
+ auto typeIds = selectTypeIdsByModuleIdStatement
+ .template valuesWithTransaction<QVarLengthArray<TypeId, 256>>(moduleId);
- return selectTypeIdsByModuleIdStatement
- .template valuesWithTransaction<QVarLengthArray<TypeId, 256>>(moduleId);
+ tracer.end(keyValue("type ids", typeIds));
+
+ return typeIds;
}
Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId) const override
{
- NanotraceHR::Tracer tracer{"get exported type names by type id"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get exported type names by type id"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto exportedTypenames = selectExportedTypesByTypeIdStatement
+ .template valuesWithTransaction<Storage::Info::ExportedTypeName, 4>(
+ typeId);
+
+ tracer.end(keyValue("exported type names", exportedTypenames));
- return selectExportedTypesByTypeIdStatement
- .template valuesWithTransaction<Storage::Info::ExportedTypeName, 4>(typeId);
+ return exportedTypenames;
}
- Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId,
- SourceId sourceId) const override
+ Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId, SourceId sourceId) const override
{
- NanotraceHR::Tracer tracer{"get exported type names by source id"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get exported type names by source id"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId),
+ keyValue("source id", sourceId)};
+
+ auto exportedTypenames = selectExportedTypesByTypeIdAndSourceIdStatement
+ .template valuesWithTransaction<Storage::Info::ExportedTypeName,
+ 4>(typeId, sourceId);
+
+ tracer.end(keyValue("exported type names", exportedTypenames));
- return selectExportedTypesByTypeIdAndSourceIdStatement
- .template valuesWithTransaction<Storage::Info::ExportedTypeName, 4>(typeId, sourceId);
+ return exportedTypenames;
}
ImportId importId(const Storage::Import &import) const override
{
- NanotraceHR::Tracer tracer{"get import id by import"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get import id by import"_t,
+ projectStorageCategory(),
+ keyValue("import", import)};
- return Sqlite::withDeferredTransaction(database, [&] {
+ auto importId = Sqlite::withDeferredTransaction(database, [&] {
return fetchImportId(import.sourceId, import);
});
+
+ tracer.end(keyValue("import id", importId));
+
+ return importId;
}
- ImportedTypeNameId importedTypeNameId(ImportId importId,
- Utils::SmallStringView typeName) override
+ ImportedTypeNameId importedTypeNameId(ImportId importId, Utils::SmallStringView typeName) override
{
+ using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"get imported type name id by import id"_t,
- projectStorageCategory()};
+ projectStorageCategory(),
+ keyValue("import id", importId),
+ keyValue("imported type name", typeName)};
- return Sqlite::withDeferredTransaction(database, [&] {
+ auto importedTypeNameId = Sqlite::withDeferredTransaction(database, [&] {
return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::QualifiedExported,
importId,
typeName);
});
+
+ tracer.end(keyValue("imported type name id", importedTypeNameId));
+
+ return importedTypeNameId;
}
- ImportedTypeNameId importedTypeNameId(SourceId sourceId,
- Utils::SmallStringView typeName) override
+ ImportedTypeNameId importedTypeNameId(SourceId sourceId, Utils::SmallStringView typeName) override
{
+ using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"get imported type name id by source id"_t,
- projectStorageCategory()};
+ projectStorageCategory(),
+ keyValue("source id", sourceId),
+ keyValue("imported type name", typeName)};
- return Sqlite::withDeferredTransaction(database, [&] {
+ auto importedTypeNameId = Sqlite::withDeferredTransaction(database, [&] {
return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported,
sourceId,
typeName);
});
+
+ tracer.end(keyValue("imported type name id", importedTypeNameId));
+
+ return importedTypeNameId;
}
QVarLengthArray<PropertyDeclarationId, 128> propertyDeclarationIds(TypeId typeId) const override
{
- NanotraceHR::Tracer tracer{"get property declaration ids"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get property declaration ids"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto propertyDeclarationIds = selectPropertyDeclarationIdsForTypeStatement
+ .template valuesWithTransaction<
+ QVarLengthArray<PropertyDeclarationId, 128>>(typeId);
+
+ tracer.end(keyValue("property declaration ids", propertyDeclarationIds));
- return selectPropertyDeclarationIdsForTypeStatement
- .template valuesWithTransaction<QVarLengthArray<PropertyDeclarationId, 128>>(typeId);
+ return propertyDeclarationIds;
}
QVarLengthArray<PropertyDeclarationId, 128> localPropertyDeclarationIds(TypeId typeId) const override
{
- NanotraceHR::Tracer tracer{"get local property declaration ids"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get local property declaration ids"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
- return selectLocalPropertyDeclarationIdsForTypeStatement
- .template valuesWithTransaction<QVarLengthArray<PropertyDeclarationId, 128>>(typeId);
+ auto propertyDeclarationIds = selectLocalPropertyDeclarationIdsForTypeStatement
+ .template valuesWithTransaction<
+ QVarLengthArray<PropertyDeclarationId, 128>>(typeId);
+
+ tracer.end(keyValue("property declaration ids", propertyDeclarationIds));
+
+ return propertyDeclarationIds;
}
PropertyDeclarationId propertyDeclarationId(TypeId typeId,
Utils::SmallStringView propertyName) const override
{
- NanotraceHR::Tracer tracer{"get property declaration id"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get property declaration id"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId),
+ keyValue("property name", propertyName)};
+
+ auto propertyDeclarationId = selectPropertyDeclarationIdForTypeAndPropertyNameStatement
+ .template valueWithTransaction<PropertyDeclarationId>(
+ typeId, propertyName);
+
+ tracer.end(keyValue("property declaration id", propertyDeclarationId));
- return selectPropertyDeclarationIdForTypeAndPropertyNameStatement
- .template valueWithTransaction<PropertyDeclarationId>(typeId, propertyName);
+ return propertyDeclarationId;
}
PropertyDeclarationId localPropertyDeclarationId(TypeId typeId,
Utils::SmallStringView propertyName) const
{
- NanotraceHR::Tracer tracer{"get local property declaration id"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get local property declaration id"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId),
+ keyValue("property name", propertyName)};
- return selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement
- .template valueWithTransaction<PropertyDeclarationId>(typeId, propertyName);
+ auto propertyDeclarationId = selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement
+ .template valueWithTransaction<PropertyDeclarationId>(
+ typeId, propertyName);
+
+ tracer.end(keyValue("property declaration id", propertyDeclarationId));
+
+ return propertyDeclarationId;
}
std::optional<Storage::Info::PropertyDeclaration> propertyDeclaration(
PropertyDeclarationId propertyDeclarationId) const override
{
- NanotraceHR::Tracer tracer{"get property declaration"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get property declaration"_t,
+ projectStorageCategory(),
+ keyValue("property declaration id", propertyDeclarationId)};
+
+ auto propertyDeclaration = selectPropertyDeclarationForPropertyDeclarationIdStatement
+ .template optionalValueWithTransaction<Storage::Info::PropertyDeclaration>(
+ propertyDeclarationId);
- return selectPropertyDeclarationForPropertyDeclarationIdStatement
- .template optionalValueWithTransaction<Storage::Info::PropertyDeclaration>(
- propertyDeclarationId);
+ tracer.end(keyValue("property declaration", propertyDeclaration));
+
+ return propertyDeclaration;
}
std::optional<Storage::Info::Type> type(TypeId typeId) const override
{
- NanotraceHR::Tracer tracer{"get type"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get type"_t, projectStorageCategory(), keyValue("type id", typeId)};
- return selectInfoTypeByTypeIdStatement.template optionalValueWithTransaction<Storage::Info::Type>(
- typeId);
+ auto type = selectInfoTypeByTypeIdStatement
+ .template optionalValueWithTransaction<Storage::Info::Type>(typeId);
+
+ tracer.end(keyValue("type", type));
+
+ return type;
}
Utils::PathString typeIconPath(TypeId typeId) const override
{
- NanotraceHR::Tracer tracer{"get type icon path"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get type icon path"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto typeIconPath = selectTypeIconPathStatement.template valueWithTransaction<Utils::PathString>(
+ typeId);
+
+ tracer.end(keyValue("type icon path", typeIconPath));
- return selectTypeIconPathStatement.template valueWithTransaction<Utils::PathString>(typeId);
+ return typeIconPath;
}
Storage::Info::TypeHints typeHints(TypeId typeId) const override
{
- NanotraceHR::Tracer tracer{"get type hints"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get type hints"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
- return selectTypeHintsStatement.template valuesWithTransaction<Storage::Info::TypeHints, 4>(
- typeId);
+ auto typeHints = selectTypeHintsStatement
+ .template valuesWithTransaction<Storage::Info::TypeHints, 4>(typeId);
+
+ tracer.end(keyValue("type hints", typeHints));
+
+ return typeHints;
}
Storage::Info::ItemLibraryEntries itemLibraryEntries(TypeId typeId) const override
{
- NanotraceHR::Tracer tracer{"get item library entries by type id"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get item library entries by type id"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
using Storage::Info::ItemLibraryProperties;
Storage::Info::ItemLibraryEntries entries;
@@ -349,13 +495,17 @@ public:
selectItemLibraryEntriesByTypeIdStatement.readCallbackWithTransaction(callback, typeId);
+ tracer.end(keyValue("item library entries", entries));
+
return entries;
}
Storage::Info::ItemLibraryEntries itemLibraryEntries(SourceId sourceId) const override
{
+ using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"get item library entries by source id"_t,
- projectStorageCategory()};
+ projectStorageCategory(),
+ keyValue("source id", sourceId)};
using Storage::Info::ItemLibraryProperties;
Storage::Info::ItemLibraryEntries entries;
@@ -379,11 +529,14 @@ public:
selectItemLibraryEntriesBySourceIdStatement.readCallbackWithTransaction(callback, sourceId);
+ tracer.end(keyValue("item library entries", entries));
+
return entries;
}
Storage::Info::ItemLibraryEntries allItemLibraryEntries() const override
{
+ using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"get all item library entries"_t, projectStorageCategory()};
using Storage::Info::ItemLibraryProperties;
@@ -408,31 +561,57 @@ public:
selectItemLibraryEntriesStatement.readCallbackWithTransaction(callback);
+ tracer.end(keyValue("item library entries", entries));
+
return entries;
}
std::vector<Utils::SmallString> signalDeclarationNames(TypeId typeId) const override
{
- NanotraceHR::Tracer tracer{"get signal names"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get signal names"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto signalDeclarationNames = selectSignalDeclarationNamesForTypeStatement
+ .template valuesWithTransaction<Utils::SmallString, 32>(
+ typeId);
+
+ tracer.end(keyValue("signal names", signalDeclarationNames));
- return selectSignalDeclarationNamesForTypeStatement
- .template valuesWithTransaction<Utils::SmallString, 32>(typeId);
+ return signalDeclarationNames;
}
std::vector<Utils::SmallString> functionDeclarationNames(TypeId typeId) const override
{
- NanotraceHR::Tracer tracer{"get function names"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get function names"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
- return selectFuncionDeclarationNamesForTypeStatement
- .template valuesWithTransaction<Utils::SmallString, 32>(typeId);
+ auto functionDeclarationNames = selectFuncionDeclarationNamesForTypeStatement
+ .template valuesWithTransaction<Utils::SmallString, 32>(
+ typeId);
+
+ tracer.end(keyValue("function names", functionDeclarationNames));
+
+ return functionDeclarationNames;
}
std::optional<Utils::SmallString> propertyName(PropertyDeclarationId propertyDeclarationId) const override
{
- NanotraceHR::Tracer tracer{"get property name"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get property name"_t,
+ projectStorageCategory(),
+ keyValue("property declaration id", propertyDeclarationId)};
+
+ auto propertyName = selectPropertyNameStatement
+ .template optionalValueWithTransaction<Utils::SmallString>(
+ propertyDeclarationId);
- return selectPropertyNameStatement.template optionalValueWithTransaction<Utils::SmallString>(
- propertyDeclarationId);
+ tracer.end(keyValue("property name", propertyName));
+
+ return propertyName;
}
const Storage::Info::CommonTypeCache<ProjectStorageInterface> &commonTypeCache() const override
@@ -443,73 +622,115 @@ public:
template<const char *moduleName, const char *typeName>
TypeId commonTypeId() const
{
- NanotraceHR::Tracer tracer{"get type id from common type cache"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get type id from common type cache"_t,
+ projectStorageCategory(),
+ keyValue("module name", std::string_view{moduleName}),
+ keyValue("type name", std::string_view{typeName})};
+
+ auto typeId = commonTypeCache_.template typeId<moduleName, typeName>();
- return commonTypeCache_.template typeId<moduleName, typeName>();
+ tracer.end(keyValue("type id", typeId));
+
+ return typeId;
}
template<typename BuiltinType>
TypeId builtinTypeId() const
{
+ using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"get builtin type id from common type cache"_t,
projectStorageCategory()};
- return commonTypeCache_.template builtinTypeId<BuiltinType>();
+ auto typeId = commonTypeCache_.template builtinTypeId<BuiltinType>();
+
+ tracer.end(keyValue("type id", typeId));
+
+ return typeId;
}
template<const char *builtinType>
TypeId builtinTypeId() const
{
+ using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"get builtin type id from common type cache"_t,
projectStorageCategory()};
- return commonTypeCache_.template builtinTypeId<builtinType>();
+ auto typeId = commonTypeCache_.template builtinTypeId<builtinType>();
+
+ tracer.end(keyValue("type id", typeId));
+
+ return typeId;
}
TypeIds prototypeIds(TypeId type) const override
{
- NanotraceHR::Tracer tracer{"get prototypes"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get prototypes"_t,
+ projectStorageCategory(),
+ keyValue("type id", type)};
+
+ auto prototypeIds = selectPrototypeIdsForTypeIdInOrderStatement
+ .template valuesWithTransaction<TypeId, 16>(type);
+
+ tracer.end(keyValue("type ids", prototypeIds));
- return selectPrototypeIdsForTypeIdInOrderStatement.template valuesWithTransaction<TypeId, 16>(
- type);
+ return prototypeIds;
}
TypeIds prototypeAndSelfIds(TypeId type) const override
{
+ using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"get prototypes and self"_t, projectStorageCategory()};
- return selectPrototypeAndSelfIdsForTypeIdInOrderStatement
- .template valuesWithTransaction<TypeId, 16>(type);
+ TypeIds prototypeAndSelfIds = selectPrototypeAndSelfIdsForTypeIdInOrderStatement
+ .template valuesWithTransaction<TypeId, 16>(type);
+
+ tracer.end(keyValue("type ids", prototypeAndSelfIds));
+
+ return prototypeAndSelfIds;
}
TypeIds heirIds(TypeId typeId) const override
{
+ using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"get heirs"_t, projectStorageCategory()};
- return selectHeirTypeIdsStatement.template valuesWithTransaction<TypeId, 64>(typeId);
+ auto heirIds = selectHeirTypeIdsStatement.template valuesWithTransaction<TypeId, 64>(typeId);
+
+ tracer.end(keyValue("type ids", heirIds));
+
+ return heirIds;
}
template<typename... TypeIds>
bool isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const
{
- NanotraceHR::Tracer tracer{"is based on"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is based on"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId),
+ keyValue("base type ids", NanotraceHR::array(baseTypeIds...))};
static_assert(((std::is_same_v<TypeId, TypeIds>) &&...), "Parameter must be a TypeId!");
- if (((typeId == baseTypeIds) || ...))
+ if (((typeId == baseTypeIds) || ...)) {
+ tracer.end(keyValue("is based on", true));
return true;
+ }
auto range = selectPrototypeIdsStatement.template rangeWithTransaction<TypeId>(typeId);
- for ([[maybe_unused]] TypeId currentTypeId : range) {
- if (((currentTypeId == baseTypeIds) || ...))
- return true;
- }
+ auto isBasedOn = std::any_of(range.begin(), range.end(), [&](TypeId currentTypeId) {
+ return ((currentTypeId == baseTypeIds) || ...);
+ });
+
+ tracer.end(keyValue("is based on", isBasedOn));
- return false;
+ return isBasedOn;
}
- bool isBasedOn(TypeId typeId) const { return isBasedOn_(typeId); }
+ bool isBasedOn(TypeId) const { return false; }
bool isBasedOn(TypeId typeId, TypeId id1) const override { return isBasedOn_(typeId, id1); }
@@ -553,26 +774,57 @@ public:
TypeId fetchTypeIdByExportedName(Utils::SmallStringView name) const
{
- NanotraceHR::Tracer tracer{"is based on"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is based on"_t,
+ projectStorageCategory(),
+ keyValue("exported type name", name)};
+
+ auto typeId = selectTypeIdByExportedNameStatement.template valueWithTransaction<TypeId>(name);
+
+ tracer.end(keyValue("type id", typeId));
- return selectTypeIdByExportedNameStatement.template valueWithTransaction<TypeId>(name);
+ return typeId;
}
TypeId fetchTypeIdByModuleIdsAndExportedName(ModuleIds moduleIds, Utils::SmallStringView name) const
{
- return selectTypeIdByModuleIdsAndExportedNameStatement.template valueWithTransaction<TypeId>(
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch type id by module ids and exported name"_t,
+ projectStorageCategory(),
+ keyValue("module ids", NanotraceHR::array(moduleIds)),
+ keyValue("exported type name", name)};
+ auto typeId = selectTypeIdByModuleIdsAndExportedNameStatement.template valueWithTransaction<TypeId>(
static_cast<void *>(moduleIds.data()), static_cast<long long>(moduleIds.size()), name);
+
+ tracer.end(keyValue("type id", typeId));
+
+ return typeId;
}
TypeId fetchTypeIdByName(SourceId sourceId, Utils::SmallStringView name)
{
- return selectTypeIdBySourceIdAndNameStatement.template valueWithTransaction<TypeId>(sourceId,
- name);
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch type id by name"_t,
+ projectStorageCategory(),
+ keyValue("source id", sourceId),
+ keyValue("internal type name", name)};
+
+ auto typeId = selectTypeIdBySourceIdAndNameStatement
+ .template valueWithTransaction<TypeId>(sourceId, name);
+
+ tracer.end(keyValue("type id", typeId));
+
+ return typeId;
}
Storage::Synchronization::Type fetchTypeByTypeId(TypeId typeId)
{
- return Sqlite::withDeferredTransaction(database, [&] {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch type by type id"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto type = Sqlite::withDeferredTransaction(database, [&] {
auto type = selectTypeByTypeIdStatement.template value<Storage::Synchronization::Type>(
typeId);
@@ -584,11 +836,18 @@ public:
return type;
});
+
+ tracer.end(keyValue("type", type));
+
+ return type;
}
Storage::Synchronization::Types fetchTypes()
{
- return Sqlite::withDeferredTransaction(database, [&] {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch types"_t, projectStorageCategory()};
+
+ auto types = Sqlite::withDeferredTransaction(database, [&] {
auto types = selectTypesStatement.template values<Storage::Synchronization::Type, 64>();
for (Storage::Synchronization::Type &type : types) {
@@ -601,20 +860,15 @@ public:
return types;
});
- }
- bool fetchIsProtype(TypeId type, TypeId prototype)
- {
- return bool(selectPrototypeIdStatement.template valueWithTransaction<TypeId>(type, prototype));
- }
+ tracer.end(keyValue("type", types));
- auto fetchPrototypes(TypeId type)
- {
- return selectPrototypeIdsInOrderStatement.template rangeWithTransaction<TypeId>(type);
+ return types;
}
SourceContextId fetchSourceContextIdUnguarded(Utils::SmallStringView sourceContextPath)
{
+ using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"fetch source context id unguarded"_t, projectStorageCategory()};
auto sourceContextId = readSourceContextId(sourceContextPath);
@@ -624,22 +878,33 @@ public:
SourceContextId fetchSourceContextId(Utils::SmallStringView sourceContextPath)
{
- NanotraceHR::Tracer tracer{"fetch source context id"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch source context id"_t,
+ projectStorageCategory(),
+ keyValue("source context path", sourceContextPath)};
+ SourceContextId sourceContextId;
try {
- return Sqlite::withDeferredTransaction(database, [&] {
+ sourceContextId = Sqlite::withDeferredTransaction(database, [&] {
return fetchSourceContextIdUnguarded(sourceContextPath);
});
} catch (const Sqlite::ConstraintPreventsModification &) {
- return fetchSourceContextId(sourceContextPath);
+ sourceContextId = fetchSourceContextId(sourceContextPath);
}
+
+ tracer.end(keyValue("source context id", sourceContextId));
+
+ return sourceContextId;
}
Utils::PathString fetchSourceContextPath(SourceContextId sourceContextId) const
{
- NanotraceHR::Tracer tracer{"fetch source context path"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch source context path"_t,
+ projectStorageCategory(),
+ keyValue("source context id", sourceContextId)};
- return Sqlite::withDeferredTransaction(database, [&] {
+ auto path = Sqlite::withDeferredTransaction(database, [&] {
auto optionalSourceContextPath = selectSourceContextPathFromSourceContextsBySourceContextIdStatement
.template optionalValue<Utils::PathString>(
sourceContextId);
@@ -649,6 +914,10 @@ public:
return std::move(*optionalSourceContextPath);
});
+
+ tracer.end(keyValue("source context path", path));
+
+ return path;
}
auto fetchAllSourceContexts() const
@@ -661,17 +930,27 @@ public:
SourceId fetchSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName)
{
- NanotraceHR::Tracer tracer{"fetch source id"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch source id"_t,
+ projectStorageCategory(),
+ keyValue("source context id", sourceContextId),
+ keyValue("source name", sourceName)};
- return Sqlite::withDeferredTransaction(database, [&] {
+ auto sourceId = Sqlite::withDeferredTransaction(database, [&] {
return fetchSourceIdUnguarded(sourceContextId, sourceName);
});
+
+ tracer.end(keyValue("source id", sourceId));
+
+ return sourceId;
}
auto fetchSourceNameAndSourceContextId(SourceId sourceId) const
{
+ using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"fetch source name and source context id"_t,
- projectStorageCategory()};
+ projectStorageCategory(),
+ keyValue("source id", sourceId)};
auto value = selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement
.template valueWithTransaction<Cache::SourceNameAndSourceContextId>(sourceId);
@@ -679,6 +958,9 @@ public:
if (!value.sourceContextId)
throw SourceIdDoesNotExists();
+ tracer.end(keyValue("source name", value.sourceName),
+ keyValue("source context id", value.sourceContextId));
+
return value;
}
@@ -692,7 +974,10 @@ public:
SourceContextId fetchSourceContextId(SourceId sourceId) const
{
- NanotraceHR::Tracer tracer{"fetch source context id"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch source context id"_t,
+ projectStorageCategory(),
+ keyValue("source id", sourceId)};
auto sourceContextId = selectSourceContextIdFromSourcesBySourceIdStatement
.template valueWithTransaction<SourceContextId>(sourceId);
@@ -700,6 +985,8 @@ public:
if (!sourceContextId)
throw SourceIdDoesNotExists();
+ tracer.end(keyValue("source context id", sourceContextId));
+
return sourceContextId;
}
@@ -712,14 +999,20 @@ public:
SourceId fetchSourceIdUnguarded(SourceContextId sourceContextId, Utils::SmallStringView sourceName)
{
- NanotraceHR::Tracer tracer{"fetch source id unguarded"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch source id unguarded"_t,
+ projectStorageCategory(),
+ keyValue("source context id", sourceContextId),
+ keyValue("source name", sourceName)};
auto sourceId = readSourceId(sourceContextId, sourceName);
- if (sourceId)
- return sourceId;
+ if (!sourceId)
+ sourceId = writeSourceId(sourceContextId, sourceName);
- return writeSourceId(sourceContextId, sourceName);
+ tracer.end(keyValue("source id", sourceId));
+
+ return sourceId;
}
auto fetchAllFileStatuses() const
@@ -731,36 +1024,64 @@ public:
FileStatus fetchFileStatus(SourceId sourceId) const override
{
- NanotraceHR::Tracer tracer{"fetch file status"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch file status"_t,
+ projectStorageCategory(),
+ keyValue("source id", sourceId)};
+
+ auto fileStatus = selectFileStatusesForSourceIdStatement
+ .template valueWithTransaction<FileStatus>(sourceId);
+
+ tracer.end(keyValue("file status", fileStatus));
- return selectFileStatusesForSourceIdStatement.template valueWithTransaction<FileStatus>(
- sourceId);
+ return fileStatus;
}
std::optional<Storage::Synchronization::ProjectData> fetchProjectData(SourceId sourceId) const override
{
- NanotraceHR::Tracer tracer{"fetch project data"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch project data"_t,
+ projectStorageCategory(),
+ keyValue("source id", sourceId)};
+
+ auto projectData = selectProjectDataForSourceIdStatement.template optionalValueWithTransaction<
+ Storage::Synchronization::ProjectData>(sourceId);
+
+ tracer.end(keyValue("project data", projectData));
- return selectProjectDataForSourceIdStatement
- .template optionalValueWithTransaction<Storage::Synchronization::ProjectData>(sourceId);
+ return projectData;
}
Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId projectSourceId) const override
{
- NanotraceHR::Tracer tracer{"fetch project datas by source id"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch project datas by source id"_t,
+ projectStorageCategory(),
+ keyValue("source id", projectSourceId)};
- return selectProjectDatasForSourceIdStatement
- .template valuesWithTransaction<Storage::Synchronization::ProjectData, 1024>(
- projectSourceId);
+ auto projectDatas = selectProjectDatasForSourceIdStatement
+ .template valuesWithTransaction<Storage::Synchronization::ProjectData, 1024>(
+ projectSourceId);
+
+ tracer.end(keyValue("project datas", projectDatas));
+
+ return projectDatas;
}
Storage::Synchronization::ProjectDatas fetchProjectDatas(const SourceIds &projectSourceIds) const
{
- NanotraceHR::Tracer tracer{"fetch project datas by source ids"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch project datas by source ids"_t,
+ projectStorageCategory(),
+ keyValue("source ids", projectSourceIds)};
+
+ auto projectDatas = selectProjectDatasForSourceIdsStatement
+ .template valuesWithTransaction<Storage::Synchronization::ProjectData, 64>(
+ toIntegers(projectSourceIds));
+
+ tracer.end(keyValue("project datas", projectDatas));
- return selectProjectDatasForSourceIdsStatement
- .template valuesWithTransaction<Storage::Synchronization::ProjectData, 64>(
- toIntegers(projectSourceIds));
+ return projectDatas;
}
void setPropertyEditorPathId(TypeId typeId, SourceId pathId)
@@ -774,7 +1095,17 @@ public:
SourceId propertyEditorPathId(TypeId typeId) const override
{
- return selectPropertyEditorPathIdStatement.template valueWithTransaction<SourceId>(typeId);
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"property editor path id"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto sourceId = selectPropertyEditorPathIdStatement.template valueWithTransaction<SourceId>(
+ typeId);
+
+ tracer.end(keyValue("source id", sourceId));
+
+ return sourceId;
}
Storage::Imports fetchDocumentImports() const
@@ -835,22 +1166,50 @@ private:
ModuleId fetchModuleId(Utils::SmallStringView moduleName)
{
- return Sqlite::withDeferredTransaction(database,
- [&] { return fetchModuleIdUnguarded(moduleName); });
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch module id"_t,
+ projectStorageCategory(),
+ keyValue("module name", moduleName)};
+
+ auto moduleId = Sqlite::withDeferredTransaction(database, [&] {
+ return fetchModuleIdUnguarded(moduleName);
+ });
+
+ tracer.end(keyValue("module id", moduleId));
+
+ return moduleId;
}
auto fetchModuleName(ModuleId id)
{
- return Sqlite::withDeferredTransaction(database, [&] { return fetchModuleNameUnguarded(id); });
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch module name"_t,
+ projectStorageCategory(),
+ keyValue("module id", id)};
+
+ auto moduleName = Sqlite::withDeferredTransaction(database, [&] {
+ return fetchModuleNameUnguarded(id);
+ });
+
+ tracer.end(keyValue("module name", moduleName));
+
+ return moduleName;
}
auto fetchAllModules() const
{
+ NanotraceHR::Tracer tracer{"fetch all modules"_t, projectStorageCategory()};
+
return selectAllModulesStatement.template valuesWithTransaction<Module, 128>();
}
void callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"call refresh meta info callback"_t,
+ projectStorageCategory(),
+ keyValue("type ids", deletedTypeIds)};
+
if (deletedTypeIds.size()) {
for (ProjectStorageObserver *observer : observers)
observer->removedTypeIds(deletedTypeIds);
@@ -882,6 +1241,25 @@ private:
< std::tie(second.typeId, second.propertyDeclarationId);
}
+ template<typename String>
+ friend void convertToString(String &string,
+ const AliasPropertyDeclaration &aliasPropertyDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(
+ keyValue("type id", aliasPropertyDeclaration.typeId),
+ keyValue("property declaration id", aliasPropertyDeclaration.propertyDeclarationId),
+ keyValue("alias imported type name id",
+ aliasPropertyDeclaration.aliasImportedTypeNameId),
+ keyValue("alias property name", aliasPropertyDeclaration.aliasPropertyName),
+ keyValue("alias property name tail", aliasPropertyDeclaration.aliasPropertyNameTail),
+ keyValue("alias property declaration id",
+ aliasPropertyDeclaration.aliasPropertyDeclarationId));
+
+ convertToString(string, dict);
+ }
+
public:
TypeId typeId;
PropertyDeclarationId propertyDeclarationId;
@@ -910,6 +1288,20 @@ private:
< std::tie(second.typeId, second.propertyDeclarationId);
}
+ template<typename String>
+ friend void convertToString(String &string, const PropertyDeclaration &propertyDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("type id", propertyDeclaration.typeId),
+ keyValue("property declaration id",
+ propertyDeclaration.propertyDeclarationId),
+ keyValue("imported type name id",
+ propertyDeclaration.importedTypeNameId));
+
+ convertToString(string, dict);
+ }
+
public:
TypeId typeId;
PropertyDeclarationId propertyDeclarationId;
@@ -931,6 +1323,17 @@ private:
return first.typeId < second.typeId;
}
+ template<typename String>
+ friend void convertToString(String &string, const Prototype &prototype)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("type id", prototype.typeId),
+ keyValue("prototype name id", prototype.prototypeNameId));
+
+ convertToString(string, dict);
+ }
+
public:
TypeId typeId;
ImportedTypeNameId prototypeNameId;
@@ -987,6 +1390,11 @@ private:
TypeIds fetchTypeIds(const SourceIds &sourceIds)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch type ids"_t,
+ projectStorageCategory(),
+ keyValue("source ids", sourceIds)};
+
return selectTypeIdsForSourceIdsStatement.template values<TypeId, 128>(toIntegers(sourceIds));
}
@@ -999,6 +1407,12 @@ private:
void synchronizeTypeTraits(TypeId typeId, Storage::TypeTraits traits)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"synchronize type traits"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId),
+ keyValue("type traits", traits)};
+
updateTypeAnnotationTraitStatement.write(typeId, traits.annotation);
}
@@ -1015,6 +1429,19 @@ private:
, hintsJson{hintsJson}
{}
+ template<typename String>
+ friend void convertToString(String &string, const TypeAnnotationView &typeAnnotationView)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("type id", typeAnnotationView.typeId),
+ keyValue("icon path", typeAnnotationView.iconPath),
+ keyValue("item library json", typeAnnotationView.itemLibraryJson),
+ keyValue("hints json", typeAnnotationView.hintsJson));
+
+ convertToString(string, dict);
+ }
+
public:
TypeId typeId;
Utils::SmallStringView iconPath;
@@ -1024,6 +1451,8 @@ private:
void updateTypeIdInTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations)
{
+ NanotraceHR::Tracer tracer{"update type id in type annotations"_t, projectStorageCategory()};
+
for (auto &annotation : typeAnnotations) {
annotation.typeId = fetchTypeIdByModuleIdAndExportedName(annotation.moduleId,
annotation.typeName);
@@ -1054,6 +1483,11 @@ private:
synchronizeTypeTraits(annotation.typeId, annotation.traits);
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert type annotations"_t,
+ projectStorageCategory(),
+ keyValue("type annotation", annotation)};
+
insertTypeAnnotationStatement.write(annotation.typeId,
annotation.sourceId,
annotation.iconPath,
@@ -1068,6 +1502,13 @@ private:
if (annotationFromDatabase.iconPath != annotation.iconPath
|| annotationFromDatabase.itemLibraryJson != annotation.itemLibraryJson
|| annotationFromDatabase.hintsJson != annotation.hintsJson) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"update type annotations"_t,
+ projectStorageCategory(),
+ keyValue("type annotation from database",
+ annotationFromDatabase),
+ keyValue("type annotation", annotation)};
+
updateTypeAnnotationStatement.write(annotation.typeId,
annotation.iconPath,
annotation.itemLibraryJson,
@@ -1081,6 +1522,11 @@ private:
auto remove = [&](const TypeAnnotationView &annotationFromDatabase) {
synchronizeTypeTraits(annotationFromDatabase.typeId, Storage::TypeTraits{});
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"remove type annotations"_t,
+ projectStorageCategory(),
+ keyValue("type annotation", annotationFromDatabase)};
+
deleteTypeAnnotationStatement.write(annotationFromDatabase.typeId);
};
@@ -1179,6 +1625,11 @@ private:
toIntegers(updatedProjectSourceIds));
auto insert = [&](const Storage::Synchronization::ProjectData &projectData) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert project data"_t,
+ projectStorageCategory(),
+ keyValue("project data", projectData)};
+
if (!projectData.projectSourceId)
throw ProjectDataHasInvalidProjectSourceId{};
if (!projectData.sourceId)
@@ -1194,6 +1645,13 @@ private:
const Storage::Synchronization::ProjectData &projectData) {
if (projectDataFromDatabase.fileType != projectData.fileType
|| !compareInvalidAreTrue(projectDataFromDatabase.moduleId, projectData.moduleId)) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"update project data"_t,
+ projectStorageCategory(),
+ keyValue("project data", projectData),
+ keyValue("project data from database",
+ projectDataFromDatabase)};
+
updateProjectDataStatement.write(projectData.projectSourceId,
projectData.sourceId,
projectData.moduleId,
@@ -1205,6 +1663,11 @@ private:
};
auto remove = [&](const Storage::Synchronization::ProjectData &projectData) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"remove project data"_t,
+ projectStorageCategory(),
+ keyValue("project data", projectData)};
+
deleteProjectDataStatement.write(projectData.projectSourceId, projectData.sourceId);
};
@@ -1227,6 +1690,11 @@ private:
toIntegers(updatedSourceIds));
auto insert = [&](const FileStatus &fileStatus) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert file status"_t,
+ projectStorageCategory(),
+ keyValue("file status", fileStatus)};
+
if (!fileStatus.sourceId)
throw FileStatusHasInvalidSourceId{};
insertFileStatusStatement.write(fileStatus.sourceId,
@@ -1237,6 +1705,13 @@ private:
auto update = [&](const FileStatus &fileStatusFromDatabase, const FileStatus &fileStatus) {
if (fileStatusFromDatabase.lastModified != fileStatus.lastModified
|| fileStatusFromDatabase.size != fileStatus.size) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"update file status"_t,
+ projectStorageCategory(),
+ keyValue("file status", fileStatus),
+ keyValue("file status from database",
+ fileStatusFromDatabase)};
+
updateFileStatusStatement.write(fileStatus.sourceId,
fileStatus.size,
fileStatus.lastModified);
@@ -1247,6 +1722,11 @@ private:
};
auto remove = [&](const FileStatus &fileStatus) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"remove file status"_t,
+ projectStorageCategory(),
+ keyValue("file status", fileStatus)};
+
deleteFileStatusStatement.write(fileStatus.sourceId);
};
@@ -1263,18 +1743,25 @@ private:
NanotraceHR::Tracer tracer{"synchronize imports"_t, projectStorageCategory()};
synchromizeModuleExportedImports(moduleExportedImports, updatedModuleIds);
+ NanotraceHR::Tracer importTracer{"synchronize qml document imports"_t,
+ projectStorageCategory()};
synchronizeDocumentImports(imports,
updatedSourceIds,
Storage::Synchronization::ImportKind::Import);
+ importTracer.end();
+ NanotraceHR::Tracer moduleDependenciesTracer{"synchronize module depdencies"_t,
+ projectStorageCategory()};
synchronizeDocumentImports(moduleDependencies,
updatedModuleDependencySourceIds,
Storage::Synchronization::ImportKind::ModuleDependency);
+ moduleDependenciesTracer.end();
}
void synchromizeModuleExportedImports(
Storage::Synchronization::ModuleExportedImports &moduleExportedImports,
const ModuleIds &updatedModuleIds)
{
+ NanotraceHR::Tracer tracer{"synchronize module exported imports"_t, projectStorageCategory()};
std::sort(moduleExportedImports.begin(),
moduleExportedImports.end(),
[](auto &&first, auto &&second) {
@@ -1296,6 +1783,13 @@ private:
};
auto insert = [&](const Storage::Synchronization::ModuleExportedImport &import) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert module exported import"_t,
+ projectStorageCategory(),
+ keyValue("module exported import", import),
+ keyValue("module id", import.moduleId)};
+ tracer.tick("exported module"_t, keyValue("module id", import.exportedModuleId));
+
if (import.version.minor) {
insertModuleExportedImportWithVersionStatement.write(import.moduleId,
import.exportedModuleId,
@@ -1320,6 +1814,13 @@ private:
};
auto remove = [&](const Storage::Synchronization::ModuleExportedImportView &view) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"remove module exported import"_t,
+ projectStorageCategory(),
+ keyValue("module exported import view", view),
+ keyValue("module id", view.moduleId)};
+ tracer.tick("exported module"_t, keyValue("module id", view.exportedModuleId));
+
deleteModuleExportedImportStatement.write(view.moduleExportedImportId);
};
@@ -1328,27 +1829,48 @@ private:
ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const override
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch module id ungarded"_t,
+ projectStorageCategory(),
+ keyValue("module name", name)};
+
auto moduleId = selectModuleIdByNameStatement.template value<ModuleId>(name);
- if (moduleId)
- return moduleId;
+ if (!moduleId)
+ moduleId = insertModuleNameStatement.template value<ModuleId>(name);
+
+ tracer.end(keyValue("module id", moduleId));
- return insertModuleNameStatement.template value<ModuleId>(name);
+ return moduleId;
}
auto fetchModuleNameUnguarded(ModuleId id) const
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch module name ungarded"_t,
+ projectStorageCategory(),
+ keyValue("module id", id)};
+
auto moduleName = selectModuleNameStatement.template value<Utils::PathString>(id);
if (moduleName.empty())
throw ModuleDoesNotExists{};
+ tracer.end(keyValue("module name", moduleName));
+
return moduleName;
}
void handleAliasPropertyDeclarationsWithPropertyType(
TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"handle alias property declarations with property type"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId),
+ keyValue("relinkable alias property declarations",
+ relinkableAliasPropertyDeclarations)};
+
auto callback = [&](TypeId typeId_,
PropertyDeclarationId propertyDeclarationId,
ImportedTypeNameId propertyImportedTypeNameId,
@@ -1378,12 +1900,25 @@ private:
void handlePropertyDeclarationWithPropertyType(TypeId typeId,
PropertyDeclarations &relinkablePropertyDeclarations)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"handle property declarations with property type"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId),
+ keyValue("relinkable property declarations",
+ relinkablePropertyDeclarations)};
+
updatesPropertyDeclarationPropertyTypeToNullStatement.readTo(relinkablePropertyDeclarations,
typeId);
}
void handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"handle prototypes"_t,
+ projectStorageCategory(),
+ keyValue("type id", prototypeId),
+ keyValue("relinkable prototypes", relinkablePrototypes)};
+
auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) {
relinkablePrototypes.emplace_back(typeId, prototypeNameId);
};
@@ -1393,6 +1928,12 @@ private:
void handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"handle extension"_t,
+ projectStorageCategory(),
+ keyValue("type id", extensionId),
+ keyValue("relinkable extensions", relinkableExtensions)};
+
auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) {
relinkableExtensions.emplace_back(typeId, extensionNameId);
};
@@ -1406,6 +1947,11 @@ private:
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"delete type"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
handlePropertyDeclarationWithPropertyType(typeId, relinkablePropertyDeclarations);
handleAliasPropertyDeclarationsWithPropertyType(typeId, relinkableAliasPropertyDeclarations);
handlePrototypes(typeId, relinkablePrototypes);
@@ -1421,7 +1967,11 @@ private:
void relinkAliasPropertyDeclarations(AliasPropertyDeclarations &aliasPropertyDeclarations,
const TypeIds &deletedTypeIds)
{
- NanotraceHR::Tracer tracer{"relink alias properties"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"relink alias properties"_t,
+ projectStorageCategory(),
+ keyValue("alias property declarations", aliasPropertyDeclarations),
+ keyValue("deleted type ids", deletedTypeIds)};
std::sort(aliasPropertyDeclarations.begin(), aliasPropertyDeclarations.end());
@@ -1451,7 +2001,12 @@ private:
void relinkPropertyDeclarations(PropertyDeclarations &relinkablePropertyDeclaration,
const TypeIds &deletedTypeIds)
{
- NanotraceHR::Tracer tracer{"relink properties"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"relink property declarations"_t,
+ projectStorageCategory(),
+ keyValue("relinkable property declarations",
+ relinkablePropertyDeclaration),
+ keyValue("deleted type ids", deletedTypeIds)};
std::sort(relinkablePropertyDeclaration.begin(), relinkablePropertyDeclaration.end());
@@ -1477,7 +2032,11 @@ private:
const TypeIds &deletedTypeIds,
Callable updateStatement)
{
- NanotraceHR::Tracer tracer{"relink prototypes"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"relink prototypes"_t,
+ projectStorageCategory(),
+ keyValue("relinkable prototypes", relinkablePrototypes),
+ keyValue("deleted type ids", deletedTypeIds)};
std::sort(relinkablePrototypes.begin(), relinkablePrototypes.end());
@@ -1507,7 +2066,12 @@ private:
Prototypes &relinkableExtensions,
TypeIds &deletedTypeIds)
{
- NanotraceHR::Tracer tracer{"delete not updated types"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"delete not updated types"_t,
+ projectStorageCategory(),
+ keyValue("updated type ids", updatedTypeIds),
+ keyValue("updated source ids", updatedSourceIds),
+ keyValue("type ids to be deleted", typeIdsToBeDeleted)};
auto callback = [&](TypeId typeId) {
deletedTypeIds.push_back(typeId);
@@ -1549,6 +2113,13 @@ private:
Utils::SmallStringView aliasPropertyName,
Utils::SmallStringView aliasPropertyNameTail)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch alias id"_t,
+ projectStorageCategory(),
+ keyValue("alias type id", aliasTypeId),
+ keyValue("alias property name", aliasPropertyName),
+ keyValue("alias property name tail", aliasPropertyNameTail)};
+
if (aliasPropertyNameTail.empty())
return fetchPropertyDeclarationIdByTypeIdAndNameUngarded(aliasTypeId, aliasPropertyName);
@@ -1561,6 +2132,11 @@ private:
void linkAliasPropertyDeclarationAliasIds(const AliasPropertyDeclarations &aliasDeclarations)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"link alias property declarations alias ids"_t,
+ projectStorageCategory(),
+ keyValue("alias property declarations", aliasDeclarations)};
+
for (const auto &aliasDeclaration : aliasDeclarations) {
auto aliasTypeId = fetchTypeId(aliasDeclaration.aliasImportedTypeNameId);
@@ -1582,6 +2158,11 @@ private:
void updateAliasPropertyDeclarationValues(const AliasPropertyDeclarations &aliasDeclarations)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"update alias property declarations"_t,
+ projectStorageCategory(),
+ keyValue("alias property declarations", aliasDeclarations)};
+
for (const auto &aliasDeclaration : aliasDeclarations) {
updatetPropertiesDeclarationValuesOfAliasStatement.write(
aliasDeclaration.propertyDeclarationId);
@@ -1592,6 +2173,10 @@ private:
void checkAliasPropertyDeclarationCycles(const AliasPropertyDeclarations &aliasDeclarations)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"check alias property declarations cycles"_t,
+ projectStorageCategory(),
+ keyValue("alias property declarations", aliasDeclarations)};
for (const auto &aliasDeclaration : aliasDeclarations)
checkForAliasChainCycle(aliasDeclaration.propertyDeclarationId);
}
@@ -1599,6 +2184,7 @@ private:
void linkAliases(const AliasPropertyDeclarations &insertedAliasPropertyDeclarations,
const AliasPropertyDeclarations &updatedAliasPropertyDeclarations)
{
+ using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"link aliases"_t, projectStorageCategory()};
linkAliasPropertyDeclarationAliasIds(insertedAliasPropertyDeclarations);
@@ -1618,6 +2204,7 @@ private:
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions)
{
+ using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"synchronize exported types"_t, projectStorageCategory()};
std::sort(exportedTypes.begin(), exportedTypes.end(), [](auto &&first, auto &&second) {
@@ -1658,6 +2245,12 @@ private:
};
auto insert = [&](const Storage::Synchronization::ExportedType &type) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert exported type"_t,
+ projectStorageCategory(),
+ keyValue("exported type", type),
+ keyValue("type id", type.typeId),
+ keyValue("module id", type.moduleId)};
if (!type.moduleId)
throw QmlDesigner::ModuleDoesNotExists{};
@@ -1687,6 +2280,13 @@ private:
auto update = [&](const Storage::Synchronization::ExportedTypeView &view,
const Storage::Synchronization::ExportedType &type) {
if (view.typeId != type.typeId) {
+ NanotraceHR::Tracer tracer{"update exported type"_t,
+ projectStorageCategory(),
+ keyValue("exported type", type),
+ keyValue("exported type view", view),
+ keyValue("type id", type.typeId),
+ keyValue("module id", type.typeId)};
+
handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations);
handleAliasPropertyDeclarationsWithPropertyType(view.typeId,
relinkableAliasPropertyDeclarations);
@@ -1699,6 +2299,12 @@ private:
};
auto remove = [&](const Storage::Synchronization::ExportedTypeView &view) {
+ NanotraceHR::Tracer tracer{"remove exported type"_t,
+ projectStorageCategory(),
+ keyValue("exported type", view),
+ keyValue("type id", view.typeId),
+ keyValue("module id", view.moduleId)};
+
handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations);
handleAliasPropertyDeclarationsWithPropertyType(view.typeId,
relinkableAliasPropertyDeclarations);
@@ -1716,6 +2322,11 @@ private:
SourceId sourceId,
TypeId typeId)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert property declaration to alias"_t,
+ projectStorageCategory(),
+ keyValue("property declaration", value)};
+
auto callback = [&](PropertyDeclarationId propertyDeclarationId) {
insertedAliasPropertyDeclarations.emplace_back(typeId,
propertyDeclarationId,
@@ -1732,11 +2343,16 @@ private:
void synchronizePropertyDeclarationsInsertProperty(
const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, TypeId typeId)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert property declaration"_t,
+ projectStorageCategory(),
+ keyValue("property declaration", value)};
+
auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId);
auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId);
if (!propertyTypeId)
- throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId)};
+ throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId), sourceId};
auto propertyDeclarationId = insertPropertyDeclarationStatement.template value<PropertyDeclarationId>(
typeId, value.name, propertyTypeId, value.traits, propertyImportedTypeNameId);
@@ -1758,13 +2374,18 @@ private:
const Storage::Synchronization::PropertyDeclaration &value,
SourceId sourceId)
{
- auto last = updatedAliasPropertyDeclarations.emplace_back(view.typeId,
- view.id,
- fetchImportedTypeNameId(value.typeName,
- sourceId),
- value.aliasPropertyName,
- value.aliasPropertyNameTail,
- view.aliasId);
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"update property declaration to alias"_t,
+ projectStorageCategory(),
+ keyValue("property declaration", value),
+ keyValue("property declaration view", view)};
+
+ updatedAliasPropertyDeclarations.emplace_back(view.typeId,
+ view.id,
+ fetchImportedTypeNameId(value.typeName, sourceId),
+ value.aliasPropertyName,
+ value.aliasPropertyNameTail,
+ view.aliasId);
}
auto synchronizePropertyDeclarationsUpdateProperty(
@@ -1773,12 +2394,18 @@ private:
SourceId sourceId,
PropertyDeclarationIds &propertyDeclarationIds)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"update property declaration"_t,
+ projectStorageCategory(),
+ keyValue("property declaration", value),
+ keyValue("property declaration view", view)};
+
auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId);
auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId);
if (!propertyTypeId)
- throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId)};
+ throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId), sourceId};
if (view.traits == value.traits && propertyTypeId == view.typeId
&& propertyImportedTypeNameId == view.typeNameId)
@@ -1792,6 +2419,9 @@ private:
propertyTypeId,
value.traits);
propertyDeclarationIds.push_back(view.id);
+
+ tracer.end(keyValue("updated", "yes"));
+
return Sqlite::UpdateChange::Update;
}
@@ -1849,6 +2479,11 @@ private:
};
auto remove = [&](const Storage::Synchronization::PropertyDeclarationView &view) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"remove property declaration"_t,
+ projectStorageCategory(),
+ keyValue("property declaratio viewn", view)};
+
auto nextPropertyDeclarationId = selectPropertyDeclarationIdPrototypeChainDownStatement
.template value<PropertyDeclarationId>(typeId,
view.name);
@@ -1865,31 +2500,47 @@ private:
Sqlite::insertUpdateDelete(range, propertyDeclarations, compareKey, insert, update, remove);
}
+ class AliasPropertyDeclarationView
+ {
+ public:
+ explicit AliasPropertyDeclarationView(Utils::SmallStringView name,
+ PropertyDeclarationId id,
+ PropertyDeclarationId aliasId)
+ : name{name}
+ , id{id}
+ , aliasId{aliasId}
+ {}
+
+ template<typename String>
+ friend void convertToString(String &string,
+ const AliasPropertyDeclarationView &aliasPropertyDeclarationView)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", aliasPropertyDeclarationView.name),
+ keyValue("id", aliasPropertyDeclarationView.id),
+ keyValue("alias id", aliasPropertyDeclarationView.aliasId));
+
+ convertToString(string, dict);
+ }
+
+ public:
+ Utils::SmallStringView name;
+ PropertyDeclarationId id;
+ PropertyDeclarationId aliasId;
+ };
+
void resetRemovedAliasPropertyDeclarationsToNull(Storage::Synchronization::Type &type,
PropertyDeclarationIds &propertyDeclarationIds)
{
+ NanotraceHR::Tracer tracer{"reset removed alias property declaration to null"_t,
+ projectStorageCategory()};
+
if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal)
return;
Storage::Synchronization::PropertyDeclarations &aliasDeclarations = type.propertyDeclarations;
- class AliasPropertyDeclarationView
- {
- public:
- explicit AliasPropertyDeclarationView(Utils::SmallStringView name,
- PropertyDeclarationId id,
- PropertyDeclarationId aliasId)
- : name{name}
- , id{id}
- , aliasId{aliasId}
- {}
-
- public:
- Utils::SmallStringView name;
- PropertyDeclarationId id;
- PropertyDeclarationId aliasId;
- };
-
std::sort(aliasDeclarations.begin(), aliasDeclarations.end(), [](auto &&first, auto &&second) {
return Sqlite::compare(first.name, second.name) < 0;
});
@@ -1910,6 +2561,11 @@ private:
};
auto remove = [&](const AliasPropertyDeclarationView &view) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"reset removed alias property declaration to null"_t,
+ projectStorageCategory(),
+ keyValue("alias property declaration view", view)};
+
updatePropertyDeclarationAliasIdToNullStatement.write(view.id);
propertyDeclarationIds.push_back(view.id);
};
@@ -1995,6 +2651,14 @@ private:
};
auto insert = [&](const Storage::Import &import) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert import"_t,
+ projectStorageCategory(),
+ keyValue("import", import),
+ keyValue("import kind", importKind),
+ keyValue("source id", import.sourceId),
+ keyValue("module id", import.moduleId)};
+
auto importId = insertDocumentImport(import, importKind, import.moduleId, ImportId{});
auto callback = [&](ModuleId exportedModuleId, int majorVersion, int minorVersion) {
Storage::Import additionImport{exportedModuleId,
@@ -2005,13 +2669,26 @@ private:
? Storage::Synchronization::ImportKind::ModuleExportedImport
: Storage::Synchronization::ImportKind::ModuleExportedModuleDependency;
- insertDocumentImport(additionImport, exportedImportKind, import.moduleId, importId);
+ NanotraceHR::Tracer tracer{"insert indirect import"_t,
+ projectStorageCategory(),
+ keyValue("import", import),
+ keyValue("import kind", exportedImportKind),
+ keyValue("source id", import.sourceId),
+ keyValue("module id", import.moduleId)};
+
+ auto indirectImportId = insertDocumentImport(additionImport,
+ exportedImportKind,
+ import.moduleId,
+ importId);
+
+ tracer.end(keyValue("import id", indirectImportId));
};
selectModuleExportedImportsForModuleIdStatement.readCallback(callback,
import.moduleId,
import.version.major.value,
import.version.minor.value);
+ tracer.end(keyValue("import id", importId));
};
auto update = [](const Storage::Synchronization::ImportView &, const Storage::Import &) {
@@ -2019,6 +2696,14 @@ private:
};
auto remove = [&](const Storage::Synchronization::ImportView &view) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"remove import"_t,
+ projectStorageCategory(),
+ keyValue("import", view),
+ keyValue("import id", view.importId),
+ keyValue("source id", view.sourceId),
+ keyValue("module id", view.moduleId)};
+
deleteDocumentImportStatement.write(view.importId);
deleteDocumentImportsWithParentImportIdStatement.write(view.sourceId, view.importId);
};
@@ -2028,6 +2713,9 @@ private:
static Utils::PathString createJson(const Storage::Synchronization::ParameterDeclarations &parameters)
{
+ NanotraceHR::Tracer tracer{"create json from parameter declarations"_t,
+ projectStorageCategory()};
+
Utils::PathString json;
json.append("[");
@@ -2057,11 +2745,20 @@ private:
TypeId fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId,
Utils::SmallStringView name) const override
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch type id by module id and exported name"_t,
+ projectStorageCategory(),
+ keyValue("module id", moduleId),
+ keyValue("exported name", name)};
+
return selectTypeIdByModuleIdAndExportedNameStatement.template value<TypeId>(moduleId, name);
}
void addTypeIdToPropertyEditorQmlPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths)
{
+ NanotraceHR::Tracer tracer{"add type id to property editor qml paths"_t,
+ projectStorageCategory()};
+
for (auto &path : paths)
path.typeId = fetchTypeIdByModuleIdAndExportedName(path.moduleId, path.typeName);
}
@@ -2075,6 +2772,19 @@ private:
, directoryId{directoryId}
{}
+ template<typename String>
+ friend void convertToString(String &string,
+ const PropertyEditorQmlPathView &propertyEditorQmlPathView)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("type id", propertyEditorQmlPathView.typeId),
+ keyValue("source id", propertyEditorQmlPathView.pathId),
+ keyValue("directory id", propertyEditorQmlPathView.directoryId));
+
+ convertToString(string, dict);
+ }
+
public:
TypeId typeId;
SourceId pathId;
@@ -2099,19 +2809,38 @@ private:
};
auto insert = [&](const PropertyEditorQmlPath &path) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert property editor paths"_t,
+ projectStorageCategory(),
+ keyValue("property editor qml path", path)};
+
if (path.typeId)
insertPropertyEditorPathStatement.write(path.typeId, path.pathId, path.directoryId);
};
auto update = [&](const PropertyEditorQmlPathView &view, const PropertyEditorQmlPath &value) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"update property editor paths"_t,
+ projectStorageCategory(),
+ keyValue("property editor qml path", value),
+ keyValue("property editor qml path view", view)};
+
if (value.pathId != view.pathId || value.directoryId != view.directoryId) {
updatePropertyEditorPathsStatement.write(value.typeId, value.pathId, value.directoryId);
+
+ tracer.end(keyValue("updated", "yes"));
+
return Sqlite::UpdateChange::Update;
}
return Sqlite::UpdateChange::No;
};
auto remove = [&](const PropertyEditorQmlPathView &view) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"remove property editor paths"_t,
+ projectStorageCategory(),
+ keyValue("property editor qml path view", view)};
+
deletePropertyEditorPathStatement.write(view.typeId);
};
@@ -2163,6 +2892,11 @@ private:
};
auto insert = [&](const Storage::Synchronization::FunctionDeclaration &value) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert function declaration"_t,
+ projectStorageCategory(),
+ keyValue("function declaration", value)};
+
Utils::PathString signature{createJson(value.parameters)};
insertFunctionDeclarationStatement.write(typeId, value.name, value.returnTypeName, signature);
@@ -2170,6 +2904,12 @@ private:
auto update = [&](const Storage::Synchronization::FunctionDeclarationView &view,
const Storage::Synchronization::FunctionDeclaration &value) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"update function declaration"_t,
+ projectStorageCategory(),
+ keyValue("function declaration", value),
+ keyValue("function declaration view", view)};
+
Utils::PathString signature{createJson(value.parameters)};
if (value.returnTypeName == view.returnTypeName)
@@ -2177,10 +2917,17 @@ private:
updateFunctionDeclarationStatement.write(view.id, value.returnTypeName);
+ tracer.end(keyValue("updated", "yes"));
+
return Sqlite::UpdateChange::Update;
};
auto remove = [&](const Storage::Synchronization::FunctionDeclarationView &view) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"remove function declaration"_t,
+ projectStorageCategory(),
+ keyValue("function declaration view", view)};
+
deleteFunctionDeclarationStatement.write(view.id);
};
@@ -2220,6 +2967,11 @@ private:
};
auto insert = [&](const Storage::Synchronization::SignalDeclaration &value) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert signal declaration"_t,
+ projectStorageCategory(),
+ keyValue("signal declaration", value)};
+
Utils::PathString signature{createJson(value.parameters)};
insertSignalDeclarationStatement.write(typeId, value.name, signature);
@@ -2231,6 +2983,11 @@ private:
};
auto remove = [&](const Storage::Synchronization::SignalDeclarationView &view) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"remove signal declaration"_t,
+ projectStorageCategory(),
+ keyValue("signal declaration view", view)};
+
deleteSignalDeclarationStatement.write(view.id);
};
@@ -2240,6 +2997,10 @@ private:
static Utils::PathString createJson(
const Storage::Synchronization::EnumeratorDeclarations &enumeratorDeclarations)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"create json from enumerator declarations"_t,
+ projectStorageCategory()};
+
Utils::PathString json;
json.append("{");
@@ -2266,7 +3027,7 @@ private:
void synchronizeEnumerationDeclarations(
TypeId typeId, Storage::Synchronization::EnumerationDeclarations &enumerationDeclarations)
{
- NanotraceHR::Tracer tracer{"synchronize enumeation declaration"_t, projectStorageCategory()};
+ NanotraceHR::Tracer tracer{"synchronize enumeration declaration"_t, projectStorageCategory()};
std::sort(enumerationDeclarations.begin(),
enumerationDeclarations.end(),
@@ -2283,6 +3044,11 @@ private:
};
auto insert = [&](const Storage::Synchronization::EnumerationDeclaration &value) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert enumeration declaration"_t,
+ projectStorageCategory(),
+ keyValue("enumeration declaration", value)};
+
Utils::PathString signature{createJson(value.enumeratorDeclarations)};
insertEnumerationDeclarationStatement.write(typeId, value.name, signature);
@@ -2290,6 +3056,12 @@ private:
auto update = [&](const Storage::Synchronization::EnumerationDeclarationView &view,
const Storage::Synchronization::EnumerationDeclaration &value) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"update enumeration declaration"_t,
+ projectStorageCategory(),
+ keyValue("enumeration declaration", value),
+ keyValue("enumeration declaration view", view)};
+
Utils::PathString enumeratorDeclarations{createJson(value.enumeratorDeclarations)};
if (enumeratorDeclarations == view.enumeratorDeclarations)
@@ -2297,10 +3069,17 @@ private:
updateEnumerationDeclarationStatement.write(view.id, enumeratorDeclarations);
+ tracer.end(keyValue("updated", "yes"));
+
return Sqlite::UpdateChange::Update;
};
auto remove = [&](const Storage::Synchronization::EnumerationDeclarationView &view) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"remove enumeration declaration"_t,
+ projectStorageCategory(),
+ keyValue("enumeration declaration view", view)};
+
deleteEnumerationDeclarationStatement.write(view.id);
};
@@ -2320,9 +3099,17 @@ private:
TypeId declareType(Storage::Synchronization::Type &type)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"declare type"_t,
+ projectStorageCategory(),
+ keyValue("source id", type.sourceId),
+ keyValue("type name", type.typeName)};
+
if (type.typeName.isEmpty()) {
type.typeId = selectTypeIdBySourceIdStatement.template value<TypeId>(type.sourceId);
+ tracer.end(keyValue("type id", type.typeId));
+
return type.typeId;
}
@@ -2332,6 +3119,8 @@ private:
type.typeId = selectTypeIdBySourceIdAndNameStatement.template value<TypeId>(type.sourceId,
type.typeName);
+ tracer.end(keyValue("type id", type.typeId));
+
return type.typeId;
}
@@ -2407,6 +3196,17 @@ private:
, defaultPropertyId{defaultPropertyId}
{}
+ template<typename String>
+ friend void convertToString(String &string, const TypeWithDefaultPropertyView &view)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("type id", view.typeId),
+ keyValue("property id", view.defaultPropertyId));
+
+ convertToString(string, dict);
+ }
+
TypeId typeId;
PropertyDeclarationId defaultPropertyId;
};
@@ -2428,6 +3228,12 @@ private:
auto update = [&](const TypeWithDefaultPropertyView &view,
const Storage::Synchronization::Type &value) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"reset default properties by update"_t,
+ projectStorageCategory(),
+ keyValue("value", value),
+ keyValue("view", view)};
+
PropertyDeclarationId valueDefaultPropertyId;
if (value.defaultPropertyName.size())
valueDefaultPropertyId = fetchPropertyDeclarationByTypeIdAndNameUngarded(
@@ -2439,6 +3245,8 @@ private:
updateDefaultPropertyIdStatement.write(value.typeId, valueDefaultPropertyId);
+ tracer.end(keyValue("updated", "yes"));
+
return Sqlite::UpdateChange::Update;
};
@@ -2464,6 +3272,12 @@ private:
auto update = [&](const TypeWithDefaultPropertyView &view,
const Storage::Synchronization::Type &value) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"reset changed default properties by update"_t,
+ projectStorageCategory(),
+ keyValue("value", value),
+ keyValue("view", view)};
+
PropertyDeclarationId valueDefaultPropertyId;
if (value.defaultPropertyName.size()) {
auto optionalValueDefaultPropertyId = fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(
@@ -2477,6 +3291,8 @@ private:
updateDefaultPropertyIdStatement.write(value.typeId, Sqlite::NullValue{});
+ tracer.end(keyValue("updated", "yes"));
+
return Sqlite::UpdateChange::Update;
};
@@ -2487,6 +3303,11 @@ private:
void checkForPrototypeChainCycle(TypeId typeId) const
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"check for prototype chain cycle"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
auto callback = [=](TypeId currentTypeId) {
if (typeId == currentTypeId)
throw PrototypeChainCycle{};
@@ -2497,6 +3318,10 @@ private:
void checkForAliasChainCycle(PropertyDeclarationId propertyDeclarationId) const
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"check for alias chain cycle"_t,
+ projectStorageCategory(),
+ keyValue("property declaration id", propertyDeclarationId)};
auto callback = [=](PropertyDeclarationId currentPropertyDeclarationId) {
if (propertyDeclarationId == currentPropertyDeclarationId)
throw AliasChainCycle{};
@@ -2509,6 +3334,12 @@ private:
std::pair<TypeId, ImportedTypeNameId> fetchImportedTypeNameIdAndTypeId(
const Storage::Synchronization::ImportedTypeName &typeName, SourceId sourceId)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch imported type name id and type id"_t,
+ projectStorageCategory(),
+ keyValue("imported type name", typeName),
+ keyValue("source id", sourceId)};
+
TypeId typeId;
ImportedTypeNameId typeNameId;
if (!std::visit([](auto &&typeName_) -> bool { return typeName_.name.isEmpty(); }, typeName)) {
@@ -2516,8 +3347,10 @@ private:
typeId = fetchTypeId(typeNameId);
+ tracer.end(keyValue("type id", typeId), keyValue("type name id", typeNameId));
+
if (!typeId)
- throw TypeNameDoesNotExists{fetchImportedTypeName(typeNameId)};
+ throw TypeNameDoesNotExists{fetchImportedTypeName(typeNameId), sourceId};
}
return {typeId, typeNameId};
@@ -2528,6 +3361,14 @@ private:
if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal)
return;
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"synchronize prototype and extension"_t,
+ projectStorageCategory(),
+ keyValue("prototype", type.prototype),
+ keyValue("extension", type.extension),
+ keyValue("type id", type.typeId),
+ keyValue("source id", type.sourceId)};
+
auto [prototypeId, prototypeTypeNameId] = fetchImportedTypeNameIdAndTypeId(type.prototype,
type.sourceId);
auto [extensionId, extensionTypeNameId] = fetchImportedTypeNameIdAndTypeId(type.extension,
@@ -2543,13 +3384,19 @@ private:
checkForPrototypeChainCycle(type.typeId);
typeIds.push_back(type.typeId);
+
+ tracer.end(keyValue("prototype id", prototypeId),
+ keyValue("prototype type name id", prototypeTypeNameId),
+ keyValue("extension id", extensionId),
+ keyValue("extension type name id", extensionTypeNameId));
}
void syncPrototypesAndExtensions(Storage::Synchronization::Types &types,
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions)
{
- NanotraceHR::Tracer tracer{"synchronize prototypes"_t, projectStorageCategory()};
+ NanotraceHR::Tracer tracer{"synchronize prototypes and extensions"_t,
+ projectStorageCategory()};
TypeIds typeIds;
typeIds.reserve(types.size());
@@ -2563,18 +3410,29 @@ private:
ImportId fetchImportId(SourceId sourceId, const Storage::Import &import) const
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch imported type name id"_t,
+ projectStorageCategory(),
+ keyValue("import", import),
+ keyValue("source id", sourceId)};
+
+ ImportId importId;
if (import.version) {
- return selectImportIdBySourceIdAndModuleIdAndVersionStatement.template value<ImportId>(
+ importId = selectImportIdBySourceIdAndModuleIdAndVersionStatement.template value<ImportId>(
sourceId, import.moduleId, import.version.major.value, import.version.minor.value);
+ } else if (import.version.major) {
+ importId = selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement
+ .template value<ImportId>(sourceId,
+ import.moduleId,
+ import.version.major.value);
+ } else {
+ importId = selectImportIdBySourceIdAndModuleIdStatement
+ .template value<ImportId>(sourceId, import.moduleId);
}
- if (import.version.major) {
- return selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement
- .template value<ImportId>(sourceId, import.moduleId, import.version.major.value);
- }
+ tracer.end(keyValue("import id", importId));
- return selectImportIdBySourceIdAndModuleIdStatement.template value<ImportId>(sourceId,
- import.moduleId);
+ return importId;
}
ImportedTypeNameId fetchImportedTypeNameId(const Storage::Synchronization::ImportedTypeName &name,
@@ -2584,6 +3442,13 @@ private:
{
auto operator()(const Storage::Synchronization::ImportedType &importedType)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch imported type name id"_t,
+ projectStorageCategory(),
+ keyValue("imported type name", importedType.name),
+ keyValue("source id", sourceId),
+ keyValue("type name kind", "exported"sv)};
+
return storage.fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported,
sourceId,
importedType.name);
@@ -2591,11 +3456,23 @@ private:
auto operator()(const Storage::Synchronization::QualifiedImportedType &importedType)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch imported type name id"_t,
+ projectStorageCategory(),
+ keyValue("imported type name", importedType.name),
+ keyValue("import", importedType.import),
+ keyValue("type name kind", "qualified exported"sv)};
+
ImportId importId = storage.fetchImportId(sourceId, importedType.import);
- return storage.fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::QualifiedExported,
- importId,
- importedType.name);
+ auto importedTypeNameId = storage.fetchImportedTypeNameId(
+ Storage::Synchronization::TypeNameKind::QualifiedExported,
+ importId,
+ importedType.name);
+
+ tracer.end(keyValue("import id", importId), keyValue("source id", sourceId));
+
+ return importedTypeNameId;
}
ProjectStorage &storage;
@@ -2610,21 +3487,39 @@ private:
Id id,
Utils::SmallStringView typeName)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch imported type name id"_t,
+ projectStorageCategory(),
+ keyValue("imported type name", typeName),
+ keyValue("kind", kind)};
+
auto importedTypeNameId = selectImportedTypeNameIdStatement
.template value<ImportedTypeNameId>(kind, id, typeName);
- if (importedTypeNameId)
- return importedTypeNameId;
+ if (!importedTypeNameId)
+ importedTypeNameId = insertImportedTypeNameIdStatement
+ .template value<ImportedTypeNameId>(kind, id, typeName);
+
+ tracer.end(keyValue("imported type name id", importedTypeNameId));
- return insertImportedTypeNameIdStatement.template value<ImportedTypeNameId>(kind, id, typeName);
+ return importedTypeNameId;
}
TypeId fetchTypeId(ImportedTypeNameId typeNameId) const
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch type id with type name kind"_t,
+ projectStorageCategory(),
+ keyValue("type name id", typeNameId)};
+
auto kind = selectKindFromImportedTypeNamesStatement
.template value<Storage::Synchronization::TypeNameKind>(typeNameId);
- return fetchTypeId(typeNameId, kind);
+ auto typeId = fetchTypeId(typeNameId, kind);
+
+ tracer.end(keyValue("type id", typeId), keyValue("type name kind", kind));
+
+ return typeId;
}
Utils::SmallString fetchImportedTypeName(ImportedTypeNameId typeNameId) const
@@ -2634,12 +3529,23 @@ private:
TypeId fetchTypeId(ImportedTypeNameId typeNameId, Storage::Synchronization::TypeNameKind kind) const
{
- if (kind == Storage::Synchronization::TypeNameKind::QualifiedExported) {
- return selectTypeIdForQualifiedImportedTypeNameNamesStatement.template value<TypeId>(
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch type id"_t,
+ projectStorageCategory(),
+ keyValue("type name id", typeNameId),
+ keyValue("type name kind", kind)};
+
+ TypeId typeId;
+ if (kind == Storage::Synchronization::TypeNameKind::Exported) {
+ typeId = selectTypeIdForImportedTypeNameNamesStatement.template value<TypeId>(typeNameId);
+ } else {
+ typeId = selectTypeIdForQualifiedImportedTypeNameNamesStatement.template value<TypeId>(
typeNameId);
}
- return selectTypeIdForImportedTypeNameNamesStatement.template value<TypeId>(typeNameId);
+ tracer.end(keyValue("type id", typeId));
+
+ return typeId;
}
class FetchPropertyDeclarationResult
@@ -2653,6 +3559,18 @@ private:
, propertyTraits{propertyTraits}
{}
+ template<typename String>
+ friend void convertToString(String &string, const FetchPropertyDeclarationResult &result)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("property type id", result.propertyTypeId),
+ keyValue("property declaration id", result.propertyDeclarationId),
+ keyValue("property traits", result.propertyTraits));
+
+ convertToString(string, dict);
+ }
+
public:
TypeId propertyTypeId;
PropertyDeclarationId propertyDeclarationId;
@@ -2662,15 +3580,33 @@ private:
auto fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(TypeId typeId,
Utils::SmallStringView name)
{
- return selectPropertyDeclarationByTypeIdAndNameStatement
- .template optionalValue<FetchPropertyDeclarationResult>(typeId, name);
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch optional property declaration by type id and name ungarded"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId),
+ keyValue("property name", name)};
+
+ auto propertyDeclaration = selectPropertyDeclarationByTypeIdAndNameStatement
+ .template optionalValue<FetchPropertyDeclarationResult>(typeId,
+ name);
+
+ tracer.end(keyValue("property declaration", propertyDeclaration));
+
+ return propertyDeclaration;
}
FetchPropertyDeclarationResult fetchPropertyDeclarationByTypeIdAndNameUngarded(
TypeId typeId, Utils::SmallStringView name)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch property declaration by type id and name ungarded"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId),
+ keyValue("property name", name)};
+
auto propertyDeclaration = fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(typeId,
name);
+ tracer.end(keyValue("property declaration", propertyDeclaration));
if (propertyDeclaration)
return *propertyDeclaration;
@@ -2681,9 +3617,17 @@ private:
PropertyDeclarationId fetchPropertyDeclarationIdByTypeIdAndNameUngarded(TypeId typeId,
Utils::SmallStringView name)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch property declaration id by type id and name ungarded"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId),
+ keyValue("property name", name)};
+
auto propertyDeclarationId = selectPropertyDeclarationIdByTypeIdAndNameStatement
.template value<PropertyDeclarationId>(typeId, name);
+ tracer.end(keyValue("property declaration id", propertyDeclarationId));
+
if (propertyDeclarationId)
return propertyDeclarationId;
@@ -2692,44 +3636,106 @@ private:
SourceContextId readSourceContextId(Utils::SmallStringView sourceContextPath)
{
- return selectSourceContextIdFromSourceContextsBySourceContextPathStatement
- .template value<SourceContextId>(sourceContextPath);
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"read source context id"_t,
+ projectStorageCategory(),
+ keyValue("source context path", sourceContextPath)};
+
+ auto sourceContextId = selectSourceContextIdFromSourceContextsBySourceContextPathStatement
+ .template value<SourceContextId>(sourceContextPath);
+
+ tracer.end(keyValue("source context id", sourceContextId));
+
+ return sourceContextId;
}
SourceContextId writeSourceContextId(Utils::SmallStringView sourceContextPath)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"write source context id"_t,
+ projectStorageCategory(),
+ keyValue("source context path", sourceContextPath)};
+
insertIntoSourceContextsStatement.write(sourceContextPath);
- return SourceContextId::create(database.lastInsertedRowId());
+ auto sourceContextId = SourceContextId::create(database.lastInsertedRowId());
+
+ tracer.end(keyValue("source context id", sourceContextId));
+
+ return sourceContextId;
}
SourceId writeSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"write source id"_t,
+ projectStorageCategory(),
+ keyValue("source context id", sourceContextId),
+ keyValue("source name", sourceName)};
+
insertIntoSourcesStatement.write(sourceContextId, sourceName);
- return SourceId::create(database.lastInsertedRowId());
+ auto sourceId = SourceId::create(database.lastInsertedRowId());
+
+ tracer.end(keyValue("source id", sourceId));
+
+ return sourceId;
}
SourceId readSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName)
{
- return selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement
- .template value<SourceId>(sourceContextId, sourceName);
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"read source id"_t,
+ projectStorageCategory(),
+ keyValue("source context id", sourceContextId),
+ keyValue("source name", sourceName)};
+
+ auto sourceId = selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement
+ .template value<SourceId>(sourceContextId, sourceName);
+
+ tracer.end(keyValue("source id", sourceId));
+
+ return sourceId;
}
auto fetchExportedTypes(TypeId typeId)
{
- return selectExportedTypesByTypeIdStatement
- .template values<Storage::Synchronization::ExportedType, 12>(typeId);
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch exported type"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto typeIds = selectExportedTypesByTypeIdStatement
+ .template values<Storage::Synchronization::ExportedType, 12>(typeId);
+
+ tracer.end(keyValue("type ids", typeIds));
+
+ return typeIds;
}
auto fetchPropertyDeclarations(TypeId typeId)
{
- return selectPropertyDeclarationsByTypeIdStatement
- .template values<Storage::Synchronization::PropertyDeclaration, 24>(typeId);
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch property declarations"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto propertyDeclarations = selectPropertyDeclarationsByTypeIdStatement
+ .template values<Storage::Synchronization::PropertyDeclaration, 24>(
+ typeId);
+
+ tracer.end(keyValue("property declarations", propertyDeclarations));
+
+ return propertyDeclarations;
}
auto fetchFunctionDeclarations(TypeId typeId)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch signal declarations"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
Storage::Synchronization::FunctionDeclarations functionDeclarations;
auto callback = [&](Utils::SmallStringView name,
@@ -2743,11 +3749,18 @@ private:
selectFunctionDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId);
+ tracer.end(keyValue("function declarations", functionDeclarations));
+
return functionDeclarations;
}
auto fetchSignalDeclarations(TypeId typeId)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch signal declarations"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
Storage::Synchronization::SignalDeclarations signalDeclarations;
auto callback = [&](Utils::SmallStringView name, SignalDeclarationId signalDeclarationId) {
@@ -2759,11 +3772,18 @@ private:
selectSignalDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId);
+ tracer.end(keyValue("signal declarations", signalDeclarations));
+
return signalDeclarations;
}
auto fetchEnumerationDeclarations(TypeId typeId)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch enumeration declarations"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
Storage::Synchronization::EnumerationDeclarations enumerationDeclarations;
auto callback = [&](Utils::SmallStringView name,
@@ -2778,6 +3798,8 @@ private:
selectEnumerationDeclarationsForTypeIdWithoutEnumeratorDeclarationsStatement
.readCallback(callback, typeId);
+ tracer.end(keyValue("enumeration declarations", enumerationDeclarations));
+
return enumerationDeclarations;
}
@@ -3662,12 +4684,19 @@ public:
"etn.minorVersion DESC NULLS FIRST LIMIT 1",
database};
mutable ReadStatement<1, 1> selectTypeIdForImportedTypeNameNamesStatement{
- "SELECT typeId FROM importedTypeNames AS itn JOIN documentImports AS di ON "
- "importOrSourceId=sourceId JOIN exportedTypeNames AS etn USING(moduleId) WHERE "
- "itn.kind=1 AND importedTypeNameId=?1 AND itn.name=etn.name AND "
- "(di.majorVersion IS NULL OR (di.majorVersion=etn.majorVersion AND (di.minorVersion IS "
- "NULL OR di.minorVersion>=etn.minorVersion))) ORDER BY di.kind, etn.majorVersion DESC "
- "NULLS FIRST, etn.minorVersion DESC NULLS FIRST LIMIT 1",
+ "WITH "
+ " importTypeNames(moduleId, name, kind, majorVersion, minorVersion) AS MATERIALIZED ( "
+ " SELECT moduleId, name, di.kind, majorVersion, minorVersion "
+ " FROM importedTypeNames AS itn JOIN documentImports AS di ON "
+ " importOrSourceId=sourceId "
+ " WHERE "
+ " importedTypeNameId=?1 AND itn.kind=1) "
+ "SELECT typeId FROM importTypeNames AS itn "
+ " JOIN exportedTypeNames AS etn USING(moduleId, name) "
+ "WHERE (itn.majorVersion IS NULL OR (itn.majorVersion=etn.majorVersion "
+ " AND (itn.minorVersion IS NULL OR itn.minorVersion>=etn.minorVersion))) "
+ "ORDER BY itn.kind, etn.majorVersion DESC NULLS FIRST, etn.minorVersion DESC NULLS FIRST "
+ "LIMIT 1",
database};
WriteStatement<0> deleteAllSourcesStatement{"DELETE FROM sources", database};
WriteStatement<0> deleteAllSourceContextsStatement{"DELETE FROM sourceContexts", database};
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp
index efe9bc58f5..a5dc60c4fa 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp
@@ -3,87 +3,186 @@
#include "projectstorageexceptions.h"
+#include <tracing/qmldesignertracing.h>
+
namespace QmlDesigner {
+using namespace NanotraceHR::Literals;
+using NanotraceHR::keyValue;
+
+namespace {
+auto &category()
+{
+ return ProjectStorageTracing::projectStorageCategory();
+}
+} // namespace
+
+NoSourcePathForInvalidSourceId::NoSourcePathForInvalidSourceId()
+{
+ category().threadEvent("NoSourcePathForInvalidSourceId"_t);
+}
+
const char *NoSourcePathForInvalidSourceId::what() const noexcept
{
return "You cannot get a file path for an invalid file path id!";
}
+NoSourceContextPathForInvalidSourceContextId::NoSourceContextPathForInvalidSourceContextId()
+{
+ category().threadEvent("NoSourceContextPathForInvalidSourceContextId"_t);
+}
+
const char *NoSourceContextPathForInvalidSourceContextId::what() const noexcept
{
return "You cannot get a directory path for an invalid directory path id!";
}
+SourceContextIdDoesNotExists::SourceContextIdDoesNotExists()
+{
+ category().threadEvent("SourceContextIdDoesNotExists"_t);
+}
+
const char *SourceContextIdDoesNotExists::what() const noexcept
{
return "The source context id does not exist in the database!";
}
+SourceIdDoesNotExists::SourceIdDoesNotExists()
+{
+ category().threadEvent("SourceIdDoesNotExists"_t);
+}
+
const char *SourceIdDoesNotExists::what() const noexcept
{
return "The source id does not exist in the database!";
}
+TypeHasInvalidSourceId::TypeHasInvalidSourceId()
+{
+ category().threadEvent("TypeHasInvalidSourceId"_t);
+}
+
const char *TypeHasInvalidSourceId::what() const noexcept
{
return "The source id is invalid!";
}
+ModuleDoesNotExists::ModuleDoesNotExists()
+{
+ category().threadEvent("ModuleDoesNotExists"_t);
+}
+
const char *ModuleDoesNotExists::what() const noexcept
{
return "The module does not exist!";
}
+ModuleAlreadyExists::ModuleAlreadyExists()
+{
+ category().threadEvent("ModuleAlreadyExists"_t);
+}
+
const char *ModuleAlreadyExists::what() const noexcept
{
return "The module does already exist!";
}
-TypeNameDoesNotExists::TypeNameDoesNotExists(std::string_view errorMessage)
- : ProjectStorageErrorWithMessage{"TypeNameDoesNotExists"sv, errorMessage}
-{}
+TypeNameDoesNotExists::TypeNameDoesNotExists(std::string_view typeName, SourceId sourceId)
+ : ProjectStorageErrorWithMessage{
+ "TypeNameDoesNotExists"sv,
+ Utils::SmallString::join(
+ {"type: ", typeName, ", source id: ", Utils::SmallString::number(sourceId.internalId())})}
+{
+ category().threadEvent("TypeNameDoesNotExists"_t,
+ keyValue("type name", typeName),
+ keyValue("source id", sourceId));
+}
+
+PropertyNameDoesNotExists::PropertyNameDoesNotExists()
+{
+ category().threadEvent("PropertyNameDoesNotExists"_t);
+}
const char *PropertyNameDoesNotExists::what() const noexcept
{
return "The property name does not exist!";
}
+PrototypeChainCycle::PrototypeChainCycle()
+{
+ category().threadEvent("PrototypeChainCycle"_t);
+}
+
const char *PrototypeChainCycle::what() const noexcept
{
return "There is a prototype chain cycle!";
}
+AliasChainCycle::AliasChainCycle()
+{
+ category().threadEvent("AliasChainCycle"_t);
+}
+
const char *AliasChainCycle::what() const noexcept
{
return "There is a prototype chain cycle!";
}
+CannotParseQmlTypesFile::CannotParseQmlTypesFile()
+{
+ category().threadEvent("CannotParseQmlTypesFile"_t);
+}
+
const char *CannotParseQmlTypesFile::what() const noexcept
{
return "Cannot parse qml types file!";
}
+CannotParseQmlDocumentFile::CannotParseQmlDocumentFile()
+{
+ category().threadEvent("CannotParseQmlDocumentFile"_t);
+}
+
const char *CannotParseQmlDocumentFile::what() const noexcept
{
return "Cannot parse qml types file!";
}
+ProjectDataHasInvalidProjectSourceId::ProjectDataHasInvalidProjectSourceId()
+{
+ category().threadEvent("ProjectDataHasInvalidProjectSourceId"_t);
+}
+
const char *ProjectDataHasInvalidProjectSourceId::what() const noexcept
{
return "The project source id is invalid!";
}
+ProjectDataHasInvalidSourceId::ProjectDataHasInvalidSourceId()
+{
+ category().threadEvent("ProjectDataHasInvalidSourceId"_t);
+}
+
const char *ProjectDataHasInvalidSourceId::what() const noexcept
{
return "The source id is invalid!";
}
+ProjectDataHasInvalidModuleId::ProjectDataHasInvalidModuleId()
+{
+ category().threadEvent("ProjectDataHasInvalidModuleId"_t);
+}
+
const char *ProjectDataHasInvalidModuleId::what() const noexcept
{
return "The module id is invalid!";
}
+FileStatusHasInvalidSourceId::FileStatusHasInvalidSourceId()
+{
+ category().threadEvent("FileStatusHasInvalidSourceId"_t);
+}
+
const char *FileStatusHasInvalidSourceId::what() const noexcept
{
return "The source id in file status is invalid!";
@@ -110,7 +209,14 @@ const char *ProjectStorageErrorWithMessage::what() const noexcept
ExportedTypeCannotBeInserted::ExportedTypeCannotBeInserted(std::string_view errorMessage)
: ProjectStorageErrorWithMessage{"ExportedTypeCannotBeInserted"sv, errorMessage}
-{}
+{
+ category().threadEvent("ExportedTypeCannotBeInserted"_t, keyValue("error message", errorMessage));
+}
+
+TypeAnnotationHasInvalidSourceId::TypeAnnotationHasInvalidSourceId()
+{
+ category().threadEvent("TypeAnnotationHasInvalidSourceId"_t);
+}
const char *TypeAnnotationHasInvalidSourceId::what() const noexcept
{
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h
index 412dd4a9ff..d85f1f7f9e 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h
@@ -5,6 +5,8 @@
#include "../include/qmldesignercorelib_global.h"
+#include "projectstorageids.h"
+
#include <exception>
namespace QmlDesigner {
@@ -13,15 +15,19 @@ using namespace std::literals::string_view_literals;
class QMLDESIGNERCORE_EXPORT ProjectStorageError : public std::exception
{
+protected:
+ ProjectStorageError() = default;
+
public:
const char *what() const noexcept override;
};
class ProjectStorageErrorWithMessage : public ProjectStorageError
{
-public:
+protected:
ProjectStorageErrorWithMessage(std::string_view error, std::string_view errorMessage);
+public:
const char *what() const noexcept override;
public:
@@ -31,42 +37,49 @@ public:
class QMLDESIGNERCORE_EXPORT NoSourcePathForInvalidSourceId : public ProjectStorageError
{
public:
+ NoSourcePathForInvalidSourceId();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT NoSourceContextPathForInvalidSourceContextId : public ProjectStorageError
{
public:
+ NoSourceContextPathForInvalidSourceContextId();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT SourceContextIdDoesNotExists : public ProjectStorageError
{
public:
+ SourceContextIdDoesNotExists();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT SourceIdDoesNotExists : public ProjectStorageError
{
public:
+ SourceIdDoesNotExists();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT TypeHasInvalidSourceId : public ProjectStorageError
{
public:
+ TypeHasInvalidSourceId();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT ModuleDoesNotExists : public ProjectStorageError
{
public:
+ ModuleDoesNotExists();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT ModuleAlreadyExists : public ProjectStorageError
{
public:
+ ModuleAlreadyExists();
const char *what() const noexcept override;
};
@@ -79,66 +92,76 @@ public:
class QMLDESIGNERCORE_EXPORT TypeNameDoesNotExists : public ProjectStorageErrorWithMessage
{
public:
- TypeNameDoesNotExists(std::string_view errorMessage);
+ TypeNameDoesNotExists(std::string_view typeName, SourceId sourceId = SourceId{});
};
class QMLDESIGNERCORE_EXPORT PropertyNameDoesNotExists : public ProjectStorageError
{
public:
+ PropertyNameDoesNotExists();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT PrototypeChainCycle : public ProjectStorageError
{
public:
+ PrototypeChainCycle();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT AliasChainCycle : public ProjectStorageError
{
public:
+ AliasChainCycle();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT CannotParseQmlTypesFile : public ProjectStorageError
{
public:
+ CannotParseQmlTypesFile();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT CannotParseQmlDocumentFile : public ProjectStorageError
{
public:
+ CannotParseQmlDocumentFile();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidProjectSourceId : public ProjectStorageError
{
public:
+ ProjectDataHasInvalidProjectSourceId();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidSourceId : public ProjectStorageError
{
public:
+ ProjectDataHasInvalidSourceId();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidModuleId : public ProjectStorageError
{
public:
+ ProjectDataHasInvalidModuleId();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT FileStatusHasInvalidSourceId : public ProjectStorageError
{
public:
+ FileStatusHasInvalidSourceId();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT TypeAnnotationHasInvalidSourceId : public ProjectStorageError
{
public:
+ TypeAnnotationHasInvalidSourceId();
const char *what() const noexcept override;
};
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h
index 427c0ff8d6..3be6fc4cae 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h
@@ -24,6 +24,20 @@ constexpr std::underlying_type_t<Enumeration> to_underlying(Enumeration enumerat
enum class FlagIs : unsigned int { False, Set, True };
+template<typename String>
+void convertToString(String &string, const FlagIs &flagIs)
+{
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+
+ if (flagIs == FlagIs::False)
+ convertToString(string, false);
+ else if (flagIs == FlagIs::True)
+ convertToString(string, true);
+ else
+ convertToString(string, "is set");
+}
+
} // namespace QmlDesigner
namespace QmlDesigner::Storage {
@@ -46,6 +60,18 @@ constexpr bool operator&(PropertyDeclarationTraits first, PropertyDeclarationTra
return static_cast<int>(first) & static_cast<int>(second);
}
+template<typename String>
+void convertToString(String &string, const PropertyDeclarationTraits &traits)
+{
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("is read only", traits & PropertyDeclarationTraits::IsReadOnly),
+ keyValue("is pointer", traits & PropertyDeclarationTraits::IsPointer),
+ keyValue("is list", traits & PropertyDeclarationTraits::IsList));
+
+ convertToString(string, dict);
+}
+
enum class TypeTraitsKind : unsigned int {
None,
Reference,
@@ -53,6 +79,25 @@ enum class TypeTraitsKind : unsigned int {
Sequence,
};
+template<typename String>
+void convertToString(String &string, const TypeTraitsKind &kind)
+{
+ switch (kind) {
+ case TypeTraitsKind::None:
+ convertToString(string, "None");
+ break;
+ case TypeTraitsKind::Reference:
+ convertToString(string, "Reference");
+ break;
+ case TypeTraitsKind::Value:
+ convertToString(string, "Value");
+ break;
+ case TypeTraitsKind::Sequence:
+ convertToString(string, "Sequence");
+ break;
+ }
+}
+
struct TypeTraits
{
constexpr TypeTraits()
@@ -100,6 +145,35 @@ struct TypeTraits
return first.type == second.type && first.annotation == second.annotation;
}
+ template<typename String>
+ friend void convertToString(String &string, const TypeTraits &typeTraits)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(
+ keyValue("kind", typeTraits.kind),
+ keyValue("is enum", typeTraits.isEnum),
+ keyValue("is file component", typeTraits.isFileComponent),
+ keyValue("is project component", typeTraits.isProjectComponent),
+ keyValue("is in project module", typeTraits.isInProjectModule),
+ keyValue("uses custom parser", typeTraits.usesCustomParser),
+ keyValue("can be container", typeTraits.canBeContainer),
+ keyValue("force clip", typeTraits.forceClip),
+ keyValue("does layout children", typeTraits.doesLayoutChildren),
+ keyValue("can be dropped in form editor", typeTraits.canBeDroppedInFormEditor),
+ keyValue("can be dropped in navigator", typeTraits.canBeDroppedInNavigator),
+ keyValue("can be dropped in view 3D", typeTraits.canBeDroppedInView3D),
+ keyValue("is movable", typeTraits.isMovable),
+ keyValue("is resizable", typeTraits.isResizable),
+ keyValue("has form editor item", typeTraits.hasFormEditorItem),
+ keyValue("is stacked container", typeTraits.isStackedContainer),
+ keyValue("takes over rendering of children", typeTraits.takesOverRenderingOfChildren),
+ keyValue("visible in navigator", typeTraits.visibleInNavigator),
+ keyValue("visible in library", typeTraits.visibleInLibrary));
+
+ convertToString(string, dict);
+ }
+
union {
struct
{
@@ -202,10 +276,22 @@ public:
explicit operator bool() const { return major && minor; }
+ template<typename String>
+ friend void convertToString(String &string, const Version &version)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("major version", version.major.value),
+ keyValue("minor version", version.minor.value));
+
+ convertToString(string, dict);
+ }
+
public:
VersionNumber major;
VersionNumber minor;
};
+
} // namespace QmlDesigner::Storage
namespace QmlDesigner::Storage::Info {
@@ -217,6 +303,17 @@ struct TypeHint
, expression{expression}
{}
+ template<typename String>
+ friend void convertToString(String &string, const TypeHint &typeHint)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", typeHint.name),
+ keyValue("expression", typeHint.expression));
+
+ convertToString(string, dict);
+ }
+
Utils::SmallString name;
Utils::PathString expression;
};
@@ -231,6 +328,18 @@ struct ItemLibraryProperty
, value{value}
{}
+ template<typename String>
+ friend void convertToString(String &string, const ItemLibraryProperty &property)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", property.name),
+ keyValue("type", property.type),
+ keyValue("value", property.value));
+
+ convertToString(string, dict);
+ }
+
Utils::SmallString name;
Utils::SmallString type;
Sqlite::Value value;
@@ -274,6 +383,24 @@ struct ItemLibraryEntry
, properties{std::move(properties)}
{}
+ template<typename String>
+ friend void convertToString(String &string, const ItemLibraryEntry &entry)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("type id", entry.typeId),
+ keyValue("name", entry.name),
+ keyValue("icon path", entry.iconPath),
+ keyValue("category", entry.category),
+ keyValue("import", entry.import),
+ keyValue("tool tip", entry.toolTip),
+ keyValue("template path", entry.templatePath),
+ keyValue("properties", entry.properties),
+ keyValue("extra file paths", entry.extraFilePaths));
+
+ convertToString(string, dict);
+ }
+
TypeId typeId;
Utils::SmallString name;
Utils::PathString iconPath;
@@ -321,6 +448,18 @@ public:
< std::tie(second.moduleId, second.name, second.version);
}
+ template<typename String>
+ friend void convertToString(String &string, const ExportedTypeName &exportedTypeName)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", exportedTypeName.name),
+ keyValue("version", exportedTypeName.version),
+ keyValue("module id", exportedTypeName.moduleId));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallString name;
Storage::Version version;
@@ -342,6 +481,19 @@ public:
, propertyTypeId{propertyTypeId}
{}
+ template<typename String>
+ friend void convertToString(String &string, const PropertyDeclaration &propertyDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("type id", propertyDeclaration.typeId),
+ keyValue("name", propertyDeclaration.name),
+ keyValue("traits", propertyDeclaration.traits),
+ keyValue("property type id", propertyDeclaration.propertyTypeId));
+
+ convertToString(string, dict);
+ }
+
TypeId typeId;
::Utils::SmallString name;
PropertyDeclarationTraits traits;
@@ -366,6 +518,18 @@ public:
, traits{traits}
{}
+ template<typename String>
+ friend void convertToString(String &string, const Type &type)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("default property id", type.defaultPropertyId),
+ keyValue("source id", type.sourceId),
+ keyValue("traits", type.traits));
+
+ convertToString(string, dict);
+ }
+
PropertyDeclarationId defaultPropertyId;
SourceId sourceId;
TypeTraits traits;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h
index a9185d91e8..04f11096bd 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h
@@ -12,6 +12,28 @@ namespace QmlDesigner {
enum class SourceType : int { Qml, QmlUi, QmlTypes, QmlDir, Directory };
+template<typename String>
+void convertToString(String &string, SourceType sourceType)
+{
+ switch (sourceType) {
+ case SourceType::Qml:
+ convertToString(string, "Qml");
+ break;
+ case SourceType::QmlUi:
+ convertToString(string, "QmlUi");
+ break;
+ case SourceType::QmlTypes:
+ convertToString(string, "QmlTypes");
+ break;
+ case SourceType::QmlDir:
+ convertToString(string, "QmlDir");
+ break;
+ case SourceType::Directory:
+ convertToString(string, "Directory");
+ break;
+ }
+}
+
class ProjectChunkId
{
public:
@@ -46,6 +68,17 @@ public:
friend bool operator<(ProjectChunkId first, ProjectPartId second) { return first.id < second; }
friend bool operator<(ProjectPartId first, ProjectChunkId second) { return first < second.id; }
+
+ template<typename String>
+ friend void convertToString(String &string, const ProjectChunkId &id)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("project part id", id.id),
+ keyValue("source type", id.sourceType));
+
+ convertToString(string, dict);
+ }
};
using ProjectChunkIds = std::vector<ProjectChunkId>;
@@ -67,6 +100,16 @@ public:
return first.id == second.id && first.sourceIds == second.sourceIds;
}
+ template<typename String>
+ friend void convertToString(String &string, const IdPaths &idPaths)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("id", idPaths.id), keyValue("source ids", idPaths.sourceIds));
+
+ convertToString(string, dict);
+ }
+
public:
ProjectChunkId id;
SourceIds sourceIds;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h
index 18c3931249..5f80505da3 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h
@@ -7,6 +7,7 @@
#include "projectstorageids.h"
#include "projectstorageinfotypes.h"
+#include <nanotrace/nanotracehr.h>
#include <sqlite/sqlitevalue.h>
#include <utils/smallstring.h>
@@ -45,6 +46,17 @@ public:
< std::tie(second.sourceId, second.moduleId, second.version);
}
+ template<typename String>
+ friend void convertToString(String &string, const Import &import)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("module id", import.moduleId),
+ keyValue("source id", import.sourceId),
+ keyValue("version", import.version));
+ convertToString(string, dict);
+ }
+
public:
Storage::Version version;
ModuleId moduleId;
@@ -57,11 +69,50 @@ namespace Synchronization {
enum class TypeNameKind { Exported = 1, QualifiedExported = 2 };
+template<typename String>
+void convertToString(String &string, const TypeNameKind &kind)
+{
+ switch (kind) {
+ case TypeNameKind::Exported:
+ convertToString(string, "Exported");
+ break;
+ case TypeNameKind::QualifiedExported:
+ convertToString(string, "QualifiedExported");
+ break;
+ }
+}
+
enum class FileType : char { QmlTypes, QmlDocument };
+template<typename String>
+void convertToString(String &string, const FileType &type)
+{
+ switch (type) {
+ case FileType::QmlTypes:
+ convertToString(string, "QmlTypes");
+ break;
+ case FileType::QmlDocument:
+ convertToString(string, "QmlDocument");
+ break;
+ }
+}
+
enum class IsQualified : int { No, Yes };
-inline int operator-(IsQualified first, IsQualified second)
+template<typename String>
+void convertToString(String &string, const IsQualified &isQualified)
+{
+ switch (isQualified) {
+ case IsQualified::No:
+ convertToString(string, "No");
+ break;
+ case IsQualified::Yes:
+ convertToString(string, "Yes");
+ break;
+ }
+}
+
+inline int operator-(IsQualified first, const IsQualified &second)
{
return static_cast<int>(first) - static_cast<int>(second);
}
@@ -78,6 +129,25 @@ enum class ImportKind : char {
ModuleExportedModuleDependency
};
+template<typename String>
+void convertToString(String &string, const ImportKind &kind)
+{
+ switch (kind) {
+ case ImportKind::Import:
+ convertToString(string, "Import");
+ break;
+ case ImportKind::ModuleDependency:
+ convertToString(string, "ModuleDependency");
+ break;
+ case ImportKind::ModuleExportedImport:
+ convertToString(string, "ModuleExportedImport");
+ break;
+ case ImportKind::ModuleExportedModuleDependency:
+ convertToString(string, "ModuleExportedModuleDependency");
+ break;
+ }
+}
+
class ImportView
{
public:
@@ -97,6 +167,19 @@ public:
&& first.version == second.version;
}
+ template<typename String>
+ friend void convertToString(String &string, const ImportView &import)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("import id", import.importId),
+ keyValue("source id", import.sourceId),
+ keyValue("module id", import.moduleId),
+ keyValue("version", import.version));
+
+ convertToString(string, dict);
+ }
+
public:
ImportId importId;
SourceId sourceId;
@@ -106,6 +189,19 @@ public:
enum class IsAutoVersion : char { No, Yes };
+template<typename String>
+void convertToString(String &string, const IsAutoVersion &isAutoVersion)
+{
+ switch (isAutoVersion) {
+ case IsAutoVersion::No:
+ convertToString(string, "No");
+ break;
+ case IsAutoVersion::Yes:
+ convertToString(string, "Yes");
+ break;
+ }
+}
+
constexpr bool operator<(IsAutoVersion first, IsAutoVersion second)
{
return to_underlying(first) < to_underlying(second);
@@ -137,6 +233,19 @@ public:
< std::tie(second.moduleId, second.exportedModuleId, second.isAutoVersion, second.version);
}
+ template<typename String>
+ friend void convertToString(String &string, const ModuleExportedImport &import)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("module id", import.moduleId),
+ keyValue("exported module id", import.exportedModuleId),
+ keyValue("version", import.version),
+ keyValue("is auto version", import.isAutoVersion));
+
+ convertToString(string, dict);
+ }
+
public:
Storage::Version version;
ModuleId moduleId;
@@ -171,6 +280,20 @@ public:
&& first.version == second.version && first.isAutoVersion == second.isAutoVersion;
}
+ template<typename String>
+ friend void convertToString(String &string, const ModuleExportedImportView &import)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("module exported import id", import.moduleExportedImportId),
+ keyValue("module id", import.moduleId),
+ keyValue("exported module id", import.exportedModuleId),
+ keyValue("version", import.version),
+ keyValue("is auto version", import.isAutoVersion));
+
+ convertToString(string, dict);
+ }
+
public:
ModuleExportedImportId moduleExportedImportId;
Storage::Version version;
@@ -192,6 +315,16 @@ public:
return first.name == second.name;
}
+ template<typename String>
+ friend void convertToString(String &string, const ImportedType &importedType)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", importedType.name));
+
+ convertToString(string, dict);
+ }
+
public:
TypeNameString name;
};
@@ -210,6 +343,17 @@ public:
return first.name == second.name && first.import == second.import;
}
+ template<typename String>
+ friend void convertToString(String &string, const QualifiedImportedType &importedType)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", importedType.name),
+ keyValue("import", importedType.import));
+
+ convertToString(string, dict);
+ }
+
public:
TypeNameString name;
Import import;
@@ -264,6 +408,19 @@ public:
< std::tie(second.moduleId, second.name, second.version);
}
+ template<typename String>
+ friend void convertToString(String &string, const ExportedType &exportedType)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", exportedType.name),
+ keyValue("module id", exportedType.moduleId),
+ keyValue("type id", exportedType.typeId),
+ keyValue("version", exportedType.version));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallString name;
Storage::Version version;
@@ -295,6 +452,20 @@ public:
, exportedTypeNameId{exportedTypeNameId}
{}
+ template<typename String>
+ friend void convertToString(String &string, const ExportedTypeView &exportedType)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", exportedType.name),
+ keyValue("module id", exportedType.moduleId),
+ keyValue("type id", exportedType.typeId),
+ keyValue("version", exportedType.version),
+ keyValue("version", exportedType.exportedTypeNameId));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallStringView name;
Storage::Version version;
@@ -305,6 +476,43 @@ public:
using ImportedTypeName = std::variant<ImportedType, QualifiedImportedType>;
+template<typename String>
+void convertToString(String &string, const ImportedTypeName &typeName)
+{
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+
+ struct Dispatcher
+ {
+ static const QmlDesigner::Storage::Import &nullImport()
+ {
+ static QmlDesigner::Storage::Import import;
+
+ return import;
+ }
+
+ void operator()(const QmlDesigner::Storage::Synchronization::ImportedType &importedType) const
+ {
+ auto dict = dictonary(keyValue("name", importedType.name));
+
+ convertToString(string, dict);
+ }
+
+ void operator()(
+ const QmlDesigner::Storage::Synchronization::QualifiedImportedType &qualifiedImportedType) const
+ {
+ auto dict = dictonary(keyValue("name", qualifiedImportedType.name),
+ keyValue("import", qualifiedImportedType.import));
+
+ convertToString(string, dict);
+ }
+
+ String &string;
+ };
+
+ std::visit(Dispatcher{string}, typeName);
+}
+
class EnumeratorDeclaration
{
public:
@@ -325,6 +533,18 @@ public:
&& first.hasValue == second.hasValue;
}
+ template<typename String>
+ friend void convertToString(String &string, const EnumeratorDeclaration &enumeratorDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", enumeratorDeclaration.name),
+ keyValue("value", enumeratorDeclaration.value),
+ keyValue("has value", enumeratorDeclaration.hasValue));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallString name;
long long value = 0;
@@ -349,6 +569,18 @@ public:
&& first.enumeratorDeclarations == second.enumeratorDeclarations;
}
+ template<typename String>
+ friend void convertToString(String &string, const EnumerationDeclaration &enumerationDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", enumerationDeclaration.name),
+ keyValue("enumerator declarations",
+ enumerationDeclaration.enumeratorDeclarations));
+
+ convertToString(string, dict);
+ }
+
public:
TypeNameString name;
EnumeratorDeclarations enumeratorDeclarations;
@@ -368,6 +600,20 @@ public:
, id{id}
{}
+ template<typename String>
+ friend void convertToString(String &string,
+ const EnumerationDeclarationView &enumerationDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", enumerationDeclaration.name),
+ keyValue("enumerator declarations",
+ enumerationDeclaration.enumeratorDeclarations),
+ keyValue("id", enumerationDeclaration.id));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallStringView name;
::Utils::SmallStringView enumeratorDeclarations;
@@ -392,6 +638,18 @@ public:
&& first.traits == second.traits;
}
+ template<typename String>
+ friend void convertToString(String &string, const ParameterDeclaration &parameterDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", parameterDeclaration.name),
+ keyValue("type name", parameterDeclaration.typeName),
+ keyValue("traits", parameterDeclaration.traits));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallString name;
TypeNameString typeName;
@@ -418,6 +676,17 @@ public:
return first.name == second.name && first.parameters == second.parameters;
}
+ template<typename String>
+ friend void convertToString(String &string, const SignalDeclaration &signalDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", signalDeclaration.name),
+ keyValue("parameters", signalDeclaration.parameters));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallString name;
ParameterDeclarations parameters;
@@ -437,6 +706,18 @@ public:
, id{id}
{}
+ template<typename String>
+ friend void convertToString(String &string, const SignalDeclarationView &signalDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", signalDeclaration.name),
+ keyValue("signature", signalDeclaration.signature),
+ keyValue("id", signalDeclaration.id));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallStringView name;
::Utils::SmallStringView signature;
@@ -467,6 +748,18 @@ public:
&& first.parameters == second.parameters;
}
+ template<typename String>
+ friend void convertToString(String &string, const FunctionDeclaration &functionDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", functionDeclaration.name),
+ keyValue("return type name", functionDeclaration.returnTypeName),
+ keyValue("parameters", functionDeclaration.parameters));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallString name;
TypeNameString returnTypeName;
@@ -489,6 +782,19 @@ public:
, id{id}
{}
+ template<typename String>
+ friend void convertToString(String &string, const FunctionDeclarationView &functionDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", functionDeclaration.name),
+ keyValue("return type name", functionDeclaration.returnTypeName),
+ keyValue("signature", functionDeclaration.signature),
+ keyValue("id", functionDeclaration.id));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallStringView name;
::Utils::SmallStringView returnTypeName;
@@ -498,6 +804,19 @@ public:
enum class PropertyKind { Property, Alias };
+template<typename String>
+void convertToString(String &string, const PropertyKind &kind)
+{
+ switch (kind) {
+ case PropertyKind::Property:
+ convertToString(string, "Property");
+ break;
+ case PropertyKind::Alias:
+ convertToString(string, "Alias");
+ break;
+ }
+}
+
class PropertyDeclaration
{
public:
@@ -567,6 +886,24 @@ public:
&& first.traits == second.traits && first.kind == second.kind;
}
+ template<typename String>
+ friend void convertToString(String &string, const PropertyDeclaration &propertyDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", propertyDeclaration.name),
+ keyValue("type name", propertyDeclaration.typeName),
+ keyValue("alias property name", propertyDeclaration.aliasPropertyName),
+ keyValue("alias property name tail",
+ propertyDeclaration.aliasPropertyNameTail),
+ keyValue("traits", propertyDeclaration.traits),
+ keyValue("type id", propertyDeclaration.typeId),
+ keyValue("property type id", propertyDeclaration.propertyTypeId),
+ keyValue("kind", propertyDeclaration.kind));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallString name;
ImportedTypeName typeName;
@@ -597,6 +934,21 @@ public:
, aliasId{aliasId}
{}
+ template<typename String>
+ friend void convertToString(String &string, const PropertyDeclarationView &propertyDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", propertyDeclaration.name),
+ keyValue("traits", propertyDeclaration.traits),
+ keyValue("type id", propertyDeclaration.typeId),
+ keyValue("type name id", propertyDeclaration.typeNameId),
+ keyValue("id", propertyDeclaration.id),
+ keyValue("alias id", propertyDeclaration.aliasId));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallStringView name;
PropertyDeclarationTraits traits = {};
@@ -608,6 +960,22 @@ public:
enum class ChangeLevel : char { Full, Minimal, ExcludeExportedTypes };
+template<typename String>
+void convertToString(String &string, const ChangeLevel &changeLevel)
+{
+ switch (changeLevel) {
+ case ChangeLevel::Full:
+ convertToString(string, "Full");
+ break;
+ case ChangeLevel::Minimal:
+ convertToString(string, "Minimal");
+ break;
+ case ChangeLevel::ExcludeExportedTypes:
+ convertToString(string, "ExcludeExportedTypes");
+ break;
+ }
+}
+
class Type
{
public:
@@ -717,6 +1085,27 @@ public:
&& first.sourceId == second.sourceId;
}
+ template<typename String>
+ friend void convertToString(String &string, const Type &type)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("type name", type.typeName),
+ keyValue("prototype", type.prototype),
+ keyValue("extension", type.extension),
+ keyValue("exported types", type.exportedTypes),
+ keyValue("property declarations", type.propertyDeclarations),
+ keyValue("function declarations", type.functionDeclarations),
+ keyValue("signal declarations", type.signalDeclarations),
+ keyValue("enumeration declarations", type.enumerationDeclarations),
+ keyValue("traits", type.traits),
+ keyValue("source id", type.sourceId),
+ keyValue("change level", type.changeLevel),
+ keyValue("default property name", type.defaultPropertyName));
+
+ convertToString(string, dict);
+ }
+
public:
TypeNameString typeName;
::Utils::SmallString defaultPropertyName;
@@ -747,6 +1136,20 @@ public:
, moduleId{moduleId}
{}
+ template<typename String>
+ friend void convertToString(String &string, const PropertyEditorQmlPath &propertyEditorQmlPath)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("type name", propertyEditorQmlPath.typeName),
+ keyValue("type id", propertyEditorQmlPath.typeId),
+ keyValue("path id", propertyEditorQmlPath.pathId),
+ keyValue("directory id", propertyEditorQmlPath.directoryId),
+ keyValue("module id", propertyEditorQmlPath.moduleId));
+
+ convertToString(string, dict);
+ }
+
public:
TypeNameString typeName;
TypeId typeId;
@@ -774,6 +1177,19 @@ public:
&& first.fileType == second.fileType;
}
+ template<typename String>
+ friend void convertToString(String &string, const ProjectData &projectData)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("project source id", projectData.projectSourceId),
+ keyValue("source id", projectData.sourceId),
+ keyValue("module id", projectData.moduleId),
+ keyValue("file type", projectData.fileType));
+
+ convertToString(string, dict);
+ }
+
public:
SourceId projectSourceId;
SourceId sourceId;
@@ -805,6 +1221,23 @@ public:
, traits{traits}
{}
+ template<typename String>
+ friend void convertToString(String &string, const TypeAnnotation &typeAnnotation)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("type name", typeAnnotation.typeName),
+ keyValue("icon path", typeAnnotation.iconPath),
+ keyValue("item library json", typeAnnotation.itemLibraryJson),
+ keyValue("hints json", typeAnnotation.hintsJson),
+ keyValue("type id", typeAnnotation.typeId),
+ keyValue("source id", typeAnnotation.sourceId),
+ keyValue("module id", typeAnnotation.moduleId),
+ keyValue("traits", typeAnnotation.traits));
+
+ convertToString(string, dict);
+ }
+
public:
TypeNameString typeName;
Utils::PathString iconPath;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp
index 62fcf310f6..6948117db6 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp
@@ -12,6 +12,8 @@
#include "sourcepath.h"
#include "sourcepathcache.h"
+#include <tracing/qmldesignertracing.h>
+
#include <sqlitedatabase.h>
#include <QDirIterator>
@@ -21,6 +23,26 @@
#include <functional>
namespace QmlDesigner {
+constexpr auto category = ProjectStorageTracing::projectStorageUpdaterCategory;
+using NanotraceHR::keyValue;
+using Tracer = ProjectStorageTracing::Category::TracerType;
+
+template<typename String>
+void convertToString(String &string, const ProjectStorageUpdater::FileState &state)
+{
+ switch (state) {
+ case ProjectStorageUpdater::FileState::Changed:
+ convertToString(string, "Changed");
+ break;
+ case ProjectStorageUpdater::FileState::NotChanged:
+ convertToString(string, "NotChanged");
+ break;
+ case ProjectStorageUpdater::FileState::NotExists:
+ convertToString(string, "NotExists");
+ break;
+ }
+}
+
namespace {
QStringList filterMultipleEntries(QStringList qmlTypes)
@@ -110,10 +132,15 @@ SourceIds filterNotUpdatedSourceIds(SourceIds updatedSourceIds, SourceIds notUpd
return filteredUpdatedSourceIds;
}
-void addSourceIds(SourceIds &sourceIds, const Storage::Synchronization::ProjectDatas &projectDatas)
+void addSourceIds(SourceIds &sourceIds,
+ const Storage::Synchronization::ProjectDatas &projectDatas,
+ TracerLiteral message,
+ Tracer &tracer)
{
- for (const auto &projectData : projectDatas)
+ for (const auto &projectData : projectDatas) {
+ tracer.tick(message, keyValue("source id", projectData.sourceId));
sourceIds.push_back(projectData.sourceId);
+ }
}
Storage::Version convertVersion(LanguageUtils::ComponentVersion version)
@@ -131,34 +158,71 @@ Storage::Synchronization::IsAutoVersion convertToIsAutoVersion(QmlDirParser::Imp
void addDependencies(Storage::Imports &dependencies,
SourceId sourceId,
const QList<QmlDirParser::Import> &qmldirDependencies,
- ProjectStorageInterface &projectStorage)
+ ProjectStorageInterface &projectStorage,
+ TracerLiteral message,
+ Tracer &tracer)
{
for (const QmlDirParser::Import &qmldirDependency : qmldirDependencies) {
ModuleId moduleId = projectStorage.moduleId(Utils::PathString{qmldirDependency.module}
+ "-cppnative");
- dependencies.emplace_back(moduleId, Storage::Version{}, sourceId);
+ auto &import = dependencies.emplace_back(moduleId, Storage::Version{}, sourceId);
+ tracer.tick(message, keyValue("import", import));
}
}
+void addModuleExportedImport(Storage::Synchronization::ModuleExportedImports &imports,
+ ModuleId moduleId,
+ ModuleId exportedModuleId,
+ Storage::Version version,
+ Storage::Synchronization::IsAutoVersion isAutoVersion,
+ std::string_view moduleName,
+ std::string_view exportedModuleName)
+{
+ NanotraceHR::Tracer tracer{"add module exported imports"_t,
+ category(),
+ keyValue("module id", moduleId),
+ keyValue("exported module id", exportedModuleId),
+ keyValue("version", version),
+ keyValue("is auto version", isAutoVersion),
+ keyValue("module name", moduleName),
+ keyValue("exported module name", exportedModuleName)};
+
+ imports.emplace_back(moduleId, exportedModuleId, version, isAutoVersion);
+}
+
void addModuleExportedImports(Storage::Synchronization::ModuleExportedImports &imports,
ModuleId moduleId,
ModuleId cppModuleId,
+ std::string_view moduleName,
+ std::string_view cppModuleName,
const QList<QmlDirParser::Import> &qmldirImports,
ProjectStorageInterface &projectStorage)
{
- for (const QmlDirParser::Import &qmldirImport : qmldirImports) {
- ModuleId exportedModuleId = projectStorage.moduleId(Utils::PathString{qmldirImport.module});
- imports.emplace_back(moduleId,
- exportedModuleId,
- convertVersion(qmldirImport.version),
- convertToIsAutoVersion(qmldirImport.flags));
+ NanotraceHR::Tracer tracer{"add module exported imports"_t,
+ category(),
+ keyValue("cpp module id", cppModuleId),
+ keyValue("module id", moduleId)};
- ModuleId exportedCppModuleId = projectStorage.moduleId(
- Utils::PathString{qmldirImport.module} + "-cppnative");
- imports.emplace_back(cppModuleId,
- exportedCppModuleId,
- Storage::Version{},
- Storage::Synchronization::IsAutoVersion::No);
+ for (const QmlDirParser::Import &qmldirImport : qmldirImports) {
+ Utils::PathString exportedModuleName{qmldirImport.module};
+ ModuleId exportedModuleId = projectStorage.moduleId(exportedModuleName);
+ addModuleExportedImport(imports,
+ moduleId,
+ exportedModuleId,
+ convertVersion(qmldirImport.version),
+ convertToIsAutoVersion(qmldirImport.flags),
+ moduleName,
+ exportedModuleName);
+
+ exportedModuleName += "-cppnative";
+ ModuleId exportedCppModuleId = projectStorage.moduleId(exportedModuleName);
+ addModuleExportedImport(imports,
+ cppModuleId,
+ exportedCppModuleId,
+ Storage::Version{},
+ Storage::Synchronization::IsAutoVersion::No,
+ cppModuleName,
+ exportedModuleName);
}
}
@@ -184,6 +248,11 @@ void ProjectStorageUpdater::update(QStringList directories,
QStringList qmlTypesPaths,
const QString &propertyEditorResourcesPath)
{
+ NanotraceHR::Tracer tracer{"update"_t,
+ category(),
+ keyValue("directories", directories),
+ keyValue("qml types paths", qmlTypesPaths)};
+
Storage::Synchronization::SynchronizationPackage package;
WatchedSourceIdsIds watchedSourceIds{Utils::span{directories}.size()};
NotUpdatedSourceIds notUpdatedSourceIds{Utils::span{directories}.size()};
@@ -198,7 +267,11 @@ void ProjectStorageUpdater::update(QStringList directories,
std::move(package.updatedFileStatusSourceIds),
std::move(notUpdatedSourceIds.fileStatusSourceIds));
- m_projectStorage.synchronize(std::move(package));
+ try {
+ m_projectStorage.synchronize(std::move(package));
+ } catch (...) {
+ qWarning() << "Project storage could not been updated!";
+ }
m_pathWatcher.updateIdPaths(createIdPaths(watchedSourceIds, m_projectPartId));
}
@@ -211,11 +284,16 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths,
if (qmlTypesPaths.empty())
return;
+ NanotraceHR::Tracer tracer{"update qmltypes file"_t, category()};
+
ModuleId moduleId = m_projectStorage.moduleId("QML-cppnative");
for (const QString &qmlTypesPath : qmlTypesPaths) {
SourceId sourceId = m_pathCache.sourceId(SourcePath{qmlTypesPath});
watchedSourceIdsIds.qmltypesSourceIds.push_back(sourceId);
+ tracer.tick("append watched qml types source id"_t,
+ keyValue("source id", sourceId),
+ keyValue("qml types path", qmlTypesPath));
Storage::Synchronization::ProjectData projectData{sourceId,
sourceId,
@@ -228,7 +306,9 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths,
notUpdatedSourceIds);
if (state == FileState::Changed) {
+ tracer.tick("append project data"_t, keyValue("project data", projectData));
package.projectDatas.push_back(std::move(projectData));
+ tracer.tick("append updated project source ids"_t, keyValue("source id", sourceId));
package.updatedProjectSourceIds.push_back(sourceId);
}
}
@@ -246,13 +326,86 @@ ProjectStorageUpdater::FileState combineState(FileStates... fileStates)
return ProjectStorageUpdater::FileState::NotExists;
}
+
} // namespace
+void ProjectStorageUpdater::updateDirectoryChanged(std::string_view directoryPath,
+ FileState qmldirState,
+ SourcePath qmldirSourcePath,
+ SourceId qmldirSourceId,
+ SourceId directorySourceId,
+ SourceContextId directoryId,
+ Storage::Synchronization::SynchronizationPackage &package,
+ NotUpdatedSourceIds &notUpdatedSourceIds,
+ WatchedSourceIdsIds &watchedSourceIdsIds,
+ Tracer &tracer)
+{
+ QmlDirParser parser;
+ if (qmldirState != FileState::NotExists)
+ parser.parse(m_fileSystem.contentAsQString(QString{qmldirSourcePath}));
+
+ if (qmldirState != FileState::NotChanged) {
+ tracer.tick("append updated source id"_t, keyValue("module id", qmldirSourceId));
+ package.updatedSourceIds.push_back(qmldirSourceId);
+ }
+
+ Utils::PathString moduleName{parser.typeNamespace()};
+ ModuleId moduleId = m_projectStorage.moduleId(moduleName);
+ Utils::PathString cppModuleName = moduleName + "-cppnative";
+ ModuleId cppModuleId = m_projectStorage.moduleId(cppModuleName);
+ ModuleId pathModuleId = m_projectStorage.moduleId(directoryPath);
+
+ auto imports = filterMultipleEntries(parser.imports());
+
+ addModuleExportedImports(package.moduleExportedImports,
+ moduleId,
+ cppModuleId,
+ moduleName,
+ cppModuleName,
+ imports,
+ m_projectStorage);
+ tracer.tick("append updated module id"_t, keyValue("module id", moduleId));
+ package.updatedModuleIds.push_back(moduleId);
+
+ const auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId);
+ addSourceIds(package.updatedSourceIds, qmlProjectDatas, "append updated source id"_t, tracer);
+ addSourceIds(package.updatedFileStatusSourceIds,
+ qmlProjectDatas,
+ "append updated file status source id"_t,
+ tracer);
+
+ auto qmlTypes = filterMultipleEntries(parser.typeInfos());
+
+ if (!qmlTypes.isEmpty()) {
+ parseTypeInfos(qmlTypes,
+ filterMultipleEntries(parser.dependencies()),
+ imports,
+ directorySourceId,
+ directoryPath,
+ cppModuleId,
+ package,
+ notUpdatedSourceIds,
+ watchedSourceIdsIds);
+ }
+ parseQmlComponents(
+ createComponents(parser.components(), moduleId, pathModuleId, m_fileSystem, directoryPath),
+ directorySourceId,
+ directoryId,
+ package,
+ notUpdatedSourceIds,
+ watchedSourceIdsIds,
+ qmldirState);
+ tracer.tick("append updated project source id"_t, keyValue("module id", moduleId));
+ package.updatedProjectSourceIds.push_back(directorySourceId);
+}
+
void ProjectStorageUpdater::updateDirectories(const QStringList &directories,
Storage::Synchronization::SynchronizationPackage &package,
NotUpdatedSourceIds &notUpdatedSourceIds,
WatchedSourceIdsIds &watchedSourceIdsIds)
{
+ NanotraceHR::Tracer tracer{"update directories"_t, category()};
+
for (const QString &directory : directories)
updateDirectory({directory}, package, notUpdatedSourceIds, watchedSourceIdsIds);
}
@@ -262,8 +415,10 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa
NotUpdatedSourceIds &notUpdatedSourceIds,
WatchedSourceIdsIds &watchedSourceIdsIds)
{
+ NanotraceHR::Tracer tracer{"update directory"_t, category(), keyValue("directory", directoryPath)};
+
SourcePath qmldirSourcePath{directoryPath + "/qmldir"};
- auto [directoryId, qmlDirSourceId] = m_pathCache.sourceContextAndSourceId(qmldirSourcePath);
+ auto [directoryId, qmldirSourceId] = m_pathCache.sourceContextAndSourceId(qmldirSourcePath);
SourcePath directorySourcePath{directoryPath + "/."};
auto directorySourceId = m_pathCache.sourceId(directorySourcePath);
@@ -271,62 +426,28 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa
if (directoryState != FileState::NotExists)
watchedSourceIdsIds.directorySourceIds.push_back(directorySourceId);
- auto qmldirState = fileState(qmlDirSourceId, package, notUpdatedSourceIds);
+ auto qmldirState = fileState(qmldirSourceId, package, notUpdatedSourceIds);
if (qmldirState != FileState::NotExists)
- watchedSourceIdsIds.qmldirSourceIds.push_back(qmlDirSourceId);
+ watchedSourceIdsIds.qmldirSourceIds.push_back(qmldirSourceId);
switch (combineState(directoryState, qmldirState)) {
case FileState::Changed: {
- QmlDirParser parser;
- if (qmldirState != FileState::NotExists)
- parser.parse(m_fileSystem.contentAsQString(QString{qmldirSourcePath}));
-
- if (qmldirState != FileState::NotChanged)
- package.updatedSourceIds.push_back(qmlDirSourceId);
-
- Utils::PathString moduleName{parser.typeNamespace()};
- ModuleId moduleId = m_projectStorage.moduleId(moduleName);
- ModuleId cppModuleId = m_projectStorage.moduleId(moduleName + "-cppnative");
- ModuleId pathModuleId = m_projectStorage.moduleId(directoryPath);
-
- auto imports = filterMultipleEntries(parser.imports());
-
- addModuleExportedImports(package.moduleExportedImports,
- moduleId,
- cppModuleId,
- imports,
- m_projectStorage);
- package.updatedModuleIds.push_back(moduleId);
-
- const auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId);
- addSourceIds(package.updatedSourceIds, qmlProjectDatas);
- addSourceIds(package.updatedFileStatusSourceIds, qmlProjectDatas);
-
- auto qmlTypes = filterMultipleEntries(parser.typeInfos());
-
- if (!qmlTypes.isEmpty()) {
- parseTypeInfos(qmlTypes,
- filterMultipleEntries(parser.dependencies()),
- imports,
- directorySourceId,
- directoryPath,
- cppModuleId,
- package,
- notUpdatedSourceIds,
- watchedSourceIdsIds);
- }
- parseQmlComponents(
- createComponents(parser.components(), moduleId, pathModuleId, m_fileSystem, directoryPath),
- directorySourceId,
- directoryId,
- package,
- notUpdatedSourceIds,
- watchedSourceIdsIds,
- qmldirState);
- package.updatedProjectSourceIds.push_back(directorySourceId);
+ tracer.tick("update directory changed"_t);
+ updateDirectoryChanged(directoryPath,
+ qmldirState,
+ qmldirSourcePath,
+ qmldirSourceId,
+ directorySourceId,
+ directoryId,
+ package,
+ notUpdatedSourceIds,
+ watchedSourceIdsIds,
+ tracer);
break;
}
case FileState::NotChanged: {
+ tracer.tick("update directory not changed"_t);
+
parseProjectDatas(m_projectStorage.fetchProjectDatas(directorySourceId),
package,
notUpdatedSourceIds,
@@ -334,19 +455,32 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa
break;
}
case FileState::NotExists: {
+ tracer.tick("update directory don't exits"_t);
+
package.updatedFileStatusSourceIds.push_back(directorySourceId);
- package.updatedFileStatusSourceIds.push_back(qmlDirSourceId);
+ package.updatedFileStatusSourceIds.push_back(qmldirSourceId);
package.updatedProjectSourceIds.push_back(directorySourceId);
- package.updatedSourceIds.push_back(qmlDirSourceId);
+ package.updatedSourceIds.push_back(qmldirSourceId);
auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId);
for (const Storage::Synchronization::ProjectData &projectData : qmlProjectDatas) {
+ tracer.tick("append updated source id"_t, keyValue("source id", projectData.sourceId));
package.updatedSourceIds.push_back(projectData.sourceId);
+ tracer.tick("append updated file status source id"_t,
+ keyValue("source id", projectData.sourceId));
package.updatedFileStatusSourceIds.push_back(projectData.sourceId);
}
break;
}
}
+
+ tracer.end(keyValue("qmldir source path", qmldirSourcePath),
+ keyValue("directory source path", directorySourcePath),
+ keyValue("directory id", directoryId),
+ keyValue("qmldir source id", qmldirSourceId),
+ keyValue("directory source source id", directorySourceId),
+ keyValue("qmldir state", qmldirState),
+ keyValue("directory state", directoryState));
}
void ProjectStorageUpdater::updatePropertyEditorPaths(
@@ -354,6 +488,10 @@ void ProjectStorageUpdater::updatePropertyEditorPaths(
Storage::Synchronization::SynchronizationPackage &package,
NotUpdatedSourceIds &notUpdatedSourceIds)
{
+ NanotraceHR::Tracer tracer{"update property editor paths"_t,
+ category(),
+ keyValue("property editor resources path", propertyEditorResourcesPath)};
+
if (propertyEditorResourcesPath.isEmpty())
return;
@@ -386,6 +524,13 @@ void ProjectStorageUpdater::updatePropertyEditorPath(
Storage::Synchronization::SynchronizationPackage &package,
SourceId directorySourceId)
{
+ NanotraceHR::Tracer tracer{"update property editor path"_t,
+ category(),
+ keyValue("directory path", directoryPath),
+ keyValue("directory source id", directorySourceId)};
+
+ tracer.tick("append updated property editor qml path source id"_t,
+ keyValue("source id", directorySourceId));
package.updatedPropertyEditorQmlPathSourceIds.push_back(directorySourceId);
auto dir = QDir{directoryPath};
const auto fileInfos = dir.entryInfoList({"*Pane.qml", "*Specifics.qml"}, QDir::Files);
@@ -398,6 +543,11 @@ void ProjectStorageUpdater::updatePropertyEditorFilePath(
Storage::Synchronization::SynchronizationPackage &package,
SourceId directorySourceId)
{
+ NanotraceHR::Tracer tracer{"update property editor file path"_t,
+ category(),
+ keyValue("directory path", path),
+ keyValue("directory source id", directorySourceId)};
+
QRegularExpression regex{R"xo(.+\/(\w+)\/(\w+)(Specifics|Pane).qml)xo"};
auto match = regex.match(path);
QString oldModuleName;
@@ -410,7 +560,12 @@ void ProjectStorageUpdater::updatePropertyEditorFilePath(
}
Storage::TypeNameString typeName{match.capturedView(2)};
SourceId pathId = m_pathCache.sourceId(SourcePath{path});
- package.propertyEditorQmlPaths.emplace_back(moduleId, typeName, pathId, directorySourceId);
+ const auto &paths = package.propertyEditorQmlPaths.emplace_back(moduleId,
+ typeName,
+ pathId,
+ directorySourceId);
+ tracer.tick("append property editor qml paths"_t,
+ keyValue("property editor qml paths", paths));
}
}
@@ -447,6 +602,10 @@ bool contains(const Container &container, Id id)
void ProjectStorageUpdater::pathsWithIdsChanged(const std::vector<IdPaths> &changedIdPaths)
{
+ NanotraceHR::Tracer tracer{"paths with ids changed"_t,
+ category(),
+ keyValue("id paths", changedIdPaths)};
+
m_changedIdPaths.insert(m_changedIdPaths.end(), changedIdPaths.begin(), changedIdPaths.end());
Storage::Synchronization::SynchronizationPackage package;
@@ -539,21 +698,35 @@ void ProjectStorageUpdater::parseTypeInfos(const QStringList &typeInfos,
NotUpdatedSourceIds &notUpdatedSourceIds,
WatchedSourceIdsIds &watchedSourceIds)
{
+ NanotraceHR::Tracer tracer{"parse type infos"_t,
+ category(),
+ keyValue("directory source id", directorySourceId),
+ keyValue("directory path", directoryPath),
+ keyValue("module id", moduleId)};
+
for (const QString &typeInfo : typeInfos) {
+ NanotraceHR::Tracer tracer{"parse type info"_t, category(), keyValue("type info", typeInfo)};
+
Utils::PathString qmltypesPath = Utils::PathString::join(
{directoryPath, "/", Utils::SmallString{typeInfo}});
SourceId sourceId = m_pathCache.sourceId(SourcePathView{qmltypesPath});
+ tracer.tick("append qmltypes source id"_t, keyValue("source id", sourceId));
watchedSourceIds.qmltypesSourceIds.push_back(sourceId);
addDependencies(package.moduleDependencies,
sourceId,
joinImports(qmldirDependencies, qmldirImports),
- m_projectStorage);
+ m_projectStorage,
+ "append module dependency"_t,
+ tracer);
+
+ tracer.tick("append module dependenct source source id"_t, keyValue("source id", sourceId));
package.updatedModuleDependencySourceIds.push_back(sourceId);
auto projectData = package.projectDatas.emplace_back(
directorySourceId, sourceId, moduleId, Storage::Synchronization::FileType::QmlTypes);
+ tracer.tick("append project data"_t, keyValue("source id", sourceId));
parseTypeInfo(projectData, qmltypesPath, package, notUpdatedSourceIds);
}
@@ -564,6 +737,8 @@ void ProjectStorageUpdater::parseProjectDatas(const Storage::Synchronization::Pr
NotUpdatedSourceIds &notUpdatedSourceIds,
WatchedSourceIdsIds &watchedSourceIds)
{
+ NanotraceHR::Tracer tracer{"parse project datas"_t, category()};
+
for (const Storage::Synchronization::ProjectData &projectData : projectDatas) {
switch (projectData.fileType) {
case Storage::Synchronization::FileType::QmlTypes: {
@@ -587,9 +762,14 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Projec
Storage::Synchronization::SynchronizationPackage &package,
NotUpdatedSourceIds &notUpdatedSourceIds) -> FileState
{
+ NanotraceHR::Tracer tracer{"parse type info"_t,
+ category(),
+ keyValue("qmltypes path", qmltypesPath)};
+
auto state = fileState(projectData.sourceId, package, notUpdatedSourceIds);
switch (state) {
case FileState::Changed: {
+ tracer.tick("append updated source ids"_t, keyValue("source id", projectData.sourceId));
package.updatedSourceIds.push_back(projectData.sourceId);
const auto content = m_fileSystem.contentAsQString(QString{qmltypesPath});
@@ -597,14 +777,16 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Projec
break;
}
case FileState::NotChanged: {
+ tracer.tick("append not updated source ids"_t, keyValue("source id", projectData.sourceId));
notUpdatedSourceIds.sourceIds.push_back(projectData.sourceId);
break;
}
case FileState::NotExists:
throw CannotParseQmlTypesFile{};
- break;
}
+ tracer.end(keyValue("state", state));
+
return state;
}
@@ -617,6 +799,14 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil
WatchedSourceIdsIds &watchedSourceIds,
FileState qmldirState)
{
+ NanotraceHR::Tracer tracer{"parse qml component"_t,
+ category(),
+ keyValue("relative file path", relativeFilePath),
+ keyValue("directory path", directoryPath),
+ keyValue("exported types", exportedTypes),
+ keyValue("directory source id", directorySourceId),
+ keyValue("qmldir state", qmldirState)};
+
if (std::find(relativeFilePath.begin(), relativeFilePath.end(), '+') != relativeFilePath.end())
return;
@@ -626,16 +816,18 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil
Storage::Synchronization::Type type;
auto state = fileState(sourceId, package, notUpdatedSourceIds);
+ tracer.tick("append watched qml source id"_t, keyValue("source id", sourceId));
watchedSourceIds.qmlSourceIds.push_back(sourceId);
switch (state) {
case FileState::NotChanged:
if (qmldirState == FileState::NotExists) {
+ tracer.tick("append not updated source id"_t, keyValue("source id", sourceId));
notUpdatedSourceIds.sourceIds.emplace_back(sourceId);
- package.projectDatas.emplace_back(directorySourceId,
- sourceId,
- ModuleId{},
- Storage::Synchronization::FileType::QmlDocument);
+
+ const auto &projectData = package.projectDatas.emplace_back(
+ directorySourceId, sourceId, ModuleId{}, Storage::Synchronization::FileType::QmlDocument);
+ tracer.tick("append project data"_t, keyValue("project data", projectData));
return;
}
@@ -649,11 +841,11 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil
break;
}
- package.projectDatas.emplace_back(directorySourceId,
- sourceId,
- ModuleId{},
- Storage::Synchronization::FileType::QmlDocument);
+ const auto &projectData = package.projectDatas.emplace_back(
+ directorySourceId, sourceId, ModuleId{}, Storage::Synchronization::FileType::QmlDocument);
+ tracer.tick("append project data"_t, keyValue("project data", projectData));
+ tracer.tick("append updated source id"_t, keyValue("source id", sourceId));
package.updatedSourceIds.push_back(sourceId);
type.typeName = SourcePath{qmlFilePath}.name();
@@ -661,6 +853,8 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil
type.sourceId = sourceId;
type.exportedTypes = std::move(exportedTypes);
+ tracer.end(keyValue("type", type));
+
package.types.push_back(std::move(type));
}
@@ -668,10 +862,13 @@ void ProjectStorageUpdater::parseQmlComponent(SourceId sourceId,
Storage::Synchronization::SynchronizationPackage &package,
NotUpdatedSourceIds &notUpdatedSourceIds)
{
+ NanotraceHR::Tracer tracer{"parse qml component"_t, category(), keyValue("source id", sourceId)};
+
auto state = fileState(sourceId, package, notUpdatedSourceIds);
if (state == FileState::NotChanged)
return;
+ tracer.tick("append updated source id"_t, keyValue("source id", sourceId));
package.updatedSourceIds.push_back(sourceId);
if (state == FileState::NotExists)
@@ -687,6 +884,8 @@ void ProjectStorageUpdater::parseQmlComponent(SourceId sourceId,
type.sourceId = sourceId;
type.changeLevel = Storage::Synchronization::ChangeLevel::ExcludeExportedTypes;
+ tracer.end(keyValue("type", type));
+
package.types.push_back(std::move(type));
}
@@ -733,6 +932,12 @@ void ProjectStorageUpdater::parseQmlComponents(Components components,
WatchedSourceIdsIds &watchedSourceIdsIds,
FileState qmldirState)
{
+ NanotraceHR::Tracer tracer{"parse qml components"_t,
+ category(),
+ keyValue("directory source id", directorySourceId),
+ keyValue("directory id", directoryId),
+ keyValue("qmldir state", qmldirState)};
+
std::sort(components.begin(), components.end(), [](auto &&first, auto &&second) {
return first.fileName < second.fileName;
});
@@ -760,22 +965,37 @@ ProjectStorageUpdater::FileState ProjectStorageUpdater::fileState(
Storage::Synchronization::SynchronizationPackage &package,
NotUpdatedSourceIds &notUpdatedSourceIds) const
{
+ NanotraceHR::Tracer tracer{"update property editor paths"_t,
+ category(),
+ keyValue("source id", sourceId)};
+
auto currentFileStatus = m_fileStatusCache.find(sourceId);
if (!currentFileStatus.isValid()) {
+ tracer.tick("append updated file status source id"_t, keyValue("source id", sourceId));
package.updatedFileStatusSourceIds.push_back(sourceId);
+
+ tracer.end(keyValue("state", FileState::NotExists));
return FileState::NotExists;
}
auto projectStorageFileStatus = m_projectStorage.fetchFileStatus(sourceId);
if (!projectStorageFileStatus.isValid() || projectStorageFileStatus != currentFileStatus) {
+ tracer.tick("append file status"_t, keyValue("file status", sourceId));
package.fileStatuses.push_back(currentFileStatus);
+
+ tracer.tick("append updated file status source id"_t, keyValue("source id", sourceId));
package.updatedFileStatusSourceIds.push_back(sourceId);
+
+ tracer.end(keyValue("state", FileState::Changed));
return FileState::Changed;
}
+ tracer.tick("append not updated file status source id"_t, keyValue("source id", sourceId));
notUpdatedSourceIds.fileStatusSourceIds.push_back(sourceId);
+
+ tracer.end(keyValue("state", FileState::NotChanged));
return FileState::NotChanged;
}
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h
index 187a2219d0..c779403565 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h
@@ -11,6 +11,8 @@
#include "projectstoragetypes.h"
#include "sourcepath.h"
+#include <tracing/qmldesignertracing.h>
+
#include <qmljs/parser/qmldirparser_p.h>
#include <QStringList>
@@ -141,6 +143,16 @@ private:
Storage::Synchronization::SynchronizationPackage &package,
NotUpdatedSourceIds &notUpdatedSourceIds,
WatchedSourceIdsIds &watchedSourceIdsIds);
+ void updateDirectoryChanged(std::string_view directoryPath,
+ FileState qmldirState,
+ SourcePath qmldirSourcePath,
+ SourceId qmldirSourceId,
+ SourceId directorySourceId,
+ SourceContextId directoryId,
+ Storage::Synchronization::SynchronizationPackage &package,
+ NotUpdatedSourceIds &notUpdatedSourceIds,
+ WatchedSourceIdsIds &watchedSourceIdsIds,
+ ProjectStorageTracing::Category::TracerType &tracer);
void updatePropertyEditorPaths(const QString &propertyEditorResourcesPath,
Storage::Synchronization::SynchronizationPackage &package,
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp
index f9eb8080f7..daa9062a57 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp
@@ -10,6 +10,8 @@
#include <sqlitedatabase.h>
+#include <tracing/qmldesignertracing.h>
+
#ifdef QDS_BUILD_QMLPARSER
#include <private/qqmldomtop_p.h>
#endif
@@ -21,6 +23,10 @@ namespace QmlDesigner {
#ifdef QDS_BUILD_QMLPARSER
+constexpr auto category = ProjectStorageTracing::projectStorageUpdaterCategory;
+using NanotraceHR::keyValue;
+using Tracer = ProjectStorageTracing::Category::TracerType;
+
namespace QmlDom = QQmlJS::Dom;
namespace Synchronization = Storage::Synchronization;
@@ -84,6 +90,11 @@ QualifiedImports createQualifiedImports(const QList<QmlDom::Import> &qmlImports,
Utils::SmallStringView directoryPath,
QmlDocumentParser::ProjectStorage &storage)
{
+ NanotraceHR::Tracer tracer{"create qualified imports"_t,
+ category(),
+ keyValue("sourceId", sourceId),
+ keyValue("directoryPath", directoryPath)};
+
QualifiedImports qualifiedImports;
for (const QmlDom::Import &qmlImport : qmlImports) {
@@ -92,6 +103,8 @@ QualifiedImports createQualifiedImports(const QList<QmlDom::Import> &qmlImports,
createImport(qmlImport, sourceId, directoryPath, storage));
}
+ tracer.end(keyValue("qualified imports", qualifiedImports));
+
return qualifiedImports;
}
@@ -280,6 +293,11 @@ Storage::Synchronization::Type QmlDocumentParser::parse(const QString &sourceCon
SourceId sourceId,
Utils::SmallStringView directoryPath)
{
+ NanotraceHR::Tracer tracer{"qml document parser parse"_t,
+ category(),
+ keyValue("sourceId", sourceId),
+ keyValue("directoryPath", directoryPath)};
+
Storage::Synchronization::Type type;
using Option = QmlDom::DomEnvironment::Option;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp
index 3768535299..8179ff625b 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp
@@ -5,6 +5,8 @@
#include "projectstorage.h"
+#include <tracing/qmldesignertracing.h>
+
#include <sqlitedatabase.h>
#ifdef QDS_BUILD_QMLPARSER
@@ -21,6 +23,10 @@ namespace QmlDesigner {
#ifdef QDS_BUILD_QMLPARSER
+constexpr auto category = ProjectStorageTracing::projectStorageUpdaterCategory;
+using NanotraceHR::keyValue;
+using Tracer = ProjectStorageTracing::Category::TracerType;
+
namespace QmlDom = QQmlJS::Dom;
namespace {
@@ -31,6 +37,8 @@ using Storage::TypeNameString;
ComponentWithoutNamespaces createComponentNameWithoutNamespaces(const QList<QQmlJSExportedScope> &objects)
{
+ NanotraceHR::Tracer tracer{"parse qmltypes file"_t, category()};
+
ComponentWithoutNamespaces componentWithoutNamespaces;
for (const auto &object : objects) {
@@ -46,13 +54,15 @@ ComponentWithoutNamespaces createComponentNameWithoutNamespaces(const QList<QQml
name);
}
+ tracer.end(keyValue("components without namespace", componentWithoutNamespaces));
+
return componentWithoutNamespaces;
}
-void appendImports(Storage::Imports &imports,
- const QString &dependency,
- SourceId sourceId,
- QmlTypesParser::ProjectStorage &storage)
+const Storage::Import &appendImports(Storage::Imports &imports,
+ const QString &dependency,
+ SourceId sourceId,
+ QmlTypesParser::ProjectStorage &storage)
{
auto spaceFound = std::find_if(dependency.begin(), dependency.end(), [](QChar c) {
return c.isSpace();
@@ -62,7 +72,7 @@ void appendImports(Storage::Imports &imports,
moduleName.append("-cppnative");
ModuleId cppModuleId = storage.moduleId(moduleName);
- imports.emplace_back(cppModuleId, Storage::Version{}, sourceId);
+ return imports.emplace_back(cppModuleId, Storage::Version{}, sourceId);
}
void addImports(Storage::Imports &imports,
@@ -71,13 +81,25 @@ void addImports(Storage::Imports &imports,
QmlTypesParser::ProjectStorage &storage,
ModuleId cppModuleId)
{
- for (const QString &dependency : dependencies)
- appendImports(imports, dependency, sourceId, storage);
+ NanotraceHR::Tracer tracer{
+ "add imports"_t,
+ category(),
+ keyValue("source id", sourceId),
+ keyValue("module id", cppModuleId),
+ };
+
+ for (const QString &dependency : dependencies) {
+ const auto &import = appendImports(imports, dependency, sourceId, storage);
+ tracer.tick("append import"_t, keyValue("import", import), keyValue("dependency", dependency));
+ }
- imports.emplace_back(cppModuleId, Storage::Version{}, sourceId);
+ const auto &import = imports.emplace_back(cppModuleId, Storage::Version{}, sourceId);
+ tracer.tick("append import"_t, keyValue("import", import));
- if (ModuleId qmlCppModuleId = storage.moduleId("QML-cppnative"); cppModuleId != qmlCppModuleId)
- imports.emplace_back(qmlCppModuleId, Storage::Version{}, sourceId);
+ if (ModuleId qmlCppModuleId = storage.moduleId("QML-cppnative"); cppModuleId != qmlCppModuleId) {
+ const auto &import = imports.emplace_back(qmlCppModuleId, Storage::Version{}, sourceId);
+ tracer.tick("append import"_t, keyValue("import", import));
+ }
}
Storage::TypeTraits createAccessTypeTraits(QQmlJSScope::AccessSemantics accessSematics)
@@ -412,6 +434,11 @@ void addType(Storage::Synchronization::Types &types,
QmlTypesParser::ProjectStorage &storage,
const ComponentWithoutNamespaces &componentNameWithoutNamespace)
{
+ NanotraceHR::Tracer tracer{"add type"_t,
+ category(),
+ keyValue("source id", sourceId),
+ keyValue("module id", cppModuleId)};
+
const auto &component = *exportScope.scope;
auto [functionsDeclarations, signalDeclarations] = createFunctionAndSignals(
@@ -421,7 +448,7 @@ void addType(Storage::Synchronization::Types &types,
auto exports = exportScope.exports;
auto enumerationTypes = addEnumerationTypes(types, typeName, sourceId, cppModuleId, enumerations);
- types.emplace_back(
+ const auto &type = types.emplace_back(
Utils::SmallStringView{typeName},
Storage::Synchronization::ImportedType{TypeNameString{component.baseTypeName()}},
Storage::Synchronization::ImportedType{TypeNameString{component.extensionTypeName()}},
@@ -432,6 +459,7 @@ void addType(Storage::Synchronization::Types &types,
std::move(functionsDeclarations),
std::move(signalDeclarations),
createEnumeration(enumerations));
+ tracer.end(keyValue("type", type));
}
void addTypes(Storage::Synchronization::Types &types,
@@ -440,6 +468,7 @@ void addTypes(Storage::Synchronization::Types &types,
QmlTypesParser::ProjectStorage &storage,
const ComponentWithoutNamespaces &componentNameWithoutNamespaces)
{
+ NanotraceHR::Tracer tracer{"add types"_t, category()};
types.reserve(Utils::usize(objects) + types.size());
for (const auto &object : objects)
@@ -458,6 +487,8 @@ void QmlTypesParser::parse(const QString &sourceContent,
Storage::Synchronization::Types &types,
const Storage::Synchronization::ProjectData &projectData)
{
+ NanotraceHR::Tracer tracer{"qmltypes parser parse"_t, category()};
+
QQmlJSTypeDescriptionReader reader({}, sourceContent);
QList<QQmlJSExportedScope> components;
QStringList dependencies;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h b/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h
index 837e58d48a..b655c5cc34 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h
@@ -117,6 +117,12 @@ public:
std::ptrdiff_t slashIndex() const { return m_slashIndex; }
+ template<typename String>
+ friend void convertToString(String &string, const SourcePath &path)
+ {
+ convertToString(string, path.toStringView());
+ }
+
private:
std::ptrdiff_t m_slashIndex = -1;
};
diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp
index be723a3dcc..b5798b713d 100644
--- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp
+++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp
@@ -3,41 +3,59 @@
#include "qmldesignertracing.h"
+#include <sqlitebasestatement.h>
+
namespace QmlDesigner {
+
+using namespace NanotraceHR::Literals;
+
namespace Tracing {
namespace {
-using TraceFile = NanotraceHR::TraceFile<tracingStatus()>;
-
-TraceFile traceFile{"qml_designer.json"};
-thread_local NanotraceHR::EventQueueData<NanotraceHR::StringViewTraceEvent, 10000, tracingStatus()>
- strinViewEventQueueData(traceFile);
-thread_local NanotraceHR::EventQueue stringViewEventQueue_ = strinViewEventQueueData.createEventQueue();
+using TraceFile = NanotraceHR::TraceFile<tracingStatus()>;
-thread_local NanotraceHR::EventQueueData<NanotraceHR::StringViewWithStringArgumentsTraceEvent, 1000, tracingStatus()>
- stringViewWithStringArgumentsEventQueueData(traceFile);
-thread_local NanotraceHR::EventQueue stringViewEventWithStringArgumentsQueue_ = stringViewWithStringArgumentsEventQueueData
- .createEventQueue();
+auto &traceFile()
+{
+ if constexpr (std::is_same_v<Sqlite::TraceFile, TraceFile>) {
+ return Sqlite::traceFile();
+ } else {
+ static TraceFile traceFile{"tracing.json"};
+ return traceFile;
+ }
+}
} // namespace
EventQueue &eventQueue()
{
- return stringViewEventQueue_;
+ thread_local NanotraceHR::EventQueue<NanotraceHR::StringViewTraceEvent, tracingStatus()>
+ stringViewEventQueue(traceFile());
+
+ return stringViewEventQueue;
}
EventQueueWithStringArguments &eventQueueWithStringArguments()
{
- return stringViewEventWithStringArgumentsQueue_;
+ thread_local NanotraceHR::EventQueue<NanotraceHR::StringViewWithStringArgumentsTraceEvent, tracingStatus()>
+ stringViewWithStringArgumentsEventQueue(traceFile());
+
+ return stringViewWithStringArgumentsEventQueue;
+}
+
+StringEventQueue &stringEventQueue()
+{
+ thread_local NanotraceHR::EventQueue<NanotraceHR::StringTraceEvent, tracingStatus()> eventQueue(
+ traceFile());
+
+ return eventQueue;
}
} // namespace Tracing
namespace ModelTracing {
namespace {
-using namespace NanotraceHR::Literals;
-thread_local Category category_{"model"_t, Tracing::stringViewEventWithStringArgumentsQueue_, category};
+thread_local Category category_{"model"_t, Tracing::stringEventQueue(), category};
} // namespace
@@ -47,4 +65,27 @@ Category &category()
}
} // namespace ModelTracing
+
+namespace ProjectStorageTracing {
+
+Category &projectStorageCategory()
+{
+ thread_local Category category{"project storage"_t,
+ Tracing::eventQueueWithStringArguments(),
+ projectStorageCategory};
+
+ return category;
+}
+
+Category &projectStorageUpdaterCategory()
+{
+ thread_local Category category{"project storage updater"_t,
+ Tracing::eventQueueWithStringArguments(),
+ projectStorageCategory};
+
+ return category;
+}
+
+} // namespace ProjectStorageTracing
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h
index fb3f23d2f1..3a33834c70 100644
--- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h
+++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h
@@ -13,7 +13,7 @@ namespace Tracing {
constexpr NanotraceHR::Tracing tracingStatus()
{
#ifdef ENABLE_QMLDESIGNER_TRACING
- return NanotraceHR::tracingStatus();
+ return NanotraceHR::Tracing::IsEnabled;
#else
return NanotraceHR::Tracing::IsDisabled;
#endif
@@ -21,9 +21,11 @@ constexpr NanotraceHR::Tracing tracingStatus()
using EventQueue = NanotraceHR::StringViewEventQueue<tracingStatus()>;
using EventQueueWithStringArguments = NanotraceHR::StringViewWithStringArgumentsEventQueue<tracingStatus()>;
+using StringEventQueue = NanotraceHR::StringEventQueue<tracingStatus()>;
-QMLDESIGNERCORE_EXPORT EventQueue &eventQueue();
-QMLDESIGNERCORE_EXPORT EventQueueWithStringArguments &eventQueueWithStringArguments();
+[[gnu::pure]] QMLDESIGNERCORE_EXPORT EventQueue &eventQueue();
+[[gnu::pure]] QMLDESIGNERCORE_EXPORT EventQueueWithStringArguments &eventQueueWithStringArguments();
+[[gnu::pure]] QMLDESIGNERCORE_EXPORT StringEventQueue &stringEventQueue();
} // namespace Tracing
@@ -31,15 +33,33 @@ namespace ModelTracing {
constexpr NanotraceHR::Tracing tracingStatus()
{
#ifdef ENABLE_MODEL_TRACING
- return NanotraceHR::tracingStatus();
+ return NanotraceHR::Tracing::IsEnabled;
#else
return NanotraceHR::Tracing::IsDisabled;
#endif
}
-using Category = NanotraceHR::StringViewWithStringArgumentsCategory<tracingStatus()>;
-using ObjectTraceToken = Category::ObjectTokenType;
-QMLDESIGNERCORE_EXPORT Category &category();
+using Category = NanotraceHR::StringCategory<tracingStatus()>;
+using AsynchronousToken = Category::AsynchronousTokenType;
+[[gnu::pure]] QMLDESIGNERCORE_EXPORT Category &category();
} // namespace ModelTracing
+
+namespace ProjectStorageTracing {
+constexpr NanotraceHR::Tracing projectStorageTracingStatus()
+{
+#ifdef ENABLE_PROJECT_STORAGE_TRACING
+ return NanotraceHR::Tracing::IsEnabled;
+#else
+ return NanotraceHR::Tracing::IsDisabled;
+#endif
+}
+
+using Category = NanotraceHR::StringViewWithStringArgumentsCategory<projectStorageTracingStatus()>;
+
+[[gnu::pure]] Category &projectStorageCategory();
+
+[[gnu::pure]] Category &projectStorageUpdaterCategory();
+
+} // namespace ProjectStorageTracing
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/documentwarningwidget.cpp b/src/plugins/qmldesigner/documentwarningwidget.cpp
index 9fb2b87635..e1b8346a66 100644
--- a/src/plugins/qmldesigner/documentwarningwidget.cpp
+++ b/src/plugins/qmldesigner/documentwarningwidget.cpp
@@ -39,8 +39,7 @@ DocumentWarningWidget::DocumentWarningWidget(QWidget *parent)
m_messageLabel->setWordWrap(true);
m_messageLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
- m_ignoreWarningsCheckBox->setText(tr("Always ignore these warnings about features "
- "not supported by Qt Quick Designer."));
+ m_ignoreWarningsCheckBox->setText(tr("Turn off warnings about unsupported Qt Design Studio features."));
connect(m_navigateLabel, &QLabel::linkActivated, this, [this](const QString &link) {
if (link == QLatin1String("goToCode")) {
@@ -93,7 +92,7 @@ void DocumentWarningWidget::refreshContent()
m_ignoreWarningsCheckBox->hide();
m_continueButton->setText(tr("OK"));
} else {
- m_headerLabel->setText(tr("This QML file contains features which are not supported by Qt Quick Designer at:"));
+ m_headerLabel->setText(tr("This QML file contains features which are not supported by Qt Design Studio at:"));
{
QSignalBlocker blocker(m_ignoreWarningsCheckBox);
m_ignoreWarningsCheckBox->setChecked(!warningsEnabled());
diff --git a/src/plugins/qmldesigner/dynamiclicensecheck.h b/src/plugins/qmldesigner/dynamiclicensecheck.h
index a3ddf6c192..35100c8fd9 100644
--- a/src/plugins/qmldesigner/dynamiclicensecheck.h
+++ b/src/plugins/qmldesigner/dynamiclicensecheck.h
@@ -54,38 +54,6 @@ inline bool dsLicenseCheckerPluginExists()
}
} // namespace Internal
-inline FoundLicense checkLicense()
-{
- static FoundLicense license = noLicense;
-
- if (license != noLicense)
- return license;
-
- if (auto plugin = Internal::licenseCheckerPlugin()) {
- bool retVal = false;
-
- bool success = QMetaObject::invokeMethod(plugin,
- "evaluationLicense",
- Qt::DirectConnection,
- Q_RETURN_ARG(bool, retVal));
-
- if (success && retVal)
- return enterprise;
-
- retVal = false;
-
- success = QMetaObject::invokeMethod(plugin,
- "qdsEnterpriseLicense",
- Qt::DirectConnection,
- Q_RETURN_ARG(bool, retVal));
- if (success && retVal)
- return enterprise;
- else
- return professional;
- }
- return community;
-}
-
inline QString licensee()
{
if (auto plugin = Internal::licenseCheckerPlugin()) {
@@ -130,7 +98,41 @@ inline bool checkEnterpriseLicense()
if (Internal::dsLicenseCheckerPluginExists())
return false;
- return true;
+ return false;
+}
+
+inline FoundLicense checkLicense()
+{
+ static FoundLicense license = noLicense;
+
+ if (license != noLicense)
+ return license;
+
+ if (auto plugin = Internal::licenseCheckerPlugin()) {
+ bool retVal = false;
+
+ bool success = QMetaObject::invokeMethod(plugin,
+ "evaluationLicense",
+ Qt::DirectConnection,
+ Q_RETURN_ARG(bool, retVal));
+
+ if (success && retVal)
+ return enterprise;
+
+ retVal = false;
+
+ success = QMetaObject::invokeMethod(plugin,
+ "qdsEnterpriseLicense",
+ Qt::DirectConnection,
+ Q_RETURN_ARG(bool, retVal));
+ if (success && retVal)
+ return enterprise;
+ else
+ return professional;
+ }
+ if (checkEnterpriseLicense())
+ return enterprise;
+ return community;
}
} // namespace Utils
diff --git a/src/plugins/qmldesigner/generateresource.cpp b/src/plugins/qmldesigner/generateresource.cpp
deleted file mode 100644
index 10cdecafdb..0000000000
--- a/src/plugins/qmldesigner/generateresource.cpp
+++ /dev/null
@@ -1,485 +0,0 @@
-// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include <generateresource.h>
-
-#include <coreplugin/actionmanager/actioncontainer.h>
-#include <coreplugin/actionmanager/actionmanager.h>
-#include <coreplugin/actionmanager/command.h>
-#include <coreplugin/documentmanager.h>
-#include <coreplugin/icore.h>
-#include <coreplugin/messagebox.h>
-#include <coreplugin/messagemanager.h>
-
-#include <projectexplorer/project.h>
-#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/projectmanager.h>
-#include <projectexplorer/target.h>
-
-#include <qmlprojectmanager/qmlprojectconstants.h>
-
-#include <qtsupport/baseqtversion.h>
-#include <qtsupport/qtkitaspect.h>
-
-#include <utils/fileutils.h>
-#include <utils/process.h>
-#include <utils/qtcassert.h>
-#include <utils/utilsicons.h>
-
-#include <QAction>
-#include <QByteArray>
-#include <QCheckBox>
-#include <QDebug>
-#include <QDialogButtonBox>
-#include <QFileDialog>
-#include <QGridLayout>
-#include <QHeaderView>
-#include <QMap>
-#include <QObject>
-#include <QProcess>
-#include <QTemporaryFile>
-#include <QXmlStreamReader>
-
-using namespace Utils;
-
-namespace QmlDesigner {
-
-QTableWidget* GenerateResource::createFilesTable(const QList<ResourceFile> &fileNames)
-{
- auto table = new QTableWidget(0, 1);
- table->setSelectionMode(QAbstractItemView::SingleSelection);
-
- QStringList labels(QCoreApplication::translate("AddImageToResources","File Name"));
- table->setHorizontalHeaderLabels(labels);
- table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
- table->verticalHeader()->hide();
- table->setShowGrid(false);
-
- QFont font;
- font.setBold(true);
-
- for (ResourceFile resource : fileNames){
- QString filePath = resource.fileName;
- auto checkboxItem = new QTableWidgetItem();
- checkboxItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
- checkboxItem->setCheckState(Qt::Checked);
- checkboxItem->setText(filePath);
-
- if (resource.inProject)
- checkboxItem->setFont(font);
-
- int row = table->rowCount();
- table->insertRow(row);
- table->setItem(row, 0, checkboxItem);
- }
-
- return table;
-}
-
-QStringList GenerateResource::getFileList(const QList<ResourceFile> &fileNames)
-{
- QStringList result;
- QDialog *dialog = new QDialog(Core::ICore::dialogParent());
- dialog->setMinimumWidth(480);
- dialog->setMinimumHeight(640);
-
- dialog->setModal(true);
- dialog->setWindowTitle(QCoreApplication::translate("AddImageToResources","Add Resources"));
- QTableWidget *table = createFilesTable(fileNames);
-
- table->setParent(dialog);
- auto mainLayout = new QGridLayout(dialog);
- mainLayout->addWidget(table, 0, 0, 1, 4);
-
- QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok
- | QDialogButtonBox::Cancel);
-
- mainLayout->addWidget(buttonBox, 3, 2, 1, 2);
-
- QObject::connect(buttonBox, &QDialogButtonBox::accepted, dialog, [dialog]() {
- dialog->accept();
- dialog->deleteLater();
- });
-
- QObject::connect(buttonBox, &QDialogButtonBox::rejected, dialog, [dialog]() {
- dialog->reject();
- dialog->deleteLater();
- });
-
- QObject::connect(dialog, &QDialog::accepted, [&result, &table]() {
- QStringList fileList;
- QString file;
-
- for (int i = 0; i < table->rowCount(); ++i) {
- if (table->item(i,0)->checkState()){
- file = table->item(i,0)->text();
- fileList.append(file);
- }
- }
-
- result = fileList;
- });
-
- dialog->exec();
-
- return result;
-}
-
-bool skipSuffix(const QString &fileName)
-{
- const QStringList suffixes = {".pri",
- ".pro",
- ".user",
- ".qrc",
- ".qds",
- "CMakeLists.txt",
- ".db",
- ".tmp",
- ".TMP",
- ".metainfo",
- ".qtds",
- ".db-shm",
- ".db-wal"};
-
- for (const auto &suffix : suffixes)
- if (fileName.endsWith(suffix))
- return true;
-
- return false;
-}
-
-QList<GenerateResource::ResourceFile> getFilesFromQrc(QFile *file, bool inProject = false)
-{
- QXmlStreamReader reader(file);
- QList<GenerateResource::ResourceFile> fileList = {};
-
- while (!reader.atEnd()) {
- const auto token = reader.readNext();
-
- if (token != QXmlStreamReader::StartElement)
- continue;
-
- if (reader.name() == QLatin1String("file")) {
- QString fileName = reader.readElementText().trimmed();
-
- if ((!fileName.startsWith("./.")) && (!fileName.startsWith("./XXXXXXX"))
- && !skipSuffix(fileName)) {
- GenerateResource::ResourceFile file;
- file.fileName = fileName;
- file.inProject = inProject;
- fileList.append(file);
- }
- }
- }
- return fileList;
-}
-
-static bool runRcc(const CommandLine &command, const FilePath &workingDir,
- const QString &resourceFile)
-{
- Utils::Process rccProcess;
- rccProcess.setWorkingDirectory(workingDir);
- rccProcess.setCommand(command);
- rccProcess.start();
- if (!rccProcess.waitForStarted()) {
- Core::MessageManager::writeDisrupting(QCoreApplication::translate(
- "QmlDesigner::GenerateResource", "Unable to generate resource file: %1")
- .arg(resourceFile));
- return false;
- }
- QByteArray stdOut;
- QByteArray stdErr;
- if (!rccProcess.readDataFromProcess(&stdOut, &stdErr)) {
- Core::MessageManager::writeDisrupting(
- QCoreApplication::translate("QmlDesigner::GenerateResource",
- "A timeout occurred running \"%1\".")
- .arg(rccProcess.commandLine().toUserOutput()));
- return false;
- }
- if (!stdOut.trimmed().isEmpty())
- Core::MessageManager::writeFlashing(QString::fromLocal8Bit(stdOut));
-
- if (!stdErr.trimmed().isEmpty())
- Core::MessageManager::writeFlashing(QString::fromLocal8Bit(stdErr));
-
- if (rccProcess.exitStatus() != QProcess::NormalExit) {
- Core::MessageManager::writeDisrupting(QCoreApplication::translate(
- "QmlDesigner::GenerateResource", "\"%1\" crashed.")
- .arg(rccProcess.commandLine().toUserOutput()));
- return false;
- }
- if (rccProcess.exitCode() != 0) {
- Core::MessageManager::writeDisrupting(QCoreApplication::translate(
- "QmlDesigner::GenerateResource", "\"%1\" failed (exit code %2).")
- .arg(rccProcess.commandLine().toUserOutput()).arg(rccProcess.exitCode()));
- return false;
- }
- return true;
-}
-
-void GenerateResource::generateMenuEntry(QObject *parent)
-{
- const Core::Context projectContext(QmlProjectManager::Constants::QML_PROJECT_ID);
- // ToDo: move this to QtCreator and add tr to the string then
- auto action = new QAction(QCoreApplication::translate("QmlDesigner::GenerateResource",
- "Generate QRC Resource File..."),
- parent);
- action->setEnabled(ProjectExplorer::ProjectManager::startupProject() != nullptr);
- // todo make it more intelligent when it gets enabled
- QObject::connect(ProjectExplorer::ProjectManager::instance(),
- &ProjectExplorer::ProjectManager::startupProjectChanged, [action]() {
- action->setEnabled(ProjectExplorer::ProjectManager::startupProject());
- });
-
- Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.CreateResource");
- QObject::connect(action, &QAction::triggered, [] () {
- auto currentProject = ProjectExplorer::ProjectManager::startupProject();
- QTC_ASSERT(currentProject, return);
- const FilePath projectPath = currentProject->projectFilePath().parentDir();
-
- auto projectFileName = Core::DocumentManager::getSaveFileNameWithExtension(
- QCoreApplication::translate("QmlDesigner::GenerateResource", "Save Project as QRC File"),
- projectPath.pathAppended(currentProject->displayName() + ".qrc"),
- QCoreApplication::translate("QmlDesigner::GenerateResource",
- "QML Resource File (*.qrc)"));
- if (projectFileName.isEmpty())
- return;
-
- QTemporaryFile temp(projectPath.toString() + "/XXXXXXX.create.resource.qrc");
- QFile persistentFile(projectFileName.toString());
-
- if (!temp.open())
- return;
-
- temp.close();
-
- QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(
- currentProject->activeTarget()->kit());
- const FilePath rccBinary = qtVersion->rccFilePath();
-
- if (!runRcc({rccBinary, {"--project", "--output", temp.fileName()}},
- projectPath, temp.fileName())) {
- return;
- }
-
- if (!temp.open())
- return;
-
- QByteArray firstLine = temp.readLine();
- QList<ResourceFile> fileList = getFilesFromQrc(&temp);
-
- QFile existingQrcFile(projectFileName.toString());
- if (existingQrcFile.exists()) {
- existingQrcFile.open(QFile::ReadOnly);
- fileList = getFilesFromQrc(&existingQrcFile, true);
- existingQrcFile.close();
- }
-
- QDir dir;
- dir.setCurrent(projectPath.toString());
-
- Utils::FilePaths paths = currentProject->files(ProjectExplorer::Project::AllFiles);
- QStringList projectFiles = {};
-
- for (const Utils::FilePath &path : paths) {
- QString relativepath = dir.relativeFilePath(path.toString());
-
- if (!skipSuffix(relativepath)) {
- projectFiles.append(relativepath);
-
- bool found = false;
- QString compareString = "./" + relativepath.trimmed();
- for (int i = 0; i < fileList.size(); ++i)
- if (fileList.at(i).fileName == compareString) {
- fileList[i].inProject = true;
- found = true;
- break;
- }
-
- if (!found) {
- ResourceFile res;
- res.fileName = "./" + relativepath.trimmed();
- res.inProject = true;
- fileList.append(res);
- }
- }
- }
-
- temp.close();
-
- QStringList modifiedList = getFileList(fileList);
-
- if (!persistentFile.open(QIODevice::ReadWrite | QIODevice::Truncate))
- return;
-
- QXmlStreamWriter writer(&persistentFile);
- writer.setAutoFormatting(true);
- writer.setAutoFormattingIndent(0);
-
- persistentFile.write(firstLine.trimmed());
- writer.writeStartElement("qresource");
-
- for (QString file : modifiedList)
- writer.writeTextElement("file", file.trimmed());
-
- writer.writeEndElement();
- persistentFile.write("\n</RCC>\n");
- persistentFile.close();
-
- });
-
- // ToDo: move this to QtCreator and add tr to the string then
- auto rccAction = new QAction(QCoreApplication::translate("QmlDesigner::GenerateResource",
- "Generate Deployable Package..."),
- parent);
- rccAction->setEnabled(ProjectExplorer::ProjectManager::startupProject() != nullptr);
- QObject::connect(ProjectExplorer::ProjectManager::instance(),
- &ProjectExplorer::ProjectManager::startupProjectChanged, [rccAction]() {
- rccAction->setEnabled(ProjectExplorer::ProjectManager::startupProject());
- });
-
- Core::Command *cmd2 = Core::ActionManager::registerAction(rccAction,
- "QmlProject.CreateRCCResource");
- QObject::connect(rccAction, &QAction::triggered, []() {
- auto currentProject = ProjectExplorer::ProjectManager::startupProject();
- QTC_ASSERT(currentProject, return);
- const FilePath projectPath = currentProject->projectFilePath().parentDir();
-
- const FilePath resourceFileName = Core::DocumentManager::getSaveFileNameWithExtension(
- QCoreApplication::translate("QmlDesigner::GenerateResource", "Save Project as Resource"),
- projectPath.pathAppended(currentProject->displayName() + ".qmlrc"),
- QCoreApplication::translate("QmlDesigner::GenerateResource",
- "QML Resource File (*.qmlrc);;Resource File (*.rcc)"));
- if (resourceFileName.isEmpty())
- return;
-
- Core::MessageManager::writeSilently(
- QCoreApplication::translate("QmlDesigner::GenerateResource",
- "Generate a resource file out of project %1 to %2")
- .arg(currentProject->displayName(), resourceFileName.toUserOutput()));
-
- QString projectFileName = currentProject->displayName() + ".qrc";
- QFile persistentFile(projectPath.toString() + "/" + projectFileName);
-
- QTemporaryFile temp(projectPath.toString() + "/XXXXXXX.create.resource.qrc");
-
- QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(
- currentProject->activeTarget()->kit());
- const FilePath rccBinary = qtVersion->rccFilePath();
-
- QXmlStreamReader reader;
- QByteArray firstLine;
-
- if (!QFileInfo(persistentFile).exists()) {
- if (!temp.open())
- return;
- temp.close();
-
- if (!runRcc({rccBinary, {"--project", "--output", temp.fileName()}},
- projectPath, resourceFileName.toUserOutput())) {
- return;
- }
- reader.setDevice(&temp);
- if (!temp.open())
- return;
- firstLine = temp.readLine();
- } else {
- reader.setDevice(&persistentFile);
- if (!persistentFile.open(QIODevice::ReadWrite))
- return;
-
- firstLine = persistentFile.readLine();
- }
-
- QList<ResourceFile> fileList = {};
-
- while (!reader.atEnd()) {
- const auto token = reader.readNext();
-
- if (token != QXmlStreamReader::StartElement)
- continue;
-
- if (reader.name() == QLatin1String("file")) {
- QString fileName = reader.readElementText().trimmed();
- if ((!fileName.startsWith("./.")) && (!fileName.startsWith("./XXXXXXX"))
- && !skipSuffix(fileName)) {
- ResourceFile file;
- file.fileName = fileName;
- file.inProject = false;
- fileList.append(file);
- }
- }
- }
-
- QDir dir;
- dir.setCurrent(projectPath.toString());
-
- Utils::FilePaths paths = currentProject->files(ProjectExplorer::Project::AllFiles);
- QStringList projectFiles = {};
-
- for (const Utils::FilePath &path : paths) {
- QString relativepath = dir.relativeFilePath(path.toString());
-
- if (!skipSuffix(relativepath)) {
- projectFiles.append(relativepath);
-
- bool found = false;
- QString compareString = "./" + relativepath.trimmed();
- for (int i = 0; i < fileList.size(); ++i)
- if (fileList.at(i).fileName == compareString) {
- fileList[i].inProject = true;
- found = true;
- }
-
- if (!found) {
- ResourceFile res;
- res.fileName = "./" + relativepath.trimmed();
- res.inProject = true;
- fileList.append(res);
- }
- }
- }
-
- temp.close();
- persistentFile.close();
- QStringList modifiedList = getFileList(fileList);
- QTemporaryFile tempFile(projectPath.toString() + "/XXXXXXX.create.modifiedresource.qrc");
-
- if (!tempFile.open())
- return;
-
- QXmlStreamWriter writer(&tempFile);
- writer.setAutoFormatting(true);
- writer.setAutoFormattingIndent(0);
-
- tempFile.write(firstLine.trimmed());
- writer.writeStartElement("qresource");
-
- for (QString file : modifiedList)
- writer.writeTextElement("file", file.trimmed());
-
- writer.writeEndElement();
- tempFile.write("\n</RCC>\n");
- tempFile.close();
-
- if (!runRcc({rccBinary, {"--binary", "--output", resourceFileName.path(), tempFile.fileName()}},
- projectPath, resourceFileName.path())) {
- return;
- }
-
- Core::AsynchronousMessageBox::information(
- QCoreApplication::translate("QmlDesigner::GenerateResource",
- "Success"),
- QCoreApplication::translate("QmlDesigner::GenerateResource",
- "Successfully generated deployable package\n %1")
- .arg(resourceFileName.toString()));
- });
-
- Core::ActionContainer *exportMenu = Core::ActionManager::actionContainer(
- QmlProjectManager::Constants::EXPORT_MENU);
- exportMenu->addAction(cmd, QmlProjectManager::Constants::G_EXPORT_GENERATE);
- exportMenu->addAction(cmd2, QmlProjectManager::Constants::G_EXPORT_GENERATE);
-}
-
-} // namespace QmlDesigner
-
diff --git a/src/plugins/qmldesigner/generateresource.h b/src/plugins/qmldesigner/generateresource.h
deleted file mode 100644
index 029283f7ef..0000000000
--- a/src/plugins/qmldesigner/generateresource.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-#include <QTableWidget>
-
-namespace QmlDesigner {
-namespace GenerateResource {
-struct ResourceFile
-{
- QString fileName;
- bool inProject;
-};
- void generateMenuEntry(QObject *parent);
- QStringList getFileList(const QList<ResourceFile> &);
- QTableWidget* createFilesTable(const QList<ResourceFile> &);
-}
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp
index 6e8a2b6024..7819c6b49d 100644
--- a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp
+++ b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp
@@ -126,7 +126,7 @@ void PuppetEnvironmentBuilder::addRendering() const
{
m_environment.set("QML_BAD_GUI_RENDER_LOOP", "true");
m_environment.set("QML_PUPPET_MODE", "true");
- m_environment.set("QML_DISABLE_DISK_CACHE", "true");
+ //m_environment.set("QML_DISABLE_DISK_CACHE", "true");
m_environment.set("QMLPUPPET_RENDER_EFFECTS", "true");
if (!m_environment.hasKey("QT_SCREEN_SCALE_FACTORS") && !m_environment.hasKey("QT_SCALE_FACTOR"))
m_environment.set("QT_AUTO_SCREEN_SCALE_FACTOR", "1");
diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h
index 81108dee41..db63d894fe 100644
--- a/src/plugins/qmldesigner/qmldesignerconstants.h
+++ b/src/plugins/qmldesigner/qmldesignerconstants.h
@@ -6,175 +6,186 @@
namespace QmlDesigner {
namespace Constants {
-const char C_BACKSPACE[] = "QmlDesigner.Backspace";
-const char C_DELETE[] = "QmlDesigner.Delete";
-const char C_DUPLICATE[] = "QmlDesigner.Duplicate";
+inline constexpr char C_BACKSPACE[] = "QmlDesigner.Backspace";
+inline constexpr char C_DELETE[] = "QmlDesigner.Delete";
+inline constexpr char C_DUPLICATE[] = "QmlDesigner.Duplicate";
// Context
-const char C_QMLDESIGNER[] = "QmlDesigner::QmlDesignerMain";
-const char C_QMLFORMEDITOR[] = "QmlDesigner::FormEditor";
-const char C_QMLEDITOR3D[] = "QmlDesigner::Editor3D";
-const char C_QMLEFFECTCOMPOSER[] = "QmlDesigner::EffectComposer";
-const char C_QMLNAVIGATOR[] = "QmlDesigner::Navigator";
-const char C_QMLTEXTEDITOR[] = "QmlDesigner::TextEditor";
-const char C_QMLMATERIALBROWSER[] = "QmlDesigner::MaterialBrowser";
-const char C_QMLASSETSLIBRARY[] = "QmlDesigner::AssetsLibrary";
-const char C_QMLCOLLECTIONEDITOR[] = "QmlDesigner::CollectionEditor";
+inline constexpr char C_QMLDESIGNER[] = "QmlDesigner::QmlDesignerMain";
+inline constexpr char C_QMLFORMEDITOR[] = "QmlDesigner::FormEditor";
+inline constexpr char C_QMLEDITOR3D[] = "QmlDesigner::Editor3D";
+inline constexpr char C_QMLEFFECTCOMPOSER[] = "QmlDesigner::EffectComposer";
+inline constexpr char C_QMLNAVIGATOR[] = "QmlDesigner::Navigator";
+inline constexpr char C_QMLTEXTEDITOR[] = "QmlDesigner::TextEditor";
+inline constexpr char C_QMLMATERIALBROWSER[] = "QmlDesigner::MaterialBrowser";
+inline constexpr char C_QMLASSETSLIBRARY[] = "QmlDesigner::AssetsLibrary";
+inline constexpr char C_QMLCOLLECTIONEDITOR[] = "QmlDesigner::CollectionEditor";
// Special context for preview menu, shared b/w designer and text editor
-const char C_QT_QUICK_TOOLS_MENU[] = "QmlDesigner::ToolsMenu";
+inline constexpr char C_QT_QUICK_TOOLS_MENU[] = "QmlDesigner::ToolsMenu";
// Actions
-const char SWITCH_TEXT_DESIGN[] = "QmlDesigner.SwitchTextDesign";
-const char RESTORE_DEFAULT_VIEW[] = "QmlDesigner.RestoreDefaultView";
-const char TOGGLE_LEFT_SIDEBAR[] = "QmlDesigner.ToggleLeftSideBar";
-const char TOGGLE_RIGHT_SIDEBAR[] = "QmlDesigner.ToggleRightSideBar";
-const char TOGGLE_STATES_EDITOR[] = "QmlDesigner.ToggleStatesEditor";
-const char GO_INTO_COMPONENT[] = "QmlDesigner.GoIntoComponent";
-const char EXPORT_AS_IMAGE[] = "QmlDesigner.ExportAsImage";
-const char TAKE_SCREENSHOT[] = "QmlDesigner.TakeScreenshot";
-const char FORMEDITOR_REFRESH[] = "QmlDesigner.FormEditor.Refresh";
-const char FORMEDITOR_SNAPPING[] = "QmlDesigner.FormEditor.Snapping";
-const char FORMEDITOR_NO_SNAPPING[] = "QmlDesigner.FormEditor.NoSnapping";
-const char FORMEDITOR_NO_SNAPPING_AND_ANCHORING[] = "QmlDesigner.FormEditor.NoSnappingAndAnchoring";
-const char FORMEDITOR_NO_SHOW_BOUNDING_RECTANGLE[] = "QmlDesigner.FormEditor.ShowBoundingRectangle";
-const char EDIT3D_SELECTION_MODE[] = "QmlDesigner.Editor3D.SelectionModeToggle";
-const char EDIT3D_MOVE_TOOL[] = "QmlDesigner.Editor3D.MoveTool";
-const char EDIT3D_ROTATE_TOOL[] = "QmlDesigner.Editor3D.RotateTool";
-const char EDIT3D_SCALE_TOOL[] = "QmlDesigner.Editor3D.ScaleTool";
-const char EDIT3D_FIT_SELECTED[] = "QmlDesigner.Editor3D.FitSelected";
-const char EDIT3D_ALIGN_CAMERAS[] = "QmlDesigner.Editor3D.AlignCameras";
-const char EDIT3D_ALIGN_VIEW[] = "QmlDesigner.Editor3D.AlignView";
-const char EDIT3D_EDIT_CAMERA[] = "QmlDesigner.Editor3D.EditCameraToggle";
-const char EDIT3D_ORIENTATION[] = "QmlDesigner.Editor3D.OrientationToggle";
-const char EDIT3D_EDIT_LIGHT[] = "QmlDesigner.Editor3D.EditLightToggle";
-const char EDIT3D_EDIT_SHOW_GRID[] = "QmlDesigner.Editor3D.ToggleGrid";
-const char EDIT3D_EDIT_SELECT_BACKGROUND_COLOR[] = "QmlDesigner.Editor3D.SelectBackgroundColor";
-const char EDIT3D_EDIT_SELECT_GRID_COLOR[] = "QmlDesigner.Editor3D.SelectGridColor";
-const char EDIT3D_EDIT_RESET_BACKGROUND_COLOR[] = "QmlDesigner.Editor3D.ResetBackgroundColor";
-const char EDIT3D_EDIT_SYNC_ENV_BACKGROUND[] = "QmlDesigner.Editor3D.SyncEnvBackground";
-const char EDIT3D_EDIT_SHOW_SELECTION_BOX[] = "QmlDesigner.Editor3D.ToggleSelectionBox";
-const char EDIT3D_EDIT_SHOW_ICON_GIZMO[] = "QmlDesigner.Editor3D.ToggleIconGizmo";
-const char EDIT3D_EDIT_SHOW_CAMERA_FRUSTUM[] = "QmlDesigner.Editor3D.ToggleCameraFrustum";
-const char EDIT3D_EDIT_SHOW_PARTICLE_EMITTER[] = "QmlDesigner.Editor3D.ToggleParticleEmitter";
-const char EDIT3D_RESET_VIEW[] = "QmlDesigner.Editor3D.ResetView";
-const char EDIT3D_PARTICLE_MODE[] = "QmlDesigner.Editor3D.ParticleViewModeToggle";
-const char EDIT3D_PARTICLES_PLAY[] = "QmlDesigner.Editor3D.ParticlesPlay";
-const char EDIT3D_PARTICLES_SEEKER[] = "QmlDesigner.Editor3D.ParticlesSeeker";
-const char EDIT3D_PARTICLES_RESTART[] = "QmlDesigner.Editor3D.ParticlesRestart";
-const char EDIT3D_SPLIT_VIEW[] = "QmlDesigner.Editor3D.SplitViewToggle";
-const char EDIT3D_VISIBILITY_TOGGLES[] = "QmlDesigner.Editor3D.VisibilityToggles";
-const char EDIT3D_BACKGROUND_COLOR_ACTIONS[] = "QmlDesigner.Editor3D.BackgroundColorActions";
-const char EDIT3D_BAKE_LIGHTS[] = "QmlDesigner.Editor3D.BakeLights";
-const char EDIT3D_SNAP_TOGGLE[] = "QmlDesigner.Editor3D.SnapToggle";
-const char EDIT3D_SNAP_CONFIG[] = "QmlDesigner.Editor3D.SnapConfig";
-
-const char QML_DESIGNER_SUBFOLDER[] = "/designer/";
-const char COMPONENT_BUNDLES_FOLDER[] = "/ComponentBundles";
-const char COMPONENT_BUNDLES_ASSET_REF_FILE[] = "_asset_ref.json";
-const char QUICK_3D_ASSETS_FOLDER[] = "/Quick3DAssets";
-const char QUICK_3D_ASSET_LIBRARY_ICON_SUFFIX[] = "_libicon";
-const char QUICK_3D_ASSET_IMPORT_DATA_NAME[] = "_importdata.json";
-const char QUICK_3D_ASSET_IMPORT_DATA_OPTIONS_KEY[] = "import_options";
-const char QUICK_3D_ASSET_IMPORT_DATA_SOURCE_KEY[] = "source_scene";
-const char DEFAULT_ASSET_IMPORT_FOLDER[] = "/asset_imports";
-const char MATERIAL_LIB_ID[] = "__materialLibrary__";
-
-const char MIME_TYPE_ITEM_LIBRARY_INFO[] = "application/vnd.qtdesignstudio.itemlibraryinfo";
-const char MIME_TYPE_ASSETS[] = "application/vnd.qtdesignstudio.assets";
-const char MIME_TYPE_MATERIAL[] = "application/vnd.qtdesignstudio.material";
-const char MIME_TYPE_TEXTURE[] = "application/vnd.qtdesignstudio.texture";
-const char MIME_TYPE_BUNDLE_EFFECT[] = "application/vnd.qtdesignstudio.bundleeffect";
-const char MIME_TYPE_BUNDLE_MATERIAL[] = "application/vnd.qtdesignstudio.bundlematerial";
-const char MIME_TYPE_BUNDLE_TEXTURE[] = "application/vnd.qtdesignstudio.bundletexture";
-const char MIME_TYPE_ASSET_IMAGE[] = "application/vnd.qtdesignstudio.asset.image";
-const char MIME_TYPE_ASSET_FONT[] = "application/vnd.qtdesignstudio.asset.font";
-const char MIME_TYPE_ASSET_SHADER[] = "application/vnd.qtdesignstudio.asset.shader";
-const char MIME_TYPE_ASSET_SOUND[] = "application/vnd.qtdesignstudio.asset.sound";
-const char MIME_TYPE_ASSET_VIDEO[] = "application/vnd.qtdesignstudio.asset.video";
-const char MIME_TYPE_ASSET_TEXTURE3D[] = "application/vnd.qtdesignstudio.asset.texture3d";
-const char MIME_TYPE_MODELNODE_LIST[] = "application/vnd.qtdesignstudio.modelnode.list";
-const char MIME_TYPE_ASSET_EFFECT[] = "application/vnd.qtdesignstudio.asset.effect";
+inline constexpr char SWITCH_TEXT_DESIGN[] = "QmlDesigner.SwitchTextDesign";
+inline constexpr char RESTORE_DEFAULT_VIEW[] = "QmlDesigner.RestoreDefaultView";
+inline constexpr char TOGGLE_LEFT_SIDEBAR[] = "QmlDesigner.ToggleLeftSideBar";
+inline constexpr char TOGGLE_RIGHT_SIDEBAR[] = "QmlDesigner.ToggleRightSideBar";
+inline constexpr char TOGGLE_STATES_EDITOR[] = "QmlDesigner.ToggleStatesEditor";
+inline constexpr char GO_INTO_COMPONENT[] = "QmlDesigner.GoIntoComponent";
+inline constexpr char EXPORT_AS_IMAGE[] = "QmlDesigner.ExportAsImage";
+inline constexpr char TAKE_SCREENSHOT[] = "QmlDesigner.TakeScreenshot";
+inline constexpr char FORMEDITOR_REFRESH[] = "QmlDesigner.FormEditor.Refresh";
+inline constexpr char FORMEDITOR_SNAPPING[] = "QmlDesigner.FormEditor.Snapping";
+inline constexpr char FORMEDITOR_NO_SNAPPING[] = "QmlDesigner.FormEditor.NoSnapping";
+inline constexpr char FORMEDITOR_NO_SNAPPING_AND_ANCHORING[]
+ = "QmlDesigner.FormEditor.NoSnappingAndAnchoring";
+inline constexpr char FORMEDITOR_NO_SHOW_BOUNDING_RECTANGLE[]
+ = "QmlDesigner.FormEditor.ShowBoundingRectangle";
+inline constexpr char EDIT3D_SELECTION_MODE[] = "QmlDesigner.Editor3D.SelectionModeToggle";
+inline constexpr char EDIT3D_MOVE_TOOL[] = "QmlDesigner.Editor3D.MoveTool";
+inline constexpr char EDIT3D_ROTATE_TOOL[] = "QmlDesigner.Editor3D.RotateTool";
+inline constexpr char EDIT3D_SCALE_TOOL[] = "QmlDesigner.Editor3D.ScaleTool";
+inline constexpr char EDIT3D_FIT_SELECTED[] = "QmlDesigner.Editor3D.FitSelected";
+inline constexpr char EDIT3D_ALIGN_CAMERAS[] = "QmlDesigner.Editor3D.AlignCameras";
+inline constexpr char EDIT3D_ALIGN_VIEW[] = "QmlDesigner.Editor3D.AlignView";
+inline constexpr char EDIT3D_EDIT_CAMERA[] = "QmlDesigner.Editor3D.EditCameraToggle";
+inline constexpr char EDIT3D_ORIENTATION[] = "QmlDesigner.Editor3D.OrientationToggle";
+inline constexpr char EDIT3D_EDIT_LIGHT[] = "QmlDesigner.Editor3D.EditLightToggle";
+inline constexpr char EDIT3D_EDIT_SHOW_GRID[] = "QmlDesigner.Editor3D.ToggleGrid";
+inline constexpr char EDIT3D_EDIT_SELECT_BACKGROUND_COLOR[]
+ = "QmlDesigner.Editor3D.SelectBackgroundColor";
+inline constexpr char EDIT3D_EDIT_SELECT_GRID_COLOR[] = "QmlDesigner.Editor3D.SelectGridColor";
+inline constexpr char EDIT3D_EDIT_RESET_BACKGROUND_COLOR[]
+ = "QmlDesigner.Editor3D.ResetBackgroundColor";
+inline constexpr char EDIT3D_EDIT_SYNC_ENV_BACKGROUND[] = "QmlDesigner.Editor3D.SyncEnvBackground";
+inline constexpr char EDIT3D_EDIT_SHOW_SELECTION_BOX[] = "QmlDesigner.Editor3D.ToggleSelectionBox";
+inline constexpr char EDIT3D_EDIT_SHOW_ICON_GIZMO[] = "QmlDesigner.Editor3D.ToggleIconGizmo";
+inline constexpr char EDIT3D_EDIT_SHOW_CAMERA_FRUSTUM[]
+ = "QmlDesigner.Editor3D.ToggleCameraFrustum";
+inline constexpr char EDIT3D_EDIT_SHOW_PARTICLE_EMITTER[]
+ = "QmlDesigner.Editor3D.ToggleParticleEmitter";
+inline constexpr char EDIT3D_RESET_VIEW[] = "QmlDesigner.Editor3D.ResetView";
+inline constexpr char EDIT3D_PARTICLE_MODE[] = "QmlDesigner.Editor3D.ParticleViewModeToggle";
+inline constexpr char EDIT3D_PARTICLES_PLAY[] = "QmlDesigner.Editor3D.ParticlesPlay";
+inline constexpr char EDIT3D_PARTICLES_SEEKER[] = "QmlDesigner.Editor3D.ParticlesSeeker";
+inline constexpr char EDIT3D_PARTICLES_RESTART[] = "QmlDesigner.Editor3D.ParticlesRestart";
+inline constexpr char EDIT3D_SPLIT_VIEW[] = "QmlDesigner.Editor3D.SplitViewToggle";
+inline constexpr char EDIT3D_VISIBILITY_TOGGLES[] = "QmlDesigner.Editor3D.VisibilityToggles";
+inline constexpr char EDIT3D_BACKGROUND_COLOR_ACTIONS[]
+ = "QmlDesigner.Editor3D.BackgroundColorActions";
+inline constexpr char EDIT3D_BAKE_LIGHTS[] = "QmlDesigner.Editor3D.BakeLights";
+inline constexpr char EDIT3D_SNAP_TOGGLE[] = "QmlDesigner.Editor3D.SnapToggle";
+inline constexpr char EDIT3D_SNAP_CONFIG[] = "QmlDesigner.Editor3D.SnapConfig";
+inline constexpr char EDIT3D_CAMERA_SPEED_CONFIG[] = "QmlDesigner.Editor3D.CameraSpeedConfig";
+
+inline constexpr char QML_DESIGNER_SUBFOLDER[] = "/designer/";
+inline constexpr char COMPONENT_BUNDLES_FOLDER[] = "/ComponentBundles";
+inline constexpr char COMPONENT_BUNDLES_ASSET_REF_FILE[] = "_asset_ref.json";
+inline constexpr char QUICK_3D_ASSETS_FOLDER[] = "/Quick3DAssets";
+inline constexpr char QUICK_3D_ASSET_LIBRARY_ICON_SUFFIX[] = "_libicon";
+inline constexpr char QUICK_3D_ASSET_IMPORT_DATA_NAME[] = "_importdata.json";
+inline constexpr char QUICK_3D_ASSET_IMPORT_DATA_OPTIONS_KEY[] = "import_options";
+inline constexpr char QUICK_3D_ASSET_IMPORT_DATA_SOURCE_KEY[] = "source_scene";
+inline constexpr char DEFAULT_ASSET_IMPORT_FOLDER[] = "/asset_imports";
+inline constexpr char DEFAULT_EFFECTS_IMPORT_FOLDER[] = "/asset_imports/Effects";
+inline constexpr char MATERIAL_LIB_ID[] = "__materialLibrary__";
+
+inline constexpr char MIME_TYPE_ITEM_LIBRARY_INFO[]
+ = "application/vnd.qtdesignstudio.itemlibraryinfo";
+inline constexpr char MIME_TYPE_ASSETS[] = "application/vnd.qtdesignstudio.assets";
+inline constexpr char MIME_TYPE_MATERIAL[] = "application/vnd.qtdesignstudio.material";
+inline constexpr char MIME_TYPE_TEXTURE[] = "application/vnd.qtdesignstudio.texture";
+inline constexpr char MIME_TYPE_BUNDLE_EFFECT[] = "application/vnd.qtdesignstudio.bundleeffect";
+inline constexpr char MIME_TYPE_BUNDLE_MATERIAL[] = "application/vnd.qtdesignstudio.bundlematerial";
+inline constexpr char MIME_TYPE_BUNDLE_TEXTURE[] = "application/vnd.qtdesignstudio.bundletexture";
+inline constexpr char MIME_TYPE_ASSET_IMAGE[] = "application/vnd.qtdesignstudio.asset.image";
+inline constexpr char MIME_TYPE_ASSET_FONT[] = "application/vnd.qtdesignstudio.asset.font";
+inline constexpr char MIME_TYPE_ASSET_SHADER[] = "application/vnd.qtdesignstudio.asset.shader";
+inline constexpr char MIME_TYPE_ASSET_SOUND[] = "application/vnd.qtdesignstudio.asset.sound";
+inline constexpr char MIME_TYPE_ASSET_VIDEO[] = "application/vnd.qtdesignstudio.asset.video";
+inline constexpr char MIME_TYPE_ASSET_TEXTURE3D[]
+ = "application/vnd.qtdesignstudio.asset.texture3d";
+inline constexpr char MIME_TYPE_MODELNODE_LIST[] = "application/vnd.qtdesignstudio.modelnode.list";
+inline constexpr char MIME_TYPE_ASSET_EFFECT[] = "application/vnd.qtdesignstudio.asset.effect";
// Menus
-const char M_VIEW_WORKSPACES[] = "QmlDesigner.Menu.View.Workspaces";
+inline constexpr char M_VIEW_WORKSPACES[] = "QmlDesigner.Menu.View.Workspaces";
const int MODELNODE_PREVIEW_IMAGE_DIMENSIONS = 150;
-const char EVENT_TIMELINE_ADDED[] = "timelineAdded";
-const char EVENT_TRANSITION_ADDED[] = "transitionAdded";
-const char EVENT_STATE_ADDED[] = "stateAdded";
-const char EVENT_STATE_ADDED_AND_CLONED[] = "stateAddedAndCloned";
-const char EVENT_STATE_CLONED[] = "stateCloned";
-const char EVENT_STATE_EXTENDED[] = "stateExtended";
-const char EVENT_CONNECTION_ADDED[] = "connectionAdded";
-const char EVENT_PROPERTY_ADDED[] = "propertyAdded";
-const char EVENT_ANNOTATION_ADDED[] = "annotationAdded";
-const char EVENT_RESOURCE_IMPORTED[] = "resourceImported";
-const char EVENT_ACTION_EXECUTED[] = "actionExecuted";
-const char EVENT_HELP_REQUESTED[] = "helpRequested";
-const char EVENT_IMPORT_ADDED[] = "importAdded:";
-const char EVENT_BINDINGEDITOR_OPENED[] = "bindingEditorOpened";
-const char EVENT_RICHTEXT_OPENED[] = "richtextEditorOpened";
-const char EVENT_FORMEDITOR_TIME[] = "formEditor";
-const char EVENT_3DEDITOR_TIME[] = "3DEditor";
-const char EVENT_TIMELINE_TIME[] = "timeline";
-const char EVENT_TRANSITIONEDITOR_TIME[] = "transitionEditor";
-const char EVENT_CURVEDITOR_TIME[] = "curveEditor";
-const char EVENT_STATESEDITOR_TIME[] = "statesEditor";
-const char EVENT_TEXTEDITOR_TIME[] = "textEditor";
-const char EVENT_TEXTUREEDITOR_TIME[] = "textureEditor";
-const char EVENT_PROPERTYEDITOR_TIME[] = "propertyEditor";
-const char EVENT_ASSETSLIBRARY_TIME[] = "assetsLibrary";
-const char EVENT_EFFECTCOMPOSER_NODE[] = "effectComposerNode";
-const char EVENT_EFFECTCOMPOSER_TIME[] = "effectComposerTime";
-const char EVENT_ITEMLIBRARY_TIME[] = "itemLibrary";
-const char EVENT_TRANSLATIONVIEW_TIME[] = "translationView";
-const char EVENT_NAVIGATORVIEW_TIME[] = "navigatorView";
-const char EVENT_DESIGNMODE_TIME[] = "designMode";
-const char EVENT_MATERIALEDITOR_TIME[] = "materialEditor";
-const char EVENT_MATERIALBROWSER_TIME[] = "materialBrowser";
-const char EVENT_CONTENTLIBRARY_TIME[] = "contentLibrary";
-const char EVENT_INSIGHT_TIME[] = "insight";
-const char EVENT_MODELEDITOR_TIME[] = "modelEditor";
-const char EVENT_TOOLBAR_MODE_CHANGE[] = "ToolBarTriggerModeChange";
-const char EVENT_TOOLBAR_PROJECT_SETTINGS[] = "ToolBarTriggerProjectSettings";
-const char EVENT_TOOLBAR_RUN_PROJECT[] = "ToolBarRunProject";
-const char EVENT_TOOLBAR_GO_FORWARD[] = "ToolBarGoForward";
-const char EVENT_TOOLBAR_GO_BACKWARD[] = "ToolBarGoBackward";
-const char EVENT_TOOLBAR_OPEN_FILE[] = "ToolBarOpenFile";
-const char EVENT_TOOLBAR_CLOSE_DOCUMENT[] = "ToolBarCloseCurrentDocument";
-const char EVENT_TOOLBAR_SHARE_APPLICATION[] = "ToolBarShareApplication";
-const char EVENT_TOOLBAR_SET_CURRENT_WORKSPACE[] = "ToolBarSetCurrentWorkspace";
-const char EVENT_TOOLBAR_EDIT_GLOBAL_ANNOTATION[] = "ToolBarEditGlobalAnnotation";
-const char EVENT_STATUSBAR_SHOW_ZOOM[] = "StatusBarShowZoomMenu";
-const char EVENT_STATUSBAR_SET_STYLE[] = "StatusBarSetCurrentStyle";
-
-const char PROPERTY_EDITOR_CLASSNAME_PROPERTY[] = "__classNamePrivateInternal";
+inline constexpr char EVENT_TIMELINE_ADDED[] = "timelineAdded";
+inline constexpr char EVENT_TRANSITION_ADDED[] = "transitionAdded";
+inline constexpr char EVENT_STATE_ADDED[] = "stateAdded";
+inline constexpr char EVENT_STATE_ADDED_AND_CLONED[] = "stateAddedAndCloned";
+inline constexpr char EVENT_STATE_CLONED[] = "stateCloned";
+inline constexpr char EVENT_STATE_EXTENDED[] = "stateExtended";
+inline constexpr char EVENT_CONNECTION_ADDED[] = "connectionAdded";
+inline constexpr char EVENT_PROPERTY_ADDED[] = "propertyAdded";
+inline constexpr char EVENT_ANNOTATION_ADDED[] = "annotationAdded";
+inline constexpr char EVENT_RESOURCE_IMPORTED[] = "resourceImported";
+inline constexpr char EVENT_ACTION_EXECUTED[] = "actionExecuted";
+inline constexpr char EVENT_HELP_REQUESTED[] = "helpRequested";
+inline constexpr char EVENT_IMPORT_ADDED[] = "importAdded:";
+inline constexpr char EVENT_BINDINGEDITOR_OPENED[] = "bindingEditorOpened";
+inline constexpr char EVENT_RICHTEXT_OPENED[] = "richtextEditorOpened";
+inline constexpr char EVENT_FORMEDITOR_TIME[] = "formEditor";
+inline constexpr char EVENT_3DEDITOR_TIME[] = "3DEditor";
+inline constexpr char EVENT_TIMELINE_TIME[] = "timeline";
+inline constexpr char EVENT_TRANSITIONEDITOR_TIME[] = "transitionEditor";
+inline constexpr char EVENT_CURVEDITOR_TIME[] = "curveEditor";
+inline constexpr char EVENT_STATESEDITOR_TIME[] = "statesEditor";
+inline constexpr char EVENT_TEXTEDITOR_TIME[] = "textEditor";
+inline constexpr char EVENT_TEXTUREEDITOR_TIME[] = "textureEditor";
+inline constexpr char EVENT_PROPERTYEDITOR_TIME[] = "propertyEditor";
+inline constexpr char EVENT_ASSETSLIBRARY_TIME[] = "assetsLibrary";
+inline constexpr char EVENT_EFFECTCOMPOSER_NODE[] = "effectComposerNode";
+inline constexpr char EVENT_EFFECTCOMPOSER_TIME[] = "effectComposerTime";
+inline constexpr char EVENT_ITEMLIBRARY_TIME[] = "itemLibrary";
+inline constexpr char EVENT_TRANSLATIONVIEW_TIME[] = "translationView";
+inline constexpr char EVENT_NAVIGATORVIEW_TIME[] = "navigatorView";
+inline constexpr char EVENT_DESIGNMODE_TIME[] = "designMode";
+inline constexpr char EVENT_MATERIALEDITOR_TIME[] = "materialEditor";
+inline constexpr char EVENT_MATERIALBROWSER_TIME[] = "materialBrowser";
+inline constexpr char EVENT_CONTENTLIBRARY_TIME[] = "contentLibrary";
+inline constexpr char EVENT_INSIGHT_TIME[] = "insight";
+inline constexpr char EVENT_MODELEDITOR_TIME[] = "modelEditor";
+inline constexpr char EVENT_TOOLBAR_MODE_CHANGE[] = "ToolBarTriggerModeChange";
+inline constexpr char EVENT_TOOLBAR_PROJECT_SETTINGS[] = "ToolBarTriggerProjectSettings";
+inline constexpr char EVENT_TOOLBAR_RUN_PROJECT[] = "ToolBarRunProject";
+inline constexpr char EVENT_TOOLBAR_GO_FORWARD[] = "ToolBarGoForward";
+inline constexpr char EVENT_TOOLBAR_GO_BACKWARD[] = "ToolBarGoBackward";
+inline constexpr char EVENT_TOOLBAR_OPEN_FILE[] = "ToolBarOpenFile";
+inline constexpr char EVENT_TOOLBAR_CLOSE_DOCUMENT[] = "ToolBarCloseCurrentDocument";
+inline constexpr char EVENT_TOOLBAR_SHARE_APPLICATION[] = "ToolBarShareApplication";
+inline constexpr char EVENT_TOOLBAR_SET_CURRENT_WORKSPACE[] = "ToolBarSetCurrentWorkspace";
+inline constexpr char EVENT_TOOLBAR_EDIT_GLOBAL_ANNOTATION[] = "ToolBarEditGlobalAnnotation";
+inline constexpr char EVENT_STATUSBAR_SHOW_ZOOM[] = "StatusBarShowZoomMenu";
+inline constexpr char EVENT_STATUSBAR_SET_STYLE[] = "StatusBarSetCurrentStyle";
+
+inline constexpr char PROPERTY_EDITOR_CLASSNAME_PROPERTY[] = "__classNamePrivateInternal";
// Copy/Paste Headers
-const char HEADER_3DPASTE_CONTENT[] = "// __QmlDesigner.Editor3D.Paste__ \n";
-
-const char OBJECT_NAME_ASSET_LIBRARY[] = "QQuickWidgetAssetLibrary";
-const char OBJECT_NAME_CONTENT_LIBRARY[] = "QQuickWidgetContentLibrary";
-const char OBJECT_NAME_BUSY_INDICATOR[] = "QQuickWidgetBusyIndicator";
-const char OBJECT_NAME_COMPONENT_LIBRARY[] = "QQuickWidgetComponentLibrary";
-const char OBJECT_NAME_EFFECT_COMPOSER[] = "QQuickWidgetEffectComposer";
-const char OBJECT_NAME_MATERIAL_BROWSER[] = "QQuickWidgetMaterialBrowser";
-const char OBJECT_NAME_MATERIAL_EDITOR[] = "QQuickWidgetMaterialEditor";
-const char OBJECT_NAME_PROPERTY_EDITOR[] = "QQuickWidgetPropertyEditor";
-const char OBJECT_NAME_COLLECTION_EDITOR[] = "QQuickWidgetQDSCollectionEditor";
-const char OBJECT_NAME_STATES_EDITOR[] = "QQuickWidgetStatesEditor";
-const char OBJECT_NAME_TEXTURE_EDITOR[] = "QQuickWidgetTextureEditor";
-const char OBJECT_NAME_TOP_TOOLBAR[] = "QQuickWidgetTopToolbar";
-const char OBJECT_NAME_STATUSBAR[] = "QQuickWidgetStatusbar";
-const char OBJECT_NAME_TOP_FEEDBACK[] = "QQuickWidgetQDSFeedback";
-const char OBJECT_NAME_NEW_DIALOG[] = "QQuickWidgetQDSNewDialog";
-const char OBJECT_NAME_SPLASH_SCREEN[] = "QQuickWidgetSplashScreen";
-const char OBJECT_NAME_WELCOME_PAGE[] = "QQuickWidgetQDSWelcomePage";
-const char OBJECT_NAME_CONNECTION_EDITOR[] = "QQuickWidgetConnectionEditor";
-
-const char ENVIRONMENT_SHOW_QML_ERRORS[] = "QMLDESIGNER_SHOW_QML_ERRORS";
+inline constexpr char HEADER_3DPASTE_CONTENT[] = "// __QmlDesigner.Editor3D.Paste__ \n";
+
+inline constexpr char OBJECT_NAME_ASSET_LIBRARY[] = "QQuickWidgetAssetLibrary";
+inline constexpr char OBJECT_NAME_CONTENT_LIBRARY[] = "QQuickWidgetContentLibrary";
+inline constexpr char OBJECT_NAME_BUSY_INDICATOR[] = "QQuickWidgetBusyIndicator";
+inline constexpr char OBJECT_NAME_COMPONENT_LIBRARY[] = "QQuickWidgetComponentLibrary";
+inline constexpr char OBJECT_NAME_EFFECT_COMPOSER[] = "QQuickWidgetEffectComposer";
+inline constexpr char OBJECT_NAME_MATERIAL_BROWSER[] = "QQuickWidgetMaterialBrowser";
+inline constexpr char OBJECT_NAME_MATERIAL_EDITOR[] = "QQuickWidgetMaterialEditor";
+inline constexpr char OBJECT_NAME_PROPERTY_EDITOR[] = "QQuickWidgetPropertyEditor";
+inline constexpr char OBJECT_NAME_COLLECTION_EDITOR[] = "QQuickWidgetQDSCollectionEditor";
+inline constexpr char OBJECT_NAME_STATES_EDITOR[] = "QQuickWidgetStatesEditor";
+inline constexpr char OBJECT_NAME_TEXTURE_EDITOR[] = "QQuickWidgetTextureEditor";
+inline constexpr char OBJECT_NAME_TOP_TOOLBAR[] = "QQuickWidgetTopToolbar";
+inline constexpr char OBJECT_NAME_STATUSBAR[] = "QQuickWidgetStatusbar";
+inline constexpr char OBJECT_NAME_TOP_FEEDBACK[] = "QQuickWidgetQDSFeedback";
+inline constexpr char OBJECT_NAME_NEW_DIALOG[] = "QQuickWidgetQDSNewDialog";
+inline constexpr char OBJECT_NAME_SPLASH_SCREEN[] = "QQuickWidgetSplashScreen";
+inline constexpr char OBJECT_NAME_WELCOME_PAGE[] = "QQuickWidgetQDSWelcomePage";
+inline constexpr char OBJECT_NAME_CONNECTION_EDITOR[] = "QQuickWidgetConnectionEditor";
+
+inline constexpr char ENVIRONMENT_SHOW_QML_ERRORS[] = "QMLDESIGNER_SHOW_QML_ERRORS";
namespace Internal {
enum { debug = 0 };
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp
index 4e98ab9258..601cf96681 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.cpp
+++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp
@@ -10,12 +10,12 @@
#include "designmodewidget.h"
#include "dynamiclicensecheck.h"
#include "exception.h"
-#include "generateresource.h"
#include "openuiqmlfiledialog.h"
#include "qmldesignerconstants.h"
#include "qmldesignerexternaldependencies.h"
#include "qmldesignerprojectmanager.h"
#include "quick2propertyeditorview.h"
+#include "resourcegenerator.h"
#include "settingspage.h"
#include "shortcutmanager.h"
#include "toolbar.h"
@@ -26,6 +26,7 @@
#include <designeractionmanager.h>
#include <eventlist/eventlistpluginview.h>
#include <formeditor/transitiontool.h>
+#include <formeditor/view3dtool.h>
#include <studioquickwidget.h>
#include <windowmanager.h>
#ifndef QDS_USE_PROJECTSTORAGE
@@ -73,10 +74,11 @@
#include <utils/qtcassert.h>
#include <utils/uniqueobjectptr.h>
+#include <qplugin.h>
#include <QAction>
#include <QApplication>
#include <QDebug>
-#include <qplugin.h>
+#include <QMessageBox>
#include <QProcessEnvironment>
#include <QQuickItem>
#include <QScreen>
@@ -259,6 +261,15 @@ QmlDesignerPlugin::~QmlDesignerPlugin()
////////////////////////////////////////////////////
bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *errorMessage/* = 0*/)
{
+ if constexpr (isUsingQmlDesignerLite()) {
+ if (!QmlDesignerBasePlugin::isLiteModeEnabled()) {
+ QMessageBox::warning(Core::ICore::dialogParent(),
+ tr("Qml Designer Lite"),
+ tr("The Qml Designer Lite plugin is not enabled."));
+ return false;
+ }
+ }
+
Sqlite::LibraryInitializer::initialize();
QDir{}.mkpath(Core::ICore::cacheResourcePath().toString());
@@ -276,7 +287,7 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e
d = new QmlDesignerPluginPrivate;
d->timer.start();
if (Core::ICore::isQtDesignStudio())
- GenerateResource::generateMenuEntry(this);
+ ResourceGenerator::generateMenuEntry(this);
const QString fontPath
= Core::ICore::resourcePath(
@@ -381,6 +392,7 @@ void QmlDesignerPlugin::integrateIntoQtCreator(QWidget *modeWidget)
Core::Context qmlDesignerNavigatorContext(Constants::C_QMLNAVIGATOR);
Core::Context qmlDesignerMaterialBrowserContext(Constants::C_QMLMATERIALBROWSER);
Core::Context qmlDesignerAssetsLibraryContext(Constants::C_QMLASSETSLIBRARY);
+ Core::Context qmlDesignerCollectionEditorContext(Constants::C_QMLCOLLECTIONEDITOR);
context->context().add(qmlDesignerMainContext);
context->context().add(qmlDesignerFormEditorContext);
@@ -388,6 +400,7 @@ void QmlDesignerPlugin::integrateIntoQtCreator(QWidget *modeWidget)
context->context().add(qmlDesignerNavigatorContext);
context->context().add(qmlDesignerMaterialBrowserContext);
context->context().add(qmlDesignerAssetsLibraryContext);
+ context->context().add(qmlDesignerCollectionEditorContext);
context->context().add(ProjectExplorer::Constants::QMLJS_LANGUAGE_ID);
d->shortCutManager.registerActions(qmlDesignerMainContext, qmlDesignerFormEditorContext,
@@ -644,6 +657,7 @@ void QmlDesignerPlugin::enforceDelayedInitialize()
d->viewManager.registerFormEditorTool(std::make_unique<TextTool>());
d->viewManager.registerFormEditorTool(std::make_unique<PathTool>(d->externalDependencies));
d->viewManager.registerFormEditorTool(std::make_unique<TransitionTool>());
+ d->viewManager.registerFormEditorTool(std::make_unique<View3DTool>());
if (Core::ICore::isQtDesignStudio()) {
d->mainWidget.initialize();
diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp
index 2f22998596..c415863e8a 100644
--- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp
+++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp
@@ -41,6 +41,7 @@
#include <QDirIterator>
#include <QFileSystemWatcher>
+#include <QLibraryInfo>
#include <QQmlEngine>
using namespace std::chrono;
@@ -281,12 +282,12 @@ AsynchronousImageCache &QmlDesignerProjectManager::asynchronousImageCache()
}
namespace {
-ProjectStorage<Sqlite::Database> *dummyProjectStorage()
+[[maybe_unused]] ProjectStorage<Sqlite::Database> *dummyProjectStorage()
{
return nullptr;
}
-ProjectStorageUpdater::PathCache *dummyPathCache()
+[[maybe_unused]] ProjectStorageUpdater::PathCache *dummyPathCache()
{
return nullptr;
}
@@ -374,6 +375,10 @@ void collectQmldirPaths(const QString &path, QStringList &qmldirPaths)
{
QDirIterator dirIterator{path, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories};
+ QString rootQmldirPath = path + "/qmldir";
+ if (!skipPath(path) && QFileInfo::exists(rootQmldirPath))
+ qmldirPaths.push_back(path);
+
while (dirIterator.hasNext()) {
auto directoryPath = dirIterator.next();
@@ -383,7 +388,7 @@ void collectQmldirPaths(const QString &path, QStringList &qmldirPaths)
}
}
-void projectQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths)
+[[maybe_unused]] void projectQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths)
{
::QmlProjectManager::QmlBuildSystem *buildSystem = getQmlBuildSystem(target);
@@ -395,13 +400,22 @@ void projectQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPa
collectQmldirPaths(importPath, qmldirPaths);
}
-void qtQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths)
+[[maybe_unused]] void qtQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths)
{
if constexpr (useProjectStorage())
collectQmldirPaths(qmlPath(target).toString(), qmldirPaths);
}
-QStringList directories(::ProjectExplorer::Target *target)
+[[maybe_unused]] void qtQmldirPathsForLiteDesigner(QStringList &qmldirPaths)
+{
+ if constexpr (useProjectStorage()) {
+ auto qmlRootPath = QLibraryInfo::path(QLibraryInfo::QmlImportsPath);
+ collectQmldirPaths(qmlRootPath + "/QtQml", qmldirPaths);
+ collectQmldirPaths(qmlRootPath + "/QtQuick", qmldirPaths);
+ }
+}
+
+[[maybe_unused]] QStringList directories(::ProjectExplorer::Target *target)
{
if (!target)
return {};
@@ -418,7 +432,20 @@ QStringList directories(::ProjectExplorer::Target *target)
return qmldirPaths;
}
-QStringList qmlTypes(::ProjectExplorer::Target *target)
+[[maybe_unused]] QStringList directoriesForLiteDesigner()
+{
+ QStringList qmldirPaths;
+ qmldirPaths.reserve(100);
+
+ qtQmldirPathsForLiteDesigner(qmldirPaths);
+
+ std::sort(qmldirPaths.begin(), qmldirPaths.end());
+ qmldirPaths.erase(std::unique(qmldirPaths.begin(), qmldirPaths.end()), qmldirPaths.end());
+
+ return qmldirPaths;
+}
+
+[[maybe_unused]] QStringList qmlTypes(::ProjectExplorer::Target *target)
{
if (!target)
return {};
@@ -426,10 +453,26 @@ QStringList qmlTypes(::ProjectExplorer::Target *target)
QStringList qmldirPaths;
qmldirPaths.reserve(2);
- const QString installDirectory = qmlPath(target).toString();
+ const QString qmlRootPath = qmlPath(target).toString();
- qmldirPaths.append(installDirectory + "/builtins.qmltypes");
- qmldirPaths.append(installDirectory + "/jsroot.qmltypes");
+ qmldirPaths.append(qmlRootPath + "/builtins.qmltypes");
+ qmldirPaths.append(qmlRootPath + "/jsroot.qmltypes");
+
+ qmldirPaths.append(
+ Core::ICore::resourcePath("qmldesigner/projectstorage/fake.qmltypes").toString());
+
+ return qmldirPaths;
+}
+
+[[maybe_unused]] QStringList qmlTypesForLiteDesigner()
+{
+ QStringList qmldirPaths;
+ qmldirPaths.reserve(2);
+
+ const auto qmlRootPath = QLibraryInfo::path(QLibraryInfo::QmlImportsPath);
+
+ qmldirPaths.append(qmlRootPath + "/builtins.qmltypes");
+ qmldirPaths.append(qmlRootPath + "/jsroot.qmltypes");
qmldirPaths.append(
Core::ICore::resourcePath("qmldesigner/projectstorage/fake.qmltypes").toString());
@@ -580,9 +623,15 @@ void QmlDesignerProjectManager::update()
if (!m_projectData || !m_projectData->projectStorageData)
return;
- m_projectData->projectStorageData->updater.update(directories(m_projectData->activeTarget),
- qmlTypes(m_projectData->activeTarget),
- propertyEditorResourcesPath());
+ if constexpr (isUsingQmlDesignerLite()) {
+ m_projectData->projectStorageData->updater.update(directoriesForLiteDesigner(),
+ qmlTypesForLiteDesigner(),
+ propertyEditorResourcesPath());
+ } else {
+ m_projectData->projectStorageData->updater.update(directories(m_projectData->activeTarget),
+ qmlTypes(m_projectData->activeTarget),
+ propertyEditorResourcesPath());
+ }
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-16px.png b/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-16px.png
new file mode 100644
index 0000000000..d9027813d0
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-16px.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-24px.png b/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-24px.png
new file mode 100644
index 0000000000..a8a0bf65a4
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-24px.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-24px@2x.png b/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-24px@2x.png
new file mode 100644
index 0000000000..a2f7bf93d3
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-24px@2x.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc
index e71d687555..34838797d4 100644
--- a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc
+++ b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc
@@ -124,5 +124,8 @@
<file>images/drop-area-24px.png</file>
<file>images/drop-area-24px@2x.png</file>
<file>images/animatedsprite-loading.png</file>
+ <file>images/extended-view3d-16px.png</file>
+ <file>images/extended-view3d-24px.png</file>
+ <file>images/extended-view3d-24px@2x.png</file>
</qresource>
</RCC>
diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
index e0ea712ebd..3f3cdb7910 100644
--- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
+++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
@@ -853,12 +853,12 @@ MetaInfo {
Type {
name: "QtQuick3D.View3D"
- icon: ":/qtquickplugin/images/default3d16.png"
+ icon: ":/qtquickplugin/images/extended-view3d-16px.png"
ItemLibraryEntry {
name: "Extended View3D"
category: "Items"
- libraryIcon: ":/qtquickplugin/images/default3d.png"
+ libraryIcon: ":/qtquickplugin/images/extended-view3d-24px.png"
version: "6.5"
requiredImport: "QtQuick3D"
QmlSource { source: ":/qtquickplugin/source/extendedview3D_template.qml" }
diff --git a/src/plugins/qmldesigner/qtquickplugin/source/listview.qml b/src/plugins/qmldesigner/qtquickplugin/source/listview.qml
index c76d9c535e..ffda2e309e 100644
--- a/src/plugins/qmldesigner/qtquickplugin/source/listview.qml
+++ b/src/plugins/qmldesigner/qtquickplugin/source/listview.qml
@@ -4,40 +4,38 @@
import QtQuick 1.0
ListView {
- width: 110
- height: 160
+ width: 160
+ height: 80
model: ListModel {
ListElement {
- name: "Grey"
- colorCode: "grey"
- }
- ListElement {
name: "Red"
colorCode: "red"
}
ListElement {
+ name: "Green"
+ colorCode: "green"
+ }
+ ListElement {
name: "Blue"
colorCode: "blue"
}
ListElement {
- name: "Green"
- colorCode: "green"
+ name: "White"
+ colorCode: "white"
}
}
- delegate: Item {
- width: 80
- height: 40
- x: 5
- Row {
- id: row1
- spacing: 10
- Rectangle { width: 40; height: 40; color: colorCode; }
- Text {
- text: name
- anchors.verticalCenter: parent.verticalCenter
- font.bold: true
- }
+ delegate: Row {
+ spacing: 5
+ Rectangle {
+ width: 100
+ height: 20
+ color: colorCode
+ }
+
+ Text {
+ width: 100
+ text: name
}
}
}
diff --git a/src/plugins/qmldesigner/qtquickplugin/source/listviewv2.qml b/src/plugins/qmldesigner/qtquickplugin/source/listviewv2.qml
index a11e3ee68d..20b08f7cfc 100644
--- a/src/plugins/qmldesigner/qtquickplugin/source/listviewv2.qml
+++ b/src/plugins/qmldesigner/qtquickplugin/source/listviewv2.qml
@@ -4,40 +4,38 @@
import QtQuick 2.0
ListView {
- width: 110
- height: 160
+ width: 160
+ height: 80
model: ListModel {
ListElement {
- name: "Grey"
- colorCode: "grey"
- }
- ListElement {
name: "Red"
colorCode: "red"
}
ListElement {
+ name: "Green"
+ colorCode: "green"
+ }
+ ListElement {
name: "Blue"
colorCode: "blue"
}
ListElement {
- name: "Green"
- colorCode: "green"
+ name: "White"
+ colorCode: "white"
}
}
- delegate: Item {
- width: 80
- height: 40
- x: 5
- Row {
- id: row1
- spacing: 10
- Rectangle { width: 40; height: 40; color: colorCode; }
- Text {
- text: name
- anchors.verticalCenter: parent.verticalCenter
- font.bold: true
- }
+ delegate: Row {
+ spacing: 5
+ Rectangle {
+ width: 100
+ height: 20
+ color: colorCode
+ }
+
+ Text {
+ width: 100
+ text: name
}
}
}
diff --git a/src/plugins/qmldesigner/settingspage.cpp b/src/plugins/qmldesigner/settingspage.cpp
index 344e2700d9..17c75a0285 100644
--- a/src/plugins/qmldesigner/settingspage.cpp
+++ b/src/plugins/qmldesigner/settingspage.cpp
@@ -135,7 +135,7 @@ SettingsPageWidget::SettingsPageWidget(ExternalDependencies &externalDependencie
m_useDefaultPuppetRadioButton = new QRadioButton(tr("Use fallback QML emulation layer"));
m_useDefaultPuppetRadioButton->setToolTip(
- tr("If you select this radio button, Qt Quick Designer always uses the "
+ tr("If you select this radio button, Qt Design Studio always uses the "
"QML emulation layer (QML Puppet) located at the following path."));
m_useDefaultPuppetRadioButton->setChecked(true);
@@ -167,7 +167,7 @@ SettingsPageWidget::SettingsPageWidget(ExternalDependencies &externalDependencie
m_designerWarningsCheckBox = new QCheckBox(
tr("Warn about unsupported features in .ui.qml files"));
m_designerWarningsCheckBox->setToolTip(
- tr("Warns about QML features that are not properly supported by the Qt Quick Designer."));
+ tr("Warns about QML features that are not properly supported by the Qt Design Studio."));
m_designerWarningsUiQmlfiles = new QCheckBox(
tr("Warn about using .qml files instead of .ui.qml files"));
diff --git a/src/plugins/qmldesigner/shortcutmanager.cpp b/src/plugins/qmldesigner/shortcutmanager.cpp
index b2bd235fdf..961aa1c952 100644
--- a/src/plugins/qmldesigner/shortcutmanager.cpp
+++ b/src/plugins/qmldesigner/shortcutmanager.cpp
@@ -231,10 +231,12 @@ void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContex
connect(Core::ICore::instance(), &Core::ICore::contextChanged, this, [&](const Core::Context &context) {
isMatBrowserActive = context.contains(Constants::C_QMLMATERIALBROWSER);
isAssetsLibraryActive = context.contains(Constants::C_QMLASSETSLIBRARY);
+ isCollectionEditorActive = context.contains(Constants::C_QMLCOLLECTIONEDITOR);
if (!context.contains(Constants::C_QMLFORMEDITOR) && !context.contains(Constants::C_QMLEDITOR3D)
&& !context.contains(Constants::C_QMLNAVIGATOR)) {
- m_deleteAction.setEnabled(isMatBrowserActive || isAssetsLibraryActive);
+ m_deleteAction.setEnabled(isMatBrowserActive || isAssetsLibraryActive
+ || isCollectionEditorActive);
m_cutAction.setEnabled(false);
m_copyAction.setEnabled(false);
m_pasteAction.setEnabled(false);
@@ -286,15 +288,15 @@ void ShortCutManager::redo()
void ShortCutManager::deleteSelected()
{
- if (isMatBrowserActive) {
- DesignerActionManager &designerActionManager = QmlDesignerPlugin::instance()->viewManager().designerActionManager();
- designerActionManager.view()->emitCustomNotification("delete_selected_material");
- } else if (isAssetsLibraryActive) {
- DesignerActionManager &designerActionManager = QmlDesignerPlugin::instance()->viewManager().designerActionManager();
- designerActionManager.view()->emitCustomNotification("delete_selected_assets");
- } else if (currentDesignDocument()) {
+ auto &actionManager = QmlDesignerPlugin::instance()->viewManager().designerActionManager();
+ if (isMatBrowserActive)
+ actionManager.view()->emitCustomNotification("delete_selected_material");
+ else if (isAssetsLibraryActive)
+ actionManager.view()->emitCustomNotification("delete_selected_assets");
+ else if (isCollectionEditorActive)
+ actionManager.view()->emitCustomNotification("delete_selected_collection");
+ else if (currentDesignDocument())
currentDesignDocument()->deleteSelected();
- }
}
void ShortCutManager::cutSelected()
diff --git a/src/plugins/qmldesigner/shortcutmanager.h b/src/plugins/qmldesigner/shortcutmanager.h
index 70b019217c..8714bb5fbc 100644
--- a/src/plugins/qmldesigner/shortcutmanager.h
+++ b/src/plugins/qmldesigner/shortcutmanager.h
@@ -64,6 +64,7 @@ private:
bool isMatBrowserActive = false;
bool isAssetsLibraryActive = false;
+ bool isCollectionEditorActive = false;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp
index 11a180578f..b71fb63bd8 100644
--- a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp
+++ b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp
@@ -38,7 +38,7 @@ QmlDesignerBasePlugin *global;
QmlDesignerBasePlugin::QmlDesignerBasePlugin()
{
global = this;
-};
+}
QmlDesignerBasePlugin::~QmlDesignerBasePlugin() = default;
@@ -73,6 +73,16 @@ QByteArray QmlDesignerBasePlugin::experimentalFeaturesSettingsKey()
return QByteArray(experimentalFeatures) + version.toLatin1();
}
+void QmlDesignerBasePlugin::enbableLiteMode()
+{
+ global->m_enableLiteMode = true;
+}
+
+bool QmlDesignerBasePlugin::isLiteModeEnabled()
+{
+ return global->m_enableLiteMode;
+}
+
bool QmlDesignerBasePlugin::initialize(const QStringList &, QString *)
{
d = std::make_unique<Data>();
diff --git a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h
index 0820c1ef24..a8dc5a7f42 100644
--- a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h
+++ b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h
@@ -24,6 +24,8 @@ public:
QmlDesignerBasePlugin();
~QmlDesignerBasePlugin();
+ static QmlDesignerBasePlugin &instance();
+
static class DesignerSettings &settings();
static QStyle *style();
static class StudioConfigSettingsPage *studioConfigSettingsPage();
@@ -31,12 +33,16 @@ public:
static bool experimentalFeaturesEnabled();
static QByteArray experimentalFeaturesSettingsKey();
+ static void enbableLiteMode();
+ static bool isLiteModeEnabled();
+
private:
bool initialize(const QStringList &arguments, QString *errorMessage) override;
private:
class Data;
std::unique_ptr<Data> d;
+ bool m_enableLiteMode = false;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesignerbase/studio/studioquickwidget.h b/src/plugins/qmldesignerbase/studio/studioquickwidget.h
index 5c4bad8dae..a02cb2ed51 100644
--- a/src/plugins/qmldesignerbase/studio/studioquickwidget.h
+++ b/src/plugins/qmldesignerbase/studio/studioquickwidget.h
@@ -55,7 +55,7 @@ class QMLDESIGNERBASE_EXPORT StudioQmlTextBackend : public QObject
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
public:
- explicit StudioQmlTextBackend(QObject *parent = nullptr) : QObject(parent) {}
+ StudioQmlTextBackend() = default;
void setText(const QString &text)
{
@@ -95,7 +95,7 @@ class QMLDESIGNERBASE_EXPORT StudioQmlComboBoxBackend : public QObject
Q_PROPERTY(QStringList model READ model NOTIFY modelChanged) //TODO turn into model
public:
- explicit StudioQmlComboBoxBackend(QObject *parent = nullptr) : QObject(parent) {}
+ StudioQmlComboBoxBackend() = default;
void setModel(const QStringList &model)
{
diff --git a/src/plugins/qmldesignerlite/CMakeLists.txt b/src/plugins/qmldesignerlite/CMakeLists.txt
new file mode 100644
index 0000000000..95c1035b09
--- /dev/null
+++ b/src/plugins/qmldesignerlite/CMakeLists.txt
@@ -0,0 +1,20 @@
+if(BUILD_DESIGNSTUDIO AND ($<CONFIG:Debug> OR WITH_TESTS))
+ set(ENABLE_COMPILE_WARNING_AS_ERROR_DEFAULT ON)
+else()
+ set(ENABLE_COMPILE_WARNING_AS_ERROR_DEFAULT OFF)
+endif()
+env_with_default("QDS_ENABLE_COMPILE_WARNING_AS_ERROR" ENV_ENABLE_COMPILE_WARNING_AS_ERROR
+ ${ENABLE_COMPILE_WARNING_AS_ERROR_DEFAULT})
+option(ENABLE_COMPILE_WARNING_AS_ERROR "Treat warnings as errors in QmlDesigner" ${ENV_ENABLE_COMPILE_WARNING_AS_ERROR})
+add_feature_info("Treat warnings as errors in QmlDesigner" ${ENABLE_COMPILE_WARNING_AS_ERROR} "")
+
+add_qtc_plugin(QmlDesignerLite
+ PLUGIN_DEPENDS QmlDesigner
+ SOURCES
+ qmldesignerliteplugin.cpp qmldesignerliteplugin.h
+)
+
+extend_qtc_plugin(QmlDesignerBase
+ CONDITION ENABLE_COMPILE_WARNING_AS_ERROR
+ PROPERTIES COMPILE_WARNING_AS_ERROR ON
+)
diff --git a/src/plugins/qmldesignerlite/QmlDesignerLite.json.in b/src/plugins/qmldesignerlite/QmlDesignerLite.json.in
new file mode 100644
index 0000000000..75410dfd58
--- /dev/null
+++ b/src/plugins/qmldesignerlite/QmlDesignerLite.json.in
@@ -0,0 +1,21 @@
+{
+ "Name" : "QmlDesignerLite",
+ "Version" : "${IDE_VERSION}",
+ "CompatVersion" : "${IDE_VERSION_COMPAT}",
+ "Vendor" : "The Qt Company Ltd",
+ "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd",
+ "License" : [ "Commercial Usage",
+ "",
+ "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.",
+ "",
+ "GNU General Public License Usage",
+ "",
+ "Alternatively, this plugin may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this plugin. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html."
+ ],
+ "Category" : "Qt Quick",
+ "Description" : "Qml Designer Lite",
+ "LongDescription": "Qml Designer Lite is a lightweight version of Qt Design Studio, providing a subset of the features of the full Qt Design Studio.",
+ "Url" : "http://www.qt.io",
+ "Experimental": true,
+ ${IDE_PLUGIN_DEPENDENCIES}
+}
diff --git a/src/plugins/qmldesignerlite/qmldesignerliteplugin.cpp b/src/plugins/qmldesignerlite/qmldesignerliteplugin.cpp
new file mode 100644
index 0000000000..4414b92f7c
--- /dev/null
+++ b/src/plugins/qmldesignerlite/qmldesignerliteplugin.cpp
@@ -0,0 +1,22 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "qmldesignerliteplugin.h"
+
+#include <qmldesignerbase/qmldesignerbaseplugin.h>
+
+namespace QmlDesigner {
+
+QmlDesignerLitePlugin::QmlDesignerLitePlugin()
+{
+ QmlDesignerBasePlugin::enbableLiteMode();
+}
+
+QmlDesignerLitePlugin::~QmlDesignerLitePlugin() = default;
+
+bool QmlDesignerLitePlugin::initialize(const QStringList &, QString *)
+{
+ return true;
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesignerlite/qmldesignerliteplugin.h b/src/plugins/qmldesignerlite/qmldesignerliteplugin.h
new file mode 100644
index 0000000000..4fb30e60c4
--- /dev/null
+++ b/src/plugins/qmldesignerlite/qmldesignerliteplugin.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <extensionsystem/iplugin.h>
+
+namespace QmlDesigner {
+
+class QmlDesignerLitePlugin final : public ExtensionSystem::IPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "QmlDesignerLite.json")
+
+public:
+ QmlDesignerLitePlugin();
+ ~QmlDesignerLitePlugin();
+
+private:
+ bool initialize(const QStringList &arguments, QString *errorMessage) override;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmlpreview/CMakeLists.txt b/src/plugins/qmlpreview/CMakeLists.txt
index 4c1889143a..440b407b70 100644
--- a/src/plugins/qmlpreview/CMakeLists.txt
+++ b/src/plugins/qmlpreview/CMakeLists.txt
@@ -4,7 +4,7 @@ add_qtc_plugin(QmlPreview
DEPENDS QmlJS Qt::QmlPrivate
PLUGIN_DEPENDS
Core ProjectExplorer QmlJSTools QtSupport
- ResourceEditor QmlProjectManager
+ ResourceEditor QmlProjectManager TextEditor
SOURCES
qmlpreviewclient.cpp qmlpreviewclient.h
qmlpreviewconnectionmanager.cpp qmlpreviewconnectionmanager.h
diff --git a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp
index 209bc06d1d..17881d00bd 100644
--- a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp
+++ b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp
@@ -20,7 +20,7 @@
#include <utils/async.h>
#include <utils/filepath.h>
#include <utils/port.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/url.h>
#include <QFutureWatcher>
diff --git a/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp b/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp
index af91963e02..3ab0647e81 100644
--- a/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp
@@ -20,7 +20,7 @@
#include <qmldebug/qmldebugcommandlinearguments.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/url.h>
diff --git a/src/plugins/qmlprojectmanager/CMakeLists.txt b/src/plugins/qmlprojectmanager/CMakeLists.txt
index b94bc29400..3067411a24 100644
--- a/src/plugins/qmlprojectmanager/CMakeLists.txt
+++ b/src/plugins/qmlprojectmanager/CMakeLists.txt
@@ -49,6 +49,7 @@ extend_qtc_plugin(QmlProjectManager
cmakeprojectconverterdialog.cpp cmakeprojectconverterdialog.h
generatecmakelists.cpp generatecmakelists.h
generatecmakelistsconstants.h
+ cmakegenerator.cpp cmakegenerator.h
boilerplate.qrc
)
@@ -56,7 +57,7 @@ add_qtc_library(QmlProjectManagerLib OBJECT
CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3
EXCLUDE_FROM_INSTALL
DEPENDS
- QmlJS Utils
+ QmlJS Utils ProjectExplorer
INCLUDES
${CMAKE_CURRENT_LIST_DIR}
SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/buildsystem
diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp
index 5cafae5660..95cc859af5 100644
--- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp
+++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp
@@ -143,6 +143,7 @@ QString jsonToQmlProject(const QJsonObject &rootObject)
appendString("mainFile", runConfig["mainFile"].toString());
appendString("mainUiFile", runConfig["mainUiFile"].toString());
appendString("targetDirectory", deploymentConfig["targetDirectory"].toString());
+ appendBool("enableCMakeGeneration", deploymentConfig["enableCMakeGeneration"].toBool());
appendBool("widgetApp", runConfig["widgetApp"].toBool());
appendStringArray("importPaths", rootObject["importPaths"].toVariant().toStringList());
appendBreak();
@@ -282,7 +283,8 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile)
|| propName.contains("mainuifile", Qt::CaseInsensitive)
|| propName.contains("forcefreetype", Qt::CaseInsensitive)) {
currentObj = &runConfigObject;
- } else if (propName.contains("targetdirectory", Qt::CaseInsensitive)) {
+ } else if (propName.contains("targetdirectory", Qt::CaseInsensitive)
+ || propName.contains("enableCMakeGeneration", Qt::CaseInsensitive)) {
currentObj = &deploymentObject;
} else if (propName.contains("qtformcus", Qt::CaseInsensitive)) {
qtForMCUs = value.toBool();
diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp
index 38f27fbc10..eb6e6de177 100644
--- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp
+++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp
@@ -422,4 +422,16 @@ void QmlProjectItem::insertAndUpdateProjectFile(const QString &key, const QJsonV
m_projectFile.writeFileContents(Converters::jsonToQmlProject(m_project).toUtf8());
}
+bool QmlProjectItem::enableCMakeGeneration() const
+{
+ return m_project["deployment"].toObject()["enableCMakeGeneration"].toBool();
+}
+
+void QmlProjectItem::setEnableCMakeGeneration(bool enable)
+{
+ QJsonObject obj = m_project["deployment"].toObject();
+ obj["enableCMakeGeneration"] = enable;
+ insertAndUpdateProjectFile("deployment", obj);
+}
+
} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h
index 214614f536..7d57ad2e60 100644
--- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h
+++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h
@@ -88,6 +88,9 @@ public:
QJsonObject project() const;
+ bool enableCMakeGeneration() const;
+ void setEnableCMakeGeneration(bool enable);
+
signals:
void qmlFilesChanged(const QSet<QString> &, const QSet<QString> &);
diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp
index f49b507972..a0b8263900 100644
--- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp
+++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp
@@ -32,6 +32,7 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/target.h>
+#include "projectexplorer/projectmanager.h"
#include "projectitem/qmlprojectitem.h"
#include "projectnode/qmlprojectnodes.h"
@@ -76,6 +77,7 @@ void updateMcuBuildStep(Target *target, bool mcuEnabled)
QmlBuildSystem::QmlBuildSystem(Target *target)
: BuildSystem(target)
+ , m_cmakeGen(new GenerateCmake::CMakeGenerator(this, this))
{
// refresh first - project information is used e.g. to decide the default RC's
refresh(RefreshOptions::Project);
@@ -85,10 +87,12 @@ QmlBuildSystem::QmlBuildSystem(Target *target)
connect(target->project(), &Project::activeTargetChanged, this, [this](Target *target) {
refresh(RefreshOptions::NoFileRefresh);
+ m_cmakeGen->initialize(qmlProject());
updateMcuBuildStep(target, qtForMCUs());
});
connect(target->project(), &Project::projectFileIsDirty, this, [this] {
refresh(RefreshOptions::Project);
+ m_cmakeGen->initialize(qmlProject());
updateMcuBuildStep(project()->activeTarget(), qtForMCUs());
});
@@ -219,6 +223,13 @@ void QmlBuildSystem::initProjectItem()
&QmlProjectItem::qmlFilesChanged,
this,
&QmlBuildSystem::refreshFiles);
+
+ connect(m_projectItem.get(),
+ &QmlProjectItem::qmlFilesChanged,
+ m_cmakeGen,
+ &GenerateCmake::CMakeGenerator::update);
+
+ m_cmakeGen->setEnabled(m_projectItem->enableCMakeGeneration());
}
void QmlBuildSystem::parseProjectFiles()
@@ -389,6 +400,16 @@ Utils::FilePath QmlBuildSystem::getStartupQmlFileWithFallback() const
return {};
}
+QmlBuildSystem *QmlBuildSystem::getStartupBuildSystem()
+{
+ auto project = ProjectExplorer::ProjectManager::startupProject();
+ if (project && project->activeTarget() && project->activeTarget()->buildSystem()) {
+ return qobject_cast<QmlProjectManager::QmlBuildSystem *>(
+ project->activeTarget()->buildSystem());
+ }
+ return nullptr;
+}
+
Utils::FilePath QmlBuildSystem::mainFilePath() const
{
const QString fileName = mainFile();
diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h
index 95b66871eb..524af5cd1b 100644
--- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h
+++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h
@@ -8,6 +8,8 @@
#include "../qmlprojectmanager_global.h"
#include <projectexplorer/buildsystem.h>
+#include "qmlprojectmanager/cmakegen/cmakegenerator.h"
+
namespace QmlProjectManager {
class QmlProject;
@@ -102,6 +104,8 @@ public:
Utils::FilePath getStartupQmlFileWithFallback() const;
+ static QmlBuildSystem *getStartupBuildSystem();
+
signals:
void projectChanged();
@@ -120,6 +124,8 @@ private:
void registerMenuButtons();
void updateDeploymentData();
friend class FilesUpdateBlocker;
+
+ GenerateCmake::CMakeGenerator* m_cmakeGen;
};
} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc b/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc
index a89643d6ff..10fab59838 100644
--- a/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc
+++ b/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc
@@ -1,5 +1,8 @@
<RCC>
<qresource prefix="/boilerplatetemplates">
+ <file>gencmakeroot.tpl</file>
+ <file>gencmakemodule.tpl</file>
+ <file>gencmakeheadercomponents.tpl</file>
<file>qmlprojectmaincpp.tpl</file>
<file>qmlprojectmaincppheader.tpl</file>
<file>qmlprojectenvheader.tpl</file>
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp
new file mode 100644
index 0000000000..736ac04ec6
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp
@@ -0,0 +1,569 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "cmakegenerator.h"
+#include "generatecmakelistsconstants.h"
+
+#include "projectexplorer/projectmanager.h"
+#include "projectexplorer/projectnodes.h"
+#include "qmlprojectmanager/qmlproject.h"
+
+#include <QRegularExpression>
+
+#include <set>
+
+namespace QmlProjectManager {
+
+namespace GenerateCmake {
+
+const char TEMPLATE_CMAKELISTS_ROOT[] = ":/boilerplatetemplates/gencmakeroot.tpl";
+const char TEMPLATE_CMAKELISTS_MODULE[] = ":/boilerplatetemplates/gencmakemodule.tpl";
+
+const char TEMPLATE_SOURCE_MAIN[] = ":/boilerplatetemplates/qmlprojectmaincpp.tpl";
+const char TEMPLATE_HEADER_IMPORT_COMPS[] = ":/boilerplatetemplates/gencmakeheadercomponents.tpl";
+const char TEMPLATE_HEADER_IMPORT_PLUGINS[] = ":/boilerplatetemplates/qmlprojectmaincppheader.tpl";
+const char TEMPLATE_HEADER_ENVIRONMENT[] = ":/boilerplatetemplates/qmlprojectenvheader.tpl";
+
+const char DO_NOT_EDIT_FILE_COMMENT[]
+ = "### This file is automatically generated by Qt Design Studio.\n"
+ "### Do not change\n\n";
+
+const char TEMPLATE_BIG_RESOURCES[] = R"(
+qt6_add_resources(%1 %2
+ BIG_RESOURCES
+ PREFIX "%3"
+ VERSION 1.0
+ FILES %4
+))";
+
+const char TEMPLATE_LINK_LIBRARIES[] = R"(
+target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
+%3
+))";
+
+CMakeGenerator::CMakeGenerator(QmlBuildSystem *bs, QObject *parent)
+ : QObject(parent)
+ , m_root(std::make_shared<Node>())
+ , m_buildSystem(bs)
+{}
+
+void CMakeGenerator::setEnabled(bool enabled)
+{
+ m_enabled = enabled;
+}
+
+void CMakeGenerator::initialize(QmlProject *project)
+{
+ if (!m_enabled)
+ return;
+
+ m_srcs.clear();
+ m_moduleNames.clear();
+
+ m_root = std::make_shared<Node>();
+ m_root->module = true;
+ m_root->uri = QString("Main");
+ m_root->name = QString("Main");
+ m_root->dir = project->rootProjectDirectory();
+
+ m_projectName = project->displayName();
+
+ ProjectExplorer::ProjectNode *rootProjectNode = project->rootProjectNode();
+ parseNodeTree(m_root, rootProjectNode);
+ parseSourceTree();
+
+ createCMakeFiles(m_root);
+ createEntryPoints(m_root);
+}
+
+void CMakeGenerator::update(const QSet<QString> &added, const QSet<QString> &removed)
+{
+ if (!m_enabled)
+ return;
+
+ std::set<NodePtr> dirtyModules;
+ for (const QString &add : added) {
+ const Utils::FilePath path = Utils::FilePath::fromString(add);
+ if (auto node = findOrCreateNode(m_root, path)) {
+ insertFile(node, path);
+ if (auto module = findModuleFor(node))
+ dirtyModules.insert(module);
+ } else {
+ qDebug() << "CmakeGen: Failed to find Folder node " << path;
+ }
+ }
+
+ for (const QString &remove : removed) {
+ const Utils::FilePath path = Utils::FilePath::fromString(remove);
+ if (auto node = findNode(m_root, path)) {
+ removeFile(node, path);
+ if (auto module = findModuleFor(node))
+ dirtyModules.insert(module);
+ }
+ }
+
+ for (auto module : dirtyModules)
+ createModuleCMakeFile(module);
+}
+
+std::vector<Utils::FilePath> CMakeGenerator::files(const NodePtr &node,
+ const FileGetter &getter) const
+{
+ std::vector<Utils::FilePath> out = getter(node);
+ for (const CMakeGenerator::NodePtr &child : node->subdirs) {
+ if (child->module)
+ continue;
+
+ auto childFiles = files(child, getter);
+ out.insert(out.end(), childFiles.begin(), childFiles.end());
+ }
+ return out;
+}
+
+std::vector<Utils::FilePath> CMakeGenerator::qmlFiles(const NodePtr &node) const
+{
+ return files(node, [](const NodePtr &n) { return n->files; });
+}
+
+std::vector<Utils::FilePath> CMakeGenerator::singletons(const NodePtr &node) const
+{
+ return files(node, [](const NodePtr &n) { return n->singletons; });
+}
+
+std::vector<Utils::FilePath> CMakeGenerator::resources(const NodePtr &node) const
+{
+ return files(node, [](const NodePtr &n) { return n->resources; });
+}
+
+std::vector<Utils::FilePath> CMakeGenerator::sources(const NodePtr &node) const
+{
+ return files(node, [](const NodePtr &n) { return n->sources; });
+}
+
+void CMakeGenerator::createCMakeFiles(const NodePtr &node) const
+{
+ if (isRootNode(node))
+ createMainCMakeFile(node);
+
+ if (node->module || hasChildModule(node))
+ createModuleCMakeFile(node);
+
+ for (const NodePtr &n : node->subdirs)
+ createCMakeFiles(n);
+}
+
+void CMakeGenerator::createMainCMakeFile(const NodePtr &node) const
+{
+ const QString appName = m_projectName + "App";
+
+ const QString qtcontrolsConfFile = makeEnvironmentVariable(Constants::ENV_VARIABLE_CONTROLCONF);
+
+ QString fileSection = "";
+ if (!qtcontrolsConfFile.isEmpty())
+ fileSection = QString(" FILES\n %1").arg(qtcontrolsConfFile);
+
+ const QString fileTemplate = readTemplate(TEMPLATE_CMAKELISTS_ROOT);
+ const QString fileContent = fileTemplate.arg(appName, m_srcs.join(" "), fileSection);
+
+ const Utils::FilePath file = node->dir.pathAppended("CMakeLists.txt");
+ writeFile(file, fileContent);
+}
+
+void CMakeGenerator::createModuleCMakeFile(const NodePtr &node) const
+{
+ Utils::FilePath writeToFile = node->dir.pathAppended("CMakeLists.txt");
+
+ if (!node->module && hasChildModule(node)) {
+ QString content(DO_NOT_EDIT_FILE_COMMENT);
+ content.append(makeSubdirectoriesBlock(node));
+ writeFile(writeToFile, content);
+ return;
+ }
+
+ QString templatePrefix;
+ templatePrefix.append(makeSubdirectoriesBlock(node));
+ templatePrefix.append(makeSingletonBlock(node));
+
+ auto [resources, bigResources] = makeResourcesBlocks(node);
+ QString moduleContent;
+ moduleContent.append(makeQmlFilesBlock(node));
+ moduleContent.append(resources);
+
+ QString templatePostfix;
+ templatePostfix.append(bigResources);
+
+ if (isRootNode(node)) {
+ writeToFile = node->dir.pathAppended("qmlModules");
+ QString pluginNames;
+ for (const QString &moduleName : m_moduleNames)
+ pluginNames.append(" " + moduleName + "plugin\n");
+
+ templatePostfix += QString::fromUtf8(TEMPLATE_LINK_LIBRARIES, -1).arg(pluginNames);
+ }
+
+ const QString fileTemplate = readTemplate(TEMPLATE_CMAKELISTS_MODULE);
+ const QString fileContent
+ = fileTemplate.arg(node->name, node->uri, templatePrefix, moduleContent, templatePostfix);
+
+ writeFile(writeToFile, fileContent);
+}
+
+void CMakeGenerator::createEntryPoints(const NodePtr &node) const
+{
+ createMainCppFile(node);
+}
+
+void CMakeGenerator::createMainCppFile(const NodePtr &node) const
+{
+ const Utils::FilePath srcDir = node->dir.pathAppended(Constants::DIRNAME_CPP);
+ if (!srcDir.exists()) {
+ srcDir.createDir();
+
+ const Utils::FilePath componentsHeaderPath = srcDir.pathAppended(
+ "import_qml_components_plugins.h");
+
+ const QString componentsHeaderContent = readTemplate(TEMPLATE_HEADER_IMPORT_COMPS);
+ writeFile(componentsHeaderPath, componentsHeaderContent);
+
+ const Utils::FilePath cppFilePath = srcDir.pathAppended("main.cpp");
+ const QString cppContent = readTemplate(TEMPLATE_SOURCE_MAIN);
+ writeFile(cppFilePath, cppContent);
+
+ const Utils::FilePath envHeaderPath = srcDir.pathAppended("app_environment.h");
+ if (m_buildSystem) {
+ QString environment;
+ const QString qtcontrolsConfFile = makeEnvironmentVariable(
+ Constants::ENV_VARIABLE_CONTROLCONF);
+ for (Utils::EnvironmentItem &envItem : m_buildSystem->environment()) {
+ QString key = envItem.name;
+ QString value = envItem.value;
+ if (value == qtcontrolsConfFile)
+ value.prepend(":/");
+ environment.append(QString(" qputenv(\"%1\", \"%2\");\n").arg(key).arg(value));
+ }
+ const QString envHeaderContent
+ = readTemplate(TEMPLATE_HEADER_ENVIRONMENT).arg(environment);
+ writeFile(envHeaderPath, envHeaderContent);
+ }
+ }
+
+ QString moduleContent;
+ for (const QString &module : m_moduleNames)
+ moduleContent.append(QString("Q_IMPORT_QML_PLUGIN(%1)\n").arg(module + "Plugin"));
+
+ const QString headerContent = readTemplate(TEMPLATE_HEADER_IMPORT_PLUGINS).arg(moduleContent);
+ const Utils::FilePath headerFilePath = srcDir.pathAppended("import_qml_plugins.h");
+ writeFile(headerFilePath, headerContent);
+}
+
+void CMakeGenerator::writeFile(const Utils::FilePath &path, const QString &content) const
+{
+ QFile fileHandle(path.toString());
+ fileHandle.open(QIODevice::WriteOnly);
+ QTextStream stream(&fileHandle);
+ stream << content;
+ fileHandle.close();
+}
+
+QString CMakeGenerator::makeRelative(const NodePtr &node, const Utils::FilePath &path) const
+{
+ const QString dir = node->dir.toString();
+ return "\"" + Utils::FilePath::calcRelativePath(path.toString(), dir) + "\"";
+}
+
+QString CMakeGenerator::makeEnvironmentVariable(const QString &key) const
+{
+ QString value;
+ if (m_buildSystem) {
+ auto envItems = m_buildSystem->environment();
+ auto confEnv = std::find_if(envItems.begin(),
+ envItems.end(),
+ [key](Utils::EnvironmentItem &item) { return item.name == key; });
+ if (confEnv != envItems.end())
+ value = confEnv->value;
+ }
+ return value;
+}
+
+QString CMakeGenerator::makeSingletonBlock(const NodePtr &node) const
+{
+ const QString setProperties(
+ "set_source_files_properties(%1\n PROPERTIES\n %2 %3\n)\n\n");
+
+ QString str;
+ for (const Utils::FilePath &path : node->singletons)
+ str.append(setProperties.arg(path.fileName()).arg("QT_QML_SINGLETON_TYPE").arg("true"));
+ return str;
+}
+
+QString CMakeGenerator::makeSubdirectoriesBlock(const NodePtr &node) const
+{
+ QString str;
+ for (const NodePtr &n : node->subdirs) {
+ if (n->module || hasChildModule(n))
+ str.append(QString("add_subdirectory(%1)\n").arg(n->dir.fileName()));
+ }
+ return str;
+}
+
+QString CMakeGenerator::makeQmlFilesBlock(const NodePtr &node) const
+{
+ QString qmlFileContent;
+ for (const Utils::FilePath &path : qmlFiles(node))
+ qmlFileContent.append(QString(" %1\n").arg(makeRelative(node, path)));
+
+ if (isRootNode(node) && qmlFileContent.isEmpty())
+ qmlFileContent.append(QString(" %1\n").arg("\"main.qml\""));
+
+ QString str;
+ if (!qmlFileContent.isEmpty())
+ str.append(QString(" QML_FILES\n%1").arg(qmlFileContent));
+
+ return str;
+}
+
+std::tuple<QString, QString> CMakeGenerator::makeResourcesBlocks(const NodePtr &node) const
+{
+ QString resourcesOut;
+ QString bigResourcesOut;
+
+ QString resourceFiles;
+ std::vector<QString> bigResources;
+ for (const Utils::FilePath &path : resources(node)) {
+ if (path.fileSize() > 5000000) {
+ bigResources.push_back(makeRelative(node, path));
+ continue;
+ }
+ resourceFiles.append(QString(" %1\n").arg(makeRelative(node, path)));
+ }
+
+ if (!resourceFiles.isEmpty())
+ resourcesOut.append(QString(" RESOURCES\n%1").arg(resourceFiles));
+
+ QString templatePostfix;
+ if (!bigResources.empty()) {
+ QString resourceContent;
+ for (const QString &res : bigResources)
+ resourceContent.append(QString("\n %1").arg(res));
+
+ const QString prefixPath = QString(node->uri).replace('.', '/');
+ const QString prefix = "/qt/qml/" + prefixPath;
+ const QString resourceName = node->name + "BigResource";
+
+ bigResourcesOut = QString::fromUtf8(TEMPLATE_BIG_RESOURCES, -1)
+ .arg(node->name, resourceName, prefix, resourceContent);
+ }
+
+ return {resourcesOut, bigResourcesOut};
+}
+
+QString CMakeGenerator::readTemplate(const QString &templatePath) const
+{
+ QFile templatefile(templatePath);
+ templatefile.open(QIODevice::ReadOnly);
+ QTextStream stream(&templatefile);
+ QString content = stream.readAll();
+ templatefile.close();
+ return content;
+}
+
+void CMakeGenerator::readQmlDir(const Utils::FilePath &filePath, NodePtr &node) const
+{
+ node->module = true;
+
+ QFile f(filePath.toString());
+ f.open(QIODevice::ReadOnly);
+ QTextStream stream(&f);
+
+ Utils::FilePath dir = filePath.parentDir();
+ while (!stream.atEnd()) {
+ const QString line = stream.readLine();
+ const QStringList tokenizedLine = line.split(QRegularExpression("\\s+"));
+ const QString maybeFileName = tokenizedLine.last();
+ if (tokenizedLine.first().compare("module", Qt::CaseInsensitive) == 0) {
+ node->uri = tokenizedLine.last();
+ node->name = QString(node->uri).replace('.', '_');
+ } else if (maybeFileName.endsWith(".qml", Qt::CaseInsensitive)) {
+ Utils::FilePath tmp = dir.pathAppended(maybeFileName);
+ if (tokenizedLine.first() == "singleton")
+ node->singletons.push_back(tmp);
+ }
+ }
+ f.close();
+}
+
+CMakeGenerator::NodePtr CMakeGenerator::findModuleFor(const NodePtr &node) const
+{
+ NodePtr current = node;
+ while (current->parent) {
+ if (current->module)
+ return current;
+
+ current = current->parent;
+ }
+ return nullptr;
+}
+
+CMakeGenerator::NodePtr CMakeGenerator::findNode(NodePtr &node, const Utils::FilePath &path) const
+{
+ const Utils::FilePath parentDir = path.parentDir();
+ for (NodePtr &child : node->subdirs) {
+ if (child->dir == parentDir)
+ return child;
+ if (path.isChildOf(child->dir))
+ return findNode(child, path);
+ }
+ return nullptr;
+}
+
+CMakeGenerator::NodePtr CMakeGenerator::findOrCreateNode(NodePtr &node,
+ const Utils::FilePath &path) const
+{
+ if (auto found = findNode(node, path))
+ return found;
+
+ if (!path.isChildOf(node->dir))
+ return nullptr;
+
+ const Utils::FilePath parentDir = path.parentDir();
+ const Utils::FilePath relative = parentDir.relativeChildPath(node->dir);
+ const QChar separator = relative.pathComponentSeparator();
+ const QList<QStringView> components = relative.pathView().split(separator);
+
+ NodePtr last = node;
+ for (const auto &comp : components) {
+ NodePtr newNode = std::make_shared<Node>();
+ newNode->parent = last;
+ newNode->name = comp.toString();
+ newNode->dir = last->dir.pathAppended(comp.toString());
+ last->subdirs.push_back(newNode);
+ last = newNode;
+ }
+ return last;
+}
+
+void CMakeGenerator::insertFile(NodePtr &node, const Utils::FilePath &path) const
+{
+ if (path.fileName() == "qmldir") {
+ readQmlDir(path, node);
+ } else if (path.suffix() == "qml" || path.suffix() == "ui.qml") {
+ node->files.push_back(path);
+ } else if (path.suffix() == "cpp") {
+ node->sources.push_back(path);
+ } else if (isResource(path)) {
+ node->resources.push_back(path);
+ }
+}
+
+void CMakeGenerator::removeFile(NodePtr &node, const Utils::FilePath &path) const
+{
+ if (path.fileName() == "qmldir") {
+ node->module = false;
+ node->singletons.clear();
+ node->uri = "";
+ node->name = path.parentDir().fileName();
+
+ } else if (path.suffix() == "qml") {
+ auto iter = std::find(node->files.begin(), node->files.end(), path);
+ if (iter != node->files.end())
+ node->files.erase(iter);
+ } else if (isResource(path)) {
+ auto iter = std::find(node->resources.begin(), node->resources.end(), path);
+ if (iter != node->resources.end())
+ node->resources.erase(iter);
+ }
+}
+
+bool CMakeGenerator::isRootNode(const NodePtr &node) const
+{
+ return node->name == "Main";
+}
+
+bool CMakeGenerator::hasChildModule(const NodePtr &node) const
+{
+ for (const NodePtr &child : node->subdirs) {
+ if (child->module)
+ return true;
+ if (hasChildModule(child))
+ return true;
+ }
+ return false;
+}
+
+bool CMakeGenerator::isResource(const Utils::FilePath &path) const
+{
+ static const QStringList suffixes = {
+ "json", "mesh", "dae", "qad", "hints", "png", "hdr", "ttf", "jpg", "JPG",
+ "js", "qsb", "frag", "frag.qsb", "vert", "vert.qsb", "svg", "ktx"};
+ return suffixes.contains(path.suffix());
+}
+
+void CMakeGenerator::printModules(const NodePtr &node) const
+{
+ if (node->module)
+ qDebug() << "Module: " << node->name;
+
+ for (const auto &child : node->subdirs)
+ printModules(child);
+}
+
+void CMakeGenerator::printNodeTree(const NodePtr &generatorNode, size_t indent) const
+{
+ auto addIndent = [](size_t level) -> QString {
+ QString str;
+ for (size_t i = 0; i < level; ++i)
+ str += " ";
+ return str;
+ };
+
+ qDebug() << addIndent(indent) << "GeneratorNode: " << generatorNode->name;
+ qDebug() << addIndent(indent) << "directory: " << generatorNode->dir;
+ qDebug() << addIndent(indent) << "files: " << generatorNode->files;
+ qDebug() << addIndent(indent) << "singletons: " << generatorNode->singletons;
+ qDebug() << addIndent(indent) << "resources: " << generatorNode->resources;
+ qDebug() << addIndent(indent) << "sources: " << generatorNode->sources;
+
+ for (const auto &child : generatorNode->subdirs)
+ printNodeTree(child, indent + 1);
+}
+
+void CMakeGenerator::parseNodeTree(NodePtr &generatorNode,
+ const ProjectExplorer::FolderNode *folderNode)
+{
+ for (const auto *childNode : folderNode->nodes()) {
+ if (const auto *subFolderNode = childNode->asFolderNode()) {
+ CMakeGenerator::NodePtr childGeneratorNode = std::make_shared<Node>();
+ childGeneratorNode->parent = generatorNode;
+ childGeneratorNode->dir = subFolderNode->filePath();
+ childGeneratorNode->name = subFolderNode->displayName();
+ childGeneratorNode->uri = childGeneratorNode->name;
+ parseNodeTree(childGeneratorNode, subFolderNode);
+ generatorNode->subdirs.push_back(childGeneratorNode);
+ } else if (auto *fileNode = childNode->asFileNode()) {
+ insertFile(generatorNode, fileNode->filePath());
+ }
+ }
+
+ if (generatorNode->name == "content")
+ generatorNode->module = true;
+
+ if (generatorNode->module)
+ m_moduleNames.push_back(generatorNode->name);
+}
+
+void CMakeGenerator::parseSourceTree()
+{
+ m_srcs.clear();
+ const QString srcDir = m_root->dir.pathAppended(Constants::DIRNAME_CPP).path();
+ QDirIterator it(srcDir, QStringList({"*.cpp"}), QDir::Files, QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ QString relative = Utils::FilePath::calcRelativePath(it.next(), m_root->dir.path());
+ m_srcs.push_back(relative);
+ }
+
+ if (m_srcs.empty())
+ m_srcs.push_back("src/main.cpp");
+}
+
+} // namespace GenerateCmake
+} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h
new file mode 100644
index 0000000000..f89257ac5d
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h
@@ -0,0 +1,103 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#pragma once
+
+#include "utils/filepath.h"
+
+#include <QObject>
+
+namespace ProjectExplorer {
+class FolderNode;
+}
+
+namespace QmlProjectManager {
+
+class QmlProject;
+class QmlBuildSystem;
+
+namespace GenerateCmake {
+
+class CMakeGenerator : public QObject
+{
+ Q_OBJECT
+
+public:
+ CMakeGenerator(QmlBuildSystem *bs, QObject *parent = nullptr);
+
+ void setEnabled(bool enabled);
+
+ void initialize(QmlProject *project);
+
+ void update(const QSet<QString> &added, const QSet<QString> &removed);
+
+private:
+ struct Node
+ {
+ std::shared_ptr<Node> parent = nullptr;
+ bool module = false;
+
+ QString uri;
+ QString name;
+ Utils::FilePath dir;
+
+ std::vector<std::shared_ptr<Node>> subdirs;
+ std::vector<Utils::FilePath> files;
+ std::vector<Utils::FilePath> singletons;
+ std::vector<Utils::FilePath> resources;
+ std::vector<Utils::FilePath> sources;
+ };
+
+ using NodePtr = std::shared_ptr<Node>;
+ using FileGetter = std::function<std::vector<Utils::FilePath>(const NodePtr &)>;
+
+ std::vector<Utils::FilePath> files(const NodePtr &node, const FileGetter &getter) const;
+ std::vector<Utils::FilePath> qmlFiles(const NodePtr &node) const;
+ std::vector<Utils::FilePath> singletons(const NodePtr &node) const;
+ std::vector<Utils::FilePath> resources(const NodePtr &node) const;
+ std::vector<Utils::FilePath> sources(const NodePtr &node) const;
+
+ void createCMakeFiles(const NodePtr &node) const;
+ void createMainCMakeFile(const NodePtr &node) const;
+ void createModuleCMakeFile(const NodePtr &node) const;
+
+ void createEntryPoints(const NodePtr &node) const;
+ void createMainCppFile(const NodePtr &node) const;
+ void writeFile(const Utils::FilePath &path, const QString &content) const;
+
+ QString makeRelative(const NodePtr &node, const Utils::FilePath &path) const;
+ QString makeEnvironmentVariable(const QString &key) const;
+ QString makeSingletonBlock(const NodePtr &node) const;
+ QString makeSubdirectoriesBlock(const NodePtr &node) const;
+ QString makeQmlFilesBlock(const NodePtr &node) const;
+ std::tuple<QString, QString> makeResourcesBlocks(const NodePtr &node) const;
+
+ QString readTemplate(const QString &templatePath) const;
+ void readQmlDir(const Utils::FilePath &filePath, NodePtr &node) const;
+
+ NodePtr findModuleFor(const NodePtr &node) const;
+ NodePtr findNode(NodePtr &node, const Utils::FilePath &path) const;
+ NodePtr findOrCreateNode(NodePtr &node, const Utils::FilePath &path) const;
+
+ void insertFile(NodePtr &node, const Utils::FilePath &path) const;
+ void removeFile(NodePtr &node, const Utils::FilePath &path) const;
+
+ bool isRootNode(const NodePtr &node) const;
+ bool hasChildModule(const NodePtr &node) const;
+ bool isResource(const Utils::FilePath &path) const;
+
+ void printModules(const NodePtr &generatorNode) const;
+ void printNodeTree(const NodePtr &generatorNode, size_t indent = 0) const;
+
+ void parseNodeTree(NodePtr &generatorNode, const ProjectExplorer::FolderNode *folderNode);
+ void parseSourceTree();
+
+ bool m_enabled = false;
+ QString m_projectName = {};
+ NodePtr m_root = {};
+ QStringList m_srcs = {};
+ std::vector<QString> m_moduleNames = {};
+ QmlBuildSystem *m_buildSystem = nullptr;
+};
+
+} // namespace GenerateCmake
+} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp
index d653847c36..f6bf1a4975 100644
--- a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp
+++ b/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp
@@ -44,9 +44,15 @@ void CmakeProjectConverter::generateMenuEntry(QObject *parent)
action->setEnabled(isProjectConvertable(ProjectExplorer::ProjectManager::startupProject()));
QObject::connect(ProjectExplorer::ProjectManager::instance(),
- &ProjectExplorer::ProjectManager::startupProjectChanged, [action]() {
- action->setEnabled(isProjectConvertable(ProjectExplorer::ProjectManager::startupProject()));
- });
+ &ProjectExplorer::ProjectManager::startupProjectChanged,
+ [action]() {
+ auto currentBuildSystem = QmlBuildSystem::getStartupBuildSystem();
+ bool isMCU = currentBuildSystem ? currentBuildSystem->qtForMCUs() : false;
+
+ action->setEnabled(isMCU
+ && isProjectConvertable(
+ ProjectExplorer::ProjectManager::startupProject()));
+ });
}
bool CmakeProjectConverter::isProjectConvertable(const ProjectExplorer::Project *project)
diff --git a/src/plugins/qmlprojectmanager/cmakegen/gencmakeheadercomponents.tpl b/src/plugins/qmlprojectmanager/cmakegen/gencmakeheadercomponents.tpl
new file mode 100644
index 0000000000..167481d7c7
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/cmakegen/gencmakeheadercomponents.tpl
@@ -0,0 +1,19 @@
+/*
+ * This file is automatically generated by Qt Design Studio.
+ * Do not change.
+*/
+
+#include "qqmlextensionplugin.h"
+
+#ifdef BUILD_QDS_COMPONENTS
+
+Q_IMPORT_QML_PLUGIN(QtQuick_Studio_ComponentsPlugin)
+Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EffectsPlugin)
+Q_IMPORT_QML_PLUGIN(QtQuick_Studio_ApplicationPlugin)
+Q_IMPORT_QML_PLUGIN(FlowViewPlugin)
+Q_IMPORT_QML_PLUGIN(QtQuick_Studio_LogicHelperPlugin)
+Q_IMPORT_QML_PLUGIN(QtQuick_Studio_MultiTextPlugin)
+Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EventSimulatorPlugin)
+Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EventSystemPlugin)
+
+#endif
diff --git a/src/plugins/qmlprojectmanager/cmakegen/gencmakemodule.tpl b/src/plugins/qmlprojectmanager/cmakegen/gencmakemodule.tpl
new file mode 100644
index 0000000000..b81d253c90
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/cmakegen/gencmakemodule.tpl
@@ -0,0 +1,14 @@
+### This file is automatically generated by Qt Design Studio.
+### Do not change
+
+%3
+
+qt_add_library(%1 STATIC)
+qt6_add_qml_module(%1
+ URI "%2"
+ VERSION 1.0
+ RESOURCE_PREFIX "/qt/qml"
+%4
+)
+
+%5
diff --git a/src/plugins/qmlprojectmanager/cmakegen/gencmakeroot.tpl b/src/plugins/qmlprojectmanager/cmakegen/gencmakeroot.tpl
new file mode 100644
index 0000000000..a23a311c1a
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/cmakegen/gencmakeroot.tpl
@@ -0,0 +1,52 @@
+cmake_minimum_required(VERSION 3.21.1)
+
+option(LINK_INSIGHT "Link Qt Insight Tracker library" ON)
+option(BUILD_QDS_COMPONENTS "Build design studio components" ON)
+
+project(%1 LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml)
+set(QML_IMPORT_PATH ${QT_QML_OUTPUT_DIRECTORY}
+ CACHE STRING "Import paths for Qt Creator's code model"
+ FORCE
+)
+
+find_package(Qt6 6.2 REQUIRED COMPONENTS Core Gui Qml Quick)
+
+if (Qt6_VERSION VERSION_GREATER_EQUAL 6.3)
+ qt_standard_project_setup()
+endif()
+
+qt_add_executable(${CMAKE_PROJECT_NAME} %2)
+
+qt_add_resources(${CMAKE_PROJECT_NAME} "configuration"
+ PREFIX "/"
+%3
+)
+
+target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
+ Qt${QT_VERSION_MAJOR}::Core
+ Qt${QT_VERSION_MAJOR}::Gui
+ Qt${QT_VERSION_MAJOR}::Quick
+ Qt${QT_VERSION_MAJOR}::Qml
+)
+
+if (BUILD_QDS_COMPONENTS)
+ include(${CMAKE_CURRENT_SOURCE_DIR}/qmlcomponents OPTIONAL)
+endif()
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/qmlmodules)
+
+if (LINK_INSIGHT)
+ include(${CMAKE_CURRENT_SOURCE_DIR}/insight OPTIONAL)
+endif ()
+
+include(GNUInstallDirs)
+install(TARGETS ${CMAKE_PROJECT_NAME}
+ BUNDLE DESTINATION .
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+)
+
diff --git a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp b/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp
index 50d8596ae3..f46b508c8c 100644
--- a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp
+++ b/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp
@@ -16,6 +16,7 @@
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
+#include <qmlprojectmanager/buildsystem/qmlbuildsystem.h>
#include <qmlprojectmanager/qmlmainfileaspect.h>
#include <qmlprojectmanager/qmlproject.h>
#include <qmlprojectmanager/qmlprojectconstants.h>
@@ -85,16 +86,6 @@ enum ProjectDirectoryError {
const QString MENU_ITEM_GENERATE = Tr::tr("Generate CMake Build Files...");
-const QmlBuildSystem *getBuildSystem()
-{
- auto project = ProjectExplorer::ProjectManager::startupProject();
- if (project && project->activeTarget() && project->activeTarget()->buildSystem()) {
- return qobject_cast<QmlProjectManager::QmlBuildSystem *>(
- project->activeTarget()->buildSystem());
- }
- return nullptr;
-}
-
void generateMenuEntry(QObject *parent)
{
Core::ActionContainer *menu = Core::ActionManager::actionContainer(Core::Constants::M_FILE);
@@ -118,7 +109,7 @@ void generateMenuEntry(QObject *parent)
QObject::connect(ProjectExplorer::ProjectManager::instance(),
&ProjectExplorer::ProjectManager::startupProjectChanged,
[action]() {
- if (auto buildSystem = getBuildSystem())
+ if (auto buildSystem = QmlBuildSystem::getStartupBuildSystem())
action->setEnabled(!buildSystem->qtForMCUs());
});
}
@@ -284,7 +275,7 @@ const QString projectEnvironmentVariable(const QString &key)
{
QString value = {};
- if (auto buildSystem = getBuildSystem()) {
+ if (auto buildSystem = QmlBuildSystem::getStartupBuildSystem()) {
auto envItems = buildSystem->environment();
auto confEnv = std::find_if(envItems.begin(), envItems.end(), [key](EnvironmentItem &item) {
return item.name == key;
@@ -636,7 +627,7 @@ bool CmakeFileGenerator::generateMainCpp(const FilePath &dir)
bool envHeaderOk = true;
QString environment;
- if (auto buildSystem = getBuildSystem()) {
+ if (auto buildSystem = QmlBuildSystem::getStartupBuildSystem()) {
for (EnvironmentItem &envItem : buildSystem->environment()) {
QString key = envItem.name;
QString value = envItem.value;
diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp
index d2cac841a9..388169b375 100644
--- a/src/plugins/qmlprojectmanager/qmlproject.cpp
+++ b/src/plugins/qmlprojectmanager/qmlproject.cpp
@@ -28,7 +28,7 @@
#include <utils/algorithm.h>
#include <utils/infobar.h>
#include <utils/mimeconstants.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QDebug>
diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp
index cd2910ec7f..62abce5fe9 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp
+++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp
@@ -50,7 +50,7 @@
#include <utils/fileutils.h>
#include <utils/fsengine/fileiconprovider.h>
#include <utils/mimeconstants.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcsettings.h>
#include <QAction>
diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
index 77650c9dd8..5742b945a8 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
+++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
@@ -36,7 +36,7 @@
#include <utils/aspects.h>
#include <utils/environment.h>
#include <utils/fileutils.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinterface.h>
#include <utils/winutils.h>
diff --git a/src/plugins/qnx/qnxanalyzesupport.cpp b/src/plugins/qnx/qnxanalyzesupport.cpp
index fe87c15eaf..d21d0b44c0 100644
--- a/src/plugins/qnx/qnxanalyzesupport.cpp
+++ b/src/plugins/qnx/qnxanalyzesupport.cpp
@@ -9,7 +9,7 @@
#include <projectexplorer/devicesupport/deviceusedportsgatherer.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <qmldebug/qmldebugcommandlinearguments.h>
diff --git a/src/plugins/qnx/qnxdebugsupport.cpp b/src/plugins/qnx/qnxdebugsupport.cpp
index b2d923f2cb..27c26e269c 100644
--- a/src/plugins/qnx/qnxdebugsupport.cpp
+++ b/src/plugins/qnx/qnxdebugsupport.cpp
@@ -33,7 +33,7 @@
#include <utils/fileutils.h>
#include <utils/pathchooser.h>
#include <utils/portlist.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinfo.h>
#include <utils/qtcassert.h>
diff --git a/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp b/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp
index a134d25615..8b3baadb20 100644
--- a/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp
+++ b/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp
@@ -18,7 +18,7 @@
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
diff --git a/src/plugins/qnx/qnxdevice.cpp b/src/plugins/qnx/qnxdevice.cpp
index c810a3f286..4d04d69b42 100644
--- a/src/plugins/qnx/qnxdevice.cpp
+++ b/src/plugins/qnx/qnxdevice.cpp
@@ -20,7 +20,7 @@
#include <utils/port.h>
#include <utils/portlist.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/wizard.h>
diff --git a/src/plugins/qnx/qnxdevicetester.cpp b/src/plugins/qnx/qnxdevicetester.cpp
index f4a94f0efc..b2208d92c1 100644
--- a/src/plugins/qnx/qnxdevicetester.cpp
+++ b/src/plugins/qnx/qnxdevicetester.cpp
@@ -6,7 +6,7 @@
#include "qnxconstants.h"
#include "qnxtr.h"
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
using namespace Utils;
diff --git a/src/plugins/qnx/qnxutils.cpp b/src/plugins/qnx/qnxutils.cpp
index 634800fe99..6ab907b6e9 100644
--- a/src/plugins/qnx/qnxutils.cpp
+++ b/src/plugins/qnx/qnxutils.cpp
@@ -5,7 +5,7 @@
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/temporaryfile.h>
#include <QDebug>
diff --git a/src/plugins/qnx/slog2inforunner.cpp b/src/plugins/qnx/slog2inforunner.cpp
index 71264da1e7..999b73b82f 100644
--- a/src/plugins/qnx/slog2inforunner.cpp
+++ b/src/plugins/qnx/slog2inforunner.cpp
@@ -8,7 +8,7 @@
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/runconfigurationaspects.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QRegularExpression>
diff --git a/src/plugins/qtapplicationmanager/appmanagerinstallpackagestep.cpp b/src/plugins/qtapplicationmanager/appmanagerinstallpackagestep.cpp
index 15d2833fa2..49ba78f669 100644
--- a/src/plugins/qtapplicationmanager/appmanagerinstallpackagestep.cpp
+++ b/src/plugins/qtapplicationmanager/appmanagerinstallpackagestep.cpp
@@ -22,7 +22,7 @@
#include <projectexplorer/target.h>
#include <projectexplorer/runconfigurationaspects.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
using namespace ProjectExplorer;
using namespace Utils;
diff --git a/src/plugins/qtapplicationmanager/appmanagerruncontrol.cpp b/src/plugins/qtapplicationmanager/appmanagerruncontrol.cpp
index 9dfa85de3c..96c8d77e4b 100644
--- a/src/plugins/qtapplicationmanager/appmanagerruncontrol.cpp
+++ b/src/plugins/qtapplicationmanager/appmanagerruncontrol.cpp
@@ -30,7 +30,7 @@
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitaspect.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
using namespace ProjectExplorer;
diff --git a/src/plugins/qtsupport/baseqtversion.cpp b/src/plugins/qtsupport/baseqtversion.cpp
index ae0ed49e06..f94ff8ccf2 100644
--- a/src/plugins/qtsupport/baseqtversion.cpp
+++ b/src/plugins/qtsupport/baseqtversion.cpp
@@ -33,7 +33,7 @@
#include <utils/fileinprojectfinder.h>
#include <utils/hostosinfo.h>
#include <utils/macroexpander.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
#include <utils/winutils.h>
diff --git a/src/plugins/qtsupport/externaleditors.cpp b/src/plugins/qtsupport/externaleditors.cpp
index 47e8d21e84..4009025b8d 100644
--- a/src/plugins/qtsupport/externaleditors.cpp
+++ b/src/plugins/qtsupport/externaleditors.cpp
@@ -19,7 +19,7 @@
#include <utils/filepath.h>
#include <utils/hostosinfo.h>
#include <utils/mimeconstants.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QDebug>
diff --git a/src/plugins/qtsupport/qtsupportplugin.cpp b/src/plugins/qtsupport/qtsupportplugin.cpp
index c8a7b7cf1f..96f1e2a7b8 100644
--- a/src/plugins/qtsupport/qtsupportplugin.cpp
+++ b/src/plugins/qtsupport/qtsupportplugin.cpp
@@ -35,7 +35,7 @@
#include <utils/filepath.h>
#include <utils/infobar.h>
#include <utils/macroexpander.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QInputDialog>
diff --git a/src/plugins/qtsupport/qtversionmanager.cpp b/src/plugins/qtsupport/qtversionmanager.cpp
index 746f4f8c5e..c269eca3cc 100644
--- a/src/plugins/qtsupport/qtversionmanager.cpp
+++ b/src/plugins/qtsupport/qtversionmanager.cpp
@@ -21,7 +21,7 @@
#include <utils/filesystemwatcher.h>
#include <utils/hostosinfo.h>
#include <utils/persistentsettings.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <nanotrace/nanotrace.h>
diff --git a/src/plugins/qtsupport/uicgenerator.cpp b/src/plugins/qtsupport/uicgenerator.cpp
index 74b8355af9..e79788f174 100644
--- a/src/plugins/qtsupport/uicgenerator.cpp
+++ b/src/plugins/qtsupport/uicgenerator.cpp
@@ -11,7 +11,7 @@
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/target.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QDateTime>
diff --git a/src/plugins/remotelinux/customcommanddeploystep.cpp b/src/plugins/remotelinux/customcommanddeploystep.cpp
index 211f48b4f4..97826fde5e 100644
--- a/src/plugins/remotelinux/customcommanddeploystep.cpp
+++ b/src/plugins/remotelinux/customcommanddeploystep.cpp
@@ -11,7 +11,7 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/runconfigurationaspects.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
using namespace ProjectExplorer;
using namespace Tasking;
diff --git a/src/plugins/remotelinux/filesystemaccess_test.cpp b/src/plugins/remotelinux/filesystemaccess_test.cpp
index d23ac3f3a1..0f2e5bd937 100644
--- a/src/plugins/remotelinux/filesystemaccess_test.cpp
+++ b/src/plugins/remotelinux/filesystemaccess_test.cpp
@@ -11,7 +11,7 @@
#include <utils/filepath.h>
#include <utils/filestreamer.h>
#include <utils/filestreamermanager.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinterface.h>
#include <utils/scopedtimer.h>
diff --git a/src/plugins/remotelinux/genericdeploystep.cpp b/src/plugins/remotelinux/genericdeploystep.cpp
index 6071c4c8ca..3d1f0e5e3a 100644
--- a/src/plugins/remotelinux/genericdeploystep.cpp
+++ b/src/plugins/remotelinux/genericdeploystep.cpp
@@ -19,7 +19,7 @@
#include <utils/algorithm.h>
#include <utils/async.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinterface.h>
using namespace ProjectExplorer;
diff --git a/src/plugins/remotelinux/genericdirectuploadstep.cpp b/src/plugins/remotelinux/genericdirectuploadstep.cpp
index 9817ce3f0a..383722873b 100644
--- a/src/plugins/remotelinux/genericdirectuploadstep.cpp
+++ b/src/plugins/remotelinux/genericdirectuploadstep.cpp
@@ -15,7 +15,7 @@
#include <projectexplorer/target.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp
index 2fc49ca8e3..31195e8cfa 100644
--- a/src/plugins/remotelinux/linuxdevice.cpp
+++ b/src/plugins/remotelinux/linuxdevice.cpp
@@ -31,7 +31,7 @@
#include <utils/infobar.h>
#include <utils/port.h>
#include <utils/portlist.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinfo.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
diff --git a/src/plugins/remotelinux/linuxdevicetester.cpp b/src/plugins/remotelinux/linuxdevicetester.cpp
index 3281d32c49..aa79244c73 100644
--- a/src/plugins/remotelinux/linuxdevicetester.cpp
+++ b/src/plugins/remotelinux/linuxdevicetester.cpp
@@ -5,6 +5,7 @@
#include "linuxdevice.h"
#include "remotelinuxtr.h"
+#include "utils/async.h"
#include <extensionsystem/pluginmanager.h>
@@ -15,8 +16,7 @@
#include <solutions/tasking/tasktreerunner.h>
#include <utils/algorithm.h>
-#include <utils/async.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
diff --git a/src/plugins/remotelinux/makeinstallstep.cpp b/src/plugins/remotelinux/makeinstallstep.cpp
index 08e65e6126..38edbb6791 100644
--- a/src/plugins/remotelinux/makeinstallstep.cpp
+++ b/src/plugins/remotelinux/makeinstallstep.cpp
@@ -21,7 +21,7 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QSet>
diff --git a/src/plugins/remotelinux/publickeydeploymentdialog.cpp b/src/plugins/remotelinux/publickeydeploymentdialog.cpp
index 6d0ead4ac6..d3ade682cb 100644
--- a/src/plugins/remotelinux/publickeydeploymentdialog.cpp
+++ b/src/plugins/remotelinux/publickeydeploymentdialog.cpp
@@ -11,7 +11,7 @@
#include <utils/filepath.h>
#include <utils/fileutils.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/theme/theme.h>
diff --git a/src/plugins/remotelinux/remotelinuxsignaloperation.cpp b/src/plugins/remotelinux/remotelinuxsignaloperation.cpp
index a4cd369ca4..95e68a6485 100644
--- a/src/plugins/remotelinux/remotelinuxsignaloperation.cpp
+++ b/src/plugins/remotelinux/remotelinuxsignaloperation.cpp
@@ -7,7 +7,7 @@
#include <utils/commandline.h>
#include <utils/fileutils.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
using namespace ProjectExplorer;
diff --git a/src/plugins/remotelinux/sshkeycreationdialog.cpp b/src/plugins/remotelinux/sshkeycreationdialog.cpp
index ca00cca76a..03c6f7a543 100644
--- a/src/plugins/remotelinux/sshkeycreationdialog.cpp
+++ b/src/plugins/remotelinux/sshkeycreationdialog.cpp
@@ -10,7 +10,7 @@
#include <utils/fileutils.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QApplication>
#include <QComboBox>
diff --git a/src/plugins/remotelinux/tarpackagedeploystep.cpp b/src/plugins/remotelinux/tarpackagedeploystep.cpp
index aa8fe3db30..5f79272f4a 100644
--- a/src/plugins/remotelinux/tarpackagedeploystep.cpp
+++ b/src/plugins/remotelinux/tarpackagedeploystep.cpp
@@ -12,7 +12,7 @@
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinterface.h>
using namespace ProjectExplorer;
diff --git a/src/plugins/screenrecorder/cropandtrim.cpp b/src/plugins/screenrecorder/cropandtrim.cpp
index ff60472fed..4f65469e62 100644
--- a/src/plugins/screenrecorder/cropandtrim.cpp
+++ b/src/plugins/screenrecorder/cropandtrim.cpp
@@ -9,7 +9,7 @@
#include <utils/fileutils.h>
#include <utils/layoutbuilder.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcsettings.h>
#include <utils/styledbar.h>
#include <utils/stylehelper.h>
diff --git a/src/plugins/screenrecorder/export.cpp b/src/plugins/screenrecorder/export.cpp
index 0c9b67d2a3..15f38993ad 100644
--- a/src/plugins/screenrecorder/export.cpp
+++ b/src/plugins/screenrecorder/export.cpp
@@ -10,7 +10,7 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/layoutbuilder.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/styledbar.h>
#include <utils/utilsicons.h>
diff --git a/src/plugins/screenrecorder/ffmpegutils.cpp b/src/plugins/screenrecorder/ffmpegutils.cpp
index 96223c3781..fa0a6f45ae 100644
--- a/src/plugins/screenrecorder/ffmpegutils.cpp
+++ b/src/plugins/screenrecorder/ffmpegutils.cpp
@@ -12,7 +12,7 @@
#endif // WITH_TESTS
#include <utils/layoutbuilder.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/utilsicons.h>
#include <coreplugin/messagemanager.h>
diff --git a/src/plugins/screenrecorder/record.cpp b/src/plugins/screenrecorder/record.cpp
index 7f14d3dc0d..aba61ed2eb 100644
--- a/src/plugins/screenrecorder/record.cpp
+++ b/src/plugins/screenrecorder/record.cpp
@@ -11,7 +11,7 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/layoutbuilder.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcsettings.h>
#include <utils/styledbar.h>
#include <utils/utilsicons.h>
diff --git a/src/plugins/silversearcher/findinfilessilversearcher.cpp b/src/plugins/silversearcher/findinfilessilversearcher.cpp
index a6f79ce609..cb290e046d 100644
--- a/src/plugins/silversearcher/findinfilessilversearcher.cpp
+++ b/src/plugins/silversearcher/findinfilessilversearcher.cpp
@@ -9,7 +9,7 @@
#include <texteditor/findinfiles.h>
#include <utils/async.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/qtcsettings.h>
diff --git a/src/plugins/squish/objectsmapdocument.cpp b/src/plugins/squish/objectsmapdocument.cpp
index 20c77d9cce..ba5f70b0cd 100644
--- a/src/plugins/squish/objectsmapdocument.cpp
+++ b/src/plugins/squish/objectsmapdocument.cpp
@@ -9,7 +9,7 @@
#include "squishtr.h"
#include <utils/fileutils.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QtCore5Compat/QTextCodec>
diff --git a/src/plugins/squish/squishprocessbase.h b/src/plugins/squish/squishprocessbase.h
index 6582c95062..d8af2e9d4f 100644
--- a/src/plugins/squish/squishprocessbase.h
+++ b/src/plugins/squish/squishprocessbase.h
@@ -5,7 +5,7 @@
#include "squishconstants.h"
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QObject>
diff --git a/src/plugins/squish/squishtools.h b/src/plugins/squish/squishtools.h
index 223bfb7c23..87b45d5af0 100644
--- a/src/plugins/squish/squishtools.h
+++ b/src/plugins/squish/squishtools.h
@@ -9,7 +9,7 @@
#include "suiteconf.h"
#include <utils/environment.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QObject>
#include <QStringList>
diff --git a/src/plugins/subversion/subversionclient.cpp b/src/plugins/subversion/subversionclient.cpp
index e30abd5bc8..e69809143d 100644
--- a/src/plugins/subversion/subversionclient.cpp
+++ b/src/plugins/subversion/subversionclient.cpp
@@ -12,7 +12,7 @@
#include <utils/commandline.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <vcsbase/vcsbaseconstants.h>
#include <vcsbase/vcsbasediffeditorcontroller.h>
diff --git a/src/plugins/terminal/shellintegration.h b/src/plugins/terminal/shellintegration.h
index 4f1215fad9..905b088b38 100644
--- a/src/plugins/terminal/shellintegration.h
+++ b/src/plugins/terminal/shellintegration.h
@@ -5,7 +5,7 @@
#pragma once
#include <utils/commandline.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <solutions/terminal/surfaceintegration.h>
diff --git a/src/plugins/terminal/terminalwidget.h b/src/plugins/terminal/terminalwidget.h
index acba039ba9..351ae45720 100644
--- a/src/plugins/terminal/terminalwidget.h
+++ b/src/plugins/terminal/terminalwidget.h
@@ -13,7 +13,7 @@
#include <coreplugin/terminal/searchableterminal.h>
#include <utils/link.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/terminalhooks.h>
#include <QFutureWatcher>
diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp
index 399e275fe4..ce6d2f4999 100644
--- a/src/plugins/texteditor/basefilefind.cpp
+++ b/src/plugins/texteditor/basefilefind.cpp
@@ -21,7 +21,7 @@
#include <utils/algorithm.h>
#include <utils/fadingindicator.h>
#include <utils/futuresynchronizer.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QComboBox>
diff --git a/src/plugins/texteditor/formattexteditor.cpp b/src/plugins/texteditor/formattexteditor.cpp
index 14f3d4e4e7..8a0fac2cd4 100644
--- a/src/plugins/texteditor/formattexteditor.cpp
+++ b/src/plugins/texteditor/formattexteditor.cpp
@@ -12,7 +12,7 @@
#include <utils/async.h>
#include <utils/differ.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/temporarydirectory.h>
#include <utils/textutils.h>
diff --git a/src/plugins/updateinfo/updateinfoplugin.cpp b/src/plugins/updateinfo/updateinfoplugin.cpp
index a23d6c7cfc..98e1160f3f 100644
--- a/src/plugins/updateinfo/updateinfoplugin.cpp
+++ b/src/plugins/updateinfo/updateinfoplugin.cpp
@@ -16,7 +16,7 @@
#include <solutions/tasking/tasktreerunner.h>
#include <utils/infobar.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QDate>
diff --git a/src/plugins/valgrind/callgrindengine.cpp b/src/plugins/valgrind/callgrindengine.cpp
index 06ff38f5ab..8422bb0176 100644
--- a/src/plugins/valgrind/callgrindengine.cpp
+++ b/src/plugins/valgrind/callgrindengine.cpp
@@ -10,7 +10,7 @@
#include <utils/filepath.h>
#include <utils/filestreamermanager.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/temporaryfile.h>
diff --git a/src/plugins/valgrind/callgrindengine.h b/src/plugins/valgrind/callgrindengine.h
index 2379e91737..c0e5ce2a8b 100644
--- a/src/plugins/valgrind/callgrindengine.h
+++ b/src/plugins/valgrind/callgrindengine.h
@@ -8,7 +8,7 @@
#include "callgrind/callgrindparsedata.h"
#include "callgrind/callgrindparser.h"
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinterface.h>
namespace Valgrind::Internal {
diff --git a/src/plugins/valgrind/callgrindtool.cpp b/src/plugins/valgrind/callgrindtool.cpp
index 9718193279..75d8dfac9d 100644
--- a/src/plugins/valgrind/callgrindtool.cpp
+++ b/src/plugins/valgrind/callgrindtool.cpp
@@ -46,7 +46,7 @@
#include <texteditor/texteditor.h>
#include <texteditor/textdocument.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
diff --git a/src/plugins/valgrind/memchecktool.cpp b/src/plugins/valgrind/memchecktool.cpp
index 742d648caf..8ae89a3193 100644
--- a/src/plugins/valgrind/memchecktool.cpp
+++ b/src/plugins/valgrind/memchecktool.cpp
@@ -45,7 +45,7 @@
#include <utils/checkablemessagebox.h>
#include <utils/pathchooser.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
diff --git a/src/plugins/valgrind/valgrindmemcheckparsertest.cpp b/src/plugins/valgrind/valgrindmemcheckparsertest.cpp
index 66824f718c..44cac3712c 100644
--- a/src/plugins/valgrind/valgrindmemcheckparsertest.cpp
+++ b/src/plugins/valgrind/valgrindmemcheckparsertest.cpp
@@ -11,7 +11,7 @@
#include "xmlprotocol/status.h"
#include "xmlprotocol/suppression.h"
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinterface.h>
#include <QFileInfo>
diff --git a/src/plugins/valgrind/valgrindprocess.cpp b/src/plugins/valgrind/valgrindprocess.cpp
index d543f3e66a..b86eb0cf7a 100644
--- a/src/plugins/valgrind/valgrindprocess.cpp
+++ b/src/plugins/valgrind/valgrindprocess.cpp
@@ -9,7 +9,7 @@
#include <solutions/tasking/barrier.h>
#include <solutions/tasking/tasktreerunner.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
diff --git a/src/plugins/vcsbase/vcsbaseclient.cpp b/src/plugins/vcsbase/vcsbaseclient.cpp
index cd937ea345..6a36d628bd 100644
--- a/src/plugins/vcsbase/vcsbaseclient.cpp
+++ b/src/plugins/vcsbase/vcsbaseclient.cpp
@@ -20,7 +20,7 @@
#include <utils/commandline.h>
#include <utils/environment.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QDebug>
diff --git a/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp b/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp
index cbd33f48fc..4ba6baba34 100644
--- a/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp
+++ b/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp
@@ -8,7 +8,7 @@
#include <utils/async.h>
#include <utils/environment.h>
#include <utils/futuresynchronizer.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
using namespace DiffEditor;
using namespace Tasking;
diff --git a/src/plugins/vcsbase/vcsbaseplugin.cpp b/src/plugins/vcsbase/vcsbaseplugin.cpp
index b506f0ed38..a66596d49f 100644
--- a/src/plugins/vcsbase/vcsbaseplugin.cpp
+++ b/src/plugins/vcsbase/vcsbaseplugin.cpp
@@ -17,7 +17,7 @@
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QAction>
diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp
index 4c29da91f2..7a52cc9609 100644
--- a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp
+++ b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp
@@ -36,7 +36,7 @@
#include <utils/completingtextedit.h>
#include <utils/fileutils.h>
#include <utils/icon.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/temporarydirectory.h>
#include <utils/theme/theme.h>
diff --git a/src/plugins/vcsbase/vcscommand.cpp b/src/plugins/vcsbase/vcscommand.cpp
index f1995b2d84..a10586458e 100644
--- a/src/plugins/vcsbase/vcscommand.cpp
+++ b/src/plugins/vcsbase/vcscommand.cpp
@@ -10,7 +10,7 @@
#include <utils/environment.h>
#include <utils/globalfilechangeblocker.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <utils/threadutils.h>
diff --git a/src/plugins/vcsbase/vcsoutputwindow.cpp b/src/plugins/vcsbase/vcsoutputwindow.cpp
index e8738907b5..747b409b5d 100644
--- a/src/plugins/vcsbase/vcsoutputwindow.cpp
+++ b/src/plugins/vcsbase/vcsoutputwindow.cpp
@@ -14,7 +14,7 @@
#include <texteditor/texteditorsettings.h>
#include <utils/filepath.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/theme/theme.h>
#include <QAction>
diff --git a/src/plugins/webassembly/webassemblyemsdk.cpp b/src/plugins/webassembly/webassemblyemsdk.cpp
index 12c4d64967..6541200eaa 100644
--- a/src/plugins/webassembly/webassemblyemsdk.cpp
+++ b/src/plugins/webassembly/webassemblyemsdk.cpp
@@ -7,7 +7,7 @@
#include <coreplugin/settingsdatabase.h>
#include <utils/environment.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/hostosinfo.h>
#include <QCache>
diff --git a/src/plugins/webassembly/webassemblyrunconfiguration.cpp b/src/plugins/webassembly/webassemblyrunconfiguration.cpp
index e78222c0a6..25ca374034 100644
--- a/src/plugins/webassembly/webassemblyrunconfiguration.cpp
+++ b/src/plugins/webassembly/webassemblyrunconfiguration.cpp
@@ -18,7 +18,7 @@
#include <projectexplorer/target.h>
#include <utils/layoutbuilder.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QComboBox>
diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt
index 9244a9454d..b9f1a1544c 100644
--- a/src/tools/qml2puppet/CMakeLists.txt
+++ b/src/tools/qml2puppet/CMakeLists.txt
@@ -1,7 +1,11 @@
cmake_minimum_required(VERSION 3.16)
+# standalone build
if (NOT QT_CREATOR_API_DEFINED)
- # standalone build
+ option(BUILD_DESIGNSTUDIO "Build puppet with Qt Design Studio version in the binary name." OFF)
+ if (BUILD_DESIGNSTUDIO)
+ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../dist/branding/qtdesignstudio")
+ endif()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake")
project(qml2puppet)
@@ -9,7 +13,7 @@ if (NOT QT_CREATOR_API_DEFINED)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
- set(CMAKE_CXX_STANDARD 17)
+ set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
@@ -102,6 +106,7 @@ extend_qtc_executable(qml2puppet
selectionboxgeometry.cpp selectionboxgeometry.h
linegeometry.cpp linegeometry.h
icongizmoimageprovider.cpp icongizmoimageprovider.h
+ boxgeometry.cpp boxgeometry.h
)
find_package(Qt6 COMPONENTS Quick3DAssetImport QUIET)
diff --git a/src/tools/qml2puppet/editor3d_qt6.qrc b/src/tools/qml2puppet/editor3d_qt6.qrc
index 59423beb91..2913fbe15e 100644
--- a/src/tools/qml2puppet/editor3d_qt6.qrc
+++ b/src/tools/qml2puppet/editor3d_qt6.qrc
@@ -47,6 +47,7 @@
<file>mockfiles/qt6/PlanarDraggable.qml</file>
<file>mockfiles/qt6/PlanarMoveHandle.qml</file>
<file>mockfiles/qt6/PlanarScaleHandle.qml</file>
+ <file>mockfiles/qt6/ReflectionProbeBox.qml</file>
<file>mockfiles/qt6/RotateGizmo.qml</file>
<file>mockfiles/qt6/RotateRing.qml</file>
<file>mockfiles/qt6/ScaleGizmo.qml</file>
diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml
index cb9276305a..446d575683 100644
--- a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml
+++ b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml
@@ -26,8 +26,9 @@ Item {
readonly property vector3d _defaultCameraPosition: Qt.vector3d(0, 600, 600)
readonly property vector3d _defaultCameraRotation: Qt.vector3d(-45, 0, 0)
readonly property real _defaultCameraLookAtDistance: _defaultCameraPosition.length()
- readonly property real _keyPanAmount: 5
+ readonly property real _keyPanAmount: _generalHelper.cameraSpeed
property bool ignoreToolState: false
+ property bool flyMode: viewRoot.flyMode
z: 10
anchors.fill: parent
@@ -150,6 +151,58 @@ Item {
_lookAtPoint, _zoomFactor, true);
}
+ function rotateCamera(angles)
+ {
+ cameraCtrl._lookAtPoint = _generalHelper.rotateCamera(camera, angles, _lookAtPoint);
+ }
+
+ function moveCamera(moveVec)
+ {
+ cameraCtrl._lookAtPoint = _generalHelper.moveCamera(camera, _lookAtPoint, _zoomFactor,
+ moveVec);
+ }
+
+ function getMoveVectorForKey(key) {
+ if (flyMode) {
+ switch (key) {
+ case Qt.Key_A:
+ case Qt.Key_Left:
+ return Qt.vector3d(_keyPanAmount, 0, 0);
+ case Qt.Key_D:
+ case Qt.Key_Right:
+ return Qt.vector3d(-_keyPanAmount, 0, 0);
+ case Qt.Key_E:
+ case Qt.Key_PageUp:
+ return Qt.vector3d(0, _keyPanAmount, 0);
+ case Qt.Key_Q:
+ case Qt.Key_PageDown:
+ return Qt.vector3d(0, -_keyPanAmount, 0);
+ case Qt.Key_W:
+ case Qt.Key_Up:
+ return Qt.vector3d(0, 0, _keyPanAmount);
+ case Qt.Key_S:
+ case Qt.Key_Down:
+ return Qt.vector3d(0, 0, -_keyPanAmount);
+ default:
+ break;
+ }
+ } else {
+ switch (key) {
+ case Qt.Key_Left:
+ return Qt.vector3d(_keyPanAmount, 0, 0);
+ case Qt.Key_Right:
+ return Qt.vector3d(-_keyPanAmount, 0, 0);
+ case Qt.Key_Up:
+ return Qt.vector3d(0, _keyPanAmount, 0);
+ case Qt.Key_Down:
+ return Qt.vector3d(0, -_keyPanAmount, 0);
+ default:
+ break;
+ }
+ }
+ return Qt.vector3d(0, 0, 0);
+ }
+
onCameraChanged: {
if (camera && _prevCamera) {
// Reset zoom on previous camera to ensure it's properties are good to copy to new cam
@@ -166,6 +219,25 @@ Item {
_prevCamera = camera;
}
+ onFlyModeChanged: {
+ if (cameraCtrl._dragging) {
+ cameraCtrl._dragging = false;
+ cameraCtrl.storeCameraState(0);
+ }
+ _generalHelper.stopAllCameraMoves()
+ }
+
+ Connections {
+ target: _generalHelper
+ enabled: viewRoot.activeSplit === cameraCtrl.splitId
+ function onRequestCameraMove(camera, moveVec) {
+ if (camera === cameraCtrl.camera) {
+ cameraCtrl.moveCamera(moveVec);
+ _generalHelper.requestRender();
+ }
+ }
+ }
+
MouseArea {
id: mouseHandler
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
@@ -190,6 +262,8 @@ Item {
}
}
onPressed: (mouse) => {
+ if (cameraCtrl.flyMode)
+ return;
viewRoot.activeSplit = cameraCtrl.splitId
if (cameraCtrl.camera && mouse.modifiers === Qt.AltModifier) {
cameraCtrl._dragging = true;
@@ -215,6 +289,8 @@ Item {
onCanceled: handleRelease()
onWheel: (wheel) => {
+ if (cameraCtrl.flyMode && cameraCtrl.splitId !== viewRoot.activeSplit)
+ return;
viewRoot.activeSplit = cameraCtrl.splitId
if (cameraCtrl.camera) {
// Empirically determined divisor for nice zoom
@@ -225,33 +301,13 @@ Item {
}
Keys.onPressed: (event) => {
- var pressPoint = Qt.vector3d(view3d.width / 2, view3d.height / 2, 0);
- var currentPoint;
-
- switch (event.key) {
- case Qt.Key_Left:
- currentPoint = pressPoint.plus(Qt.vector3d(_keyPanAmount, 0, 0));
- break;
- case Qt.Key_Right:
- currentPoint = pressPoint.plus(Qt.vector3d(-_keyPanAmount, 0, 0));
- break;
- case Qt.Key_Up:
- currentPoint = pressPoint.plus(Qt.vector3d(0, _keyPanAmount, 0));
- break;
- case Qt.Key_Down:
- currentPoint = pressPoint.plus(Qt.vector3d(0, -_keyPanAmount, 0));
- break;
- default:
- break;
- }
+ event.accepted = true;
+ _generalHelper.startCameraMove(cameraCtrl.camera, cameraCtrl.getMoveVectorForKey(event.key));
+ }
- if (currentPoint) {
- _lookAtPoint = _generalHelper.panCamera(
- camera, cameraCtrl.camera.sceneTransform,
- cameraCtrl.camera.position, _lookAtPoint,
- pressPoint, currentPoint, _zoomFactor);
- event.accepted = true;
- }
+ Keys.onReleased: (event) => {
+ event.accepted = true;
+ _generalHelper.stopCameraMove(cameraCtrl.getMoveVectorForKey(event.key));
}
OriginGizmo {
diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml
index f526f01e2d..23fc1c6a78 100644
--- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml
+++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml
@@ -37,6 +37,7 @@ Item {
property color gridColor: "#cccccc"
property bool syncEnvBackground: false
property bool splitView: false
+ property bool flyMode: false
enum SelectionMode { Item, Group }
enum TransformMode { Move, Rotate, Scale }
@@ -104,11 +105,13 @@ Item {
"cameraLookAt": cameraControls[i]._lookAtPoint,
"z": 1,
"sceneEnv.debugSettings.materialOverride": materialOverrides[i],
- "sceneEnv.debugSettings.wireframeEnabled": showWireframes[i]});
+ "sceneEnv.debugSettings.wireframeEnabled": showWireframes[i],
+ "selectedNode": selectedNode});
editViews[i].usePerspective = Qt.binding(function() {return usePerspective;});
editViews[i].showSceneLight = Qt.binding(function() {return showEditLight;});
editViews[i].showGrid = Qt.binding(function() {return showGrid;});
editViews[i].gridColor = Qt.binding(function() {return gridColor;});
+ editViews[i].selectedNode = Qt.binding(function() {return selectedNode;});
}
editViews[0].cameraLookAt = Qt.binding(function() {return cameraControl0._lookAtPoint;});
editViews[1].cameraLookAt = Qt.binding(function() {return cameraControl1._lookAtPoint;});
@@ -127,6 +130,7 @@ Item {
selectionBoxCount = 0;
editViewsChanged();
+ cameraControls[activeSplit].forceActiveFocus();
return true;
}
return false;
@@ -349,6 +353,11 @@ Item {
cameraControls[i].restoreDefaultState();
}
+ if ("flyMode" in toolStates)
+ flyMode = toolStates.flyMode;
+ else if (resetToDefault)
+ flyMode = false;
+
if ("splitView" in toolStates)
splitView = toolStates.splitView;
else if (resetToDefault)
@@ -596,6 +605,16 @@ Item {
return activeOverlayView.gizmoAt(splitPoint.x, splitPoint.y);
}
+ function rotateEditCamera(angles)
+ {
+ cameraControls[activeSplit].rotateCamera(angles);
+ }
+
+ function moveEditCamera(amounts)
+ {
+ cameraControls[activeSplit].moveCamera(amounts);
+ }
+
Component.onCompleted: {
createEditViews();
selectObjects([]);
@@ -820,6 +839,9 @@ Item {
property bool initialMoveBlock: false
onPressed: (mouse) => {
+ if (viewRoot.flyMode)
+ return;
+
viewRoot.updateActiveSplit(mouse.x, mouse.y);
let splitPoint = viewRoot.resolveSplitPoint(mouse.x, mouse.y);
diff --git a/src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml b/src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml
index c82303d779..d127e99d74 100644
--- a/src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml
+++ b/src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml
@@ -1,9 +1,9 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-import QtQuick 6.0
-import QtQuick3D 6.0
-import GridGeometry 1.0
+import QtQuick
+import QtQuick3D
+import GridGeometry
Node {
id: grid
@@ -74,6 +74,10 @@ Node {
name: "3D Edit View Helper Grid"
}
+ castsShadows: false
+ receivesShadows: false
+ castsReflections: false
+
materials: [
GridMaterial {
id: mainGridMaterial
@@ -94,6 +98,10 @@ Node {
name: "3D Edit View Helper Grid subdivisions"
}
+ castsShadows: false
+ receivesShadows: false
+ castsReflections: false
+
materials: [
GridMaterial {
id: subGridMaterial
@@ -113,6 +121,11 @@ Node {
isCenterLine: true
name: "3D Edit View Helper Grid Z Axis"
}
+
+ castsShadows: false
+ receivesShadows: false
+ castsReflections: false
+
materials: [
GridMaterial {
id: vCenterLineMaterial
@@ -132,6 +145,11 @@ Node {
isCenterLine: true
name: "3D Edit View Helper Grid X Axis"
}
+
+ castsShadows: false
+ receivesShadows: false
+ castsReflections: false
+
materials: [
GridMaterial {
id: hCenterLineMaterial
diff --git a/src/tools/qml2puppet/mockfiles/qt6/ReflectionProbeBox.qml b/src/tools/qml2puppet/mockfiles/qt6/ReflectionProbeBox.qml
new file mode 100644
index 0000000000..63d4fb8811
--- /dev/null
+++ b/src/tools/qml2puppet/mockfiles/qt6/ReflectionProbeBox.qml
@@ -0,0 +1,39 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import QtQuick
+import QtQuick3D
+import BoxGeometry
+
+Model {
+ id: reflectionProbeGizmo
+
+ property Node selectedNode
+
+ readonly property bool _edit3dLocked: true // Make this non-pickable
+ readonly property bool _isProbe: selectedNode instanceof ReflectionProbe
+
+ visible: _isProbe
+ scale: _isProbe ? Qt.vector3d(selectedNode.boxSize.x,
+ selectedNode.boxSize.y,
+ selectedNode.boxSize.z)
+ : Qt.vector3d(1, 1, 1)
+
+ geometry: BoxGeometry {}
+
+ castsShadows: false
+ receivesShadows: false
+ castsReflections: false
+
+ position: _isProbe ? selectedNode.scenePosition.plus(selectedNode.boxOffset)
+ : Qt.vector3d(0, 0, 0)
+
+ materials: boxMaterial
+
+ PrincipledMaterial {
+ id: boxMaterial
+ baseColor: "#03fcdb"
+ lighting: DefaultMaterial.NoLighting
+ cullMode: Material.NoCulling
+ }
+}
diff --git a/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml
index 6b2cda1398..1de1cebd14 100644
--- a/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml
+++ b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml
@@ -1,8 +1,8 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-import QtQuick 6.0
-import QtQuick3D 6.0
+import QtQuick
+import QtQuick3D
View3D {
id: sceneView
@@ -17,6 +17,7 @@ View3D {
property alias sceneEnv: sceneEnv
property vector3d cameraLookAt
property var selectionBoxes: []
+ property Node selectedNode
// Measuring the distance from camera to lookAt plus the distance of lookAt from grid plane
// gives a reasonable grid spacing in most cases while keeping spacing constant when
@@ -79,6 +80,11 @@ View3D {
linearFade: 0
}
+ ReflectionProbeBox {
+ id: reflectionProbeBox
+ selectedNode: sceneView.selectedNode
+ }
+
// Initial camera position and rotation should be such that they look at origin.
// Otherwise EditCameraController._lookAtPoint needs to be initialized to correct
// point.
diff --git a/src/tools/qml2puppet/mockfiles/qt6/SelectionBox.qml b/src/tools/qml2puppet/mockfiles/qt6/SelectionBox.qml
index fe6cc0af37..9381bfbd72 100644
--- a/src/tools/qml2puppet/mockfiles/qt6/SelectionBox.qml
+++ b/src/tools/qml2puppet/mockfiles/qt6/SelectionBox.qml
@@ -1,9 +1,9 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-import QtQuick 6.0
-import QtQuick3D 6.0
-import SelectionBoxGeometry 1.0
+import QtQuick
+import QtQuick3D
+import SelectionBoxGeometry
Node {
id: selectionBox
@@ -36,6 +36,7 @@ Node {
castsShadows: false
receivesShadows: false
+ castsReflections: false
DefaultMaterial {
id: boxMaterial
diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/boxgeometry.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/boxgeometry.cpp
new file mode 100644
index 0000000000..e0465134e9
--- /dev/null
+++ b/src/tools/qml2puppet/qml2puppet/editor3d/boxgeometry.cpp
@@ -0,0 +1,103 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifdef QUICK3D_MODULE
+
+#include "boxgeometry.h"
+
+namespace QmlDesigner::Internal {
+
+BoxGeometry::BoxGeometry()
+ : GeometryBase()
+{
+}
+
+BoxGeometry::~BoxGeometry()
+{
+}
+
+QVector3D BoxGeometry::extent() const
+{
+ return m_extent;
+}
+
+void BoxGeometry::setExtent(const QVector3D &extent)
+{
+ if (m_extent == extent)
+ return;
+
+ m_extent = extent;
+
+ emit extentChanged();
+ updateGeometry();
+}
+
+void BoxGeometry::doUpdateGeometry()
+{
+ GeometryBase::doUpdateGeometry();
+
+ QByteArray vertexData;
+ QByteArray indexData;
+ const QVector3D bounds = m_extent / 2.f;
+
+ fillVertexData(vertexData, indexData, bounds);
+
+ addAttribute(QQuick3DGeometry::Attribute::IndexSemantic, 0,
+ QQuick3DGeometry::Attribute::U16Type);
+
+ setVertexData(vertexData);
+ setIndexData(indexData);
+ setBounds(-bounds, bounds);
+}
+
+void BoxGeometry::fillVertexData(QByteArray &vertexData, QByteArray &indexData,
+ const QVector3D &bounds)
+{
+ int vertexSize = 8 * 3 * int(sizeof(float));
+ int indexSize = 12 * 2 * int(sizeof(quint16));
+
+ vertexData.resize(vertexSize);
+ indexData.resize(indexSize);
+
+ auto dataPtr = reinterpret_cast<float *>(vertexData.data());
+ auto indexPtr = reinterpret_cast<quint16 *>(indexData.data());
+
+ QVector3D corners[8];
+
+ corners[0] = QVector3D( bounds.x(), bounds.y(), bounds.z());
+ corners[1] = QVector3D(-bounds.x(), bounds.y(), bounds.z());
+ corners[2] = QVector3D(-bounds.x(), -bounds.y(), bounds.z());
+ corners[3] = QVector3D( bounds.x(), -bounds.y(), bounds.z());
+ corners[4] = QVector3D( bounds.x(), bounds.y(), -bounds.z());
+ corners[5] = QVector3D(-bounds.x(), bounds.y(), -bounds.z());
+ corners[6] = QVector3D(-bounds.x(), -bounds.y(), -bounds.z());
+ corners[7] = QVector3D( bounds.x(), -bounds.y(), -bounds.z());
+
+ for (int i = 0; i < 8; ++i) {
+ const QVector3D &vert = corners[i];
+ *dataPtr++ = vert.x(); *dataPtr++ = vert.y(); *dataPtr++ = vert.z();
+ }
+
+ auto addLine = [&](int start, int end) {
+ *indexPtr++ = start; *indexPtr++ = end;
+ };
+
+ addLine(0, 1);
+ addLine(1, 2);
+ addLine(2, 3);
+ addLine(3, 0);
+
+ addLine(4, 5);
+ addLine(5, 6);
+ addLine(6, 7);
+ addLine(7, 4);
+
+ addLine(0, 4);
+ addLine(1, 5);
+ addLine(2, 6);
+ addLine(3, 7);
+}
+
+} // namespace QmlDesigner::Internal
+
+#endif // QUICK3D_MODULE
diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/boxgeometry.h b/src/tools/qml2puppet/qml2puppet/editor3d/boxgeometry.h
new file mode 100644
index 0000000000..f4219d7961
--- /dev/null
+++ b/src/tools/qml2puppet/qml2puppet/editor3d/boxgeometry.h
@@ -0,0 +1,42 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#ifdef QUICK3D_MODULE
+
+#include "geometrybase.h"
+
+#include <QVector3D>
+
+namespace QmlDesigner::Internal {
+
+class BoxGeometry : public GeometryBase
+{
+ Q_OBJECT
+ Q_PROPERTY(QVector3D extent READ extent WRITE setExtent NOTIFY extentChanged)
+
+public:
+
+ BoxGeometry();
+ ~BoxGeometry() override;
+
+ QVector3D extent() const;
+ void setExtent(const QVector3D &extent);
+
+signals:
+ void extentChanged();
+
+protected:
+ void doUpdateGeometry() override;
+
+private:
+ void fillVertexData(QByteArray &vertexData, QByteArray &indexData, const QVector3D &bounds);
+ QVector3D m_extent = { 1.f, 1.f, 1.f };
+};
+
+} // namespace QmlDesigner::Internal
+
+QML_DECLARE_TYPE(QmlDesigner::Internal::BoxGeometry)
+
+#endif // QUICK3D_MODULE
diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/editor3d.pri b/src/tools/qml2puppet/qml2puppet/editor3d/editor3d.pri
deleted file mode 100644
index da7ba93e78..0000000000
--- a/src/tools/qml2puppet/qml2puppet/editor3d/editor3d.pri
+++ /dev/null
@@ -1,19 +0,0 @@
-HEADERS += $$PWD/generalhelper.h \
- $$PWD/mousearea3d.h \
- $$PWD/geometrybase.h \
- $$PWD/camerageometry.h \
- $$PWD/lightgeometry.h \
- $$PWD/gridgeometry.h \
- $$PWD/selectionboxgeometry.h \
- $$PWD/linegeometry.h \
- $$PWD/icongizmoimageprovider.h
-
-SOURCES += $$PWD/generalhelper.cpp \
- $$PWD/mousearea3d.cpp \
- $$PWD/geometrybase.cpp \
- $$PWD/camerageometry.cpp \
- $$PWD/lightgeometry.cpp \
- $$PWD/gridgeometry.cpp \
- $$PWD/selectionboxgeometry.cpp \
- $$PWD/linegeometry.cpp \
- $$PWD/icongizmoimageprovider.cpp
diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp
index ecb71ce383..0fb708c6ff 100644
--- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp
+++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp
@@ -66,6 +66,11 @@ GeneralHelper::GeneralHelper()
QList<QColor> defaultBg;
defaultBg.append(QColor());
m_bgColor = QVariant::fromValue(defaultBg);
+
+ m_camMoveData.timer.setInterval(16);
+ QObject::connect(&m_camMoveData.timer, &QTimer::timeout, this, [this]() {
+ emit requestCameraMove(m_camMoveData.camera, m_camMoveData.combinedMoveVector);
+ });
}
void GeneralHelper::requestOverlayUpdate()
@@ -142,14 +147,117 @@ QVector3D GeneralHelper::panCamera(QQuick3DCamera *camera, const QMatrix4x4 star
const float *dataPtr(startTransform.data());
const QVector3D xAxis = QVector3D(dataPtr[0], dataPtr[1], dataPtr[2]).normalized();
const QVector3D yAxis = QVector3D(dataPtr[4], dataPtr[5], dataPtr[6]).normalized();
- const QVector3D xDelta = -1.f * xAxis * dragVector.x();
+ const QVector3D xDelta = xAxis * dragVector.x();
const QVector3D yDelta = yAxis * dragVector.y();
- const QVector3D delta = (xDelta + yDelta) * zoomFactor;
+ const QVector3D delta = (yDelta - xDelta) * zoomFactor;
camera->setPosition(startPosition + delta);
return startLookAt + delta;
}
+// Moves camera in 3D space and returns new look-at point
+QVector3D GeneralHelper::moveCamera(QQuick3DCamera *camera, const QVector3D &startLookAt,
+ float zoomFactor, const QVector3D &moveVector)
+{
+
+ if (moveVector.length() < 0.001f)
+ return startLookAt;
+
+ QMatrix4x4 m = camera->sceneTransform(); // Works because edit camera is at scene root
+ const float *dataPtr(m.data());
+ const QVector3D xAxis = QVector3D(dataPtr[0], dataPtr[1], dataPtr[2]).normalized();
+ const QVector3D yAxis = QVector3D(dataPtr[4], dataPtr[5], dataPtr[6]).normalized();
+ const QVector3D zAxis = QVector3D(dataPtr[8], dataPtr[9], dataPtr[10]).normalized();
+ const QVector3D xDelta = xAxis * moveVector.x();
+ const QVector3D yDelta = yAxis * moveVector.y();
+ const QVector3D zDelta = zAxis * moveVector.z();
+ const QVector3D delta = (yDelta - xDelta - zDelta) * zoomFactor;
+
+ camera->setPosition(camera->position() + delta);
+
+ return startLookAt + delta;
+}
+
+// Rotates camera and returns the new look-at point
+QVector3D GeneralHelper::rotateCamera(QQuick3DCamera *camera, const QPointF &angles,
+ const QVector3D &lookAtPoint)
+{
+ float lookAtDist = (camera->scenePosition() - lookAtPoint).length();
+
+ if (qAbs(angles.y()) > 0.001f)
+ camera->rotate(angles.y(), QVector3D(1.f, 0.f, 0.f), QQuick3DNode::LocalSpace);
+ // Rotation around Y-axis is done in scene space to keep horizon level
+ if (qAbs(angles.x()) > 0.001f) {
+ // Since we are rotating in scene space, adjust direction according to camera up-vector
+ float angle = angles.x();
+ if (camera->up().y() <= 0)
+ angle = -angle;
+
+ camera->rotate(angle, QVector3D(0.f, 1.f, 0.f), QQuick3DNode::SceneSpace);
+ }
+
+ QMatrix4x4 m = camera->sceneTransform();
+ const float *dataPtr(m.data());
+ QVector3D newLookVector(dataPtr[8], dataPtr[9], dataPtr[10]);
+
+ newLookVector.normalize();
+ newLookVector *= lookAtDist;
+
+ return camera->scenePosition() - newLookVector;
+}
+
+void GeneralHelper::updateCombinedCameraMoveVector()
+{
+ QVector3D combinedVec;
+ for (const QVector3D &vec : std::as_const(m_camMoveData.moveVectors))
+ combinedVec += vec;
+ m_camMoveData.combinedMoveVector = combinedVec;
+}
+
+// Key events can be buffered and there are repeat delays imposed by OS, so to get smooth camera
+// movement in response to keys, register start/stop of moves along each axis and use timer to
+// trigger new moves along registered axes.
+void GeneralHelper::startCameraMove(QQuick3DCamera *camera, const QVector3D moveVector)
+{
+ if (moveVector.isNull())
+ return;
+
+ if (m_camMoveData.camera != camera) {
+ m_camMoveData.camera = camera;
+ m_camMoveData.moveVectors.clear();
+ }
+
+ if (!m_camMoveData.moveVectors.contains(moveVector)) {
+ m_camMoveData.moveVectors.append(moveVector);
+ updateCombinedCameraMoveVector();
+ }
+
+ if (!m_camMoveData.timer.isActive()) {
+ m_camMoveData.timer.start();
+ emit requestCameraMove(camera, m_camMoveData.combinedMoveVector);
+ }
+}
+
+void GeneralHelper::stopCameraMove(const QVector3D moveVector)
+{
+ if (moveVector.isNull())
+ return;
+
+ m_camMoveData.moveVectors.removeOne(moveVector);
+
+ updateCombinedCameraMoveVector();
+
+ if (m_camMoveData.moveVectors.isEmpty())
+ m_camMoveData.timer.stop();
+}
+
+void GeneralHelper::stopAllCameraMoves()
+{
+ m_camMoveData.moveVectors.clear();
+ m_camMoveData.combinedMoveVector = {};
+ m_camMoveData.timer.stop();
+}
+
float GeneralHelper::zoomCamera([[maybe_unused]] QQuick3DViewport *viewPort,
QQuick3DCamera *camera,
float distance,
@@ -1014,6 +1122,14 @@ void GeneralHelper::setSnapPositionInterval(double interval)
}
}
+void GeneralHelper::setCameraSpeed(double speed)
+{
+ if (m_cameraSpeed != speed) {
+ m_cameraSpeed = speed;
+ emit cameraSpeedChanged();
+ }
+}
+
QString GeneralHelper::formatVectorDragTooltip(const QVector3D &vec, const QString &suffix) const
{
return QObject::tr("x:%L1 y:%L2 z:%L3%L4")
diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h
index fd76cadcfc..3ba6f8b1d9 100644
--- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h
+++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h
@@ -10,6 +10,7 @@
#include <QMatrix4x4>
#include <QObject>
#include <QPointer>
+#include <QPointF>
#include <QQuaternion>
#include <QTimer>
#include <QUrl>
@@ -37,6 +38,7 @@ class GeneralHelper : public QObject
Q_PROPERTY(bool isMacOS READ isMacOS CONSTANT)
Q_PROPERTY(QVariant bgColor READ bgColor NOTIFY bgColorChanged FINAL)
Q_PROPERTY(double minGridStep READ minGridStep NOTIFY minGridStepChanged FINAL)
+ Q_PROPERTY(double cameraSpeed READ cameraSpeed NOTIFY cameraSpeedChanged FINAL)
public:
GeneralHelper();
@@ -52,6 +54,15 @@ public:
const QVector3D &startPosition, const QVector3D &startLookAt,
const QVector3D &pressPos, const QVector3D &currentPos,
float zoomFactor);
+ Q_INVOKABLE QVector3D moveCamera(QQuick3DCamera *camera,const QVector3D &startLookAt,
+ float zoomFactor, const QVector3D &moveVector);
+ Q_INVOKABLE QVector3D rotateCamera(QQuick3DCamera *camera, const QPointF &angles,
+ const QVector3D &lookAtPoint);
+
+ Q_INVOKABLE void startCameraMove(QQuick3DCamera *camera, const QVector3D moveVector);
+ Q_INVOKABLE void stopCameraMove(const QVector3D moveVector);
+ Q_INVOKABLE void stopAllCameraMoves();
+
Q_INVOKABLE float zoomCamera(QQuick3DViewport *viewPort, QQuick3DCamera *camera, float distance,
float defaultLookAtDistance, const QVector3D &lookAt,
float zoomFactor, bool relative);
@@ -125,12 +136,14 @@ public:
void setSnapPositionInterval(double interval);
void setSnapRotationInterval(double interval) { m_snapRotationInterval = interval; }
void setSnapScaleInterval(double interval) { m_snapScaleInterval = interval / 100.; }
+ void setCameraSpeed(double speed);
Q_INVOKABLE QString snapPositionDragTooltip(const QVector3D &pos) const;
Q_INVOKABLE QString snapRotationDragTooltip(double angle) const;
Q_INVOKABLE QString snapScaleDragTooltip(const QVector3D &scale) const;
double minGridStep() const;
+ double cameraSpeed() const { return m_cameraSpeed; }
void setBgColor(const QVariant &colors);
QVariant bgColor() const { return m_bgColor; }
@@ -149,6 +162,9 @@ signals:
void minGridStepChanged();
void updateDragTooltip();
void sceneEnvDataChanged();
+ void requestCameraMove(QQuick3DCamera *camera, const QVector3D &moveVector);
+ void requestRender();
+ void cameraSpeedChanged();
private:
void handlePendingToolStateUpdate();
@@ -163,6 +179,15 @@ private:
QHash<QString, QVariantMap> m_toolStates;
QHash<QString, QVariantMap> m_toolStatesPending;
QSet<QQuick3DNode *> m_rotationBlockedNodes;
+ void updateCombinedCameraMoveVector();
+
+ struct CameraMoveKeyData {
+ QQuick3DCamera *camera;
+ QList<QVector3D> moveVectors;
+ QVector3D combinedMoveVector;
+ QTimer timer;
+ };
+ CameraMoveKeyData m_camMoveData;
struct SceneEnvData {
QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes backgroundMode;
@@ -193,6 +218,7 @@ private:
double m_snapPositionInterval = 50.;
double m_snapRotationInterval = 5.;
double m_snapScaleInterval = .1;
+ double m_cameraSpeed = 10.;
QVariant m_bgColor;
};
diff --git a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp
index 941a1d50b9..a1c727f215 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp
+++ b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp
@@ -291,7 +291,7 @@ static void removeObjectFromList(const QQmlProperty &property,
int count = listReference.count();
- QObjectList objectList;
+ QList<QPointer<QObject>> objectList;
for (int i = 0; i < count; i ++) {
QObject *listItem = listReference.at(i);
@@ -301,8 +301,10 @@ static void removeObjectFromList(const QQmlProperty &property,
listReference.clear();
- for (QObject *object : std::as_const(objectList))
- listReference.append(object);
+ for (QObject *object : std::as_const(objectList)) {
+ if (object)
+ listReference.append(object);
+ }
}
void ObjectNodeInstance::removeFromOldProperty(QObject *object, QObject *oldParent, const PropertyName &oldParentProperty)
diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp
index 7ea140d1aa..c7cab45ed4 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp
+++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp
@@ -40,6 +40,7 @@
#include "requestmodelnodepreviewimagecommand.h"
#include "changeauxiliarycommand.h"
+#include "../editor3d/boxgeometry.h"
#include "../editor3d/generalhelper.h"
#include "../editor3d/mousearea3d.h"
#include "../editor3d/camerageometry.h"
@@ -253,8 +254,11 @@ void Qt5InformationNodeInstanceServer::handleInputEvents()
QGuiApplication::sendEvent(m_editView3DData.window, &me);
// Context menu requested
- if (command.button() == Qt::RightButton && command.modifiers() == Qt::NoModifier)
+ if (command.type() == QEvent::MouseButtonPress
+ && command.buttons() == Qt::RightButton
+ && command.modifiers() == Qt::NoModifier) {
getNodeAtPos(command.pos());
+ }
}
}
@@ -339,7 +343,7 @@ void Qt5InformationNodeInstanceServer::updateRotationBlocks(
#endif
}
-void Qt5InformationNodeInstanceServer::updateSnapSettings(
+void Qt5InformationNodeInstanceServer::updateSnapAndCameraSettings(
[[maybe_unused]] const QVector<PropertyValueContainer> &valueChanges)
{
#ifdef QUICK3D_MODULE
@@ -368,6 +372,8 @@ void Qt5InformationNodeInstanceServer::updateSnapSettings(
} else if (container.name() == "snapAbs3d") {
helper->setSnapAbsolute(container.value().toBool());
changed = true;
+ } else if (container.name() == "cameraTotalSpeed3d") {
+ helper->setCameraSpeed(container.value().toDouble());
}
}
if (changed)
@@ -470,6 +476,42 @@ void Qt5InformationNodeInstanceServer::getNodeAtPos([[maybe_unused]] const QPoin
#endif
}
+void Qt5InformationNodeInstanceServer::getNodeAtMainScenePos(
+ [[maybe_unused]] const QPointF &pos, [[maybe_unused]] qint32 viewId)
+{
+#ifdef QUICK3D_MODULE
+ // Pick a Quick3DModel at scene position in the main scene
+ auto helper = qobject_cast<QmlDesigner::Internal::GeneralHelper *>(m_3dHelper);
+
+ if (!helper || !hasInstanceForId(viewId))
+ return;
+
+ ServerNodeInstance view = instanceForId(viewId);
+ auto viewObj = qobject_cast<QQuick3DViewport *>(view.internalObject());
+
+ if (viewObj) {
+ // Render the main view to make sure everything is up to date
+ updateNodesRecursive(viewObj);
+ renderWindow();
+
+ QPointF viewPos = viewObj->mapFromScene(pos);
+
+ QQuick3DModel *hitModel = helper->pickViewAt(viewObj, viewPos.x(), viewPos.y()).objectHit();
+ QObject *resolvedPick = helper->resolvePick(hitModel);
+
+ qint32 instanceId = -1;
+ if (hasInstanceForObject(resolvedPick))
+ instanceId = instanceForObject(resolvedPick).instanceId();
+
+ QVariantList data;
+ data.append(instanceId);
+ data.append(QVector3D());
+ nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::NodeAtPos,
+ QVariant::fromValue(data)});
+ }
+#endif
+}
+
void Qt5InformationNodeInstanceServer::createEditView3D()
{
#ifdef QUICK3D_MODULE
@@ -482,10 +524,14 @@ void Qt5InformationNodeInstanceServer::createEditView3D()
qmlRegisterType<QmlDesigner::Internal::GridGeometry>("GridGeometry", 1, 0, "GridGeometry");
qmlRegisterType<QmlDesigner::Internal::SelectionBoxGeometry>("SelectionBoxGeometry", 1, 0, "SelectionBoxGeometry");
qmlRegisterType<QmlDesigner::Internal::LineGeometry>("LineGeometry", 1, 0, "LineGeometry");
+ qmlRegisterType<QmlDesigner::Internal::BoxGeometry>("BoxGeometry", 1, 0, "BoxGeometry");
auto helper = new QmlDesigner::Internal::GeneralHelper();
QObject::connect(helper, &QmlDesigner::Internal::GeneralHelper::toolStateChanged,
this, &Qt5InformationNodeInstanceServer::handleToolStateChanged);
+ QObject::connect(helper, &QmlDesigner::Internal::GeneralHelper::requestRender, this, [this]() {
+ render3DEditView(1);
+ });
engine()->rootContext()->setContextProperty("_generalHelper", helper);
engine()->addImageProvider(QLatin1String("IconGizmoImageProvider"),
new QmlDesigner::Internal::IconGizmoImageProvider);
@@ -2059,7 +2105,7 @@ void Qt5InformationNodeInstanceServer::createScene(const CreateSceneCommand &com
setup3DEditView(instanceList, command);
updateRotationBlocks(command.auxiliaryChanges);
updateMaterialPreviewData(command.auxiliaryChanges);
- updateSnapSettings(command.auxiliaryChanges);
+ updateSnapAndCameraSettings(command.auxiliaryChanges);
updateColorSettings(command.auxiliaryChanges);
}
@@ -2275,14 +2321,15 @@ void Qt5InformationNodeInstanceServer::setSceneEnvironmentData(
}
// Returns list of camera objects to align
-// If m_selectedCameras contains cameras, return those
+// If m_selectedCameras contains cameras, return those, unless preferCurrentSceneCamera is true
// If no cameras have been selected yet, return camera associated with current view3D, if any
// If scene is not View3D scene, return first camera in the scene
-QVariantList Qt5InformationNodeInstanceServer::alignCameraList() const
+QVariantList Qt5InformationNodeInstanceServer::alignCameraList(
+ [[maybe_unused]] bool preferCurrentSceneCamera) const
{
QVariantList cameras;
#ifdef QUICK3D_MODULE
- if (m_selectedCameras.contains(m_active3DScene)) {
+ if (!preferCurrentSceneCamera && m_selectedCameras.contains(m_active3DScene)) {
const QObjectList cameraList = m_selectedCameras[m_active3DScene];
for (const auto camera : cameraList) {
if (hasInstanceForObject(camera) && find3DSceneRoot(camera) == m_active3DScene)
@@ -2415,6 +2462,7 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c
if (!m_editView3DSetupDone)
return;
+#ifdef QUICK3D_MODULE
QVariantMap updatedToolState;
QVariantMap updatedViewState;
int renderCount = 1;
@@ -2436,10 +2484,14 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c
QMetaObject::invokeMethod(m_editView3DData.rootItem, "alignCamerasToView",
Q_ARG(QVariant, alignCameraList()));
break;
- case View3DActionType::AlignViewToCamera:
+ case View3DActionType::AlignViewToCamera: {
+ bool preferCurrentSceneCamera = false;
+ if (!command.value().isNull())
+ preferCurrentSceneCamera = command.value().toBool();
QMetaObject::invokeMethod(m_editView3DData.rootItem, "alignViewToCamera",
- Q_ARG(QVariant, alignCameraList()));
+ Q_ARG(QVariant, alignCameraList(preferCurrentSceneCamera)));
break;
+ }
case View3DActionType::SelectionModeToggle:
updatedToolState.insert("selectionMode", command.isEnabled() ? 1 : 0);
break;
@@ -2496,16 +2548,37 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c
case View3DActionType::ParticlesSeek:
m_particleAnimationDriver->setSeekerPosition(command.position());
break;
-#endif
-#ifdef QUICK3D_MODULE
+#endif // QUICK3D_PARTICLES_MODULE
case View3DActionType::GetNodeAtPos: {
getNodeAtPos(command.value().toPointF());
return;
}
-#endif
+ case View3DActionType::GetNodeAtMainScenePos: {
+ QVariantList data = command.value().toList();
+ if (data.size() == 2)
+ getNodeAtMainScenePos(data[0].toPointF(), qint32(data[1].toInt()));
+ return;
+ }
case View3DActionType::SplitViewToggle:
updatedToolState.insert("splitView", command.isEnabled());
break;
+ case View3DActionType::FlyModeToggle:
+ updatedToolState.insert("flyMode", command.isEnabled());
+ break;
+ case View3DActionType::EditCameraRotation:
+ QMetaObject::invokeMethod(m_editView3DData.rootItem, "rotateEditCamera",
+ Q_ARG(QVariant, command.value()));
+ break;
+ case View3DActionType::EditCameraMove:
+ QMetaObject::invokeMethod(m_editView3DData.rootItem, "moveEditCamera",
+ Q_ARG(QVariant, command.value()));
+ break;
+ case View3DActionType::EditCameraStopAllMoves: {
+ auto helper = qobject_cast<QmlDesigner::Internal::GeneralHelper *>(m_3dHelper);
+ if (helper)
+ emit helper->stopAllCameraMoves();
+ break;
+ }
case View3DActionType::ShowWireframe:
updatedToolState.insert("showWireframe", command.value().toList());
break;
@@ -2529,6 +2602,7 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c
}
render3DEditView(renderCount);
+#endif // QUICK3D_MODULE
}
void Qt5InformationNodeInstanceServer::requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command)
@@ -2541,7 +2615,7 @@ void Qt5InformationNodeInstanceServer::changeAuxiliaryValues(const ChangeAuxilia
{
updateRotationBlocks(command.auxiliaryChanges);
updateMaterialPreviewData(command.auxiliaryChanges);
- updateSnapSettings(command.auxiliaryChanges);
+ updateSnapAndCameraSettings(command.auxiliaryChanges);
updateColorSettings(command.auxiliaryChanges);
Qt5NodeInstanceServer::changeAuxiliaryValues(command);
render3DEditView();
diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h
index cb7dd20a96..4f7fcd7177 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h
+++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h
@@ -124,10 +124,11 @@ private:
void resolveImportSupport();
void updateMaterialPreviewData(const QVector<PropertyValueContainer> &valueChanges);
void updateRotationBlocks(const QVector<PropertyValueContainer> &valueChanges);
- void updateSnapSettings(const QVector<PropertyValueContainer> &valueChanges);
+ void updateSnapAndCameraSettings(const QVector<PropertyValueContainer> &valueChanges);
void updateColorSettings(const QVector<PropertyValueContainer> &valueChanges);
void removeRotationBlocks(const QVector<qint32> &instanceIds);
void getNodeAtPos(const QPointF &pos);
+ void getNodeAtMainScenePos(const QPointF &pos, qint32 viewId);
void createAuxiliaryQuickView(const QUrl &url, RenderViewData &viewData);
#ifdef QUICK3D_PARTICLES_MODULE
@@ -136,7 +137,7 @@ private:
void handleParticleSystemDeselected();
#endif
void setSceneEnvironmentData(qint32 instanceId);
- QVariantList alignCameraList() const;
+ QVariantList alignCameraList(bool preferCurrentSceneCamera = false) const;
void updateSceneEnvToHelper();
bool isSceneEnvironmentBgProperty(const PropertyName &name) const;
diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp
index 3a7882871c..f9191950fd 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp
+++ b/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp
@@ -36,6 +36,8 @@
#include "dummycontextobject.h"
+#include <qmlprivategate.h>
+
#include <private/qquickdesignersupport_p.h>
namespace QmlDesigner {
@@ -217,4 +219,37 @@ void QmlDesigner::Qt5RenderNodeInstanceServer::removeSharedMemory(const QmlDesig
ImageContainer::removeSharedMemorys(command.keyNumbers());
}
+void Qt5RenderNodeInstanceServer::changePropertyValues(const ChangeValuesCommand &command)
+{
+ Qt5NodeInstanceServer::changePropertyValues(command);
+
+ const QVector<PropertyValueContainer> values = command.valueChanges();
+ for (const PropertyValueContainer &container : values) {
+ // In case an effect item visibility changed to false, make sure all children are rendered
+ // again as they might not have valid pixmaps yet
+ if (container.name() == "visible" && !container.value().toBool()
+ && hasInstanceForId(container.instanceId())) {
+ ServerNodeInstance instance = instanceForId(container.instanceId());
+ if (instance.isSubclassOf("QtQuick/PropertyChanges")) {
+ QObject *targetObject = Internal::QmlPrivateGate::PropertyChanges::targetObject(
+ instance.internalInstance()->object());
+ if (hasInstanceForObject(targetObject))
+ instance = instanceForObject(targetObject);
+ }
+
+ if (instance.hasParent() && instance.propertyNames().contains("_isEffectItem"))
+ makeDirtyRecursive(instance.parent());
+ }
+ }
+}
+
+void Qt5RenderNodeInstanceServer::makeDirtyRecursive(const ServerNodeInstance &instance)
+{
+ const QList<ServerNodeInstance> children = instance.childItems();
+ for (const auto &child : children) {
+ m_dirtyInstanceSet.insert(child);
+ makeDirtyRecursive(child);
+ }
+}
+
} // namespace QmlDesigner
diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.h
index 53cb6fffb1..738aa47b18 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.h
+++ b/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.h
@@ -17,6 +17,7 @@ public:
void clearScene(const ClearSceneCommand &command) override;
void completeComponent(const CompleteComponentCommand &command) override;
void removeSharedMemory(const RemoveSharedMemoryCommand &command) override;
+ void changePropertyValues(const ChangeValuesCommand &command) override;
protected:
void collectItemChangesAndSendChangeCommands() override;
@@ -24,6 +25,8 @@ protected:
void resizeCanvasToRootItem() override;
private:
+ void makeDirtyRecursive(const ServerNodeInstance &instance);
+
QSet<ServerNodeInstance> m_dirtyInstanceSet;
};
diff --git a/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp
index 9d432fe047..f2fbb97aba 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp
+++ b/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp
@@ -280,10 +280,10 @@ static bool layerEnabledAndEffect(QQuickItem *item)
QRectF QuickItemNodeInstance::boundingRect() const
{
if (quickItem()) {
- if (quickItem()->clip()) {
- return quickItem()->boundingRect();
- } else if (layerEnabledAndEffect(quickItem())) {
+ if (layerEnabledAndEffect(quickItem())) {
return ServerNodeInstance::effectAdjustedBoundingRect(quickItem());
+ } else if (quickItem()->clip()) {
+ return quickItem()->boundingRect();
} else {
QSize maximumSize(4000, 4000);
auto isValidSize = [maximumSize] (const QRectF& rect) {
diff --git a/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp
index 758e18df35..ca05a5ef03 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp
+++ b/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp
@@ -26,6 +26,8 @@
#include <qmlprivategate.h>
+#include <QtQuick/private/qquickitem_p.h>
+
#include <QHash>
#include <QSet>
#include <QDebug>
@@ -116,8 +118,13 @@ bool ServerNodeInstance::isSubclassOf(QObject *object, const QByteArray &superTy
QRectF ServerNodeInstance::effectAdjustedBoundingRect(QQuickItem *item)
{
- if (item)
- return item->boundingRect().adjusted(-40, -40, 40, 40);
+ if (item) {
+ QQuickItemPrivate *pItem = QQuickItemPrivate::get(item);
+ if (pItem && pItem->layer() && pItem->layer()->sourceRect().isValid())
+ return pItem->layer()->sourceRect();
+ else
+ return item->boundingRect();
+ }
return {};
}
diff --git a/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp b/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp
index 8abfed5916..9d390714bb 100644
--- a/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp
+++ b/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp
@@ -10,6 +10,7 @@
#include <QQmlComponent>
#include <QFileInfo>
#include <QProcessEnvironment>
+#include <QJsonValue>
#include <private/qabstractfileengine_p.h>
#include <private/qfsfileengine_p.h>
@@ -35,16 +36,484 @@
#include <private/qquickanimation_p.h>
#include <private/qqmlmetatype_p.h>
#include <private/qqmltimer_p.h>
+#include <private/qqmlanybinding_p.h>
#ifdef QUICK3D_MODULE
#include <private/qquick3dobject_p.h>
#include <private/qquick3drepeater_p.h>
#endif
+
+
namespace QmlDesigner {
namespace Internal {
+static void addToPropertyNameListIfNotBlackListed(QQuickDesignerSupport::PropertyNameList *propertyNameList,
+ const QQuickDesignerSupport::PropertyName &propertyName)
+{
+ if (!QQuickDesignerSupportProperties::isPropertyBlackListed(propertyName))
+ propertyNameList->append(propertyName);
+}
+
+static QQuickDesignerSupport::PropertyNameList propertyNameListForWritablePropertiesInternal(QObject *object,
+ const QQuickDesignerSupport::PropertyName &baseName,
+ QObjectList *inspectedObjects,
+ int depth = 0)
+{
+ QQuickDesignerSupport::PropertyNameList propertyNameList;
+
+ if (depth > 2)
+ return propertyNameList;
+
+ if (!inspectedObjects->contains(object))
+ inspectedObjects->append(object);
+
+ const QMetaObject *metaObject = object->metaObject();
+ for (int index = 0; index < metaObject->propertyCount(); ++index) {
+ QMetaProperty metaProperty = metaObject->property(index);
+ QQmlProperty declarativeProperty(object, QString::fromUtf8(metaProperty.name()));
+ if (declarativeProperty.isValid() && !declarativeProperty.isWritable() && declarativeProperty.propertyTypeCategory() == QQmlProperty::Object) {
+ if (declarativeProperty.name() != QLatin1String("parent")) {
+ QObject *childObject = QQmlMetaType::toQObject(declarativeProperty.read());
+ if (childObject)
+ propertyNameList.append(propertyNameListForWritablePropertiesInternal(childObject,
+ baseName + QQuickDesignerSupport::PropertyName(metaProperty.name())
+ + '.', inspectedObjects,
+ depth + 1));
+ }
+ } else if (QQmlGadgetPtrWrapper *valueType
+ = QQmlGadgetPtrWrapper::instance(qmlEngine(object), metaProperty.metaType())) {
+
+
+ const QVariant value = metaProperty.read(object);
+ QMetaType jsType = QMetaType::fromType<QJSValue>();
+ int userType = value.userType();
+
+ //qDebug() << jsType << jsType.id();
+ //qDebug() << "tp" << value.typeName();
+ //qDebug() << "ut" << userType;
+
+ if (userType == jsType.id()) {
+ qDebug() << "js value found";
+ //QJSValue jsValue = value.value<QJSValue>(); //crashes
+ //qDebug() << jsValue.isObject();
+ //qDebug() << jsValue.isQObject();
+ } else {
+
+
+
+ valueType->setValue(value);
+ propertyNameList.append(propertyNameListForWritablePropertiesInternal(valueType,
+ baseName + QQuickDesignerSupport::PropertyName(metaProperty.name())
+ + '.', inspectedObjects,
+ depth + 1));
+ }
+
+ }
+
+ if (metaProperty.isReadable() && metaProperty.isWritable()) {
+ addToPropertyNameListIfNotBlackListed(&propertyNameList,
+ baseName + QQuickDesignerSupport::PropertyName(metaProperty.name()));
+ }
+ }
+
+ return propertyNameList;
+}
+
+static QQuickDesignerSupport::PropertyNameList propertyNameListForWritablePropertiesFork(QObject *object)
+{
+ QObjectList localObjectList;
+ return propertyNameListForWritablePropertiesInternal(object, {}, &localObjectList);
+}
+
+static QQuickDesignerSupport::PropertyNameList allPropertyNamesFork(QObject *object,
+ const QQuickDesignerSupport::PropertyName &baseName = QQuickDesignerSupport::PropertyName(),
+ QObjectList *inspectedObjects = nullptr,
+ int depth = 0)
+{
+ QQuickDesignerSupport::PropertyNameList propertyNameList;
+
+ QObjectList localObjectList;
+
+ if (inspectedObjects == nullptr)
+ inspectedObjects = &localObjectList;
+
+ if (depth > 2)
+ return propertyNameList;
+
+ if (!inspectedObjects->contains(object))
+ inspectedObjects->append(object);
+
+ const QMetaObject *metaObject = object->metaObject();
+
+ QStringList deferredPropertyNames;
+ const int namesIndex = metaObject->indexOfClassInfo("DeferredPropertyNames");
+ if (namesIndex != -1) {
+ QMetaClassInfo classInfo = metaObject->classInfo(namesIndex);
+ deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(','));
+ }
+
+ for (int index = 0; index < metaObject->propertyCount(); ++index) {
+ QMetaProperty metaProperty = metaObject->property(index);
+ QQmlProperty declarativeProperty(object, QString::fromUtf8(metaProperty.name()));
+ if (declarativeProperty.isValid() && declarativeProperty.propertyTypeCategory() == QQmlProperty::Object) {
+ if (declarativeProperty.name() != QLatin1String("parent")
+ && !deferredPropertyNames.contains(declarativeProperty.name())) {
+ QObject *childObject = QQmlMetaType::toQObject(declarativeProperty.read());
+ if (childObject)
+ propertyNameList.append(allPropertyNamesFork(childObject,
+ baseName
+ + QQuickDesignerSupport::PropertyName(metaProperty.name())
+ + '.', inspectedObjects,
+ depth + 1));
+ }
+ } else if (QQmlGadgetPtrWrapper *valueType
+ = QQmlGadgetPtrWrapper::instance(qmlEngine(object), metaProperty.metaType())) {
+
+ const QVariant value = metaProperty.read(object);
+ propertyNameList.append(baseName + QQuickDesignerSupport::PropertyName(metaProperty.name()));
+ const QJsonValue jsonValue = value.toJsonValue();
+
+ if (!jsonValue.isNull()) {
+ qDebug() << "llokhere";
+ qDebug() << "name" << metaProperty.name();
+ qDebug() << "value" << value;
+ qDebug() << jsonValue;
+ }
+
+ if (value.isValid() && jsonValue.isNull()) {
+ //qDebug() << "llokhere crash";
+ //qDebug() << "name" << metaProperty.name();
+ //qDebug() << "value" << value;
+ //qDebug() << jsonValue;
+
+
+ QMetaType jsType = QMetaType::fromType<QJSValue>();
+
+ int userType = value.userType();
+
+ //qDebug() << jsType << jsType.id();
+ //qDebug() << "tp" << value.typeName();
+ //qDebug() << "ut" << userType;
+
+ if (userType == jsType.id()) {
+ qDebug() << "js value found";
+ //QJSValue jsValue = value.value<QJSValue>(); //crashes
+ //qDebug() << jsValue.isObject();
+ //qDebug() << jsValue.isQObject();
+ } else {
+
+
+ valueType->setValue(value);
+ propertyNameList.append(allPropertyNamesFork(valueType,
+ baseName
+ + QQuickDesignerSupport::PropertyName(metaProperty.name())
+ + '.', inspectedObjects,
+ depth + 1));
+ }
+
+
+ }
+ } else {
+ addToPropertyNameListIfNotBlackListed(&propertyNameList,
+ baseName + QQuickDesignerSupport::PropertyName(metaProperty.name()));
+ }
+ }
+
+ return propertyNameList;
+}
+
+class DesignerCustomObjectDataFork
+{
+public:
+ static void registerData(QObject *object);
+ static DesignerCustomObjectDataFork *get(QObject *object);
+ static QVariant getResetValue(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName);
+ static void doResetProperty(QObject *object, QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName);
+ static bool hasValidResetBinding(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName);
+ static bool hasBindingForProperty(QObject *object,
+ QQmlContext *context,
+ const QQuickDesignerSupport::PropertyName &propertyName,
+ bool *hasChanged);
+ static void setPropertyBinding(QObject *object,
+ QQmlContext *context,
+ const QQuickDesignerSupport::PropertyName &propertyName,
+ const QString &expression);
+ static void keepBindingFromGettingDeleted(QObject *object,
+ QQmlContext *context,
+ const QQuickDesignerSupport::PropertyName &propertyName);
+ void handleDestroyed();
+
+private:
+ DesignerCustomObjectDataFork(QObject *object);
+ void populateResetHashes();
+ QObject *object() const;
+ QVariant getResetValue(const QQuickDesignerSupport::PropertyName &propertyName) const;
+ void doResetProperty(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName);
+ bool hasValidResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const;
+ QQmlAnyBinding getResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const;
+ bool hasBindingForProperty(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName, bool *hasChanged) const;
+ void setPropertyBinding(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName, const QString &expression);
+ void keepBindingFromGettingDeleted(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName);
+
+ QObject *m_object;
+ QHash<QQuickDesignerSupport::PropertyName, QVariant> m_resetValueHash;
+ QHash<QQuickDesignerSupport::PropertyName, QQmlAnyBinding> m_resetBindingHash;
+ mutable QHash<QQuickDesignerSupport::PropertyName, bool> m_hasBindingHash;
+};
+
+typedef QHash<QObject*, DesignerCustomObjectDataFork*> CustomObjectDataHash;
+Q_GLOBAL_STATIC(CustomObjectDataHash, s_designerObjectToDataHash)
+
+struct HandleDestroyedFunctor {
+ DesignerCustomObjectDataFork *data;
+ void operator()() { data->handleDestroyed(); }
+};
+
+using namespace Qt::StringLiterals;
+
+DesignerCustomObjectDataFork::DesignerCustomObjectDataFork(QObject *object)
+ : m_object(object)
+{
+ if (object) {
+ populateResetHashes();
+ s_designerObjectToDataHash()->insert(object, this);
+
+ HandleDestroyedFunctor functor;
+ functor.data = this;
+ QObject::connect(object, &QObject::destroyed, functor);
+ }
+}
+
+void DesignerCustomObjectDataFork::registerData(QObject *object)
+{
+ new DesignerCustomObjectDataFork(object);
+}
+
+DesignerCustomObjectDataFork *DesignerCustomObjectDataFork::get(QObject *object)
+{
+ return s_designerObjectToDataHash()->value(object);
+}
+
+QVariant DesignerCustomObjectDataFork::getResetValue(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName)
+{
+ DesignerCustomObjectDataFork* data = get(object);
+
+ if (data)
+ return data->getResetValue(propertyName);
+
+ return QVariant();
+}
+
+void DesignerCustomObjectDataFork::doResetProperty(QObject *object, QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName)
+{
+ DesignerCustomObjectDataFork* data = get(object);
+
+ if (data)
+ data->doResetProperty(context, propertyName);
+}
+
+bool DesignerCustomObjectDataFork::hasValidResetBinding(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName)
+{
+ DesignerCustomObjectDataFork* data = get(object);
+
+ if (data)
+ return data->hasValidResetBinding(propertyName);
+
+ return false;
+}
+
+bool DesignerCustomObjectDataFork::hasBindingForProperty(QObject *object,
+ QQmlContext *context,
+ const QQuickDesignerSupport::PropertyName &propertyName,
+ bool *hasChanged)
+{
+ DesignerCustomObjectDataFork* data = get(object);
+
+ if (data)
+ return data->hasBindingForProperty(context, propertyName, hasChanged);
+
+ return false;
+}
+
+void DesignerCustomObjectDataFork::setPropertyBinding(QObject *object,
+ QQmlContext *context,
+ const QQuickDesignerSupport::PropertyName &propertyName,
+ const QString &expression)
+{
+ DesignerCustomObjectDataFork* data = get(object);
+
+ if (data)
+ data->setPropertyBinding(context, propertyName, expression);
+}
+
+void DesignerCustomObjectDataFork::keepBindingFromGettingDeleted(QObject *object,
+ QQmlContext *context,
+ const QQuickDesignerSupport::PropertyName &propertyName)
+{
+ DesignerCustomObjectDataFork* data = get(object);
+
+ if (data)
+ data->keepBindingFromGettingDeleted(context, propertyName);
+}
+
+void DesignerCustomObjectDataFork::populateResetHashes()
+{
+ const QQuickDesignerSupport::PropertyNameList propertyNameList =
+ propertyNameListForWritablePropertiesFork(object());
+
+ const QMetaObject *mo = object()->metaObject();
+ QByteArrayList deferredPropertyNames;
+ const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames");
+ if (namesIndex != -1) {
+ QMetaClassInfo classInfo = mo->classInfo(namesIndex);
+ deferredPropertyNames = QByteArray(classInfo.value()).split(',');
+ }
+
+ for (const QQuickDesignerSupport::PropertyName &propertyName : propertyNameList) {
+
+ if (deferredPropertyNames.contains(propertyName))
+ continue;
+
+ QQmlProperty property(object(), QString::fromUtf8(propertyName), QQmlEngine::contextForObject(object()));
+
+ auto binding = QQmlAnyBinding::ofProperty(property);
+
+ if (binding) {
+ m_resetBindingHash.insert(propertyName, binding);
+ } else if (property.isWritable()) {
+ m_resetValueHash.insert(propertyName, property.read());
+ }
+ }
+}
+
+QObject *DesignerCustomObjectDataFork::object() const
+{
+ return m_object;
+}
+
+QVariant DesignerCustomObjectDataFork::getResetValue(const QQuickDesignerSupport::PropertyName &propertyName) const
+{
+ return m_resetValueHash.value(propertyName);
+}
+
+void DesignerCustomObjectDataFork::doResetProperty(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName)
+{
+ QQmlProperty property(object(), QString::fromUtf8(propertyName), context);
+
+ if (!property.isValid())
+ return;
+
+ // remove existing binding
+ QQmlAnyBinding::takeFrom(property);
+
+
+ if (hasValidResetBinding(propertyName)) {
+ QQmlAnyBinding binding = getResetBinding(propertyName);
+ binding.installOn(property);
+
+ if (binding.isAbstractPropertyBinding()) {
+ // for new style properties, we will evaluate during setBinding anyway
+ static_cast<QQmlBinding *>(binding.asAbstractBinding())->update();
+ }
+
+ } else if (property.isResettable()) {
+ property.reset();
+ } else if (property.propertyTypeCategory() == QQmlProperty::List) {
+ QQmlListReference list = qvariant_cast<QQmlListReference>(property.read());
+
+ if (!QQuickDesignerSupportProperties::hasFullImplementedListInterface(list)) {
+ qWarning() << "Property list interface not fully implemented for Class " << property.property().typeName() << " in property " << property.name() << "!";
+ return;
+ }
+
+ list.clear();
+ } else if (property.isWritable()) {
+ if (property.read() == getResetValue(propertyName))
+ return;
+
+ property.write(getResetValue(propertyName));
+ }
+}
+
+bool DesignerCustomObjectDataFork::hasValidResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const
+{
+ return m_resetBindingHash.contains(propertyName) && m_resetBindingHash.value(propertyName);
+}
+
+QQmlAnyBinding DesignerCustomObjectDataFork::getResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const
+{
+ return m_resetBindingHash.value(propertyName);
+}
+
+bool DesignerCustomObjectDataFork::hasBindingForProperty(QQmlContext *context,
+ const QQuickDesignerSupport::PropertyName &propertyName,
+ bool *hasChanged) const
+{
+ if (QQuickDesignerSupportProperties::isPropertyBlackListed(propertyName))
+ return false;
+
+ QQmlProperty property(object(), QString::fromUtf8(propertyName), context);
+
+ bool hasBinding = QQmlAnyBinding::ofProperty(property);
+
+ if (hasChanged) {
+ *hasChanged = hasBinding != m_hasBindingHash.value(propertyName, false);
+ if (*hasChanged)
+ m_hasBindingHash.insert(propertyName, hasBinding);
+ }
+
+ return hasBinding;
+}
+
+void DesignerCustomObjectDataFork::setPropertyBinding(QQmlContext *context,
+ const QQuickDesignerSupport::PropertyName &propertyName,
+ const QString &expression)
+{
+ QQmlProperty property(object(), QString::fromUtf8(propertyName), context);
+
+ if (!property.isValid())
+ return;
+
+ if (property.isProperty()) {
+ QString url = u"@designer"_s;
+ int lineNumber = 0;
+ QQmlAnyBinding binding = QQmlAnyBinding::createFromCodeString(property,
+ expression, object(), QQmlContextData::get(context), url, lineNumber);
+
+ binding.installOn(property);
+ if (binding.isAbstractPropertyBinding()) {
+ // for new style properties, we will evaluate during setBinding anyway
+ static_cast<QQmlBinding *>(binding.asAbstractBinding())->update();
+ }
+
+ if (binding.hasError()) {
+ if (property.property().userType() == QMetaType::QString)
+ property.write(QVariant(QLatin1Char('#') + expression + QLatin1Char('#')));
+ }
+
+ } else {
+ qWarning() << Q_FUNC_INFO << ": Cannot set binding for property" << propertyName << ": property is unknown for type";
+ }
+}
+
+void DesignerCustomObjectDataFork::keepBindingFromGettingDeleted(QQmlContext *context,
+ const QQuickDesignerSupport::PropertyName &propertyName)
+{
+ //Refcounting is taking care
+ Q_UNUSED(context);
+ Q_UNUSED(propertyName);
+}
+
+void DesignerCustomObjectDataFork::handleDestroyed()
+{
+ s_designerObjectToDataHash()->remove(m_object);
+ delete this;
+}
+
+
+
namespace QmlPrivateGate {
bool isPropertyBlackListed(const QmlDesigner::PropertyName &propertyName)
@@ -54,12 +523,14 @@ bool isPropertyBlackListed(const QmlDesigner::PropertyName &propertyName)
PropertyNameList allPropertyNames(QObject *object)
{
- return QQuickDesignerSupportProperties::allPropertyNames(object);
+ return allPropertyNamesFork(object);
+ //return QQuickDesignerSupportProperties::allPropertyNames(object);
}
PropertyNameList propertyNameListForWritableProperties(QObject *object)
{
- return QQuickDesignerSupportProperties::propertyNameListForWritableProperties(object);
+ return propertyNameListForWritablePropertiesFork(object);
+ //return QQuickDesignerSupportProperties::propertyNameListForWritableProperties(object);
}
void tweakObjects(QObject *object)
@@ -187,7 +658,8 @@ bool hasFullImplementedListInterface(const QQmlListReference &list)
void registerCustomData(QObject *object)
{
- QQuickDesignerSupportProperties::registerCustomData(object);
+ DesignerCustomObjectDataFork::registerData(object);
+ //QQuickDesignerSupportProperties::registerCustomData(object);
}
QVariant getResetValue(QObject *object, const PropertyName &propertyName)
@@ -201,7 +673,8 @@ QVariant getResetValue(QObject *object, const PropertyName &propertyName)
else if (propertyName == "Layout.fillWidth")
return false;
else
- return QQuickDesignerSupportProperties::getResetValue(object, propertyName);
+ return DesignerCustomObjectDataFork::getResetValue(object, propertyName);
+ //return QQuickDesignerSupportProperties::getResetValue(object, propertyName);
}
static void setProperty(QObject *object, QQmlContext *context, const PropertyName &propertyName, const QVariant &value)
@@ -221,7 +694,9 @@ void doResetProperty(QObject *object, QQmlContext *context, const PropertyName &
else if (propertyName == "Layout.fillWidth")
setProperty(object, context, propertyName, getResetValue(object, propertyName));
else
- QQuickDesignerSupportProperties::doResetProperty(object, context, propertyName);
+ DesignerCustomObjectDataFork::doResetProperty(object, context, propertyName);
+
+ //QQuickDesignerSupportProperties::doResetProperty(object, context, propertyName);
}
bool hasValidResetBinding(QObject *object, const PropertyName &propertyName)
@@ -234,17 +709,22 @@ bool hasValidResetBinding(QObject *object, const PropertyName &propertyName)
return true;
else if (propertyName == "Layout.fillWidth")
return true;
- return QQuickDesignerSupportProperties::hasValidResetBinding(object, propertyName);
+ return
+ DesignerCustomObjectDataFork::hasValidResetBinding(object, propertyName);
+ //QQuickDesignerSupportProperties::hasValidResetBinding(object, propertyName);
}
bool hasBindingForProperty(QObject *object, QQmlContext *context, const PropertyName &propertyName, bool *hasChanged)
{
- return QQuickDesignerSupportProperties::hasBindingForProperty(object, context, propertyName, hasChanged);
+ return DesignerCustomObjectDataFork::hasBindingForProperty(object, context, propertyName, hasChanged);
+ //return QQuickDesignerSupportProperties::hasBindingForProperty(object, context, propertyName, hasChanged);
}
void setPropertyBinding(QObject *object, QQmlContext *context, const PropertyName &propertyName, const QString &expression)
{
- QQuickDesignerSupportProperties::setPropertyBinding(object, context, propertyName, expression);
+ DesignerCustomObjectDataFork::setPropertyBinding(object, context, propertyName, expression);
+
+ //QQuickDesignerSupportProperties::setPropertyBinding(object, context, propertyName, expression);
}
void emitComponentComplete(QObject *item)
@@ -317,7 +797,8 @@ void doComponentCompleteRecursive(QObject *object, NodeInstanceServer *nodeInsta
void keepBindingFromGettingDeleted(QObject *object, QQmlContext *context, const PropertyName &propertyName)
{
- QQuickDesignerSupportProperties::keepBindingFromGettingDeleted(object, context, propertyName);
+ DesignerCustomObjectDataFork::keepBindingFromGettingDeleted(object, context, propertyName);
+ //QQuickDesignerSupportProperties::keepBindingFromGettingDeleted(object, context, propertyName);
}
bool objectWasDeleted(QObject *object)
diff --git a/src/tools/sdktool/CMakeLists.txt b/src/tools/sdktool/CMakeLists.txt
index 19b8571f56..97c96909b6 100644
--- a/src/tools/sdktool/CMakeLists.txt
+++ b/src/tools/sdktool/CMakeLists.txt
@@ -7,7 +7,7 @@ project(sdktool)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
-set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
diff --git a/src/tools/wininterrupt/CMakeLists.txt b/src/tools/wininterrupt/CMakeLists.txt
index ee40ff769b..fdd5a33e4d 100644
--- a/src/tools/wininterrupt/CMakeLists.txt
+++ b/src/tools/wininterrupt/CMakeLists.txt
@@ -4,7 +4,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake")
project(wininterrupt)
-set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)