From e514572516403238a988a142c04b732a955ffe9a Mon Sep 17 00:00:00 2001 From: vmora Date: Tue, 9 Jul 2013 16:02:16 +0200 Subject: [PATCH 1/2] - added test for issue #7550 --- tests/src/python/CMakeLists.txt | 1 + .../src/python/test_qgsspatialiteprovider.py | 85 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 tests/src/python/test_qgsspatialiteprovider.py diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt index 6c6797bc5130..fc962bb2ea0c 100644 --- a/tests/src/python/CMakeLists.txt +++ b/tests/src/python/CMakeLists.txt @@ -24,3 +24,4 @@ ADD_PYTHON_TEST(PyQgsComposerLabel test_qgscomposerlabel.py) ADD_PYTHON_TEST(PyQgsExpression test_qgsexpression.py) #ADD_PYTHON_TEST(PyQgsPalLabeling test_qgspallabeling.py) ADD_PYTHON_TEST(PyQgsVectorFileWriter test_qgsvectorfilewriter.py) +ADD_PYTHON_TEST(PyQgsSpatialiteProvider test_qgsspatialiteprovider.py) diff --git a/tests/src/python/test_qgsspatialiteprovider.py b/tests/src/python/test_qgsspatialiteprovider.py new file mode 100644 index 000000000000..3c88951a9ae3 --- /dev/null +++ b/tests/src/python/test_qgsspatialiteprovider.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsSpatialiteProvider + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" +__author__ = 'Vincent Mora' +__date__ = '09/07/2013' +__copyright__ = 'Copyright 2013, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import os + +from qgis.core import * + +from utilities import (getQgisTestApp, + TestCase, + unittest + ) + +from pyspatialite import dbapi2 as sqlite3 + +# Convenience instances in case you may need them +QGISAPP, CANVAS, IFACE, PARENT = getQgisTestApp() + + +def die(error_message): + raise Exception(error_message) + +class TestQgsSpatialiteProvider(TestCase): + + @classmethod + def setUpClass(cls): + """Run before all tests""" + if os.path.exists("test.sqlite") : + os.remove("test.sqlite") + con = sqlite3.connect("test.sqlite") + cur = con.cursor() + sql = "SELECT InitSpatialMetadata()" + cur.execute(sql) + sql = "CREATE TABLE test_pg (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL)" + cur.execute(sql) + sql = "SELECT AddGeometryColumn('test_pg', 'geometry', 4326, 'POLYGON', 'XY')" + cur.execute(sql) + sql = "INSERT INTO test_pg (id, name, geometry) " + sql += "VALUES (11, 'toto', GeomFromText('POLYGON((0 0,1 0,1 1,0 1,0 0))', 4326))" + rs = cur.execute(sql) + con.commit() + con.close() + + @classmethod + def tearDownClass(cls): + """Run after all tests""" + # for the time beeing, keep the file to check with qgis + #if os.path.exists("test.sqlite") : + # os.remove("test.sqlite") + pass + + def setUp(self): + """Run before each test.""" + pass + + def tearDown(self): + """Run after each test.""" + pass + + def test_SplitFeature(self): + """Create spatialite database""" + layer = QgsVectorLayer("dbname=test.sqlite table=test_pg (geometry)", "test_pg", "spatialite") + help(QgsVectorLayer) + assert(layer.isValid()) + assert(layer.hasGeometryType()) + layer.startEditing() + layer.splitFeatures([QgsPoint(0.5, -0.5), QgsPoint(0.5, 1.5)], 0)==0 or die("error in split") + layer.commitChanges() or die("error in commit") + + + +if __name__ == '__main__': + unittest.main() + + From 61062d719ea362a1216747282f931dce62a313f5 Mon Sep 17 00:00:00 2001 From: vmora Date: Tue, 9 Jul 2013 21:32:17 +0200 Subject: [PATCH 2/2] - solved issue #7550 - implemented getPrimaryKey member function of QgsSpatiaLiteProvider - in addFeatures, if an attribute is a primary key, its value is set to NULL so that sqlite generates a value automagically --- .../spatialite/qgsspatialiteprovider.cpp | 43 +++++++++++++++++++ .../src/python/test_qgsspatialiteprovider.py | 1 - 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/providers/spatialite/qgsspatialiteprovider.cpp b/src/providers/spatialite/qgsspatialiteprovider.cpp index 3f1ddb45016a..374774a256c7 100644 --- a/src/providers/spatialite/qgsspatialiteprovider.cpp +++ b/src/providers/spatialite/qgsspatialiteprovider.cpp @@ -3507,6 +3507,9 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList & flist ) QString separator; int ia, ret; + + const QString primaryKey = getPrimaryKey(); + if ( flist.size() == 0 ) return true; const QgsAttributes & attributevec = flist[0].attributes(); @@ -3605,6 +3608,13 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList & flist ) if ( fieldname.isEmpty() || fieldname == mGeometryColumn ) continue; + // replace primary key with NULL so that sqlite will generate one for us + if ( primaryKey == fieldname ) + { + v = QVariant(); + assert(v.toString().isEmpty()); + } + QVariant::Type type = attributeFields[i].type(); if ( v.toString().isEmpty() ) { @@ -5197,3 +5207,36 @@ QGISEXTERN bool deleteLayer( const QString& dbPath, const QString& tableName, QS return true; } + +QString QgsSpatiaLiteProvider::getPrimaryKey() +{ + char **results; + int rows; + int columns; + char *errMsg = NULL; + QString sql = QString( "PRAGMA table_info(%1)" ).arg( mQuery ); + + int ret = sqlite3_get_table( sqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg ); + if ( ret != SQLITE_OK ) + { + QgsDebugMsg( "sqlite error: " + QString::fromUtf8( errMsg ) ); + } + + int pkColIdx=0; + for (; (pkColIdx < columns) && (QString("pk") != results[pkColIdx]) ; pkColIdx++){} + assert(pkColIdx