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

Generated by: LCOV version 1.14