Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support OGR GPKG in datasource uri connection #36660

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions python/core/auto_generated/qgsdatasourceuri.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Constructor for QgsDataSourceUri which parses an input ``uri`` string.
QString connectionInfo( bool expandAuthConfig = true ) const;
%Docstring
Returns the connection part of the URI.
This may be a file path in case of a filesystem-based DB (spatialite or GPKG).
%End

QString uri( bool expandAuthConfig = true ) const;
Expand Down
29 changes: 27 additions & 2 deletions src/core/qgsdatasourceuri.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ QgsDataSourceUri::QgsDataSourceUri()
}

QgsDataSourceUri::QgsDataSourceUri( const QString &u )
: mRawUri( u )
{
QString uri = u;
int i = 0;
Expand Down Expand Up @@ -532,13 +533,30 @@ QString QgsDataSourceUri::connectionInfo( bool expandAuthConfig ) const
}
}

// Is this an OGR file-based DB?
if ( connectionItems.isEmpty() )
{
// Handles OGR geopackages, gets the raw uri and remove what is after the '|'
const QStringList parts{ mRawUri.split( '|' ) };
if ( ! parts.isEmpty() )
{
connectionItems.push_back( parts.at( 0 ) );
}
}

return connectionItems.join( QStringLiteral( " " ) );
}

QString QgsDataSourceUri::uri( bool expandAuthConfig ) const
{
QString uri = connectionInfo( expandAuthConfig );

// Filsystem based?
if ( uri.compare( mRawUri, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
{
return uri;
}

if ( !mKeyColumn.isEmpty() )
{
uri += QStringLiteral( " key='%1'" ).arg( escape( mKeyColumn ) );
Expand Down Expand Up @@ -572,8 +590,15 @@ QString QgsDataSourceUri::uri( bool expandAuthConfig ) const
QgsDebugMsg( QStringLiteral( "invalid uri parameter %1 skipped" ).arg( it.key() ) );
continue;
}

uri += ' ' + it.key() + "='" + escape( it.value() ) + '\'';
// filesystem based
if ( it.key() + '=' + it.value() == uri || it.key().contains( uri ) )
{
return mRawUri;
}
else
{
uri += ' ' + it.key() + "='" + escape( it.value() ) + '\'';
}
}

QString columnName( mGeometryColumn );
Expand Down
4 changes: 4 additions & 0 deletions src/core/qgsdatasourceuri.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class CORE_EXPORT QgsDataSourceUri

/**
* Returns the connection part of the URI.
* This may be a file path in case of a filesystem-based DB (spatialite or GPKG).
*/
QString connectionInfo( bool expandAuthConfig = true ) const;

Expand Down Expand Up @@ -308,6 +309,9 @@ class CORE_EXPORT QgsDataSourceUri

/* data */

//! The original unmodified/unparsed uri string
QString mRawUri;

//! host name
QString mHost;
//! port the database server listens on
Expand Down
22 changes: 11 additions & 11 deletions src/ui/qgsprojectpropertiesbase.ui
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@
</sizepolicy>
</property>
<property name="currentIndex">
<number>8</number>
<number>4</number>
</property>
<widget class="QWidget" name="mProjOptsGeneral">
<layout class="QVBoxLayout" name="verticalLayout_6">
Expand Down Expand Up @@ -274,8 +274,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>590</width>
<height>828</height>
<width>537</width>
<height>855</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
Expand Down Expand Up @@ -897,8 +897,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>603</width>
<height>161</height>
<width>546</width>
<height>164</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
Expand Down Expand Up @@ -972,8 +972,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>290</width>
<height>543</height>
<width>269</width>
<height>553</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12">
Expand Down Expand Up @@ -1392,7 +1392,7 @@
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="mAutoTransaction">
<property name="toolTip">
<string>When enabled, layers from the same database connection will be put into a transaction group. Their edit state will be synchronized and changes to these layers will be sent to the provider immediately. Only supported on postgres provider.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When enabled, layers from the same database connection will be put into a transaction group. &lt;/p&gt;&lt;p&gt;Their edit state will be synchronized and changes to these layers will be sent to the provider immediately. &lt;/p&gt;&lt;p&gt;Only supported on:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Postgres&lt;/li&gt;&lt;li&gt;Spatialite&lt;/li&gt;&lt;li&gt; GeoPackage&lt;/li&gt;&lt;li&gt;Oracle&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Automatically create transaction groups where possible</string>
Expand Down Expand Up @@ -1548,8 +1548,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>178</width>
<height>54</height>
<width>167</width>
<height>55</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_17">
Expand Down Expand Up @@ -1611,7 +1611,7 @@
<x>0</x>
<y>0</y>
<width>671</width>
<height>3090</height>
<height>3139</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_13">
Expand Down
61 changes: 61 additions & 0 deletions tests/src/core/testqgsdatasourceuri.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ class TestQgsDataSourceUri: public QObject
private slots:
void checkparser();
void checkparser_data();

/**
* connectionInfo is used in several places to extract the
* connection part of a layer URI, this means that for a networked
* DB we get the connection string, but for a file-based DB (spatialite and
* GPKG we need to get the file path. Let's test it.
*/
void checkConnectionInfo();
void checkConnectionInfo_data();
void checkAuthParams();
};

Expand Down Expand Up @@ -230,6 +239,58 @@ void TestQgsDataSourceUri::checkparser()
QCOMPARE( ds.param( "myparam" ), myparam );
}

void TestQgsDataSourceUri::checkConnectionInfo_data()
{
QTest::addColumn<QString>( "uri" );
QTest::addColumn<QString>( "connectionInfo" );
QTest::addColumn<QString>( "roundtrip" );

// Test roundtrip
QTest::newRow( "gpx" ) << "testdata/layers.gpx?type=track"
<< "testdata/layers.gpx?type=track"
<< "testdata/layers.gpx?type=track";
QTest::newRow( "shapefile" ) << R"(/my/path/to\ my/hapefile.shp)"
<< R"(/my/path/to\ my/hapefile.shp)"
<< R"(/my/path/to\ my/hapefile.shp)";
// Real DBs
QTest::newRow( "PG" ) << "dbname='mydb' host='myhost' user='91divoc' password='quarantine' port='5432' mode='2' schema=myschema table=my_table (geom)"
<< "dbname='mydb' host=myhost port=5432 user='91divoc' password='quarantine'"
<< "dbname='mydb' host=myhost port=5432 user='91divoc' password='quarantine' mode='2' table=\"myschema\".\"my_table\" (geom)";
QTest::newRow( "PG service" ) << "service=my_service schema=myschema table=my_table"
<< "service='my_service'"
<< "service='my_service' table=\"myschema\".\"my_table\"";
QTest::newRow( "spatialite" ) << R"(dbname='/home/qgis/dev/QGIS/tests/testdata/provider/spatialite.db' table="somedata" (geom))"
<< R"(dbname='/home/qgis/dev/QGIS/tests/testdata/provider/spatialite.db')"
<< R"(dbname='/home/qgis/dev/QGIS/tests/testdata/provider/spatialite.db' table="somedata" (geom))";
// This fail because the data source uri parsed adds another back slash, it is probably not an issue
//QTest::newRow( "spatialite on windows" ) << R"(dbname='C:\my fancy path\qgis\spatialite.db' table="somedata" (geom))"
// << R"(dbname='C:\my fancy path\qgis\spatialite.db')";
QTest::newRow( "geopackage" ) << "/home/qgis/dev/QGIS/tests/testdata/provider/geopackage.gpkg|layername=my_layer"
<< "/home/qgis/dev/QGIS/tests/testdata/provider/geopackage.gpkg"
<< "/home/qgis/dev/QGIS/tests/testdata/provider/geopackage.gpkg|layername=my_layer";
QTest::newRow( "geopackage layerid" ) << "/home/qgis/dev/QGIS/tests/testdata/provider/geopackage.gpkg|layerId=my_layer"
<< "/home/qgis/dev/QGIS/tests/testdata/provider/geopackage.gpkg"
<< "/home/qgis/dev/QGIS/tests/testdata/provider/geopackage.gpkg|layerId=my_layer";
QTest::newRow( "geopackage camel case" ) << "/home/qgis/dev/QGIS/tests/testdata/provider/geopackage.gpkg|layerName=my_layer"
<< "/home/qgis/dev/QGIS/tests/testdata/provider/geopackage.gpkg"
<< "/home/qgis/dev/QGIS/tests/testdata/provider/geopackage.gpkg|layerName=my_layer";
QTest::newRow( "geopackage no layername" ) << "/home/qgis/dev/QGIS/tests/testdata/provider/geopackage.gpkg"
<< "/home/qgis/dev/QGIS/tests/testdata/provider/geopackage.gpkg"
<< "/home/qgis/dev/QGIS/tests/testdata/provider/geopackage.gpkg";
QTest::newRow( "geopackage no path" ) << "geopackage.gpkg|layername=my_layer"
<< "geopackage.gpkg"
<< "geopackage.gpkg|layername=my_layer";
}

void TestQgsDataSourceUri::checkConnectionInfo()
{
QFETCH( QString, uri );
QFETCH( QString, connectionInfo );
QFETCH( QString, roundtrip );
QCOMPARE( QgsDataSourceUri( uri ).connectionInfo( false ), connectionInfo );
QCOMPARE( QgsDataSourceUri( uri ).uri( ), roundtrip );
}

void TestQgsDataSourceUri::checkAuthParams()
{
// some providers rely on the QgsDataSourceUri params for storing and retrieving username, password and authentication.
Expand Down