Skip to content

Commit e9b8cb7

Browse files
Add support for Carrara URIs (#862)
* Add utility function to diferentiate when running on Carrara * Update data model to be compatible with Carrara * Construct correct relative path * Access root group iside try catch * Validate URI before creation * Update `uri` helper to use string_view when possible * Use membership test to decide id dataset is legacy * Rename symbols * Open root group in try catch to produce expected error message * Fix delimiter in split * Revert using view::split * Add deprecation message * Add trivial to test to compare legacy and new dataset format * Make vcf_header array open more defensive * Use DataProtocol exposed by core * Change verbage of deprecation warning
1 parent e72dab9 commit e9b8cb7

227 files changed

Lines changed: 275 additions & 84 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apis/python/tests/test_tiledbvcf.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2457,3 +2457,11 @@ def test_delete_dataset(tmp_path):
24572457

24582458
# Check that the dataset does not exist
24592459
assert not os.path.exists(uri)
2460+
2461+
def test_equality_old_new_format():
2462+
old_ds = tiledbvcf.Dataset(os.path.join(TESTS_INPUT_DIR, "arrays/old_format"))
2463+
new_ds = tiledbvcf.Dataset(os.path.join(TESTS_INPUT_DIR, "arrays/new_format"))
2464+
2465+
assert old_ds.count() == new_ds.count()
2466+
assert old_ds.samples() == new_ds.samples()
2467+
assert old_ds.read().equals(new_ds.read())

libtiledbvcf/src/dataset/tiledbvcfdataset.cc

Lines changed: 162 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,9 @@ void TileDBVCFDataset::create(const CreationParams& params) {
210210

211211
check_attribute_names(params.extra_attributes);
212212

213+
// Validate the uri is appropriate for the backend it is going to write to
214+
utils::validate_uri(params.uri, ctx);
215+
213216
// Create root group, which creates the root directory
214217
create_group(ctx, params.uri);
215218

@@ -317,29 +320,37 @@ void TileDBVCFDataset::create_empty_metadata(
317320
create_group(ctx, metadata_group_uri(root_uri));
318321
create_sample_header_array(ctx, root_uri, checksum);
319322

320-
// Group assets use full paths for tiledb cloud, relative paths otherwise
321-
bool relative = !cloud_dataset(root_uri);
323+
if (ctx.data_protocol(root_uri) == tiledb::Context::DataProtocol::v2) {
324+
// Group assets use full paths for tiledb cloud, relative paths otherwise
325+
bool relative = !cloud_dataset(root_uri);
322326

323-
// Add arrays to the root group
324-
// We add the vcf_header array to the root group to simplify array opening.
325-
Group root_group(ctx, root_uri, TILEDB_WRITE);
326-
auto array_uri = vcf_headers_uri(root_uri, relative);
327-
LOG_DEBUG(
328-
"Adding array name='{}' uri='{}' to group uri='{}'",
329-
VCF_HEADER_ARRAY,
330-
array_uri,
331-
root_uri);
332-
root_group.add_member(array_uri, relative, VCF_HEADER_ARRAY);
333-
334-
// Add the metadata group to the root group, so it will be deleted when the
335-
// dataset is deleted.
336-
auto group_uri = metadata_group_uri(root_uri, relative);
337-
LOG_DEBUG(
338-
"Adding group name='{}' uri='{}' to group uri='{}'",
339-
METADATA_GROUP,
340-
group_uri,
341-
root_uri);
342-
root_group.add_member(group_uri, relative, METADATA_GROUP);
327+
// Add arrays to the root group
328+
// We add the vcf_header array to the root group to simplify array opening.
329+
// Until 0.38.1 the `vcf_headers` array was created under the `metadata`
330+
// group but registered under to root group. This is incompatible with the
331+
// Carrara data model. Now the `vcf_headers` will be registered under the
332+
// `metadata` group in which physically exists.
333+
Group root_group(ctx, root_uri, TILEDB_WRITE);
334+
Group metadata_group(ctx, metadata_group_uri(root_uri), TILEDB_WRITE);
335+
336+
auto array_uri = vcf_headers_uri_v2(root_uri, relative);
337+
LOG_DEBUG(
338+
"Adding array name='{}' uri='{}' to group uri='{}'",
339+
VCF_HEADER_ARRAY,
340+
array_uri,
341+
metadata_group_uri(root_uri));
342+
metadata_group.add_member(array_uri, relative, VCF_HEADER_ARRAY);
343+
344+
// Add the metadata group to the root group, so it will be deleted when the
345+
// dataset is deleted.
346+
auto group_uri = metadata_group_uri(root_uri, relative);
347+
LOG_DEBUG(
348+
"Adding group name='{}' uri='{}' to group uri='{}'",
349+
METADATA_GROUP,
350+
group_uri,
351+
root_uri);
352+
root_group.add_member(group_uri, relative, METADATA_GROUP);
353+
}
343354
}
344355

345356
void TileDBVCFDataset::create_empty_data_array(
@@ -458,17 +469,19 @@ void TileDBVCFDataset::create_empty_data_array(
458469

459470
Array::create(data_array_uri(root_uri), schema);
460471

461-
// Add the array to the root group
462-
// Group assests use full paths for tiledb cloud, relative paths otherwise
463-
bool relative = !cloud_dataset(root_uri);
464-
auto array_uri = data_array_uri(root_uri, relative);
465-
LOG_DEBUG(
466-
"Adding array name='{}' uri='{}' to group uri='{}'",
467-
DATA_ARRAY,
468-
array_uri,
469-
root_uri);
470-
Group root_group(ctx, root_uri, TILEDB_WRITE);
471-
root_group.add_member(array_uri, relative, DATA_ARRAY);
472+
if (ctx.data_protocol(root_uri) == tiledb::Context::DataProtocol::v2) {
473+
// Add the array to the root group
474+
// Group assests use full paths for tiledb cloud, relative paths otherwise
475+
bool relative = !cloud_dataset(root_uri);
476+
auto array_uri = data_array_uri(root_uri, relative);
477+
LOG_DEBUG(
478+
"Adding array name='{}' uri='{}' to group uri='{}'",
479+
DATA_ARRAY,
480+
array_uri,
481+
root_uri);
482+
Group root_group(ctx, root_uri, TILEDB_WRITE);
483+
root_group.add_member(array_uri, relative, DATA_ARRAY);
484+
}
472485
}
473486

474487
void TileDBVCFDataset::create_sample_header_array(
@@ -1074,11 +1087,116 @@ std::unique_ptr<tiledb::Array> TileDBVCFDataset::open_array(
10741087

10751088
std::unique_ptr<tiledb::Array> TileDBVCFDataset::open_vcf_array(
10761089
tiledb_query_type_t query_type) {
1077-
return open_array(
1078-
query_type,
1079-
VCF_HEADER_ARRAY,
1080-
vcf_headers_uri(root_uri_),
1081-
vcf_headers_uri(root_uri_, false, true));
1090+
std::string array_uri;
1091+
1092+
// Until 0.38.1 `vcf_headers` array was registered under the root group.
1093+
// Now it is registered under `metadata` group in which physically exist.
1094+
std::unique_ptr<tiledb::Group> root_group;
1095+
try {
1096+
root_group = std::make_unique<tiledb::Group>(*ctx_, root_uri_, TILEDB_READ);
1097+
} catch (const tiledb::TileDBError& ex) {
1098+
throw std::runtime_error(
1099+
"Cannot open TileDB-VCF dataset; dataset '" + root_uri_ +
1100+
"' or its metadata does not exist. TileDB error message: " +
1101+
std::string(ex.what()));
1102+
}
1103+
1104+
// We are opening a legacy dataset where the `vcf_headers` array is
1105+
// registered under the root group
1106+
if (utils::has_member(*root_group, VCF_HEADER_ARRAY)) {
1107+
if (ctx_->data_protocol(root_uri_) == tiledb::Context::DataProtocol::v3) {
1108+
// This is an legacy dataset registered under TileDB Carrara
1109+
throw std::runtime_error(
1110+
"Cannot open TileDB-VCF dataset; dataset '" + root_uri_ +
1111+
"' data format is not supported by Carrara. To migrate it remove "
1112+
"the `vcf_headers` array from the root group and add it to the "
1113+
"`metadata` group using the physical path of the dataset.");
1114+
}
1115+
1116+
LOG_WARN(
1117+
"**DEPRECATED** Accessing {} registered under root group. To migrate "
1118+
"it remove the `vcf_headers` array from the root group and add it to "
1119+
"the `metadata` group using the physical path of the dataset.",
1120+
VCF_HEADER_ARRAY);
1121+
LOG_DEBUG("Fallback to '{}' registered under root group", VCF_HEADER_ARRAY);
1122+
return open_array(
1123+
query_type,
1124+
VCF_HEADER_ARRAY,
1125+
vcf_headers_uri(root_uri_),
1126+
vcf_headers_uri(root_uri_, false, true));
1127+
} else if (utils::has_member(*root_group, METADATA_GROUP)) {
1128+
// Load the `metadata` group
1129+
std::string metadata_uri;
1130+
1131+
// If the group member uri is a cloud uri and the root uri is not,
1132+
// use the non cloud uri instead of the group member uri
1133+
if (!cloud_dataset(root_uri_) &&
1134+
cloud_dataset(root_group->member(METADATA_GROUP).uri())) {
1135+
metadata_uri = metadata_group_uri(root_uri_);
1136+
LOG_DEBUG(
1137+
"Override group uri '{}'. Open '{}' using uri path '{}'",
1138+
root_group->member(METADATA_GROUP).uri(),
1139+
METADATA_GROUP,
1140+
metadata_uri);
1141+
} else {
1142+
metadata_uri = root_group->member(METADATA_GROUP).uri();
1143+
LOG_DEBUG("Open '{}' using group uri '{}'", METADATA_GROUP, metadata_uri);
1144+
}
1145+
1146+
Group metadata_group(*ctx_, metadata_uri, TILEDB_READ);
1147+
1148+
if (utils::has_member(metadata_group, VCF_HEADER_ARRAY)) {
1149+
// If the group member uri is a cloud uri and the root uri is not,
1150+
// use the uri_path instead of the group member uri
1151+
if (!cloud_dataset(root_uri_) &&
1152+
cloud_dataset(metadata_group.member(VCF_HEADER_ARRAY).uri())) {
1153+
array_uri = vcf_headers_uri(root_uri_);
1154+
LOG_DEBUG(
1155+
"Override group uri '{}'. Open '{}' using uri path '{}'",
1156+
metadata_group.member(VCF_HEADER_ARRAY).uri(),
1157+
VCF_HEADER_ARRAY,
1158+
array_uri);
1159+
} else {
1160+
array_uri = metadata_group.member(VCF_HEADER_ARRAY).uri();
1161+
LOG_DEBUG(
1162+
"Open '{}' using array uri '{}'", VCF_HEADER_ARRAY, array_uri);
1163+
}
1164+
} else {
1165+
array_uri = vcf_headers_uri(root_uri_);
1166+
LOG_DEBUG(
1167+
"'{}' is not registered under '{}'. Open '{}' using array uri '{}'",
1168+
VCF_HEADER_ARRAY,
1169+
METADATA_GROUP,
1170+
VCF_HEADER_ARRAY,
1171+
array_uri);
1172+
}
1173+
} else {
1174+
// The group seems to have to member added. Fallback to absolute path
1175+
array_uri = vcf_headers_uri(root_uri_);
1176+
LOG_DEBUG(
1177+
"'{}' and '{}' not registered under root group. Open '{}' using array "
1178+
"uri '{}'",
1179+
VCF_HEADER_ARRAY,
1180+
METADATA_GROUP,
1181+
VCF_HEADER_ARRAY,
1182+
array_uri);
1183+
}
1184+
1185+
try {
1186+
// Open the array with the uri detemined above
1187+
return std::unique_ptr<Array>(new Array(*ctx_, array_uri, query_type));
1188+
} catch (const tiledb::TileDBError& ex) {
1189+
try {
1190+
// Last chance, try the legacy uri to support legacy cloud arrays
1191+
return std::unique_ptr<Array>(new Array(
1192+
*ctx_, vcf_headers_uri(root_uri_, false, true), query_type));
1193+
} catch (const tiledb::TileDBError& ex) {
1194+
throw std::runtime_error(
1195+
"Cannot open TileDB-VCF dataset; dataset '" + root_uri_ +
1196+
"' or its metadata does not exist. TileDB error message: " +
1197+
std::string(ex.what()));
1198+
}
1199+
}
10821200
}
10831201

10841202
std::shared_ptr<tiledb::Array> TileDBVCFDataset::open_data_array(
@@ -2095,6 +2213,12 @@ std::string TileDBVCFDataset::vcf_headers_uri(
20952213
return utils::uri_join(group, VCF_HEADER_ARRAY, delimiter);
20962214
}
20972215

2216+
std::string TileDBVCFDataset::vcf_headers_uri_v2(
2217+
const std::string& root_uri, bool relative) {
2218+
auto root = relative ? "" : metadata_group_uri(root_uri, relative, false);
2219+
return utils::uri_join(root, VCF_HEADER_ARRAY, '/');
2220+
}
2221+
20982222
bool TileDBVCFDataset::cloud_dataset(const std::string& root_uri) {
20992223
return utils::starts_with(root_uri, "tiledb://");
21002224
}

libtiledbvcf/src/dataset/tiledbvcfdataset.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,10 @@ class TileDBVCFDataset {
989989
static std::string vcf_headers_uri(
990990
const std::string& root_uri, bool relative = false, bool legacy = false);
991991

992+
/** Returns the URI of the VCF header data array for the dataset. */
993+
static std::string vcf_headers_uri_v2(
994+
const std::string& root_uri, bool relative = false);
995+
992996
/** Returns true if the array starts with the tiledb:// URI **/
993997
static bool cloud_dataset(const std::string& root_uri);
994998

libtiledbvcf/src/stats/allele_count.cc

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,16 @@ void AlleleCount::create(
106106
Array array(ctx, uri, TILEDB_WRITE);
107107
array.put_metadata("version", TILEDB_UINT32, 1, &ALLELE_COUNT_VERSION);
108108

109-
// Add array to root group
110-
// Group assets use full paths for tiledb cloud, relative paths otherwise
111-
auto relative = !utils::starts_with(root_uri, "tiledb://");
112-
auto array_uri = AlleleCount::root_uri(root_uri, relative);
113-
LOG_DEBUG("Adding array '{}' to group '{}'", array_uri, root_uri);
114-
Group root_group(ctx, root_uri, TILEDB_WRITE);
115-
root_group.add_member(array_uri, relative, ALLELE_COUNT_ARRAY);
109+
if (ctx.data_protocol(root_uri) == tiledb::Context::DataProtocol::v2) {
110+
// Add array to root group
111+
// Group assets use full paths for tiledb cloud, relative paths otherwise
112+
auto relative = !utils::starts_with(root_uri, "tiledb://");
113+
auto array_uri = AlleleCount::root_uri(root_uri, relative);
114+
LOG_DEBUG("Adding array '{}' to group '{}'", array_uri, root_uri);
115+
Group root_group(ctx, root_uri, TILEDB_WRITE);
116+
117+
root_group.add_member(array_uri, relative, ALLELE_COUNT_ARRAY);
118+
}
116119
}
117120

118121
bool AlleleCount::exists(const Group& group) {

libtiledbvcf/src/stats/sample_stats.cc

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -140,14 +140,17 @@ void SampleStats::create(
140140
Array array(ctx, uri, TILEDB_WRITE);
141141
array.put_metadata("version", TILEDB_INT32, 1, &SAMPLE_STATS_VERSION);
142142

143-
// Add array to root group
144-
// Group assets use full paths for tiledb cloud, relative paths otherwise
145-
auto relative = !utils::starts_with(root_uri, "tiledb://");
146-
auto array_uri = SampleStats::root_uri(root_uri, relative);
147-
LOG_DEBUG(
148-
"[SampleStats] Adding array '{}' to group '{}'", array_uri, root_uri);
149-
Group root_group(ctx, root_uri, TILEDB_WRITE);
150-
root_group.add_member(array_uri, relative, SAMPLE_STATS_ARRAY);
143+
if (ctx.data_protocol(root_uri) == tiledb::Context::DataProtocol::v2) {
144+
// Add array to root group
145+
// Group assets use full paths for tiledb cloud, relative paths otherwise
146+
auto relative = !utils::starts_with(root_uri, "tiledb://");
147+
auto array_uri = SampleStats::root_uri(root_uri, relative);
148+
LOG_DEBUG(
149+
"[SampleStats] Adding array '{}' to group '{}'", array_uri, root_uri);
150+
151+
Group root_group(ctx, root_uri, TILEDB_WRITE);
152+
root_group.add_member(array_uri, relative, SAMPLE_STATS_ARRAY);
153+
}
151154
}
152155

153156
bool SampleStats::exists(const Group& group) {

libtiledbvcf/src/stats/variant_stats.cc

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,13 +157,16 @@ void VariantStats::create(
157157
Array array(ctx, uri, TILEDB_WRITE);
158158
array.put_metadata("version", TILEDB_UINT32, 1, &array_version_);
159159

160-
// Add array to root group
161-
// Group assests use full paths for tiledb cloud, relative paths otherwise
162-
auto relative = !utils::starts_with(root_uri, "tiledb://");
163-
auto array_uri = VariantStats::root_uri(root_uri, relative);
164-
LOG_DEBUG("Adding array '{}' to group '{}'", array_uri, root_uri);
165-
Group root_group(ctx, root_uri, TILEDB_WRITE);
166-
root_group.add_member(array_uri, relative, VARIANT_STATS_ARRAY);
160+
if (ctx.data_protocol(root_uri) == tiledb::Context::DataProtocol::v2) {
161+
// Add array to root group
162+
// Group assests use full paths for tiledb cloud, relative paths otherwise
163+
auto relative = !utils::starts_with(root_uri, "tiledb://");
164+
auto array_uri = VariantStats::root_uri(root_uri, relative);
165+
LOG_DEBUG("Adding array '{}' to group '{}'", array_uri, root_uri);
166+
Group root_group(ctx, root_uri, TILEDB_WRITE);
167+
168+
root_group.add_member(array_uri, relative, VARIANT_STATS_ARRAY);
169+
}
167170
}
168171

169172
bool VariantStats::exists(const Group& group) {

0 commit comments

Comments
 (0)