Skip to content

Commit

Permalink
[FEATURE][oracle] Allow restricting table list for a connection
Browse files Browse the repository at this point in the history
to a specific schema

This allows a schema to be set in the connection properties for
an oracle db connection. If set, only tables within that schema
will be scanned and listed for the connection.

Previously the option existed to restrict the scan to tables
which belong to the user, but this option does not support
the use case where a connection must access tables from a different
user, and the default "scan everything" setting is too expensive
(since it often takes multiple minutes to perform, especially
when geometryless tables are shown).

Sponsored by Open Spatial (http://www.openspatial.com)
  • Loading branch information
nyalldawson committed Apr 22, 2018
1 parent 31faa11 commit 2273dfa
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 120 deletions.
2 changes: 2 additions & 0 deletions src/gui/qgsmanageconnectionsdialog.cpp
Expand Up @@ -548,6 +548,7 @@ QDomDocument QgsManageConnectionsDialog::saveOracleConnections( const QStringLis
el.setAttribute( QStringLiteral( "database" ), settings.value( path + "/database", "" ).toString() );
el.setAttribute( QStringLiteral( "dboptions" ), settings.value( path + "/dboptions", "" ).toString() );
el.setAttribute( QStringLiteral( "dbworkspace" ), settings.value( path + "/dbworkspace", "" ).toString() );
el.setAttribute( QStringLiteral( "schema" ), settings.value( path + "/schema", QString() ).toString() );
el.setAttribute( QStringLiteral( "estimatedMetadata" ), settings.value( path + "/estimatedMetadata", "0" ).toString() );
el.setAttribute( QStringLiteral( "userTablesOnly" ), settings.value( path + "/userTablesOnly", "0" ).toString() );
el.setAttribute( QStringLiteral( "geometryColumnsOnly" ), settings.value( path + "/geometryColumnsOnly", "0" ).toString() );
Expand Down Expand Up @@ -1088,6 +1089,7 @@ void QgsManageConnectionsDialog::loadOracleConnections( const QDomDocument &doc,
settings.setValue( QStringLiteral( "/database" ), child.attribute( QStringLiteral( "database" ) ) );
settings.setValue( QStringLiteral( "/dboptions" ), child.attribute( QStringLiteral( "dboptions" ) ) );
settings.setValue( QStringLiteral( "/dbworkspace" ), child.attribute( QStringLiteral( "dbworkspace" ) ) );
settings.setValue( QStringLiteral( "/schema" ), child.attribute( QStringLiteral( "schema" ) ) );
settings.setValue( QStringLiteral( "/estimatedMetadata" ), child.attribute( QStringLiteral( "estimatedMetadata" ) ) );
settings.setValue( QStringLiteral( "/userTablesOnly" ), child.attribute( QStringLiteral( "userTablesOnly" ) ) );
settings.setValue( QStringLiteral( "/geometryColumnsOnly" ), child.attribute( QStringLiteral( "geometryColumnsOnly" ) ) );
Expand Down
4 changes: 3 additions & 1 deletion src/providers/oracle/qgsoraclecolumntypethread.cpp
Expand Up @@ -21,9 +21,10 @@ email : jef at norbit dot de

#include <QMetaType>

QgsOracleColumnTypeThread::QgsOracleColumnTypeThread( const QString &name, bool useEstimatedMetadata, bool allowGeometrylessTables )
QgsOracleColumnTypeThread::QgsOracleColumnTypeThread( const QString &name, const QString &limitToSchema, bool useEstimatedMetadata, bool allowGeometrylessTables )
: QThread()
, mName( name )
, mSchema( limitToSchema )
, mUseEstimatedMetadata( useEstimatedMetadata )
, mAllowGeometrylessTables( allowGeometrylessTables )
, mStopped( false )
Expand Down Expand Up @@ -52,6 +53,7 @@ void QgsOracleColumnTypeThread::run()
emit progressMessage( tr( "Retrieving tables of %1…" ).arg( mName ) );
QVector<QgsOracleLayerProperty> layerProperties;
if ( !conn->supportedLayers( layerProperties,
mSchema,
QgsOracleConn::geometryColumnsOnly( mName ),
QgsOracleConn::userTablesOnly( mName ),
mAllowGeometrylessTables ) ||
Expand Down
10 changes: 10 additions & 0 deletions src/providers/oracle/qgsoraclecolumntypethread.h
Expand Up @@ -28,7 +28,16 @@ class QgsOracleColumnTypeThread : public QThread
{
Q_OBJECT
public:

/**
*
* \param connName
* \param limitToSchema If specified, only tables from this schema will be scanned
* \param useEstimatedMetaData
* \param allowGeometrylessTables
*/
QgsOracleColumnTypeThread( const QString &connName,
const QString &limitToSchema,
bool useEstimatedMetaData,
bool allowGeometrylessTables );

Expand All @@ -54,6 +63,7 @@ class QgsOracleColumnTypeThread : public QThread
QgsOracleColumnTypeThread() = default;

QString mName;
QString mSchema;
bool mUseEstimatedMetadata = false;
bool mAllowGeometrylessTables = false;
bool mStopped = false;
Expand Down
20 changes: 14 additions & 6 deletions src/providers/oracle/qgsoracleconn.cpp
Expand Up @@ -240,7 +240,7 @@ QStringList QgsOracleConn::pkCandidates( const QString &ownerName, const QString
return cols;
}

bool QgsOracleConn::tableInfo( bool geometryColumnsOnly, bool userTablesOnly, bool allowGeometrylessTables )
bool QgsOracleConn::tableInfo( const QString &schema, bool geometryColumnsOnly, bool userTablesOnly, bool allowGeometrylessTables )
{
QgsDebugMsgLevel( QStringLiteral( "Entering." ), 4 );

Expand All @@ -258,16 +258,18 @@ bool QgsOracleConn::tableInfo( bool geometryColumnsOnly, bool userTablesOnly, bo
.arg( geometryColumnsOnly ? QStringLiteral( "c.srid" ) : QStringLiteral( "NULL AS srid" ) )
.arg( prefix )
.arg( geometryColumnsOnly ? QStringLiteral( "sdo_geom_metadata" ) : QStringLiteral( "tab_columns" ) )
.arg( userTablesOnly ? QStringLiteral( "" ) : QStringLiteral( " AND c.owner='xxxxSCHEMAxxxx'" ) )
.arg( userTablesOnly ? QString() : QStringLiteral( " AND c.owner=%1" ).arg( schema.isEmpty() ? QStringLiteral( "o.owner" ) : quotedValue( schema ) ) )
.arg( geometryColumnsOnly ? QString() : QStringLiteral( " WHERE c.data_type='SDO_GEOMETRY'" ) );

if ( allowGeometrylessTables )
{

// also here!
sql += QStringLiteral( " UNION SELECT %1,object_name,NULL AS column_name,NULL AS srid,object_type AS type"
" FROM %2_objects c WHERE c.object_type IN ('TABLE','VIEW','SYNONYM')" )
.arg( owner, prefix );
" FROM %2_objects c WHERE c.object_type IN ('TABLE','VIEW','SYNONYM') %3" )
.arg( owner,
prefix,
userTablesOnly || schema.isEmpty() ? QString() : QStringLiteral( " AND c.owner=%1" ).arg( quotedValue( schema ) ) );
}

// sql = "SELECT * FROM (" + sql + ")";
Expand Down Expand Up @@ -302,10 +304,10 @@ bool QgsOracleConn::tableInfo( bool geometryColumnsOnly, bool userTablesOnly, bo
return true;
}

bool QgsOracleConn::supportedLayers( QVector<QgsOracleLayerProperty> &layers, bool geometryTablesOnly, bool userTablesOnly, bool allowGeometrylessTables )
bool QgsOracleConn::supportedLayers( QVector<QgsOracleLayerProperty> &layers, const QString &limitToSchema, bool geometryTablesOnly, bool userTablesOnly, bool allowGeometrylessTables )
{
// Get the list of supported tables
if ( !tableInfo( geometryTablesOnly, userTablesOnly, allowGeometrylessTables ) )
if ( !tableInfo( limitToSchema, geometryTablesOnly, userTablesOnly, allowGeometrylessTables ) )
{
QgsMessageLog::logMessage( tr( "Unable to get list of spatially enabled tables from the database" ), tr( "Oracle" ) );
return false;
Expand Down Expand Up @@ -786,6 +788,12 @@ bool QgsOracleConn::userTablesOnly( const QString &connName )
return settings.value( "/Oracle/connections/" + connName + "/userTablesOnly", false ).toBool();
}

QString QgsOracleConn::restrictToSchema( const QString &connName )
{
QgsSettings settings;
return settings.value( "/Oracle/connections/" + connName + "/schema" ).toString();
}

bool QgsOracleConn::geometryColumnsOnly( const QString &connName )
{
QgsSettings settings;
Expand Down
18 changes: 15 additions & 3 deletions src/providers/oracle/qgsoracleconn.h
Expand Up @@ -127,16 +127,27 @@ class QgsOracleConn : public QObject
*/
static QString quotedValue( const QVariant &value, QVariant::Type type = QVariant::Invalid );

//! Get the list of supported layers
/**
* Get the list of supported layers.
*
* If \a limitToSchema is specified, than only layers from the matching schema will be
* returned.
*
*/
bool supportedLayers( QVector<QgsOracleLayerProperty> &layers,
const QString &limitToSchema,
bool geometryTablesOnly,
bool userTablesOnly = true,
bool allowGeometrylessTables = false );

void retrieveLayerTypes( QgsOracleLayerProperty &layerProperty, bool useEstimatedMetadata, bool onlyExistingTypes );

//! Gets information about the spatial tables
bool tableInfo( bool geometryTablesOnly, bool userTablesOnly, bool allowGeometrylessTables );
/**
* Gets information about the spatial tables.
*
* If \a schema is specified, only tables from this schema will be retrieved.
*/
bool tableInfo( const QString &schema, bool geometryTablesOnly, bool userTablesOnly, bool allowGeometrylessTables );

//! Get primary key candidates (all int4 columns)
QStringList pkCandidates( const QString &ownerName, const QString &viewName );
Expand All @@ -163,6 +174,7 @@ class QgsOracleConn : public QObject
static void setSelectedConnection( const QString &connName );
static QgsDataSourceUri connUri( const QString &connName );
static bool userTablesOnly( const QString &connName );
static QString restrictToSchema( const QString &connName );
static bool geometryColumnsOnly( const QString &connName );
static bool allowGeometrylessTables( const QString &connName );
static bool estimatedMetadata( const QString &connName );
Expand Down
1 change: 1 addition & 0 deletions src/providers/oracle/qgsoracledataitems.cpp
Expand Up @@ -91,6 +91,7 @@ QVector<QgsDataItem *> QgsOracleConnectionItem::createChildren()
if ( !mColumnTypeThread )
{
mColumnTypeThread = new QgsOracleColumnTypeThread( mName,
QgsOracleConn::restrictToSchema( mName ),
/* useEstimatedMetadata */ true,
QgsOracleConn::allowGeometrylessTables( mName ) );

Expand Down
5 changes: 5 additions & 0 deletions src/providers/oracle/qgsoraclenewconnection.cpp
Expand Up @@ -30,6 +30,9 @@ QgsOracleNewConnection::QgsOracleNewConnection( QWidget *parent, const QString &
, mOriginalConnName( connName )
{
setupUi( this );

txtSchema->setShowClearButton( true );

connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsOracleNewConnection::showHelp );
connect( btnConnect, &QPushButton::clicked, this, &QgsOracleNewConnection::testConnection );

Expand All @@ -50,6 +53,7 @@ QgsOracleNewConnection::QgsOracleNewConnection( QWidget *parent, const QString &
txtPort->setText( port );
txtOptions->setText( settings.value( key + QStringLiteral( "/dboptions" ) ).toString() );
txtWorkspace->setText( settings.value( key + QStringLiteral( "/dbworkspace" ) ).toString() );
txtSchema->setText( settings.value( key + QStringLiteral( "/schema" ) ).toString() );
cb_userTablesOnly->setChecked( settings.value( key + QStringLiteral( "/userTablesOnly" ), false ).toBool() );
cb_geometryColumnsOnly->setChecked( settings.value( key + QStringLiteral( "/geometryColumnsOnly" ), true ).toBool() );
cb_allowGeometrylessTables->setChecked( settings.value( key + QStringLiteral( "/allowGeometrylessTables" ), false ).toBool() );
Expand Down Expand Up @@ -136,6 +140,7 @@ void QgsOracleNewConnection::accept()
settings.setValue( baseKey + QStringLiteral( "/savePassword" ), chkStorePassword->isChecked() ? QStringLiteral( "true" ) : QStringLiteral( "false" ) );
settings.setValue( baseKey + QStringLiteral( "/dboptions" ), txtOptions->text() );
settings.setValue( baseKey + QStringLiteral( "/dbworkspace" ), txtWorkspace->text() );
settings.setValue( baseKey + QStringLiteral( "/schema" ), txtSchema->text() );

QDialog::accept();
}
Expand Down
3 changes: 2 additions & 1 deletion src/providers/oracle/qgsoraclesourceselect.cpp
Expand Up @@ -44,7 +44,7 @@ QWidget *QgsOracleSourceSelectDelegate::createEditor( QWidget *parent, const QSt

QString tableName = index.sibling( index.row(), QgsOracleTableModel::DbtmTable ).data( Qt::DisplayRole ).toString();
if ( tableName.isEmpty() )
return 0;
return nullptr;

if ( index.column() == QgsOracleTableModel::DbtmSql )
{
Expand Down Expand Up @@ -515,6 +515,7 @@ void QgsOracleSourceSelect::on_btnConnect_clicked()
mTablesTreeDelegate->setConnectionInfo( uri );

mColumnTypeThread = new QgsOracleColumnTypeThread( cmbConnections->currentText(),
QgsOracleConn::restrictToSchema( cmbConnections->currentText() ),
uri.useEstimatedMetadata(),
cbxAllowGeometrylessTables->isChecked() );

Expand Down

0 comments on commit 2273dfa

Please sign in to comment.