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

Generated by: LCOV version 1.14