Skip to content

Commit

Permalink
OGR_PG_SKIP_CONFLICTS: optionally insert with ON CONFLICT DO NOTHING
Browse files Browse the repository at this point in the history
  • Loading branch information
jef-n committed Jun 8, 2024
1 parent 3bb0d36 commit 5497c4b
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 1 deletion.
54 changes: 54 additions & 0 deletions autotest/ogr/ogr_pg.py
Original file line number Diff line number Diff line change
Expand Up @@ -6067,3 +6067,57 @@ def test_ogr_pg_no_postgis_GEOMETRY_NAME(pg_ds):
gdal.GetLastErrorMsg()
== "GEOMETRY_NAME=foo ignored, and set instead to 'wkb_geometry' as it is the only geometry column name recognized for non-PostGIS enabled databases."
)


###############################################################################
# Test ignored conflicts


@only_without_postgis
def test_ogr_pg_skip_conflicts(pg_ds):
pg_ds.ExecuteSQL(
"CREATE TABLE test_ogr_skip_conflicts(id SERIAL PRIMARY KEY, gml_id character(16), beginnt character(20), UNIQUE(gml_id, beginnt))"
)

with gdal.config_option("OGR_PG_SKIP_CONFLICTS", "YES"):
# OGR_PG_SKIP_CONFLICTS and OGR_PG_RETRIEVE_FID cannot be used at the same time
with gdal.config_option("OGR_PG_RETRIEVE_FID", "YES"):
pg_ds = reconnect(pg_ds, update=1)
lyr = pg_ds.GetLayerByName("test_ogr_skip_conflicts")
feat = ogr.Feature(lyr.GetLayerDefn())
feat["gml_id"] = "DERPLP0300000cG3"
feat["beginnt"] = "2020-07-10T04:48:14Z"
with gdal.quiet_errors():
assert lyr.CreateFeature(feat) != ogr.OGRERR_NONE

with gdal.config_option("OGR_PG_RETRIEVE_FID", "NO"):
pg_ds = reconnect(pg_ds, update=1)
lyr = pg_ds.GetLayerByName("test_ogr_skip_conflicts")

assert lyr.GetFeatureCount() == 0

feat = ogr.Feature(lyr.GetLayerDefn())
feat["gml_id"] = "DERPLP0300000cG3"
feat["beginnt"] = "2020-07-10T04:48:14Z"
assert lyr.CreateFeature(feat) == ogr.OGRERR_NONE
assert lyr.GetFeatureCount() == 1

# Insert w/o OGR_PG_SKIP_CONFLICTS=YES succeeds, but doesn't add a feature
with gdal.config_option("OGR_PG_SKIP_CONFLICTS", "YES"):
pg_ds = reconnect(pg_ds, update=1)
lyr = pg_ds.GetLayerByName("test_ogr_skip_conflicts")

assert lyr.GetFeatureCount() == 1

feat = ogr.Feature(lyr.GetLayerDefn())
feat["gml_id"] = "DERPLP0300000cG3"
feat["beginnt"] = "2020-07-10T04:48:14Z"
assert lyr.CreateFeature(feat) == ogr.OGRERR_NONE
assert lyr.GetFeatureCount() == 1

# Other feature succeeds and increments the feature count
feat = ogr.Feature(lyr.GetLayerDefn())
feat["gml_id"] = "DERPLP0300000cG4"
feat["beginnt"] = "2020-07-10T04:48:14Z"
assert lyr.CreateFeature(feat) == ogr.OGRERR_NONE
assert lyr.GetFeatureCount() == 2
4 changes: 4 additions & 0 deletions doc/source/drivers/vector/nas.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ population - which was default in ALKIS-Import. The information found there was
redundant to the relation fields also contained in original elements/tables.
Enabling the option also made progress reporting available.

Duplicate data in datasets will usually causes errors. When importing separate
datasets into PostgreSQL it is useful to enable "OGR_PG_SKIP_CONFLICTS" to skip
conflicting features.

This driver was implemented within the context of the `PostNAS
project <http://trac.wheregroup.com/PostNAS>`__, which has more
information on its use and other related projects.
Expand Down
10 changes: 10 additions & 0 deletions doc/source/drivers/vector/pg.rst
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,16 @@ The following configuration options are available:
create) the ``ogr_system_tables.metadata`` table to retrieve and store
layer metadata.

- .. config:: OGR_PG_SKIP_CONFLICTS
:choices: YES, NO
:default: NO

If set to "YES" (not the default),
conflicts when inserting features will be skipped
(requires OGR_PG_RETRIEVE_FID to be off and only applies when PG_USE_COPY
is off).


Examples
~~~~~~~~

Expand Down
1 change: 1 addition & 0 deletions ogr/ogrsf_frmts/pg/ogr_pg.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ class OGRPGTableLayer final : public OGRPGLayer
void CheckGeomTypeCompatibility(int iGeomField, OGRGeometry *poGeom);

int bRetrieveFID = true;
int bSkipConflicts = false;
int bHasWarnedAlreadySetFID = false;

char **papszOverrideColumnTypes = nullptr;
Expand Down
13 changes: 12 additions & 1 deletion ogr/ogrsf_frmts/pg/ogrpgtablelayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,9 @@ OGRPGTableLayer::OGRPGTableLayer(OGRPGDataSource *poDSIn,
// Just in provision for people yelling about broken backward
// compatibility.
bRetrieveFID(
CPLTestBool(CPLGetConfigOption("OGR_PG_RETRIEVE_FID", "TRUE")))
CPLTestBool(CPLGetConfigOption("OGR_PG_RETRIEVE_FID", "TRUE"))),
bSkipConflicts(
CPLTestBool(CPLGetConfigOption("OGR_PG_SKIP_CONFLICTS", "FALSE")))
{
poDS = poDSIn;
pszQueryStatement = nullptr;
Expand Down Expand Up @@ -2069,10 +2071,19 @@ OGRErr OGRPGTableLayer::CreateFeatureViaInsert(OGRFeature *poFeature)
if (bRetrieveFID && pszFIDColumn != nullptr &&
poFeature->GetFID() == OGRNullFID)
{
if (bSkipConflicts)
{
CPLError(CE_Failure, CPLE_AppDefined,
"fid retrieval and skipping conflicts are not supported "
"at the same time.");
return OGRERR_FAILURE;
}
bReturnRequested = TRUE;
osCommand += " RETURNING ";
osCommand += OGRPGEscapeColumnName(pszFIDColumn);
}
else if (bSkipConflicts)
osCommand += " ON CONFLICT DO NOTHING";

/* -------------------------------------------------------------------- */
/* Execute the insert. */
Expand Down

0 comments on commit 5497c4b

Please sign in to comment.