LCOV - code coverage report
Current view: top level - frmts/tiledb - tiledbmultidimgroup.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 183 241 75.9 %
Date: 2024-05-13 13:33:37 Functions: 19 19 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL TileDB Driver
       4             :  * Purpose:  Implement GDAL TileDB multidimensional support based on https://www.tiledb.io
       5             :  * Author:   TileDB, Inc
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2023, TileDB, Inc
       9             :  *
      10             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include "tiledbmultidim.h"
      30             : 
      31             : #include "memmultidim.h"
      32             : 
      33             : /************************************************************************/
      34             : /*                   TileDBGroup::~TileDBGroup()                        */
      35             : /************************************************************************/
      36             : 
      37         116 : TileDBGroup::~TileDBGroup()
      38             : {
      39          58 :     m_oMapGroups.clear();
      40          58 :     m_oMapArrays.clear();
      41          58 :     if (m_poTileDBGroup)
      42             :     {
      43             :         try
      44             :         {
      45          58 :             m_poTileDBGroup->close();
      46          58 :             m_poTileDBGroup.reset();
      47             :         }
      48           0 :         catch (const std::exception &e)
      49             :         {
      50             :             // Will leak memory, but better that than crashing
      51             :             // Cf https://github.com/TileDB-Inc/TileDB/issues/4101
      52           0 :             CPL_IGNORE_RET_VAL(m_poTileDBGroup.release());
      53           0 :             CPLError(CE_Failure, CPLE_AppDefined,
      54           0 :                      "TileDBGroup::~TileDBGroup(): %s", e.what());
      55             :         }
      56             :     }
      57         116 : }
      58             : 
      59             : /************************************************************************/
      60             : /*                   TileDBGroup::GetGroupNames()                       */
      61             : /************************************************************************/
      62             : 
      63             : std::vector<std::string>
      64           7 : TileDBGroup::GetGroupNames(CSLConstList /*papszOptions*/) const
      65             : {
      66           7 :     if (!EnsureOpenAs(TILEDB_READ))
      67           0 :         return {};
      68             : 
      69          14 :     std::vector<std::string> aosNames;
      70          17 :     for (uint64_t i = 0; i < m_poTileDBGroup->member_count(); ++i)
      71             :     {
      72          20 :         auto obj = m_poTileDBGroup->member(i);
      73          10 :         if (obj.type() == tiledb::Object::Type::Group)
      74             :         {
      75           3 :             if (obj.name().has_value())
      76           3 :                 aosNames.push_back(*(obj.name()));
      77             :             else
      78           0 :                 aosNames.push_back(CPLGetFilename(obj.uri().c_str()));
      79             :         }
      80             :     }
      81           7 :     return aosNames;
      82             : }
      83             : 
      84             : /************************************************************************/
      85             : /*                   TileDBGroup::OpenFromDisk()                        */
      86             : /************************************************************************/
      87             : 
      88             : /* static */
      89          31 : std::shared_ptr<TileDBGroup> TileDBGroup::OpenFromDisk(
      90             :     const std::shared_ptr<TileDBSharedResource> &poSharedResource,
      91             :     const std::string &osParentName, const std::string &osName,
      92             :     const std::string &osPath)
      93             : {
      94             :     const auto eType =
      95          31 :         tiledb::Object::object(poSharedResource->GetCtx(), osPath).type();
      96          31 :     if (eType != tiledb::Object::Type::Group)
      97             :     {
      98           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s is not a TileDB group",
      99             :                  osPath.c_str());
     100           0 :         return nullptr;
     101             :     }
     102             : 
     103             :     auto poTileDBGroup = std::make_unique<tiledb::Group>(
     104          62 :         poSharedResource->GetCtx(), osPath, TILEDB_READ);
     105             : 
     106             :     auto poGroup =
     107          62 :         TileDBGroup::Create(poSharedResource, osParentName, osName, osPath);
     108          31 :     poGroup->m_poTileDBGroup = std::move(poTileDBGroup);
     109          31 :     return poGroup;
     110             : }
     111             : 
     112             : /************************************************************************/
     113             : /*                   TileDBGroup::CreateOnDisk()                        */
     114             : /************************************************************************/
     115             : 
     116             : /* static */
     117          27 : std::shared_ptr<TileDBGroup> TileDBGroup::CreateOnDisk(
     118             :     const std::shared_ptr<TileDBSharedResource> &poSharedResource,
     119             :     const std::string &osParentName, const std::string &osName,
     120             :     const std::string &osPath)
     121             : {
     122             :     try
     123             :     {
     124          27 :         tiledb::create_group(poSharedResource->GetCtx(), osPath);
     125             :     }
     126           0 :     catch (const std::exception &e)
     127             :     {
     128           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     129           0 :         return nullptr;
     130             :     }
     131             : 
     132             :     auto poTileDBGroup = std::make_unique<tiledb::Group>(
     133          54 :         poSharedResource->GetCtx(), osPath, TILEDB_WRITE);
     134             : 
     135             :     auto poGroup =
     136          54 :         TileDBGroup::Create(poSharedResource, osParentName, osName, osPath);
     137          27 :     poGroup->m_poTileDBGroup = std::move(poTileDBGroup);
     138          27 :     return poGroup;
     139             : }
     140             : 
     141             : /************************************************************************/
     142             : /*                   TileDBGroup::EnsureOpenAs()                        */
     143             : /************************************************************************/
     144             : 
     145         160 : bool TileDBGroup::EnsureOpenAs(tiledb_query_type_t mode) const
     146             : {
     147         160 :     if (!m_poTileDBGroup)
     148           0 :         return false;
     149         160 :     if (m_poTileDBGroup->query_type() == mode && m_poTileDBGroup->is_open())
     150          72 :         return true;
     151             :     try
     152             :     {
     153          88 :         m_poTileDBGroup->close();
     154          88 :         m_poTileDBGroup->open(mode);
     155             :     }
     156           0 :     catch (const std::exception &e)
     157             :     {
     158           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     159           0 :         m_poTileDBGroup.reset();
     160           0 :         return false;
     161             :     }
     162          88 :     return true;
     163             : }
     164             : 
     165             : /************************************************************************/
     166             : /*                 TileDBGroup::HasObjectOfSameName()                   */
     167             : /************************************************************************/
     168             : 
     169          39 : bool TileDBGroup::HasObjectOfSameName(const std::string &osName) const
     170             : {
     171          39 :     if (m_oMapGroups.find(osName) != m_oMapGroups.end())
     172             :     {
     173           2 :         CPLError(CE_Failure, CPLE_AppDefined, "A group named %s already exists",
     174             :                  osName.c_str());
     175           2 :         return true;
     176             :     }
     177          37 :     if (m_oMapArrays.find(osName) != m_oMapArrays.end())
     178             :     {
     179           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     180             :                  "An array named %s already exists", osName.c_str());
     181           1 :         return true;
     182             :     }
     183             : 
     184          36 :     if (!EnsureOpenAs(TILEDB_READ))
     185           0 :         return {};
     186          47 :     for (uint64_t i = 0; i < m_poTileDBGroup->member_count(); ++i)
     187             :     {
     188          11 :         auto obj = m_poTileDBGroup->member(i);
     189          11 :         std::string osObjName = obj.name().has_value()
     190          22 :                                     ? *(obj.name())
     191          22 :                                     : CPLGetFilename(obj.uri().c_str());
     192          11 :         if (osName == osObjName)
     193             :         {
     194           0 :             if (obj.type() == tiledb::Object::Type::Group)
     195             :             {
     196           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     197             :                          "A group named %s already exists", osName.c_str());
     198           0 :                 return true;
     199             :             }
     200           0 :             else if (obj.type() == tiledb::Object::Type::Array)
     201             :             {
     202           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     203             :                          "An array named %s already exists", osName.c_str());
     204           0 :                 return true;
     205             :             }
     206             :         }
     207             :     }
     208          36 :     return false;
     209             : }
     210             : 
     211             : /************************************************************************/
     212             : /*                   TileDBGroup::OpenGroup()                           */
     213             : /************************************************************************/
     214             : 
     215             : std::shared_ptr<GDALGroup>
     216           3 : TileDBGroup::OpenGroup(const std::string &osName,
     217             :                        CSLConstList /*papszOptions*/) const
     218             : {
     219           3 :     auto oIter = m_oMapGroups.find(osName);
     220           3 :     if (oIter != m_oMapGroups.end())
     221             :     {
     222           1 :         return oIter->second;
     223             :     }
     224           2 :     if (!m_poTileDBGroup)
     225           0 :         return nullptr;
     226             : 
     227             :     // Try to match by member name property first, and if not use the
     228             :     // last part of their URI
     229           4 :     std::string osSubPath;
     230           4 :     std::string osSubPathCandidate;
     231           2 :     for (uint64_t i = 0; i < m_poTileDBGroup->member_count(); ++i)
     232             :     {
     233           1 :         auto obj = m_poTileDBGroup->member(i);
     234           1 :         if (obj.type() == tiledb::Object::Type::Group)
     235             :         {
     236           1 :             if (obj.name().has_value() && *(obj.name()) == osName)
     237             :             {
     238           1 :                 osSubPath = obj.uri();
     239           1 :                 break;
     240             :             }
     241           0 :             else if (CPLGetFilename(obj.uri().c_str()) == osName)
     242             :             {
     243           0 :                 osSubPathCandidate = osSubPath;
     244             :             }
     245             :         }
     246             :     }
     247           2 :     if (osSubPath.empty())
     248           1 :         osSubPath = std::move(osSubPathCandidate);
     249           2 :     if (osSubPath.empty())
     250           1 :         return nullptr;
     251             : 
     252             :     auto poSubGroup = TileDBGroup::OpenFromDisk(
     253           2 :         m_poSharedResource, m_osFullName, osName, osSubPath);
     254           1 :     if (!poSubGroup)
     255           0 :         return nullptr;
     256             : 
     257           1 :     m_oMapGroups[osName] = poSubGroup;
     258             : 
     259           1 :     return poSubGroup;
     260             : }
     261             : 
     262             : /************************************************************************/
     263             : /*                   TileDBGroup::CreateGroup()                         */
     264             : /************************************************************************/
     265             : 
     266           5 : std::shared_ptr<GDALGroup> TileDBGroup::CreateGroup(const std::string &osName,
     267             :                                                     CSLConstList papszOptions)
     268             : {
     269           5 :     if (!m_poSharedResource->IsUpdatable())
     270             :     {
     271           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     272             :                  "Dataset not open in update mode");
     273           1 :         return nullptr;
     274             :     }
     275             : 
     276           4 :     if (HasObjectOfSameName(osName))
     277           2 :         return nullptr;
     278             : 
     279           4 :     std::string osSubPath = m_poTileDBGroup->uri() + "/" +
     280           6 :                             TileDBSharedResource::SanitizeNameForPath(osName);
     281           2 :     const char *pszURI = CSLFetchNameValue(papszOptions, "URI");
     282           2 :     if (pszURI)
     283           0 :         osSubPath = pszURI;
     284             :     auto poSubGroup =
     285           4 :         CreateOnDisk(m_poSharedResource, m_osFullName, osName, osSubPath);
     286           2 :     if (!poSubGroup)
     287           0 :         return nullptr;
     288             : 
     289           2 :     if (!AddMember(osSubPath, osName))
     290           0 :         return nullptr;
     291           2 :     m_oMapGroups[osName] = poSubGroup;
     292             : 
     293           2 :     return poSubGroup;
     294             : }
     295             : 
     296             : /************************************************************************/
     297             : /*                   TileDBGroup::AddMember()                           */
     298             : /************************************************************************/
     299             : 
     300          32 : bool TileDBGroup::AddMember(const std::string &osPath,
     301             :                             const std::string &osName)
     302             : {
     303          32 :     if (!EnsureOpenAs(TILEDB_WRITE))
     304           0 :         return false;
     305             : 
     306             :     try
     307             :     {
     308          32 :         m_poTileDBGroup->add_member(osPath, /* relative= */ false, osName);
     309             :     }
     310           0 :     catch (const std::exception &e)
     311             :     {
     312           0 :         CPLError(CE_Failure, CPLE_AppDefined, "AddMember() failed with: %s",
     313           0 :                  e.what());
     314           0 :         return false;
     315             :     }
     316             :     // Force close() and re-open() to avoid
     317             :     // https://github.com/TileDB-Inc/TileDB/issues/4101
     318             :     try
     319             :     {
     320          32 :         m_poTileDBGroup->close();
     321          32 :         m_poTileDBGroup->open(TILEDB_WRITE);
     322             :     }
     323           0 :     catch (const std::exception &e)
     324             :     {
     325           0 :         CPLError(CE_Failure, CPLE_AppDefined, "AddMember() failed with: %s",
     326           0 :                  e.what());
     327           0 :         m_poTileDBGroup.reset();
     328           0 :         return false;
     329             :     }
     330          32 :     return true;
     331             : }
     332             : 
     333             : /************************************************************************/
     334             : /*                    TileDBGroup::CreateDimension()                    */
     335             : /************************************************************************/
     336             : 
     337          36 : std::shared_ptr<GDALDimension> TileDBGroup::CreateDimension(
     338             :     const std::string &osName, const std::string &osType,
     339             :     const std::string &osDirection, GUInt64 nSize, CSLConstList)
     340             : {
     341          36 :     if (osName.empty())
     342             :     {
     343           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     344             :                  "Empty dimension name not supported");
     345           0 :         return nullptr;
     346             :     }
     347             : 
     348          36 :     if (m_oMapDimensions.find(osName) != m_oMapDimensions.end())
     349             :     {
     350           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     351             :                  "A dimension with same name already exists");
     352           0 :         return nullptr;
     353             :     }
     354             :     auto newDim(std::make_shared<GDALDimensionWeakIndexingVar>(
     355          72 :         GetFullName(), osName, osType, osDirection, nSize));
     356          36 :     m_oMapDimensions[osName] = newDim;
     357          36 :     return newDim;
     358             : }
     359             : 
     360             : /************************************************************************/
     361             : /*                   TileDBGroup::GetMDArrayNames()                     */
     362             : /************************************************************************/
     363             : 
     364             : std::vector<std::string>
     365          27 : TileDBGroup::GetMDArrayNames(CSLConstList /*papszOptions */) const
     366             : {
     367          27 :     if (!EnsureOpenAs(TILEDB_READ))
     368           0 :         return {};
     369             : 
     370          54 :     std::vector<std::string> aosNames;
     371          85 :     for (uint64_t i = 0; i < m_poTileDBGroup->member_count(); ++i)
     372             :     {
     373         116 :         auto obj = m_poTileDBGroup->member(i);
     374          58 :         if (obj.type() == tiledb::Object::Type::Array)
     375             :         {
     376         114 :             tiledb::ArraySchema schema(m_poSharedResource->GetCtx(), obj.uri());
     377          57 :             if (schema.array_type() == TILEDB_DENSE)
     378             :             {
     379             :                 const std::string osName =
     380         171 :                     obj.name().has_value() ? *(obj.name())
     381         171 :                                            : CPLGetFilename(obj.uri().c_str());
     382          57 :                 const auto nAttributes = schema.attribute_num();
     383          57 :                 if (nAttributes != 1)
     384             :                 {
     385           0 :                     for (uint32_t iAttr = 0; iAttr < nAttributes; ++iAttr)
     386             :                     {
     387           0 :                         aosNames.push_back(osName + "." +
     388           0 :                                            schema.attribute(iAttr).name());
     389             :                     }
     390             :                 }
     391             :                 else
     392             :                 {
     393          57 :                     aosNames.push_back(osName);
     394             :                 }
     395             :             }
     396             :         }
     397             :     }
     398             : 
     399             :     // As array creation is deferred, the above loop didn't get freshly
     400             :     // created arrays
     401          46 :     for (const auto &kv : m_oMapArrays)
     402             :     {
     403          19 :         if (std::find(aosNames.begin(), aosNames.end(), kv.first) ==
     404          38 :             aosNames.end())
     405             :         {
     406           1 :             aosNames.push_back(kv.first);
     407             :         }
     408             :     }
     409             : 
     410          27 :     return aosNames;
     411             : }
     412             : 
     413             : /************************************************************************/
     414             : /*                   TileDBGroup::OpenMDArray()                         */
     415             : /************************************************************************/
     416             : 
     417             : std::shared_ptr<GDALMDArray>
     418          62 : TileDBGroup::OpenMDArray(const std::string &osName,
     419             :                          CSLConstList papszOptions) const
     420             : {
     421          62 :     auto oIter = m_oMapArrays.find(osName);
     422          62 :     if (oIter != m_oMapArrays.end())
     423             :     {
     424          22 :         return oIter->second;
     425             :     }
     426          40 :     if (!m_poTileDBGroup)
     427           0 :         return nullptr;
     428             : 
     429          80 :     std::string osNamePrefix = osName;
     430          80 :     std::string osNameSuffix;
     431          40 :     const auto nLastDot = osName.rfind('.');
     432          40 :     if (nLastDot != std::string::npos)
     433             :     {
     434           0 :         osNamePrefix = osName.substr(0, nLastDot);
     435           0 :         osNameSuffix = osName.substr(nLastDot + 1);
     436             :     }
     437             : 
     438             :     // Try to match by member name property first, and if not use the
     439             :     // last part of their URI
     440          80 :     std::string osSubPath;
     441          80 :     std::string osSubPathCandidate;
     442          63 :     for (uint64_t i = 0; i < m_poTileDBGroup->member_count(); ++i)
     443             :     {
     444          58 :         auto obj = m_poTileDBGroup->member(i);
     445             : 
     446             :         const auto MatchNameSuffix =
     447          81 :             [this, &obj, &osName, &osNamePrefix,
     448         370 :              &osNameSuffix](const std::string &osObjName)
     449             :         {
     450         162 :             tiledb::ArraySchema schema(m_poSharedResource->GetCtx(), obj.uri());
     451          81 :             if (osNameSuffix.empty() && osObjName == osName)
     452             :             {
     453          35 :                 return true;
     454             :             }
     455          46 :             else if (osObjName == osNamePrefix && !osNameSuffix.empty() &&
     456           0 :                      schema.has_attribute(osNameSuffix))
     457             :             {
     458           0 :                 return true;
     459             :             }
     460          46 :             return false;
     461          58 :         };
     462             : 
     463          58 :         if (obj.type() == tiledb::Object::Type::Array)
     464             :         {
     465          58 :             if (obj.name().has_value() && MatchNameSuffix(*(obj.name())))
     466             :             {
     467          35 :                 osSubPath = obj.uri();
     468          35 :                 break;
     469             :             }
     470          23 :             else if (MatchNameSuffix(CPLGetFilename(obj.uri().c_str())))
     471             :             {
     472           0 :                 osSubPathCandidate = obj.uri();
     473             :             }
     474             :         }
     475             :     }
     476          40 :     if (osSubPath.empty())
     477           5 :         osSubPath = std::move(osSubPathCandidate);
     478          40 :     if (osSubPath.empty())
     479           5 :         return nullptr;
     480             : 
     481          35 :     if (m_oSetArrayInOpening.find(osName) != m_oSetArrayInOpening.end())
     482           2 :         return nullptr;
     483          33 :     m_oSetArrayInOpening.insert(osName);
     484           0 :     auto poArray = TileDBArray::OpenFromDisk(m_poSharedResource, m_pSelf.lock(),
     485          33 :                                              m_osFullName, osName, osNameSuffix,
     486          66 :                                              osSubPath, papszOptions);
     487          33 :     m_oSetArrayInOpening.erase(osName);
     488          33 :     if (!poArray)
     489           0 :         return nullptr;
     490             : 
     491          33 :     m_oMapArrays[osName] = poArray;
     492             : 
     493          33 :     return poArray;
     494             : }
     495             : 
     496             : /************************************************************************/
     497             : /*                   TileDBGroup::CreateMDArray()                       */
     498             : /************************************************************************/
     499             : 
     500          40 : std::shared_ptr<GDALMDArray> TileDBGroup::CreateMDArray(
     501             :     const std::string &osName,
     502             :     const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
     503             :     const GDALExtendedDataType &oDataType, CSLConstList papszOptions)
     504             : {
     505          40 :     if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "IN_MEMORY", "NO")))
     506             :     {
     507             :         auto poArray =
     508           8 :             MEMMDArray::Create(std::string(), osName, aoDimensions, oDataType);
     509           4 :         if (!poArray || !poArray->Init())
     510           0 :             return nullptr;
     511           4 :         return poArray;
     512             :     }
     513             : 
     514          36 :     if (!m_poSharedResource->IsUpdatable())
     515             :     {
     516           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     517             :                  "Dataset not open in update mode");
     518           1 :         return nullptr;
     519             :     }
     520             : 
     521          35 :     if (HasObjectOfSameName(osName))
     522           1 :         return nullptr;
     523             : 
     524          34 :     if (!EnsureOpenAs(TILEDB_WRITE))
     525           0 :         return nullptr;
     526             : 
     527          68 :     auto poSelf = std::dynamic_pointer_cast<TileDBGroup>(m_pSelf.lock());
     528          34 :     CPLAssert(poSelf);
     529             :     auto poArray =
     530          34 :         TileDBArray::CreateOnDisk(m_poSharedResource, poSelf, osName,
     531          68 :                                   aoDimensions, oDataType, papszOptions);
     532          34 :     if (!poArray)
     533           4 :         return nullptr;
     534             : 
     535          30 :     m_oMapArrays[osName] = poArray;
     536          30 :     return poArray;
     537             : }
     538             : 
     539             : /************************************************************************/
     540             : /*                  TileDBGroup::CreateAttribute()                      */
     541             : /************************************************************************/
     542             : 
     543           5 : std::shared_ptr<GDALAttribute> TileDBGroup::CreateAttribute(
     544             :     const std::string &osName, const std::vector<GUInt64> &anDimensions,
     545             :     const GDALExtendedDataType &oDataType, CSLConstList papszOptions)
     546             : {
     547           5 :     return CreateAttributeImpl(osName, anDimensions, oDataType, papszOptions);
     548             : }
     549             : 
     550             : /************************************************************************/
     551             : /*                   TileDBGroup::GetAttribute()                        */
     552             : /************************************************************************/
     553             : 
     554             : std::shared_ptr<GDALAttribute>
     555           4 : TileDBGroup::GetAttribute(const std::string &osName) const
     556             : {
     557           4 :     return GetAttributeImpl(osName);
     558             : }
     559             : 
     560             : /************************************************************************/
     561             : /*                   TileDBGroup::GetAttributes()                       */
     562             : /************************************************************************/
     563             : 
     564             : std::vector<std::shared_ptr<GDALAttribute>>
     565           4 : TileDBGroup::GetAttributes(CSLConstList /* papszOptions */) const
     566             : {
     567           4 :     return GetAttributesImpl();
     568             : }
     569             : 
     570             : /************************************************************************/
     571             : /*                   TileDBGroup::DeleteAttribute()                     */
     572             : /************************************************************************/
     573             : 
     574           3 : bool TileDBGroup::DeleteAttribute(const std::string &osName,
     575             :                                   CSLConstList papszOptions)
     576             : {
     577           3 :     return DeleteAttributeImpl(osName, papszOptions);
     578             : }

Generated by: LCOV version 1.14