Skip to content

Commit

Permalink
ogr2ogr: transfer relationships (when possible) from source dataset t…
Browse files Browse the repository at this point in the history
…o target dataset

Fixes #11397
  • Loading branch information
rouault committed Dec 2, 2024
1 parent 08aca88 commit 79420ec
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 0 deletions.
85 changes: 85 additions & 0 deletions apps/ogr2ogr_lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2217,6 +2217,88 @@ GDALVectorTranslateCreateCopy(GDALDriver *poDriver, const char *pszDest,
return poOut;
}

/************************************************************************/
/* CopyRelationships() */
/************************************************************************/

static void CopyRelationships(GDALDataset *poODS, GDALDataset *poDS)
{
if (!poODS->GetDriver()->GetMetadataItem(GDAL_DCAP_CREATE_RELATIONSHIP))
return;

const auto aosRelationshipNames = poDS->GetRelationshipNames();
if (aosRelationshipNames.empty())
return;

// Collect target layer names
std::set<std::string> oSetDestLayerNames;
for (const auto &poLayer : poDS->GetLayers())
{
oSetDestLayerNames.insert(poLayer->GetName());
}

// Iterate over all source relationships
for (const auto &osRelationshipName : aosRelationshipNames)
{
const auto poSrcRelationship =
poDS->GetRelationship(osRelationshipName);
if (!poSrcRelationship)
continue;

// Skip existing relationship of the same name
if (poODS->GetRelationship(osRelationshipName))
continue;

bool canAdd = true;
const auto &osLeftTableName = poSrcRelationship->GetLeftTableName();
if (!osLeftTableName.empty() &&
!cpl::contains(oSetDestLayerNames, osLeftTableName))
{
CPLDebug("GDALVectorTranslate",
"Skipping relationship %s because its left table (%s) "
"does not exist in target dataset",
osRelationshipName.c_str(), osLeftTableName.c_str());
canAdd = false;
}

const auto &osRightTableName = poSrcRelationship->GetRightTableName();
if (!osRightTableName.empty() &&
!cpl::contains(oSetDestLayerNames, osRightTableName))
{
CPLDebug("GDALVectorTranslate",
"Skipping relationship %s because its right table (%s) "
"does not exist in target dataset",
osRelationshipName.c_str(), osRightTableName.c_str());
canAdd = false;
}

const auto &osMappingTableName =
poSrcRelationship->GetMappingTableName();
if (!osMappingTableName.empty() &&
!cpl::contains(oSetDestLayerNames, osMappingTableName))
{
CPLDebug("GDALVectorTranslate",
"Skipping relationship %s because its mapping table (%s) "
"does not exist in target dataset",
osRelationshipName.c_str(), osMappingTableName.c_str());
canAdd = false;
}

if (canAdd)
{
std::string osFailureReason;
if (!poODS->AddRelationship(
std::make_unique<GDALRelationship>(*poSrcRelationship),
osFailureReason))
{
CPLDebug("GDALVectorTranslate",
"Cannot add relationship %s: %s",
osRelationshipName.c_str(), osFailureReason.c_str());
}
}
}
}

/************************************************************************/
/* GDALVectorTranslate() */
/************************************************************************/
Expand Down Expand Up @@ -3590,6 +3672,9 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS,
GDALDestroyScaledProgress(pProgressArg);
}
}

CopyRelationships(poODS, poDS);

/* -------------------------------------------------------------------- */
/* Process DS style table */
/* -------------------------------------------------------------------- */
Expand Down
Binary file not shown.
73 changes: 73 additions & 0 deletions autotest/utilities/test_ogr2ogr_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -3008,3 +3008,76 @@ def my_handler(errorClass, errno, msg):
"2022/05/31 12:34:56",
"2022/05/31 12:34:56+1230",
]


###############################################################################


@gdaltest.enable_exceptions()
@pytest.mark.require_driver("GPKG")
def test_ogr2ogr_lib_transfer_gpkg_relationships(tmp_vsimem):

out_filename = str(tmp_vsimem / "relationships.gpkg")
gdal.VectorTranslate(out_filename, "../ogr/data/gpkg/relation_mapping_table.gpkg")

with gdal.OpenEx(out_filename) as ds:
assert ds.GetRelationshipNames() == ["a_b_attributes"]
relationship = ds.GetRelationship("a_b_attributes")
assert relationship.GetLeftTableName() == "a"
assert relationship.GetRightTableName() == "b"
assert relationship.GetMappingTableName() == "my_mapping_table"

gdal.VectorTranslate(
out_filename,
"../ogr/data/gpkg/relation_mapping_table.gpkg",
layers=["a", "my_mapping_table"],
)
with gdal.OpenEx(out_filename) as ds:
assert ds.GetRelationshipNames() is None

gdal.VectorTranslate(
out_filename,
"../ogr/data/gpkg/relation_mapping_table.gpkg",
layers=["b", "my_mapping_table"],
)
with gdal.OpenEx(out_filename) as ds:
assert ds.GetRelationshipNames() is None

gdal.VectorTranslate(
out_filename, "../ogr/data/gpkg/relation_mapping_table.gpkg", layers=["a", "b"]
)
with gdal.OpenEx(out_filename) as ds:
assert ds.GetRelationshipNames() is None


###############################################################################


@gdaltest.enable_exceptions()
@pytest.mark.require_driver("OpenFileGDB")
def test_ogr2ogr_lib_transfer_filegdb_relationships(tmp_vsimem):

out_filename = str(tmp_vsimem / "relationships.gdb")
gdal.VectorTranslate(
out_filename, "../ogr/data/filegdb/relationships.gdb", format="OpenFileGDB"
)

with gdal.OpenEx(out_filename) as ds:
assert set(ds.GetRelationshipNames()) == set(
[
"composite_many_to_many",
"composite_one_to_one",
"points__ATTACHREL",
"simple_attributed",
"simple_backward_message_direction",
"simple_both_message_direction",
"simple_forward_message_direction",
"simple_many_to_many",
"simple_one_to_many",
"simple_relationship_one_to_one",
]
)
relationship = ds.GetRelationship("composite_many_to_many")
assert relationship.GetLeftTableName() == "table6"
assert relationship.GetRightTableName() == "table7"
assert relationship.GetMappingTableName() == "composite_many_to_many"

0 comments on commit 79420ec

Please sign in to comment.