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 : }
|