Skip to content

Commit

Permalink
More use of unique_ptrs to simplify sqlite3 memory management
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Nov 7, 2017
1 parent 7d3e491 commit 108de77
Showing 1 changed file with 85 additions and 42 deletions.
127 changes: 85 additions & 42 deletions src/app/qgscustomprojectiondialog.cpp
Expand Up @@ -43,7 +43,7 @@ extern "C"
}

/**
* Destroys OGR data sources.
* Closes a sqlite3 database.
*/
struct QgsSqlite3Closer
{
Expand All @@ -57,7 +57,52 @@ struct QgsSqlite3Closer
}
};

/**
* Finalizes an sqlite3 statement.
*/
struct QgsSqlite3StatementFinalizer
{

/**
* Finalizes an sqlite3 \a statement.
*/
void operator()( sqlite3_stmt *statement )
{
sqlite3_finalize( statement );
}
};

/**
* Unique pointer for sqlite3 prepared statements, which automatically finalizes
* the statement when the pointer goes out of scope or is reset.
*/
class sqlite3_statement_unique_ptr : public std::unique_ptr< sqlite3_stmt, QgsSqlite3StatementFinalizer>
{
public:

/**
* Steps to the next record in the statement, returning the sqlite3 result code.
*/
int step()
{
return sqlite3_step( get() );
}

/**
* Returns the column value from the current statement row as a string.
*/
QString columnAsText( int column )
{
return QString::fromUtf8( ( char * ) sqlite3_column_text( get(), column ) );
}

};


/**
* Unique pointer for sqlite3 databases, which automatically closes
* the database when the pointer goes out of scope or is reset.
*/
class sqlite3_database_unique_ptr : public std::unique_ptr< sqlite3, QgsSqlite3Closer>
{
public:
Expand Down Expand Up @@ -95,8 +140,22 @@ class sqlite3_database_unique_ptr : public std::unique_ptr< sqlite3, QgsSqlite3C
{
return QString( sqlite3_errmsg( get() ) );
}
};

/**
* Prepares a \a sql statement, returning the result. The \a resultCode
* argument will be filled with the sqlite3 result code.
*/
sqlite3_statement_unique_ptr prepare( const QString &sql, int &resultCode )
{
sqlite3_stmt *preparedStatement = nullptr;
const char *tail = nullptr;
resultCode = sqlite3_prepare( get(), sql.toUtf8(), sql.toUtf8().length(), &preparedStatement, &tail );
sqlite3_statement_unique_ptr s;
s.reset( preparedStatement );
return s;
}

};


QgsCustomProjectionDialog::QgsCustomProjectionDialog( QWidget *parent, Qt::WindowFlags fl )
Expand Down Expand Up @@ -146,8 +205,7 @@ void QgsCustomProjectionDialog::populateList()
{
//Setup connection to the existing custom CRS database:
sqlite3_database_unique_ptr database;
const char *tail = nullptr;
sqlite3_stmt *preparedStatement = nullptr;
sqlite3_statement_unique_ptr preparedStatement;
//check the db is available
int result = database.open_v2( QgsApplication::qgisUserDatabaseFilePath(), SQLITE_OPEN_READONLY, nullptr );
if ( result != SQLITE_OK )
Expand All @@ -159,16 +217,15 @@ void QgsCustomProjectionDialog::populateList()
}
QString sql = QStringLiteral( "select srs_id,description,parameters from tbl_srs" );
QgsDebugMsg( QString( "Query to populate existing list:%1" ).arg( sql ) );
result = sqlite3_prepare( database.get(), sql.toUtf8(), sql.toUtf8().length(), &preparedStatement, &tail );
// XXX Need to free memory from the error msg if one is set
preparedStatement = database.prepare( sql, result );
if ( result == SQLITE_OK )
{
QgsCoordinateReferenceSystem crs;
while ( sqlite3_step( preparedStatement ) == SQLITE_ROW )
while ( preparedStatement.step() == SQLITE_ROW )
{
QString id = QString::fromUtf8( ( char * ) sqlite3_column_text( preparedStatement, 0 ) );
QString name = QString::fromUtf8( ( char * ) sqlite3_column_text( preparedStatement, 1 ) );
QString parameters = QString::fromUtf8( ( char * ) sqlite3_column_text( preparedStatement, 2 ) );
QString id = preparedStatement.columnAsText( 0 );
QString name = preparedStatement.columnAsText( 1 );
QString parameters = preparedStatement.columnAsText( 2 );

crs.createFromProj4( parameters );
mExistingCRSnames[id] = name;
Expand All @@ -184,7 +241,7 @@ void QgsCustomProjectionDialog::populateList()
{
QgsDebugMsg( QString( "Populate list query failed: %1" ).arg( sql ) );
}
sqlite3_finalize( preparedStatement );
preparedStatement.reset();

leNameList->sortByColumn( QgisCrsNameColumn, Qt::AscendingOrder );

Expand All @@ -202,8 +259,6 @@ void QgsCustomProjectionDialog::populateList()
bool QgsCustomProjectionDialog::deleteCrs( const QString &id )
{
sqlite3_database_unique_ptr database;
const char *tail = nullptr;
sqlite3_stmt *preparedStatement = nullptr;

QString sql = "delete from tbl_srs where srs_id=" + quotedValue( id );
QgsDebugMsg( sql );
Expand All @@ -217,11 +272,12 @@ bool QgsCustomProjectionDialog::deleteCrs( const QString &id )
// database if it does not exist.
Q_ASSERT( result == SQLITE_OK );
}
result = sqlite3_prepare( database.get(), sql.toUtf8(), sql.toUtf8().length(), &preparedStatement, &tail );
// XXX Need to free memory from the error msg if one is set
if ( result != SQLITE_OK || sqlite3_step( preparedStatement ) != SQLITE_DONE )
{
QgsDebugMsg( QString( "failed to remove CRS from database in custom projection dialog: %1 [%2]" ).arg( sql, database.errorMessage() ) );
sqlite3_statement_unique_ptr preparedStatement = database.prepare( sql, result );
if ( result != SQLITE_OK || preparedStatement.step() != SQLITE_DONE )
{
QgsDebugMsg( QString( "failed to remove CRS from database in custom projection dialog: %1 [%2]" ).arg( sql, database.errorMessage() ) );
}
}

QgsCoordinateReferenceSystem::invalidateCache();
Expand All @@ -233,10 +289,8 @@ bool QgsCustomProjectionDialog::deleteCrs( const QString &id )
void QgsCustomProjectionDialog::insertProjection( const QString &projectionAcronym )
{
sqlite3_database_unique_ptr database;
sqlite3_stmt *preparedStatement = nullptr;
sqlite3_database_unique_ptr srsDatabase;
QString sql;
const char *tail = nullptr;
//check the db is available
int result = database.open( QgsApplication::qgisUserDatabaseFilePath() );
if ( result != SQLITE_OK )
Expand All @@ -258,33 +312,25 @@ void QgsCustomProjectionDialog::insertProjection( const QString &projectionAcro
// Set up the query to retrieve the projection information needed to populate the PROJECTION list
QString srsSql = "select acronym,name,notes,parameters from tbl_projection where acronym=" + quotedValue( projectionAcronym );

const char *srsTail = nullptr;
sqlite3_stmt *srsPreparedStatement = nullptr;
srsResult = sqlite3_prepare( srsDatabase.get(), srsSql.toUtf8(), srsSql.length(), &srsPreparedStatement, &srsTail );
// XXX Need to free memory from the error msg if one is set
sqlite3_statement_unique_ptr srsPreparedStatement = srsDatabase.prepare( srsSql, srsResult );
if ( srsResult == SQLITE_OK )
{
if ( sqlite3_step( srsPreparedStatement ) == SQLITE_ROW )
if ( srsPreparedStatement.step() == SQLITE_ROW )
{
QgsDebugMsg( "Trying to insert projection" );
// We have the result from system srs.db. Now insert into user db.
sql = "insert into tbl_projection(acronym,name,notes,parameters) values ("
+ quotedValue( QString::fromUtf8( ( char * )sqlite3_column_text( srsPreparedStatement, 0 ) ) )
+ ',' + quotedValue( QString::fromUtf8( ( char * )sqlite3_column_text( srsPreparedStatement, 1 ) ) )
+ ',' + quotedValue( QString::fromUtf8( ( char * )sqlite3_column_text( srsPreparedStatement, 2 ) ) )
+ ',' + quotedValue( QString::fromUtf8( ( char * )sqlite3_column_text( srsPreparedStatement, 3 ) ) )
+ ')'
;
result = sqlite3_prepare( database.get(), sql.toUtf8(), sql.length(), &preparedStatement, &tail );
if ( result != SQLITE_OK || sqlite3_step( preparedStatement ) != SQLITE_DONE )
+ quotedValue( srsPreparedStatement.columnAsText( 0 ) )
+ ',' + quotedValue( srsPreparedStatement.columnAsText( 1 ) )
+ ',' + quotedValue( srsPreparedStatement.columnAsText( 2 ) )
+ ',' + quotedValue( srsPreparedStatement.columnAsText( 3 ) )
+ ')';
sqlite3_statement_unique_ptr preparedStatement = database.prepare( sql, result );
if ( result != SQLITE_OK || preparedStatement.step() != SQLITE_DONE )
{
QgsDebugMsg( QString( "Update or insert failed in custom projection dialog: %1 [%2]" ).arg( sql, database.errorMessage() ) );
}

sqlite3_finalize( preparedStatement );
}

sqlite3_finalize( srsPreparedStatement );
}
else
{
Expand Down Expand Up @@ -320,8 +366,6 @@ bool QgsCustomProjectionDialog::saveCrs( QgsCoordinateReferenceSystem parameters
;
QgsDebugMsg( sql );
sqlite3_database_unique_ptr database;
const char *tail = nullptr;
sqlite3_stmt *preparedStatement = nullptr;
//check if the db is available
int result = database.open( QgsApplication::qgisUserDatabaseFilePath() );
if ( result != SQLITE_OK )
Expand All @@ -332,14 +376,13 @@ bool QgsCustomProjectionDialog::saveCrs( QgsCoordinateReferenceSystem parameters
// database if it does not exist.
Q_ASSERT( result == SQLITE_OK );
}
result = sqlite3_prepare( database.get(), sql.toUtf8(), sql.toUtf8().length(), &preparedStatement, &tail );
// XXX Need to free memory from the error msg if one is set
if ( result != SQLITE_OK || sqlite3_step( preparedStatement ) != SQLITE_DONE )
sqlite3_statement_unique_ptr preparedStatement = database.prepare( sql, result );
if ( result != SQLITE_OK || preparedStatement.step() != SQLITE_DONE )
{
QgsDebugMsg( QString( "failed to write to database in custom projection dialog: %1 [%2]" ).arg( sql, database.errorMessage() ) );
}

sqlite3_finalize( preparedStatement );
preparedStatement.reset();
if ( result != SQLITE_OK )
return false;
}
Expand Down

0 comments on commit 108de77

Please sign in to comment.