Skip to content

Commit

Permalink
Split handling of literal default values from provider side
Browse files Browse the repository at this point in the history
default value SQL clauses

QgsVectorDataProvider now has two methods:

- defaultValueClause: returns SQL fragment which must be evaluated
by the provider to obtain the default value, eg sequence values
- defaultValue: returns the literal constant default value
for a field
  • Loading branch information
nyalldawson committed Nov 7, 2016
1 parent 59b10d6 commit 1fea20d
Show file tree
Hide file tree
Showing 17 changed files with 87 additions and 40 deletions.
15 changes: 13 additions & 2 deletions python/core/qgsvectordataprovider.sip
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,20 @@ class QgsVectorDataProvider : QgsDataProvider
const QMap<qint64, QgsGeometry> &geometry_map );

/**
* Returns the default value for field specified by @c fieldId
* Returns any literal default values which are present at the provider for a specified
* field index.
* @see defaultValueClause()
*/
virtual QVariant defaultValue( int fieldIndex ) const;

/**
* Returns any default value clauses which are present at the provider for a specified
* field index. These clauses are usually SQL fragments which must be evaluated by the
* provider, eg sequence values.
* @see defaultValue()
* @note added in QGIS 3.0
*/
virtual QVariant defaultValue( int fieldId ) const;
virtual QString defaultValueClause( int fieldIndex ) const;

/**
* Returns any constraints which are present at the provider for a specified
Expand Down
6 changes: 3 additions & 3 deletions src/app/qgisapp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6988,7 +6988,7 @@ void QgisApp::mergeAttributesOfSelectedFeatures()
QgsField fld( vl->fields().at( i ) );
bool isDefaultValue = vl->fields().fieldOrigin( i ) == QgsFields::OriginProvider &&
vl->dataProvider() &&
vl->dataProvider()->defaultValue( vl->fields().fieldOriginIndex( i ) ) == val;
vl->dataProvider()->defaultValueClause( vl->fields().fieldOriginIndex( i ) ) == val;

// convert to destination data type
if ( !isDefaultValue && !fld.convertCompatible( val ) )
Expand Down Expand Up @@ -7167,7 +7167,7 @@ void QgisApp::mergeSelectedFeatures()
QVariant val = attrs.at( i );
bool isDefaultValue = vl->fields().fieldOrigin( i ) == QgsFields::OriginProvider &&
vl->dataProvider() &&
vl->dataProvider()->defaultValue( vl->fields().fieldOriginIndex( i ) ) == val;
vl->dataProvider()->defaultValueClause( vl->fields().fieldOriginIndex( i ) ) == val;

// convert to destination data type
if ( !isDefaultValue && !vl->fields().at( i ).convertCompatible( val ) )
Expand Down Expand Up @@ -7481,7 +7481,7 @@ void QgisApp::editPaste( QgsMapLayer *destinationLayer )
}
else
{
defVal = pasteVectorLayer->dataProvider()->defaultValue( dst );
defVal = pasteVectorLayer->dataProvider()->defaultValueClause( dst );
}

if ( defVal.isValid() && !defVal.isNull() )
Expand Down
2 changes: 1 addition & 1 deletion src/app/qgsfeatureaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ bool QgsFeatureAction::addFeature( const QgsAttributeMap& defaultAttributes, boo
}
else
{
v = provider->defaultValue( idx );
v = provider->defaultValueClause( idx );
}

mFeature->setAttribute( idx, v );
Expand Down
2 changes: 1 addition & 1 deletion src/app/qgsidentifyresultsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ void QgsIdentifyResultsDialog::addFeature( QgsVectorLayer *vlayer, const QgsFeat

QString defVal;
if ( fields.fieldOrigin( i ) == QgsFields::OriginProvider && vlayer->dataProvider() )
defVal = vlayer->dataProvider()->defaultValue( fields.fieldOriginIndex( i ) ).toString();
defVal = vlayer->dataProvider()->defaultValueClause( fields.fieldOriginIndex( i ) );

QString value = defVal == attrs.at( i ) ? defVal : fields.at( i ).displayString( attrs.at( i ) );
QgsTreeWidgetItem *attrItem = new QgsTreeWidgetItem( QStringList() << QString::number( i ) << value );
Expand Down
4 changes: 2 additions & 2 deletions src/app/qgsmergeattributesdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ QgsAttributes QgsMergeAttributesDialog::mergedAttributes() const
if ( !mVectorLayer->defaultValueExpression( fieldIdx ).isEmpty() )
results[fieldIdx] = mVectorLayer->defaultValue( fieldIdx, mFeatureList.at( 0 ), &context );
else if ( mVectorLayer->dataProvider() )
results[fieldIdx] = mVectorLayer->dataProvider()->defaultValue( fieldIdx );
results[fieldIdx] = mVectorLayer->dataProvider()->defaultValueClause( fieldIdx );
else
results[fieldIdx] = QVariant();
continue;
Expand All @@ -567,7 +567,7 @@ QgsAttributes QgsMergeAttributesDialog::mergedAttributes() const
}
else if ( mVectorLayer->dataProvider() )
{
results[fieldIdx] = mVectorLayer->dataProvider()->defaultValue( fieldIdx );
results[fieldIdx] = mVectorLayer->dataProvider()->defaultValueClause( fieldIdx );
}
widgetIndex++;
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/qgsofflineediting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,7 @@ void QgsOfflineEditing::applyFeaturesAdded( QgsVectorLayer* offlineLayer, QgsVec
if ( !remoteLayer->defaultValueExpression( k ).isEmpty() )
newAttrs[k] = remoteLayer->defaultValue( k, f, &context );
else if ( remoteFlds.fieldOrigin( k ) == QgsFields::OriginProvider )
newAttrs[k] = remoteLayer->dataProvider()->defaultValue( remoteFlds.fieldOriginIndex( k ) );
newAttrs[k] = remoteLayer->dataProvider()->defaultValueClause( remoteFlds.fieldOriginIndex( k ) );
}

f.setAttributes( newAttrs );
Expand Down
6 changes: 6 additions & 0 deletions src/core/qgsvectordataprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ QVariant QgsVectorDataProvider::defaultValue( int fieldId ) const
return QVariant();
}

QString QgsVectorDataProvider::defaultValueClause( int fieldIndex ) const
{
Q_UNUSED( fieldIndex );
return QString();
}

QgsFieldConstraints::Constraints QgsVectorDataProvider::fieldConstraints( int fieldIndex ) const
{
QgsFields f = fields();
Expand Down
15 changes: 13 additions & 2 deletions src/core/qgsvectordataprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,20 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider
const QgsGeometryMap &geometry_map );

/**
* Returns the default value for field specified by @c fieldId
* Returns any literal default values which are present at the provider for a specified
* field index.
* @see defaultValueClause()
*/
virtual QVariant defaultValue( int fieldIndex ) const;

/**
* Returns any default value clauses which are present at the provider for a specified
* field index. These clauses are usually SQL fragments which must be evaluated by the
* provider, eg sequence values.
* @see defaultValue()
* @note added in QGIS 3.0
*/
virtual QVariant defaultValue( int fieldId ) const;
virtual QString defaultValueClause( int fieldIndex ) const;

/**
* Returns any constraints which are present at the provider for a specified
Expand Down
2 changes: 1 addition & 1 deletion src/core/qgsvectorlayereditutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPoint>& splitLine, bo
QgsAttributes newAttributes = feat.attributes();
Q_FOREACH ( int pkIdx, L->dataProvider()->pkAttributeIndexes() )
{
const QVariant defaultValue = L->dataProvider()->defaultValue( pkIdx );
const QVariant defaultValue = L->dataProvider()->defaultValueClause( pkIdx );
if ( !defaultValue.isNull() )
{
newAttributes[ pkIdx ] = defaultValue;
Expand Down
2 changes: 1 addition & 1 deletion src/gui/editorwidgets/core/qgseditorwidgetfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ QString QgsEditorWidgetFactory::representValue( QgsVectorLayer* vl, int fieldIdx

QString defVal;
if ( vl->fields().fieldOrigin( fieldIdx ) == QgsFields::OriginProvider && vl->dataProvider() )
defVal = vl->dataProvider()->defaultValue( vl->fields().fieldOriginIndex( fieldIdx ) ).toString();
defVal = vl->dataProvider()->defaultValueClause( vl->fields().fieldOriginIndex( fieldIdx ) );

return value == defVal ? defVal : vl->fields().at( fieldIdx ).displayString( value );
}
Expand Down
2 changes: 1 addition & 1 deletion src/gui/editorwidgets/core/qgseditorwidgetwrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ QgsField QgsEditorWidgetWrapper::field() const

QVariant QgsEditorWidgetWrapper::defaultValue() const
{
mDefaultValue = layer()->dataProvider()->defaultValue( mFieldIdx );
mDefaultValue = layer()->dataProvider()->defaultValueClause( mFieldIdx );

return mDefaultValue;
}
Expand Down
13 changes: 5 additions & 8 deletions src/providers/mssql/qgsmssqlprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ void QgsMssqlProvider::loadFields()

if ( !query.value( 12 ).isNull() )
{
mDefaultValues.insert( i, query.value( 12 ) );
mDefaultValues.insert( i, query.value( 12 ).toString() );
}
++i;
}
Expand Down Expand Up @@ -501,12 +501,9 @@ QString QgsMssqlProvider::quotedValue( const QVariant& value )
}
}

QVariant QgsMssqlProvider::defaultValue( int fieldId ) const
QString QgsMssqlProvider::defaultValueClause( int fieldId ) const
{
if ( mDefaultValues.contains( fieldId ) )
return mDefaultValues[fieldId];
else
return QVariant( QString::null );
return mDefaultValues.value( fieldId, QString() );
}

QString QgsMssqlProvider::storageType() const
Expand Down Expand Up @@ -815,7 +812,7 @@ bool QgsMssqlProvider::addFeatures( QgsFeatureList & flist )
if ( fld.name().isEmpty() )
continue; // invalid

if ( mDefaultValues.contains( i ) && mDefaultValues[i] == attrs.at( i ) )
if ( mDefaultValues.contains( i ) && mDefaultValues.value( i ) == attrs.at( i ).toString() )
continue; // skip fields having default values

if ( !first )
Expand Down Expand Up @@ -886,7 +883,7 @@ bool QgsMssqlProvider::addFeatures( QgsFeatureList & flist )
if ( fld.name().isEmpty() )
continue; // invalid

if ( mDefaultValues.contains( i ) && mDefaultValues[i] == attrs.at( i ) )
if ( mDefaultValues.contains( i ) && mDefaultValues.value( i ) == attrs.at( i ).toString() )
continue; // skip fields having default values

QVariant::Type type = fld.type();
Expand Down
4 changes: 2 additions & 2 deletions src/providers/mssql/qgsmssqlprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ class QgsMssqlProvider : public QgsVectorDataProvider
//! Convert values to quoted values for database work *
static QString quotedValue( const QVariant& value );

QVariant defaultValue( int fieldId ) const override;
QString defaultValueClause( int fieldId ) const override;

//! Import a vector layer into the database
static QgsVectorLayerImport::ImportError createEmptyLayer(
Expand All @@ -212,7 +212,7 @@ class QgsMssqlProvider : public QgsVectorDataProvider

//! Fields
QgsFields mAttributeFields;
QMap<int, QVariant> mDefaultValues;
QMap<int, QString> mDefaultValues;

mutable QgsMssqlGeometryParser mParser;

Expand Down
24 changes: 18 additions & 6 deletions src/providers/postgres/qgspostgresprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1716,15 +1716,27 @@ bool QgsPostgresProvider::isValid() const
return mValid;
}

QString QgsPostgresProvider::defaultValueClause( int fieldId ) const
{
QString defVal = mDefaultValues.value( fieldId, QString() );

if ( !providerProperty( EvaluateDefaultValues, false ).toBool() && !defVal.isEmpty() )
{
return defVal;
}

return QString();
}

QVariant QgsPostgresProvider::defaultValue( int fieldId ) const
{
QVariant defVal = mDefaultValues.value( fieldId, QString::null );
QString defVal = mDefaultValues.value( fieldId, QString() );

if ( providerProperty( EvaluateDefaultValues, false ).toBool() && !defVal.isNull() )
if ( providerProperty( EvaluateDefaultValues, false ).toBool() && !defVal.isEmpty() )
{
QgsField fld = field( fieldId );

QgsPostgresResult res( connectionRO()->PQexec( QStringLiteral( "SELECT %1" ).arg( defVal.toString() ) ) );
QgsPostgresResult res( connectionRO()->PQexec( QStringLiteral( "SELECT %1" ).arg( defVal ) ) );

if ( res.result() )
return convertValue( fld.type(), fld.subType(), res.PQgetvalue( 0, 0 ) );
Expand All @@ -1735,7 +1747,7 @@ QVariant QgsPostgresProvider::defaultValue( int fieldId ) const
}
}

return defVal;
return QVariant();
}

QString QgsPostgresProvider::paramValue( const QString& fieldValue, const QString &defaultValue ) const
Expand Down Expand Up @@ -1891,7 +1903,7 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist )
values += delim + QStringLiteral( "$%1" ).arg( defaultValues.size() + offset );
delim = ',';
fieldId << idx;
defaultValues << defaultValue( idx ).toString();
defaultValues << defaultValueClause( idx );
}
}

Expand Down Expand Up @@ -1928,7 +1940,7 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist )

insert += delim + quotedIdentifier( fieldname );

QString defVal = defaultValue( idx ).toString();
QString defVal = defaultValueClause( idx );

if ( i == flist.size() )
{
Expand Down
1 change: 1 addition & 0 deletions src/providers/postgres/qgspostgresprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ class QgsPostgresProvider : public QgsVectorDataProvider
virtual bool isSaveAndLoadStyleToDBSupported() const override { return true; }
QgsAttributeList attributeIndexes() const override;
QgsAttributeList pkAttributeIndexes() const override { return mPrimaryKeyAttrs; }
QString defaultValueClause( int fieldId ) const override;
QVariant defaultValue( int fieldId ) const override;

/** Adds a list of features
Expand Down
23 changes: 16 additions & 7 deletions tests/src/python/test_provider_postgres.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
QgsTransactionGroup,
QgsField,
QgsFieldConstraints,
QgsDataProvider,
NULL
)
from qgis.gui import QgsEditorWidgetRegistry
Expand Down Expand Up @@ -84,9 +85,17 @@ def partiallyCompiledFilters(self):

# HERE GO THE PROVIDER SPECIFIC TESTS
def testDefaultValue(self):
self.assertEqual(self.provider.defaultValue(0), 'nextval(\'qgis_test."someData_pk_seq"\'::regclass)')
self.provider.setProviderProperty(QgsDataProvider.EvaluateDefaultValues, True)
self.assertTrue(isinstance(self.provider.defaultValue(0), int))
self.assertEqual(self.provider.defaultValue(1), NULL)
self.assertEqual(self.provider.defaultValue(2), '\'qgis\'::text')
self.assertEqual(self.provider.defaultValue(2), 'qgis')
self.provider.setProviderProperty(QgsDataProvider.EvaluateDefaultValues, False)

def testDefaultValueClause(self):
self.provider.setProviderProperty(QgsDataProvider.EvaluateDefaultValues, False)
self.assertEqual(self.provider.defaultValueClause(0), 'nextval(\'qgis_test."someData_pk_seq"\'::regclass)')
self.assertFalse(self.provider.defaultValueClause(1))
self.assertEqual(self.provider.defaultValueClause(2), '\'qgis\'::text')

def testDateTimeTypes(self):
vl = QgsVectorLayer('%s table="qgis_test"."date_times" sql=' % (self.dbconn), "testdatetimes", "postgres")
Expand Down Expand Up @@ -248,7 +257,7 @@ def testPktMapInsert(self):
vl = QgsVectorLayer('{} table="qgis_test"."{}" key="obj_id" sql='.format(self.dbconn, 'oid_serial_table'), "oid_serial", "postgres")
self.assertTrue(vl.isValid())
f = QgsFeature(vl.fields())
f['obj_id'] = vl.dataProvider().defaultValue(0)
f['obj_id'] = vl.dataProvider().defaultValueClause(0)
f['name'] = 'Test'
r, f = vl.dataProvider().addFeatures([f])
self.assertTrue(r)
Expand Down Expand Up @@ -553,17 +562,17 @@ def testKey(lyr, key, kfnames):
oflds = olyr.fields()
if key is None:
# if the pkey was not given, it will create a pkey
self.assertEquals(oflds.size(), flds.size() + 1)
self.assertEquals(oflds[0].name(), kfnames[0])
self.assertEqual(oflds.size(), flds.size() + 1)
self.assertEqual(oflds[0].name(), kfnames[0])
for i in range(flds.size()):
self.assertEqual(oflds[i + 1].name(), flds[i].name())
else:
# pkey was given, no extra field generated
self.assertEquals(oflds.size(), flds.size())
self.assertEqual(oflds.size(), flds.size())
for i in range(oflds.size()):
self.assertEqual(oflds[i].name(), flds[i].name())
pks = olyr.pkAttributeList()
self.assertEquals(len(pks), len(kfnames))
self.assertEqual(len(pks), len(kfnames))
for i in range(0, len(kfnames)):
self.assertEqual(oflds[pks[i]].name(), kfnames[i])

Expand Down
4 changes: 2 additions & 2 deletions tests/src/python/test_qgsrelationeditwidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def test_link_feature(self):
wrapper = self.createWrapper(self.vl_a, '"name"=\'Douglas Adams\'') # NOQA

f = QgsFeature(self.vl_b.fields())
f.setAttributes([self.vl_b.dataProvider().defaultValue(0), 'The Hitchhiker\'s Guide to the Galaxy'])
f.setAttributes([self.vl_b.dataProvider().defaultValueClause(0), 'The Hitchhiker\'s Guide to the Galaxy'])
self.vl_b.addFeature(f)

def choose_linked_feature():
Expand Down Expand Up @@ -318,7 +318,7 @@ def addFeature(self, layer, defaultValues, defaultGeometry):
if v:
values.append(v)
else:
values.append(layer.dataProvider().defaultValue(i))
values.append(layer.dataProvider().defaultValueClause(i))
f = QgsFeature(layer.fields())
f.setAttributes(self.values)
f.setGeometry(defaultGeometry)
Expand Down

0 comments on commit 1fea20d

Please sign in to comment.