From dd11d8acf1dec571cd1d130e191091c6039e925d Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 11 Feb 2025 15:05:58 +1000 Subject: [PATCH 1/3] Add provider test for adding feature with all null attributes --- tests/src/python/providertestbase.py | 38 ++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/src/python/providertestbase.py b/tests/src/python/providertestbase.py index 073ada0f14f9..6355499210a0 100644 --- a/tests/src/python/providertestbase.py +++ b/tests/src/python/providertestbase.py @@ -1139,6 +1139,44 @@ def testAddFeatureExtraAttributes(self): ], ) + def testAddFeatureAllNull(self): + if not getattr(self, "getEditableLayer", None): + return + + l = self.getEditableLayer() + self.assertTrue(l.isValid()) + + f1 = QgsFeature() + f1.setAttributes([NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL]) + f1.setGeometry(QgsGeometry.fromWkt("Point (-72.345 71.987)")) + + if ( + l.dataProvider().capabilities() + & QgsVectorDataProvider.Capability.AddFeatures + ): + # expect success + result, added = l.dataProvider().addFeatures([f1]) + self.assertTrue( + result, + "Provider reported AddFeatures capability, but returned False to addFeatures", + ) + + # check result + f_new = next( + l.dataProvider().getFeatures( + QgsFeatureRequest().setFilterFid(added[0].id()) + ) + ) + self.assertEqual( + f_new.attributes()[1:], [NULL, NULL, NULL, NULL, NULL, NULL, NULL] + ) + else: + # expect fail + self.assertFalse( + l.dataProvider().addFeatures([f1]), + "Provider reported no AddFeatures capability, but returned true to addFeatures", + ) + def testAddFeatureWrongGeomType(self): if not getattr(self, "getEditableLayer", None): return From 398fc2c30add367ae8ab968ef32b9b0247ce1e15 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 11 Feb 2025 11:27:58 +1000 Subject: [PATCH 2/3] [oracle] Fix handling of type null variants --- src/providers/oracle/qgsoracleprovider.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/providers/oracle/qgsoracleprovider.cpp b/src/providers/oracle/qgsoracleprovider.cpp index 3e070a29c93a..f154bbfc8f00 100644 --- a/src/providers/oracle/qgsoracleprovider.cpp +++ b/src/providers/oracle/qgsoracleprovider.cpp @@ -1394,10 +1394,16 @@ bool QgsOracleProvider::addFeatures( QgsFeatureList &flist, QgsFeatureSink::Flag QVariant value = attributevec.value( fieldId[i], QVariant() ); QgsField fld = field( fieldId[i] ); - if ( ( value.isNull() && mPrimaryKeyAttrs.contains( i ) && !defaultValues.at( i ).isEmpty() ) || ( value.toString() == defaultValues[i] ) ) + if ( ( QgsVariantUtils::isNull( value ) && mPrimaryKeyAttrs.contains( i ) && !defaultValues.at( i ).isEmpty() ) || ( value.toString() == defaultValues[i] ) ) { value = evaluateDefaultExpression( defaultValues[i], fld.type() ); } + else if ( QgsVariantUtils::isNull( value ) ) + { + // don't use typed null variants, always use invalid variants. Otherwise the connection + // may incorrectly try to coerce a null value to the variant type + value = QVariant(); + } features->setAttribute( fieldId[i], value ); QgsDebugMsgLevel( QStringLiteral( "addBindValue: %1" ).arg( value.toString() ), 4 ); From c9cc3bd53be9a05255d87b7db4df163dd5777b9e Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 12 Feb 2025 06:23:58 +1000 Subject: [PATCH 3/3] Override base test to account for extra attributes in layer --- tests/src/python/test_provider_postgres.py | 40 ++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/src/python/test_provider_postgres.py b/tests/src/python/test_provider_postgres.py index fbd4e98279eb..c6cd57c785f7 100644 --- a/tests/src/python/test_provider_postgres.py +++ b/tests/src/python/test_provider_postgres.py @@ -4960,6 +4960,46 @@ def testAddFeatureMissingAttributes(self): f2.setAttributes([7, 330, "qgis", "qgis", NULL]) self.testGetFeatures(l.dataProvider(), [f1, f2]) + def testAddFeatureAllNull(self): + # overridden from base test because of extra attributes in layer + if not getattr(self, "getEditableLayer", None): + return + + l = self.getEditableLayer() + self.assertTrue(l.isValid()) + + f1 = QgsFeature() + f1.setAttributes([8, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL]) + f1.setGeometry(QgsGeometry.fromWkt("Point (-72.345 71.987)")) + + if ( + l.dataProvider().capabilities() + & QgsVectorDataProvider.Capability.AddFeatures + ): + # expect success + result, added = l.dataProvider().addFeatures([f1]) + self.assertTrue( + result, + "Provider reported AddFeatures capability, but returned False to addFeatures", + ) + + # check result + f_new = next( + l.dataProvider().getFeatures( + QgsFeatureRequest().setFilterFid(added[0].id()) + ) + ) + self.assertEqual( + f_new.attributes(), + [8, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL], + ) + else: + # expect fail + self.assertFalse( + l.dataProvider().addFeatures([f1]), + "Provider reported no AddFeatures capability, but returned true to addFeatures", + ) + def testAddFeature(self): if not getattr(self, "getEditableLayer", None): return