1 /***************************************************************************
2                     qgsvirtuallayersqlitehelper.cpp
3 begin                : December 2015
4 copyright            : (C) 2015 Hugo Mercier, Oslandia
5 email                : hugo dot mercier at oslandia dot com
6  ***************************************************************************/
7 
8 /***************************************************************************
9  *                                                                         *
10  *   This program is free software; you can redistribute it and/or modify  *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version.                                   *
14  *                                                                         *
15  ***************************************************************************/
16 
17 #include <QString>
18 #include <QVariant>
19 
20 #include <stdexcept>
21 
22 #include "qgsvirtuallayersqlitehelper.h"
23 #include "qgslogger.h"
24 
QgsScopedSqlite(const QString & path,bool withExtension)25 QgsScopedSqlite::QgsScopedSqlite( const QString &path, bool withExtension )
26 {
27   if ( withExtension )
28   {
29     // register a statically-linked function as extension
30     // for all future database connection
31     sqlite3_auto_extension( reinterpret_cast < void( * )() > ( qgsvlayerModuleInit ) );
32   }
33   int r;
34   r = sqlite3_open( path.toUtf8().constData(), &db_ );
35   if ( withExtension )
36   {
37     // reset the automatic extensions
38     sqlite3_reset_auto_extension();
39   }
40 
41   if ( r )
42   {
43     QString err = QStringLiteral( "%1 [%2]" ).arg( sqlite3_errmsg( db_ ), path );
44     QgsDebugMsg( err );
45     throw std::runtime_error( err.toUtf8().constData() );
46   }
47   // enable extended result codes
48   sqlite3_extended_result_codes( db_, 1 );
49 }
50 
QgsScopedSqlite(QgsScopedSqlite & other)51 QgsScopedSqlite::QgsScopedSqlite( QgsScopedSqlite &other )
52 {
53   db_ = other.db_;
54   other.db_ = nullptr;
55 }
56 
operator =(QgsScopedSqlite & other)57 QgsScopedSqlite &QgsScopedSqlite::operator=( QgsScopedSqlite &other )
58 {
59   reset( other.release() );
60   return *this;
61 }
62 
~QgsScopedSqlite()63 QgsScopedSqlite::~QgsScopedSqlite()
64 {
65   close_();
66 }
67 
get() const68 sqlite3 *QgsScopedSqlite::get() const { return db_; }
69 
interrupt()70 bool QgsScopedSqlite::interrupt()
71 {
72   bool rc = false;
73   if ( db_ )
74   {
75     sqlite3_interrupt( db_ );
76     rc = true;
77   }
78   return rc;
79 }
80 
release()81 sqlite3 *QgsScopedSqlite::release()
82 {
83   sqlite3 *pp = db_;
84   db_ = nullptr;
85   return pp;
86 }
87 
reset(sqlite3 * db)88 void QgsScopedSqlite::reset( sqlite3 *db )
89 {
90   close_();
91   db_ = db;
92 }
93 
close_()94 void QgsScopedSqlite::close_()
95 {
96   if ( db_ )
97     sqlite3_close( db_ );
98 }
99 
100 namespace Sqlite
101 {
Query(sqlite3 * db,const QString & q)102   Query::Query( sqlite3 *db, const QString &q )
103     : db_( db )
104     , nBind_( 1 )
105   {
106     QByteArray ba( q.toUtf8() );
107     int r = sqlite3_prepare_v2( db, ba.constData(), ba.size(), &stmt_, nullptr );
108     if ( r )
109     {
110       QString err = QStringLiteral( "Query preparation error on %1: %2" ).arg( q, sqlite3_errmsg( db ) );
111       throw std::runtime_error( err.toUtf8().constData() );
112     }
113   }
114 
~Query()115   Query::~Query()
116   {
117     sqlite3_finalize( stmt_ );
118   }
119 
step()120   int Query::step() { return sqlite3_step( stmt_ ); }
121 
bind(const QVariant & value,int idx)122   Query &Query::bind( const QVariant &value, int idx )
123   {
124     switch ( value.type() )
125     {
126       case QVariant::String:
127       {
128         QByteArray ba( value.toString().toUtf8() );
129         int r = sqlite3_bind_text( stmt_, idx, ba.constData(), ba.size(), SQLITE_TRANSIENT );
130         if ( r )
131         {
132           throw std::runtime_error( sqlite3_errmsg( db_ ) );
133         }
134         return *this;
135       }
136 
137       case QVariant::Double:
138       {
139         bool ok; // no reason to fail double conversion
140         double dbl = value.toDouble( &ok );
141         int r = sqlite3_bind_double( stmt_, idx, dbl );
142         if ( r )
143         {
144           throw std::runtime_error( sqlite3_errmsg( db_ ) );
145         }
146         return *this;
147       }
148 
149       default:
150         // Unsupported type
151         Q_ASSERT( false );
152     }
153 
154     return *this;
155   }
156 
bind(const QVariant & value)157   Query &Query::bind( const QVariant &value )
158   {
159     return bind( value, nBind_++ );
160   }
161 
exec(sqlite3 * db,const QString & sql)162   void Query::exec( sqlite3 *db, const QString &sql )
163   {
164     char *errMsg = nullptr;
165     int r = sqlite3_exec( db, sql.toUtf8().constData(), nullptr, nullptr, &errMsg );
166     if ( r )
167     {
168       QString err = QStringLiteral( "Query execution error on %1: %2 - %3" ).arg( sql ).arg( r ).arg( errMsg );
169       throw std::runtime_error( err.toUtf8().constData() );
170     }
171   }
172 
reset()173   void Query::reset()
174   {
175     int r = sqlite3_reset( stmt_ );
176     if ( r )
177     {
178       throw std::runtime_error( sqlite3_errmsg( db_ ) );
179     }
180     nBind_ = 1;
181   }
182 
columnCount() const183   int Query::columnCount() const
184   {
185     return sqlite3_column_count( stmt_ );
186   }
187 
columnName(int i) const188   QString Query::columnName( int i ) const
189   {
190     return QString( sqlite3_column_name( stmt_, i ) );
191   }
192 
columnType(int i) const193   int Query::columnType( int i ) const
194   {
195     return sqlite3_column_type( stmt_, i );
196   }
197 
columnInt(int i) const198   int Query::columnInt( int i ) const
199   {
200     return sqlite3_column_int( stmt_, i );
201   }
202 
columnInt64(int i) const203   qint64 Query::columnInt64( int i ) const
204   {
205     return sqlite3_column_int64( stmt_, i );
206   }
207 
columnDouble(int i) const208   double Query::columnDouble( int i ) const
209   {
210     return sqlite3_column_double( stmt_, i );
211   }
212 
columnText(int i) const213   QString Query::columnText( int i ) const
214   {
215     int size = sqlite3_column_bytes( stmt_, i );
216     const char *str = reinterpret_cast< const char * >( sqlite3_column_text( stmt_, i ) );
217     return QString::fromUtf8( str, size );
218   }
219 
columnBlob(int i) const220   QByteArray Query::columnBlob( int i ) const
221   {
222     int size = sqlite3_column_bytes( stmt_, i );
223     const char *data = reinterpret_cast< const char * >( sqlite3_column_blob( stmt_, i ) );
224     // data is not copied. QByteArray is just here a augmented pointer
225     return QByteArray::fromRawData( data, size );
226   }
227 
stmt()228   sqlite3_stmt *Query::stmt() { return stmt_; }
229 
230 }
231