LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_sql.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 127 132 96.2 %
Date: 2025-02-20 10:14:44 Functions: 21 21 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::GDALVectorSQLAlgorithm()           */
      29             : /************************************************************************/
      30             : 
      31          14 : GDALVectorSQLAlgorithm::GDALVectorSQLAlgorithm(bool standaloneStep)
      32             :     : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
      33          14 :                                       standaloneStep)
      34             : {
      35          28 :     AddArg("sql", 0, _("SQL statement(s)"), &m_sql)
      36          14 :         .SetPositional()
      37          14 :         .SetRequired()
      38          14 :         .SetPackedValuesAllowed(false)
      39          14 :         .SetReadFromFileAtSyntaxAllowed()
      40          28 :         .SetMetaVar("<statement>|@<filename>")
      41          14 :         .SetRemoveSQLCommentsEnabled();
      42             :     AddArg("output-layer", standaloneStep ? 0 : 'l', _("Output layer name(s)"),
      43          14 :            &m_outputLayer);
      44          14 :     AddArg("dialect", 0, _("SQL dialect (e.g. OGRSQL, SQLITE)"), &m_dialect);
      45          14 : }
      46             : 
      47             : /************************************************************************/
      48             : /*                   GDALVectorSQLAlgorithmDataset                      */
      49             : /************************************************************************/
      50             : 
      51             : namespace
      52             : {
      53             : class GDALVectorSQLAlgorithmDataset final : public GDALDataset
      54             : {
      55             :     GDALDataset &m_oSrcDS;
      56             :     std::vector<OGRLayer *> m_layers{};
      57             : 
      58             :     CPL_DISALLOW_COPY_ASSIGN(GDALVectorSQLAlgorithmDataset)
      59             : 
      60             :   public:
      61           4 :     explicit GDALVectorSQLAlgorithmDataset(GDALDataset &oSrcDS)
      62           4 :         : m_oSrcDS(oSrcDS)
      63             :     {
      64           4 :     }
      65             : 
      66           8 :     ~GDALVectorSQLAlgorithmDataset() override
      67           4 :     {
      68           7 :         for (OGRLayer *poLayer : m_layers)
      69           3 :             m_oSrcDS.ReleaseResultSet(poLayer);
      70           8 :     }
      71             : 
      72           3 :     void AddLayer(OGRLayer *poLayer)
      73             :     {
      74           3 :         m_layers.push_back(poLayer);
      75           3 :     }
      76             : 
      77           9 :     int GetLayerCount() override
      78             :     {
      79           9 :         return static_cast<int>(m_layers.size());
      80             :     }
      81             : 
      82           3 :     OGRLayer *GetLayer(int idx) override
      83             :     {
      84           3 :         return idx >= 0 && idx < GetLayerCount() ? m_layers[idx] : nullptr;
      85             :     }
      86             : };
      87             : }  // namespace
      88             : 
      89             : /************************************************************************/
      90             : /*               GDALVectorSQLAlgorithmDatasetMultiLayer                */
      91             : /************************************************************************/
      92             : 
      93             : namespace
      94             : {
      95             : 
      96             : class ProxiedSQLLayer final : public OGRProxiedLayer
      97             : {
      98             :     OGRFeatureDefn *m_poLayerDefn = nullptr;
      99             : 
     100             :     CPL_DISALLOW_COPY_ASSIGN(ProxiedSQLLayer)
     101             : 
     102             :   public:
     103           4 :     ProxiedSQLLayer(const std::string &osName, OGRLayerPool *poPoolIn,
     104             :                     OpenLayerFunc pfnOpenLayerIn,
     105             :                     ReleaseLayerFunc pfnReleaseLayerIn,
     106             :                     FreeUserDataFunc pfnFreeUserDataIn, void *pUserDataIn)
     107           4 :         : OGRProxiedLayer(poPoolIn, pfnOpenLayerIn, pfnReleaseLayerIn,
     108           4 :                           pfnFreeUserDataIn, pUserDataIn)
     109             :     {
     110           4 :         SetDescription(osName.c_str());
     111           4 :     }
     112             : 
     113           8 :     ~ProxiedSQLLayer()
     114           4 :     {
     115           4 :         if (m_poLayerDefn)
     116           4 :             m_poLayerDefn->Release();
     117           8 :     }
     118             : 
     119           4 :     const char *GetName() override
     120             :     {
     121           4 :         return GetDescription();
     122             :     }
     123             : 
     124          16 :     OGRFeatureDefn *GetLayerDefn() override
     125             :     {
     126          16 :         if (!m_poLayerDefn)
     127             :         {
     128           4 :             m_poLayerDefn = OGRProxiedLayer::GetLayerDefn()->Clone();
     129           4 :             m_poLayerDefn->SetName(GetDescription());
     130             :         }
     131          16 :         return m_poLayerDefn;
     132             :     }
     133             : };
     134             : 
     135             : class GDALVectorSQLAlgorithmDatasetMultiLayer final : public GDALDataset
     136             : {
     137             :     // We can't safely have 2 SQL layers active simultaneously on the same
     138             :     // source dataset. So each time we access one, we must close the last
     139             :     // active one.
     140             :     OGRLayerPool m_oPool{1};
     141             :     GDALDataset &m_oSrcDS;
     142             :     std::vector<std::unique_ptr<OGRLayer>> m_layers{};
     143             : 
     144             :     struct UserData
     145             :     {
     146             :         GDALDataset &oSrcDS;
     147             :         std::string osSQL{};
     148             :         std::string osDialect{};
     149             :         std::string osLayerName{};
     150             : 
     151           4 :         UserData(GDALDataset &oSrcDSIn, const std::string &osSQLIn,
     152             :                  const std::string &osDialectIn,
     153             :                  const std::string &osLayerNameIn)
     154           4 :             : oSrcDS(oSrcDSIn), osSQL(osSQLIn), osDialect(osDialectIn),
     155           4 :               osLayerName(osLayerNameIn)
     156             :         {
     157           4 :         }
     158             :         CPL_DISALLOW_COPY_ASSIGN(UserData)
     159             :     };
     160             : 
     161             :     CPL_DISALLOW_COPY_ASSIGN(GDALVectorSQLAlgorithmDatasetMultiLayer)
     162             : 
     163             :   public:
     164           2 :     explicit GDALVectorSQLAlgorithmDatasetMultiLayer(GDALDataset &oSrcDS)
     165           2 :         : m_oSrcDS(oSrcDS)
     166             :     {
     167           2 :     }
     168             : 
     169           4 :     void AddLayer(const std::string &osSQL, const std::string &osDialect,
     170             :                   const std::string &osLayerName)
     171             :     {
     172           4 :         const auto OpenLayer = [](void *pUserDataIn)
     173             :         {
     174           4 :             UserData *pUserData = static_cast<UserData *>(pUserDataIn);
     175           4 :             return pUserData->oSrcDS.ExecuteSQL(
     176             :                 pUserData->osSQL.c_str(), nullptr,
     177           4 :                 pUserData->osDialect.empty() ? nullptr
     178           4 :                                              : pUserData->osDialect.c_str());
     179             :         };
     180             : 
     181           4 :         const auto CloseLayer = [](OGRLayer *poLayer, void *pUserDataIn)
     182             :         {
     183           4 :             UserData *pUserData = static_cast<UserData *>(pUserDataIn);
     184           4 :             pUserData->oSrcDS.ReleaseResultSet(poLayer);
     185           4 :         };
     186             : 
     187           4 :         const auto DeleteUserData = [](void *pUserDataIn)
     188           4 :         { delete static_cast<UserData *>(pUserDataIn); };
     189             : 
     190           4 :         auto pUserData = new UserData(m_oSrcDS, osSQL, osDialect, osLayerName);
     191             :         auto poLayer = std::make_unique<ProxiedSQLLayer>(
     192           0 :             osLayerName, &m_oPool, OpenLayer, CloseLayer, DeleteUserData,
     193           4 :             pUserData);
     194           4 :         m_layers.push_back(std::move(poLayer));
     195           4 :     }
     196             : 
     197           8 :     int GetLayerCount() override
     198             :     {
     199           8 :         return static_cast<int>(m_layers.size());
     200             :     }
     201             : 
     202           4 :     OGRLayer *GetLayer(int idx) override
     203             :     {
     204           4 :         return idx >= 0 && idx < GetLayerCount() ? m_layers[idx].get()
     205           4 :                                                  : nullptr;
     206             :     }
     207             : };
     208             : }  // namespace
     209             : 
     210             : /************************************************************************/
     211             : /*                 GDALVectorSQLAlgorithm::RunStep()                    */
     212             : /************************************************************************/
     213             : 
     214           8 : bool GDALVectorSQLAlgorithm::RunStep(GDALProgressFunc, void *)
     215             : {
     216           8 :     CPLAssert(m_inputDataset.GetDatasetRef());
     217           8 :     CPLAssert(m_outputDataset.GetName().empty());
     218           8 :     CPLAssert(!m_outputDataset.GetDatasetRef());
     219             : 
     220           8 :     if (!m_outputLayer.empty() && m_outputLayer.size() != m_sql.size())
     221             :     {
     222           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     223             :                     "There should be as many layer names in --output-layer as "
     224             :                     "in --statement");
     225           1 :         return false;
     226             :     }
     227             : 
     228           7 :     auto poSrcDS = m_inputDataset.GetDatasetRef();
     229             : 
     230           7 :     if (m_sql.size() == 1)
     231             :     {
     232           4 :         auto outDS = std::make_unique<GDALVectorSQLAlgorithmDataset>(*poSrcDS);
     233           4 :         outDS->SetDescription(poSrcDS->GetDescription());
     234             : 
     235           4 :         const auto nErrorCounter = CPLGetErrorCounter();
     236           8 :         OGRLayer *poLayer = poSrcDS->ExecuteSQL(
     237           4 :             m_sql[0].c_str(), nullptr,
     238           5 :             m_dialect.empty() ? nullptr : m_dialect.c_str());
     239           4 :         if (!poLayer)
     240             :         {
     241           1 :             if (nErrorCounter == CPLGetErrorCounter())
     242             :             {
     243           0 :                 ReportError(CE_Failure, CPLE_AppDefined,
     244             :                             "Execution of the SQL statement '%s' did not "
     245             :                             "result in a result layer.",
     246           0 :                             m_sql[0].c_str());
     247             :             }
     248           1 :             return false;
     249             :         }
     250             : 
     251           3 :         if (!m_outputLayer.empty())
     252             :         {
     253           1 :             const std::string &osLayerName = m_outputLayer[0];
     254           1 :             poLayer->GetLayerDefn()->SetName(osLayerName.c_str());
     255           1 :             poLayer->SetDescription(osLayerName.c_str());
     256             :         }
     257           3 :         outDS->AddLayer(poLayer);
     258           3 :         m_outputDataset.Set(std::move(outDS));
     259             :     }
     260             :     else
     261             :     {
     262             :         // First pass to check all statements are valid and figure out layer
     263             :         // names
     264           3 :         std::set<std::string> setOutputLayerNames;
     265           3 :         std::vector<std::string> aosLayerNames;
     266           8 :         for (const std::string &sql : m_sql)
     267             :         {
     268           6 :             const auto nErrorCounter = CPLGetErrorCounter();
     269           6 :             auto poLayer = poSrcDS->ExecuteSQL(
     270             :                 sql.c_str(), nullptr,
     271           6 :                 m_dialect.empty() ? nullptr : m_dialect.c_str());
     272           6 :             if (!poLayer)
     273             :             {
     274           1 :                 if (nErrorCounter == CPLGetErrorCounter())
     275             :                 {
     276           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
     277             :                                 "Execution of the SQL statement '%s' did not "
     278             :                                 "result in a result layer.",
     279             :                                 sql.c_str());
     280             :                 }
     281           1 :                 return false;
     282             :             }
     283             : 
     284          10 :             std::string osLayerName;
     285             : 
     286           5 :             if (!m_outputLayer.empty())
     287             :             {
     288           2 :                 osLayerName = m_outputLayer[aosLayerNames.size()];
     289             :             }
     290           6 :             else if (cpl::contains(setOutputLayerNames,
     291           3 :                                    poLayer->GetDescription()))
     292             :             {
     293           1 :                 int num = 1;
     294           0 :                 do
     295             :                 {
     296           1 :                     osLayerName = poLayer->GetDescription();
     297           1 :                     ++num;
     298           1 :                     osLayerName += std::to_string(num);
     299           1 :                 } while (cpl::contains(setOutputLayerNames, osLayerName));
     300             :             }
     301             : 
     302           5 :             if (!osLayerName.empty())
     303             :             {
     304           3 :                 poLayer->GetLayerDefn()->SetName(osLayerName.c_str());
     305           3 :                 poLayer->SetDescription(osLayerName.c_str());
     306             :             }
     307           5 :             setOutputLayerNames.insert(poLayer->GetDescription());
     308           5 :             aosLayerNames.push_back(poLayer->GetDescription());
     309             : 
     310           5 :             poSrcDS->ReleaseResultSet(poLayer);
     311             :         }
     312             : 
     313             :         auto outDS =
     314           2 :             std::make_unique<GDALVectorSQLAlgorithmDatasetMultiLayer>(*poSrcDS);
     315           2 :         outDS->SetDescription(poSrcDS->GetDescription());
     316             : 
     317           6 :         for (size_t i = 0; i < aosLayerNames.size(); ++i)
     318             :         {
     319           4 :             outDS->AddLayer(m_sql[i], m_dialect, aosLayerNames[i]);
     320             :         }
     321             : 
     322           2 :         m_outputDataset.Set(std::move(outDS));
     323             :     }
     324             : 
     325           5 :     return true;
     326             : }
     327             : 
     328             : //! @endcond

Generated by: LCOV version 1.14