LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_sql.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 154 155 99.4 %
Date: 2025-06-19 12:30:01 Functions: 22 22 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  "sql" step of "vector pipeline"
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalalg_vector_sql.h"
      14             : 
      15             : #include "gdal_priv.h"
      16             : #include "ogrsf_frmts.h"
      17             : #include "ogrlayerpool.h"
      18             : 
      19             : #include <set>
      20             : 
      21             : //! @cond Doxygen_Suppress
      22             : 
      23             : #ifndef _
      24             : #define _(x) (x)
      25             : #endif
      26             : 
      27             : /************************************************************************/
      28             : /*           GDALVectorSQLAlgorithm::GetConstructorOptions()            */
      29             : /************************************************************************/
      30             : 
      31             : /* static */ GDALVectorSQLAlgorithm::ConstructorOptions
      32          33 : GDALVectorSQLAlgorithm::GetConstructorOptions(bool standaloneStep)
      33             : {
      34          33 :     ConstructorOptions opts;
      35          33 :     opts.SetStandaloneStep(standaloneStep);
      36          33 :     opts.SetOutputDatasetRequired(false);
      37          33 :     opts.SetUpdateMutualExclusionGroup("output-update");
      38          33 :     opts.SetOutputDatasetMutualExclusionGroup("output-update");
      39          33 :     opts.SetAddInputLayerNameArgument(false);
      40          33 :     return opts;
      41             : }
      42             : 
      43             : /************************************************************************/
      44             : /*           GDALVectorSQLAlgorithm::GDALVectorSQLAlgorithm()           */
      45             : /************************************************************************/
      46             : 
      47          33 : GDALVectorSQLAlgorithm::GDALVectorSQLAlgorithm(bool standaloneStep)
      48             :     : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
      49          33 :                                       GetConstructorOptions(standaloneStep))
      50             : {
      51          66 :     auto &sqlArg = AddArg("sql", 0, _("SQL statement(s)"), &m_sql)
      52          33 :                        .SetRequired()
      53          33 :                        .SetPackedValuesAllowed(false)
      54          33 :                        .SetReadFromFileAtSyntaxAllowed()
      55          66 :                        .SetMetaVar("<statement>|@<filename>")
      56          33 :                        .SetRemoveSQLCommentsEnabled();
      57          33 :     if (!standaloneStep)
      58          19 :         sqlArg.SetPositional();
      59             :     AddArg("output-layer", standaloneStep ? 0 : 'l', _("Output layer name(s)"),
      60          33 :            &m_outputLayer);
      61          33 :     AddArg("dialect", 0, _("SQL dialect (e.g. OGRSQL, SQLITE)"), &m_dialect);
      62          33 :     AddArg("quiet", 'q', _("Quiet mode"), &m_quiet);
      63          33 : }
      64             : 
      65             : /************************************************************************/
      66             : /*                   GDALVectorSQLAlgorithmDataset                      */
      67             : /************************************************************************/
      68             : 
      69             : namespace
      70             : {
      71             : class GDALVectorSQLAlgorithmDataset final : public GDALDataset
      72             : {
      73             :     GDALDataset &m_oSrcDS;
      74             :     std::vector<OGRLayer *> m_layers{};
      75             : 
      76             :     CPL_DISALLOW_COPY_ASSIGN(GDALVectorSQLAlgorithmDataset)
      77             : 
      78             :   public:
      79           5 :     explicit GDALVectorSQLAlgorithmDataset(GDALDataset &oSrcDS)
      80           5 :         : m_oSrcDS(oSrcDS)
      81             :     {
      82           5 :     }
      83             : 
      84          10 :     ~GDALVectorSQLAlgorithmDataset() override
      85           5 :     {
      86           9 :         for (OGRLayer *poLayer : m_layers)
      87           4 :             m_oSrcDS.ReleaseResultSet(poLayer);
      88          10 :     }
      89             : 
      90           4 :     void AddLayer(OGRLayer *poLayer)
      91             :     {
      92           4 :         m_layers.push_back(poLayer);
      93           4 :     }
      94             : 
      95          12 :     int GetLayerCount() override
      96             :     {
      97          12 :         return static_cast<int>(m_layers.size());
      98             :     }
      99             : 
     100           4 :     OGRLayer *GetLayer(int idx) override
     101             :     {
     102           4 :         return idx >= 0 && idx < GetLayerCount() ? m_layers[idx] : nullptr;
     103             :     }
     104             : };
     105             : }  // namespace
     106             : 
     107             : /************************************************************************/
     108             : /*               GDALVectorSQLAlgorithmDatasetMultiLayer                */
     109             : /************************************************************************/
     110             : 
     111             : namespace
     112             : {
     113             : 
     114             : class ProxiedSQLLayer final : public OGRProxiedLayer
     115             : {
     116             :     OGRFeatureDefn *m_poLayerDefn = nullptr;
     117             : 
     118             :     CPL_DISALLOW_COPY_ASSIGN(ProxiedSQLLayer)
     119             : 
     120             :   public:
     121           4 :     ProxiedSQLLayer(const std::string &osName, OGRLayerPool *poPoolIn,
     122             :                     OpenLayerFunc pfnOpenLayerIn,
     123             :                     ReleaseLayerFunc pfnReleaseLayerIn,
     124             :                     FreeUserDataFunc pfnFreeUserDataIn, void *pUserDataIn)
     125           4 :         : OGRProxiedLayer(poPoolIn, pfnOpenLayerIn, pfnReleaseLayerIn,
     126           4 :                           pfnFreeUserDataIn, pUserDataIn)
     127             :     {
     128           4 :         SetDescription(osName.c_str());
     129           4 :     }
     130             : 
     131           8 :     ~ProxiedSQLLayer()
     132           4 :     {
     133           4 :         if (m_poLayerDefn)
     134           4 :             m_poLayerDefn->Release();
     135           8 :     }
     136             : 
     137           4 :     const char *GetName() override
     138             :     {
     139           4 :         return GetDescription();
     140             :     }
     141             : 
     142          16 :     OGRFeatureDefn *GetLayerDefn() override
     143             :     {
     144          16 :         if (!m_poLayerDefn)
     145             :         {
     146           4 :             m_poLayerDefn = OGRProxiedLayer::GetLayerDefn()->Clone();
     147           4 :             m_poLayerDefn->SetName(GetDescription());
     148             :         }
     149          16 :         return m_poLayerDefn;
     150             :     }
     151             : };
     152             : 
     153             : class GDALVectorSQLAlgorithmDatasetMultiLayer final : public GDALDataset
     154             : {
     155             :     // We can't safely have 2 SQL layers active simultaneously on the same
     156             :     // source dataset. So each time we access one, we must close the last
     157             :     // active one.
     158             :     OGRLayerPool m_oPool{1};
     159             :     GDALDataset &m_oSrcDS;
     160             :     std::vector<std::unique_ptr<ProxiedSQLLayer>> m_layers{};
     161             : 
     162             :     struct UserData
     163             :     {
     164             :         GDALDataset &oSrcDS;
     165             :         std::string osSQL{};
     166             :         std::string osDialect{};
     167             :         std::string osLayerName{};
     168             : 
     169           4 :         UserData(GDALDataset &oSrcDSIn, const std::string &osSQLIn,
     170             :                  const std::string &osDialectIn,
     171             :                  const std::string &osLayerNameIn)
     172           4 :             : oSrcDS(oSrcDSIn), osSQL(osSQLIn), osDialect(osDialectIn),
     173           4 :               osLayerName(osLayerNameIn)
     174             :         {
     175           4 :         }
     176             :         CPL_DISALLOW_COPY_ASSIGN(UserData)
     177             :     };
     178             : 
     179             :     CPL_DISALLOW_COPY_ASSIGN(GDALVectorSQLAlgorithmDatasetMultiLayer)
     180             : 
     181             :   public:
     182           2 :     explicit GDALVectorSQLAlgorithmDatasetMultiLayer(GDALDataset &oSrcDS)
     183           2 :         : m_oSrcDS(oSrcDS)
     184             :     {
     185           2 :     }
     186             : 
     187           4 :     void AddLayer(const std::string &osSQL, const std::string &osDialect,
     188             :                   const std::string &osLayerName)
     189             :     {
     190           4 :         const auto OpenLayer = [](void *pUserDataIn)
     191             :         {
     192           4 :             UserData *pUserData = static_cast<UserData *>(pUserDataIn);
     193           4 :             return pUserData->oSrcDS.ExecuteSQL(
     194             :                 pUserData->osSQL.c_str(), nullptr,
     195           4 :                 pUserData->osDialect.empty() ? nullptr
     196           4 :                                              : pUserData->osDialect.c_str());
     197             :         };
     198             : 
     199           4 :         const auto CloseLayer = [](OGRLayer *poLayer, void *pUserDataIn)
     200             :         {
     201           4 :             UserData *pUserData = static_cast<UserData *>(pUserDataIn);
     202           4 :             pUserData->oSrcDS.ReleaseResultSet(poLayer);
     203           4 :         };
     204             : 
     205           4 :         const auto DeleteUserData = [](void *pUserDataIn)
     206           4 :         { delete static_cast<UserData *>(pUserDataIn); };
     207             : 
     208           4 :         auto pUserData = new UserData(m_oSrcDS, osSQL, osDialect, osLayerName);
     209           4 :         m_layers.emplace_back(std::make_unique<ProxiedSQLLayer>(
     210           0 :             osLayerName, &m_oPool, OpenLayer, CloseLayer, DeleteUserData,
     211           4 :             pUserData));
     212           4 :     }
     213             : 
     214           8 :     int GetLayerCount() override
     215             :     {
     216           8 :         return static_cast<int>(m_layers.size());
     217             :     }
     218             : 
     219           4 :     OGRLayer *GetLayer(int idx) override
     220             :     {
     221           4 :         return idx >= 0 && idx < GetLayerCount() ? m_layers[idx].get()
     222           4 :                                                  : nullptr;
     223             :     }
     224             : };
     225             : }  // namespace
     226             : 
     227             : /************************************************************************/
     228             : /*                 GDALVectorSQLAlgorithm::RunStep()                    */
     229             : /************************************************************************/
     230             : 
     231          12 : bool GDALVectorSQLAlgorithm::RunStep(GDALPipelineStepRunContext &)
     232             : {
     233          12 :     auto poSrcDS = m_inputDataset[0].GetDatasetRef();
     234          12 :     CPLAssert(poSrcDS);
     235             : 
     236          12 :     auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
     237          12 :     if (outputArg && !outputArg->IsExplicitlySet())
     238             :     {
     239             :         // Mode where we update a dataset.
     240           5 :         for (const auto &sql : m_sql)
     241             :         {
     242           3 :             const auto nErrorCounter = CPLGetErrorCounter();
     243           3 :             OGRLayer *poLayer = poSrcDS->ExecuteSQL(
     244             :                 sql.c_str(), nullptr,
     245           3 :                 m_dialect.empty() ? nullptr : m_dialect.c_str());
     246           3 :             const bool bResultSet = poLayer != nullptr;
     247           3 :             poSrcDS->ReleaseResultSet(poLayer);
     248           3 :             if (bResultSet && !m_quiet)
     249             :             {
     250           1 :                 ReportError(CE_Warning, CPLE_AppDefined,
     251             :                             "Execution of the SQL statement '%s' returned a "
     252             :                             "result set. It will be ignored. You may silence "
     253             :                             "this warning with the 'quiet' argument.",
     254             :                             sql.c_str());
     255             :             }
     256           3 :             else if (CPLGetErrorCounter() > nErrorCounter &&
     257           1 :                      CPLGetLastErrorType() == CE_Failure)
     258             :             {
     259           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     260             :                             "Execution of the SQL statement '%s' failed.%s",
     261             :                             sql.c_str(),
     262           1 :                             m_update ? ""
     263             :                                      : " Perhaps you need to specify the "
     264             :                                        "'update' argument?");
     265           1 :                 return false;
     266             :             }
     267             :         }
     268           2 :         return true;
     269             :     }
     270             : 
     271           9 :     CPLAssert(m_outputDataset.GetName().empty());
     272           9 :     CPLAssert(!m_outputDataset.GetDatasetRef());
     273             : 
     274           9 :     if (!m_outputLayer.empty() && m_outputLayer.size() != m_sql.size())
     275             :     {
     276           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     277             :                     "There should be as many layer names in --output-layer as "
     278             :                     "in --statement");
     279           1 :         return false;
     280             :     }
     281             : 
     282           8 :     if (m_sql.size() == 1)
     283             :     {
     284           5 :         auto outDS = std::make_unique<GDALVectorSQLAlgorithmDataset>(*poSrcDS);
     285           5 :         outDS->SetDescription(poSrcDS->GetDescription());
     286             : 
     287           5 :         const auto nErrorCounter = CPLGetErrorCounter();
     288          10 :         OGRLayer *poLayer = poSrcDS->ExecuteSQL(
     289           5 :             m_sql[0].c_str(), nullptr,
     290           6 :             m_dialect.empty() ? nullptr : m_dialect.c_str());
     291           5 :         if (!poLayer)
     292             :         {
     293           1 :             if (nErrorCounter == CPLGetErrorCounter())
     294             :             {
     295           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     296             :                             "Execution of the SQL statement '%s' did not "
     297             :                             "result in a result layer.",
     298           1 :                             m_sql[0].c_str());
     299             :             }
     300           1 :             return false;
     301             :         }
     302             : 
     303           4 :         if (!m_outputLayer.empty())
     304             :         {
     305           1 :             const std::string &osLayerName = m_outputLayer[0];
     306           1 :             poLayer->GetLayerDefn()->SetName(osLayerName.c_str());
     307           1 :             poLayer->SetDescription(osLayerName.c_str());
     308             :         }
     309           4 :         outDS->AddLayer(poLayer);
     310           4 :         m_outputDataset.Set(std::move(outDS));
     311             :     }
     312             :     else
     313             :     {
     314             :         // First pass to check all statements are valid and figure out layer
     315             :         // names
     316           3 :         std::set<std::string> setOutputLayerNames;
     317           3 :         std::vector<std::string> aosLayerNames;
     318           8 :         for (const std::string &sql : m_sql)
     319             :         {
     320           6 :             const auto nErrorCounter = CPLGetErrorCounter();
     321           6 :             auto poLayer = poSrcDS->ExecuteSQL(
     322             :                 sql.c_str(), nullptr,
     323           6 :                 m_dialect.empty() ? nullptr : m_dialect.c_str());
     324           6 :             if (!poLayer)
     325             :             {
     326           1 :                 if (nErrorCounter == CPLGetErrorCounter())
     327             :                 {
     328           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
     329             :                                 "Execution of the SQL statement '%s' did not "
     330             :                                 "result in a result layer.",
     331             :                                 sql.c_str());
     332             :                 }
     333           1 :                 return false;
     334             :             }
     335             : 
     336          10 :             std::string osLayerName;
     337             : 
     338           5 :             if (!m_outputLayer.empty())
     339             :             {
     340           2 :                 osLayerName = m_outputLayer[aosLayerNames.size()];
     341             :             }
     342             :             else
     343             :             {
     344           3 :                 osLayerName = poLayer->GetDescription();
     345           3 :                 for (int num = 2;
     346           4 :                      cpl::contains(setOutputLayerNames, osLayerName); ++num)
     347             :                 {
     348           1 :                     osLayerName = poLayer->GetDescription();
     349           1 :                     osLayerName += std::to_string(num);
     350             :                 }
     351             :             }
     352             : 
     353           5 :             if (!osLayerName.empty())
     354             :             {
     355           5 :                 poLayer->GetLayerDefn()->SetName(osLayerName.c_str());
     356           5 :                 poLayer->SetDescription(osLayerName.c_str());
     357             :             }
     358           5 :             setOutputLayerNames.insert(poLayer->GetDescription());
     359           5 :             aosLayerNames.push_back(poLayer->GetDescription());
     360             : 
     361           5 :             poSrcDS->ReleaseResultSet(poLayer);
     362             :         }
     363             : 
     364             :         auto outDS =
     365           2 :             std::make_unique<GDALVectorSQLAlgorithmDatasetMultiLayer>(*poSrcDS);
     366           2 :         outDS->SetDescription(poSrcDS->GetDescription());
     367             : 
     368           6 :         for (size_t i = 0; i < aosLayerNames.size(); ++i)
     369             :         {
     370           4 :             outDS->AddLayer(m_sql[i], m_dialect, aosLayerNames[i]);
     371             :         }
     372             : 
     373           2 :         m_outputDataset.Set(std::move(outDS));
     374             :     }
     375             : 
     376           6 :     return true;
     377             : }
     378             : 
     379             : GDALVectorSQLAlgorithmStandalone::~GDALVectorSQLAlgorithmStandalone() = default;
     380             : 
     381             : //! @endcond

Generated by: LCOV version 1.14