LCOV - code coverage report
Current view: top level - core - qgssqliteutils.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 42 183 23.0 %
Date: 2021-04-10 08:29:14 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :                           qgssqliteutils.cpp
       3                 :            :                            -------------------
       4                 :            :     begin                : Nov, 2017
       5                 :            :     copyright            : (C) 2017 by Nyall Dawson
       6                 :            :     email                : nyall dot dawson at gmail dot com
       7                 :            :  ***************************************************************************/
       8                 :            : 
       9                 :            : /***************************************************************************
      10                 :            :  *                                                                         *
      11                 :            :  *   This program is free software; you can redistribute it and/or modify  *
      12                 :            :  *   it under the terms of the GNU General Public License as published by  *
      13                 :            :  *   the Free Software Foundation; either version 2 of the License, or     *
      14                 :            :  *   (at your option) any later version.                                   *
      15                 :            :  *                                                                         *
      16                 :            :  ***************************************************************************/
      17                 :            : 
      18                 :            : #include "qgssqliteutils.h"
      19                 :            : 
      20                 :            : #include <sqlite3.h>
      21                 :            : #include <cstdarg>
      22                 :            : #include <QVariant>
      23                 :            : #include <QSet>
      24                 :            : 
      25                 :            : // Temporary solution until GDAL Unique support is available
      26                 :            : #include <regex>
      27                 :            : #include <sstream>
      28                 :            : #include <algorithm>
      29                 :            : // end temporary
      30                 :            : 
      31                 :          5 : void QgsSqlite3Closer::operator()( sqlite3 *database )
      32                 :            : {
      33                 :          5 :   sqlite3_close_v2( database );
      34                 :          5 : }
      35                 :            : 
      36                 :         62 : void QgsSqlite3StatementFinalizer::operator()( sqlite3_stmt *statement )
      37                 :            : {
      38                 :         62 :   sqlite3_finalize( statement );
      39                 :         62 : }
      40                 :            : 
      41                 :          2 : int sqlite3_statement_unique_ptr::step()
      42                 :            : {
      43                 :          2 :   return sqlite3_step( get() );
      44                 :            : }
      45                 :            : 
      46                 :          0 : QString sqlite3_statement_unique_ptr::columnName( int column ) const
      47                 :            : {
      48                 :          0 :   return QString::fromUtf8( static_cast<const char *>( sqlite3_column_name( get(), column ) ) );
      49                 :            : }
      50                 :            : 
      51                 :          0 : double sqlite3_statement_unique_ptr::columnAsDouble( int column ) const
      52                 :            : {
      53                 :          0 :   return sqlite3_column_double( get(), column );
      54                 :            : }
      55                 :            : 
      56                 :          0 : int sqlite3_statement_unique_ptr::columnCount() const
      57                 :            : {
      58                 :          0 :   return sqlite3_column_count( get() );
      59                 :            : }
      60                 :            : 
      61                 :       1465 : QString sqlite3_statement_unique_ptr::columnAsText( int column ) const
      62                 :            : {
      63                 :       1465 :   return QString::fromUtf8( reinterpret_cast<const char *>( sqlite3_column_text( get(), column ) ) );
      64                 :            : }
      65                 :            : 
      66                 :          0 : QByteArray sqlite3_statement_unique_ptr::columnAsBlob( int column ) const
      67                 :            : {
      68                 :          0 :   const void *blob = sqlite3_column_blob( get(), column );
      69                 :          0 :   int size = sqlite3_column_bytes( get(), column );
      70                 :          0 :   return QByteArray( reinterpret_cast<const char *>( blob ), size );
      71                 :            : }
      72                 :            : 
      73                 :          0 : qlonglong sqlite3_statement_unique_ptr::columnAsInt64( int column ) const
      74                 :            : {
      75                 :          0 :   return sqlite3_column_int64( get(), column );
      76                 :            : }
      77                 :            : 
      78                 :          5 : int sqlite3_database_unique_ptr::open( const QString &path )
      79                 :            : {
      80                 :          5 :   sqlite3 *database = nullptr;
      81                 :          5 :   int result = sqlite3_open( path.toUtf8(), &database );
      82                 :          5 :   reset( database );
      83                 :          5 :   return result;
      84                 :          0 : }
      85                 :            : 
      86                 :          2 : int sqlite3_database_unique_ptr::open_v2( const QString &path, int flags, const char *zVfs )
      87                 :            : {
      88                 :          2 :   sqlite3 *database = nullptr;
      89                 :          2 :   int result = sqlite3_open_v2( path.toUtf8(), &database, flags, zVfs );
      90                 :          2 :   reset( database );
      91                 :          2 :   return result;
      92                 :          0 : }
      93                 :            : 
      94                 :          0 : QString sqlite3_database_unique_ptr::errorMessage() const
      95                 :            : {
      96                 :          0 :   return QString( sqlite3_errmsg( get() ) );
      97                 :            : }
      98                 :            : 
      99                 :         62 : sqlite3_statement_unique_ptr sqlite3_database_unique_ptr::prepare( const QString &sql, int &resultCode ) const
     100                 :            : {
     101                 :         62 :   sqlite3_stmt *preparedStatement = nullptr;
     102                 :         62 :   const char *tail = nullptr;
     103                 :         62 :   resultCode = sqlite3_prepare( get(), sql.toUtf8(), sql.toUtf8().length(), &preparedStatement, &tail );
     104                 :         62 :   sqlite3_statement_unique_ptr s;
     105                 :         62 :   s.reset( preparedStatement );
     106                 :         62 :   return s;
     107                 :         62 : }
     108                 :            : 
     109                 :          0 : int sqlite3_database_unique_ptr::exec( const QString &sql, QString &errorMessage ) const
     110                 :            : {
     111                 :            :   char *errMsg;
     112                 :            : 
     113                 :          0 :   int ret = sqlite3_exec( get(), sql.toUtf8(), nullptr, nullptr, &errMsg );
     114                 :            : 
     115                 :          0 :   if ( errMsg )
     116                 :            :   {
     117                 :          0 :     errorMessage = QString::fromUtf8( errMsg );
     118                 :          0 :     sqlite3_free( errMsg );
     119                 :          0 :   }
     120                 :            : 
     121                 :          0 :   return ret;
     122                 :          0 : }
     123                 :            : 
     124                 :          0 : QSet<QString> QgsSqliteUtils::uniqueFields( sqlite3 *connection, const QString &tableName, QString &errorMessage )
     125                 :            : {
     126                 :          0 :   QSet<QString> uniqueFieldsResults;
     127                 :          0 :   char *zErrMsg = 0;
     128                 :          0 :   std::vector<std::string> rows;
     129                 :          0 :   QByteArray tableNameUtf8 = tableName.toUtf8();
     130                 :          0 :   QString sql = qgs_sqlite3_mprintf( "select sql from sqlite_master "
     131                 :          0 :                                      "where type='table' and name='%q'", tableNameUtf8.constData() );
     132                 :          0 :   auto cb = [ ](
     133                 :            :               void *data /* Data provided in the 4th argument of sqlite3_exec() */,
     134                 :            :               int /* The number of columns in row */,
     135                 :            :               char **argv /* An array of strings representing fields in the row */,
     136                 :            :               char ** /* An array of strings representing column names */ ) -> int
     137                 :            :   {
     138                 :          0 :     static_cast<std::vector<std::string>*>( data )->push_back( argv[0] );
     139                 :          0 :     return 0;
     140                 :          0 :   };
     141                 :            : 
     142                 :          0 :   int rc = sqlite3_exec( connection, sql.toUtf8(), cb, ( void * )&rows, &zErrMsg );
     143                 :          0 :   if ( rc != SQLITE_OK )
     144                 :            :   {
     145                 :          0 :     errorMessage = zErrMsg;
     146                 :          0 :     sqlite3_free( zErrMsg );
     147                 :          0 :     return uniqueFieldsResults;
     148                 :            :   }
     149                 :            : 
     150                 :            :   // Match identifiers with " or ` or no delimiter (and no spaces).
     151                 :          0 :   std::smatch uniqueFieldMatch;
     152                 :          0 :   static const std::regex sFieldIdentifierRe { R"raw(\s*(["`]([^"`]+)["`])|(([^\s]+)\s).*)raw" };
     153                 :          0 :   for ( auto tableDefinition : rows )
     154                 :            :   {
     155                 :          0 :     tableDefinition = tableDefinition.substr( tableDefinition.find( '(' ), tableDefinition.rfind( ')' ) );
     156                 :          0 :     std::stringstream tableDefinitionStream { tableDefinition };
     157                 :          0 :     while ( tableDefinitionStream.good() )
     158                 :            :     {
     159                 :          0 :       std::string fieldStr;
     160                 :          0 :       std::getline( tableDefinitionStream, fieldStr, ',' );
     161                 :          0 :       std::string upperCaseFieldStr { fieldStr };
     162                 :          0 :       std::transform( upperCaseFieldStr.begin(), upperCaseFieldStr.end(), upperCaseFieldStr.begin(), ::toupper );
     163                 :          0 :       if ( upperCaseFieldStr.find( "UNIQUE" ) != std::string::npos )
     164                 :            :       {
     165                 :          0 :         if ( std::regex_search( fieldStr, uniqueFieldMatch, sFieldIdentifierRe ) )
     166                 :            :         {
     167                 :          0 :           const std::string quoted { uniqueFieldMatch.str( 2 ) };
     168                 :          0 :           uniqueFieldsResults.insert( QString::fromStdString( quoted.length() ? quoted :  uniqueFieldMatch.str( 4 ) ) );
     169                 :          0 :         }
     170                 :          0 :       }
     171                 :          0 :     }
     172                 :          0 :   }
     173                 :          0 :   rows.clear();
     174                 :            : 
     175                 :            :   // Search indexes:
     176                 :          0 :   sql = qgs_sqlite3_mprintf( "SELECT sql FROM sqlite_master WHERE type='index' AND"
     177                 :          0 :                              " tbl_name='%q' AND sql LIKE 'CREATE UNIQUE INDEX%%'", tableNameUtf8.constData() );
     178                 :          0 :   rc = sqlite3_exec( connection, sql.toUtf8(), cb, ( void * )&rows, &zErrMsg );
     179                 :          0 :   if ( rc != SQLITE_OK )
     180                 :            :   {
     181                 :          0 :     errorMessage = zErrMsg;
     182                 :          0 :     sqlite3_free( zErrMsg );
     183                 :          0 :     return uniqueFieldsResults;
     184                 :            :   }
     185                 :            : 
     186                 :          0 :   if ( rows.size() > 0 )
     187                 :            :   {
     188                 :          0 :     static const std::regex sFieldIndexIdentifierRe { R"raw(\(\s*[`"]?([^",`\)]+)["`]?\s*\))raw" };
     189                 :          0 :     for ( auto indexDefinition : rows )
     190                 :            :     {
     191                 :          0 :       std::string upperCaseIndexDefinition { indexDefinition };
     192                 :          0 :       std::transform( upperCaseIndexDefinition.begin(), upperCaseIndexDefinition.end(), upperCaseIndexDefinition.begin(), ::toupper );
     193                 :          0 :       if ( upperCaseIndexDefinition.find( "UNIQUE" ) != std::string::npos )
     194                 :            :       {
     195                 :          0 :         indexDefinition = indexDefinition.substr( indexDefinition.find( '(' ), indexDefinition.rfind( ')' ) );
     196                 :          0 :         if ( std::regex_search( indexDefinition, uniqueFieldMatch, sFieldIndexIdentifierRe ) )
     197                 :            :         {
     198                 :          0 :           uniqueFieldsResults.insert( QString::fromStdString( uniqueFieldMatch.str( 1 ) ) );
     199                 :          0 :         }
     200                 :          0 :       }
     201                 :          0 :     }
     202                 :          0 :   }
     203                 :          0 :   return uniqueFieldsResults;
     204                 :          0 : }
     205                 :            : 
     206                 :          0 : long long QgsSqliteUtils::nextSequenceValue( sqlite3 *connection, const QString &tableName, QString errorMessage )
     207                 :            : {
     208                 :          0 :   long long result { -1 };
     209                 :          0 :   sqlite3_database_unique_ptr dsPtr;
     210                 :          0 :   dsPtr.reset( connection );
     211                 :          0 :   const QString quotedTableName { QgsSqliteUtils::quotedValue( tableName ) };
     212                 :            : 
     213                 :            :   int resultCode;
     214                 :          0 :   sqlite3_statement_unique_ptr stmt { dsPtr.prepare( QStringLiteral( "SELECT seq FROM sqlite_sequence WHERE name = %1" )
     215                 :          0 :                                       .arg( quotedTableName ), resultCode )};
     216                 :          0 :   if ( resultCode == SQLITE_OK )
     217                 :            :   {
     218                 :          0 :     stmt.step();
     219                 :          0 :     result = sqlite3_column_int64( stmt.get(), 0 );
     220                 :            :     // Try to create the sequence in case this is an empty layer
     221                 :          0 :     if ( sqlite3_column_count( stmt.get() ) == 0 )
     222                 :            :     {
     223                 :          0 :       dsPtr.exec( QStringLiteral( "INSERT INTO sqlite_sequence (name, seq) VALUES (%1, 1)" ).arg( quotedTableName ), errorMessage );
     224                 :          0 :       if ( errorMessage.isEmpty() )
     225                 :            :       {
     226                 :          0 :         result = 1;
     227                 :          0 :       }
     228                 :            :       else
     229                 :            :       {
     230                 :          0 :         errorMessage = QObject::tr( "Error retrieving default value for %1" ).arg( tableName );
     231                 :            :       }
     232                 :          0 :     }
     233                 :            :     else // increment
     234                 :            :     {
     235                 :          0 :       if ( dsPtr.exec( QStringLiteral( "UPDATE sqlite_sequence SET seq = %1 WHERE name = %2" )
     236                 :          0 :                        .arg( QString::number( ++result ), quotedTableName ),
     237                 :          0 :                        errorMessage ) != SQLITE_OK )
     238                 :            :       {
     239                 :          0 :         errorMessage = QObject::tr( "Error retrieving default value for %1" ).arg( tableName );
     240                 :          0 :         result = -1;
     241                 :          0 :       }
     242                 :            :     }
     243                 :          0 :   }
     244                 :            : 
     245                 :          0 :   dsPtr.release();
     246                 :          0 :   return result;
     247                 :          0 : }
     248                 :            : 
     249                 :          1 : QString QgsSqliteUtils::quotedString( const QString &value )
     250                 :            : {
     251                 :          1 :   if ( value.isNull() )
     252                 :          0 :     return QStringLiteral( "NULL" );
     253                 :            : 
     254                 :          1 :   QString v = value;
     255                 :          1 :   v.replace( '\'', QLatin1String( "''" ) );
     256                 :          1 :   return v.prepend( '\'' ).append( '\'' );
     257                 :          1 : }
     258                 :            : 
     259                 :          0 : QString QgsSqliteUtils::quotedIdentifier( const QString &identifier )
     260                 :            : {
     261                 :          0 :   QString id( identifier );
     262                 :          0 :   id.replace( '\"', QLatin1String( "\"\"" ) );
     263                 :          0 :   return id.prepend( '\"' ).append( '\"' );
     264                 :          0 : }
     265                 :            : 
     266                 :          0 : QString QgsSqliteUtils::quotedValue( const QVariant &value )
     267                 :            : {
     268                 :          0 :   if ( value.isNull() )
     269                 :          0 :     return QStringLiteral( "NULL" );
     270                 :            : 
     271                 :          0 :   switch ( value.type() )
     272                 :            :   {
     273                 :            :     case QVariant::Int:
     274                 :            :     case QVariant::LongLong:
     275                 :            :     case QVariant::Double:
     276                 :          0 :       return value.toString();
     277                 :            : 
     278                 :            :     case QVariant::Bool:
     279                 :            :       //SQLite has no boolean literals
     280                 :          0 :       return value.toBool() ? QStringLiteral( "1" ) : QStringLiteral( "0" );
     281                 :            : 
     282                 :            :     default:
     283                 :            :     case QVariant::String:
     284                 :          0 :       QString v = value.toString();
     285                 :            :       // https://www.sqlite.org/lang_expr.html :
     286                 :            :       // """A string constant is formed by enclosing the string in single quotes (').
     287                 :            :       // A single quote within the string can be encoded by putting two single quotes
     288                 :            :       // in a row - as in Pascal. C-style escapes using the backslash character are not supported because they are not standard SQL. """
     289                 :          0 :       return v.replace( '\'', QLatin1String( "''" ) ).prepend( '\'' ).append( '\'' );
     290                 :          0 :   }
     291                 :          0 : }
     292                 :            : 
     293                 :          0 : QStringList QgsSqliteUtils::systemTables()
     294                 :            : {
     295                 :          0 :   return QStringList() << QStringLiteral( "SpatialIndex" ) << QStringLiteral( "geom_cols_ref_sys" ) << QStringLiteral( "geometry_columns" )
     296                 :          0 :          << QStringLiteral( "geometry_columns_auth" ) << QStringLiteral( "views_geometry_columns" ) << QStringLiteral( "virts_geometry_columns" )
     297                 :          0 :          << QStringLiteral( "spatial_ref_sys" ) << QStringLiteral( "spatial_ref_sys_all" ) << QStringLiteral( "spatial_ref_sys_aux" )
     298                 :          0 :          << QStringLiteral( "sqlite_sequence" ) << QStringLiteral( "tableprefix_metadata" ) << QStringLiteral( "tableprefix_rasters" )
     299                 :          0 :          << QStringLiteral( "layer_params" ) << QStringLiteral( "layer_statistics" ) << QStringLiteral( "layer_sub_classes" )
     300                 :          0 :          << QStringLiteral( "layer_table_layout" ) << QStringLiteral( "pattern_bitmaps" ) << QStringLiteral( "symbol_bitmaps" )
     301                 :          0 :          << QStringLiteral( "project_defs" ) << QStringLiteral( "raster_pyramids" ) << QStringLiteral( "sqlite_stat1" ) << QStringLiteral( "sqlite_stat2" )
     302                 :          0 :          << QStringLiteral( "spatialite_history" ) << QStringLiteral( "geometry_columns_field_infos" ) << QStringLiteral( "geometry_columns_statistics" )
     303                 :          0 :          << QStringLiteral( "geometry_columns_time" ) << QStringLiteral( "sql_statements_log" ) << QStringLiteral( "vector_layers" )
     304                 :          0 :          << QStringLiteral( "vector_layers_auth" ) << QStringLiteral( "vector_layers_field_infos" ) << QStringLiteral( "vector_layers_statistics" )
     305                 :          0 :          << QStringLiteral( "views_geometry_columns_auth" ) << QStringLiteral( "views_geometry_columns_field_infos" )
     306                 :          0 :          << QStringLiteral( "views_geometry_columns_statistics" ) << QStringLiteral( "virts_geometry_columns_auth" )
     307                 :          0 :          << QStringLiteral( "virts_geometry_columns_field_infos" ) << QStringLiteral( "virts_geometry_columns_statistics" )
     308                 :          0 :          << QStringLiteral( "virts_layer_statistics" ) << QStringLiteral( "views_layer_statistics" )
     309                 :          0 :          << QStringLiteral( "ElementaryGeometries" );
     310                 :          0 : }
     311                 :            : 
     312                 :         65 : QString qgs_sqlite3_mprintf( const char *format, ... )
     313                 :            : {
     314                 :            :   va_list ap;
     315                 :         65 :   va_start( ap, format );
     316                 :         65 :   char *c_str = sqlite3_vmprintf( format, ap );
     317                 :         65 :   va_end( ap );
     318                 :         65 :   QString res( QString::fromUtf8( c_str ) );
     319                 :         65 :   sqlite3_free( c_str );
     320                 :         65 :   return res;
     321                 :         65 : }

Generated by: LCOV version 1.14