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 : }
|