Skip to content

Commit 73674e8

Browse files
authored
[spatialite provider] auto-generate auto-incrementing primary keys (#5849)
1 parent 7a1a4ed commit 73674e8

File tree

3 files changed

+94
-26
lines changed

3 files changed

+94
-26
lines changed

src/providers/spatialite/qgsspatialiteprovider.cpp

+75-25
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@ QgsSpatiaLiteProvider::QgsSpatiaLiteProvider( QString const &uri )
582582
return;
583583
}
584584

585-
if ( mTableBased && hasRowid() )
585+
if ( mTableBased && hasRowid() && mPrimaryKey.isEmpty() )
586586
{
587587
mPrimaryKey = QStringLiteral( "ROWID" );
588588
}
@@ -860,39 +860,65 @@ void QgsSpatiaLiteProvider::fetchConstraints()
860860

861861
Q_FOREACH ( int fieldIdx, mPrimaryKeyAttrs )
862862
{
863-
//primary keys are unique, not null
864863
QgsFieldConstraints constraints = mAttributeFields.at( fieldIdx ).constraints();
865864
constraints.setConstraint( QgsFieldConstraints::ConstraintUnique, QgsFieldConstraints::ConstraintOriginProvider );
866865
constraints.setConstraint( QgsFieldConstraints::ConstraintNotNull, QgsFieldConstraints::ConstraintOriginProvider );
867866
mAttributeFields[ fieldIdx ].setConstraints( constraints );
867+
868+
if ( mAttributeFields[ fieldIdx ].name() == mPrimaryKey )
869+
{
870+
QString sql = QStringLiteral( "SELECT sql FROM sqlite_master WHERE tbl_name=%1" ).arg( quotedIdentifier( mTableName ) );
871+
int ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
872+
if ( ret != SQLITE_OK )
873+
{
874+
handleError( sql, errMsg );
875+
return;
876+
}
877+
878+
if ( rows >= 1 )
879+
{
880+
QString tableSql = QString::fromUtf8( results[ 1 ] );
881+
QRegularExpression rx( QStringLiteral( "[(,]\\s*%1\\s+INTEGER PRIMARY KEY AUTOINCREMENT" ).arg( mPrimaryKey ), QRegularExpression::CaseInsensitiveOption );
882+
if ( tableSql.contains( rx ) )
883+
{
884+
mPrimaryKeyAutoIncrement = true;
885+
insertDefaultValue( fieldIdx, tr( "Autogenerate" ) );
886+
}
887+
}
888+
sqlite3_free_table( results );
889+
}
868890
}
869891
}
870892

871893
void QgsSpatiaLiteProvider::insertDefaultValue( int fieldIndex, QString defaultVal )
872894
{
873895
if ( !defaultVal.isEmpty() )
874896
{
875-
QVariant defaultVariant;
876-
switch ( mAttributeFields.at( fieldIndex ).type() )
897+
QVariant defaultVariant = defaultVal;
898+
899+
if ( mAttributeFields.at( fieldIndex ).name() != mPrimaryKey || ( mAttributeFields.at( fieldIndex ).name() == mPrimaryKey && !mPrimaryKeyAutoIncrement ) )
877900
{
878-
case QVariant::LongLong:
879-
defaultVariant = defaultVal.toLongLong();
880-
break;
901+
switch ( mAttributeFields.at( fieldIndex ).type() )
902+
{
903+
case QVariant::LongLong:
904+
defaultVariant = defaultVal.toLongLong();
905+
break;
881906

882-
case QVariant::Double:
883-
defaultVariant = defaultVal.toDouble();
884-
break;
907+
case QVariant::Double:
908+
defaultVariant = defaultVal.toDouble();
909+
break;
885910

886-
default:
887-
{
888-
if ( defaultVal.startsWith( '\'' ) )
889-
defaultVal = defaultVal.remove( 0, 1 );
890-
if ( defaultVal.endsWith( '\'' ) )
891-
defaultVal.chop( 1 );
892-
defaultVal.replace( QLatin1String( "''" ), QLatin1String( "'" ) );
911+
default:
912+
{
913+
if ( defaultVal.startsWith( '\'' ) )
914+
defaultVal = defaultVal.remove( 0, 1 );
915+
if ( defaultVal.endsWith( '\'' ) )
916+
defaultVal.chop( 1 );
917+
defaultVal.replace( QLatin1String( "''" ), QLatin1String( "'" ) );
893918

894-
defaultVariant = defaultVal;
895-
break;
919+
defaultVariant = defaultVal;
920+
break;
921+
}
896922
}
897923
}
898924
mDefaultValues.insert( fieldIndex, defaultVariant );
@@ -978,6 +1004,12 @@ void QgsSpatiaLiteProvider::loadFields()
9781004
}
9791005
sqlite3_free_table( results );
9801006

1007+
if ( pkCount == 1 )
1008+
{
1009+
// setting the Primary Key column name
1010+
mPrimaryKey = pkName;
1011+
}
1012+
9811013
// check for constraints
9821014
fetchConstraints();
9831015

@@ -986,7 +1018,6 @@ void QgsSpatiaLiteProvider::loadFields()
9861018
{
9871019
determineViewPrimaryKey();
9881020
}
989-
9901021
}
9911022
else
9921023
{
@@ -1037,12 +1068,12 @@ void QgsSpatiaLiteProvider::loadFields()
10371068
}
10381069
}
10391070
sqlite3_finalize( stmt );
1040-
}
10411071

1042-
if ( pkCount == 1 )
1043-
{
1044-
// setting the Primary Key column name
1045-
mPrimaryKey = pkName;
1072+
if ( pkCount == 1 )
1073+
{
1074+
// setting the Primary Key column name
1075+
mPrimaryKey = pkName;
1076+
}
10461077
}
10471078

10481079
updatePrimaryKeyCapabilities();
@@ -3932,6 +3963,11 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags )
39323963
{
39333964
++ia;
39343965
}
3966+
else if ( fieldname == mPrimaryKey && mPrimaryKeyAutoIncrement && v == QVariant( tr( "Autogenerate" ) ) )
3967+
{
3968+
// use auto-generated value if user hasn't specified a unique value
3969+
++ia;
3970+
}
39353971
else if ( v.isNull() )
39363972
{
39373973
// binding a NULL value
@@ -4379,6 +4415,20 @@ QVariant QgsSpatiaLiteProvider::defaultValue( int fieldId ) const
43794415
return mDefaultValues.value( fieldId, QVariant() );
43804416
}
43814417

4418+
bool QgsSpatiaLiteProvider::skipConstraintCheck( int fieldIndex, QgsFieldConstraints::Constraint constraint, const QVariant &value ) const
4419+
{
4420+
Q_UNUSED( constraint );
4421+
4422+
// If the field is the primary key, skip in case it's autog-enerated / auto-incrementing
4423+
if ( mAttributeFields.at( fieldIndex ).name() == mPrimaryKey && mPrimaryKeyAutoIncrement )
4424+
{
4425+
const QVariant defVal = mDefaultValues.value( fieldIndex );
4426+
return defVal.toInt() == value.toInt();
4427+
}
4428+
4429+
return false;
4430+
}
4431+
43824432
void QgsSpatiaLiteProvider::closeDb()
43834433
{
43844434
// trying to close the SQLite DB

src/providers/spatialite/qgsspatialiteprovider.h

+4
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ class QgsSpatiaLiteProvider: public QgsVectorDataProvider
113113
bool changeGeometryValues( const QgsGeometryMap &geometry_map ) override;
114114
QgsVectorDataProvider::Capabilities capabilities() const override;
115115
QVariant defaultValue( int fieldId ) const override;
116+
virtual bool skipConstraintCheck( int fieldIndex, QgsFieldConstraints::Constraint constraint, const QVariant &value = QVariant() ) const override;
116117
bool createAttributeIndex( int field ) override;
117118

118119
/**
@@ -250,6 +251,9 @@ class QgsSpatiaLiteProvider: public QgsVectorDataProvider
250251
//! Name of the primary key column in the table
251252
QString mPrimaryKey;
252253

254+
//! Flag indicating whether the primary key is auto-generated
255+
bool mPrimaryKeyAutoIncrement = false;
256+
253257
//! List of primary key columns in the table
254258
QgsAttributeList mPrimaryKeyAttrs;
255259

tests/src/python/test_provider_spatialite.py

+15-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
from qgis.testing import start_app, unittest
3535
from utilities import unitTestDataPath
3636
from providertestbase import ProviderTestCase
37-
from qgis.PyQt.QtCore import QVariant
37+
from qgis.PyQt.QtCore import QObject, QVariant
3838

3939
from qgis.utils import spatialite_connect
4040

@@ -173,6 +173,12 @@ def setUpClass(cls):
173173
sql = "SELECT AddGeometryColumn('test_relation_b', 'Geometry', 4326, 'POLYGON', 'XY')"
174174
cur.execute(sql)
175175

176+
# table to test auto increment
177+
sql = "CREATE TABLE test_autoincrement(id INTEGER PRIMARY KEY AUTOINCREMENT, num INTEGER);"
178+
cur.execute(sql)
179+
sql = "INSERT INTO test_autoincrement (num) VALUES (123);"
180+
cur.execute(sql)
181+
176182
# tables with constraints
177183
sql = "CREATE TABLE test_constraints(id INTEGER PRIMARY KEY, num INTEGER NOT NULL, desc TEXT UNIQUE, desc2 TEXT, num2 INTEGER NOT NULL UNIQUE)"
178184
cur.execute(sql)
@@ -568,6 +574,14 @@ def testUniqueConstraint(self):
568574
self.assertTrue(fields.at(4).constraints().constraints() & QgsFieldConstraints.ConstraintUnique)
569575
self.assertEqual(fields.at(4).constraints().constraintOrigin(QgsFieldConstraints.ConstraintUnique), QgsFieldConstraints.ConstraintOriginProvider)
570576

577+
def testSkipConstraintCheck(self):
578+
vl = QgsVectorLayer("dbname=%s table=test_autoincrement" % self.dbname, "test_autoincrement",
579+
"spatialite")
580+
self.assertTrue(vl.isValid())
581+
582+
self.assertTrue(vl.dataProvider().skipConstraintCheck(0, QgsFieldConstraints.ConstraintUnique, str("Autogenerate")))
583+
self.assertFalse(vl.dataProvider().skipConstraintCheck(0, QgsFieldConstraints.ConstraintUnique, 123))
584+
571585
# This test would fail. It would require turning on WAL
572586
def XXXXXtestLocking(self):
573587

0 commit comments

Comments
 (0)