Skip to content

Commit

Permalink
[FEATURE] Detect default literal values from OGR layers
Browse files Browse the repository at this point in the history
Requires GDAL >= 2
  • Loading branch information
nyalldawson committed Nov 7, 2016
1 parent 0ae610c commit b0bd61f
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 1 deletion.
36 changes: 36 additions & 0 deletions src/providers/ogr/qgsogrprovider.cpp
Expand Up @@ -840,6 +840,7 @@ void QgsOgrProvider::loadFields()
QgsOgrConnPool::instance()->invalidateConnections( dataSourceUri() );
//the attribute fields need to be read again when the encoding changes
mAttributeFields.clear();
mDefaultValues.clear();
if ( !ogrLayer )
return;

Expand Down Expand Up @@ -947,6 +948,13 @@ void QgsOgrProvider::loadFields()
constraints.setConstraint( QgsFieldConstraints::ConstraintNotNull, QgsFieldConstraints::ConstraintOriginProvider );
newField.setConstraints( constraints );
}

// check if field has default value
QString defaultValue = textEncoding()->toUnicode( OGR_Fld_GetDefault( fldDef ) );
if ( !defaultValue.isEmpty() && !OGR_Fld_IsDefaultDriverSpecific( fldDef ) )
{
mDefaultValues.insert( i + ( mFirstFieldIsFid ? 1 : 0 ), defaultValue );
}
#endif

mAttributeFields.append( newField );
Expand Down Expand Up @@ -1081,6 +1089,34 @@ QgsRectangle QgsOgrProvider::extent() const
return mExtentRect;
}

QVariant QgsOgrProvider::defaultValue( int fieldId ) const
{
if ( fieldId < 0 || fieldId >= mAttributeFields.count() )
return QVariant();

QString defaultVal = mDefaultValues.value( fieldId, QString() );
if ( defaultVal.isEmpty() )
return QVariant();

QVariant resultVar = defaultVal;
if ( defaultVal == QStringLiteral( "CURRENT_TIMESTAMP" ) )
resultVar = QDateTime::currentDateTime();
else if ( defaultVal == QStringLiteral( "CURRENT_DATE" ) )
resultVar = QDate::currentDate();
else if ( defaultVal == QStringLiteral( "CURRENT_TIME" ) )
resultVar = QTime::currentTime();
else if ( defaultVal.startsWith( '\'' ) )
{
defaultVal = defaultVal.remove( 0, 1 );
defaultVal.chop( 1 );
defaultVal.replace( "''", "'" );
resultVar = defaultVal;
}

( void )mAttributeFields.at( fieldId ).convertCompatible( resultVar );
return resultVar;
}

void QgsOgrProvider::updateExtents()
{
invalidateCachedExtent( true );
Expand Down
7 changes: 7 additions & 0 deletions src/providers/ogr/qgsogrprovider.h
Expand Up @@ -124,6 +124,8 @@ class QgsOgrProvider : public QgsVectorDataProvider

virtual QgsRectangle extent() const override;

QVariant defaultValue( int fieldId ) const override;

/** Update the extents
*/
virtual void updateExtents() override;
Expand Down Expand Up @@ -297,7 +299,12 @@ class QgsOgrProvider : public QgsVectorDataProvider
private:
unsigned char *getGeometryPointer( OGRFeatureH fet );
QString ogrWkbGeometryTypeName( OGRwkbGeometryType type ) const;

QgsFields mAttributeFields;

//! Map of field index to default value
QMap<int, QString> mDefaultValues;

bool mFirstFieldIsFid;
OGRDataSourceH ogrDataSource;
mutable OGREnvelope* mExtent;
Expand Down
45 changes: 44 additions & 1 deletion tests/src/python/test_provider_ogr_sqlite.py
Expand Up @@ -20,9 +20,10 @@
import glob
from osgeo import gdal, ogr

from qgis.core import QgsVectorLayer, QgsFeature, QgsGeometry, QgsFeatureRequest, QgsField, QgsFieldConstraints
from qgis.core import QgsVectorLayer, QgsFeature, QgsGeometry, QgsFeatureRequest, QgsField, QgsFieldConstraints, NULL
from qgis.testing import start_app, unittest
from utilities import unitTestDataPath
from qgis.PyQt.QtCore import QDate, QTime, QDateTime

start_app()

Expand Down Expand Up @@ -160,6 +161,48 @@ def testNotNullConstraint(self):
self.assertTrue(fields.at(2).constraints().constraints() & QgsFieldConstraints.ConstraintNotNull)
self.assertEqual(fields.at(2).constraints().constraintOrigin(QgsFieldConstraints.ConstraintNotNull), QgsFieldConstraints.ConstraintOriginProvider)

@unittest.expectedFailure(int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 0, 0))
def testDefaultValues(self):
""" test detection of defaults on OGR layer """

tmpfile = os.path.join(self.basetestpath, 'testDefaults.sqlite')
ds = ogr.GetDriverByName('SQLite').CreateDataSource(tmpfile)
lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint, options=['FID=fid'])
lyr.CreateField(ogr.FieldDefn('field1', ogr.OFTInteger))
fld2 = ogr.FieldDefn('field2', ogr.OFTInteger)
fld2.SetDefault('5')
lyr.CreateField(fld2)
fld3 = ogr.FieldDefn('field3', ogr.OFTString)
fld3.SetDefault("'some ''default'")
lyr.CreateField(fld3)
fld4 = ogr.FieldDefn('field4', ogr.OFTDate)
fld4.SetDefault("CURRENT_DATE")
lyr.CreateField(fld4)
fld5 = ogr.FieldDefn('field5', ogr.OFTTime)
fld5.SetDefault("CURRENT_TIME")
lyr.CreateField(fld5)
fld6 = ogr.FieldDefn('field6', ogr.OFTDateTime)
fld6.SetDefault("CURRENT_TIMESTAMP")
lyr.CreateField(fld6)

ds = None

vl = QgsVectorLayer('{}'.format(tmpfile), 'test', 'ogr')
self.assertTrue(vl.isValid())

# test some bad indexes
self.assertFalse(vl.dataProvider().defaultValue(-1))
self.assertFalse(vl.dataProvider().defaultValue(1001))

# test default
self.assertEqual(vl.dataProvider().defaultValue(1), NULL)
self.assertEqual(vl.dataProvider().defaultValue(2), 5)
self.assertEqual(vl.dataProvider().defaultValue(3), "some 'default")
self.assertEqual(vl.dataProvider().defaultValue(4), QDate.currentDate())
# time may pass, so we allow 1 second difference here
self.assertTrue(vl.dataProvider().defaultValue(5).secsTo(QTime.currentTime()) < 1)
self.assertTrue(vl.dataProvider().defaultValue(6).secsTo(QDateTime.currentDateTime()) < 1)


if __name__ == '__main__':
unittest.main()

0 comments on commit b0bd61f

Please sign in to comment.