Skip to content

Commit 5b72daa

Browse files
committedApr 8, 2014
[FEATURE] Oracle: local cache of the list of tables
Caching is done using SQLite database (could be shared also with other providers)
1 parent 35e4350 commit 5b72daa

9 files changed

+501
-17
lines changed
 

‎src/providers/oracle/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ SET(ORACLE_SRCS
99
qgsoracledataitems.cpp
1010
qgsoraclesourceselect.cpp
1111
qgsoraclenewconnection.cpp
12+
qgsoracletablecache.cpp
1213
qgsoracletablemodel.cpp
1314
qgsoraclecolumntypethread.cpp
1415
qgsoraclefeatureiterator.cpp
@@ -20,6 +21,7 @@ SET(ORACLE_MOC_HDRS
2021
qgsoracledataitems.h
2122
qgsoraclesourceselect.h
2223
qgsoraclenewconnection.h
24+
qgsoracletablecache.h
2325
qgsoracletablemodel.h
2426
qgsoraclecolumntypethread.h
2527
)

‎src/providers/oracle/qgsoraclecolumntypethread.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,17 @@ void QgsOracleColumnTypeThread::stop()
3737

3838
void QgsOracleColumnTypeThread::run()
3939
{
40+
mStopped = false;
41+
4042
QgsDataSourceURI uri = QgsOracleConn::connUri( mName );
4143
QgsOracleConn *conn = QgsOracleConn::connectDb( uri.connectionInfo() );
4244
if ( !conn )
4345
{
4446
QgsDebugMsg( "Connection failed - " + uri.connectionInfo() );
47+
mStopped = true;
4548
return;
4649
}
4750

48-
mStopped = false;
49-
5051
emit progressMessage( tr( "Retrieving tables of %1..." ).arg( mName ) );
5152
QVector<QgsOracleLayerProperty> layerProperties;
5253
if ( !conn->supportedLayers( layerProperties,
@@ -84,6 +85,10 @@ void QgsOracleColumnTypeThread::run()
8485
emit setLayerType( layerProperty );
8586
}
8687

88+
// store the list for later use (cache)
89+
if ( !mStopped )
90+
mLayerProperties = layerProperties;
91+
8792
emit progress( 0, 0 );
8893
emit progressMessage( tr( "Table retrieval finished." ) );
8994

‎src/providers/oracle/qgsoraclecolumntypethread.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ class QgsOracleColumnTypeThread : public QThread
3636
// by emitting the setLayerType() signal.
3737
virtual void run();
3838

39+
bool isStopped() const { return mStopped; }
40+
QVector<QgsOracleLayerProperty> layerProperties() const { return mLayerProperties; }
41+
QString connectionName() const { return mName; }
42+
bool useEstimatedMetadata() const { return mUseEstimatedMetadata; }
43+
bool allowGeometrylessTables() const { return mAllowGeometrylessTables; }
44+
3945
signals:
4046
void setLayerType( QgsOracleLayerProperty layerProperty );
4147
void progress( int, int );
@@ -51,7 +57,7 @@ class QgsOracleColumnTypeThread : public QThread
5157
bool mUseEstimatedMetadata;
5258
bool mAllowGeometrylessTables;
5359
bool mStopped;
54-
QList<QgsOracleLayerProperty> layerProperties;
60+
QVector<QgsOracleLayerProperty> mLayerProperties;
5561
};
5662

5763
#endif // QGSORACLECOLUMNTYPETHREAD_H

‎src/providers/oracle/qgsoracleconn.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ struct QgsOracleLayerProperty
4747

4848
int size() const { Q_ASSERT( types.size() == srids.size() ); return types.size(); }
4949

50+
bool operator==( const QgsOracleLayerProperty& other )
51+
{
52+
return types == other.types && srids == other.srids && ownerName == other.ownerName &&
53+
tableName == other.tableName && geometryColName == other.geometryColName &&
54+
isView == other.isView && pkCols == other.pkCols && sql == other.sql;
55+
}
56+
5057
QgsOracleLayerProperty at( int i ) const
5158
{
5259
QgsOracleLayerProperty property;

‎src/providers/oracle/qgsoraclenewconnection.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ class QgsOracleNewConnection : public QDialog, private Ui::QgsOracleNewConnectio
3232
QgsOracleNewConnection( QWidget *parent = 0, const QString& connName = QString::null, Qt::WindowFlags fl = QgisGui::ModalDialogFlags );
3333
//! Destructor
3434
~QgsOracleNewConnection();
35+
36+
QString originalConnName() const { return mOriginalConnName; }
37+
QString connName() const { return txtName->text(); }
38+
3539
public slots:
3640
void accept();
3741
void on_btnConnect_clicked();

‎src/providers/oracle/qgsoraclesourceselect.cpp

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ email : jef at norbit dot de
2323
#include "qgscontexthelp.h"
2424
#include "qgsoracleprovider.h"
2525
#include "qgsoraclenewconnection.h"
26+
#include "qgsoracletablecache.h"
2627
#include "qgsmanageconnectionsdialog.h"
2728
#include "qgsquerybuilder.h"
2829
#include "qgsdatasourceuri.h"
@@ -79,7 +80,8 @@ QWidget *QgsOracleSourceSelectDelegate::createEditor( QWidget *parent, const QSt
7980
if ( values.size() == 0 )
8081
{
8182
QString ownerName = index.sibling( index.row(), QgsOracleTableModel::dbtmOwner ).data( Qt::DisplayRole ).toString();
82-
values = mConn->pkCandidates( ownerName, tableName );
83+
if ( conn() )
84+
values = conn()->pkCandidates( ownerName, tableName );
8385
}
8486

8587
if ( values.size() == 0 )
@@ -198,8 +200,6 @@ QgsOracleSourceSelect::QgsOracleSourceSelect( QWidget *parent, Qt::WindowFlags f
198200
connect( mBuildQueryButton, SIGNAL( clicked() ), this, SLOT( buildQuery() ) );
199201
}
200202

201-
populateConnectionList();
202-
203203
mSearchModeComboBox->addItem( tr( "Wildcard" ) );
204204
mSearchModeComboBox->addItem( tr( "RegExp" ) );
205205

@@ -251,6 +251,8 @@ QgsOracleSourceSelect::QgsOracleSourceSelect( QWidget *parent, Qt::WindowFlags f
251251
mSearchModeComboBox->setVisible( false );
252252
mSearchModeLabel->setVisible( false );
253253
mSearchTableEdit->setVisible( false );
254+
255+
populateConnectionList();
254256
}
255257
/** Autoconnected SLOTS **/
256258
// Slot for adding a new connection
@@ -274,6 +276,8 @@ void QgsOracleSourceSelect::on_btnDelete_clicked()
274276

275277
QgsOracleConn::deleteConnection( cmbConnections->currentText() );
276278

279+
QgsOracleTableCache::removeFromCache( cmbConnections->currentText() );
280+
277281
populateConnectionList();
278282
emit connectionsChanged();
279283
}
@@ -304,6 +308,9 @@ void QgsOracleSourceSelect::on_btnEdit_clicked()
304308
QgsOracleNewConnection *nc = new QgsOracleNewConnection( this, cmbConnections->currentText() );
305309
if ( nc->exec() )
306310
{
311+
if ( nc->connName() != nc->originalConnName() )
312+
QgsOracleTableCache::renameConnectionInCache( nc->originalConnName(), nc->connName() );
313+
307314
populateConnectionList();
308315
emit connectionsChanged();
309316
}
@@ -321,6 +328,15 @@ void QgsOracleSourceSelect::on_cmbConnections_currentIndexChanged( const QString
321328
cbxAllowGeometrylessTables->blockSignals( true );
322329
cbxAllowGeometrylessTables->setChecked( QgsOracleConn::allowGeometrylessTables( text ) );
323330
cbxAllowGeometrylessTables->blockSignals( false );
331+
332+
// populate the table list
333+
QgsDataSourceURI uri = QgsOracleConn::connUri( cmbConnections->currentText() );
334+
mConnInfo = uri.connectionInfo();
335+
mUseEstimatedMetadata = uri.useEstimatedMetadata();
336+
337+
QgsDebugMsg( "Connection info: " + uri.connectionInfo() );
338+
339+
loadTableFromCache();
324340
}
325341

326342
void QgsOracleSourceSelect::on_cbxAllowGeometrylessTables_stateChanged( int )
@@ -452,6 +468,8 @@ void QgsOracleSourceSelect::populateConnectionList()
452468
btnDelete->setDisabled( cmbConnections->count() == 0 );
453469
btnConnect->setDisabled( cmbConnections->count() == 0 );
454470
cmbConnections->setDisabled( cmbConnections->count() == 0 );
471+
472+
on_cmbConnections_currentIndexChanged( cmbConnections->currentText() );
455473
}
456474

457475
// Slot for performing action when the Add button is clicked
@@ -498,18 +516,12 @@ void QgsOracleSourceSelect::on_btnConnect_clicked()
498516
QModelIndex rootItemIndex = mTableModel.indexFromItem( mTableModel.invisibleRootItem() );
499517
mTableModel.removeRows( 0, mTableModel.rowCount( rootItemIndex ), rootItemIndex );
500518

501-
// populate the table list
502-
QgsDataSourceURI uri = QgsOracleConn::connUri( cmbConnections->currentText() );
503-
504-
QgsDebugMsg( "Connection info: " + uri.connectionInfo() );
505-
506-
mConnInfo = uri.connectionInfo();
507-
mUseEstimatedMetadata = uri.useEstimatedMetadata();
508-
509519
QApplication::setOverrideCursor( Qt::BusyCursor );
510520

521+
QgsDataSourceURI uri = QgsOracleConn::connUri( cmbConnections->currentText() );
522+
511523
mIsConnected = true;
512-
mTablesTreeDelegate->setConn( QgsOracleConn::connectDb( uri.connectionInfo() ) );
524+
mTablesTreeDelegate->setConnectionInfo( uri.connectionInfo() );
513525

514526
mColumnTypeThread = new QgsOracleColumnTypeThread( cmbConnections->currentText(),
515527
mUseEstimatedMetadata,
@@ -544,8 +556,31 @@ void QgsOracleSourceSelect::finishList()
544556
mTablesTreeView->sortByColumn( QgsOracleTableModel::dbtmOwner, Qt::AscendingOrder );
545557
}
546558

559+
static QgsOracleTableCache::CacheFlags _currentFlags( QString connName, bool useEstimatedMetadata, bool allowGeometrylessTables )
560+
{
561+
QgsOracleTableCache::CacheFlags flags;
562+
if ( QgsOracleConn::geometryColumnsOnly( connName ) )
563+
flags |= QgsOracleTableCache::OnlyLookIntoMetadataTable;
564+
if ( QgsOracleConn::userTablesOnly( connName ) )
565+
flags |= QgsOracleTableCache::OnlyLookForUserTables;
566+
if ( QgsOracleConn::onlyExistingTypes( connName ) )
567+
flags |= QgsOracleTableCache::OnlyExistingGeometryTypes;
568+
if ( useEstimatedMetadata )
569+
flags |= QgsOracleTableCache::UseEstimatedTableMetadata;
570+
if ( allowGeometrylessTables )
571+
flags |= QgsOracleTableCache::AllowGeometrylessTables;
572+
return flags;
573+
}
574+
547575
void QgsOracleSourceSelect::columnThreadFinished()
548576
{
577+
if ( !mColumnTypeThread->isStopped() )
578+
{
579+
QString connName = mColumnTypeThread->connectionName();
580+
QgsOracleTableCache::CacheFlags flags = _currentFlags( connName, mColumnTypeThread->useEstimatedMetadata(), mColumnTypeThread->allowGeometrylessTables() );
581+
QgsOracleTableCache::saveToCache( connName, flags, mColumnTypeThread->layerProperties() );
582+
}
583+
549584
delete mColumnTypeThread;
550585
mColumnTypeThread = 0;
551586
btnConnect->setText( tr( "Connect" ) );
@@ -628,3 +663,26 @@ void QgsOracleSourceSelect::setSearchExpression( const QString& regexp )
628663
{
629664
Q_UNUSED( regexp );
630665
}
666+
667+
void QgsOracleSourceSelect::loadTableFromCache()
668+
{
669+
QModelIndex rootItemIndex = mTableModel.indexFromItem( mTableModel.invisibleRootItem() );
670+
mTableModel.removeRows( 0, mTableModel.rowCount( rootItemIndex ), rootItemIndex );
671+
672+
QString connName = cmbConnections->currentText();
673+
QVector<QgsOracleLayerProperty> layers;
674+
if ( !QgsOracleTableCache::loadFromCache( connName, _currentFlags( connName, mUseEstimatedMetadata, cbxAllowGeometrylessTables->isChecked() ), layers ) )
675+
return;
676+
677+
foreach ( const QgsOracleLayerProperty& layerProperty, layers )
678+
mTableModel.addTableEntry( layerProperty );
679+
680+
QApplication::setOverrideCursor( Qt::BusyCursor );
681+
682+
QgsDataSourceURI uri = QgsOracleConn::connUri( connName );
683+
684+
mIsConnected = true;
685+
mTablesTreeDelegate->setConnectionInfo( uri.connectionInfo() );
686+
687+
finishList();
688+
}

‎src/providers/oracle/qgsoraclesourceselect.h

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,22 @@ class QgsOracleSourceSelectDelegate : public QItemDelegate
5353
void setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const;
5454
void setEditorData( QWidget *editor, const QModelIndex &index ) const;
5555

56-
void setConn( QgsOracleConn *conn ) { if ( mConn ) mConn->disconnect(); mConn = conn; }
56+
void setConnectionInfo( const QString& connInfo ) { mConnInfo = connInfo; }
57+
58+
protected:
59+
void setConn( QgsOracleConn *conn ) const { if ( mConn ) mConn->disconnect(); mConn = conn; }
60+
61+
QgsOracleConn* conn() const
62+
{
63+
if ( !mConn )
64+
setConn( QgsOracleConn::connectDb( mConnInfo ) );
65+
return mConn;
66+
}
5767

5868
private:
59-
QgsOracleConn *mConn;
69+
QString mConnInfo;
70+
//! lazily initialized connection (to detect possible primary keys)
71+
mutable QgsOracleConn *mConn;
6072
};
6173

6274

@@ -136,6 +148,9 @@ class QgsOracleSourceSelect : public QDialog, private Ui::QgsDbSourceSelectBase
136148
//! Embedded mode, without 'Close'
137149
bool mEmbeddedMode;
138150

151+
//! try to load list of tables from local cache
152+
void loadTableFromCache();
153+
139154
// queue another query for the thread
140155
void addSearchGeometryColumn( QgsOracleLayerProperty layerProperty );
141156

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
/***************************************************************************
2+
qgsoraclestablecache.cpp
3+
-------------------
4+
begin : April 2014
5+
copyright : (C) 2014 by Martin Dobias
6+
email : wonder.sk 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 "qgsoracletablecache.h"
19+
20+
#include <sqlite3.h>
21+
22+
#include "qgsapplication.h"
23+
24+
#include <QDir>
25+
26+
27+
28+
static bool _executeSqliteStatement( sqlite3* db, const QString& sql )
29+
{
30+
sqlite3_stmt* stmt;
31+
if ( sqlite3_prepare_v2( db, sql.toUtf8().data(), -1, &stmt, NULL ) != SQLITE_OK )
32+
return false;
33+
34+
return sqlite3_step( stmt ) == SQLITE_DONE;
35+
}
36+
37+
static bool _removeFromCache( sqlite3* db, const QString& connName )
38+
{
39+
QString tblName = "oracle_" + connName;
40+
41+
QString sqlDeleteFromMeta = QString( "DELETE FROM meta_oracle WHERE conn = %1" ).arg( QgsOracleConn::quotedValue( connName ) );
42+
bool res1 = _executeSqliteStatement( db, sqlDeleteFromMeta );
43+
44+
QString sqlDropTable = QString( "DROP TABLE IF EXISTS %1" ).arg( QgsOracleConn::quotedIdentifier( tblName ) );
45+
bool res2 = _executeSqliteStatement( db, sqlDropTable );
46+
47+
return res1 && res2;
48+
}
49+
50+
51+
static sqlite3* _openCacheDatabase()
52+
{
53+
sqlite3* database;
54+
if ( sqlite3_open_v2( QgsOracleTableCache::cacheDatabaseFilename().toUtf8().data(), &database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0 ) != SQLITE_OK )
55+
return 0;
56+
57+
if ( !_executeSqliteStatement( database, "CREATE TABLE IF NOT EXISTS meta_oracle(conn text primary_key, flags int)" ) )
58+
{
59+
sqlite3_close( database );
60+
return 0;
61+
}
62+
63+
return database;
64+
}
65+
66+
67+
static bool _hasCache( sqlite3* db, const QString& connName, int flags = -1 ) // flags == -1 implies any flags
68+
{
69+
QString sqlCacheForConn = QString( "SELECT * FROM meta_oracle WHERE conn = %1" ).arg( QgsOracleConn::quotedValue( connName ) );
70+
if ( flags >= 0 )
71+
sqlCacheForConn.append( QString( " AND flags = %1" ).arg( flags ) );
72+
73+
char **results;
74+
int rows, columns;
75+
char *errMsg = NULL;
76+
bool res = sqlite3_get_table( db, sqlCacheForConn.toUtf8(), &results, &rows, &columns, &errMsg ) == SQLITE_OK;
77+
bool hasCache = ( res && rows == 1 );
78+
sqlite3_free_table( results );
79+
80+
return hasCache;
81+
}
82+
83+
84+
static bool _renameConnectionInCache( sqlite3* db, const QString& oldName, const QString& newName )
85+
{
86+
if ( !_hasCache( db, oldName ) )
87+
return true;
88+
89+
QString sql1 = QString( "ALTER TABLE %1 RENAME TO %2" ).arg( QgsOracleConn::quotedIdentifier( "oracle_" + oldName ) ).arg( QgsOracleConn::quotedIdentifier( "oracle_" + newName ) );
90+
bool res1 = _executeSqliteStatement( db, sql1 );
91+
92+
QString sql2 = QString( "UPDATE meta_oracle SET conn = %1 WHERE conn = %2" ).arg( QgsOracleConn::quotedIdentifier( newName ) ).arg( QgsOracleConn::quotedIdentifier( oldName ) );
93+
bool res2 = _executeSqliteStatement( db, sql2 );
94+
95+
return res1 && res2;
96+
}
97+
98+
99+
100+
QString QgsOracleTableCache::cacheDatabaseFilename()
101+
{
102+
return QgsApplication::qgisSettingsDirPath() + QDir::separator() + "data_sources_cache.db";
103+
}
104+
105+
bool QgsOracleTableCache::hasCache( const QString& connName, CacheFlags flags )
106+
{
107+
sqlite3* db = _openCacheDatabase();
108+
if ( !db )
109+
return false;
110+
111+
bool hasCache = _hasCache( db, connName, ( int ) flags );
112+
113+
sqlite3_close( db );
114+
return hasCache;
115+
}
116+
117+
118+
bool QgsOracleTableCache::saveToCache( const QString& connName, CacheFlags flags, const QVector<QgsOracleLayerProperty>& layers )
119+
{
120+
sqlite3* db = _openCacheDatabase();
121+
if ( !db )
122+
return false;
123+
124+
QString tblNameRaw = "oracle_" + connName;
125+
QString tblName = QgsOracleConn::quotedIdentifier( tblNameRaw );
126+
127+
// recreate the cache table
128+
129+
if ( !_removeFromCache( db, connName ) )
130+
{
131+
sqlite3_close( db );
132+
return false;
133+
}
134+
135+
QString sqlCreateTable = QString( "CREATE TABLE %1 (ownername text, tablename text, geometrycolname text, isview int, sql text, pkcols text, geomtypes text, geomsrids text)" ).arg( tblName );
136+
QString sqlInsertToMeta = QString( "INSERT INTO meta_oracle VALUES (%1, %2)" ).arg( QgsOracleConn::quotedValue( connName ) ).arg(( int ) flags );
137+
138+
bool res1 = _executeSqliteStatement( db, sqlCreateTable );
139+
bool res2 = _executeSqliteStatement( db, sqlInsertToMeta );
140+
if ( !res1 || !res2 )
141+
{
142+
sqlite3_close( db );
143+
return false;
144+
}
145+
146+
// insert data
147+
148+
_executeSqliteStatement( db, "BEGIN" );
149+
150+
QString sqlInsert = QString( "INSERT INTO %1 VALUES(?,?,?,?,?,?,?,?)" ).arg( tblName );
151+
sqlite3_stmt* stmtInsert;
152+
if ( sqlite3_prepare_v2( db, sqlInsert.toUtf8().data(), -1, &stmtInsert, 0 ) != SQLITE_OK )
153+
{
154+
sqlite3_close( db );
155+
return false;
156+
}
157+
158+
bool insertOk = true;
159+
foreach ( const QgsOracleLayerProperty& item, layers )
160+
{
161+
sqlite3_bind_text( stmtInsert, 1, item.ownerName.toUtf8().data(), -1, SQLITE_TRANSIENT );
162+
sqlite3_bind_text( stmtInsert, 2, item.tableName.toUtf8().data(), -1, SQLITE_TRANSIENT );
163+
sqlite3_bind_text( stmtInsert, 3, item.geometryColName.toUtf8().data(), -1, SQLITE_TRANSIENT );
164+
sqlite3_bind_int( stmtInsert, 4, item.isView );
165+
sqlite3_bind_text( stmtInsert, 5, item.sql.toUtf8().data(), -1, SQLITE_TRANSIENT );
166+
167+
sqlite3_bind_text( stmtInsert, 6, item.pkCols.join( "," ).toUtf8().data(), -1, SQLITE_TRANSIENT );
168+
169+
QStringList geomTypes;
170+
foreach ( QGis::WkbType geomType, item.types )
171+
geomTypes.append( QString::number( static_cast<ulong>( geomType ) ) );
172+
sqlite3_bind_text( stmtInsert, 7, geomTypes.join( "," ).toUtf8().data(), -1, SQLITE_TRANSIENT );
173+
174+
QStringList geomSrids;
175+
foreach ( int geomSrid, item.srids )
176+
geomSrids.append( QString::number( geomSrid ) );
177+
sqlite3_bind_text( stmtInsert, 8, geomSrids.join( "," ).toUtf8().data(), -1, SQLITE_TRANSIENT );
178+
179+
if ( sqlite3_step( stmtInsert ) != SQLITE_DONE )
180+
insertOk = false;
181+
182+
sqlite3_reset( stmtInsert );
183+
}
184+
185+
sqlite3_finalize( stmtInsert );
186+
187+
_executeSqliteStatement( db, "COMMIT" );
188+
189+
sqlite3_close( db );
190+
return insertOk;
191+
}
192+
193+
194+
bool QgsOracleTableCache::loadFromCache( const QString& connName, CacheFlags flags, QVector<QgsOracleLayerProperty>& layers )
195+
{
196+
sqlite3* db = _openCacheDatabase();
197+
if ( !db )
198+
return false;
199+
200+
if ( !_hasCache( db, connName, ( int ) flags ) )
201+
return false;
202+
203+
sqlite3_stmt* stmt;
204+
QString sql = QString( "SELECT * FROM %1" ).arg( QgsOracleConn::quotedIdentifier( "oracle_" + connName ) );
205+
if ( sqlite3_prepare_v2( db, sql.toUtf8().data(), -1, &stmt, NULL ) != SQLITE_OK )
206+
{
207+
sqlite3_close( db );
208+
return false;
209+
}
210+
211+
while ( sqlite3_step( stmt ) == SQLITE_ROW )
212+
{
213+
QgsOracleLayerProperty layer;
214+
layer.ownerName = QString::fromUtf8(( const char * ) sqlite3_column_text( stmt, 0 ) );
215+
layer.tableName = QString::fromUtf8(( const char * ) sqlite3_column_text( stmt, 1 ) );
216+
layer.geometryColName = QString::fromUtf8(( const char * ) sqlite3_column_text( stmt, 2 ) );
217+
layer.isView = sqlite3_column_int( stmt, 3 );
218+
layer.sql = QString::fromUtf8(( const char* ) sqlite3_column_text( stmt, 4 ) );
219+
220+
QString pkCols = QString::fromUtf8(( const char* ) sqlite3_column_text( stmt, 5 ) );
221+
layer.pkCols = pkCols.split( ",", QString::SkipEmptyParts );
222+
223+
QString geomTypes = QString::fromUtf8(( const char* ) sqlite3_column_text( stmt, 6 ) );
224+
foreach ( QString geomType, geomTypes.split( ",", QString::SkipEmptyParts ) )
225+
layer.types.append( static_cast<QGis::WkbType>( geomType.toInt() ) );
226+
227+
QString geomSrids = QString::fromUtf8(( const char* ) sqlite3_column_text( stmt, 7 ) );
228+
foreach ( QString geomSrid, geomSrids.split( ",", QString::SkipEmptyParts ) )
229+
layer.srids.append( geomSrid.toInt() );
230+
231+
layers.append( layer );
232+
}
233+
234+
sqlite3_finalize( stmt );
235+
236+
sqlite3_close( db );
237+
return true;
238+
}
239+
240+
241+
bool QgsOracleTableCache::removeFromCache( const QString& connName )
242+
{
243+
sqlite3* db = _openCacheDatabase();
244+
if ( !db )
245+
return false;
246+
247+
bool res = _removeFromCache( db, connName );
248+
249+
sqlite3_close( db );
250+
return res;
251+
}
252+
253+
254+
bool QgsOracleTableCache::renameConnectionInCache( const QString& oldName, const QString& newName )
255+
{
256+
sqlite3* db = _openCacheDatabase();
257+
if ( !db )
258+
return false;
259+
260+
bool res = _renameConnectionInCache( db, oldName, newName );
261+
262+
sqlite3_close( db );
263+
return res;
264+
}
265+
266+
267+
#if 0
268+
// testing routine - ideally it should be a unit test
269+
void _testTableCache()
270+
{
271+
QString connName = "local";
272+
QVector<QgsOracleLayerProperty> layers;
273+
274+
// fetch
275+
276+
QgsDataSourceURI uri = QgsOracleConn::connUri( connName );
277+
QgsOracleConn* c = QgsOracleConn::connectDb( uri.connectionInfo() );
278+
if ( !c )
279+
return;
280+
281+
c->supportedLayers( layers, true );
282+
283+
bool useEstimated = true;
284+
bool onlyExisting = QgsOracleConn::onlyExistingTypes( connName );
285+
286+
for ( QVector<QgsOracleLayerProperty>::iterator it = layers.begin(), end = layers.end(); it != end; ++it )
287+
{
288+
QgsOracleLayerProperty &layerProperty = *it;
289+
c->retrieveLayerTypes( layerProperty, useEstimated, onlyExisting );
290+
}
291+
292+
c->disconnect();
293+
294+
// save
295+
296+
QgsOracleTableCache::CacheFlags flags = QgsOracleTableCache::UseEstimatedTableMetadata | QgsOracleTableCache::OnlyExistingGeometryTypes;
297+
QgsOracleTableCache::saveToCache( connName, flags, layers );
298+
299+
// load
300+
301+
QVector<QgsOracleLayerProperty> layersLoaded;
302+
QgsOracleTableCache::loadFromCache( connName, flags, layersLoaded );
303+
304+
// compare
305+
306+
foreach ( const QgsOracleLayerProperty& item, layers )
307+
qDebug( "== %s %s", item.tableName.toAscii().data(), item.geometryColName.toAscii().data() );
308+
309+
foreach ( const QgsOracleLayerProperty& item, layersLoaded )
310+
qDebug( "++ %s %s", item.tableName.toAscii().data(), item.geometryColName.toAscii().data() );
311+
312+
Q_ASSERT( layers == layersLoaded );
313+
}
314+
#endif
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/***************************************************************************
2+
qgsoraclestablecache.h
3+
-------------------
4+
begin : April 2014
5+
copyright : (C) 2014 by Martin Dobias
6+
email : wonder.sk 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+
#ifndef QGSORACLETABLECACHE_H
19+
#define QGSORACLETABLECACHE_H
20+
21+
#include <sqlite3.h>
22+
23+
#include <QFlags>
24+
25+
#include "qgsoracleconn.h"
26+
27+
/**
28+
* This class contains routines for local caching of listing of layers, so the add Oracle
29+
* layer dialog does not need to discover the tables every time the user wants to add a layer.
30+
*
31+
* The cached entries are stored in SQLite database in QGIS user directory (usually ~/.qgis2).
32+
* The database can be used for other data sources, too. Each saved connection's list is stored
33+
* in one table "oracle_xyz" (where xyz is the name of the connection). There is one meta table
34+
* "meta_oracle" which has a list of cached connections and the combination of flags used for
35+
* the list. The cached entries are used only in case the flags are exactly the same.
36+
*
37+
*/
38+
class QgsOracleTableCache
39+
{
40+
public:
41+
42+
enum CacheFlag
43+
{
44+
OnlyLookIntoMetadataTable = 0x01,
45+
OnlyLookForUserTables = 0x02,
46+
UseEstimatedTableMetadata = 0x04,
47+
OnlyExistingGeometryTypes = 0x08,
48+
AllowGeometrylessTables = 0x10
49+
};
50+
Q_DECLARE_FLAGS( CacheFlags, CacheFlag )
51+
52+
//! Return name of the file used for the cached entries
53+
static QString cacheDatabaseFilename();
54+
55+
//! check whether the given connection is cached (with equal flags)
56+
static bool hasCache( const QString& connName, CacheFlags flags );
57+
58+
//! Store the given list of entries (layers) into the cache. Returns true on success.
59+
static bool saveToCache( const QString& connName, CacheFlags flags, const QVector<QgsOracleLayerProperty>& layers );
60+
61+
//! Try to load cached entries for the given connection and its flags. Returns true on success.
62+
static bool loadFromCache( const QString& connName, CacheFlags flags, QVector<QgsOracleLayerProperty>& layers );
63+
64+
//! Remove cached entries for given connection. Returns true on success.
65+
static bool removeFromCache( const QString& connName );
66+
67+
//! Rename cached connection (useful when an existing connection gets renamed). Returns true on success.
68+
static bool renameConnectionInCache( const QString& oldName, const QString& newName );
69+
};
70+
71+
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsOracleTableCache::CacheFlags )
72+
73+
#endif // QGSORACLETABLECACHE_H

0 commit comments

Comments
 (0)
Please sign in to comment.