From 05c61502fc78e408a0c0f086d8a9fbcc6d8d9fbe Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 23 Jan 2020 17:58:19 +0100 Subject: [PATCH 1/8] OGR: respect provider default values Fixes #33383 (for OGR, spatialite commit follows) --- src/core/providers/ogr/qgsogrprovider.cpp | 7 ++- tests/src/python/test_provider_ogr.py | 56 +++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/core/providers/ogr/qgsogrprovider.cpp b/src/core/providers/ogr/qgsogrprovider.cpp index 30e28e1258d1..bea467e605f3 100644 --- a/src/core/providers/ogr/qgsogrprovider.cpp +++ b/src/core/providers/ogr/qgsogrprovider.cpp @@ -1584,7 +1584,12 @@ bool QgsOgrProvider::addFeaturePrivate( QgsFeature &f, Flags flags ) OGRFieldType type = OGR_Fld_GetType( fldDef ); QVariant attrVal = attrs.at( qgisAttId ); - if ( attrVal.isNull() || ( type != OFTString && attrVal.toString().isEmpty() ) ) + // The field value is equal to the default (that might be a provider-side expression) + if ( mDefaultValues.contains( qgisAttId ) && attrVal.toString() == mDefaultValues.value( qgisAttId ) ) + { + OGR_F_UnsetField( feature.get(), ogrAttId ); + } + else if ( attrVal.isNull() || ( type != OFTString && attrVal.toString().isEmpty() ) ) { // Starting with GDAL 2.2, there are 2 concepts: unset fields and null fields // whereas previously there was only unset fields. For a GeoJSON output, diff --git a/tests/src/python/test_provider_ogr.py b/tests/src/python/test_provider_ogr.py index e94af6fe7722..5de76a615d13 100644 --- a/tests/src/python/test_provider_ogr.py +++ b/tests/src/python/test_provider_ogr.py @@ -15,6 +15,7 @@ import sys import tempfile import hashlib +from datetime import datetime from osgeo import gdal, ogr # NOQA from qgis.PyQt.QtCore import QVariant, QByteArray @@ -27,6 +28,7 @@ from qgis.testing import start_app, unittest from utilities import unitTestDataPath +from qgis.utils import spatialite_connect start_app() TEST_DATA_DIR = unitTestDataPath() @@ -609,6 +611,60 @@ def testReloadDataAndFeatureCount(self): self.assertEqual(vl.featureCount(), 1) gdal.Unlink(filename) + def testSpatialiteDefaultValues(self): + """Test wether in spatialite table with default values like CURRENT_TIMESTAMP or + (datetime('now','localtime')) they are respected. See GH #33383""" + + # Create the test table + + dbname = os.path.join(tempfile.gettempdir(), "test.sqlite") + if os.path.exists(dbname): + os.remove(dbname) + con = spatialite_connect(dbname, isolation_level=None) + cur = con.cursor() + cur.execute("BEGIN") + sql = "SELECT InitSpatialMetadata()" + cur.execute(sql) + + # simple table with primary key + sql = """ + CREATE TABLE test_table ( + id integer primary key autoincrement, + comment text, + created_at_01 text DEFAULT (datetime('now','localtime')), + created_at_02 text DEFAULT CURRENT_TIMESTAMP, + anumber INTEGER DEFAULT 123 + ) + """ + cur.execute(sql) + cur.execute("COMMIT") + con.close() + + vl = QgsVectorLayer(dbname + '|layername=test_table', 'test_table', 'ogr') + self.assertTrue(vl.isValid()) + feature = QgsFeature(vl.fields()) + for idx in range(vl.fields().count()): + default = vl.dataProvider().defaultValueClause(idx) + if default != '': + feature.setAttribute(idx, default) + else: + feature.setAttribute(idx, 'A comment') + + # Save it for the test + now = datetime.now() + + self.assertTrue(vl.dataProvider().addFeature(feature)) + del(vl) + + # Verify + vl2 = QgsVectorLayer(dbname + '|layername=test_table', 'test_table', 'ogr') + self.assertTrue(vl2.isValid()) + feature = next(vl2.getFeatures()) + self.assertTrue(feature.attribute(2).startswith(now.strftime('%Y-%m-%d'))) + self.assertTrue(feature.attribute(3).startswith(now.strftime('%Y-%m-%d'))) + self.assertEqual(feature.attribute(4), 123) + self.assertEqual(feature.attribute(1), 'A comment') + if __name__ == '__main__': unittest.main() From 13681cc9b5b1302e643c7fcdea96ff730fe0975f Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 23 Jan 2020 17:59:26 +0100 Subject: [PATCH 2/8] Spatialite: respect provider default values Fixes #33383 --- .../spatialite/qgsspatialiteprovider.cpp | 74 ++++++++++++------- .../spatialite/qgsspatialiteprovider.h | 3 + tests/src/python/test_provider_spatialite.py | 54 ++++++++++++++ 3 files changed, 106 insertions(+), 25 deletions(-) diff --git a/src/providers/spatialite/qgsspatialiteprovider.cpp b/src/providers/spatialite/qgsspatialiteprovider.cpp index 4ba2c140bb34..32bcc268806c 100644 --- a/src/providers/spatialite/qgsspatialiteprovider.cpp +++ b/src/providers/spatialite/qgsspatialiteprovider.cpp @@ -983,6 +983,11 @@ void QgsSpatiaLiteProvider::insertDefaultValue( int fieldIndex, QString defaultV } } +QString QgsSpatiaLiteProvider::defaultValueClause( int fieldIndex ) const +{ + return mDefaultValues.value( fieldIndex, QString() ).toString(); +} + void QgsSpatiaLiteProvider::handleError( const QString &sql, char *errorMessage, bool rollback ) { QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errorMessage ? errorMessage : tr( "unknown cause" ) ), tr( "SpatiaLite" ) ); @@ -3957,10 +3962,11 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags ) sqlite3_stmt *stmt = nullptr; char *errMsg = nullptr; bool toCommit = false; - QString sql; QString values; QString separator; int ia, ret; + // SQL for single row + QString sql; if ( flist.isEmpty() ) return true; @@ -3971,46 +3977,57 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags ) { toCommit = true; - sql = QStringLiteral( "INSERT INTO %1(" ).arg( QgsSqliteUtils::quotedIdentifier( mTableName ) ); + QString baseSql { QStringLiteral( "INSERT INTO %1(" ).arg( QgsSqliteUtils::quotedIdentifier( mTableName ) ) }; values = QStringLiteral( ") VALUES (" ); separator.clear(); if ( !mGeometryColumn.isEmpty() ) { - sql += separator + QgsSqliteUtils::quotedIdentifier( mGeometryColumn ); + baseSql += separator + QgsSqliteUtils::quotedIdentifier( mGeometryColumn ); values += separator + geomParam(); separator = ','; } - for ( int i = 0; i < attributevec.count(); ++i ) + for ( QgsFeatureList::iterator feature = flist.begin(); feature != flist.end(); ++feature ) { - if ( i >= mAttributeFields.count() ) - continue; - QString fieldname = mAttributeFields.at( i ).name(); - if ( fieldname.isEmpty() || fieldname == mGeometryColumn ) - continue; + sql = baseSql; - sql += separator + QgsSqliteUtils::quotedIdentifier( fieldname ); - values += separator + '?'; - separator = ','; - } + // looping on each feature to insert + QgsAttributes attributevec = feature->attributes(); - sql += values; - sql += ')'; + // Default indexes (to be skipped) + QList defaultIndexes; - // SQLite prepared statement - ret = sqlite3_prepare_v2( mSqliteHandle, sql.toUtf8().constData(), -1, &stmt, nullptr ); - if ( ret == SQLITE_OK ) - { - for ( QgsFeatureList::iterator feature = flist.begin(); feature != flist.end(); ++feature ) + for ( int i = 0; i < attributevec.count(); ++i ) { - // looping on each feature to insert - QgsAttributes attributevec = feature->attributes(); + if ( mDefaultValues.contains( i ) && defaultValue( i ) == attributevec.at( i ).toString() ) + { + defaultIndexes.push_back( i ); + continue; + } - // resetting Prepared Statement and bindings - sqlite3_reset( stmt ); - sqlite3_clear_bindings( stmt ); + if ( i >= mAttributeFields.count() ) + continue; + + QString fieldname = mAttributeFields.at( i ).name(); + if ( fieldname.isEmpty() || fieldname == mGeometryColumn ) + { + continue; + } + + sql += separator + QgsSqliteUtils::quotedIdentifier( fieldname ); + values += separator + '?'; + separator = ','; + } + + sql += values; + sql += ')'; + + // SQLite prepared statement + ret = sqlite3_prepare_v2( mSqliteHandle, sql.toUtf8().constData(), -1, &stmt, nullptr ); + if ( ret == SQLITE_OK ) + { // initializing the column counter ia = 0; @@ -4039,6 +4056,11 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags ) for ( int i = 0; i < attributevec.count(); ++i ) { + if ( defaultIndexes.contains( i ) ) + { + continue; + } + QVariant v = attributevec.at( i ); // binding values for each attribute @@ -4103,6 +4125,8 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags ) // performing actual row insert ret = sqlite3_step( stmt ); + qDebug() << sqlite3_expanded_sql( stmt ); + if ( ret == SQLITE_DONE || ret == SQLITE_ROW ) { // update feature id diff --git a/src/providers/spatialite/qgsspatialiteprovider.h b/src/providers/spatialite/qgsspatialiteprovider.h index ede7ae64212a..05f8e0e2b524 100644 --- a/src/providers/spatialite/qgsspatialiteprovider.h +++ b/src/providers/spatialite/qgsspatialiteprovider.h @@ -382,6 +382,9 @@ class QgsSpatiaLiteProvider: public QgsVectorDataProvider friend class QgsSpatiaLiteFeatureSource; + // QgsVectorDataProvider interface + public: + virtual QString defaultValueClause( int fieldIndex ) const override; }; class QgsSpatiaLiteProviderMetadata: public QgsProviderMetadata diff --git a/tests/src/python/test_provider_spatialite.py b/tests/src/python/test_provider_spatialite.py index a41484af24a9..6c1d918f0a48 100644 --- a/tests/src/python/test_provider_spatialite.py +++ b/tests/src/python/test_provider_spatialite.py @@ -17,6 +17,7 @@ import sys import shutil import tempfile +from datetime import datetime from qgis.core import (QgsProviderRegistry, QgsVectorLayer, @@ -1159,6 +1160,59 @@ def testBigint(self): self.assertEqual(l.uniqueValues(1), {1, 2}) self.assertEqual(l.uniqueValues(0), {987654321012345, 987654321012346}) + def testSpatialiteDefaultValues(self): + """Test wether in spatialite table with default values like CURRENT_TIMESTAMP or + (datetime('now','localtime')) they are respected. See GH #33383""" + + # Create the test table + + dbname = os.path.join(tempfile.gettempdir(), "test.sqlite") + if os.path.exists(dbname): + os.remove(dbname) + con = spatialite_connect(dbname, isolation_level=None) + cur = con.cursor() + cur.execute("BEGIN") + sql = "SELECT InitSpatialMetadata()" + cur.execute(sql) + + # simple table with primary key + sql = """ + CREATE TABLE test_table ( + id integer primary key autoincrement, + comment text, + created_at_01 text DEFAULT (datetime('now','localtime')), + created_at_02 text DEFAULT CURRENT_TIMESTAMP, + anumber INTEGER DEFAULT 123 + ) + """ + cur.execute(sql) + cur.execute("COMMIT") + con.close() + + vl = QgsVectorLayer("dbname='%s' table='test_table'" % dbname, 'test_table', 'spatialite') + self.assertTrue(vl.isValid()) + feature = QgsFeature(vl.fields()) + for idx in range(vl.fields().count()): + default = vl.dataProvider().defaultValueClause(idx) + if default != '': + feature.setAttribute(idx, default) + else: + feature.setAttribute(idx, 'A comment') + + # Save it for the test + now = datetime.now() + + self.assertTrue(vl.dataProvider().addFeature(feature)) + del(vl) + + # Verify + vl2 = QgsVectorLayer("dbname='%s' table='test_table'" % dbname, 'test_table', 'spatialite') + self.assertTrue(vl2.isValid()) + feature = next(vl2.getFeatures()) + self.assertTrue(feature.attribute(2).startswith(now.strftime('%Y-%m-%d'))) + self.assertTrue(feature.attribute(3).startswith(now.strftime('%Y-%m-%d'))) + self.assertEqual(feature.attribute(4), 123) + if __name__ == '__main__': unittest.main() From f259345c8e79492131792f399a5a2da80a2eacd0 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 23 Jan 2020 21:02:19 +0100 Subject: [PATCH 3/8] Fix ogr and spatialite default values --- src/core/providers/ogr/qgsogrprovider.cpp | 14 ++-- .../spatialite/qgsspatialiteprovider.cpp | 66 ++++++++++++++----- .../spatialite/qgsspatialiteprovider.h | 5 +- tests/src/python/test_provider_ogr.py | 12 ++-- tests/src/python/test_provider_spatialite.py | 11 ++-- 5 files changed, 73 insertions(+), 35 deletions(-) diff --git a/src/core/providers/ogr/qgsogrprovider.cpp b/src/core/providers/ogr/qgsogrprovider.cpp index bea467e605f3..ddfe7b0b395f 100644 --- a/src/core/providers/ogr/qgsogrprovider.cpp +++ b/src/core/providers/ogr/qgsogrprovider.cpp @@ -1209,13 +1209,18 @@ void QgsOgrProvider::loadFields() QString defaultValue = textEncoding()->toUnicode( OGR_Fld_GetDefault( fldDef ) ); if ( !defaultValue.isEmpty() && !OGR_Fld_IsDefaultDriverSpecific( fldDef ) ) { + if ( defaultValue.startsWith( '\'' ) ) + { + defaultValue = defaultValue.remove( 0, 1 ); + defaultValue.chop( 1 ); + defaultValue.replace( QLatin1String( "''" ), QLatin1String( "'" ) ); + } mDefaultValues.insert( createdFields, defaultValue ); } mAttributeFields.append( newField ); createdFields++; } - } @@ -1389,13 +1394,6 @@ QVariant QgsOgrProvider::defaultValue( int fieldId ) const 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( QLatin1String( "''" ), QLatin1String( "'" ) ); - resultVar = defaultVal; - } ( void )mAttributeFields.at( fieldId ).convertCompatible( resultVar ); return resultVar; diff --git a/src/providers/spatialite/qgsspatialiteprovider.cpp b/src/providers/spatialite/qgsspatialiteprovider.cpp index 32bcc268806c..4616d3b3e694 100644 --- a/src/providers/spatialite/qgsspatialiteprovider.cpp +++ b/src/providers/spatialite/qgsspatialiteprovider.cpp @@ -956,19 +956,22 @@ void QgsSpatiaLiteProvider::insertDefaultValue( int fieldIndex, QString defaultV if ( mAttributeFields.at( fieldIndex ).name() != mPrimaryKey || ( mAttributeFields.at( fieldIndex ).name() == mPrimaryKey && !mPrimaryKeyAutoIncrement ) ) { + bool ok; switch ( mAttributeFields.at( fieldIndex ).type() ) { case QVariant::LongLong: - defaultVariant = defaultVal.toLongLong(); + defaultVariant = defaultVal.toLongLong( &ok ); break; case QVariant::Double: - defaultVariant = defaultVal.toDouble(); + defaultVariant = defaultVal.toDouble( &ok ); break; default: { - if ( defaultVal.startsWith( '\'' ) ) + // Literal string? + ok = defaultVal.startsWith( '\'' ); + if ( ok ) defaultVal = defaultVal.remove( 0, 1 ); if ( defaultVal.endsWith( '\'' ) ) defaultVal.chop( 1 ); @@ -978,14 +981,20 @@ void QgsSpatiaLiteProvider::insertDefaultValue( int fieldIndex, QString defaultV break; } } + + if ( ! ok ) // Must be a SQL clause + { + mDefaultValueClause.insert( fieldIndex, defaultVal ); + } + } - mDefaultValues.insert( fieldIndex, defaultVariant ); + mDefaultValues.insert( fieldIndex, defaultVal ); } } QString QgsSpatiaLiteProvider::defaultValueClause( int fieldIndex ) const { - return mDefaultValues.value( fieldIndex, QString() ).toString(); + return mDefaultValueClause.value( fieldIndex, QString() ); } void QgsSpatiaLiteProvider::handleError( const QString &sql, char *errorMessage, bool rollback ) @@ -3962,7 +3971,7 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags ) sqlite3_stmt *stmt = nullptr; char *errMsg = nullptr; bool toCommit = false; - QString values; + QString baseValues; QString separator; int ia, ret; // SQL for single row @@ -3978,19 +3987,20 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags ) toCommit = true; QString baseSql { QStringLiteral( "INSERT INTO %1(" ).arg( QgsSqliteUtils::quotedIdentifier( mTableName ) ) }; - values = QStringLiteral( ") VALUES (" ); + baseValues = QStringLiteral( ") VALUES (" ); separator.clear(); if ( !mGeometryColumn.isEmpty() ) { baseSql += separator + QgsSqliteUtils::quotedIdentifier( mGeometryColumn ); - values += separator + geomParam(); + baseValues += separator + geomParam(); separator = ','; } for ( QgsFeatureList::iterator feature = flist.begin(); feature != flist.end(); ++feature ) { + QString values { baseValues }; sql = baseSql; // looping on each feature to insert @@ -4001,7 +4011,7 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags ) for ( int i = 0; i < attributevec.count(); ++i ) { - if ( mDefaultValues.contains( i ) && defaultValue( i ) == attributevec.at( i ).toString() ) + if ( mDefaultValues.contains( i ) && mDefaultValues.value( i ) == attributevec.at( i ).toString() ) { defaultIndexes.push_back( i ); continue; @@ -4125,7 +4135,7 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags ) // performing actual row insert ret = sqlite3_step( stmt ); - qDebug() << sqlite3_expanded_sql( stmt ); + sqlite3_finalize( stmt ); if ( ret == SQLITE_DONE || ret == SQLITE_ROW ) { @@ -4145,14 +4155,13 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags ) break; } } + } // prepared statement - sqlite3_finalize( stmt ); + if ( ret == SQLITE_DONE || ret == SQLITE_ROW ) + { + ret = sqlite3_exec( mSqliteHandle, "COMMIT", nullptr, nullptr, &errMsg ); + } - if ( ret == SQLITE_DONE || ret == SQLITE_ROW ) - { - ret = sqlite3_exec( mSqliteHandle, "COMMIT", nullptr, nullptr, &errMsg ); - } - } // prepared statement } // BEGIN statement if ( ret != SQLITE_OK ) @@ -4552,7 +4561,30 @@ QgsVectorDataProvider::Capabilities QgsSpatiaLiteProvider::capabilities() const QVariant QgsSpatiaLiteProvider::defaultValue( int fieldId ) const { - return mDefaultValues.value( fieldId, QVariant() ); + 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( QLatin1String( "''" ), QLatin1String( "'" ) ); + resultVar = defaultVal; + } + + ( void )mAttributeFields.at( fieldId ).convertCompatible( resultVar ); + return resultVar; } bool QgsSpatiaLiteProvider::skipConstraintCheck( int fieldIndex, QgsFieldConstraints::Constraint constraint, const QVariant &value ) const diff --git a/src/providers/spatialite/qgsspatialiteprovider.h b/src/providers/spatialite/qgsspatialiteprovider.h index 05f8e0e2b524..1640d9fb3b9c 100644 --- a/src/providers/spatialite/qgsspatialiteprovider.h +++ b/src/providers/spatialite/qgsspatialiteprovider.h @@ -221,6 +221,9 @@ class QgsSpatiaLiteProvider: public QgsVectorDataProvider QgsFields mAttributeFields; + //! Map of field index to default value SQL fragments + QMap mDefaultValueClause; + //! Flag indicating if the layer data source is a valid SpatiaLite layer bool mValid = false; @@ -264,7 +267,7 @@ class QgsSpatiaLiteProvider: public QgsVectorDataProvider QString mGeometryColumn; //! Map of field index to default value - QMap mDefaultValues; + QMap mDefaultValues; //! Name of the SpatialIndex table QString mIndexTable; diff --git a/tests/src/python/test_provider_ogr.py b/tests/src/python/test_provider_ogr.py index 5de76a615d13..0931eefc1ae8 100644 --- a/tests/src/python/test_provider_ogr.py +++ b/tests/src/python/test_provider_ogr.py @@ -630,10 +630,11 @@ def testSpatialiteDefaultValues(self): sql = """ CREATE TABLE test_table ( id integer primary key autoincrement, - comment text, + comment TEXT, created_at_01 text DEFAULT (datetime('now','localtime')), created_at_02 text DEFAULT CURRENT_TIMESTAMP, - anumber INTEGER DEFAULT 123 + anumber INTEGER DEFAULT 123, + atext TEXT default 'My default' ) """ cur.execute(sql) @@ -644,8 +645,8 @@ def testSpatialiteDefaultValues(self): self.assertTrue(vl.isValid()) feature = QgsFeature(vl.fields()) for idx in range(vl.fields().count()): - default = vl.dataProvider().defaultValueClause(idx) - if default != '': + default = vl.dataProvider().defaultValue(idx) + if default is not None: feature.setAttribute(idx, default) else: feature.setAttribute(idx, 'A comment') @@ -660,10 +661,11 @@ def testSpatialiteDefaultValues(self): vl2 = QgsVectorLayer(dbname + '|layername=test_table', 'test_table', 'ogr') self.assertTrue(vl2.isValid()) feature = next(vl2.getFeatures()) + self.assertEqual(feature.attribute(1), 'A comment') self.assertTrue(feature.attribute(2).startswith(now.strftime('%Y-%m-%d'))) self.assertTrue(feature.attribute(3).startswith(now.strftime('%Y-%m-%d'))) self.assertEqual(feature.attribute(4), 123) - self.assertEqual(feature.attribute(1), 'A comment') + self.assertEqual(feature.attribute(5), 'My default') if __name__ == '__main__': diff --git a/tests/src/python/test_provider_spatialite.py b/tests/src/python/test_provider_spatialite.py index 6c1d918f0a48..13ff13a03ff5 100644 --- a/tests/src/python/test_provider_spatialite.py +++ b/tests/src/python/test_provider_spatialite.py @@ -1166,7 +1166,7 @@ def testSpatialiteDefaultValues(self): # Create the test table - dbname = os.path.join(tempfile.gettempdir(), "test.sqlite") + dbname = os.path.join(tempfile.gettempdir(), "test_default_values.sqlite") if os.path.exists(dbname): os.remove(dbname) con = spatialite_connect(dbname, isolation_level=None) @@ -1182,7 +1182,8 @@ def testSpatialiteDefaultValues(self): comment text, created_at_01 text DEFAULT (datetime('now','localtime')), created_at_02 text DEFAULT CURRENT_TIMESTAMP, - anumber INTEGER DEFAULT 123 + anumber INTEGER DEFAULT 123, + atext TEXT default 'My default' ) """ cur.execute(sql) @@ -1193,8 +1194,8 @@ def testSpatialiteDefaultValues(self): self.assertTrue(vl.isValid()) feature = QgsFeature(vl.fields()) for idx in range(vl.fields().count()): - default = vl.dataProvider().defaultValueClause(idx) - if default != '': + default = vl.dataProvider().defaultValue(idx) + if default is not None: feature.setAttribute(idx, default) else: feature.setAttribute(idx, 'A comment') @@ -1209,9 +1210,11 @@ def testSpatialiteDefaultValues(self): vl2 = QgsVectorLayer("dbname='%s' table='test_table'" % dbname, 'test_table', 'spatialite') self.assertTrue(vl2.isValid()) feature = next(vl2.getFeatures()) + self.assertEqual(feature.attribute(1), 'A comment') self.assertTrue(feature.attribute(2).startswith(now.strftime('%Y-%m-%d'))) self.assertTrue(feature.attribute(3).startswith(now.strftime('%Y-%m-%d'))) self.assertEqual(feature.attribute(4), 123) + self.assertEqual(feature.attribute(5), 'My default') if __name__ == '__main__': From ebf7d247d8efa3d387447a68b9dd4a494d887b57 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Fri, 24 Jan 2020 11:58:19 +0100 Subject: [PATCH 4/8] Provider default values: more tests and homogenize Fixes #33383 Homogenization is not complete but at least there are test cases for the future. --- src/core/providers/ogr/qgsogrprovider.cpp | 2 +- .../spatialite/qgsspatialiteprovider.cpp | 60 ++++++++++--------- tests/src/python/test_provider_ogr.py | 38 +++++++++--- tests/src/python/test_provider_postgres.py | 56 +++++++++++++++++ tests/src/python/test_provider_spatialite.py | 37 +++++++++--- tests/testdata/provider/testdata_pg.sql | 15 +++++ 6 files changed, 160 insertions(+), 48 deletions(-) diff --git a/src/core/providers/ogr/qgsogrprovider.cpp b/src/core/providers/ogr/qgsogrprovider.cpp index ddfe7b0b395f..9c8977ad91b3 100644 --- a/src/core/providers/ogr/qgsogrprovider.cpp +++ b/src/core/providers/ogr/qgsogrprovider.cpp @@ -1385,7 +1385,7 @@ QVariant QgsOgrProvider::defaultValue( int fieldId ) const QString defaultVal = mDefaultValues.value( fieldId, QString() ); if ( defaultVal.isEmpty() ) - return QVariant(); + return defaultVal; QVariant resultVar = defaultVal; if ( defaultVal == QStringLiteral( "CURRENT_TIMESTAMP" ) ) diff --git a/src/providers/spatialite/qgsspatialiteprovider.cpp b/src/providers/spatialite/qgsspatialiteprovider.cpp index 4616d3b3e694..1c4e44de30fc 100644 --- a/src/providers/spatialite/qgsspatialiteprovider.cpp +++ b/src/providers/spatialite/qgsspatialiteprovider.cpp @@ -982,7 +982,7 @@ void QgsSpatiaLiteProvider::insertDefaultValue( int fieldIndex, QString defaultV } } - if ( ! ok ) // Must be a SQL clause + if ( ! ok ) // Must be a SQL clause and not a literal { mDefaultValueClause.insert( fieldIndex, defaultVal ); } @@ -992,6 +992,36 @@ void QgsSpatiaLiteProvider::insertDefaultValue( int fieldIndex, QString defaultV } } + +QVariant QgsSpatiaLiteProvider::defaultValue( int fieldId ) const +{ + // TODO: backend-side evaluation + 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( QLatin1String( "''" ), QLatin1String( "'" ) ); + resultVar = defaultVal; + } + + ( void )mAttributeFields.at( fieldId ).convertCompatible( resultVar ); + return resultVar; +} + QString QgsSpatiaLiteProvider::defaultValueClause( int fieldIndex ) const { return mDefaultValueClause.value( fieldIndex, QString() ); @@ -4559,34 +4589,6 @@ QgsVectorDataProvider::Capabilities QgsSpatiaLiteProvider::capabilities() const return mEnabledCapabilities; } -QVariant QgsSpatiaLiteProvider::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( QLatin1String( "''" ), QLatin1String( "'" ) ); - resultVar = defaultVal; - } - - ( void )mAttributeFields.at( fieldId ).convertCompatible( resultVar ); - return resultVar; -} - bool QgsSpatiaLiteProvider::skipConstraintCheck( int fieldIndex, QgsFieldConstraints::Constraint constraint, const QVariant &value ) const { Q_UNUSED( constraint ) diff --git a/tests/src/python/test_provider_ogr.py b/tests/src/python/test_provider_ogr.py index 0931eefc1ae8..94f9bd622155 100644 --- a/tests/src/python/test_provider_ogr.py +++ b/tests/src/python/test_provider_ogr.py @@ -628,7 +628,7 @@ def testSpatialiteDefaultValues(self): # simple table with primary key sql = """ - CREATE TABLE test_table ( + CREATE TABLE test_table_default_values ( id integer primary key autoincrement, comment TEXT, created_at_01 text DEFAULT (datetime('now','localtime')), @@ -641,24 +641,44 @@ def testSpatialiteDefaultValues(self): cur.execute("COMMIT") con.close() - vl = QgsVectorLayer(dbname + '|layername=test_table', 'test_table', 'ogr') + vl = QgsVectorLayer(dbname + '|layername=test_table_default_values', 'test_table_default_values', 'ogr') self.assertTrue(vl.isValid()) + + # Save it for the test + now = datetime.now() + + # Test default values + dp = vl.dataProvider() + #FIXME: should it be None? + self.assertTrue(dp.defaultValue(0).isNull()) + self.assertTrue(dp.defaultValue(1).isNull()) + #FIXME: This fails because there is no backend-side evaluation in this provider + #self.assertTrue(dp.defaultValue(2).startswith(now.strftime('%Y-%m-%d'))) + self.assertTrue(dp.defaultValue(3).startswith(now.strftime('%Y-%m-%d'))) + self.assertEqual(dp.defaultValue(4), 123) + self.assertEqual(dp.defaultValue(5), 'My default') + + self.assertEqual(dp.defaultValueClause(0), 'Autogenerate') + self.assertEqual(dp.defaultValueClause(1), '') + self.assertEqual(dp.defaultValueClause(2), "datetime('now','localtime')") + self.assertEqual(dp.defaultValueClause(3), "CURRENT_TIMESTAMP") + #FIXME: ogr provider simply returns values when asked for clauses + #self.assertEqual(dp.defaultValueClause(4), '') + #self.assertEqual(dp.defaultValueClause(5), '') + feature = QgsFeature(vl.fields()) for idx in range(vl.fields().count()): default = vl.dataProvider().defaultValue(idx) - if default is not None: - feature.setAttribute(idx, default) - else: + if not default: feature.setAttribute(idx, 'A comment') - - # Save it for the test - now = datetime.now() + else: + feature.setAttribute(idx, default) self.assertTrue(vl.dataProvider().addFeature(feature)) del(vl) # Verify - vl2 = QgsVectorLayer(dbname + '|layername=test_table', 'test_table', 'ogr') + vl2 = QgsVectorLayer(dbname + '|layername=test_table_default_values', 'test_table_default_values', 'ogr') self.assertTrue(vl2.isValid()) feature = next(vl2.getFeatures()) self.assertEqual(feature.attribute(1), 'A comment') diff --git a/tests/src/python/test_provider_postgres.py b/tests/src/python/test_provider_postgres.py index 3ea154e0fb64..241ff807b6ff 100644 --- a/tests/src/python/test_provider_postgres.py +++ b/tests/src/python/test_provider_postgres.py @@ -24,6 +24,7 @@ import os import time +from datetime import datetime from qgis.core import ( QgsVectorLayer, @@ -1438,6 +1439,61 @@ def testIdentityPk(self): self.assertTrue(vl.isValid()) feature = next(vl.getFeatures()) self.assertIsNotNone(feature.id()) + + def testDefaultValuesAndClauses(self): + """Test wether default values like CURRENT_TIMESTAMP or + now() they are respected. See GH #33383""" + + # Create the test table + + vl = QgsVectorLayer(self.dbconn + ' sslmode=disable table="public"."test_table_default_values" sql=', 'test', 'postgres') + self.assertTrue(vl.isValid()) + + # Save it for the test + now = datetime.now() + + # Test default values + dp = vl.dataProvider() + dp.setProviderProperty(QgsDataProvider.EvaluateDefaultValues, 1) + self.assertIsNotNone(dp.defaultValue(0)) + self.assertIsNone(dp.defaultValue(1)) + self.assertTrue(dp.defaultValue(2).startswith(now.strftime('%Y-%m-%d'))) + self.assertTrue(dp.defaultValue(3).startswith(now.strftime('%Y-%m-%d'))) + self.assertEqual(dp.defaultValue(4), 123) + self.assertEqual(dp.defaultValue(5), 'My default') + + #FIXME: the provider should return the clause definition + # regardless of the EvaluateDefaultValues setting + dp.setProviderProperty(QgsDataProvider.EvaluateDefaultValues, 0) + self.assertEqual(dp.defaultValueClause(0), "nextval('test_table_default_values_id_seq'::regclass)") + self.assertEqual(dp.defaultValueClause(1), '') + self.assertEqual(dp.defaultValueClause(2), "now()") + self.assertEqual(dp.defaultValueClause(3), "CURRENT_TIMESTAMP") + self.assertEqual(dp.defaultValueClause(4), '123') + self.assertEqual(dp.defaultValueClause(5), "'My default'::text") + #FIXME: the test fails if the value is not reset to 1 + dp.setProviderProperty(QgsDataProvider.EvaluateDefaultValues, 1) + + feature = QgsFeature(vl.fields()) + for idx in range(vl.fields().count()): + default = vl.dataProvider().defaultValue(idx) + if default is not None: + feature.setAttribute(idx, default) + else: + feature.setAttribute(idx, 'A comment') + + self.assertTrue(vl.dataProvider().addFeature(feature)) + del(vl) + + # Verify + vl2 = QgsVectorLayer(self.dbconn + ' sslmode=disable table="public"."test_table_default_values" sql=', 'test', 'postgres') + self.assertTrue(vl2.isValid()) + feature = next(vl2.getFeatures()) + self.assertEqual(feature.attribute(1), 'A comment') + self.assertTrue(feature.attribute(2).startswith(now.strftime('%Y-%m-%d'))) + self.assertTrue(feature.attribute(3).startswith(now.strftime('%Y-%m-%d'))) + self.assertEqual(feature.attribute(4), 123) + self.assertEqual(feature.attribute(5), 'My default') class TestPyQgsPostgresProviderCompoundKey(unittest.TestCase, ProviderTestCase): diff --git a/tests/src/python/test_provider_spatialite.py b/tests/src/python/test_provider_spatialite.py index 13ff13a03ff5..f54dbe1bc0ff 100644 --- a/tests/src/python/test_provider_spatialite.py +++ b/tests/src/python/test_provider_spatialite.py @@ -1177,7 +1177,7 @@ def testSpatialiteDefaultValues(self): # simple table with primary key sql = """ - CREATE TABLE test_table ( + CREATE TABLE test_table_default_values ( id integer primary key autoincrement, comment text, created_at_01 text DEFAULT (datetime('now','localtime')), @@ -1190,24 +1190,43 @@ def testSpatialiteDefaultValues(self): cur.execute("COMMIT") con.close() - vl = QgsVectorLayer("dbname='%s' table='test_table'" % dbname, 'test_table', 'spatialite') + vl = QgsVectorLayer("dbname='%s' table='test_table_default_values'" % dbname, 'test_table_default_values', 'spatialite') self.assertTrue(vl.isValid()) + + # Save it for the test + now = datetime.now() + + # Test default values + dp = vl.dataProvider() + # FIXME: should it be None? + self.assertTrue(dp.defaultValue(0).isNull()) + self.assertIsNone(dp.defaultValue(1)) + # FIXME: This fails because there is no backend-side evaluation in this provider + #self.assertTrue(dp.defaultValue(2).startswith(now.strftime('%Y-%m-%d'))) + self.assertTrue(dp.defaultValue(3).startswith(now.strftime('%Y-%m-%d'))) + self.assertEqual(dp.defaultValue(4), 123) + self.assertEqual(dp.defaultValue(5), 'My default') + + self.assertEqual(dp.defaultValueClause(0), '') + self.assertEqual(dp.defaultValueClause(1), '') + self.assertEqual(dp.defaultValueClause(2), "datetime('now','localtime')") + self.assertEqual(dp.defaultValueClause(3), "CURRENT_TIMESTAMP") + self.assertEqual(dp.defaultValueClause(4), '') + self.assertEqual(dp.defaultValueClause(5), '') + feature = QgsFeature(vl.fields()) for idx in range(vl.fields().count()): default = vl.dataProvider().defaultValue(idx) - if default is not None: - feature.setAttribute(idx, default) - else: + if not default: feature.setAttribute(idx, 'A comment') - - # Save it for the test - now = datetime.now() + else: + feature.setAttribute(idx, default) self.assertTrue(vl.dataProvider().addFeature(feature)) del(vl) # Verify - vl2 = QgsVectorLayer("dbname='%s' table='test_table'" % dbname, 'test_table', 'spatialite') + vl2 = QgsVectorLayer("dbname='%s' table='test_table_default_values'" % dbname, 'test_table_default_values', 'spatialite') self.assertTrue(vl2.isValid()) feature = next(vl2.getFeatures()) self.assertEqual(feature.attribute(1), 'A comment') diff --git a/tests/testdata/provider/testdata_pg.sql b/tests/testdata/provider/testdata_pg.sql index 96c3efe023ae..f71d56e7df58 100644 --- a/tests/testdata/provider/testdata_pg.sql +++ b/tests/testdata/provider/testdata_pg.sql @@ -686,3 +686,18 @@ CREATE TABLE qgis_test.b29560 ( INSERT INTO qgis_test.b29560 (geom) VALUES ('POLYGON EMPTY'::geometry); + +--------------------------------------------- +-- +-- Aspatial table with default values +-- + +CREATE TABLE test_table_default_values ( + id SERIAL primary key, + comment TEXT, + created_at_01 text DEFAULT now(), + created_at_02 text DEFAULT CURRENT_TIMESTAMP, + anumber INTEGER DEFAULT 123, + atext TEXT default 'My default' +) + From f8e255e45d85a5458dd0996e1ac14686b290e40c Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Fri, 24 Jan 2020 18:07:46 +0100 Subject: [PATCH 5/8] Spelling --- tests/src/python/test_provider_ogr.py | 2 +- tests/src/python/test_provider_postgres.py | 2 +- tests/src/python/test_provider_spatialite.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/src/python/test_provider_ogr.py b/tests/src/python/test_provider_ogr.py index 94f9bd622155..f0958a674c91 100644 --- a/tests/src/python/test_provider_ogr.py +++ b/tests/src/python/test_provider_ogr.py @@ -612,7 +612,7 @@ def testReloadDataAndFeatureCount(self): gdal.Unlink(filename) def testSpatialiteDefaultValues(self): - """Test wether in spatialite table with default values like CURRENT_TIMESTAMP or + """Test whether in spatialite table with default values like CURRENT_TIMESTAMP or (datetime('now','localtime')) they are respected. See GH #33383""" # Create the test table diff --git a/tests/src/python/test_provider_postgres.py b/tests/src/python/test_provider_postgres.py index 241ff807b6ff..68bbcffd45e3 100644 --- a/tests/src/python/test_provider_postgres.py +++ b/tests/src/python/test_provider_postgres.py @@ -1441,7 +1441,7 @@ def testIdentityPk(self): self.assertIsNotNone(feature.id()) def testDefaultValuesAndClauses(self): - """Test wether default values like CURRENT_TIMESTAMP or + """Test whether default values like CURRENT_TIMESTAMP or now() they are respected. See GH #33383""" # Create the test table diff --git a/tests/src/python/test_provider_spatialite.py b/tests/src/python/test_provider_spatialite.py index f54dbe1bc0ff..badf5fcf4c0c 100644 --- a/tests/src/python/test_provider_spatialite.py +++ b/tests/src/python/test_provider_spatialite.py @@ -1161,7 +1161,7 @@ def testBigint(self): self.assertEqual(l.uniqueValues(0), {987654321012345, 987654321012346}) def testSpatialiteDefaultValues(self): - """Test wether in spatialite table with default values like CURRENT_TIMESTAMP or + """Test whether in spatialite table with default values like CURRENT_TIMESTAMP or (datetime('now','localtime')) they are respected. See GH #33383""" # Create the test table From 439a3aa10469c0f817870bc0fa6904eb6d73bdd2 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 28 Jan 2020 09:09:05 +0100 Subject: [PATCH 6/8] Code layout and revert default value for OGR --- src/core/providers/ogr/qgsogrprovider.cpp | 2 +- tests/src/python/test_provider_ogr.py | 2 +- tests/src/python/test_provider_postgres.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/providers/ogr/qgsogrprovider.cpp b/src/core/providers/ogr/qgsogrprovider.cpp index 9c8977ad91b3..ddfe7b0b395f 100644 --- a/src/core/providers/ogr/qgsogrprovider.cpp +++ b/src/core/providers/ogr/qgsogrprovider.cpp @@ -1385,7 +1385,7 @@ QVariant QgsOgrProvider::defaultValue( int fieldId ) const QString defaultVal = mDefaultValues.value( fieldId, QString() ); if ( defaultVal.isEmpty() ) - return defaultVal; + return QVariant(); QVariant resultVar = defaultVal; if ( defaultVal == QStringLiteral( "CURRENT_TIMESTAMP" ) ) diff --git a/tests/src/python/test_provider_ogr.py b/tests/src/python/test_provider_ogr.py index f0958a674c91..fe5a0715547b 100644 --- a/tests/src/python/test_provider_ogr.py +++ b/tests/src/python/test_provider_ogr.py @@ -651,7 +651,7 @@ def testSpatialiteDefaultValues(self): dp = vl.dataProvider() #FIXME: should it be None? self.assertTrue(dp.defaultValue(0).isNull()) - self.assertTrue(dp.defaultValue(1).isNull()) + self.assertIsNone(dp.defaultValue(1)) #FIXME: This fails because there is no backend-side evaluation in this provider #self.assertTrue(dp.defaultValue(2).startswith(now.strftime('%Y-%m-%d'))) self.assertTrue(dp.defaultValue(3).startswith(now.strftime('%Y-%m-%d'))) diff --git a/tests/src/python/test_provider_postgres.py b/tests/src/python/test_provider_postgres.py index 68bbcffd45e3..76187e8ecdee 100644 --- a/tests/src/python/test_provider_postgres.py +++ b/tests/src/python/test_provider_postgres.py @@ -1439,7 +1439,7 @@ def testIdentityPk(self): self.assertTrue(vl.isValid()) feature = next(vl.getFeatures()) self.assertIsNotNone(feature.id()) - + def testDefaultValuesAndClauses(self): """Test whether default values like CURRENT_TIMESTAMP or now() they are respected. See GH #33383""" From 171c5228417cdd679e8662f09e1b2fff70803c62 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 28 Jan 2020 13:42:03 +0100 Subject: [PATCH 7/8] Fix spatialite PKs autoincrement backticks ... also fixes "Autogenerate" for PKs Fixes #34085 --- src/providers/spatialite/qgsspatialiteprovider.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/providers/spatialite/qgsspatialiteprovider.cpp b/src/providers/spatialite/qgsspatialiteprovider.cpp index 1c4e44de30fc..982609334e80 100644 --- a/src/providers/spatialite/qgsspatialiteprovider.cpp +++ b/src/providers/spatialite/qgsspatialiteprovider.cpp @@ -936,7 +936,7 @@ void QgsSpatiaLiteProvider::fetchConstraints() if ( rows >= 1 ) { QString tableSql = QString::fromUtf8( results[ 1 ] ); - QRegularExpression rx( QStringLiteral( "[(,]\\s*(?:%1|\"%1\")\\s+INTEGER PRIMARY KEY AUTOINCREMENT" ).arg( mPrimaryKey ), QRegularExpression::CaseInsensitiveOption ); + QRegularExpression rx( QStringLiteral( "[(,]\\s*(?:%1|\"%1\"|`%1`)\\s+INTEGER PRIMARY KEY AUTOINCREMENT" ).arg( mPrimaryKey ), QRegularExpression::CaseInsensitiveOption ); if ( tableSql.contains( rx ) ) { mPrimaryKeyAutoIncrement = true; @@ -1024,6 +1024,10 @@ QVariant QgsSpatiaLiteProvider::defaultValue( int fieldId ) const QString QgsSpatiaLiteProvider::defaultValueClause( int fieldIndex ) const { + if ( mAttributeFields.at( fieldIndex ).name() == mPrimaryKey && mPrimaryKeyAutoIncrement ) + { + return tr( "Autogenerate" ); + } return mDefaultValueClause.value( fieldIndex, QString() ); } From be63036249b87ecb2ed2d26d75ac21a6d228f6fe Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 28 Jan 2020 13:43:10 +0100 Subject: [PATCH 8/8] Update tests for defaultValue/Clause --- tests/src/python/test_provider_postgres.py | 8 +++++++- tests/src/python/test_provider_spatialite.py | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/src/python/test_provider_postgres.py b/tests/src/python/test_provider_postgres.py index 76187e8ecdee..4b425f618751 100644 --- a/tests/src/python/test_provider_postgres.py +++ b/tests/src/python/test_provider_postgres.py @@ -1449,12 +1449,18 @@ def testDefaultValuesAndClauses(self): vl = QgsVectorLayer(self.dbconn + ' sslmode=disable table="public"."test_table_default_values" sql=', 'test', 'postgres') self.assertTrue(vl.isValid()) + dp = vl.dataProvider() + + # Clean the table + dp.deleteFeatures(dp.allFeatureIds()) + # Save it for the test now = datetime.now() # Test default values - dp = vl.dataProvider() dp.setProviderProperty(QgsDataProvider.EvaluateDefaultValues, 1) + # FIXME: spatialite provider (and OGR) return a NULL here and the following passes + # self.assertTrue(dp.defaultValue(0).isNull()) self.assertIsNotNone(dp.defaultValue(0)) self.assertIsNone(dp.defaultValue(1)) self.assertTrue(dp.defaultValue(2).startswith(now.strftime('%Y-%m-%d'))) diff --git a/tests/src/python/test_provider_spatialite.py b/tests/src/python/test_provider_spatialite.py index badf5fcf4c0c..aaf447067026 100644 --- a/tests/src/python/test_provider_spatialite.py +++ b/tests/src/python/test_provider_spatialite.py @@ -1178,7 +1178,7 @@ def testSpatialiteDefaultValues(self): # simple table with primary key sql = """ CREATE TABLE test_table_default_values ( - id integer primary key autoincrement, + `id` integer primary key autoincrement, comment text, created_at_01 text DEFAULT (datetime('now','localtime')), created_at_02 text DEFAULT CURRENT_TIMESTAMP, @@ -1207,7 +1207,7 @@ def testSpatialiteDefaultValues(self): self.assertEqual(dp.defaultValue(4), 123) self.assertEqual(dp.defaultValue(5), 'My default') - self.assertEqual(dp.defaultValueClause(0), '') + self.assertEqual(dp.defaultValueClause(0), 'Autogenerate') self.assertEqual(dp.defaultValueClause(1), '') self.assertEqual(dp.defaultValueClause(2), "datetime('now','localtime')") self.assertEqual(dp.defaultValueClause(3), "CURRENT_TIMESTAMP")