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

Generated by: LCOV version 1.14