LCOV - code coverage report
Current view: top level - frmts/tiledb - tiledbmultidimgroup.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 184 241 76.3 %
Date: 2024-11-21 22:18:42 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         167 : bool TileDBGroup::EnsureOpenAs(tiledb_query_type_t mode) const
     130             : {
     131         167 :     if (!m_poTileDBGroup)
     132           0 :         return false;
     133         167 :     if (m_poTileDBGroup->query_type() == mode && m_poTileDBGroup->is_open())
     134          79 :         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             :     // Try to match by member name property first, and if not use the
     212             :     // last part of their URI
     213           4 :     std::string osSubPath;
     214           4 :     std::string osSubPathCandidate;
     215           2 :     for (uint64_t i = 0; i < m_poTileDBGroup->member_count(); ++i)
     216             :     {
     217           1 :         auto obj = m_poTileDBGroup->member(i);
     218           1 :         if (obj.type() == tiledb::Object::Type::Group)
     219             :         {
     220           1 :             if (obj.name().has_value() && *(obj.name()) == osName)
     221             :             {
     222           1 :                 osSubPath = obj.uri();
     223           1 :                 break;
     224             :             }
     225           0 :             else if (CPLGetFilename(obj.uri().c_str()) == osName)
     226             :             {
     227           0 :                 osSubPathCandidate = osSubPath;
     228             :             }
     229             :         }
     230             :     }
     231           2 :     if (osSubPath.empty())
     232           1 :         osSubPath = std::move(osSubPathCandidate);
     233           2 :     if (osSubPath.empty())
     234           1 :         return nullptr;
     235             : 
     236             :     auto poSubGroup = TileDBGroup::OpenFromDisk(
     237           2 :         m_poSharedResource, m_osFullName, osName, osSubPath);
     238           1 :     if (!poSubGroup)
     239           0 :         return nullptr;
     240             : 
     241           1 :     m_oMapGroups[osName] = poSubGroup;
     242             : 
     243           1 :     return poSubGroup;
     244             : }
     245             : 
     246             : /************************************************************************/
     247             : /*                   TileDBGroup::CreateGroup()                         */
     248             : /************************************************************************/
     249             : 
     250           5 : std::shared_ptr<GDALGroup> TileDBGroup::CreateGroup(const std::string &osName,
     251             :                                                     CSLConstList papszOptions)
     252             : {
     253           5 :     if (!m_poSharedResource->IsUpdatable())
     254             :     {
     255           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     256             :                  "Dataset not open in update mode");
     257           1 :         return nullptr;
     258             :     }
     259             : 
     260           4 :     if (HasObjectOfSameName(osName))
     261           2 :         return nullptr;
     262             : 
     263           4 :     std::string osSubPath = m_poTileDBGroup->uri() + "/" +
     264           6 :                             TileDBSharedResource::SanitizeNameForPath(osName);
     265           2 :     const char *pszURI = CSLFetchNameValue(papszOptions, "URI");
     266           2 :     if (pszURI)
     267           0 :         osSubPath = pszURI;
     268             :     auto poSubGroup =
     269           4 :         CreateOnDisk(m_poSharedResource, m_osFullName, osName, osSubPath);
     270           2 :     if (!poSubGroup)
     271           0 :         return nullptr;
     272             : 
     273           2 :     if (!AddMember(osSubPath, osName))
     274           0 :         return nullptr;
     275           2 :     m_oMapGroups[osName] = poSubGroup;
     276             : 
     277           2 :     return poSubGroup;
     278             : }
     279             : 
     280             : /************************************************************************/
     281             : /*                   TileDBGroup::AddMember()                           */
     282             : /************************************************************************/
     283             : 
     284          32 : bool TileDBGroup::AddMember(const std::string &osPath,
     285             :                             const std::string &osName)
     286             : {
     287          32 :     if (!EnsureOpenAs(TILEDB_WRITE))
     288           0 :         return false;
     289             : 
     290             :     try
     291             :     {
     292          32 :         m_poTileDBGroup->add_member(osPath, /* relative= */ false, osName);
     293             :     }
     294           0 :     catch (const std::exception &e)
     295             :     {
     296           0 :         CPLError(CE_Failure, CPLE_AppDefined, "AddMember() failed with: %s",
     297           0 :                  e.what());
     298           0 :         return false;
     299             :     }
     300             :     // Force close() and re-open() to avoid
     301             :     // https://github.com/TileDB-Inc/TileDB/issues/4101
     302             :     try
     303             :     {
     304          32 :         m_poTileDBGroup->close();
     305          32 :         m_poTileDBGroup->open(TILEDB_WRITE);
     306             :     }
     307           0 :     catch (const std::exception &e)
     308             :     {
     309           0 :         CPLError(CE_Failure, CPLE_AppDefined, "AddMember() failed with: %s",
     310           0 :                  e.what());
     311           0 :         m_poTileDBGroup.reset();
     312           0 :         return false;
     313             :     }
     314          32 :     return true;
     315             : }
     316             : 
     317             : /************************************************************************/
     318             : /*                    TileDBGroup::CreateDimension()                    */
     319             : /************************************************************************/
     320             : 
     321          36 : std::shared_ptr<GDALDimension> TileDBGroup::CreateDimension(
     322             :     const std::string &osName, const std::string &osType,
     323             :     const std::string &osDirection, GUInt64 nSize, CSLConstList)
     324             : {
     325          36 :     if (osName.empty())
     326             :     {
     327           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     328             :                  "Empty dimension name not supported");
     329           0 :         return nullptr;
     330             :     }
     331             : 
     332          36 :     if (m_oMapDimensions.find(osName) != m_oMapDimensions.end())
     333             :     {
     334           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     335             :                  "A dimension with same name already exists");
     336           0 :         return nullptr;
     337             :     }
     338             :     auto newDim(std::make_shared<GDALDimensionWeakIndexingVar>(
     339          72 :         GetFullName(), osName, osType, osDirection, nSize));
     340          36 :     m_oMapDimensions[osName] = newDim;
     341          36 :     return newDim;
     342             : }
     343             : 
     344             : /************************************************************************/
     345             : /*                   TileDBGroup::GetMDArrayNames()                     */
     346             : /************************************************************************/
     347             : 
     348             : std::vector<std::string>
     349          34 : TileDBGroup::GetMDArrayNames(CSLConstList /*papszOptions */) const
     350             : {
     351          34 :     if (!EnsureOpenAs(TILEDB_READ))
     352           0 :         return {};
     353             : 
     354          68 :     std::vector<std::string> aosNames;
     355         100 :     for (uint64_t i = 0; i < m_poTileDBGroup->member_count(); ++i)
     356             :     {
     357         132 :         auto obj = m_poTileDBGroup->member(i);
     358          66 :         if (obj.type() == tiledb::Object::Type::Array)
     359             :         {
     360         130 :             tiledb::ArraySchema schema(m_poSharedResource->GetCtx(), obj.uri());
     361          65 :             if (schema.array_type() == TILEDB_DENSE)
     362             :             {
     363             :                 const std::string osName =
     364         187 :                     obj.name().has_value() ? *(obj.name())
     365         203 :                                            : CPLGetFilename(obj.uri().c_str());
     366          65 :                 const auto nAttributes = schema.attribute_num();
     367          65 :                 if (nAttributes != 1)
     368             :                 {
     369           0 :                     for (uint32_t iAttr = 0; iAttr < nAttributes; ++iAttr)
     370             :                     {
     371           0 :                         aosNames.push_back(osName + "." +
     372           0 :                                            schema.attribute(iAttr).name());
     373             :                     }
     374             :                 }
     375             :                 else
     376             :                 {
     377          65 :                     aosNames.push_back(osName);
     378             :                 }
     379             :             }
     380             :         }
     381             :     }
     382             : 
     383             :     // As array creation is deferred, the above loop didn't get freshly
     384             :     // created arrays
     385          53 :     for (const auto &kv : m_oMapArrays)
     386             :     {
     387          19 :         if (std::find(aosNames.begin(), aosNames.end(), kv.first) ==
     388          38 :             aosNames.end())
     389             :         {
     390           1 :             aosNames.push_back(kv.first);
     391             :         }
     392             :     }
     393             : 
     394          34 :     return aosNames;
     395             : }
     396             : 
     397             : /************************************************************************/
     398             : /*                   TileDBGroup::OpenMDArray()                         */
     399             : /************************************************************************/
     400             : 
     401             : std::shared_ptr<GDALMDArray>
     402          63 : TileDBGroup::OpenMDArray(const std::string &osName,
     403             :                          CSLConstList papszOptions) const
     404             : {
     405          63 :     auto oIter = m_oMapArrays.find(osName);
     406          63 :     if (oIter != m_oMapArrays.end())
     407             :     {
     408          22 :         return oIter->second;
     409             :     }
     410          41 :     if (!m_poTileDBGroup)
     411           0 :         return nullptr;
     412             : 
     413          82 :     std::string osNamePrefix = osName;
     414          82 :     std::string osNameSuffix;
     415          41 :     const auto nLastDot = osName.rfind('.');
     416          41 :     if (nLastDot != std::string::npos)
     417             :     {
     418           0 :         osNamePrefix = osName.substr(0, nLastDot);
     419           0 :         osNameSuffix = osName.substr(nLastDot + 1);
     420             :     }
     421             : 
     422             :     // Try to match by member name property first, and if not use the
     423             :     // last part of their URI
     424          82 :     std::string osSubPath;
     425          82 :     std::string osSubPathCandidate;
     426          65 :     for (uint64_t i = 0; i < m_poTileDBGroup->member_count(); ++i)
     427             :     {
     428          59 :         auto obj = m_poTileDBGroup->member(i);
     429             : 
     430             :         const auto MatchNameSuffix =
     431          82 :             [this, &obj, &osName, &osNamePrefix,
     432         374 :              &osNameSuffix](const std::string &osObjName)
     433             :         {
     434         164 :             tiledb::ArraySchema schema(m_poSharedResource->GetCtx(), obj.uri());
     435          82 :             if (osNameSuffix.empty() && osObjName == osName)
     436             :             {
     437          36 :                 return true;
     438             :             }
     439          46 :             else if (osObjName == osNamePrefix && !osNameSuffix.empty() &&
     440           0 :                      schema.has_attribute(osNameSuffix))
     441             :             {
     442           0 :                 return true;
     443             :             }
     444          46 :             return false;
     445          59 :         };
     446             : 
     447          59 :         if (obj.type() == tiledb::Object::Type::Array)
     448             :         {
     449          59 :             if (obj.name().has_value() && MatchNameSuffix(*(obj.name())))
     450             :             {
     451          35 :                 osSubPath = obj.uri();
     452          35 :                 break;
     453             :             }
     454          24 :             else if (MatchNameSuffix(CPLGetFilename(obj.uri().c_str())))
     455             :             {
     456           1 :                 osSubPathCandidate = obj.uri();
     457             :             }
     458             :         }
     459             :     }
     460          41 :     if (osSubPath.empty())
     461           6 :         osSubPath = std::move(osSubPathCandidate);
     462          41 :     if (osSubPath.empty())
     463           5 :         return nullptr;
     464             : 
     465          36 :     if (m_oSetArrayInOpening.find(osName) != m_oSetArrayInOpening.end())
     466           2 :         return nullptr;
     467          34 :     m_oSetArrayInOpening.insert(osName);
     468           0 :     auto poArray = TileDBArray::OpenFromDisk(m_poSharedResource, m_pSelf.lock(),
     469          34 :                                              m_osFullName, osName, osNameSuffix,
     470          68 :                                              osSubPath, papszOptions);
     471          34 :     m_oSetArrayInOpening.erase(osName);
     472          34 :     if (!poArray)
     473           0 :         return nullptr;
     474             : 
     475          34 :     m_oMapArrays[osName] = poArray;
     476             : 
     477          34 :     return poArray;
     478             : }
     479             : 
     480             : /************************************************************************/
     481             : /*                   TileDBGroup::CreateMDArray()                       */
     482             : /************************************************************************/
     483             : 
     484          40 : std::shared_ptr<GDALMDArray> TileDBGroup::CreateMDArray(
     485             :     const std::string &osName,
     486             :     const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
     487             :     const GDALExtendedDataType &oDataType, CSLConstList papszOptions)
     488             : {
     489          40 :     if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "IN_MEMORY", "NO")))
     490             :     {
     491             :         auto poArray =
     492           8 :             MEMMDArray::Create(std::string(), osName, aoDimensions, oDataType);
     493           4 :         if (!poArray || !poArray->Init())
     494           0 :             return nullptr;
     495           4 :         return poArray;
     496             :     }
     497             : 
     498          36 :     if (!m_poSharedResource->IsUpdatable())
     499             :     {
     500           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     501             :                  "Dataset not open in update mode");
     502           1 :         return nullptr;
     503             :     }
     504             : 
     505          35 :     if (HasObjectOfSameName(osName))
     506           1 :         return nullptr;
     507             : 
     508          34 :     if (!EnsureOpenAs(TILEDB_WRITE))
     509           0 :         return nullptr;
     510             : 
     511          68 :     auto poSelf = std::dynamic_pointer_cast<TileDBGroup>(m_pSelf.lock());
     512          34 :     CPLAssert(poSelf);
     513             :     auto poArray =
     514          34 :         TileDBArray::CreateOnDisk(m_poSharedResource, poSelf, osName,
     515          68 :                                   aoDimensions, oDataType, papszOptions);
     516          34 :     if (!poArray)
     517           4 :         return nullptr;
     518             : 
     519          30 :     m_oMapArrays[osName] = poArray;
     520          30 :     return poArray;
     521             : }
     522             : 
     523             : /************************************************************************/
     524             : /*                  TileDBGroup::CreateAttribute()                      */
     525             : /************************************************************************/
     526             : 
     527           5 : std::shared_ptr<GDALAttribute> TileDBGroup::CreateAttribute(
     528             :     const std::string &osName, const std::vector<GUInt64> &anDimensions,
     529             :     const GDALExtendedDataType &oDataType, CSLConstList papszOptions)
     530             : {
     531           5 :     return CreateAttributeImpl(osName, anDimensions, oDataType, papszOptions);
     532             : }
     533             : 
     534             : /************************************************************************/
     535             : /*                   TileDBGroup::GetAttribute()                        */
     536             : /************************************************************************/
     537             : 
     538             : std::shared_ptr<GDALAttribute>
     539           4 : TileDBGroup::GetAttribute(const std::string &osName) const
     540             : {
     541           4 :     return GetAttributeImpl(osName);
     542             : }
     543             : 
     544             : /************************************************************************/
     545             : /*                   TileDBGroup::GetAttributes()                       */
     546             : /************************************************************************/
     547             : 
     548             : std::vector<std::shared_ptr<GDALAttribute>>
     549           4 : TileDBGroup::GetAttributes(CSLConstList /* papszOptions */) const
     550             : {
     551           4 :     return GetAttributesImpl();
     552             : }
     553             : 
     554             : /************************************************************************/
     555             : /*                   TileDBGroup::DeleteAttribute()                     */
     556             : /************************************************************************/
     557             : 
     558           3 : bool TileDBGroup::DeleteAttribute(const std::string &osName,
     559             :                                   CSLConstList papszOptions)
     560             : {
     561           3 :     return DeleteAttributeImpl(osName, papszOptions);
     562             : }

Generated by: LCOV version 1.14