Skip to content

Commit 2869681

Browse files
authored
Merge pull request #7976 from elpaso/bugfix-19895-gpkg-vacuum
[browser] GPKG VACUUM menu item
2 parents 0ee119f + b81da85 commit 2869681

File tree

2 files changed

+189
-135
lines changed

2 files changed

+189
-135
lines changed

src/providers/ogr/qgsgeopackagedataitems.cpp

+177-134
Original file line numberDiff line numberDiff line change
@@ -74,26 +74,15 @@ QVector<QgsDataItem *> QgsGeoPackageRootItem::createChildren()
7474
}
7575

7676
#ifdef HAVE_GUI
77-
QList<QAction *> QgsGeoPackageRootItem::actions( QWidget *parent )
77+
QList<QAction *> QgsGeoPackageAbstractLayerItem::actions( QWidget * )
7878
{
7979
QList<QAction *> lst;
80-
81-
QAction *actionNew = new QAction( tr( "New Connection…" ), parent );
82-
connect( actionNew, &QAction::triggered, this, &QgsGeoPackageRootItem::newConnection );
83-
lst.append( actionNew );
84-
85-
QAction *actionCreateDatabase = new QAction( tr( "Create Database…" ), parent );
86-
connect( actionCreateDatabase, &QAction::triggered, this, &QgsGeoPackageRootItem::createDatabase );
87-
lst.append( actionCreateDatabase );
88-
80+
QAction *actionDeleteLayer = new QAction( tr( "Delete Layer '%1'…" ).arg( mName ), this );
81+
connect( actionDeleteLayer, &QAction::triggered, this, &QgsGeoPackageAbstractLayerItem::deleteLayer );
82+
lst.append( actionDeleteLayer );
8983
return lst;
9084
}
9185

92-
QWidget *QgsGeoPackageRootItem::paramWidget()
93-
{
94-
return nullptr;
95-
}
96-
9786
void QgsGeoPackageRootItem::onConnectionsChanged()
9887
{
9988
refresh();
@@ -121,7 +110,6 @@ void QgsGeoPackageRootItem::createDatabase()
121110
}
122111
#endif
123112

124-
125113
QgsGeoPackageCollectionItem::QgsGeoPackageCollectionItem( QgsDataItem *parent, const QString &name, const QString &path )
126114
: QgsDataCollectionItem( parent, name, path )
127115
, mPath( path )
@@ -131,7 +119,6 @@ QgsGeoPackageCollectionItem::QgsGeoPackageCollectionItem( QgsDataItem *parent, c
131119
}
132120

133121

134-
135122
QVector<QgsDataItem *> QgsGeoPackageCollectionItem::createChildren()
136123
{
137124
QVector<QgsDataItem *> children;
@@ -163,6 +150,25 @@ bool QgsGeoPackageCollectionItem::equal( const QgsDataItem *other )
163150
}
164151

165152
#ifdef HAVE_GUI
153+
QList<QAction *> QgsGeoPackageRootItem::actions( QWidget *parent )
154+
{
155+
QList<QAction *> lst;
156+
157+
QAction *actionNew = new QAction( tr( "New Connection…" ), parent );
158+
connect( actionNew, &QAction::triggered, this, &QgsGeoPackageRootItem::newConnection );
159+
lst.append( actionNew );
160+
161+
QAction *actionCreateDatabase = new QAction( tr( "Create Database…" ), parent );
162+
connect( actionCreateDatabase, &QAction::triggered, this, &QgsGeoPackageRootItem::createDatabase );
163+
lst.append( actionCreateDatabase );
164+
165+
return lst;
166+
}
167+
168+
QWidget *QgsGeoPackageRootItem::paramWidget()
169+
{
170+
return nullptr;
171+
}
166172

167173
QList<QAction *> QgsGeoPackageCollectionItem::actions( QWidget *parent )
168174
{
@@ -350,8 +356,162 @@ bool QgsGeoPackageCollectionItem::handleDrop( const QMimeData *data, Qt::DropAct
350356
}
351357
return true;
352358
}
359+
360+
QList<QAction *> QgsGeoPackageConnectionItem::actions( QWidget *parent )
361+
{
362+
QList<QAction *> lst;
363+
364+
QAction *actionDeleteConnection = new QAction( tr( "Remove Connection" ), parent );
365+
connect( actionDeleteConnection, &QAction::triggered, this, &QgsGeoPackageConnectionItem::deleteConnection );
366+
lst.append( actionDeleteConnection );
367+
368+
// Add table to existing DB
369+
QAction *actionAddTable = new QAction( tr( "Create a New Layer or Table…" ), parent );
370+
connect( actionAddTable, &QAction::triggered, this, &QgsGeoPackageConnectionItem::addTable );
371+
lst.append( actionAddTable );
372+
373+
// Run VACUUM
374+
QAction *actionVacuumDb = new QAction( tr( "Compact database (VACUUM)" ), parent );
375+
connect( actionVacuumDb, &QAction::triggered, this, &QgsGeoPackageConnectionItem::vacuumGeoPackageDbAction );
376+
lst.append( actionVacuumDb );
377+
378+
379+
return lst;
380+
}
381+
382+
void QgsGeoPackageCollectionItem::deleteConnection()
383+
{
384+
QgsOgrDbConnection::deleteConnection( name(), QStringLiteral( "GPKG" ) );
385+
mParent->refreshConnections();
386+
}
387+
388+
void QgsGeoPackageCollectionItem::addTable()
389+
{
390+
QgsNewGeoPackageLayerDialog dialog( nullptr );
391+
dialog.setDatabasePath( mPath );
392+
dialog.setCrs( QgsProject::instance()->defaultCrsForNewLayers() );
393+
dialog.setOverwriteBehavior( QgsNewGeoPackageLayerDialog::AddNewLayer );
394+
dialog.lockDatabasePath();
395+
if ( dialog.exec() == QDialog::Accepted )
396+
{
397+
refreshConnections();
398+
}
399+
}
400+
401+
void QgsGeoPackageCollectionItem::addConnection()
402+
{
403+
QgsOgrDbConnection connection( mName, QStringLiteral( "GPKG" ) );
404+
connection.setPath( mPath );
405+
connection.save();
406+
mParent->refreshConnections();
407+
}
408+
409+
void QgsGeoPackageCollectionItem::vacuumGeoPackageDbAction()
410+
{
411+
QString errCause;
412+
bool result = QgsGeoPackageCollectionItem::vacuumGeoPackageDb( mPath, mName, errCause );
413+
if ( result && errCause.isEmpty() )
414+
{
415+
QMessageBox::information( nullptr, tr( "Database compact (VACUUM)" ), tr( "Database <b>%1</b> has been compacted successfully." ).arg( mName ) );
416+
}
417+
else
418+
{
419+
QMessageBox::warning( nullptr, tr( "Database compact (VACUUM)" ), errCause );
420+
}
421+
}
422+
423+
void QgsGeoPackageAbstractLayerItem::deleteLayer()
424+
{
425+
// Check if the layer(s) are in the registry
426+
QList<QgsMapLayer *> layersList;
427+
const auto mapLayers( QgsProject::instance()->mapLayers() );
428+
for ( QgsMapLayer *layer : mapLayers )
429+
{
430+
if ( layer->publicSource() == mUri )
431+
{
432+
layersList << layer;
433+
}
434+
}
435+
436+
if ( ! layersList.isEmpty( ) )
437+
{
438+
if ( QMessageBox::question( nullptr, QObject::tr( "Delete Layer" ), QObject::tr( "The layer <b>%1</b> exists in the current project <b>%2</b>,"
439+
" do you want to remove it from the project and delete it?" ).arg( mName, layersList.at( 0 )->name() ), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
440+
{
441+
return;
442+
}
443+
}
444+
else if ( QMessageBox::question( nullptr, QObject::tr( "Delete Layer" ),
445+
QObject::tr( "Are you sure you want to delete layer <b>%1</b> from GeoPackage?" ).arg( mName ),
446+
QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
447+
{
448+
return;
449+
}
450+
451+
if ( layersList.isEmpty() )
452+
{
453+
QgsProject::instance()->removeMapLayers( layersList );
454+
}
455+
456+
QString errCause;
457+
bool res = executeDeleteLayer( errCause );
458+
if ( !res )
459+
{
460+
QMessageBox::warning( nullptr, tr( "Delete Layer" ), errCause );
461+
}
462+
else
463+
{
464+
QMessageBox::information( nullptr, tr( "Delete Layer" ), tr( "Layer <b>%1</b> deleted successfully." ).arg( mName ) );
465+
if ( mParent )
466+
mParent->refreshConnections();
467+
}
468+
469+
}
353470
#endif
354471

472+
bool QgsGeoPackageCollectionItem::vacuumGeoPackageDb( const QString &path, const QString &name, QString &errCause )
473+
{
474+
bool result = false;
475+
// Better safe than sorry
476+
if ( ! path.isEmpty( ) )
477+
{
478+
char *errmsg = nullptr;
479+
sqlite3_database_unique_ptr database;
480+
int status = database.open_v2( path, SQLITE_OPEN_READWRITE, nullptr );
481+
if ( status != SQLITE_OK )
482+
{
483+
errCause = sqlite3_errmsg( database.get() );
484+
}
485+
else
486+
{
487+
( void )sqlite3_exec(
488+
database.get(), /* An open database */
489+
"VACUUM", /* SQL to be evaluated */
490+
nullptr, /* Callback function */
491+
nullptr, /* 1st argument to callback */
492+
&errmsg /* Error msg written here */
493+
);
494+
}
495+
if ( status != SQLITE_OK || errmsg )
496+
{
497+
errCause = tr( "There was an error compacting (VACUUM) the database <b>%1</b>: %2" )
498+
.arg( name )
499+
.arg( QString::fromUtf8( errmsg ) );
500+
}
501+
else
502+
{
503+
result = true;
504+
}
505+
sqlite3_free( errmsg );
506+
}
507+
else
508+
{
509+
// This should never happen!
510+
errCause = tr( "Layer path is empty: layer cannot be deleted!" );
511+
}
512+
return result;
513+
}
514+
355515
bool QgsGeoPackageCollectionItem::deleteGeoPackageRasterLayer( const QString &uri, QString &errCause )
356516
{
357517
bool result = false;
@@ -438,16 +598,6 @@ bool QgsGeoPackageCollectionItem::deleteGeoPackageRasterLayer( const QString &ur
438598
);
439599
sqlite3_free( sql );
440600
}
441-
// Vacuum
442-
{
443-
( void )sqlite3_exec(
444-
database.get(), /* An open database */
445-
"VACUUM", /* SQL to be evaluated */
446-
nullptr, /* Callback function */
447-
nullptr, /* 1st argument to callback */
448-
nullptr /* Error msg written here */
449-
);
450-
}
451601

452602
if ( status == SQLITE_OK )
453603
{
@@ -486,113 +636,6 @@ bool QgsGeoPackageConnectionItem::equal( const QgsDataItem *other )
486636

487637
}
488638

489-
#ifdef HAVE_GUI
490-
QList<QAction *> QgsGeoPackageConnectionItem::actions( QWidget *parent )
491-
{
492-
QList<QAction *> lst;
493-
494-
QAction *actionDeleteConnection = new QAction( tr( "Remove Connection" ), parent );
495-
connect( actionDeleteConnection, &QAction::triggered, this, &QgsGeoPackageConnectionItem::deleteConnection );
496-
lst.append( actionDeleteConnection );
497-
498-
// Add table to existing DB
499-
QAction *actionAddTable = new QAction( tr( "Create a New Layer or Table…" ), parent );
500-
connect( actionAddTable, &QAction::triggered, this, &QgsGeoPackageConnectionItem::addTable );
501-
lst.append( actionAddTable );
502-
503-
504-
return lst;
505-
}
506-
507-
void QgsGeoPackageCollectionItem::deleteConnection()
508-
{
509-
QgsOgrDbConnection::deleteConnection( name(), QStringLiteral( "GPKG" ) );
510-
mParent->refreshConnections();
511-
}
512-
513-
514-
void QgsGeoPackageCollectionItem::addTable()
515-
{
516-
QgsNewGeoPackageLayerDialog dialog( nullptr );
517-
dialog.setDatabasePath( mPath );
518-
dialog.setCrs( QgsProject::instance()->defaultCrsForNewLayers() );
519-
dialog.setOverwriteBehavior( QgsNewGeoPackageLayerDialog::AddNewLayer );
520-
dialog.lockDatabasePath();
521-
if ( dialog.exec() == QDialog::Accepted )
522-
{
523-
refreshConnections();
524-
}
525-
}
526-
527-
void QgsGeoPackageCollectionItem::addConnection()
528-
{
529-
QgsOgrDbConnection connection( mName, QStringLiteral( "GPKG" ) );
530-
connection.setPath( mPath );
531-
connection.save();
532-
mParent->refreshConnections();
533-
}
534-
535-
#endif
536-
537-
#ifdef HAVE_GUI
538-
QList<QAction *> QgsGeoPackageAbstractLayerItem::actions( QWidget * )
539-
{
540-
QList<QAction *> lst;
541-
QAction *actionDeleteLayer = new QAction( tr( "Delete Layer '%1'…" ).arg( mName ), this );
542-
connect( actionDeleteLayer, &QAction::triggered, this, &QgsGeoPackageAbstractLayerItem::deleteLayer );
543-
lst.append( actionDeleteLayer );
544-
return lst;
545-
}
546-
547-
void QgsGeoPackageAbstractLayerItem::deleteLayer()
548-
{
549-
// Check if the layer(s) are in the registry
550-
QList<QgsMapLayer *> layersList;
551-
const auto mapLayers( QgsProject::instance()->mapLayers() );
552-
for ( QgsMapLayer *layer : mapLayers )
553-
{
554-
if ( layer->publicSource() == mUri )
555-
{
556-
layersList << layer;
557-
}
558-
}
559-
560-
if ( ! layersList.isEmpty( ) )
561-
{
562-
if ( QMessageBox::question( nullptr, QObject::tr( "Delete Layer" ), QObject::tr( "The layer <b>%1</b> exists in the current project <b>%2</b>,"
563-
" do you want to remove it from the project and delete it?" ).arg( mName, layersList.at( 0 )->name() ), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
564-
{
565-
return;
566-
}
567-
}
568-
else if ( QMessageBox::question( nullptr, QObject::tr( "Delete Layer" ),
569-
QObject::tr( "Are you sure you want to delete layer <b>%1</b> from GeoPackage?" ).arg( mName ),
570-
QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
571-
{
572-
return;
573-
}
574-
575-
if ( layersList.isEmpty() )
576-
{
577-
QgsProject::instance()->removeMapLayers( layersList );
578-
}
579-
580-
QString errCause;
581-
bool res = executeDeleteLayer( errCause );
582-
if ( !res )
583-
{
584-
QMessageBox::warning( nullptr, tr( "Delete Layer" ), errCause );
585-
}
586-
else
587-
{
588-
QMessageBox::information( nullptr, tr( "Delete Layer" ), tr( "Layer <b>%1</b> deleted successfully." ).arg( mName ) );
589-
if ( mParent )
590-
mParent->refreshConnections();
591-
}
592-
593-
}
594-
#endif
595-
596639
QgsGeoPackageAbstractLayerItem::QgsGeoPackageAbstractLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri, QgsLayerItem::LayerType layerType, const QString &providerKey )
597640
: QgsLayerItem( parent, name, path, uri, layerType, providerKey )
598641
{

src/providers/ogr/qgsgeopackagedataitems.h

+12-1
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,25 @@ class QgsGeoPackageCollectionItem : public QgsDataCollectionItem
8888
//! Returns the layer type from \a geometryType
8989
static QgsLayerItem::LayerType layerTypeFromDb( const QString &geometryType );
9090

91-
//! Delete a geopackage layer
91+
//! Deletes a geopackage raster layer
9292
static bool deleteGeoPackageRasterLayer( const QString &uri, QString &errCause );
9393

94+
/**
95+
* Compacts (VACUUM) a geopackage database
96+
* \param path DB path
97+
* \param name DB name
98+
* \param errCause contains the error message
99+
* \return true on success
100+
*/
101+
static bool vacuumGeoPackageDb( const QString &path, const QString &name, QString &errCause );
102+
94103
public slots:
95104
#ifdef HAVE_GUI
96105
void addTable();
97106
void addConnection();
98107
void deleteConnection();
108+
//! Compacts (VACUUM) a geopackage database
109+
void vacuumGeoPackageDbAction();
99110
#endif
100111

101112
protected:

0 commit comments

Comments
 (0)