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