Skip to content

Commit

Permalink
Correct handling of date and time values in mssql provider
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Jan 29, 2016
1 parent 78d9617 commit 49e4567
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 10 deletions.
5 changes: 4 additions & 1 deletion src/providers/mssql/qgsmssqlfeatureiterator.cpp
Expand Up @@ -284,7 +284,10 @@ bool QgsMssqlFeatureIterator::fetchFeature( QgsFeature& feature )
for ( int i = 0; i < mAttributesToFetch.count(); i++ )
{
QVariant v = mQuery->value( i );
feature.setAttribute( mAttributesToFetch[i], mQuery->value( i ) );
const QgsField &fld = mSource->mFields.at( i );
if ( v.type() != fld.type() )
v = QgsVectorDataProvider::convertValue( fld.type(), v.toString() );
feature.setAttribute( mAttributesToFetch.at( i ), v );
}

feature.setFeatureId( mQuery->record().value( mSource->mFidColName ).toLongLong() );
Expand Down
33 changes: 31 additions & 2 deletions src/providers/mssql/qgsmssqlprovider.cpp
Expand Up @@ -160,6 +160,11 @@ QgsMssqlProvider::QgsMssqlProvider( const QString& uri )
<< QgsVectorDataProvider::NativeType( tr( "Decimal number (real)" ), "real", QVariant::Double )
<< QgsVectorDataProvider::NativeType( tr( "Decimal number (double)" ), "float", QVariant::Double )

// date/time types
<< QgsVectorDataProvider::NativeType( tr( "Date" ), "date", QVariant::Date, -1, -1, -1, -1 )
<< QgsVectorDataProvider::NativeType( tr( "Time" ), "time", QVariant::Time, -1, -1, -1, -1 )
<< QgsVectorDataProvider::NativeType( tr( "Date & Time" ), "datetime", QVariant::DateTime, -1, -1, -1, -1 )

// string types
<< QgsVectorDataProvider::NativeType( tr( "Text, fixed length (char)" ), "char", QVariant::String, 1, 255 )
<< QgsVectorDataProvider::NativeType( tr( "Text, limited variable length (varchar)" ), "varchar", QVariant::String, 1, 255 )
Expand Down Expand Up @@ -321,11 +326,14 @@ QVariant::Type QgsMssqlProvider::DecodeSqlType( const QString& sqlTypeName )
{
type = QVariant::Date;
}
else if ( sqlTypeName.startsWith( "time", Qt::CaseInsensitive ) ||
sqlTypeName.startsWith( "timestamp", Qt::CaseInsensitive ) )
else if ( sqlTypeName.startsWith( "timestamp", Qt::CaseInsensitive ) )
{
type = QVariant::String;
}
else if ( sqlTypeName.startsWith( "time", Qt::CaseInsensitive ) )
{
type = QVariant::Time;
}
else
{
QgsDebugMsg( QString( "Unknown field type: %1" ).arg( sqlTypeName ) );
Expand Down Expand Up @@ -408,6 +416,15 @@ void QgsMssqlProvider::loadFields()
query.value( 7 ).toInt(),
query.value( 8 ).toInt() ) );
}
else if ( sqlType == QVariant::Date || sqlType == QVariant::DateTime || sqlType == QVariant::Time )
{
mAttributeFields.append(
QgsField(
query.value( 3 ).toString(), sqlType,
sqlTypeName,
-1,
-1 ) );
}
else
{
mAttributeFields.append(
Expand Down Expand Up @@ -1455,8 +1472,20 @@ bool QgsMssqlProvider::convertField( QgsField &field )
break;

case QVariant::DateTime:
fieldType = "datetime";
fieldPrec = -1;
break;

case QVariant::Date:
fieldType = "date";
fieldPrec = -1;
break;

case QVariant::Time:
fieldType = "time";
fieldPrec = -1;
break;

case QVariant::String:
fieldType = "nvarchar(max)";
fieldPrec = -1;
Expand Down
24 changes: 23 additions & 1 deletion tests/src/python/test_provider_mssql.py
Expand Up @@ -18,7 +18,7 @@
from qgis.core import NULL

from qgis.core import QgsVectorLayer, QgsFeatureRequest, QgsFeature, QgsProviderRegistry
from PyQt4.QtCore import QSettings
from PyQt4.QtCore import QSettings, QDate, QTime, QDateTime, QVariant
from utilities import (unitTestDataPath,
getQgisTestApp,
unittest,
Expand Down Expand Up @@ -53,5 +53,27 @@ def enableCompiler(self):
def disableCompiler(self):
QSettings().setValue(u'/qgis/compileExpressions', False)

# HERE GO THE PROVIDER SPECIFIC TESTS
def testDateTimeTypes(self):
vl = QgsVectorLayer('%s table="qgis_test"."date_times" sql=' % (self.dbconn), "testdatetimes", "mssql")
assert(vl.isValid())

fields = vl.dataProvider().fields()
self.assertEqual(fields.at(fields.indexFromName('date_field')).type(), QVariant.Date)
self.assertEqual(fields.at(fields.indexFromName('time_field')).type(), QVariant.Time)
self.assertEqual(fields.at(fields.indexFromName('datetime_field')).type(), QVariant.DateTime)

f = vl.getFeatures(QgsFeatureRequest()).next()

date_idx = vl.fieldNameIndex('date_field')
assert isinstance(f.attributes()[date_idx], QDate)
self.assertEqual(f.attributes()[date_idx], QDate(2004, 3, 4))
time_idx = vl.fieldNameIndex('time_field')
assert isinstance(f.attributes()[time_idx], QTime)
self.assertEqual(f.attributes()[time_idx], QTime(13, 41, 52))
datetime_idx = vl.fieldNameIndex('datetime_field')
assert isinstance(f.attributes()[datetime_idx], QDateTime)
self.assertEqual(f.attributes()[datetime_idx], QDateTime(QDate(2004, 3, 4), QTime(13, 41, 52)))

if __name__ == '__main__':
unittest.main()
3 changes: 3 additions & 0 deletions tests/src/python/test_provider_postgres.py
Expand Up @@ -72,10 +72,13 @@ def testDateTimeTypes(self):

date_idx = vl.fieldNameIndex('date_field')
assert isinstance(f.attributes()[date_idx], QDate)
self.assertEqual(f.attributes()[date_idx], QDate(2004, 3, 4))
time_idx = vl.fieldNameIndex('time_field')
assert isinstance(f.attributes()[time_idx], QTime)
self.assertEqual(f.attributes()[time_idx], QTime(13, 41, 52))
datetime_idx = vl.fieldNameIndex('datetime_field')
assert isinstance(f.attributes()[datetime_idx], QDateTime)
self.assertEqual(f.attributes()[datetime_idx], QDateTime(QDate(2004, 3, 4), QTime(13, 41, 52)))

def testQueryLayers(self):
def test_query(dbconn, query, key):
Expand Down
3 changes: 3 additions & 0 deletions tests/src/python/test_provider_tabfile.py
Expand Up @@ -47,10 +47,13 @@ def testDateTimeFormats(self):

date_idx = vl.fieldNameIndex('date')
assert isinstance(f.attributes()[date_idx], QDate)
self.assertEqual(f.attributes()[date_idx], QDate(2004, 5, 3))
time_idx = vl.fieldNameIndex('time')
assert isinstance(f.attributes()[time_idx], QTime)
self.assertEqual(f.attributes()[time_idx], QTime(13, 41, 00))
datetime_idx = vl.fieldNameIndex('date_time')
assert isinstance(f.attributes()[datetime_idx], QDateTime)
self.assertEqual(f.attributes()[datetime_idx], QDateTime(QDate(2004, 5, 3), QTime(13, 41, 00)))

if __name__ == '__main__':
unittest.main()
12 changes: 6 additions & 6 deletions tests/src/python/test_qgsvectorfilewriter.py
Expand Up @@ -72,7 +72,7 @@ def testDateTimeWriteShapefile(self):

ft = QgsFeature()
ft.setGeometry(QgsGeometry.fromPoint(QgsPoint(10, 10)))
ft.setAttributes([1, QDate(2014, 0o3, 0o5), QTime(13, 45, 22), QDateTime(QDate(2014, 0o3, 0o5), QTime(13, 45, 22))])
ft.setAttributes([1, QDate(2014, 3, 5), QTime(13, 45, 22), QDateTime(QDate(2014, 3, 5), QTime(13, 45, 22))])
res, features = provider.addFeatures([ft])
assert res
assert len(features) > 0
Expand Down Expand Up @@ -103,15 +103,15 @@ def testDateTimeWriteShapefile(self):

date_idx = created_layer.fieldNameIndex('date_f')
assert isinstance(f.attributes()[date_idx], QDate)
self.assertEqual(f.attributes()[date_idx], QDate(2014, 0o3, 0o5))
self.assertEqual(f.attributes()[date_idx], QDate(2014, 3, 5))
time_idx = created_layer.fieldNameIndex('time_f')
#shapefiles do not support time types
assert isinstance(f.attributes()[time_idx], basestring)
self.assertEqual(f.attributes()[time_idx], '13:45:22')
#shapefiles do not support datetime types
datetime_idx = created_layer.fieldNameIndex('dt_f')
assert isinstance(f.attributes()[datetime_idx], QDate)
self.assertEqual(f.attributes()[datetime_idx], QDate(2014, 0o3, 0o5))
self.assertEqual(f.attributes()[datetime_idx], QDate(2014, 3, 5))

def testDateTimeWriteTabfile(self):
"""Check writing date and time fields to an MapInfo tabfile."""
Expand All @@ -128,7 +128,7 @@ def testDateTimeWriteTabfile(self):

ft = QgsFeature()
ft.setGeometry(QgsGeometry.fromPoint(QgsPoint(10, 10)))
ft.setAttributes([1, QDate(2014, 0o3, 0o5), QTime(13, 45, 22), QDateTime(QDate(2014, 0o3, 0o5), QTime(13, 45, 22))])
ft.setAttributes([1, QDate(2014, 3, 5), QTime(13, 45, 22), QDateTime(QDate(2014, 3, 5), QTime(13, 45, 22))])
res, features = provider.addFeatures([ft])
assert res
assert len(features) > 0
Expand Down Expand Up @@ -157,13 +157,13 @@ def testDateTimeWriteTabfile(self):

date_idx = created_layer.fieldNameIndex('date_f')
assert isinstance(f.attributes()[date_idx], QDate)
self.assertEqual(f.attributes()[date_idx], QDate(2014, 0o3, 0o5))
self.assertEqual(f.attributes()[date_idx], QDate(2014, 3, 5))
time_idx = created_layer.fieldNameIndex('time_f')
assert isinstance(f.attributes()[time_idx], QTime)
self.assertEqual(f.attributes()[time_idx], QTime(13, 45, 22))
datetime_idx = created_layer.fieldNameIndex('dt_f')
assert isinstance(f.attributes()[datetime_idx], QDateTime)
self.assertEqual(f.attributes()[datetime_idx], QDateTime(QDate(2014, 0o3, 0o5), QTime(13, 45, 22)))
self.assertEqual(f.attributes()[datetime_idx], QDateTime(QDate(2014, 3, 5), QTime(13, 45, 22)))

if __name__ == '__main__':
unittest.main()
11 changes: 11 additions & 0 deletions tests/testdata/provider/testdata_mssql.sql
Expand Up @@ -8,10 +8,21 @@ CREATE TABLE qgis_test.[someData] (
geom geometry
)

CREATE TABLE qgis_test.[date_times] (
id integer PRIMARY KEY,
date_field date,
time_field time,
datetime_field datetime
);


INSERT INTO qgis_test.[someData] (pk, cnt, name, name2, geom) VALUES
(5, -200, NULL, 'NuLl', geometry::STGeomFromText( 'Point(-71.123 78.23)', 4326 )),
(3, 300, 'Pear', 'PEaR', NULL),
(1, 100, 'Orange', 'oranGe', geometry::STGeomFromText( 'Point(-70.332 66.33)', 4326 )),
(2, 200, 'Apple', 'Apple', geometry::STGeomFromText( 'Point(-68.2 70.8)', 4326 )),
(4, 400, 'Honey', 'Honey', geometry::STGeomFromText( 'Point(-65.32 78.3)', 4326 ))
;

INSERT INTO qgis_test.[date_times] (id, date_field, time_field, datetime_field ) VALUES
(1, '2004-03-04', '13:41:52', '2004-03-04 13:41:52' );

0 comments on commit 49e4567

Please sign in to comment.