QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
qgssqliteutils.cpp
Go to the documentation of this file.
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 <cstdarg>
21#include <sqlite3.h>
22
23#include "qgsvariantutils.h"
24
25#include <QSet>
26#include <QVariant>
27
28// Temporary solution until GDAL Unique support is available
29#include <regex>
30#include <sstream>
31#include <algorithm>
32// end temporary
33
35{
36 sqlite3_close_v2( database );
37}
38
39void QgsSqlite3StatementFinalizer::operator()( sqlite3_stmt *statement ) const
40{
41 sqlite3_finalize( statement );
42}
43
45{
46 return sqlite3_step( get() );
47}
48
50{
51 return QString::fromUtf8( static_cast<const char *>( sqlite3_column_name( get(), column ) ) );
52}
53
55{
56 return sqlite3_column_double( get(), column );
57}
58
60{
61 return sqlite3_column_count( get() );
62}
63
65{
66 return QString::fromUtf8( reinterpret_cast<const char *>( sqlite3_column_text( get(), column ) ) );
67}
68
69QByteArray sqlite3_statement_unique_ptr::columnAsBlob( int column ) const
70{
71 const void *blob = sqlite3_column_blob( get(), column );
72 const int size = sqlite3_column_bytes( get(), column );
73 return QByteArray( reinterpret_cast<const char *>( blob ), size );
74}
75
77{
78 return sqlite3_column_int64( get(), column );
79}
80
81int sqlite3_database_unique_ptr::open( const QString &path )
82{
83 sqlite3 *database = nullptr;
84 const int result = sqlite3_open( path.toUtf8(), &database );
85 reset( database );
86 return result;
87}
88
89int sqlite3_database_unique_ptr::open_v2( const QString &path, int flags, const char *zVfs )
90{
91 sqlite3 *database = nullptr;
92 const int result = sqlite3_open_v2( path.toUtf8(), &database, flags, zVfs );
93 reset( database );
94 return result;
95}
96
98{
99 return QString( sqlite3_errmsg( get() ) );
100}
101
102sqlite3_statement_unique_ptr sqlite3_database_unique_ptr::prepare( const QString &sql, int &resultCode ) const
103{
104 sqlite3_stmt *preparedStatement = nullptr;
105 const char *tail = nullptr;
106 resultCode = sqlite3_prepare( get(), sql.toUtf8(), sql.toUtf8().length(), &preparedStatement, &tail );
108 s.reset( preparedStatement );
109 return s;
110}
111
112int sqlite3_database_unique_ptr::exec( const QString &sql, QString &errorMessage ) const
113{
114 char *errMsg;
115
116 const int ret = sqlite3_exec( get(), sql.toUtf8(), nullptr, nullptr, &errMsg );
117
118 if ( errMsg )
119 {
120 errorMessage = QString::fromUtf8( errMsg );
121 sqlite3_free( errMsg );
122 }
123
124 return ret;
125}
126
127QSet<QString> QgsSqliteUtils::uniqueFields( sqlite3 *connection, const QString &tableName, QString &errorMessage )
128{
129 QSet<QString> uniqueFieldsResults;
130 char *zErrMsg = nullptr;
131 std::vector<std::string> rows;
132 const QByteArray tableNameUtf8 = tableName.toUtf8();
133 QString sql = qgs_sqlite3_mprintf( "select sql from sqlite_master "
134 "where type='table' and name='%q'", tableNameUtf8.constData() );
135 auto cb = [ ](
136 void *data /* Data provided in the 4th argument of sqlite3_exec() */,
137 int /* The number of columns in row */,
138 char **argv /* An array of strings representing fields in the row */,
139 char ** /* An array of strings representing column names */ ) -> int
140 {
141 static_cast<std::vector<std::string>*>( data )->push_back( argv[0] );
142 return 0;
143 };
144
145 int rc = sqlite3_exec( connection, sql.toUtf8(), cb, ( void * )&rows, &zErrMsg );
146 if ( rc != SQLITE_OK )
147 {
148 errorMessage = zErrMsg;
149 sqlite3_free( zErrMsg );
150 return uniqueFieldsResults;
151 }
152
153 // Match identifiers with " or ` or no delimiter (and no spaces).
154 std::smatch uniqueFieldMatch;
155 static const std::regex sFieldIdentifierRe { R"raw(\s*(["`]([^"`]+)["`])|(([^\s]+)\s).*)raw" };
156 for ( auto tableDefinition : rows )
157 {
158 tableDefinition = tableDefinition.substr( tableDefinition.find( '(' ), tableDefinition.rfind( ')' ) );
159 std::stringstream tableDefinitionStream { tableDefinition };
160 while ( tableDefinitionStream.good() )
161 {
162 std::string fieldStr;
163 std::getline( tableDefinitionStream, fieldStr, ',' );
164 std::string upperCaseFieldStr { fieldStr };
165 std::transform( upperCaseFieldStr.begin(), upperCaseFieldStr.end(), upperCaseFieldStr.begin(), ::toupper );
166 if ( upperCaseFieldStr.find( "UNIQUE" ) != std::string::npos )
167 {
168 if ( std::regex_search( fieldStr, uniqueFieldMatch, sFieldIdentifierRe ) )
169 {
170 const std::string quoted { uniqueFieldMatch.str( 2 ) };
171 uniqueFieldsResults.insert( QString::fromStdString( quoted.length() ? quoted : uniqueFieldMatch.str( 4 ) ) );
172 }
173 }
174 }
175 }
176 rows.clear();
177
178 // Search indexes:
179 sql = qgs_sqlite3_mprintf( "SELECT sql FROM sqlite_master WHERE type='index' AND"
180 " tbl_name='%q' AND sql LIKE 'CREATE UNIQUE INDEX%%'", tableNameUtf8.constData() );
181 rc = sqlite3_exec( connection, sql.toUtf8(), cb, ( void * )&rows, &zErrMsg );
182 if ( rc != SQLITE_OK )
183 {
184 errorMessage = zErrMsg;
185 sqlite3_free( zErrMsg );
186 return uniqueFieldsResults;
187 }
188
189 if ( rows.size() > 0 )
190 {
191 static const std::regex sFieldIndexIdentifierRe { R"raw(\‍(\s*[`"]?([^",`\)]+)["`]?\s*\))raw" };
192 for ( auto indexDefinition : rows )
193 {
194 std::string upperCaseIndexDefinition { indexDefinition };
195 std::transform( upperCaseIndexDefinition.begin(), upperCaseIndexDefinition.end(), upperCaseIndexDefinition.begin(), ::toupper );
196 if ( upperCaseIndexDefinition.find( "UNIQUE" ) != std::string::npos )
197 {
198 indexDefinition = indexDefinition.substr( indexDefinition.find( '(' ), indexDefinition.rfind( ')' ) );
199 if ( std::regex_search( indexDefinition, uniqueFieldMatch, sFieldIndexIdentifierRe ) )
200 {
201 uniqueFieldsResults.insert( QString::fromStdString( uniqueFieldMatch.str( 1 ) ) );
202 }
203 }
204 }
205 }
206 return uniqueFieldsResults;
207}
208
209long long QgsSqliteUtils::nextSequenceValue( sqlite3 *connection, const QString &tableName, QString errorMessage )
210{
211 long long result { -1 };
213
214 // this is MESSY -- this function does not have ownership of connection, so this is a HACK:
215 // we intentionally .release() at the end of the function accordingly -- be careful if adding additional return paths!!
216 dsPtr.reset( connection );
217
218 const QString quotedTableName { QgsSqliteUtils::quotedValue( tableName ) };
219
220 int resultCode = 0;
221 sqlite3_statement_unique_ptr stmt { dsPtr.prepare( QStringLiteral( "SELECT seq FROM sqlite_sequence WHERE name = %1" )
222 .arg( quotedTableName ), resultCode )};
223 if ( resultCode == SQLITE_OK && stmt.step() )
224 {
225 result = sqlite3_column_int64( stmt.get(), 0 );
226 // Try to create the sequence in case this is an empty layer
227 if ( sqlite3_column_count( stmt.get() ) == 0 )
228 {
229 dsPtr.exec( QStringLiteral( "INSERT INTO sqlite_sequence (name, seq) VALUES (%1, 1)" ).arg( quotedTableName ), errorMessage );
230 if ( errorMessage.isEmpty() )
231 {
232 result = 1;
233 }
234 else
235 {
236 errorMessage = QObject::tr( "Error retrieving default value for %1" ).arg( tableName );
237 }
238 }
239 else // increment
240 {
241 if ( dsPtr.exec( QStringLiteral( "UPDATE sqlite_sequence SET seq = %1 WHERE name = %2" )
242 .arg( QString::number( ++result ), quotedTableName ),
243 errorMessage ) != SQLITE_OK )
244 {
245 errorMessage = QObject::tr( "Error retrieving default value for %1" ).arg( tableName );
246 result = -1;
247 }
248 }
249 }
250
251 // INTENTIONAL HACK -- see above
252 ( void )dsPtr.release();
253 return result;
254}
255
256QString QgsSqliteUtils::quotedString( const QString &value )
257{
258 if ( value.isNull() )
259 return QStringLiteral( "NULL" );
260
261 QString v = value;
262 v.replace( '\'', QLatin1String( "''" ) );
263 return v.prepend( '\'' ).append( '\'' );
264}
265
266QString QgsSqliteUtils::quotedIdentifier( const QString &identifier )
267{
268 QString id( identifier );
269 id.replace( '\"', QLatin1String( "\"\"" ) );
270 return id.prepend( '\"' ).append( '\"' );
271}
272
273QString QgsSqliteUtils::quotedValue( const QVariant &value )
274{
275 if ( QgsVariantUtils::isNull( value ) )
276 return QStringLiteral( "NULL" );
277
278 switch ( value.userType() )
279 {
280 case QMetaType::Type::Int:
281 case QMetaType::Type::LongLong:
282 case QMetaType::Type::Double:
283 return value.toString();
284
285 case QMetaType::Type::Bool:
286 //SQLite has no boolean literals
287 return value.toBool() ? QStringLiteral( "1" ) : QStringLiteral( "0" );
288
289 default:
290 case QMetaType::Type::QString:
291 QString v = value.toString();
292 // https://www.sqlite.org/lang_expr.html :
293 // """A string constant is formed by enclosing the string in single quotes (').
294 // A single quote within the string can be encoded by putting two single quotes
295 // in a row - as in Pascal. C-style escapes using the backslash character are not supported because they are not standard SQL. """
296 return v.replace( '\'', QLatin1String( "''" ) ).prepend( '\'' ).append( '\'' );
297 }
298}
299
301{
302 return QStringList() << QStringLiteral( "ElementaryGeometries" ) << QStringLiteral( "SpatialIndex" )
303 << QStringLiteral( "geom_cols_ref_sys" ) << QStringLiteral( "geometry_columns" )
304 << QStringLiteral( "geometry_columns_auth" ) << QStringLiteral( "geometry_columns_field_infos" )
305 << QStringLiteral( "geometry_columns_statistics" ) << QStringLiteral( "geometry_columns_time" )
306 << QStringLiteral( "layer_params" ) << QStringLiteral( "layer_statistics" ) << QStringLiteral( "layer_sub_classes" )
307 << QStringLiteral( "layer_table_layout" ) << QStringLiteral( "pattern_bitmaps" ) << QStringLiteral( "project_defs" )
308 << QStringLiteral( "raster_pyramids" ) << QStringLiteral( "spatial_ref_sys" ) << QStringLiteral( "spatial_ref_sys_all" )
309 << QStringLiteral( "spatial_ref_sys_aux" ) << QStringLiteral( "spatialite_history" ) << QStringLiteral( "sql_statements_log" )
310 << QStringLiteral( "sqlite_sequence" ) << QStringLiteral( "sqlite_stat1" ) << QStringLiteral( "sqlite_stat2" )
311 << QStringLiteral( "symbol_bitmaps" ) << QStringLiteral( "tableprefix_metadata" ) << QStringLiteral( "tableprefix_rasters" )
312 << QStringLiteral( "vector_layers" ) << QStringLiteral( "vector_layers_auth" ) << QStringLiteral( "vector_layers_field_infos" )
313 << QStringLiteral( "vector_layers_statistics" ) << QStringLiteral( "views_geometry_columns" )
314 << QStringLiteral( "views_geometry_columns_auth" ) << QStringLiteral( "views_geometry_columns_field_infos" )
315 << QStringLiteral( "views_geometry_columns_statistics" ) << QStringLiteral( "views_layer_statistics" )
316 << QStringLiteral( "virts_geometry_columns" ) << QStringLiteral( "virts_geometry_columns_auth" )
317 << QStringLiteral( "virts_geometry_columns_field_infos" ) << QStringLiteral( "virts_geometry_columns_statistics" )
318 << QStringLiteral( "virts_layer_statistics" )
319 // Additional tables to be hidden
320 << QStringLiteral( "all_buckets_objects" ) << QStringLiteral( "byfoot" ) << QStringLiteral( "byfoot_data" )
321 << QStringLiteral( "data_licenses" ) << QStringLiteral( "ISO_metadata" ) << QStringLiteral( "ISO_metadata_reference" )
322 << QStringLiteral( "ISO_metadata_view" ) << QStringLiteral( "KNN2" ) << QStringLiteral( "KNN" )
323 << QStringLiteral( "networks" ) << QStringLiteral( "raster_coverages" ) << QStringLiteral( "raster_coverages_keyword" )
324 << QStringLiteral( "raster_coverages_ref_sys" ) << QStringLiteral( "raster_coverages_srid" )
325 << QStringLiteral( "rl2map_configurations" ) << QStringLiteral( "rl2map_configurations_view" )
326 // SE_ (Styled Elements)
327 << QStringLiteral( "SE_external_graphics" ) << QStringLiteral( "SE_external_graphics_view" )
328 << QStringLiteral( "SE_fonts" ) << QStringLiteral( "SE_fonts_view" ) << QStringLiteral( "SE_group_styles" )
329 << QStringLiteral( "SE_group_styles_view" ) << QStringLiteral( "SE_raster_styled_layers" )
330 << QStringLiteral( "SE_raster_styled_layers_view" ) << QStringLiteral( "SE_raster_styles" )
331 << QStringLiteral( "SE_raster_styles_view" ) << QStringLiteral( "SE_styled_group_refs" )
332 << QStringLiteral( "SE_styled_group_styles" ) << QStringLiteral( "SE_styled_groups" )
333 << QStringLiteral( "SE_styled_groups_view" ) << QStringLiteral( "SE_vector_styled_layers" )
334 << QStringLiteral( "SE_vector_styled_layers_view" ) << QStringLiteral( "SE_vector_styles" )
335 << QStringLiteral( "SE_vector_styles_view" )
336 << QStringLiteral( "sqlite_stat3" ) << QStringLiteral( "stored_procedures" ) << QStringLiteral( "stored_variables" )
337 << QStringLiteral( "topologies" ) << QStringLiteral( "vector_coverages" ) << QStringLiteral( "vector_coverages_keyword" )
338 << QStringLiteral( "vector_coverages_ref_sys" ) << QStringLiteral( "vector_coverages_srid" )
339 // WMS
340 << QStringLiteral( "wms_getcapabilities" ) << QStringLiteral( "wms_getmap" ) << QStringLiteral( "wms_ref_sys" )
341 << QStringLiteral( "wms_settings" );
342}
343
344QString qgs_sqlite3_mprintf( const char *format, ... )
345{
346 va_list ap;
347 va_start( ap, format );
348 char *c_str = sqlite3_vmprintf( format, ap );
349 va_end( ap );
350 QString res( QString::fromUtf8( c_str ) );
351 sqlite3_free( c_str );
352 return res;
353}
static QString quotedString(const QString &value)
Returns a quoted string value, surround by ' characters and with special characters correctly escaped...
static QStringList systemTables()
Returns a string list of SQLite (and spatialite) system tables.
static QString quotedIdentifier(const QString &identifier)
Returns a properly quoted version of identifier.
static QSet< QString > uniqueFields(sqlite3 *connection, const QString &tableName, QString &errorMessage)
Returns a list of field names for connection and tableName having a UNIQUE constraint,...
static long long nextSequenceValue(sqlite3 *connection, const QString &tableName, QString errorMessage)
Increments and returns an SQLITE sequence of the table "sqlite_sequence" for tableName and returns it...
static QString quotedValue(const QVariant &value)
Returns a properly quoted and escaped version of value for use in SQL strings.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
int open(const QString &path)
Opens the database at the specified file path.
QString errorMessage() const
Returns the most recent error message encountered by the database.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
int exec(const QString &sql, QString &errorMessage) const
Executes the sql command in the database.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
QString columnAsText(int column) const
Returns the column value from the current statement row as a string.
QString columnName(int column) const
Returns the name of column.
double columnAsDouble(int column) const
Gets column value from the current statement row as a double.
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
qlonglong columnAsInt64(int column) const
Gets column value from the current statement row as a long long integer (64 bits).
int columnCount() const
Gets the number of columns that this statement returns.
QByteArray columnAsBlob(int column) const
Returns the column value from the current statement row as raw byte array.
struct sqlite3 sqlite3
QString qgs_sqlite3_mprintf(const char *format,...)
Wraps sqlite3_mprintf() by automatically freeing the memory.
QString CORE_EXPORT qgs_sqlite3_mprintf(const char *format,...)
Wraps sqlite3_mprintf() by automatically freeing the memory.
void operator()(sqlite3 *database) const
Closes an sqlite database.
void operator()(sqlite3_stmt *statement) const
Finalizes an sqlite3 statement.