mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-15 00:04:00 -04:00
Add method to fetch constraints from a vector data provider
Implemented for unique and not null constraints for postgres provider
This commit is contained in:
parent
5e3bef7799
commit
d1fd588499
@ -55,6 +55,17 @@ class QgsVectorDataProvider : QgsDataProvider
|
|||||||
/** Bitmask of all provider's editing capabilities */
|
/** Bitmask of all provider's editing capabilities */
|
||||||
static const int EditingCapabilities;
|
static const int EditingCapabilities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constraints which may be present on a field.
|
||||||
|
* @note added in QGIS 3.0
|
||||||
|
*/
|
||||||
|
enum Constraint
|
||||||
|
{
|
||||||
|
ConstraintNotNull, //!< Field may not be null
|
||||||
|
ConstraintUnique, //!< Field must have a unique value
|
||||||
|
};
|
||||||
|
typedef QFlags<QgsVectorDataProvider::Constraint> Constraints;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor of the vector provider
|
* Constructor of the vector provider
|
||||||
* @param uri uniform resource locator (URI) for a dataset
|
* @param uri uniform resource locator (URI) for a dataset
|
||||||
@ -230,6 +241,13 @@ class QgsVectorDataProvider : QgsDataProvider
|
|||||||
*/
|
*/
|
||||||
virtual QVariant defaultValue( int fieldId ) const;
|
virtual QVariant defaultValue( int fieldId ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns any constraints which are present at the provider for a specified
|
||||||
|
* field index.
|
||||||
|
* @note added in QGIS 3.0
|
||||||
|
*/
|
||||||
|
virtual QgsVectorDataProvider::Constraints fieldConstraints( int fieldIndex ) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes geometries of existing features
|
* Changes geometries of existing features
|
||||||
* @param geometry_map A QgsGeometryMap whose index contains the feature IDs
|
* @param geometry_map A QgsGeometryMap whose index contains the feature IDs
|
||||||
@ -434,3 +452,4 @@ class QgsVectorDataProvider : QgsDataProvider
|
|||||||
};
|
};
|
||||||
|
|
||||||
QFlags<QgsVectorDataProvider::Capability> operator|(QgsVectorDataProvider::Capability f1, QFlags<QgsVectorDataProvider::Capability> f2);
|
QFlags<QgsVectorDataProvider::Capability> operator|(QgsVectorDataProvider::Capability f1, QFlags<QgsVectorDataProvider::Capability> f2);
|
||||||
|
QFlags<QgsVectorDataProvider::Constraint> operator|(QgsVectorDataProvider::Constraint f1, QFlags<QgsVectorDataProvider::Constraint> f2);
|
||||||
|
@ -98,6 +98,12 @@ QVariant QgsVectorDataProvider::defaultValue( int fieldId ) const
|
|||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QgsVectorDataProvider::Constraints QgsVectorDataProvider::fieldConstraints( int fieldIndex ) const
|
||||||
|
{
|
||||||
|
Q_UNUSED( fieldIndex );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool QgsVectorDataProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
|
bool QgsVectorDataProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
|
||||||
{
|
{
|
||||||
Q_UNUSED( geometry_map );
|
Q_UNUSED( geometry_map );
|
||||||
|
@ -106,6 +106,17 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider
|
|||||||
ChangeAttributeValues | ChangeGeometries | AddAttributes | DeleteAttributes |
|
ChangeAttributeValues | ChangeGeometries | AddAttributes | DeleteAttributes |
|
||||||
RenameAttributes;
|
RenameAttributes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constraints which may be present on a field.
|
||||||
|
* @note added in QGIS 3.0
|
||||||
|
*/
|
||||||
|
enum Constraint
|
||||||
|
{
|
||||||
|
ConstraintNotNull = 1, //!< Field may not be null
|
||||||
|
ConstraintUnique = 1 << 1, //!< Field must have a unique value
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS( Constraints, Constraint )
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor of the vector provider
|
* Constructor of the vector provider
|
||||||
* @param uri uniform resource locator (URI) for a dataset
|
* @param uri uniform resource locator (URI) for a dataset
|
||||||
@ -281,6 +292,13 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider
|
|||||||
*/
|
*/
|
||||||
virtual QVariant defaultValue( int fieldId ) const;
|
virtual QVariant defaultValue( int fieldId ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns any constraints which are present at the provider for a specified
|
||||||
|
* field index.
|
||||||
|
* @note added in QGIS 3.0
|
||||||
|
*/
|
||||||
|
virtual Constraints fieldConstraints( int fieldIndex ) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes geometries of existing features
|
* Changes geometries of existing features
|
||||||
* @param geometry_map A QgsGeometryMap whose index contains the feature IDs
|
* @param geometry_map A QgsGeometryMap whose index contains the feature IDs
|
||||||
@ -520,6 +538,7 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider
|
|||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsVectorDataProvider::Capabilities )
|
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsVectorDataProvider::Capabilities )
|
||||||
|
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsVectorDataProvider::Constraints )
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -718,6 +718,7 @@ bool QgsPostgresProvider::loadFields()
|
|||||||
|
|
||||||
QMap<int, QMap<int, QString> > fmtFieldTypeMap, descrMap, defValMap;
|
QMap<int, QMap<int, QString> > fmtFieldTypeMap, descrMap, defValMap;
|
||||||
QMap<int, QMap<int, int> > attTypeIdMap;
|
QMap<int, QMap<int, int> > attTypeIdMap;
|
||||||
|
QMap<int, QMap<int, bool> > notNullMap, uniqueMap;
|
||||||
if ( result.PQnfields() > 0 )
|
if ( result.PQnfields() > 0 )
|
||||||
{
|
{
|
||||||
// Collect table oids
|
// Collect table oids
|
||||||
@ -742,9 +743,13 @@ bool QgsPostgresProvider::loadFields()
|
|||||||
QString tableoidsFilter = '(' + tableoidsList.join( QStringLiteral( "," ) ) + ')';
|
QString tableoidsFilter = '(' + tableoidsList.join( QStringLiteral( "," ) ) + ')';
|
||||||
|
|
||||||
// Collect formatted field types
|
// Collect formatted field types
|
||||||
sql = "SELECT attrelid, attnum, pg_catalog.format_type(atttypid,atttypmod), pg_catalog.col_description(attrelid,attnum), pg_catalog.pg_get_expr(adbin,adrelid), atttypid"
|
sql = "SELECT attrelid, attnum, pg_catalog.format_type(atttypid,atttypmod), pg_catalog.col_description(attrelid,attnum), pg_catalog.pg_get_expr(adbin,adrelid), atttypid, attnotnull::int, indisunique::int"
|
||||||
" FROM pg_attribute"
|
" FROM pg_attribute"
|
||||||
" LEFT OUTER JOIN pg_attrdef ON attrelid=adrelid AND attnum=adnum"
|
" LEFT OUTER JOIN pg_attrdef ON attrelid=adrelid AND attnum=adnum"
|
||||||
|
|
||||||
|
// find unique constraints if present. Text cast required to handle int2vector comparison. Distinct required as multiple unique constraints may exist
|
||||||
|
" LEFT OUTER JOIN ( SELECT DISTINCT indrelid, indkey, indisunique FROM pg_index WHERE indisunique ) uniq ON attrelid=indrelid AND attnum::text=indkey::text "
|
||||||
|
|
||||||
" WHERE attrelid IN " + tableoidsFilter;
|
" WHERE attrelid IN " + tableoidsFilter;
|
||||||
QgsPostgresResult fmtFieldTypeResult( connectionRO()->PQexec( sql ) );
|
QgsPostgresResult fmtFieldTypeResult( connectionRO()->PQexec( sql ) );
|
||||||
for ( int i = 0; i < fmtFieldTypeResult.PQntuples(); ++i )
|
for ( int i = 0; i < fmtFieldTypeResult.PQntuples(); ++i )
|
||||||
@ -755,10 +760,14 @@ bool QgsPostgresProvider::loadFields()
|
|||||||
QString descr = fmtFieldTypeResult.PQgetvalue( i, 3 );
|
QString descr = fmtFieldTypeResult.PQgetvalue( i, 3 );
|
||||||
QString defVal = fmtFieldTypeResult.PQgetvalue( i, 4 );
|
QString defVal = fmtFieldTypeResult.PQgetvalue( i, 4 );
|
||||||
int attType = fmtFieldTypeResult.PQgetvalue( i, 5 ).toInt();
|
int attType = fmtFieldTypeResult.PQgetvalue( i, 5 ).toInt();
|
||||||
|
bool attNotNull = fmtFieldTypeResult.PQgetvalue( i, 6 ).toInt();
|
||||||
|
bool uniqueConstraint = fmtFieldTypeResult.PQgetvalue( i, 7 ).toInt();
|
||||||
fmtFieldTypeMap[attrelid][attnum] = formatType;
|
fmtFieldTypeMap[attrelid][attnum] = formatType;
|
||||||
descrMap[attrelid][attnum] = descr;
|
descrMap[attrelid][attnum] = descr;
|
||||||
defValMap[attrelid][attnum] = defVal;
|
defValMap[attrelid][attnum] = defVal;
|
||||||
attTypeIdMap[attrelid][attnum] = attType;
|
attTypeIdMap[attrelid][attnum] = attType;
|
||||||
|
notNullMap[attrelid][attnum] = attNotNull;
|
||||||
|
uniqueMap[attrelid][attnum] = uniqueConstraint;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -988,6 +997,14 @@ bool QgsPostgresProvider::loadFields()
|
|||||||
|
|
||||||
mAttrPalIndexName.insert( i, fieldName );
|
mAttrPalIndexName.insert( i, fieldName );
|
||||||
mDefaultValues.insert( mAttributeFields.size(), defValMap[tableoid][attnum] );
|
mDefaultValues.insert( mAttributeFields.size(), defValMap[tableoid][attnum] );
|
||||||
|
|
||||||
|
Constraints constraints = 0;
|
||||||
|
if ( notNullMap[tableoid][attnum] )
|
||||||
|
constraints |= ConstraintNotNull;
|
||||||
|
if ( uniqueMap[tableoid][attnum] )
|
||||||
|
constraints |= ConstraintUnique;
|
||||||
|
mFieldConstraints.insert( mAttributeFields.size(), constraints );
|
||||||
|
|
||||||
mAttributeFields.append( QgsField( fieldName, fieldType, fieldTypeName, fieldSize, fieldPrec, fieldComment, fieldSubType ) );
|
mAttributeFields.append( QgsField( fieldName, fieldType, fieldTypeName, fieldSize, fieldPrec, fieldComment, fieldSubType ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1719,6 +1736,11 @@ QVariant QgsPostgresProvider::defaultValue( int fieldId ) const
|
|||||||
return defVal;
|
return defVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QgsVectorDataProvider::Constraints QgsPostgresProvider::fieldConstraints( int fieldIndex ) const
|
||||||
|
{
|
||||||
|
return mFieldConstraints.value( fieldIndex, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
QString QgsPostgresProvider::paramValue( const QString& fieldValue, const QString &defaultValue ) const
|
QString QgsPostgresProvider::paramValue( const QString& fieldValue, const QString &defaultValue ) const
|
||||||
{
|
{
|
||||||
if ( fieldValue.isNull() )
|
if ( fieldValue.isNull() )
|
||||||
|
@ -161,6 +161,7 @@ class QgsPostgresProvider : public QgsVectorDataProvider
|
|||||||
QgsAttributeList attributeIndexes() const override;
|
QgsAttributeList attributeIndexes() const override;
|
||||||
QgsAttributeList pkAttributeIndexes() const override { return mPrimaryKeyAttrs; }
|
QgsAttributeList pkAttributeIndexes() const override { return mPrimaryKeyAttrs; }
|
||||||
QVariant defaultValue( int fieldId ) const override;
|
QVariant defaultValue( int fieldId ) const override;
|
||||||
|
Constraints fieldConstraints( int fieldIndex ) const override;
|
||||||
|
|
||||||
/** Adds a list of features
|
/** Adds a list of features
|
||||||
@return true in case of success and false in case of failure*/
|
@return true in case of success and false in case of failure*/
|
||||||
@ -493,6 +494,7 @@ class QgsPostgresProvider : public QgsVectorDataProvider
|
|||||||
void setTransaction( QgsTransaction* transaction ) override;
|
void setTransaction( QgsTransaction* transaction ) override;
|
||||||
|
|
||||||
QHash<int, QString> mDefaultValues;
|
QHash<int, QString> mDefaultValues;
|
||||||
|
QHash<int, QgsVectorDataProvider::Constraints > mFieldConstraints;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ from qgis.core import (
|
|||||||
QgsFeatureRequest,
|
QgsFeatureRequest,
|
||||||
QgsFeature,
|
QgsFeature,
|
||||||
QgsTransactionGroup,
|
QgsTransactionGroup,
|
||||||
|
QgsVectorDataProvider,
|
||||||
NULL
|
NULL
|
||||||
)
|
)
|
||||||
from qgis.gui import QgsEditorWidgetRegistry
|
from qgis.gui import QgsEditorWidgetRegistry
|
||||||
@ -437,6 +438,34 @@ class TestPyQgsPostgresProvider(unittest.TestCase, ProviderTestCase):
|
|||||||
self.assertTrue(isinstance(f.attributes()[value_idx], list))
|
self.assertTrue(isinstance(f.attributes()[value_idx], list))
|
||||||
self.assertEqual(f.attributes()[value_idx], [1.1, 2, -5.12345])
|
self.assertEqual(f.attributes()[value_idx], [1.1, 2, -5.12345])
|
||||||
|
|
||||||
|
def testNotNullConstraint(self):
|
||||||
|
vl = QgsVectorLayer('%s table="qgis_test"."constraints" sql=' % (self.dbconn), "constraints", "postgres")
|
||||||
|
self.assertTrue(vl.isValid())
|
||||||
|
self.assertEqual(len(vl.fields()), 4)
|
||||||
|
|
||||||
|
# test some bad field indexes
|
||||||
|
self.assertEqual(vl.dataProvider().fieldConstraints(-1), QgsVectorDataProvider.Constraints())
|
||||||
|
self.assertEqual(vl.dataProvider().fieldConstraints(1001), QgsVectorDataProvider.Constraints())
|
||||||
|
|
||||||
|
self.assertTrue(vl.dataProvider().fieldConstraints(0) & QgsVectorDataProvider.ConstraintNotNull)
|
||||||
|
self.assertFalse(vl.dataProvider().fieldConstraints(1) & QgsVectorDataProvider.ConstraintNotNull)
|
||||||
|
self.assertTrue(vl.dataProvider().fieldConstraints(2) & QgsVectorDataProvider.ConstraintNotNull)
|
||||||
|
self.assertFalse(vl.dataProvider().fieldConstraints(3) & QgsVectorDataProvider.ConstraintNotNull)
|
||||||
|
|
||||||
|
def testUniqueConstraint(self):
|
||||||
|
vl = QgsVectorLayer('%s table="qgis_test"."constraints" sql=' % (self.dbconn), "constraints", "postgres")
|
||||||
|
self.assertTrue(vl.isValid())
|
||||||
|
self.assertEqual(len(vl.fields()), 4)
|
||||||
|
|
||||||
|
# test some bad field indexes
|
||||||
|
self.assertEqual(vl.dataProvider().fieldConstraints(-1), QgsVectorDataProvider.Constraints())
|
||||||
|
self.assertEqual(vl.dataProvider().fieldConstraints(1001), QgsVectorDataProvider.Constraints())
|
||||||
|
|
||||||
|
self.assertTrue(vl.dataProvider().fieldConstraints(0) & QgsVectorDataProvider.ConstraintUnique)
|
||||||
|
self.assertTrue(vl.dataProvider().fieldConstraints(1) & QgsVectorDataProvider.ConstraintUnique)
|
||||||
|
self.assertTrue(vl.dataProvider().fieldConstraints(2) & QgsVectorDataProvider.ConstraintUnique)
|
||||||
|
self.assertFalse(vl.dataProvider().fieldConstraints(3) & QgsVectorDataProvider.ConstraintUnique)
|
||||||
|
|
||||||
# See http://hub.qgis.org/issues/15188
|
# See http://hub.qgis.org/issues/15188
|
||||||
def testNumericPrecision(self):
|
def testNumericPrecision(self):
|
||||||
uri = 'point?field=f1:int'
|
uri = 'point?field=f1:int'
|
||||||
|
21
tests/testdata/provider/testdata_pg.sql
vendored
21
tests/testdata/provider/testdata_pg.sql
vendored
@ -458,3 +458,24 @@ CREATE TABLE qgis_test.widget_styles(
|
|||||||
|
|
||||||
INSERT INTO qgis_editor_widget_styles VALUES
|
INSERT INTO qgis_editor_widget_styles VALUES
|
||||||
('qgis_test', 'widget_styles', 'fld1', 'FooEdit', '<config><option key="param1" value="value1"/><option key="param2" value="2"/></config>');
|
('qgis_test', 'widget_styles', 'fld1', 'FooEdit', '<config><option key="param1" value="value1"/><option key="param2" value="2"/></config>');
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------
|
||||||
|
-- Table for constraint tests
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS qgis_test.constraints;
|
||||||
|
CREATE TABLE qgis_test.constraints
|
||||||
|
(
|
||||||
|
gid serial NOT NULL PRIMARY KEY, -- implicit unique key
|
||||||
|
val int, -- unique constraint
|
||||||
|
name text NOT NULL, -- unique index
|
||||||
|
description text,
|
||||||
|
CONSTRAINT constraint_val UNIQUE (val),
|
||||||
|
CONSTRAINT constraint_val2 UNIQUE (val) -- create double unique constraint for test
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX constraints_uniq
|
||||||
|
ON qgis_test.constraints
|
||||||
|
USING btree
|
||||||
|
(name COLLATE pg_catalog."default"); -- unique index
|
||||||
|
Loading…
x
Reference in New Issue
Block a user