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