Skip to content

Commit e375d1d

Browse files
phborbanyalldawson
authored andcommitted
[BUG FIX][Split Tool] Bug fix for split features tool
Fixes #19936 by prioritising existing attributes over provider side defaults in some circumstances
1 parent f410b4a commit e375d1d

File tree

3 files changed

+36
-18
lines changed

3 files changed

+36
-18
lines changed

src/core/qgsvectorlayerutils.cpp

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -380,17 +380,32 @@ QgsFeature QgsVectorLayerUtils::createFeature( const QgsVectorLayer *layer, cons
380380
bool checkUnique = true;
381381

382382
// in order of priority:
383+
// 1. passed attribute value and if field does not have a unique constraint like primary key
384+
if ( attributes.contains( idx ) )
385+
{
386+
v = attributes.value( idx );
387+
if ( fields.at( idx ).constraints().constraints() & QgsFieldConstraints::ConstraintUnique
388+
&& QgsVectorLayerUtils::valueExists( layer, idx, v ) )
389+
{
390+
// unique constraint violated
391+
QVariant uniqueValue = QgsVectorLayerUtils::createUniqueValue( layer, idx, v );
392+
if ( uniqueValue.isValid() )
393+
v = uniqueValue;
394+
}
395+
checkUnique = false;
396+
}
383397

384-
// 1. client side default expression
385-
if ( layer->defaultValueDefinition( idx ).isValid() )
398+
// 2. client side default expression
399+
// note - deliberately not using else if!
400+
if ( !v.isValid() && layer->defaultValueDefinition( idx ).isValid() )
386401
{
387402
// client side default expression set - takes precedence over all. Why? Well, this is the only default
388403
// which QGIS users have control over, so we assume that they're deliberately overriding any
389404
// provider defaults for some good reason and we should respect that
390405
v = layer->defaultValue( idx, newFeature, evalContext );
391406
}
392407

393-
// 2. provider side default value clause
408+
// 3. provider side default value clause
394409
// note - not an else if deliberately. Users may return null from a default value expression to fallback to provider defaults
395410
if ( !v.isValid() && fields.fieldOrigin( idx ) == QgsFields::OriginProvider )
396411
{
@@ -403,7 +418,7 @@ QgsFeature QgsVectorLayerUtils::createFeature( const QgsVectorLayer *layer, cons
403418
}
404419
}
405420

406-
// 3. provider side default literal
421+
// 4. provider side default literal
407422
// note - deliberately not using else if!
408423
if ( !v.isValid() && fields.fieldOrigin( idx ) == QgsFields::OriginProvider )
409424
{
@@ -416,13 +431,6 @@ QgsFeature QgsVectorLayerUtils::createFeature( const QgsVectorLayer *layer, cons
416431
}
417432
}
418433

419-
// 4. passed attribute value
420-
// note - deliberately not using else if!
421-
if ( !v.isValid() && attributes.contains( idx ) )
422-
{
423-
v = attributes.value( idx );
424-
}
425-
426434
// last of all... check that unique constraints are respected
427435
// we can't handle not null or expression constraints here, since there's no way to pick a sensible
428436
// value if the constraint is violated

tests/src/python/test_provider_postgres.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -808,14 +808,21 @@ def testVectorLayerUtilsCreateFeatureWithProviderDefault(self):
808808
default_clause = 'nextval(\'qgis_test."someData_pk_seq"\'::regclass)'
809809
self.assertEqual(vl.dataProvider().defaultValueClause(0), default_clause)
810810

811-
# check that provider default clause takes precedence over passed attribute values
812-
# this also checks that the inbuilt unique constraint handling is bypassed in the case of a provider default clause
811+
# If an attribute map is provided, QgsVectorLayerUtils.createFeature must
812+
# respect it, otherwise default values from provider are checked.
813+
# User's choice will not be respected if the value violates unique constraints.
814+
# See https://issues.qgis.org/issues/19936
813815
f = QgsVectorLayerUtils.createFeature(vl, attributes={1: 5, 3: 'map'})
814-
self.assertEqual(f.attributes(), [default_clause, 5, "'qgis'::text", "'qgis'::text", None, None])
816+
# changed so that createFeature respects user choice
817+
self.assertEqual(f.attributes(), [default_clause, 5, "'qgis'::text", 'map', None, None])
815818

816-
# test take vector layer default value expression overrides postgres provider default clause
817819
vl.setDefaultValueDefinition(3, QgsDefaultValue("'mappy'"))
820+
# test ignore vector layer default value expression overrides postgres provider default clause,
821+
# due to user's choice
818822
f = QgsVectorLayerUtils.createFeature(vl, attributes={1: 5, 3: 'map'})
823+
self.assertEqual(f.attributes(), [default_clause, 5, "'qgis'::text", 'map', None, None])
824+
# Since user did not enter a default for field 3, test must return the default value chosen
825+
f = QgsVectorLayerUtils.createFeature(vl, attributes={1: 5})
819826
self.assertEqual(f.attributes(), [default_clause, 5, "'qgis'::text", 'mappy', None, None])
820827

821828
# See https://issues.qgis.org/issues/15188

tests/src/python/test_qgsvectorlayerutils.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -258,21 +258,24 @@ def testCreateFeature(self):
258258
# layer with default value expression
259259
layer.setDefaultValueDefinition(2, QgsDefaultValue('3*4'))
260260
f = QgsVectorLayerUtils.createFeature(layer)
261-
self.assertEqual(f.attributes(), [NULL, NULL, 12.0])
262-
# we expect the default value expression to take precedence over the attribute map
261+
self.assertEqual(f.attributes(), [NULL, NULL, 12])
262+
# we do not expect the default value expression to take precedence over the attribute map
263263
f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'a', 2: 6.0})
264-
self.assertEqual(f.attributes(), ['a', NULL, 12.0])
264+
self.assertEqual(f.attributes(), ['a', NULL, 6.0])
265265
# layer with default value expression based on geometry
266266
layer.setDefaultValueDefinition(2, QgsDefaultValue('3*$x'))
267267
f = QgsVectorLayerUtils.createFeature(layer, g)
268+
#adjusted so that input value and output feature are the same
268269
self.assertEqual(f.attributes(), [NULL, NULL, 300.0])
269270
layer.setDefaultValueDefinition(2, QgsDefaultValue(None))
270271

271272
# test with violated unique constraints
272273
layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintUnique)
273274
f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123})
275+
# since field 1 has Unique Constraint, it ignores value 123 that already has been set and sets to 128
274276
self.assertEqual(f.attributes(), ['test_1', 128, NULL])
275277
layer.setFieldConstraint(0, QgsFieldConstraints.ConstraintUnique)
278+
# since field 0 and 1 already have values test_1 and 123, the output must be a new unique value
276279
f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123})
277280
self.assertEqual(f.attributes(), ['test_4', 128, NULL])
278281

0 commit comments

Comments
 (0)