mirror of
https://github.com/qgis/QGIS.git
synced 2025-03-12 00:02:25 -04:00
- Remove use of surfaceType variable that is never read - Avoid self assignment of nRings variable
3786 lines
113 KiB
C++
3786 lines
113 KiB
C++
/***************************************************************************
|
|
qgsoracleprovider.cpp - QGIS data provider for Oracle layers
|
|
-------------------
|
|
begin : August 2012
|
|
copyright : (C) 2012 by Juergen E. Fischer
|
|
email : jef at norbit dot de
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "qgsfeature.h"
|
|
#include "qgsfields.h"
|
|
#include "qgsgeometry.h"
|
|
#include "qgsmessageoutput.h"
|
|
#include "qgsmessagelog.h"
|
|
#include "qgsrectangle.h"
|
|
#include "qgscoordinatereferencesystem.h"
|
|
#include "qgsvectorlayerexporter.h"
|
|
#include "qgslogger.h"
|
|
|
|
#include "qgsoracleprovider.h"
|
|
#include "qgsoracletablemodel.h"
|
|
#include "qgsoracledataitems.h"
|
|
#include "qgsoraclefeatureiterator.h"
|
|
#include "qgsoracleconnpool.h"
|
|
#include "qgsoracletransaction.h"
|
|
|
|
#ifdef HAVE_GUI
|
|
#include "qgsoraclesourceselect.h"
|
|
#include "qgssourceselectprovider.h"
|
|
#endif
|
|
|
|
#include <QSqlRecord>
|
|
#include <QSqlField>
|
|
#include <QMessageBox>
|
|
|
|
#include "ocispatial/wkbptr.h"
|
|
|
|
const QString ORACLE_KEY = "oracle";
|
|
const QString ORACLE_DESCRIPTION = "Oracle data provider";
|
|
|
|
QgsOracleProvider::QgsOracleProvider( QString const &uri, const ProviderOptions &options )
|
|
: QgsVectorDataProvider( uri, options )
|
|
, mValid( false )
|
|
, mIsQuery( false )
|
|
, mPrimaryKeyType( PktUnknown )
|
|
, mFeaturesCounted( -1 )
|
|
, mDetectedGeomType( QgsWkbTypes::Unknown )
|
|
, mRequestedGeomType( QgsWkbTypes::Unknown )
|
|
, mHasSpatialIndex( false )
|
|
, mSpatialIndexName( QString() )
|
|
, mShared( new QgsOracleSharedData )
|
|
{
|
|
static int geomMetaType = -1;
|
|
if ( geomMetaType < 0 )
|
|
geomMetaType = qRegisterMetaType<QOCISpatialGeometry>();
|
|
|
|
QgsDebugMsg( QStringLiteral( "URI: %1 " ).arg( uri ) );
|
|
|
|
mUri = QgsDataSourceUri( uri );
|
|
|
|
// populate members from the uri structure
|
|
mOwnerName = mUri.schema();
|
|
mTableName = mUri.table();
|
|
mGeometryColumn = mUri.geometryColumn();
|
|
mSqlWhereClause = mUri.sql();
|
|
mSrid = mUri.srid().toInt();
|
|
mRequestedGeomType = mUri.wkbType();
|
|
mUseEstimatedMetadata = mUri.useEstimatedMetadata();
|
|
mIncludeGeoAttributes = mUri.hasParam( "includegeoattributes" ) ? mUri.param( "includegeoattributes" ) == "true" : false;
|
|
|
|
QgsOracleConn *conn = connectionRO();
|
|
if ( !conn )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( mOwnerName.isEmpty() && mTableName.startsWith( "(" ) && mTableName.endsWith( ")" ) )
|
|
{
|
|
mIsQuery = true;
|
|
mQuery = mTableName;
|
|
mTableName = "";
|
|
}
|
|
else
|
|
{
|
|
mIsQuery = false;
|
|
|
|
if ( mOwnerName.isEmpty() )
|
|
{
|
|
mOwnerName = conn->currentUser();
|
|
}
|
|
|
|
if ( !mOwnerName.isEmpty() )
|
|
{
|
|
mQuery += quotedIdentifier( mOwnerName ) + ".";
|
|
}
|
|
|
|
if ( !mTableName.isEmpty() )
|
|
{
|
|
mQuery += quotedIdentifier( mTableName );
|
|
}
|
|
}
|
|
|
|
QgsDebugMsg( QStringLiteral( "Connection info is %1" ).arg( mUri.connectionInfo( false ) ) );
|
|
QgsDebugMsg( QStringLiteral( "Geometry column is: %1" ).arg( mGeometryColumn ) );
|
|
QgsDebugMsg( QStringLiteral( "Owner is: %1" ).arg( mOwnerName ) );
|
|
QgsDebugMsg( QStringLiteral( "Table name is: %1" ).arg( mTableName ) );
|
|
QgsDebugMsg( QStringLiteral( "Query is: %1" ).arg( mQuery ) );
|
|
QgsDebugMsg( QStringLiteral( "Where clause is: %1" ).arg( mSqlWhereClause ) );
|
|
QgsDebugMsg( QStringLiteral( "SRID is: %1" ).arg( mSrid ) );
|
|
QgsDebugMsg( QStringLiteral( "Using estimated metadata: %1" ).arg( mUseEstimatedMetadata ? "yes" : "no" ) );
|
|
|
|
// no table/query passed, the provider could be used to get tables
|
|
if ( mQuery.isEmpty() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( !hasSufficientPermsAndCapabilities() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// get geometry details
|
|
if ( !getGeometryDetails() ) // gets srid, geometry and data type
|
|
{
|
|
// the table is not a geometry table
|
|
mValid = false;
|
|
disconnectDb();
|
|
return;
|
|
}
|
|
|
|
mLayerExtent.setMinimal();
|
|
|
|
// get always generated key
|
|
if ( !determineAlwaysGeneratedKeys() )
|
|
{
|
|
mValid = false;
|
|
disconnectDb();
|
|
return;
|
|
}
|
|
|
|
// set the primary key
|
|
if ( !determinePrimaryKey() )
|
|
{
|
|
mValid = false;
|
|
disconnectDb();
|
|
return;
|
|
}
|
|
|
|
//fill type names into sets
|
|
setNativeTypes( QList<NativeType>()
|
|
// integer types
|
|
<< QgsVectorDataProvider::NativeType( tr( "Whole number" ), "number(10,0)", QVariant::Int )
|
|
<< QgsVectorDataProvider::NativeType( tr( "Whole big number" ), "number(20,0)", QVariant::LongLong )
|
|
<< QgsVectorDataProvider::NativeType( tr( "Decimal number (numeric)" ), "number", QVariant::Double, 1, 38, 0, 38 )
|
|
<< QgsVectorDataProvider::NativeType( tr( "Decimal number (decimal)" ), "double precision", QVariant::Double )
|
|
|
|
// floating point
|
|
<< QgsVectorDataProvider::NativeType( tr( "Decimal number (real)" ), "binary_float", QVariant::Double )
|
|
<< QgsVectorDataProvider::NativeType( tr( "Decimal number (double)" ), "binary_double", QVariant::Double )
|
|
|
|
// string types
|
|
<< QgsVectorDataProvider::NativeType( tr( "Text, fixed length (char)" ), "CHAR", QVariant::String, 1, 255 )
|
|
<< QgsVectorDataProvider::NativeType( tr( "Text, limited variable length (varchar2)" ), "VARCHAR2", QVariant::String, 1, 255 )
|
|
<< QgsVectorDataProvider::NativeType( tr( "Text, unlimited length (long)" ), "LONG", QVariant::String )
|
|
|
|
// date type
|
|
<< QgsVectorDataProvider::NativeType( tr( "Date" ), "DATE", QVariant::Date, 38, 38, 0, 0 )
|
|
<< QgsVectorDataProvider::NativeType( tr( "Date & Time" ), "TIMESTAMP(6)", QVariant::DateTime, 38, 38, 6, 6 )
|
|
);
|
|
|
|
QString key;
|
|
switch ( mPrimaryKeyType )
|
|
{
|
|
case PktRowId:
|
|
key = "ROWID";
|
|
break;
|
|
|
|
case PktInt:
|
|
case PktFidMap:
|
|
{
|
|
Q_ASSERT( mPrimaryKeyType != PktInt || mPrimaryKeyAttrs.size() == 1 );
|
|
|
|
QString delim;
|
|
const auto constMPrimaryKeyAttrs = mPrimaryKeyAttrs;
|
|
for ( int idx : constMPrimaryKeyAttrs )
|
|
{
|
|
Q_ASSERT( idx >= 0 && idx < mAttributeFields.size() );
|
|
key += delim + mAttributeFields.at( idx ).name();
|
|
delim = ",";
|
|
}
|
|
}
|
|
break;
|
|
case PktUnknown:
|
|
mValid = false;
|
|
break;
|
|
}
|
|
|
|
if ( mValid )
|
|
{
|
|
mUri.setKeyColumn( key );
|
|
setDataSourceUri( mUri.uri( false ) );
|
|
}
|
|
else
|
|
{
|
|
disconnectDb();
|
|
}
|
|
}
|
|
|
|
QgsOracleProvider::~QgsOracleProvider()
|
|
{
|
|
QgsDebugMsg( QStringLiteral( "deconstructing." ) );
|
|
|
|
disconnectDb();
|
|
}
|
|
|
|
QString QgsOracleProvider::getWorkspace() const
|
|
{
|
|
return mUri.param( "dbworkspace" );
|
|
}
|
|
|
|
void QgsOracleProvider::setWorkspace( const QString &workspace )
|
|
{
|
|
QgsDataSourceUri prevUri( mUri );
|
|
|
|
disconnectDb();
|
|
|
|
if ( workspace.isEmpty() )
|
|
mUri.removeParam( "dbworkspace" );
|
|
else
|
|
mUri.setParam( "dbworkspace", workspace );
|
|
|
|
QgsOracleConn *conn = connectionRO();
|
|
if ( !conn )
|
|
{
|
|
mUri = prevUri;
|
|
QgsDebugMsg( QStringLiteral( "restoring previous uri:%1" ).arg( mUri.uri( false ) ) );
|
|
conn = connectionRO();
|
|
}
|
|
else
|
|
{
|
|
setDataSourceUri( mUri.uri( false ) );
|
|
}
|
|
}
|
|
|
|
QgsAbstractFeatureSource *QgsOracleProvider::featureSource() const
|
|
{
|
|
return new QgsOracleFeatureSource( this );
|
|
}
|
|
|
|
void QgsOracleProvider::disconnectDb()
|
|
{
|
|
QgsOracleConn *conn = QgsOracleConn::connectDb( mUri, false );
|
|
if ( conn )
|
|
conn->disconnect();
|
|
}
|
|
|
|
QgsOracleConn *QgsOracleProvider::connectionRW()
|
|
{
|
|
return mTransaction ? mTransaction->connection() : QgsOracleConn::connectDb( mUri, false );
|
|
}
|
|
|
|
QgsOracleConn *QgsOracleProvider::connectionRO() const
|
|
{
|
|
return mTransaction ? mTransaction->connection() : QgsOracleConn::connectDb( mUri, false );
|
|
}
|
|
|
|
bool QgsOracleProvider::exec( QSqlQuery &qry, QString sql, const QVariantList &args )
|
|
{
|
|
QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( sql ), 4 );
|
|
|
|
qry.setForwardOnly( true );
|
|
|
|
bool res = qry.prepare( sql );
|
|
if ( res )
|
|
{
|
|
for ( const auto &arg : args )
|
|
{
|
|
QgsDebugMsgLevel( QStringLiteral( " ARG: %1 [%2]" ).arg( arg.toString() ).arg( arg.typeName() ), 4 );
|
|
qry.addBindValue( arg );
|
|
}
|
|
res = qry.exec();
|
|
}
|
|
|
|
if ( !res )
|
|
{
|
|
QgsDebugMsg( QStringLiteral( "SQL: %1\nERROR: %2" )
|
|
.arg( qry.lastQuery() )
|
|
.arg( qry.lastError().text() ) );
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
void QgsOracleProvider::setTransaction( QgsTransaction *transaction )
|
|
{
|
|
// static_cast since layers cannot be added to a transaction of a non-matching provider
|
|
mTransaction = static_cast<QgsOracleTransaction *>( transaction );
|
|
}
|
|
|
|
QgsTransaction *QgsOracleProvider::transaction() const
|
|
{
|
|
return mTransaction;
|
|
}
|
|
|
|
QString QgsOracleProvider::storageType() const
|
|
{
|
|
return "Oracle database with locator/spatial extension";
|
|
}
|
|
|
|
QString QgsOracleProvider::pkParamWhereClause() const
|
|
{
|
|
QgsOracleConn *conn = connectionRO();
|
|
QString whereClause;
|
|
|
|
switch ( mPrimaryKeyType )
|
|
{
|
|
case PktInt:
|
|
case PktFidMap:
|
|
{
|
|
Q_ASSERT( mPrimaryKeyAttrs.size() >= 1 );
|
|
|
|
QString delim = "";
|
|
for ( int i = 0; i < mPrimaryKeyAttrs.size(); i++ )
|
|
{
|
|
int idx = mPrimaryKeyAttrs[i];
|
|
QgsField fld = field( idx );
|
|
|
|
whereClause += delim + QString( "%1=?" ).arg( conn->fieldExpression( fld ) );
|
|
delim = " AND ";
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PktRowId:
|
|
return "ROWID=?";
|
|
break;
|
|
|
|
case PktUnknown:
|
|
Q_ASSERT( !"FAILURE: Primary key unknown" );
|
|
whereClause = "NULL IS NOT NULL";
|
|
break;
|
|
}
|
|
|
|
if ( !mSqlWhereClause.isEmpty() )
|
|
{
|
|
if ( !whereClause.isEmpty() )
|
|
whereClause += " AND ";
|
|
|
|
whereClause += "(" + mSqlWhereClause + ")";
|
|
}
|
|
|
|
return whereClause;
|
|
}
|
|
|
|
void QgsOracleProvider::appendPkParams( QgsFeatureId fid, QSqlQuery &qry ) const
|
|
{
|
|
switch ( mPrimaryKeyType )
|
|
{
|
|
case PktInt:
|
|
QgsDebugMsgLevel( QStringLiteral( "addBindValue pk %1" ).arg( FID_TO_STRING( fid ) ), 4 );
|
|
qry.addBindValue( FID_TO_STRING( fid ) );
|
|
break;
|
|
|
|
case PktRowId:
|
|
case PktFidMap:
|
|
{
|
|
QVariant pkValsVariant = mShared->lookupKey( fid );
|
|
if ( !pkValsVariant.isNull() )
|
|
{
|
|
const auto constToList = pkValsVariant.toList();
|
|
for ( const QVariant &v : constToList )
|
|
{
|
|
QgsDebugMsgLevel( QStringLiteral( "addBindValue pk %1" ).arg( FID_TO_STRING( fid ) ), 4 );
|
|
qry.addBindValue( v );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QgsDebugMsg( QStringLiteral( "key values for fid %1 not found." ).arg( fid ) );
|
|
for ( int i = 0; i < mPrimaryKeyAttrs.size(); i++ )
|
|
{
|
|
QgsDebugMsgLevel( QStringLiteral( "addBindValue pk NULL" ).arg( fid ), 4 );
|
|
qry.addBindValue( QVariant() );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case PktUnknown:
|
|
QgsDebugMsg( QStringLiteral( "Unknown key type" ) );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
QString QgsOracleUtils::whereClause( QgsFeatureId featureId, const QgsFields &fields, QgsOraclePrimaryKeyType primaryKeyType, const QList<int> &primaryKeyAttrs, std::shared_ptr<QgsOracleSharedData> sharedData, QVariantList &args )
|
|
{
|
|
QString whereClause;
|
|
|
|
switch ( primaryKeyType )
|
|
{
|
|
case PktInt:
|
|
Q_ASSERT( primaryKeyAttrs.size() == 1 );
|
|
whereClause = QString( "%1=?" ).arg( QgsOracleConn::quotedIdentifier( fields.at( primaryKeyAttrs[0] ).name() ) );
|
|
args << featureId;
|
|
break;
|
|
|
|
case PktRowId:
|
|
case PktFidMap:
|
|
{
|
|
QVariant pkValsVariant = sharedData->lookupKey( featureId );
|
|
if ( !pkValsVariant.isNull() )
|
|
{
|
|
QList<QVariant> pkVals = pkValsVariant.toList();
|
|
|
|
if ( primaryKeyType == PktFidMap )
|
|
{
|
|
Q_ASSERT( pkVals.size() == primaryKeyAttrs.size() );
|
|
|
|
QString delim = "";
|
|
for ( int i = 0; i < primaryKeyAttrs.size(); i++ )
|
|
{
|
|
int idx = primaryKeyAttrs[i];
|
|
QgsField fld = fields.at( idx );
|
|
|
|
whereClause += delim + QString( "%1=?" ).arg( QgsOracleConn::fieldExpression( fld ) );
|
|
args << pkVals[i];
|
|
delim = " AND ";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
whereClause += QString( "ROWID=?" );
|
|
args << pkVals[0];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QgsDebugMsg( QStringLiteral( "FAILURE: Key values for feature %1 not found." ).arg( featureId ) );
|
|
whereClause = "NULL IS NOT NULL";
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PktUnknown:
|
|
Q_ASSERT( !"FAILURE: Primary key unknown" );
|
|
whereClause = "NULL IS NOT NULL";
|
|
break;
|
|
}
|
|
|
|
return whereClause;
|
|
}
|
|
|
|
QString QgsOracleUtils::whereClause( QgsFeatureIds featureIds, const QgsFields &fields, QgsOraclePrimaryKeyType primaryKeyType, const QList<int> &primaryKeyAttrs, std::shared_ptr<QgsOracleSharedData> sharedData, QVariantList &args )
|
|
{
|
|
QStringList whereClauses;
|
|
const auto constFeatureIds = featureIds;
|
|
for ( const QgsFeatureId featureId : constFeatureIds )
|
|
{
|
|
whereClauses << whereClause( featureId, fields, primaryKeyType, primaryKeyAttrs, sharedData, args );
|
|
}
|
|
return whereClauses.isEmpty() ? "" : whereClauses.join( " OR " ).prepend( "(" ).append( ")" );
|
|
}
|
|
|
|
QString QgsOracleUtils::andWhereClauses( const QString &c1, const QString &c2 )
|
|
{
|
|
if ( c1.isEmpty() )
|
|
return c2;
|
|
if ( c2.isEmpty() )
|
|
return c1;
|
|
|
|
return QString( "(%1) AND (%2)" ).arg( c1 ).arg( c2 );
|
|
}
|
|
|
|
QString QgsOracleProvider::whereClause( QgsFeatureId featureId, QVariantList &args ) const
|
|
{
|
|
return QgsOracleUtils::whereClause( featureId, mAttributeFields, mPrimaryKeyType, mPrimaryKeyAttrs, mShared, args );
|
|
}
|
|
|
|
void QgsOracleProvider::setExtent( QgsRectangle &newExtent )
|
|
{
|
|
mLayerExtent.setXMaximum( newExtent.xMaximum() );
|
|
mLayerExtent.setXMinimum( newExtent.xMinimum() );
|
|
mLayerExtent.setYMaximum( newExtent.yMaximum() );
|
|
mLayerExtent.setYMinimum( newExtent.yMinimum() );
|
|
}
|
|
|
|
/**
|
|
* Returns the feature type
|
|
*/
|
|
QgsWkbTypes::Type QgsOracleProvider::wkbType() const
|
|
{
|
|
return mRequestedGeomType != QgsWkbTypes::Unknown ? mRequestedGeomType : mDetectedGeomType;
|
|
}
|
|
|
|
QgsField QgsOracleProvider::field( int index ) const
|
|
{
|
|
if ( index < 0 || index >= mAttributeFields.size() )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "FAILURE: Field %1 not found." ).arg( index ), tr( "Oracle" ) );
|
|
throw OracleFieldNotFound();
|
|
}
|
|
|
|
return mAttributeFields.at( index );
|
|
}
|
|
|
|
QgsFeatureIterator QgsOracleProvider::getFeatures( const QgsFeatureRequest &request ) const
|
|
{
|
|
if ( !mValid )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Read attempt on an invalid oracle data source" ), tr( "Oracle" ) );
|
|
return QgsFeatureIterator();
|
|
}
|
|
|
|
return QgsFeatureIterator( new QgsOracleFeatureIterator( new QgsOracleFeatureSource( this ), true, request ) );
|
|
}
|
|
|
|
/**
|
|
* Returns the number of fields
|
|
*/
|
|
uint QgsOracleProvider::fieldCount() const
|
|
{
|
|
return mAttributeFields.size();
|
|
}
|
|
|
|
QgsFields QgsOracleProvider::fields() const
|
|
{
|
|
return mAttributeFields;
|
|
}
|
|
|
|
QString QgsOracleProvider::dataComment() const
|
|
{
|
|
return mDataComment;
|
|
}
|
|
|
|
bool QgsOracleProvider::loadFields()
|
|
{
|
|
mAttributeFields.clear();
|
|
mDefaultValues.clear();
|
|
|
|
QgsOracleConn *conn = connectionRO();
|
|
QSqlQuery qry( *conn );
|
|
|
|
QMap<QString, QString> comments;
|
|
QMap<QString, QString> types;
|
|
QMap<QString, QVariant> defvalues;
|
|
|
|
if ( !mIsQuery )
|
|
{
|
|
QgsDebugMsg( QStringLiteral( "Loading fields for table %1" ).arg( mTableName ) );
|
|
|
|
if ( exec( qry, QString( "SELECT comments FROM all_tab_comments WHERE owner=? AND table_name=?" ),
|
|
QVariantList() << mOwnerName << mTableName ) )
|
|
{
|
|
if ( qry.next() )
|
|
mDataComment = qry.value( 0 ).toString();
|
|
else if ( exec( qry, QString( "SELECT comments FROM all_mview_comments WHERE owner=? AND mview_name=?" ),
|
|
QVariantList() << mOwnerName << mTableName ) )
|
|
{
|
|
if ( qry.next() )
|
|
mDataComment = qry.value( 0 ).toString();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Loading comment for table %1.%2 failed [%3]" )
|
|
.arg( mOwnerName )
|
|
.arg( mTableName )
|
|
.arg( qry.lastError().text() ),
|
|
tr( "Oracle" ) );
|
|
}
|
|
|
|
qry.finish();
|
|
|
|
if ( exec( qry, QString( "SELECT column_name,comments FROM all_col_comments t WHERE t.owner=? AND t.table_name=?" ),
|
|
QVariantList() << mOwnerName << mTableName ) )
|
|
{
|
|
while ( qry.next() )
|
|
{
|
|
if ( qry.value( 0 ).toString() == mGeometryColumn )
|
|
continue;
|
|
comments.insert( qry.value( 0 ).toString(), qry.value( 1 ).toString() );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Loading comment for columns of table %1.%2 failed [%3]" ).arg( mOwnerName ).arg( mTableName ).arg( qry.lastError().text() ), tr( "Oracle" ) );
|
|
}
|
|
|
|
qry.finish();
|
|
|
|
QVariantList args;
|
|
QString sql( "SELECT"
|
|
" t.column_name"
|
|
",CASE WHEN t.data_type_owner IS NULL THEN t.data_type ELSE t.data_type_owner||'.'||t.data_type END"
|
|
",t.data_precision"
|
|
",t.data_scale"
|
|
",t.char_length"
|
|
",t.char_used"
|
|
",t.data_default"
|
|
" FROM all_tab_columns t"
|
|
" WHERE t.owner=? AND t.table_name=?" ) ;
|
|
args << mOwnerName << mTableName;
|
|
|
|
if ( !mGeometryColumn.isEmpty() )
|
|
{
|
|
sql += " AND t.column_name<>?";
|
|
args << mGeometryColumn;
|
|
}
|
|
|
|
if ( !mIncludeGeoAttributes )
|
|
{
|
|
sql += " AND (t.data_type_owner<>'MDSYS' OR t.data_type<>'SDO_GEOMETRY')";
|
|
}
|
|
|
|
sql += " ORDER BY t.column_id";
|
|
|
|
if ( exec( qry, sql, args ) )
|
|
{
|
|
while ( qry.next() )
|
|
{
|
|
QString name = qry.value( 0 ).toString();
|
|
QString type = qry.value( 1 ).toString();
|
|
int prec = qry.value( 2 ).toInt();
|
|
int scale = qry.value( 3 ).toInt();
|
|
int clength = qry.value( 4 ).toInt();
|
|
bool cused = qry.value( 5 ).toString() == "C";
|
|
QVariant defValue = qry.value( 6 );
|
|
|
|
if ( type == "CHAR" || type == "VARCHAR2" || type == "VARCHAR" )
|
|
{
|
|
types.insert( name, QString( "%1(%2 %3)" ).arg( type ).arg( clength ).arg( cused ? "CHAR" : "BYTE" ) );
|
|
}
|
|
else if ( type == "NCHAR" || type == "NVARCHAR2" || type == "NVARCHAR" )
|
|
{
|
|
types.insert( name, QString( "%1(%2)" ).arg( type ).arg( clength ) );
|
|
}
|
|
else if ( type == "NUMBER" )
|
|
{
|
|
if ( scale == 0 )
|
|
{
|
|
types.insert( name, QString( "%1(%2)" ).arg( type ).arg( prec ) );
|
|
}
|
|
else
|
|
{
|
|
types.insert( name, QString( "%1(%2,%3)" ).arg( type ).arg( prec ).arg( scale ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
types.insert( name, type );
|
|
}
|
|
|
|
defvalues.insert( name, defValue );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Loading field types for table %1.%2 failed [%3]" )
|
|
.arg( mOwnerName )
|
|
.arg( mTableName )
|
|
.arg( qry.lastError().text() ),
|
|
tr( "Oracle" ) );
|
|
}
|
|
|
|
if ( !mGeometryColumn.isEmpty() )
|
|
{
|
|
if ( exec( qry, QString( "SELECT i.index_name,i.domidx_opstatus"
|
|
" FROM all_indexes i"
|
|
" JOIN all_ind_columns c ON i.owner=c.index_owner AND i.index_name=c.index_name AND c.column_name=?"
|
|
" WHERE i.table_owner=? AND i.table_name=? AND i.ityp_owner='MDSYS' AND i.ityp_name='SPATIAL_INDEX'" ),
|
|
QVariantList() << mGeometryColumn << mOwnerName << mTableName ) )
|
|
{
|
|
if ( qry.next() )
|
|
{
|
|
mSpatialIndexName = qry.value( 0 ).toString();
|
|
if ( qry.value( 1 ).toString() != "VALID" )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Invalid spatial index %1 on column %2.%3.%4 found - expect poor performance." )
|
|
.arg( mSpatialIndexName )
|
|
.arg( mOwnerName )
|
|
.arg( mTableName )
|
|
.arg( mGeometryColumn ),
|
|
tr( "Oracle" ) );
|
|
}
|
|
else
|
|
{
|
|
QgsDebugMsg( QStringLiteral( "Valid spatial index %1 found" ).arg( mSpatialIndexName ) );
|
|
mHasSpatialIndex = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Probing for spatial index on column %1.%2.%3 failed [%4]" )
|
|
.arg( mOwnerName )
|
|
.arg( mTableName )
|
|
.arg( mGeometryColumn )
|
|
.arg( qry.lastError().text() ),
|
|
tr( "Oracle" ) );
|
|
}
|
|
}
|
|
|
|
mEnabledCapabilities |= QgsVectorDataProvider::CreateSpatialIndex;
|
|
}
|
|
|
|
if ( !mGeometryColumn.isEmpty() )
|
|
{
|
|
if ( !mHasSpatialIndex )
|
|
{
|
|
mHasSpatialIndex = qry.exec( QString( "SELECT %2 FROM %1 WHERE sdo_filter(%2,mdsys.sdo_geometry(2003,%3,NULL,mdsys.sdo_elem_info_array(1,1003,3),mdsys.sdo_ordinate_array(-1,-1,1,1)))='TRUE'" )
|
|
.arg( mQuery )
|
|
.arg( quotedIdentifier( mGeometryColumn ) )
|
|
.arg( mSrid < 1 ? "NULL" : QString::number( mSrid ) ) );
|
|
}
|
|
|
|
if ( !mHasSpatialIndex )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "No spatial index on column %1.%2.%3 found - expect poor performance." )
|
|
.arg( mOwnerName )
|
|
.arg( mTableName )
|
|
.arg( mGeometryColumn ),
|
|
tr( "Oracle" ) );
|
|
}
|
|
}
|
|
|
|
qry.finish();
|
|
|
|
if ( !exec( qry, QString( "SELECT * FROM %1 WHERE 1=0" ).arg( mQuery ), QVariantList() ) )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Retrieving fields from '%1' failed [%2]" ).arg( mQuery ).arg( qry.lastError().text() ), tr( "Oracle" ) );
|
|
return false;
|
|
}
|
|
|
|
QSqlRecord record = qry.record();
|
|
|
|
for ( int i = 0; i < record.count(); i++ )
|
|
{
|
|
QSqlField field = record.field( i );
|
|
|
|
if ( field.name() == mGeometryColumn )
|
|
continue;
|
|
|
|
if ( !mIsQuery && !types.contains( field.name() ) )
|
|
continue;
|
|
|
|
QVariant::Type type = field.type();
|
|
|
|
if ( types.value( field.name() ) == "DATE" )
|
|
{
|
|
// date types are incorrectly detected as datetime
|
|
type = QVariant::Date;
|
|
}
|
|
|
|
QgsField newField( field.name(), type, types.value( field.name() ), field.length(), field.precision(), comments.value( field.name() ) );
|
|
|
|
QgsFieldConstraints constraints;
|
|
if ( mPrimaryKeyAttrs.contains( i ) )
|
|
constraints.setConstraint( QgsFieldConstraints::ConstraintNotNull, QgsFieldConstraints::ConstraintOriginProvider );
|
|
if ( mPrimaryKeyAttrs.contains( i ) )
|
|
constraints.setConstraint( QgsFieldConstraints::ConstraintUnique, QgsFieldConstraints::ConstraintOriginProvider );
|
|
newField.setConstraints( constraints );
|
|
|
|
mAttributeFields.append( newField );
|
|
mDefaultValues.append( defvalues.value( field.name(), QVariant() ) );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool QgsOracleProvider::hasSufficientPermsAndCapabilities()
|
|
{
|
|
QgsDebugMsg( QStringLiteral( "Checking for permissions on the relation" ) );
|
|
|
|
mEnabledCapabilities = QgsVectorDataProvider::SelectAtId | QgsVectorDataProvider::TransactionSupport;
|
|
|
|
QgsOracleConn *conn = connectionRO();
|
|
QSqlQuery qry( *conn );
|
|
if ( !mIsQuery )
|
|
{
|
|
if ( conn->currentUser() == mOwnerName )
|
|
{
|
|
// full set of privileges for the owner
|
|
mEnabledCapabilities |= QgsVectorDataProvider::DeleteFeatures
|
|
| QgsVectorDataProvider::ChangeAttributeValues
|
|
| QgsVectorDataProvider::AddFeatures
|
|
| QgsVectorDataProvider::AddAttributes
|
|
| QgsVectorDataProvider::DeleteAttributes
|
|
| QgsVectorDataProvider::ChangeGeometries
|
|
| QgsVectorDataProvider::RenameAttributes
|
|
;
|
|
}
|
|
else if ( exec( qry, QString( "SELECT privilege FROM all_tab_privs WHERE table_schema=? AND table_name=? AND privilege IN ('DELETE','UPDATE','INSERT','ALTER TABLE')" ),
|
|
QVariantList() << mOwnerName << mTableName ) )
|
|
{
|
|
// check grants
|
|
while ( qry.next() )
|
|
{
|
|
QString priv = qry.value( 0 ).toString();
|
|
|
|
if ( priv == "DELETE" )
|
|
{
|
|
mEnabledCapabilities |= QgsVectorDataProvider::DeleteFeatures;
|
|
}
|
|
else if ( priv == "UPDATE" )
|
|
{
|
|
mEnabledCapabilities |= QgsVectorDataProvider::ChangeAttributeValues;
|
|
}
|
|
else if ( priv == "INSERT" )
|
|
{
|
|
mEnabledCapabilities |= QgsVectorDataProvider::AddFeatures;
|
|
}
|
|
else if ( priv == "ALTER TABLE" )
|
|
{
|
|
mEnabledCapabilities |= QgsVectorDataProvider::AddAttributes | QgsVectorDataProvider::DeleteAttributes | QgsVectorDataProvider::RenameAttributes;
|
|
}
|
|
}
|
|
|
|
if ( !mGeometryColumn.isNull() )
|
|
{
|
|
if ( exec( qry, QString( "SELECT 1 FROM all_col_privs WHERE table_schema=? AND table_name=? AND column_name=? AND privilege='UPDATE'" ),
|
|
QVariantList() << mOwnerName << mTableName << mGeometryColumn ) )
|
|
{
|
|
if ( qry.next() )
|
|
mEnabledCapabilities |= QgsVectorDataProvider::ChangeGeometries;
|
|
}
|
|
else
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Unable to determine geometry column access privileges for column %1.%2.\nThe error message from the database was:\n%3.\nSQL: %4" )
|
|
.arg( mQuery )
|
|
.arg( mGeometryColumn )
|
|
.arg( qry.lastError().text() )
|
|
.arg( qry.lastQuery() ),
|
|
tr( "Oracle" ) );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Unable to determine table access privileges for the table %1.\nThe error message from the database was:\n%2.\nSQL: %3" )
|
|
.arg( mQuery )
|
|
.arg( qry.lastError().text() )
|
|
.arg( qry.lastQuery() ),
|
|
tr( "Oracle" ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Check if the sql is a select query
|
|
if ( !mQuery.startsWith( "(" ) && !mQuery.endsWith( ")" ) )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "The custom query is not a select query." ), tr( "Oracle" ) );
|
|
return false;
|
|
}
|
|
|
|
if ( !exec( qry, QString( "SELECT * FROM %1 WHERE 1=0" ).arg( mQuery ), QVariantList() ) )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" )
|
|
.arg( qry.lastError().text() )
|
|
.arg( qry.lastQuery() ), tr( "Oracle" ) );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
qry.finish();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool QgsOracleProvider::determinePrimaryKey()
|
|
{
|
|
if ( !loadFields() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// check to see if there is an unique index on the relation, which
|
|
// can be used as a key into the table. Primary keys are always
|
|
// unique indices, so we catch them as well.
|
|
QgsOracleConn *conn = connectionRO();
|
|
QSqlQuery qry( *conn );
|
|
if ( !mIsQuery )
|
|
{
|
|
if ( !exec( qry, QString( "SELECT column_name"
|
|
" FROM all_ind_columns a"
|
|
" JOIN all_constraints b ON a.index_name=constraint_name AND a.index_owner=b.owner"
|
|
" WHERE b.constraint_type='P' AND b.owner=? AND b.table_name=?" ),
|
|
QVariantList() << mOwnerName << mTableName ) )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" )
|
|
.arg( qry.lastError().text() )
|
|
.arg( qry.lastQuery() ), tr( "Oracle" ) );
|
|
return false;
|
|
}
|
|
|
|
bool isInt = true;
|
|
|
|
while ( qry.next() )
|
|
{
|
|
QString name = qry.value( 0 ).toString();
|
|
|
|
int idx = mAttributeFields.indexFromName( name );
|
|
if ( idx < 0 )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Primary key field %1 not found in %2" ).arg( name ).arg( mQuery ), tr( "Oracle" ) );
|
|
return false;
|
|
}
|
|
|
|
QgsField fld = mAttributeFields.at( idx );
|
|
|
|
if ( isInt &&
|
|
fld.type() != QVariant::Int &&
|
|
fld.type() != QVariant::LongLong &&
|
|
!( fld.type() == QVariant::Double && fld.precision() == 0 ) )
|
|
isInt = false;
|
|
|
|
mPrimaryKeyAttrs << idx;
|
|
}
|
|
|
|
if ( mPrimaryKeyAttrs.size() > 0 )
|
|
{
|
|
mPrimaryKeyType = ( mPrimaryKeyAttrs.size() == 1 && isInt ) ? PktInt : PktFidMap;
|
|
}
|
|
else if ( !exec( qry, QString( "SELECT 1 FROM all_tables WHERE owner=? AND table_name=?" ), QVariantList() << mOwnerName << mTableName ) )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" )
|
|
.arg( qry.lastError().text() )
|
|
.arg( qry.lastQuery() ), tr( "Oracle" ) );
|
|
}
|
|
else if ( qry.next() )
|
|
{
|
|
// is table
|
|
QgsMessageLog::logMessage( tr( "No primary key found, using ROWID." ), tr( "Oracle" ) );
|
|
mPrimaryKeyType = PktRowId;
|
|
}
|
|
else
|
|
{
|
|
QString primaryKey = mUri.keyColumn();
|
|
mPrimaryKeyType = PktUnknown;
|
|
|
|
if ( !primaryKey.isEmpty() )
|
|
{
|
|
int idx = fieldNameIndex( primaryKey );
|
|
|
|
if ( idx >= 0 )
|
|
{
|
|
QgsField fld = mAttributeFields.at( idx );
|
|
|
|
if ( mUseEstimatedMetadata || uniqueData( mQuery, primaryKey ) )
|
|
{
|
|
mPrimaryKeyType = ( fld.type() == QVariant::Int || fld.type() == QVariant::LongLong || ( fld.type() == QVariant::Double && fld.precision() == 0 ) ) ? PktInt : PktFidMap;
|
|
mPrimaryKeyAttrs << idx;
|
|
}
|
|
else
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Primary key field '%1' for view not unique." ).arg( primaryKey ), tr( "Oracle" ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Key field '%1' for view not found." ).arg( primaryKey ), tr( "Oracle" ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QgsMessageLog::logMessage( tr( "No key field for view given." ), tr( "Oracle" ) );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QString primaryKey = mUri.keyColumn();
|
|
int idx = fieldNameIndex( mUri.keyColumn() );
|
|
|
|
if ( idx >= 0 && (
|
|
mAttributeFields.at( idx ).type() == QVariant::Int ||
|
|
mAttributeFields.at( idx ).type() == QVariant::LongLong ||
|
|
mAttributeFields.at( idx ).type() == QVariant::Double
|
|
) )
|
|
{
|
|
if ( mUseEstimatedMetadata || uniqueData( mQuery, primaryKey ) )
|
|
{
|
|
mPrimaryKeyType = PktInt;
|
|
mPrimaryKeyAttrs << idx;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QgsMessageLog::logMessage( tr( "No key field for query given." ), tr( "Oracle" ) );
|
|
mPrimaryKeyType = PktUnknown;
|
|
}
|
|
}
|
|
|
|
qry.finish();
|
|
|
|
mValid = mPrimaryKeyType != PktUnknown;
|
|
|
|
const auto constMPrimaryKeyAttrs = mPrimaryKeyAttrs;
|
|
for ( int fieldIdx : constMPrimaryKeyAttrs )
|
|
{
|
|
//primary keys are unique, not null
|
|
QgsFieldConstraints constraints = mAttributeFields.at( fieldIdx ).constraints();
|
|
constraints.setConstraint( QgsFieldConstraints::ConstraintUnique, QgsFieldConstraints::ConstraintOriginProvider );
|
|
constraints.setConstraint( QgsFieldConstraints::ConstraintNotNull, QgsFieldConstraints::ConstraintOriginProvider );
|
|
mAttributeFields[ fieldIdx ].setConstraints( constraints );
|
|
}
|
|
|
|
return mValid;
|
|
}
|
|
|
|
bool QgsOracleProvider::determineAlwaysGeneratedKeys()
|
|
{
|
|
if ( !loadFields() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// check to see if there is an unique index on the relation, which
|
|
// can be used as a key into the table. Primary keys are always
|
|
// unique indices, so we catch them as well.
|
|
QgsOracleConn *conn = connectionRO();
|
|
QSqlQuery qry( *conn );
|
|
|
|
QString sql = QStringLiteral( "SELECT a.column_name "
|
|
"FROM all_tab_identity_cols a "
|
|
"WHERE a.owner = '%1' "
|
|
"AND a.table_name = '%2' "
|
|
"AND a.generation_type = 'ALWAYS'" ).arg( mOwnerName ).arg( mTableName );
|
|
|
|
if ( exec( qry, sql, QVariantList() ) )
|
|
{
|
|
while ( qry.next() )
|
|
{
|
|
if ( mAttributeFields.names().contains( qry.value( 0 ).toString() ) )
|
|
{
|
|
mAlwaysGeneratedKeyAttrs.append( mAttributeFields.indexOf( qry.value( 0 ).toString() ) );
|
|
}
|
|
}
|
|
mValid = true;
|
|
}
|
|
else
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" )
|
|
.arg( qry.lastError().text() )
|
|
.arg( qry.lastQuery() ), tr( "Oracle" ) );
|
|
mValid = false;
|
|
}
|
|
return mValid;
|
|
}
|
|
|
|
bool QgsOracleProvider::uniqueData( QString query, QString colName )
|
|
{
|
|
Q_UNUSED( query )
|
|
// Check to see if the given column contains unique data
|
|
QgsOracleConn *conn = connectionRO();
|
|
QSqlQuery qry( *conn );
|
|
|
|
QString table = mQuery;
|
|
if ( !mSqlWhereClause.isEmpty() )
|
|
{
|
|
table += " WHERE " + mSqlWhereClause;
|
|
}
|
|
|
|
QString sql = QString( "SELECT (SELECT count(distinct %1) FROM %2)-(SELECT count(%1) FROM %2) FROM dual" )
|
|
.arg( quotedIdentifier( colName ) )
|
|
.arg( mQuery );
|
|
|
|
if ( !exec( qry, sql, QVariantList() ) || !qry.next() )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" )
|
|
.arg( qry.lastError().text() )
|
|
.arg( qry.lastQuery() ), tr( "Oracle" ) );
|
|
return false;
|
|
}
|
|
|
|
return qry.value( 0 ).toInt() == 0;
|
|
}
|
|
|
|
// Returns the minimum value of an attribute
|
|
QVariant QgsOracleProvider::minimumValue( int index ) const
|
|
{
|
|
QgsOracleConn *conn = connectionRO();
|
|
if ( !conn )
|
|
return QVariant( QString() );
|
|
|
|
try
|
|
{
|
|
// get the field name
|
|
QgsField fld = field( index );
|
|
QString sql = QString( "SELECT min(%1) FROM %2" )
|
|
.arg( quotedIdentifier( fld.name() ) )
|
|
.arg( mQuery );
|
|
|
|
if ( !mSqlWhereClause.isEmpty() )
|
|
{
|
|
sql += QString( " WHERE %1" ).arg( mSqlWhereClause );
|
|
}
|
|
|
|
QSqlQuery qry( *conn );
|
|
|
|
if ( !exec( qry, sql, QVariantList() ) )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" )
|
|
.arg( qry.lastError().text() )
|
|
.arg( qry.lastQuery() ), tr( "Oracle" ) );
|
|
return QVariant( QString() );
|
|
}
|
|
|
|
if ( qry.next() )
|
|
{
|
|
return qry.value( 0 );
|
|
}
|
|
}
|
|
catch ( OracleFieldNotFound )
|
|
{
|
|
;
|
|
}
|
|
return QVariant( QString() );
|
|
}
|
|
|
|
// Returns the list of unique values of an attribute
|
|
QSet<QVariant> QgsOracleProvider::uniqueValues( int index, int limit ) const
|
|
{
|
|
QSet<QVariant> uniqueValues;
|
|
|
|
QgsOracleConn *conn = connectionRO();
|
|
if ( !conn )
|
|
return uniqueValues;
|
|
|
|
try
|
|
{
|
|
// get the field name
|
|
QgsField fld = field( index );
|
|
QString sql = QString( "SELECT DISTINCT %1 FROM %2" )
|
|
.arg( quotedIdentifier( fld.name() ) )
|
|
.arg( mQuery );
|
|
|
|
if ( !mSqlWhereClause.isEmpty() )
|
|
{
|
|
sql += QString( " WHERE %1" ).arg( mSqlWhereClause );
|
|
}
|
|
|
|
sql += QString( " ORDER BY %1" )
|
|
.arg( quotedIdentifier( fld.name() ) );
|
|
|
|
if ( limit >= 0 )
|
|
{
|
|
sql = QString( "SELECT * FROM (%1) WHERE rownum<=%2" ).arg( sql ).arg( limit );
|
|
}
|
|
|
|
QSqlQuery qry( *conn );
|
|
|
|
if ( !exec( qry, sql, QVariantList() ) )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" )
|
|
.arg( qry.lastError().text() )
|
|
.arg( qry.lastQuery() ), tr( "Oracle" ) );
|
|
return QSet<QVariant>();
|
|
}
|
|
|
|
while ( qry.next() )
|
|
{
|
|
uniqueValues << qry.value( 0 );
|
|
}
|
|
}
|
|
catch ( OracleFieldNotFound )
|
|
{
|
|
return QSet<QVariant>();
|
|
}
|
|
|
|
return uniqueValues;
|
|
}
|
|
|
|
// Returns the maximum value of an attribute
|
|
QVariant QgsOracleProvider::maximumValue( int index ) const
|
|
{
|
|
QgsOracleConn *conn = connectionRO();
|
|
if ( !conn )
|
|
return QVariant();
|
|
|
|
try
|
|
{
|
|
// get the field name
|
|
QgsField fld = field( index );
|
|
QString sql = QString( "SELECT max(%1) FROM %2" )
|
|
.arg( quotedIdentifier( fld.name() ) )
|
|
.arg( mQuery );
|
|
|
|
if ( !mSqlWhereClause.isEmpty() )
|
|
{
|
|
sql += QString( " WHERE %1" ).arg( mSqlWhereClause );
|
|
}
|
|
|
|
QSqlQuery qry( *conn );
|
|
|
|
if ( !exec( qry, sql, QVariantList() ) )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" )
|
|
.arg( qry.lastError().text() )
|
|
.arg( qry.lastQuery() ), tr( "Oracle" ) );
|
|
return QVariant( QString() );
|
|
}
|
|
|
|
if ( qry.next() )
|
|
{
|
|
return qry.value( 0 );
|
|
}
|
|
}
|
|
catch ( OracleFieldNotFound )
|
|
{
|
|
;
|
|
}
|
|
|
|
return QVariant( QString() );
|
|
}
|
|
|
|
|
|
bool QgsOracleProvider::isValid() const
|
|
{
|
|
return mValid;
|
|
}
|
|
|
|
QVariant QgsOracleProvider::defaultValue( int fieldId ) const
|
|
{
|
|
QString defVal = mDefaultValues.value( fieldId, QString() ).toString();
|
|
|
|
if ( providerProperty( EvaluateDefaultValues, false ).toBool() && !defVal.isEmpty() )
|
|
{
|
|
QgsField fld = field( fieldId );
|
|
return evaluateDefaultExpression( defVal, fld.type() );
|
|
}
|
|
|
|
return defVal;
|
|
}
|
|
|
|
QString QgsOracleProvider::defaultValueClause( int fieldId ) const
|
|
{
|
|
QString defVal = mDefaultValues.value( fieldId, QString() ).toString();
|
|
|
|
if ( !providerProperty( EvaluateDefaultValues, false ).toBool() && !defVal.isEmpty() )
|
|
{
|
|
return defVal;
|
|
}
|
|
|
|
return QString();
|
|
}
|
|
|
|
|
|
bool QgsOracleProvider::skipConstraintCheck( int fieldIndex, QgsFieldConstraints::Constraint constraint, const QVariant &value ) const
|
|
{
|
|
Q_UNUSED( constraint );
|
|
if ( providerProperty( EvaluateDefaultValues, false ).toBool() )
|
|
{
|
|
return !mDefaultValues.value( fieldIndex ).toString().isEmpty();
|
|
}
|
|
else
|
|
{
|
|
// stricter check - if we are evaluating default values only on commit then we can only bypass the check
|
|
// if the attribute values matches the original default clause
|
|
return mDefaultValues.value( fieldIndex ) == value.toString() && !value.isNull();
|
|
}
|
|
}
|
|
|
|
QVariant QgsOracleProvider::evaluateDefaultExpression( const QString &value, const QVariant::Type &fieldType ) const
|
|
{
|
|
if ( value.isEmpty() )
|
|
{
|
|
return QVariant( fieldType );
|
|
}
|
|
|
|
QgsOracleConn *conn = connectionRO();
|
|
QSqlQuery qry( *conn );
|
|
if ( !exec( qry, QString( "SELECT %1 FROM dual" ).arg( value ), QVariantList() ) || !qry.next() )
|
|
{
|
|
throw OracleException( tr( "Evaluation of default value failed" ), qry );
|
|
}
|
|
|
|
// return the evaluated value
|
|
return convertValue( fieldType, qry.value( 0 ).toString() );
|
|
}
|
|
|
|
|
|
bool QgsOracleProvider::addFeatures( QgsFeatureList &flist, QgsFeatureSink::Flags flags )
|
|
{
|
|
if ( flist.size() == 0 )
|
|
return true;
|
|
|
|
QgsOracleConn *conn = connectionRW();
|
|
if ( mIsQuery || !conn )
|
|
return false;
|
|
|
|
bool returnvalue = true;
|
|
|
|
if ( !( flags & QgsFeatureSink::FastInsert ) && !getWorkspace().isEmpty() && getWorkspace().compare( QStringLiteral( "LIVE" ), Qt::CaseInsensitive ) != 0 )
|
|
{
|
|
static bool warn = true;
|
|
if ( warn )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Retrieval of updated primary keys from versioned tables not supported" ), tr( "Oracle" ) );
|
|
warn = false;
|
|
}
|
|
flags |= QgsFeatureSink::FastInsert;
|
|
}
|
|
|
|
QSqlDatabase db( *conn );
|
|
|
|
try
|
|
{
|
|
QSqlQuery ins( db ), getfid( db ), identitytype( db );
|
|
|
|
if ( !conn->begin( db ) )
|
|
{
|
|
throw OracleException( tr( "Could not start transaction" ), db );
|
|
}
|
|
|
|
// Prepare the INSERT statement
|
|
QString insert = QStringLiteral( "INSERT INTO %1(" ).arg( mQuery );
|
|
QString values = QStringLiteral( ") VALUES (" );
|
|
QString delim;
|
|
|
|
QStringList defaultValues;
|
|
QList<int> fieldId;
|
|
|
|
if ( !mGeometryColumn.isNull() )
|
|
{
|
|
insert += quotedIdentifier( mGeometryColumn );
|
|
values += '?';
|
|
delim = ',';
|
|
}
|
|
|
|
if ( mPrimaryKeyType == PktInt || mPrimaryKeyType == PktFidMap )
|
|
{
|
|
QString keys, kdelim;
|
|
|
|
const auto constMPrimaryKeyAttrs = mPrimaryKeyAttrs;
|
|
for ( int idx : constMPrimaryKeyAttrs )
|
|
{
|
|
QgsField fld = field( idx );
|
|
keys += kdelim + quotedIdentifier( fld.name() );
|
|
if ( mAlwaysGeneratedKeyAttrs.contains( idx ) )
|
|
continue;
|
|
insert += delim + quotedIdentifier( fld.name() );
|
|
values += delim + '?';
|
|
delim = ',';
|
|
kdelim = ',';
|
|
fieldId << idx;
|
|
defaultValues << defaultValue( idx ).toString();
|
|
}
|
|
|
|
if ( !getfid.prepare( QStringLiteral( "SELECT %1 FROM %2 WHERE ROWID=?" ).arg( keys ).arg( mQuery ) ) )
|
|
{
|
|
throw OracleException( tr( "Could not prepare get feature id statement" ), getfid );
|
|
}
|
|
}
|
|
|
|
QgsAttributes attributevec = flist[0].attributes();
|
|
|
|
// look for unique attribute values to place in statement instead of passing as parameter
|
|
// e.g. for defaults
|
|
for ( int idx = 0; idx < std::min( attributevec.size(), mAttributeFields.size() ); ++idx )
|
|
{
|
|
QVariant v = attributevec[idx];
|
|
if ( mAlwaysGeneratedKeyAttrs.contains( idx ) )
|
|
continue;
|
|
if ( !v.isValid() )
|
|
continue;
|
|
|
|
if ( fieldId.contains( idx ) )
|
|
continue;
|
|
|
|
QgsField fld = mAttributeFields.at( idx );
|
|
|
|
QgsDebugMsgLevel( "Checking field against: " + fld.name(), 4 );
|
|
|
|
if ( fld.name().isEmpty() || fld.name() == mGeometryColumn )
|
|
continue;
|
|
|
|
insert += delim + quotedIdentifier( fld.name() );
|
|
|
|
QString defVal = defaultValue( idx ).toString();
|
|
|
|
values += delim + '?';
|
|
defaultValues.append( defVal );
|
|
fieldId.append( idx );
|
|
|
|
delim = ',';
|
|
}
|
|
|
|
insert += values + ")";
|
|
|
|
QgsDebugMsgLevel( QStringLiteral( "SQL prepare: %1" ).arg( insert ), 4 );
|
|
if ( !ins.prepare( insert ) )
|
|
{
|
|
throw OracleException( tr( "Could not prepare insert statement" ), ins );
|
|
}
|
|
|
|
for ( QgsFeatureList::iterator features = flist.begin(); features != flist.end(); ++features )
|
|
{
|
|
QgsAttributes attributevec = features->attributes();
|
|
|
|
QgsDebugMsgLevel( QStringLiteral( "insert feature %1" ).arg( features->id() ), 4 );
|
|
|
|
if ( !mGeometryColumn.isNull() )
|
|
{
|
|
appendGeomParam( features->geometry(), ins );
|
|
}
|
|
|
|
for ( int i = 0; i < fieldId.size(); i++ )
|
|
{
|
|
QVariant value = attributevec.value( fieldId[i], QVariant() );
|
|
|
|
QgsField fld = field( fieldId[i] );
|
|
if ( ( value.isNull() && mPrimaryKeyAttrs.contains( i ) && !defaultValues.at( i ).isEmpty() ) ||
|
|
( value.toString() == defaultValues[i] ) )
|
|
{
|
|
value = evaluateDefaultExpression( defaultValues[i], fld.type() );
|
|
}
|
|
features->setAttribute( fieldId[i], value );
|
|
|
|
QgsDebugMsgLevel( QStringLiteral( "addBindValue: %1" ).arg( value.toString() ), 4 );
|
|
ins.addBindValue( value );
|
|
}
|
|
|
|
if ( !ins.exec() )
|
|
throw OracleException( tr( "Could not insert feature %1" ).arg( features->id() ), ins );
|
|
|
|
if ( !( flags & QgsFeatureSink::FastInsert ) )
|
|
{
|
|
if ( mPrimaryKeyType == PktRowId )
|
|
{
|
|
features->setId( mShared->lookupFid( QList<QVariant>() << QVariant( ins.lastInsertId() ) ) );
|
|
QgsDebugMsgLevel( QStringLiteral( "new fid=%1" ).arg( features->id() ), 4 );
|
|
}
|
|
else if ( mPrimaryKeyType == PktInt || mPrimaryKeyType == PktFidMap )
|
|
{
|
|
if ( ins.lastInsertId().isValid() )
|
|
{
|
|
getfid.addBindValue( QVariant( ins.lastInsertId() ) );
|
|
if ( !getfid.exec() || !getfid.next() )
|
|
throw OracleException( tr( "Could not retrieve feature id %1" ).arg( features->id() ), getfid );
|
|
|
|
int col = 0;
|
|
const auto constMPrimaryKeyAttrs = mPrimaryKeyAttrs;
|
|
for ( int idx : constMPrimaryKeyAttrs )
|
|
{
|
|
QgsField fld = field( idx );
|
|
|
|
QVariant v = getfid.value( col++ );
|
|
if ( v.type() != fld.type() )
|
|
v = QgsVectorDataProvider::convertValue( fld.type(), v.toString() );
|
|
features->setAttribute( idx, v );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ins.finish();
|
|
|
|
if ( !conn->commit( db ) )
|
|
{
|
|
throw OracleException( tr( "Could not commit transaction" ), db );
|
|
}
|
|
|
|
if ( !( flags & QgsFeatureSink::FastInsert ) )
|
|
{
|
|
// update feature ids
|
|
if ( mPrimaryKeyType == PktInt || mPrimaryKeyType == PktFidMap )
|
|
{
|
|
for ( QgsFeatureList::iterator features = flist.begin(); features != flist.end(); ++features )
|
|
{
|
|
QgsAttributes attributevec = features->attributes();
|
|
|
|
if ( mPrimaryKeyType == PktInt )
|
|
{
|
|
features->setId( STRING_TO_FID( attributevec[ mPrimaryKeyAttrs[0] ] ) );
|
|
}
|
|
else
|
|
{
|
|
QVariantList primaryKeyVals;
|
|
|
|
const auto constMPrimaryKeyAttrs = mPrimaryKeyAttrs;
|
|
for ( int idx : constMPrimaryKeyAttrs )
|
|
{
|
|
primaryKeyVals << attributevec[ idx ];
|
|
}
|
|
|
|
features->setId( mShared->lookupFid( primaryKeyVals ) );
|
|
}
|
|
QgsDebugMsgLevel( QStringLiteral( "new fid=%1" ).arg( features->id() ), 4 );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( mFeaturesCounted >= 0 )
|
|
mFeaturesCounted += flist.size();
|
|
|
|
if ( mTransaction )
|
|
mTransaction->dirtyLastSavePoint();
|
|
}
|
|
catch ( OracleException &e )
|
|
{
|
|
QgsDebugMsg( QStringLiteral( "Oracle error: %1" ).arg( e.errorMessage() ) );
|
|
pushError( tr( "Oracle error while adding features: %1" ).arg( e.errorMessage() ) );
|
|
if ( !conn->rollback( db ) )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Could not rollback transaction" ), tr( "Oracle" ) );
|
|
}
|
|
returnvalue = false;
|
|
}
|
|
|
|
return returnvalue;
|
|
}
|
|
|
|
bool QgsOracleProvider::deleteFeatures( const QgsFeatureIds &id )
|
|
{
|
|
bool returnvalue = true;
|
|
|
|
QgsOracleConn *conn = connectionRW();
|
|
if ( mIsQuery || !conn )
|
|
return false;
|
|
|
|
QSqlDatabase db( *conn );
|
|
|
|
try
|
|
{
|
|
QSqlQuery qry( db );
|
|
|
|
if ( !conn->begin( db ) )
|
|
{
|
|
throw OracleException( tr( "Could not start transaction" ), db );
|
|
}
|
|
|
|
for ( QgsFeatureIds::const_iterator it = id.begin(); it != id.end(); ++it )
|
|
{
|
|
QVariantList args;
|
|
QString sql = QString( "DELETE FROM %1 WHERE %2" )
|
|
.arg( mQuery ).arg( whereClause( *it, args ) );
|
|
QgsDebugMsg( "delete sql: " + sql );
|
|
|
|
if ( !exec( qry, sql, args ) )
|
|
throw OracleException( tr( "Deletion of feature %1 failed" ).arg( *it ), qry );
|
|
|
|
mShared->removeFid( *it );
|
|
}
|
|
|
|
qry.finish();
|
|
|
|
if ( !conn->commit( db ) )
|
|
{
|
|
throw OracleException( tr( "Could not commit transaction" ), db );
|
|
}
|
|
|
|
if ( mTransaction )
|
|
mTransaction->dirtyLastSavePoint();
|
|
|
|
mFeaturesCounted -= id.size();
|
|
}
|
|
catch ( OracleException &e )
|
|
{
|
|
pushError( tr( "Oracle error while deleting features: %1" ).arg( e.errorMessage() ) );
|
|
if ( !conn->rollback( db ) )
|
|
QgsMessageLog::logMessage( tr( "Could not rollback transaction" ), tr( "Oracle" ) );
|
|
returnvalue = false;
|
|
}
|
|
|
|
return returnvalue;
|
|
}
|
|
|
|
bool QgsOracleProvider::addAttributes( const QList<QgsField> &attributes )
|
|
{
|
|
bool returnvalue = true;
|
|
|
|
QgsOracleConn *conn = connectionRW();
|
|
if ( mIsQuery || !conn )
|
|
return false;
|
|
|
|
QSqlDatabase db( *conn );
|
|
|
|
try
|
|
{
|
|
QSqlQuery qry( db );
|
|
|
|
if ( !conn->begin( db ) )
|
|
{
|
|
throw OracleException( tr( "Could not start transaction" ), db );
|
|
}
|
|
|
|
for ( QList<QgsField>::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter )
|
|
{
|
|
QString type = iter->typeName().toLower();
|
|
if ( type == "char" || type == "varchar2" )
|
|
{
|
|
type = QString( "%1(%2 char)" ).arg( type ).arg( iter->length() );
|
|
}
|
|
else if ( type == "number" )
|
|
{
|
|
if ( iter->precision() > 0 )
|
|
{
|
|
type = QString( "%1(%2,%3)" ).arg( type ).arg( iter->length() ).arg( iter->precision() );
|
|
}
|
|
else
|
|
{
|
|
type = QString( "%1(%2,%3)" ).arg( type ).arg( iter->length() );
|
|
}
|
|
}
|
|
|
|
QString sql = QString( "ALTER TABLE %1 ADD %2 %3" )
|
|
.arg( mQuery )
|
|
.arg( quotedIdentifier( iter->name() ) )
|
|
.arg( type );
|
|
QgsDebugMsg( sql );
|
|
|
|
if ( !exec( qry, sql, QVariantList() ) )
|
|
throw OracleException( tr( "Adding attribute %1 failed" ).arg( iter->name() ), qry );
|
|
|
|
if ( !iter->comment().isEmpty() )
|
|
{
|
|
sql = QString( "COMMENT ON COLUMN %1.%2 IS ?" )
|
|
.arg( mQuery )
|
|
.arg( quotedIdentifier( iter->name() ) );
|
|
if ( !exec( qry, sql, QVariantList() << iter->comment() ) )
|
|
throw OracleException( tr( "Setting comment on %1 failed" ).arg( iter->name() ), qry );
|
|
}
|
|
|
|
qry.finish();
|
|
|
|
}
|
|
|
|
if ( !conn->commit( db ) )
|
|
{
|
|
throw OracleException( tr( "Could not commit transaction" ), db );
|
|
}
|
|
|
|
if ( mTransaction )
|
|
mTransaction->dirtyLastSavePoint();
|
|
}
|
|
catch ( OracleException &e )
|
|
{
|
|
pushError( tr( "Oracle error while adding attributes: %1" ).arg( e.errorMessage() ) );
|
|
if ( !conn->rollback( db ) )
|
|
QgsMessageLog::logMessage( tr( "Could not rollback transaction" ), tr( "Oracle" ) );
|
|
returnvalue = false;
|
|
}
|
|
|
|
if ( !loadFields() )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Could not reload fields." ), tr( "Oracle" ) );
|
|
}
|
|
|
|
return returnvalue;
|
|
}
|
|
|
|
bool QgsOracleProvider::deleteAttributes( const QgsAttributeIds &ids )
|
|
{
|
|
bool returnvalue = true;
|
|
|
|
QgsOracleConn *conn = connectionRW();
|
|
if ( mIsQuery || !conn )
|
|
return false;
|
|
|
|
QSqlDatabase db( *conn );
|
|
|
|
try
|
|
{
|
|
QSqlQuery qry( db );
|
|
|
|
if ( !conn->begin( db ) )
|
|
{
|
|
throw OracleException( tr( "Could not start transaction" ), db );
|
|
}
|
|
|
|
qry.finish();
|
|
|
|
QList<int> idsList = ids.values();
|
|
std::sort( idsList.begin(), idsList.end(), qGreater<int>() );
|
|
|
|
const auto constIdsList = idsList;
|
|
for ( int id : constIdsList )
|
|
{
|
|
QgsField fld = mAttributeFields.at( id );
|
|
|
|
QString sql = QString( "ALTER TABLE %1 DROP COLUMN %2" )
|
|
.arg( mQuery )
|
|
.arg( quotedIdentifier( fld.name() ) );
|
|
|
|
//send sql statement and do error handling
|
|
if ( !exec( qry, sql, QVariantList() ) )
|
|
throw OracleException( tr( "Dropping column %1 failed" ).arg( fld.name() ), qry );
|
|
|
|
//delete the attribute from mAttributeFields
|
|
mAttributeFields.remove( id );
|
|
mDefaultValues.removeAt( id );
|
|
}
|
|
|
|
if ( !conn->commit( db ) )
|
|
{
|
|
throw OracleException( tr( "Could not commit transaction" ), db );
|
|
}
|
|
|
|
if ( mTransaction )
|
|
mTransaction->dirtyLastSavePoint();
|
|
}
|
|
catch ( OracleException &e )
|
|
{
|
|
pushError( tr( "Oracle error while deleting attributes: %1" ).arg( e.errorMessage() ) );
|
|
if ( !conn->rollback( db ) )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Could not rollback transaction" ), tr( "Oracle" ) );
|
|
}
|
|
returnvalue = false;
|
|
}
|
|
|
|
if ( !loadFields() )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Could not reload fields." ), tr( "Oracle" ) );
|
|
}
|
|
|
|
return returnvalue;
|
|
}
|
|
|
|
bool QgsOracleProvider::renameAttributes( const QgsFieldNameMap &renamedAttributes )
|
|
{
|
|
QgsOracleConn *conn = connectionRW();
|
|
if ( mIsQuery || !conn )
|
|
return false;
|
|
|
|
QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
|
|
for ( ; renameIt != renamedAttributes.constEnd(); ++renameIt )
|
|
{
|
|
int fieldIndex = renameIt.key();
|
|
if ( fieldIndex < 0 || fieldIndex >= mAttributeFields.count() )
|
|
{
|
|
pushError( tr( "Invalid attribute index: %1" ).arg( fieldIndex ) );
|
|
return false;
|
|
}
|
|
if ( mAttributeFields.indexFromName( renameIt.value() ) >= 0 )
|
|
{
|
|
//field name already in use
|
|
pushError( tr( "Error renaming field %1: name '%2' already exists" ).arg( fieldIndex ).arg( renameIt.value() ) );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
QSqlDatabase db( *conn );
|
|
|
|
bool returnvalue = true;
|
|
|
|
try
|
|
{
|
|
QSqlQuery qry( db );
|
|
|
|
if ( !conn->begin( db ) )
|
|
{
|
|
throw OracleException( tr( "Could not start transaction" ), db );
|
|
}
|
|
|
|
qry.finish();
|
|
|
|
for ( renameIt = renamedAttributes.constBegin(); renameIt != renamedAttributes.constEnd(); ++renameIt )
|
|
{
|
|
QString src( mAttributeFields.at( renameIt.key() ).name() );
|
|
|
|
if ( !exec( qry, QString( "ALTER TABLE %1 RENAME COLUMN %2 TO %3" )
|
|
.arg( mQuery,
|
|
quotedIdentifier( src ),
|
|
quotedIdentifier( renameIt.value() ) ), QVariantList() ) )
|
|
{
|
|
throw OracleException( tr( "Renaming column %1 to %2 failed" )
|
|
.arg( quotedIdentifier( src ),
|
|
quotedIdentifier( renameIt.value() ) ),
|
|
qry );
|
|
}
|
|
}
|
|
|
|
if ( !conn->commit( db ) )
|
|
{
|
|
throw OracleException( tr( "Could not commit transaction" ), db );
|
|
}
|
|
|
|
if ( mTransaction )
|
|
mTransaction->dirtyLastSavePoint();
|
|
}
|
|
catch ( OracleException &e )
|
|
{
|
|
pushError( tr( "Oracle error while renaming attributes: %1" ).arg( e.errorMessage() ) );
|
|
if ( !conn->rollback( db ) )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Could not rollback transaction" ), tr( "Oracle" ) );
|
|
}
|
|
returnvalue = false;
|
|
}
|
|
|
|
if ( !loadFields() )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Could not reload fields." ), tr( "Oracle" ) );
|
|
returnvalue = false;
|
|
}
|
|
|
|
return returnvalue;
|
|
}
|
|
|
|
|
|
bool QgsOracleProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_map )
|
|
{
|
|
bool returnvalue = true;
|
|
|
|
QgsOracleConn *conn = connectionRW();
|
|
if ( mIsQuery || !conn )
|
|
return false;
|
|
|
|
if ( attr_map.isEmpty() )
|
|
return true;
|
|
|
|
QSqlDatabase db( *conn );
|
|
|
|
try
|
|
{
|
|
QSqlQuery qry( db );
|
|
if ( !conn->begin( db ) )
|
|
{
|
|
throw OracleException( tr( "Could not start transaction" ), db );
|
|
}
|
|
|
|
// cycle through the features
|
|
for ( QgsChangedAttributesMap::const_iterator iter = attr_map.begin(); iter != attr_map.end(); ++iter )
|
|
{
|
|
QgsFeatureId fid = iter.key();
|
|
|
|
// skip added features
|
|
if ( FID_IS_NEW( fid ) )
|
|
continue;
|
|
|
|
const QgsAttributeMap &attrs = iter.value();
|
|
if ( attrs.isEmpty() )
|
|
continue;
|
|
|
|
QString sql = QString( "UPDATE %1 SET " ).arg( mQuery );
|
|
|
|
bool pkChanged = false;
|
|
QList<int> params;
|
|
|
|
// cycle through the changed attributes of the feature
|
|
QString delim;
|
|
for ( QgsAttributeMap::const_iterator siter = attrs.begin(); siter != attrs.end(); ++siter )
|
|
{
|
|
try
|
|
{
|
|
QgsField fld = field( siter.key() );
|
|
|
|
pkChanged = pkChanged || mPrimaryKeyAttrs.contains( siter.key() );
|
|
|
|
sql += delim + QString( "%1=?" ).arg( quotedIdentifier( fld.name() ) );
|
|
delim = ",";
|
|
|
|
params << siter.key();
|
|
}
|
|
catch ( OracleFieldNotFound )
|
|
{
|
|
// Field was missing - shouldn't happen
|
|
}
|
|
}
|
|
|
|
QVariantList args;
|
|
sql += QString( " WHERE %1" ).arg( whereClause( fid, args ) );
|
|
|
|
if ( !qry.prepare( sql ) )
|
|
{
|
|
throw OracleException( tr( "Could not prepare update statement." ), qry );
|
|
}
|
|
|
|
const auto constParams = params;
|
|
for ( int idx : constParams )
|
|
{
|
|
const QgsField &fld = field( idx );
|
|
|
|
if ( fld.typeName().endsWith( ".SDO_GEOMETRY" ) )
|
|
{
|
|
QgsGeometry g;
|
|
if ( !attrs[idx].isNull() )
|
|
{
|
|
g = QgsGeometry::fromWkt( attrs[ idx ].toString() );
|
|
}
|
|
appendGeomParam( g, qry );
|
|
}
|
|
else
|
|
{
|
|
qry.addBindValue( attrs[ idx ] );
|
|
}
|
|
}
|
|
|
|
for ( const auto &arg : args )
|
|
qry.addBindValue( arg );
|
|
|
|
if ( !qry.exec() )
|
|
throw OracleException( tr( "Update of feature %1 failed" ).arg( iter.key() ), qry );
|
|
|
|
qry.finish();
|
|
|
|
// update feature id map if key was changed
|
|
if ( pkChanged && mPrimaryKeyType == PktFidMap )
|
|
{
|
|
QVariant v = mShared->removeFid( fid );
|
|
|
|
QList<QVariant> k = v.toList();
|
|
|
|
for ( int i = 0; i < mPrimaryKeyAttrs.size(); i++ )
|
|
{
|
|
int idx = mPrimaryKeyAttrs[i];
|
|
if ( !attrs.contains( idx ) )
|
|
continue;
|
|
|
|
k[i] = attrs[ idx ];
|
|
}
|
|
|
|
mShared->insertFid( fid, k );
|
|
}
|
|
}
|
|
|
|
if ( !conn->commit( db ) )
|
|
{
|
|
throw OracleException( tr( "Could not commit transaction" ), db );
|
|
}
|
|
|
|
if ( mTransaction )
|
|
mTransaction->dirtyLastSavePoint();
|
|
}
|
|
catch ( OracleException &e )
|
|
{
|
|
pushError( tr( "Oracle error while changing attributes: %1" ).arg( e.errorMessage() ) );
|
|
if ( !conn->rollback( db ) )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Could not rollback transaction" ), tr( "Oracle" ) );
|
|
}
|
|
returnvalue = false;
|
|
}
|
|
|
|
return returnvalue;
|
|
}
|
|
|
|
void QgsOracleProvider::appendGeomParam( const QgsGeometry &geom, QSqlQuery &qry ) const
|
|
{
|
|
QOCISpatialGeometry g;
|
|
|
|
QByteArray wkb = geom.asWkb();
|
|
|
|
wkbPtr ptr;
|
|
ptr.ucPtr = !geom.isEmpty() ? reinterpret_cast< unsigned char * >( const_cast<char *>( wkb.constData() ) ) : nullptr;
|
|
g.isNull = !ptr.ucPtr;
|
|
g.gtype = -1;
|
|
g.srid = mSrid < 1 ? -1 : mSrid;
|
|
|
|
if ( !g.isNull )
|
|
{
|
|
ptr.ucPtr++; // skip endianness
|
|
|
|
g.eleminfo.clear();
|
|
g.ordinates.clear();
|
|
|
|
int iOrdinate = 1;
|
|
QgsWkbTypes::Type type = ( QgsWkbTypes::Type ) * ptr.iPtr++;
|
|
int dim = 2;
|
|
|
|
switch ( type )
|
|
{
|
|
case QgsWkbTypes::Point25D:
|
|
case QgsWkbTypes::PointZ:
|
|
dim = 3;
|
|
FALLTHROUGH
|
|
|
|
case QgsWkbTypes::Point:
|
|
g.srid = mSrid;
|
|
g.gtype = SDO_GTYPE( dim, GtPoint );
|
|
g.x = *ptr.dPtr++;
|
|
g.y = *ptr.dPtr++;
|
|
g.z = dim == 3 ? *ptr.dPtr++ : 0.0;
|
|
break;
|
|
|
|
case QgsWkbTypes::LineString25D:
|
|
case QgsWkbTypes::MultiLineString25D:
|
|
case QgsWkbTypes::LineStringZ:
|
|
case QgsWkbTypes::MultiLineStringZ:
|
|
dim = 3;
|
|
FALLTHROUGH
|
|
|
|
case QgsWkbTypes::LineString:
|
|
case QgsWkbTypes::MultiLineString:
|
|
{
|
|
g.gtype = SDO_GTYPE( dim, GtLine );
|
|
int nLines = 1;
|
|
if ( type == QgsWkbTypes::MultiLineString25D || type == QgsWkbTypes::MultiLineString || type == QgsWkbTypes::MultiLineStringZ )
|
|
{
|
|
g.gtype = SDO_GTYPE( dim, GtMultiLine );
|
|
nLines = *ptr.iPtr++;
|
|
ptr.ucPtr++; // Skip endianness of first linestring
|
|
ptr.iPtr++; // Skip type of first linestring
|
|
}
|
|
|
|
for ( int iLine = 0; iLine < nLines; iLine++ )
|
|
{
|
|
g.eleminfo << iOrdinate << 2 << 1;
|
|
|
|
for ( int i = 0, n = *ptr.iPtr++; i < n; i++ )
|
|
{
|
|
g.ordinates << *ptr.dPtr++;
|
|
g.ordinates << *ptr.dPtr++;
|
|
if ( dim == 3 )
|
|
g.ordinates << *ptr.dPtr++;
|
|
iOrdinate += dim;
|
|
}
|
|
|
|
ptr.ucPtr++; // Skip endianness of next linestring
|
|
ptr.iPtr++; // Skip type of next linestring
|
|
}
|
|
}
|
|
break;
|
|
|
|
case QgsWkbTypes::Polygon25D:
|
|
case QgsWkbTypes::MultiPolygon25D:
|
|
case QgsWkbTypes::PolygonZ:
|
|
case QgsWkbTypes::MultiPolygonZ:
|
|
dim = 3;
|
|
FALLTHROUGH
|
|
|
|
case QgsWkbTypes::Polygon:
|
|
case QgsWkbTypes::MultiPolygon:
|
|
{
|
|
g.gtype = SDO_GTYPE( dim, GtPolygon );
|
|
int nPolygons = 1;
|
|
if ( QgsWkbTypes::flatType( type ) == QgsWkbTypes::MultiPolygon )
|
|
{
|
|
g.gtype = SDO_GTYPE( dim, GtMultiPolygon );
|
|
nPolygons = *ptr.iPtr++;
|
|
|
|
ptr.ucPtr++; // Skip endianness of first polygon
|
|
ptr.iPtr++; // Skip type of first polygon
|
|
}
|
|
|
|
for ( int iPolygon = 0; iPolygon < nPolygons; iPolygon++ )
|
|
{
|
|
for ( int iRing = 0, nRings = *ptr.iPtr++; iRing < nRings; iRing++ )
|
|
{
|
|
g.eleminfo << iOrdinate << ( iRing == 0 ? 1003 : 2003 ) << 1;
|
|
|
|
// TODO: verify ring orientation
|
|
for ( int i = 0, n = *ptr.iPtr++; i < n; i++ )
|
|
{
|
|
g.ordinates << *ptr.dPtr++;
|
|
g.ordinates << *ptr.dPtr++;
|
|
if ( dim == 3 )
|
|
g.ordinates << *ptr.dPtr++;
|
|
iOrdinate += dim;
|
|
}
|
|
}
|
|
|
|
ptr.ucPtr++; // Skip endianness of next polygon
|
|
ptr.iPtr++; // Skip type of next polygon
|
|
}
|
|
}
|
|
break;
|
|
|
|
case QgsWkbTypes::MultiPoint25D:
|
|
case QgsWkbTypes::MultiPointZ:
|
|
dim = 3;
|
|
FALLTHROUGH
|
|
|
|
case QgsWkbTypes::MultiPoint:
|
|
{
|
|
g.gtype = SDO_GTYPE( dim, GtMultiPoint );
|
|
int n = *ptr.iPtr++;
|
|
|
|
g.eleminfo << 1 << 1 << n;
|
|
|
|
for ( int i = 0; i < n; i++ )
|
|
{
|
|
ptr.ucPtr++; // Skip endianness of point
|
|
ptr.iPtr++; // Skip type of point
|
|
|
|
g.ordinates << *ptr.dPtr++;
|
|
g.ordinates << *ptr.dPtr++;
|
|
if ( dim == 3 )
|
|
g.ordinates << *ptr.dPtr++;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case QgsWkbTypes::CircularStringZ:
|
|
case QgsWkbTypes::CompoundCurveZ:
|
|
case QgsWkbTypes::MultiCurveZ:
|
|
dim = 3;
|
|
FALLTHROUGH
|
|
|
|
case QgsWkbTypes::CircularString:
|
|
case QgsWkbTypes::CompoundCurve:
|
|
case QgsWkbTypes::MultiCurve:
|
|
{
|
|
g.gtype = SDO_GTYPE( dim, GtLine );
|
|
int nCurves = 1;
|
|
QgsWkbTypes::Type curveType = type;
|
|
if ( type == QgsWkbTypes::MultiCurve || type == QgsWkbTypes::MultiCurveZ )
|
|
{
|
|
g.gtype = SDO_GTYPE( dim, GtMultiLine );
|
|
nCurves = *ptr.iPtr++;
|
|
|
|
ptr.ucPtr++; // Skip endianness of first curve
|
|
curveType = ( QgsWkbTypes::Type ) * ptr.iPtr++; // type of first curve
|
|
}
|
|
|
|
for ( int iCurve = 0; iCurve < nCurves; iCurve++ )
|
|
{
|
|
int nLines = 1;
|
|
QgsWkbTypes::Type lineType = curveType;
|
|
if ( curveType == QgsWkbTypes::CompoundCurve || curveType == QgsWkbTypes::CompoundCurveZ )
|
|
{
|
|
g.gtype = SDO_GTYPE( dim, GtMultiLine );
|
|
nLines = *ptr.iPtr++;
|
|
|
|
// Oracle don't store compound curve with only one line
|
|
if ( nLines > 1 )
|
|
{
|
|
g.eleminfo << iOrdinate << 4 << nLines;
|
|
}
|
|
|
|
ptr.ucPtr++; // Skip endianness of first linestring
|
|
lineType = ( QgsWkbTypes::Type ) * ptr.iPtr++; // type of first linestring
|
|
}
|
|
|
|
for ( int iLine = 0; iLine < nLines; iLine++ )
|
|
{
|
|
bool circularString = lineType == QgsWkbTypes::CircularString || lineType == QgsWkbTypes::CircularStringZ;
|
|
|
|
g.eleminfo << iOrdinate << 2 << ( circularString ? 2 : 1 );
|
|
|
|
for ( int i = 0, n = *ptr.iPtr++; i < n; i++ )
|
|
{
|
|
// Inside a compound curve, two consecutives lines share start/end points
|
|
// We don't repeat this point in ordinates, so we skip the last point (except for last line)
|
|
if ( ( curveType == QgsWkbTypes::CompoundCurve || curveType == QgsWkbTypes::CompoundCurveZ )
|
|
&& i == n - 1 && iLine < nLines - 1 )
|
|
{
|
|
ptr.dPtr += dim;
|
|
continue;
|
|
}
|
|
|
|
g.ordinates << *ptr.dPtr++;
|
|
g.ordinates << *ptr.dPtr++;
|
|
if ( dim == 3 )
|
|
g.ordinates << *ptr.dPtr++;
|
|
|
|
iOrdinate += dim;
|
|
}
|
|
|
|
ptr.ucPtr++; // Skip endianness of next linestring
|
|
lineType = ( QgsWkbTypes::Type ) * ptr.iPtr++; // type of next linestring
|
|
}
|
|
|
|
curveType = lineType; // type of next curve
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case QgsWkbTypes::CurvePolygonZ:
|
|
case QgsWkbTypes::MultiSurfaceZ:
|
|
dim = 3;
|
|
FALLTHROUGH
|
|
|
|
case QgsWkbTypes::CurvePolygon:
|
|
case QgsWkbTypes::MultiSurface:
|
|
{
|
|
g.gtype = SDO_GTYPE( dim, GtPolygon );
|
|
int nSurfaces = 1;
|
|
if ( type == QgsWkbTypes::MultiSurface || type == QgsWkbTypes::MultiSurfaceZ )
|
|
{
|
|
g.gtype = SDO_GTYPE( dim, GtMultiPolygon );
|
|
nSurfaces = *ptr.iPtr++;
|
|
|
|
ptr.ucPtr++; // Skip endianness of first surface
|
|
ptr.iPtr++; // Skip type of first surface
|
|
}
|
|
|
|
for ( int iSurface = 0; iSurface < nSurfaces; iSurface++ )
|
|
{
|
|
const int nRings = *ptr.iPtr++;
|
|
|
|
ptr.ucPtr++; // Skip endianness of first ring
|
|
QgsWkbTypes::Type ringType = ( QgsWkbTypes::Type ) * ptr.iPtr++; // type of first ring
|
|
|
|
QgsWkbTypes::Type lineType;
|
|
for ( int iRing = 0; iRing < nRings; iRing++ )
|
|
{
|
|
int nLines = 1;
|
|
lineType = ringType;
|
|
if ( QgsWkbTypes::flatType( ringType ) == QgsWkbTypes::CompoundCurve )
|
|
{
|
|
nLines = *ptr.iPtr++;
|
|
|
|
ptr.ucPtr++; // Skip endianness of first linestring
|
|
lineType = ( QgsWkbTypes::Type ) * ptr.iPtr++; // type of first linestring
|
|
}
|
|
|
|
// Oracle don't store compound curve with only one line
|
|
g.eleminfo << iOrdinate
|
|
<< ( iRing == 0 ? 1000 : 2000 ) + ( nLines > 1 ? 5 : 3 )
|
|
<< ( nLines > 1 ? nLines : ( QgsWkbTypes::flatType( ringType ) == QgsWkbTypes::CircularString ? 2 : 1 ) );
|
|
|
|
for ( int iLine = 0; iLine < nLines; iLine++ )
|
|
{
|
|
if ( nLines > 1 )
|
|
g.eleminfo << iOrdinate << 2 << ( QgsWkbTypes::flatType( lineType ) == QgsWkbTypes::CircularString ? 2 : 1 );
|
|
|
|
for ( int i = 0, n = *ptr.iPtr++; i < n; i++ )
|
|
{
|
|
// Inside a compound polygon, two consecutives lines share start/end points
|
|
// We don't repeat this point in ordinates, so we skip the last point (except for last line)
|
|
if ( QgsWkbTypes::flatType( ringType ) == QgsWkbTypes::CompoundCurve
|
|
&& i == n - 1 && iLine < nLines - 1 )
|
|
{
|
|
ptr.dPtr += dim;
|
|
continue;
|
|
}
|
|
|
|
g.ordinates << *ptr.dPtr++;
|
|
g.ordinates << *ptr.dPtr++;
|
|
if ( dim == 3 )
|
|
g.ordinates << *ptr.dPtr++;
|
|
|
|
iOrdinate += dim;
|
|
}
|
|
|
|
ptr.ucPtr++; // Skip endianness of next linestring
|
|
lineType = ( QgsWkbTypes::Type ) * ptr.iPtr++; // type of next linestring
|
|
}
|
|
|
|
ringType = lineType; // type of next curve
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
// unsupported M values
|
|
case QgsWkbTypes::PointM:
|
|
case QgsWkbTypes::PointZM:
|
|
case QgsWkbTypes::LineStringM:
|
|
case QgsWkbTypes::LineStringZM:
|
|
case QgsWkbTypes::PolygonM:
|
|
case QgsWkbTypes::PolygonZM:
|
|
case QgsWkbTypes::MultiPointM:
|
|
case QgsWkbTypes::MultiPointZM:
|
|
case QgsWkbTypes::MultiLineStringM:
|
|
case QgsWkbTypes::MultiLineStringZM:
|
|
case QgsWkbTypes::MultiPolygonM:
|
|
case QgsWkbTypes::MultiPolygonZM:
|
|
case QgsWkbTypes::CircularStringM:
|
|
case QgsWkbTypes::CircularStringZM:
|
|
case QgsWkbTypes::CompoundCurveM:
|
|
case QgsWkbTypes::CompoundCurveZM:
|
|
case QgsWkbTypes::MultiCurveM:
|
|
case QgsWkbTypes::MultiCurveZM:
|
|
case QgsWkbTypes::CurvePolygonM:
|
|
case QgsWkbTypes::CurvePolygonZM:
|
|
case QgsWkbTypes::MultiSurfaceM:
|
|
case QgsWkbTypes::MultiSurfaceZM:
|
|
|
|
// other unsupported or missing geometry types
|
|
case QgsWkbTypes::GeometryCollection:
|
|
case QgsWkbTypes::GeometryCollectionZ:
|
|
case QgsWkbTypes::GeometryCollectionM:
|
|
case QgsWkbTypes::GeometryCollectionZM:
|
|
case QgsWkbTypes::Triangle:
|
|
case QgsWkbTypes::TriangleZ:
|
|
case QgsWkbTypes::TriangleM:
|
|
case QgsWkbTypes::TriangleZM:
|
|
case QgsWkbTypes::Unknown:
|
|
case QgsWkbTypes::NoGeometry:
|
|
|
|
g.isNull = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
QgsDebugMsgLevel( QStringLiteral( "addBindValue geometry: isNull=%1 gtype=%2 srid=%3 p=%4,%5,%6 eleminfo=%7 ordinates=%8" )
|
|
.arg( g.isNull )
|
|
.arg( g.gtype )
|
|
.arg( g.srid )
|
|
.arg( g.x ).arg( g.y ).arg( g.z )
|
|
.arg( g.eleminfo.size() )
|
|
.arg( g.ordinates.size() )
|
|
, 4 );
|
|
qry.addBindValue( QVariant::fromValue( g ) );
|
|
}
|
|
|
|
bool QgsOracleProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
|
|
{
|
|
QgsOracleConn *conn = connectionRW();
|
|
if ( mIsQuery || mGeometryColumn.isNull() || !conn )
|
|
return false;
|
|
|
|
QSqlDatabase db( *conn );
|
|
|
|
bool returnvalue = true;
|
|
|
|
try
|
|
{
|
|
QSqlQuery qry( db );
|
|
if ( !conn->begin( db ) )
|
|
{
|
|
throw OracleException( tr( "Could not start transaction" ), db );
|
|
}
|
|
|
|
QString update = QString( "UPDATE %1 SET %2=? WHERE %3" )
|
|
.arg( mQuery )
|
|
.arg( quotedIdentifier( mGeometryColumn ) )
|
|
.arg( pkParamWhereClause() );
|
|
QgsDebugMsgLevel( QStringLiteral( "SQL prepare: %1" ).arg( update ), 4 );
|
|
if ( !qry.prepare( update ) )
|
|
{
|
|
throw OracleException( tr( "Could not prepare update statement." ), qry );
|
|
}
|
|
|
|
for ( QgsGeometryMap::const_iterator iter = geometry_map.constBegin();
|
|
iter != geometry_map.constEnd();
|
|
++iter )
|
|
{
|
|
appendGeomParam( iter.value(), qry );
|
|
appendPkParams( iter.key(), qry );
|
|
|
|
if ( !qry.exec() )
|
|
throw OracleException( tr( "Update of feature %1 failed" ).arg( iter.key() ), qry );
|
|
}
|
|
|
|
qry.finish();
|
|
|
|
if ( !conn->commit( db ) )
|
|
{
|
|
throw OracleException( tr( "Could not commit transaction" ), db );
|
|
}
|
|
|
|
if ( mTransaction )
|
|
mTransaction->dirtyLastSavePoint();
|
|
}
|
|
catch ( OracleException &e )
|
|
{
|
|
pushError( tr( "Oracle error while changing geometry values: %1" ).arg( e.errorMessage() ) );
|
|
if ( !conn->rollback( db ) )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Could not rollback transaction" ), tr( "Oracle" ) );
|
|
}
|
|
returnvalue = false;
|
|
}
|
|
|
|
QgsDebugMsg( QStringLiteral( "exiting." ) );
|
|
|
|
return returnvalue;
|
|
}
|
|
|
|
QgsVectorDataProvider::Capabilities QgsOracleProvider::capabilities() const
|
|
{
|
|
return mEnabledCapabilities;
|
|
}
|
|
|
|
bool QgsOracleProvider::setSubsetString( const QString &theSQL, bool updateFeatureCount )
|
|
{
|
|
QgsOracleConn *conn = connectionRO();
|
|
if ( !conn )
|
|
return false;
|
|
|
|
if ( theSQL.trimmed() == mSqlWhereClause )
|
|
return true;
|
|
|
|
QString prevWhere = mSqlWhereClause;
|
|
|
|
mSqlWhereClause = theSQL.trimmed();
|
|
|
|
QString sql = QString( "SELECT * FROM %1 WHERE " ).arg( mQuery );
|
|
|
|
if ( !mSqlWhereClause.isEmpty() )
|
|
{
|
|
sql += "(" + mSqlWhereClause + ") AND ";
|
|
}
|
|
|
|
sql += "1=0";
|
|
|
|
QSqlQuery qry( *conn );
|
|
if ( !exec( qry, sql, QVariantList() ) )
|
|
{
|
|
pushError( qry.lastError().text() );
|
|
mSqlWhereClause = prevWhere;
|
|
qry.finish();
|
|
return false;
|
|
}
|
|
qry.finish();
|
|
|
|
if ( mPrimaryKeyType == PktInt && !mUseEstimatedMetadata && !uniqueData( mQuery, mAttributeFields.at( mPrimaryKeyAttrs[0] ).name() ) )
|
|
{
|
|
mSqlWhereClause = prevWhere;
|
|
return false;
|
|
}
|
|
|
|
// Update datasource uri too
|
|
mUri.setSql( theSQL );
|
|
// Update yet another copy of the uri. Why are there 3 copies of the
|
|
// uri? Perhaps this needs some rationalisation.....
|
|
setDataSourceUri( mUri.uri( false ) );
|
|
|
|
if ( updateFeatureCount )
|
|
{
|
|
mFeaturesCounted = -1;
|
|
}
|
|
mLayerExtent.setMinimal();
|
|
|
|
emit dataChanged();
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns the feature count
|
|
*/
|
|
long QgsOracleProvider::featureCount() const
|
|
{
|
|
QgsOracleConn *conn = connectionRO();
|
|
if ( mFeaturesCounted >= 0 || !conn )
|
|
return mFeaturesCounted;
|
|
|
|
// get total number of features
|
|
QString sql;
|
|
|
|
// use estimated metadata even when there is a where clause,
|
|
// although we get an incorrect feature count for the subset
|
|
// - but make huge dataset usable.
|
|
QVariantList args;
|
|
if ( !mIsQuery && mUseEstimatedMetadata )
|
|
{
|
|
sql = QString( "SELECT num_rows FROM all_tables WHERE owner=? AND table_name=?" );
|
|
args << mOwnerName << mTableName;
|
|
}
|
|
else
|
|
{
|
|
sql = QString( "SELECT count(*) FROM %1" ).arg( mQuery );
|
|
|
|
if ( !mSqlWhereClause.isEmpty() )
|
|
{
|
|
sql += " WHERE " + mSqlWhereClause;
|
|
}
|
|
}
|
|
|
|
QSqlQuery qry( *conn );
|
|
if ( exec( qry, sql, args ) && qry.next() )
|
|
{
|
|
mFeaturesCounted = qry.value( 0 ).toInt();
|
|
}
|
|
qry.finish();
|
|
|
|
QgsDebugMsg( "number of features: " + QString::number( mFeaturesCounted ) );
|
|
|
|
return mFeaturesCounted;
|
|
}
|
|
|
|
QgsRectangle QgsOracleProvider::extent() const
|
|
{
|
|
QgsOracleConn *conn = connectionRO();
|
|
if ( mGeometryColumn.isNull() || !conn )
|
|
return QgsRectangle();
|
|
|
|
if ( mLayerExtent.isEmpty() )
|
|
{
|
|
QString sql;
|
|
QSqlQuery qry( *conn );
|
|
bool ok = false;
|
|
|
|
if ( !mIsQuery )
|
|
{
|
|
if ( mUseEstimatedMetadata )
|
|
{
|
|
// TODO: make SDO_DIMNAME values configurable (#16252)
|
|
if ( exec( qry, QStringLiteral( "SELECT sdo_lb,sdo_ub FROM mdsys.all_sdo_geom_metadata m, table(m.diminfo) WHERE owner=? AND table_name=? AND column_name=? AND sdo_dimname='X'" ),
|
|
QVariantList() << mOwnerName << mTableName << mGeometryColumn ) &&
|
|
qry.next() )
|
|
{
|
|
mLayerExtent.setXMinimum( qry.value( 0 ).toDouble() );
|
|
mLayerExtent.setXMaximum( qry.value( 1 ).toDouble() );
|
|
|
|
if ( exec( qry, QStringLiteral( "SELECT sdo_lb,sdo_ub FROM mdsys.all_sdo_geom_metadata m, table(m.diminfo) WHERE owner=? AND table_name=? AND column_name=? AND sdo_dimname='Y'" ),
|
|
QVariantList() << mOwnerName << mTableName << mGeometryColumn ) &&
|
|
qry.next() )
|
|
{
|
|
mLayerExtent.setYMinimum( qry.value( 0 ).toDouble() );
|
|
mLayerExtent.setYMaximum( qry.value( 1 ).toDouble() );
|
|
return mLayerExtent;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( mHasSpatialIndex && mUseEstimatedMetadata )
|
|
{
|
|
ok = exec( qry,
|
|
QStringLiteral( "SELECT SDO_TUNE.EXTENT_OF(?,?) FROM dual" ),
|
|
QVariantList() << QString( "%1.%2" ).arg( mOwnerName ).arg( mTableName ) << mGeometryColumn );
|
|
}
|
|
}
|
|
|
|
if ( !ok )
|
|
{
|
|
sql = QString( "SELECT SDO_AGGR_MBR(%1) FROM %2" ).arg( quotedIdentifier( mGeometryColumn ) ).arg( mQuery );
|
|
|
|
if ( !mSqlWhereClause.isEmpty() )
|
|
sql += QString( " WHERE %1" ).arg( mSqlWhereClause );
|
|
|
|
ok = exec( qry, sql, QVariantList() );
|
|
}
|
|
|
|
if ( ok && qry.next() )
|
|
{
|
|
QByteArray ba( qry.value( 0 ).toByteArray() );
|
|
QgsGeometry g;
|
|
g.fromWkb( ba );
|
|
mLayerExtent = g.boundingBox();
|
|
QgsDebugMsg( "extent: " + mLayerExtent.toString() );
|
|
}
|
|
else
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Could not retrieve extents: %1\nSQL: %2" ).arg( qry.lastError().text() ).arg( qry.lastQuery() ), tr( "Oracle" ) );
|
|
}
|
|
}
|
|
|
|
return mLayerExtent;
|
|
}
|
|
|
|
void QgsOracleProvider::updateExtents()
|
|
{
|
|
mLayerExtent.setMinimal();
|
|
}
|
|
|
|
bool QgsOracleProvider::getGeometryDetails()
|
|
{
|
|
if ( mGeometryColumn.isNull() )
|
|
{
|
|
mDetectedGeomType = QgsWkbTypes::NoGeometry;
|
|
mValid = true;
|
|
return true;
|
|
}
|
|
|
|
QString ownerName = mOwnerName;
|
|
QString tableName = mTableName;
|
|
QString geomCol = mGeometryColumn;
|
|
|
|
QgsOracleConn *conn = connectionRO();
|
|
QSqlQuery qry( *conn );
|
|
if ( mIsQuery )
|
|
{
|
|
if ( !exec( qry, QString( "SELECT %1 FROM %2 WHERE 1=0" ).arg( quotedIdentifier( mGeometryColumn ) ).arg( mQuery ), QVariantList() ) )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Could not execute query.\nThe error message from the database was:\n%1.\nSQL: %2" )
|
|
.arg( qry.lastError().text() )
|
|
.arg( qry.lastQuery() ), tr( "Oracle" ) );
|
|
mValid = false;
|
|
return false;
|
|
}
|
|
|
|
ownerName = "";
|
|
tableName = mQuery;
|
|
}
|
|
|
|
int detectedSrid = -1;
|
|
QgsWkbTypes::Type detectedType = QgsWkbTypes::Unknown;
|
|
mHasSpatialIndex = false;
|
|
|
|
if ( mIsQuery )
|
|
{
|
|
detectedSrid = mSrid;
|
|
detectedType = mRequestedGeomType;
|
|
}
|
|
|
|
if ( !ownerName.isEmpty() )
|
|
{
|
|
if ( exec( qry, QString( "SELECT srid FROM mdsys.all_sdo_geom_metadata WHERE owner=? AND table_name=? AND column_name=?" ),
|
|
QVariantList() << ownerName << tableName << geomCol ) )
|
|
{
|
|
if ( qry.next() )
|
|
{
|
|
detectedSrid = qry.value( 0 ).toInt();
|
|
}
|
|
else
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Could not retrieve SRID of %1.\nThe error message from the database was:\n%2.\nSQL: %3" )
|
|
.arg( mQuery )
|
|
.arg( qry.lastError().text() )
|
|
.arg( qry.lastQuery() ), tr( "Oracle" ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Could not determine SRID of %1.\nThe error message from the database was:\n%2.\nSQL: %3" )
|
|
.arg( mQuery )
|
|
.arg( qry.lastError().text() )
|
|
.arg( qry.lastQuery() ), tr( "Oracle" ) );
|
|
}
|
|
|
|
if ( exec( qry, QString( mUseEstimatedMetadata
|
|
? "SELECT DISTINCT gtype FROM (SELECT t.%1.sdo_gtype AS gtype FROM %2 t WHERE t.%1 IS NOT NULL AND rownum<100) WHERE rownum<=2"
|
|
: "SELECT DISTINCT t.%1.sdo_gtype FROM %2 t WHERE t.%1 IS NOT NULL AND rownum<=2" ).arg( quotedIdentifier( geomCol ) ).arg( mQuery ), QVariantList() ) )
|
|
{
|
|
if ( qry.next() )
|
|
{
|
|
detectedType = QgsOracleConn::wkbTypeFromDatabase( qry.value( 0 ).toInt() );
|
|
if ( qry.next() )
|
|
{
|
|
detectedType = QgsWkbTypes::Unknown;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
detectedType = QgsWkbTypes::Unknown;
|
|
QgsMessageLog::logMessage( tr( "%1 has no valid geometry types.\nSQL: %2" )
|
|
.arg( mQuery )
|
|
.arg( qry.lastQuery() ), tr( "Oracle" ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Could not determine geometry type of %1.\nThe error message from the database was:\n%2.\nSQL: %3" )
|
|
.arg( mQuery )
|
|
.arg( qry.lastError().text() )
|
|
.arg( qry.lastQuery() ), tr( "Oracle" ) );
|
|
}
|
|
}
|
|
|
|
if ( detectedType == QgsWkbTypes::Unknown || detectedSrid <= 0 )
|
|
{
|
|
QgsOracleLayerProperty layerProperty;
|
|
|
|
if ( !mIsQuery )
|
|
{
|
|
layerProperty.ownerName = ownerName;
|
|
layerProperty.tableName = tableName;
|
|
layerProperty.geometryColName = mGeometryColumn;
|
|
layerProperty.types << detectedType;
|
|
layerProperty.srids << detectedSrid;
|
|
|
|
QString delim = "";
|
|
|
|
if ( !mSqlWhereClause.isEmpty() )
|
|
{
|
|
layerProperty.sql += delim + "(" + mSqlWhereClause + ")";
|
|
delim = " AND ";
|
|
}
|
|
|
|
conn->retrieveLayerTypes( layerProperty, mUseEstimatedMetadata, false );
|
|
|
|
Q_ASSERT( layerProperty.types.size() == layerProperty.srids.size() );
|
|
}
|
|
|
|
if ( layerProperty.types.isEmpty() )
|
|
{
|
|
// no data - so take what's requested
|
|
if ( mRequestedGeomType == QgsWkbTypes::Unknown )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Geometry type and srid for empty column %1 of %2 undefined." ).arg( mGeometryColumn ).arg( mQuery ) );
|
|
}
|
|
|
|
detectedType = QgsWkbTypes::Unknown;
|
|
detectedSrid = -1;
|
|
}
|
|
else
|
|
{
|
|
// requested type && srid is available
|
|
if ( mRequestedGeomType == QgsWkbTypes::Unknown || layerProperty.types.contains( mRequestedGeomType ) )
|
|
{
|
|
if ( layerProperty.size() == 1 )
|
|
{
|
|
// only what we requested is available
|
|
detectedType = layerProperty.types.at( 0 );
|
|
detectedSrid = layerProperty.srids.at( 0 );
|
|
}
|
|
else
|
|
{
|
|
// we need to filter
|
|
detectedType = QgsWkbTypes::Unknown;
|
|
detectedSrid = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// geometry type undetermined or not unrequested
|
|
QgsMessageLog::logMessage( tr( "Feature type or srid for %1 of %2 could not be determined or was not requested." ).arg( mGeometryColumn ).arg( mQuery ) );
|
|
detectedType = QgsWkbTypes::Unknown;
|
|
detectedSrid = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
mDetectedGeomType = detectedType;
|
|
if ( detectedSrid != -1 )
|
|
mSrid = detectedSrid;
|
|
|
|
QgsDebugMsg( QStringLiteral( "Detected Oracle SRID is %1" ).arg( mSrid ) );
|
|
QgsDebugMsg( QStringLiteral( "Detected type is %1" ).arg( mDetectedGeomType ) );
|
|
QgsDebugMsg( QStringLiteral( "Requested type is %1" ).arg( mRequestedGeomType ) );
|
|
|
|
mValid = ( mDetectedGeomType != QgsWkbTypes::Unknown || mRequestedGeomType != QgsWkbTypes::Unknown );
|
|
|
|
if ( !mValid )
|
|
return false;
|
|
|
|
QgsDebugMsg( QStringLiteral( "Feature type name is %1" ).arg( QgsWkbTypes::displayString( wkbType() ) ) );
|
|
|
|
return mValid;
|
|
}
|
|
|
|
bool QgsOracleProvider::createSpatialIndex()
|
|
{
|
|
QgsOracleConn *conn = connectionRW();
|
|
if ( !conn )
|
|
return false;
|
|
|
|
QSqlQuery qry( *conn );
|
|
|
|
if ( !crs().isGeographic() )
|
|
{
|
|
// TODO: make precision configurable
|
|
// TODO: make SDO_DIMNAME values configurable (#16252)
|
|
QgsRectangle r( extent() );
|
|
if ( !exec( qry, QString( "UPDATE mdsys.user_sdo_geom_metadata SET diminfo=mdsys.sdo_dim_array("
|
|
"mdsys.sdo_dim_element('X', ?, ?, 0.001),"
|
|
"mdsys.sdo_dim_element('Y', ?, ?, 0.001)"
|
|
") WHERE table_name=? AND column_name=?" ),
|
|
QVariantList() << r.xMinimum() << r.xMaximum() << r.yMinimum() << r.yMaximum() << mTableName << mGeometryColumn )
|
|
)
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Could not update metadata for %1.%2.\nSQL: %3\nError: %4" )
|
|
.arg( mTableName )
|
|
.arg( mGeometryColumn )
|
|
.arg( qry.lastQuery() )
|
|
.arg( qry.lastError().text() ),
|
|
tr( "Oracle" ) );
|
|
return false;
|
|
}
|
|
|
|
if ( qry.numRowsAffected() == 0 )
|
|
{
|
|
if ( !exec( qry, QString( "INSERT INTO mdsys.user_sdo_geom_metadata(table_name,column_name,srid,diminfo) VALUES (?,?,?,mdsys.sdo_dim_array("
|
|
"mdsys.sdo_dim_element('X', ?, ?, 0.001),"
|
|
"mdsys.sdo_dim_element('Y', ?, ?, 0.001)"
|
|
"))" ),
|
|
QVariantList() << mTableName << mGeometryColumn << ( mSrid < 1 ? QVariant( QVariant::Int ) : mSrid )
|
|
<< r.xMinimum() << r.xMaximum() << r.yMinimum() << r.yMaximum() )
|
|
)
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Could not insert metadata for %1.%2.\nSQL: %3\nError: %4" )
|
|
.arg( quotedValue( mTableName ) )
|
|
.arg( quotedValue( mGeometryColumn ) )
|
|
.arg( qry.lastQuery() )
|
|
.arg( qry.lastError().text() ),
|
|
tr( "Oracle" ) );
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QgsDebugMsg( QStringLiteral( "geographic CRS" ) );
|
|
}
|
|
|
|
if ( !mHasSpatialIndex )
|
|
{
|
|
int n = 0;
|
|
if ( exec( qry, QString( "SELECT coalesce(substr(max(index_name),10),'0') FROM all_indexes WHERE index_name LIKE 'QGIS_IDX_%' ESCAPE '#' ORDER BY index_name" ), QVariantList() ) &&
|
|
qry.next() )
|
|
{
|
|
n = qry.value( 0 ).toInt() + 1;
|
|
}
|
|
|
|
if ( !exec( qry, QString( "CREATE INDEX QGIS_IDX_%1 ON %2.%3(%4) INDEXTYPE IS MDSYS.SPATIAL_INDEX PARALLEL" )
|
|
.arg( n, 10, 10, QChar( '0' ) )
|
|
.arg( quotedIdentifier( mOwnerName ) )
|
|
.arg( quotedIdentifier( mTableName ) )
|
|
.arg( quotedIdentifier( mGeometryColumn ) ), QVariantList() ) )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Creation spatial index failed.\nSQL: %1\nError: %2" )
|
|
.arg( qry.lastQuery() )
|
|
.arg( qry.lastError().text() ),
|
|
tr( "Oracle" ) );
|
|
return false;
|
|
}
|
|
|
|
mSpatialIndexName = QString( "QGIS_IDX_%1" ).arg( n, 10, 10, QChar( '0' ) );
|
|
}
|
|
else
|
|
{
|
|
if ( !exec( qry, QString( "ALTER INDEX %1 REBUILD" ).arg( mSpatialIndexName ), QVariantList() ) )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Rebuild of spatial index failed.\nSQL: %1\nError: %2" )
|
|
.arg( qry.lastQuery() )
|
|
.arg( qry.lastError().text() ),
|
|
tr( "Oracle" ) );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool QgsOracleProvider::convertField( QgsField &field )
|
|
{
|
|
QString fieldType = "VARCHAR2(2047)"; //default to string
|
|
int fieldSize = field.length();
|
|
int fieldPrec = field.precision();
|
|
switch ( field.type() )
|
|
{
|
|
case QVariant::LongLong:
|
|
fieldType = "NUMBER(20,0)";
|
|
fieldSize = -1;
|
|
fieldPrec = 0;
|
|
break;
|
|
|
|
case QVariant::DateTime:
|
|
fieldType = "TIMESTAMP";
|
|
fieldPrec = -1;
|
|
break;
|
|
|
|
|
|
case QVariant::Time:
|
|
case QVariant::String:
|
|
fieldType = "VARCHAR2(2047)";
|
|
fieldPrec = -1;
|
|
break;
|
|
|
|
case QVariant::Date:
|
|
fieldType = "DATE";
|
|
fieldPrec = -1;
|
|
break;
|
|
|
|
case QVariant::Int:
|
|
fieldType = "NUMBER(10,0)";
|
|
fieldSize = -1;
|
|
fieldPrec = 0;
|
|
break;
|
|
|
|
case QVariant::Double:
|
|
if ( fieldSize <= 0 || fieldPrec <= 0 )
|
|
{
|
|
fieldType = "BINARY_DOUBLE";
|
|
fieldSize = -1;
|
|
fieldPrec = -1;
|
|
}
|
|
else
|
|
{
|
|
fieldType = QString( "NUMBER(%1,%2)" ).arg( fieldSize ).arg( fieldPrec );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
field.setTypeName( fieldType );
|
|
field.setLength( fieldSize );
|
|
field.setPrecision( fieldPrec );
|
|
return true;
|
|
}
|
|
|
|
QgsVectorLayerExporter::ExportError QgsOracleProvider::createEmptyLayer(
|
|
const QString &uri,
|
|
const QgsFields &fields,
|
|
QgsWkbTypes::Type wkbType,
|
|
const QgsCoordinateReferenceSystem &srs,
|
|
bool overwrite,
|
|
QMap<int, int> &oldToNewAttrIdxMap,
|
|
QString &errorMessage,
|
|
const QMap<QString, QVariant> *options )
|
|
{
|
|
Q_UNUSED( wkbType )
|
|
Q_UNUSED( options )
|
|
|
|
// populate members from the uri structure
|
|
QgsDataSourceUri dsUri( uri );
|
|
QString ownerName = dsUri.schema();
|
|
|
|
QgsDebugMsg( QStringLiteral( "Connection info is: %1" ).arg( dsUri.connectionInfo( false ) ) );
|
|
|
|
// create the table
|
|
QgsOracleConn *conn = QgsOracleConn::connectDb( dsUri, false );
|
|
if ( !conn )
|
|
{
|
|
errorMessage = QObject::tr( "Connection to database failed" );
|
|
return QgsVectorLayerExporter::ErrConnectionFailed;
|
|
}
|
|
|
|
if ( ownerName.isEmpty() )
|
|
{
|
|
ownerName = conn->currentUser();
|
|
}
|
|
|
|
if ( ownerName.isEmpty() )
|
|
{
|
|
errorMessage = QObject::tr( "No owner name found" );
|
|
return QgsVectorLayerExporter::ErrInvalidLayer;
|
|
}
|
|
|
|
QString tableName = dsUri.table();
|
|
QString geometryColumn = dsUri.geometryColumn();
|
|
|
|
QString primaryKey = dsUri.keyColumn();
|
|
QString primaryKeyType;
|
|
|
|
QString ownerTableName = quotedIdentifier( ownerName ) + "." + quotedIdentifier( tableName );
|
|
|
|
QgsDebugMsg( QStringLiteral( "Geometry column is: %1" ).arg( geometryColumn ) );
|
|
QgsDebugMsg( QStringLiteral( "Owner is: %1" ).arg( ownerName ) );
|
|
QgsDebugMsg( QStringLiteral( "Table name is: %1" ).arg( tableName ) );
|
|
|
|
// get the pk's name and type
|
|
|
|
// if no pk name was passed, define the new pk field name
|
|
if ( primaryKey.isEmpty() )
|
|
{
|
|
int index = 0;
|
|
QString pk = primaryKey = "id";
|
|
while ( fields.indexFromName( primaryKey ) >= 0 )
|
|
{
|
|
primaryKey = QString( "%1_%2" ).arg( pk ).arg( index++ );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int idx = fields.indexFromName( primaryKey );
|
|
if ( idx >= 0 )
|
|
{
|
|
QgsField fld = fields.at( idx );
|
|
if ( convertField( fld ) )
|
|
{
|
|
primaryKeyType = fld.typeName();
|
|
}
|
|
}
|
|
}
|
|
|
|
QSqlDatabase db( *conn );
|
|
QSqlQuery qry( db );
|
|
bool created = false;
|
|
try
|
|
{
|
|
if ( !conn->begin( db ) )
|
|
{
|
|
throw OracleException( tr( "Could not start transaction" ), db );
|
|
}
|
|
|
|
if ( !exec( qry, QString( "SELECT 1 FROM all_tables WHERE owner=? AND table_name=?" ),
|
|
QVariantList() << ownerName << tableName
|
|
) )
|
|
{
|
|
throw OracleException( tr( "Could not determine table existence." ), qry );
|
|
}
|
|
|
|
bool exists = qry.next();
|
|
|
|
if ( exists )
|
|
{
|
|
if ( overwrite )
|
|
{
|
|
// delete the table if exists, then re-create it
|
|
if ( !exec( qry, QString( "DROP TABLE %1" ).arg( ownerTableName ), QVariantList() ) )
|
|
{
|
|
throw OracleException( tr( "Table %1 could not be dropped." ).arg( ownerTableName ), qry );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw OracleException( tr( "Table %1 already exists." ).arg( ownerTableName ), qry );
|
|
}
|
|
}
|
|
|
|
QString sql = QString( "CREATE TABLE %1(" ).arg( ownerTableName );
|
|
QString delim;
|
|
|
|
if ( !primaryKey.isEmpty() && !primaryKeyType.isEmpty() )
|
|
{
|
|
sql += QString( "%1 %2 PRIMARY KEY" ).arg( quotedIdentifier( primaryKey ) ).arg( primaryKeyType );
|
|
delim = ",";
|
|
}
|
|
|
|
// create geometry column
|
|
sql += QString( "%1%2 MDSYS.SDO_GEOMETRY)" ).arg( delim ).arg( quotedIdentifier( geometryColumn ) );
|
|
delim = ",";
|
|
|
|
if ( !exec( qry, sql, QVariantList() ) )
|
|
{
|
|
throw OracleException( tr( "Table creation failed." ), qry );
|
|
}
|
|
|
|
created = true;
|
|
|
|
// TODO: make precision configurable
|
|
QString diminfo;
|
|
if ( srs.isGeographic() )
|
|
{
|
|
diminfo = "mdsys.sdo_dim_array("
|
|
"mdsys.sdo_dim_element('Longitude', -180, 180, 0.001),"
|
|
"mdsys.sdo_dim_element('Latitude', -90, 90, 0.001)"
|
|
")";
|
|
}
|
|
else
|
|
{
|
|
diminfo = "mdsys.sdo_dim_array("
|
|
"mdsys.sdo_dim_element('X', NULL, NULL, 0.001),"
|
|
"mdsys.sdo_dim_element('Y', NULL, NULL, 0.001)"
|
|
")";
|
|
}
|
|
|
|
int srid = 0;
|
|
QStringList parts = srs.authid().split( ":" );
|
|
if ( parts.size() == 2 )
|
|
{
|
|
// apparently some EPSG codes don't have the auth_name setup in cs_srs
|
|
if ( !exec( qry, QString( "SELECT srid FROM mdsys.cs_srs WHERE coalesce(auth_name,'EPSG')=? AND auth_srid=?" ),
|
|
QVariantList() << parts[0] << parts[1] ) )
|
|
{
|
|
throw OracleException( tr( "Could not lookup authid %1:%2" ).arg( parts[0] ).arg( parts[1] ), qry );
|
|
}
|
|
|
|
if ( qry.next() )
|
|
{
|
|
srid = qry.value( 0 ).toInt();
|
|
}
|
|
}
|
|
|
|
if ( srid == 0 )
|
|
{
|
|
QgsDebugMsg( QStringLiteral( "%1:%2 not found in mdsys.cs_srs - trying WKT" ).arg( parts[0] ).arg( parts[1] ) );
|
|
|
|
QString wkt = srs.toWkt();
|
|
if ( !exec( qry, QStringLiteral( "SELECT srid FROM mdsys.cs_srs WHERE wktext=?" ), QVariantList() << wkt ) )
|
|
{
|
|
throw OracleException( tr( "Could not lookup WKT." ), qry );
|
|
}
|
|
|
|
if ( qry.next() )
|
|
{
|
|
srid = qry.value( 0 ).toInt();
|
|
}
|
|
else
|
|
{
|
|
if ( !exec( qry, QStringLiteral( "SELECT max(srid)+1 FROM sdo_coord_ref_system" ), QVariantList() ) || !qry.next() )
|
|
{
|
|
throw OracleException( tr( "Could not determine new srid." ), qry );
|
|
}
|
|
|
|
srid = qry.value( 0 ).toInt();
|
|
|
|
QString sql;
|
|
|
|
if ( !exec( qry, QStringLiteral( "INSERT"
|
|
" INTO sdo_coord_ref_system(srid,coord_ref_sys_name,coord_ref_sys_kind,legacy_wktext,is_valid,is_legacy,information_source)"
|
|
" VALUES (?,?,?,?,'TRUE','TRUE','GDAL/OGR via QGIS')" ),
|
|
QVariantList() << srid << srs.description() << ( srs.isGeographic() ? "GEOGRAPHIC2D" : "PROJECTED" ) << wkt ) )
|
|
{
|
|
throw OracleException( tr( "CRS not found and could not be created." ), qry );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !exec( qry, QString( "INSERT INTO mdsys.user_sdo_geom_metadata(table_name,column_name,srid,diminfo) VALUES (?,?,?,?)" ),
|
|
QVariantList() << tableName.toUpper() << geometryColumn.toUpper() << srid << diminfo ) )
|
|
{
|
|
throw OracleException( tr( "Could not insert metadata." ), qry );
|
|
}
|
|
|
|
if ( !conn->commit( db ) )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Could not commit transaction" ), tr( "Oracle" ) );
|
|
}
|
|
}
|
|
catch ( OracleException &e )
|
|
{
|
|
errorMessage = QObject::tr( "Creation of data source %1 failed: \n%2" )
|
|
.arg( ownerTableName )
|
|
.arg( e.errorMessage() );
|
|
|
|
if ( !conn->rollback( db ) )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Could not rollback transaction" ), tr( "Oracle" ) );
|
|
}
|
|
|
|
if ( created )
|
|
{
|
|
if ( !exec( qry, QString( "DROP TABLE %1" ).arg( ownerTableName ), QVariantList() ) )
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Drop created table %1 failed.\nSQL: %2\nError: %3" )
|
|
.arg( qry.lastQuery() )
|
|
.arg( qry.lastError().text() ), tr( "Oracle" ) );
|
|
}
|
|
}
|
|
|
|
conn->disconnect();
|
|
|
|
return QgsVectorLayerExporter::ErrCreateLayer;
|
|
}
|
|
|
|
conn->disconnect();
|
|
|
|
QgsDebugMsg( QStringLiteral( "layer %1 created" ).arg( ownerTableName ) );
|
|
|
|
// use the provider to edit the table1
|
|
dsUri.setDataSource( ownerName, tableName, geometryColumn, QString(), primaryKey );
|
|
|
|
QgsDataProvider::ProviderOptions providerOptions;
|
|
QgsOracleProvider *provider = new QgsOracleProvider( dsUri.uri( false ), providerOptions );
|
|
if ( !provider->isValid() )
|
|
{
|
|
errorMessage = QObject::tr( "Loading of the layer %1 failed" ).arg( ownerTableName );
|
|
|
|
delete provider;
|
|
return QgsVectorLayerExporter::ErrInvalidLayer;
|
|
}
|
|
|
|
QgsDebugMsg( QStringLiteral( "layer loaded" ) );
|
|
|
|
// add fields to the layer
|
|
oldToNewAttrIdxMap.clear();
|
|
|
|
if ( fields.size() > 0 )
|
|
{
|
|
int offset = 0;
|
|
|
|
QSet<QString> names;
|
|
QList<QgsField> launderedFields;
|
|
for ( int i = 0; i < fields.size(); i++ )
|
|
{
|
|
QgsField fld = fields.at( i );
|
|
|
|
QString name = fld.name().left( 30 ).toUpper();
|
|
|
|
if ( names.contains( name ) )
|
|
{
|
|
int j;
|
|
int n;
|
|
for ( j = 1, n = 10; j < 3; j++, n *= 10 )
|
|
{
|
|
int k;
|
|
for ( k = 0; k < n && names.contains( name ); k++ )
|
|
{
|
|
name = QString( "%1%2" ).arg( name.left( 30 - j ) ).arg( k, j, 10, QChar( '0' ) );
|
|
}
|
|
|
|
if ( k < n )
|
|
break;
|
|
}
|
|
|
|
if ( j == 3 )
|
|
{
|
|
errorMessage = QObject::tr( "Field name clash found (%1 not remappable)" ).arg( fld.name() );
|
|
|
|
delete provider;
|
|
return QgsVectorLayerExporter::ErrAttributeTypeUnsupported;
|
|
}
|
|
}
|
|
|
|
if ( fld.name() == geometryColumn )
|
|
geometryColumn = name;
|
|
|
|
fld.setName( name );
|
|
launderedFields << fld;
|
|
names << name;
|
|
}
|
|
|
|
// get the list of fields
|
|
QList<QgsField> flist;
|
|
for ( int i = 0; i < launderedFields.size(); i++ )
|
|
{
|
|
QgsField fld = launderedFields[i];
|
|
|
|
if ( fld.name() == primaryKey )
|
|
{
|
|
oldToNewAttrIdxMap.insert( i, 0 );
|
|
continue;
|
|
}
|
|
|
|
if ( fld.name() == geometryColumn )
|
|
{
|
|
QgsDebugMsg( QStringLiteral( "Found a field with the same name of the geometry column. Skip it!" ) );
|
|
continue;
|
|
}
|
|
|
|
if ( !convertField( fld ) )
|
|
{
|
|
errorMessage = QObject::tr( "Unsupported type for field %1" ).arg( fld.name() );
|
|
|
|
delete provider;
|
|
return QgsVectorLayerExporter::ErrAttributeTypeUnsupported;
|
|
}
|
|
|
|
QgsDebugMsg( QStringLiteral( "Field #%1 name %2 type %3 typename %4 width %5 precision %6" )
|
|
.arg( i )
|
|
.arg( fld.name() ).arg( QVariant::typeToName( fld.type() ) ).arg( fld.typeName() )
|
|
.arg( fld.length() ).arg( fld.precision() ) );
|
|
|
|
flist.append( fld );
|
|
oldToNewAttrIdxMap.insert( i, offset++ );
|
|
}
|
|
|
|
if ( !provider->addAttributes( flist ) )
|
|
{
|
|
errorMessage = QObject::tr( "Creation of fields failed" );
|
|
|
|
delete provider;
|
|
return QgsVectorLayerExporter::ErrAttributeCreationFailed;
|
|
}
|
|
|
|
QgsDebugMsg( QStringLiteral( "Done creating fields" ) );
|
|
}
|
|
else
|
|
{
|
|
QgsDebugMsg( QStringLiteral( "No fields created." ) );
|
|
}
|
|
|
|
delete provider;
|
|
|
|
return QgsVectorLayerExporter::NoError;
|
|
}
|
|
|
|
QgsCoordinateReferenceSystem QgsOracleProvider::crs() const
|
|
{
|
|
QgsCoordinateReferenceSystem srs;
|
|
QgsOracleConn *conn = connectionRO();
|
|
if ( !conn )
|
|
return srs;
|
|
|
|
QSqlQuery qry( *conn );
|
|
|
|
// apparently some EPSG codes don't have the auth_name setup in cs_srs
|
|
if ( exec( qry, QString( "SELECT coalesce(auth_name,'EPSG'),auth_srid,wktext FROM mdsys.cs_srs WHERE srid=?" ), QVariantList() << mSrid ) )
|
|
{
|
|
if ( qry.next() )
|
|
{
|
|
if ( qry.value( 0 ).toString() == "EPSG" )
|
|
{
|
|
srs.createFromOgcWmsCrs( QString( "EPSG:%1" ).arg( qry.value( 1 ).toString() ) );
|
|
}
|
|
else
|
|
{
|
|
srs.createFromWkt( qry.value( 2 ).toString() );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Oracle SRID %1 not found." ).arg( mSrid ), tr( "Oracle" ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QgsMessageLog::logMessage( tr( "Lookup of Oracle SRID %1 failed.\nSQL: %2\nError: %3" )
|
|
.arg( mSrid )
|
|
.arg( qry.lastQuery() )
|
|
.arg( qry.lastError().text() ),
|
|
tr( "Oracle" ) );
|
|
}
|
|
|
|
return srs;
|
|
}
|
|
|
|
QString QgsOracleProvider::subsetString() const
|
|
{
|
|
return mSqlWhereClause;
|
|
}
|
|
|
|
QString QgsOracleProvider::getTableName()
|
|
{
|
|
return mTableName;
|
|
}
|
|
|
|
size_t QgsOracleProvider::layerCount() const
|
|
{
|
|
return 1; // XXX need to return actual number of layers
|
|
} // QgsOracleProvider::layerCount()
|
|
|
|
|
|
QString QgsOracleProvider::name() const
|
|
{
|
|
return ORACLE_KEY;
|
|
} // QgsOracleProvider::name()
|
|
|
|
QString QgsOracleProvider::description() const
|
|
{
|
|
return ORACLE_DESCRIPTION;
|
|
} // QgsOracleProvider::description()
|
|
|
|
|
|
QgsOracleProvider *QgsOracleProviderMetadata::createProvider(
|
|
const QString &uri,
|
|
const QgsDataProvider::ProviderOptions &options )
|
|
{
|
|
return new QgsOracleProvider( uri, options );
|
|
}
|
|
|
|
QList< QgsDataItemProvider * > QgsOracleProviderMetadata::dataItemProviders() const
|
|
{
|
|
return QList< QgsDataItemProvider * >() << new QgsOracleDataItemProvider;
|
|
}
|
|
|
|
QgsTransaction *QgsOracleProviderMetadata::createTransaction( const QString &connString )
|
|
{
|
|
return new QgsOracleTransaction( connString );
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
QgsVectorLayerExporter::ExportError QgsOracleProviderMetadata::createEmptyLayer( const QString &uri,
|
|
const QgsFields &fields,
|
|
QgsWkbTypes::Type wkbType,
|
|
const QgsCoordinateReferenceSystem &srs,
|
|
bool overwrite,
|
|
QMap<int, int> &oldToNewAttrIdxMap,
|
|
QString &errorMessage,
|
|
const QMap<QString, QVariant> *options )
|
|
{
|
|
return QgsOracleProvider::createEmptyLayer(
|
|
uri, fields, wkbType, srs, overwrite,
|
|
oldToNewAttrIdxMap, errorMessage, options
|
|
);
|
|
}
|
|
|
|
void QgsOracleProviderMetadata::cleanupProvider()
|
|
{
|
|
QgsOracleConnPool::cleanupInstance();
|
|
}
|
|
|
|
// ----------
|
|
|
|
QgsFeatureId QgsOracleSharedData::lookupFid( const QVariantList &v )
|
|
{
|
|
QMutexLocker locker( &mMutex );
|
|
|
|
QMap<QVariantList, QgsFeatureId>::const_iterator it = mKeyToFid.constFind( v );
|
|
|
|
if ( it != mKeyToFid.constEnd() )
|
|
{
|
|
return it.value();
|
|
}
|
|
|
|
mFidToKey.insert( ++mFidCounter, v );
|
|
mKeyToFid.insert( v, mFidCounter );
|
|
|
|
return mFidCounter;
|
|
}
|
|
|
|
QVariant QgsOracleSharedData::removeFid( QgsFeatureId fid )
|
|
{
|
|
QMutexLocker locker( &mMutex );
|
|
|
|
QVariantList v = mFidToKey[ fid ];
|
|
mFidToKey.remove( fid );
|
|
mKeyToFid.remove( v );
|
|
return v;
|
|
}
|
|
|
|
void QgsOracleSharedData::insertFid( QgsFeatureId fid, const QVariantList &k )
|
|
{
|
|
QMutexLocker locker( &mMutex );
|
|
|
|
mFidToKey.insert( fid, k );
|
|
mKeyToFid.insert( k, fid );
|
|
}
|
|
|
|
QVariantList QgsOracleSharedData::lookupKey( QgsFeatureId featureId )
|
|
{
|
|
QMutexLocker locker( &mMutex );
|
|
|
|
QMap<QgsFeatureId, QVariantList>::const_iterator it = mFidToKey.find( featureId );
|
|
if ( it != mFidToKey.constEnd() )
|
|
return it.value();
|
|
return QVariantList();
|
|
}
|
|
|
|
bool QgsOracleProviderMetadata::saveStyle( const QString &uri,
|
|
const QString &qmlStyle,
|
|
const QString &sldStyle,
|
|
const QString &styleName,
|
|
const QString &styleDescription,
|
|
const QString &uiFileContent,
|
|
bool useAsDefault,
|
|
QString &errCause )
|
|
{
|
|
QgsDataSourceUri dsUri( uri );
|
|
|
|
QgsOracleConn *conn = QgsOracleConn::connectDb( dsUri, false );
|
|
if ( !conn )
|
|
{
|
|
errCause = QObject::tr( "Could not connect to database" );
|
|
return false;
|
|
}
|
|
|
|
QSqlQuery qry = QSqlQuery( *conn );
|
|
if ( !qry.exec( "SELECT COUNT(*) FROM user_tables WHERE table_name='LAYER_STYLES'" ) || !qry.next() )
|
|
{
|
|
errCause = QObject::tr( "Unable to check layer style existence [%1]" ).arg( qry.lastError().text() );
|
|
conn->disconnect();
|
|
return false;
|
|
}
|
|
else if ( qry.value( 0 ).toInt() == 0 )
|
|
{
|
|
QgsDebugMsg( QStringLiteral( "Creating layer style table." ) );
|
|
|
|
if ( !qry.exec( "CREATE TABLE layer_styles("
|
|
"id INTEGER PRIMARY KEY,"
|
|
"f_table_catalog VARCHAR2(30) NOT NULL,"
|
|
"f_table_schema VARCHAR2(30) NOT NULL,"
|
|
"f_table_name VARCHAR2(30) NOT NULL,"
|
|
"f_geometry_column VARCHAR2(30) NOT NULL,"
|
|
"stylename VARCHAR2(2047),"
|
|
"styleqml CLOB,"
|
|
"stylesld CLOB,"
|
|
"useasdefault INTEGER,"
|
|
"description VARCHAR2(2047),"
|
|
"owner VARCHAR2(30),"
|
|
"ui CLOB,"
|
|
"update_time timestamp"
|
|
")" ) )
|
|
{
|
|
errCause = QObject::tr( "Unable to create layer style table [%1]" ).arg( qry.lastError().text() );
|
|
conn->disconnect();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int id;
|
|
QString sql;
|
|
|
|
if ( !qry.prepare( QStringLiteral( "SELECT id,stylename FROM layer_styles"
|
|
" WHERE f_table_catalog=?"
|
|
" AND f_table_schema=?"
|
|
" AND f_table_name=?"
|
|
" AND f_geometry_column=?"
|
|
" AND styleName=?" ) ) ||
|
|
!(
|
|
qry.addBindValue( dsUri.database() ),
|
|
qry.addBindValue( dsUri.schema() ),
|
|
qry.addBindValue( dsUri.table() ),
|
|
qry.addBindValue( dsUri.geometryColumn() ),
|
|
qry.addBindValue( styleName.isEmpty() ? dsUri.table() : styleName ),
|
|
qry.exec( sql )
|
|
) )
|
|
{
|
|
errCause = QObject::tr( "Unable to check style existence [%1]" ).arg( qry.lastError().text() );
|
|
conn->disconnect();
|
|
return false;
|
|
}
|
|
else if ( qry.next() )
|
|
{
|
|
if ( QMessageBox::question( nullptr, QObject::tr( "Save style in database" ),
|
|
QObject::tr( "A style named \"%1\" already exists in the database for this layer. Do you want to overwrite it?" )
|
|
.arg( styleName.isEmpty() ? dsUri.table() : styleName ),
|
|
QMessageBox::Yes | QMessageBox::No ) == QMessageBox::No )
|
|
{
|
|
errCause = QObject::tr( "Operation aborted. No changes were made in the database" );
|
|
conn->disconnect();
|
|
return false;
|
|
}
|
|
|
|
id = qry.value( 0 ).toInt();
|
|
|
|
sql = QString( "UPDATE layer_styles"
|
|
" SET update_time=(select current_timestamp from dual),"
|
|
"f_table_catalog=?,"
|
|
"f_table_schema=?,"
|
|
"f_table_name=?,"
|
|
"f_geometry_column=?,"
|
|
"styleName=?,"
|
|
"styleQML=?,"
|
|
"styleSLD=?,"
|
|
"useAsDefault=?,"
|
|
"description=?,"
|
|
"owner=?"
|
|
"%1"
|
|
" WHERE id=%2" )
|
|
.arg( uiFileContent.isEmpty() ? "" : ",ui=?" )
|
|
.arg( id );
|
|
}
|
|
else if ( qry.exec( "select coalesce(max(id)+1,0) FROM layer_styles" ) && qry.next() )
|
|
{
|
|
id = qry.value( 0 ).toInt();
|
|
|
|
sql = QString( "INSERT INTO layer_styles("
|
|
"id,update_time,f_table_catalog,f_table_schema,f_table_name,f_geometry_column,styleName,styleQML,styleSLD,useAsDefault,description,owner%1"
|
|
") VALUES ("
|
|
"%2,"
|
|
"(select current_timestamp from dual)"
|
|
"%3"
|
|
")" )
|
|
.arg( uiFileContent.isEmpty() ? "" : ",ui" )
|
|
.arg( id )
|
|
.arg( QString( ",?" ).repeated( uiFileContent.isEmpty() ? 10 : 11 ) );
|
|
}
|
|
else
|
|
{
|
|
errCause = QObject::tr( "Cannot fetch new layer style id." );
|
|
conn->disconnect();
|
|
return false;
|
|
}
|
|
|
|
if ( !qry.prepare( sql ) )
|
|
{
|
|
errCause = QObject::tr( "Could not prepare insert/update [%1]" ).arg( qry.lastError().text() );
|
|
QgsDebugMsg( QStringLiteral( "prepare insert/update failed" ) );
|
|
conn->disconnect();
|
|
return false;
|
|
}
|
|
|
|
qry.addBindValue( dsUri.database() );
|
|
qry.addBindValue( dsUri.schema() );
|
|
qry.addBindValue( dsUri.table() );
|
|
qry.addBindValue( dsUri.geometryColumn() );
|
|
qry.addBindValue( styleName.isEmpty() ? dsUri.table() : styleName );
|
|
qry.addBindValue( qmlStyle );
|
|
qry.addBindValue( sldStyle );
|
|
qry.addBindValue( useAsDefault ? 1 : 0 );
|
|
qry.addBindValue( styleDescription.isEmpty() ? QDateTime::currentDateTime().toString() : styleDescription );
|
|
qry.addBindValue( dsUri.username() );
|
|
if ( !uiFileContent.isEmpty() )
|
|
qry.addBindValue( uiFileContent );
|
|
|
|
if ( !qry.exec() )
|
|
{
|
|
errCause = QObject::tr( "Could not execute insert/update [%1]" ).arg( qry.lastError().text() );
|
|
QgsDebugMsg( QStringLiteral( "execute insert/update failed" ) );
|
|
conn->disconnect();
|
|
return false;
|
|
}
|
|
|
|
if ( useAsDefault )
|
|
{
|
|
if ( !qry.prepare( QStringLiteral( "UPDATE layer_styles"
|
|
" SET useasdefault=0,update_time=(select current_timestamp from dual)"
|
|
" WHERE f_table_catalog=?"
|
|
" AND f_table_schema=?"
|
|
" AND f_table_name=?"
|
|
" AND f_geometry_column=?"
|
|
" AND id<>?" ) ) ||
|
|
!(
|
|
qry.addBindValue( dsUri.database() ),
|
|
qry.addBindValue( dsUri.schema() ),
|
|
qry.addBindValue( dsUri.table() ),
|
|
qry.addBindValue( dsUri.geometryColumn() ),
|
|
qry.addBindValue( id ),
|
|
qry.exec()
|
|
) )
|
|
{
|
|
errCause = QObject::tr( "Could not reset default status [%1]" ).arg( qry.lastError().text() );
|
|
QgsDebugMsg( QStringLiteral( "execute update failed" ) );
|
|
conn->disconnect();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
conn->disconnect();
|
|
|
|
return true;
|
|
}
|
|
|
|
QString QgsOracleProviderMetadata::loadStyle( const QString &uri, QString &errCause )
|
|
{
|
|
QgsDataSourceUri dsUri( uri );
|
|
|
|
QgsOracleConn *conn = QgsOracleConn::connectDb( dsUri, false );
|
|
if ( !conn )
|
|
{
|
|
errCause = QObject::tr( "Could not connect to database" );
|
|
return QString();
|
|
}
|
|
|
|
QSqlQuery qry( *conn );
|
|
|
|
QString style;
|
|
if ( !qry.exec( "SELECT COUNT(*) FROM user_tables WHERE table_name='LAYER_STYLES'" ) || !qry.next() || qry.value( 0 ).toInt() == 0 )
|
|
{
|
|
errCause = QObject::tr( "Unable to find layer style table [%1]" ).arg( qry.lastError().text() );
|
|
conn->disconnect();
|
|
return QString();
|
|
}
|
|
else if ( !qry.prepare( QStringLiteral( "SELECT styleQML FROM ("
|
|
"SELECT styleQML"
|
|
" FROM layer_styles"
|
|
" WHERE f_table_catalog=?"
|
|
" AND f_table_schema=?"
|
|
" AND f_table_name=?"
|
|
" AND f_geometry_column=?"
|
|
" ORDER BY useAsDefault DESC"
|
|
") WHERE rownum=1" ) ) ||
|
|
!(
|
|
qry.addBindValue( dsUri.database() ),
|
|
qry.addBindValue( dsUri.schema() ),
|
|
qry.addBindValue( dsUri.table() ),
|
|
qry.addBindValue( dsUri.geometryColumn() ),
|
|
qry.exec() ) )
|
|
{
|
|
errCause = QObject::tr( "Could not retrieve style [%1]" ).arg( qry.lastError().text() );
|
|
}
|
|
else if ( !qry.next() )
|
|
{
|
|
errCause = QObject::tr( "Style not found" );
|
|
}
|
|
else
|
|
{
|
|
style = qry.value( 0 ).toString();
|
|
}
|
|
|
|
conn->disconnect();
|
|
|
|
return style;
|
|
}
|
|
|
|
int QgsOracleProviderMetadata::listStyles( const QString &uri,
|
|
QStringList &ids,
|
|
QStringList &names,
|
|
QStringList &descriptions,
|
|
QString &errCause )
|
|
{
|
|
QgsDataSourceUri dsUri( uri );
|
|
|
|
QgsOracleConn *conn = QgsOracleConn::connectDb( dsUri, false );
|
|
if ( !conn )
|
|
{
|
|
errCause = QObject::tr( "Could not connect to database" );
|
|
return -1;
|
|
}
|
|
|
|
QSqlQuery qry( *conn );
|
|
|
|
int res = -1;
|
|
if ( !qry.exec( "SELECT count(*) FROM user_tables WHERE table_name='LAYER_STYLES'" ) || !qry.next() )
|
|
{
|
|
errCause = QObject::tr( "Could not verify existence of layer style table [%1]" ).arg( qry.lastError().text() );
|
|
}
|
|
else if ( qry.value( 0 ).toInt() == 0 )
|
|
{
|
|
errCause = QObject::tr( "Layer style table does not exist [%1]" ).arg( qry.value( 0 ).toString() );
|
|
}
|
|
else
|
|
{
|
|
if ( !qry.prepare( QStringLiteral( "SELECT id,styleName,description FROM layer_styles WHERE f_table_catalog=? AND f_table_schema=? AND f_table_name=? AND f_geometry_column=?" ) ) ||
|
|
!(
|
|
qry.addBindValue( dsUri.database() ),
|
|
qry.addBindValue( dsUri.schema() ),
|
|
qry.addBindValue( dsUri.table() ),
|
|
qry.addBindValue( dsUri.geometryColumn() ),
|
|
qry.exec() ) )
|
|
{
|
|
errCause = QObject::tr( "No style for layer found" );
|
|
}
|
|
else
|
|
{
|
|
res = 0;
|
|
while ( qry.next() )
|
|
{
|
|
ids << qry.value( 0 ).toString();
|
|
names << qry.value( 1 ).toString();
|
|
descriptions << qry.value( 2 ).toString();
|
|
res++;
|
|
}
|
|
|
|
qry.finish();
|
|
|
|
if ( qry.prepare( QStringLiteral( "SELECT id,styleName,description FROM layer_styles WHERE NOT (f_table_catalog=? AND f_table_schema=? AND f_table_name=? AND f_geometry_column=?) ORDER BY update_time DESC" ) ) &&
|
|
(
|
|
qry.addBindValue( dsUri.database() ),
|
|
qry.addBindValue( dsUri.schema() ),
|
|
qry.addBindValue( dsUri.table() ),
|
|
qry.addBindValue( dsUri.geometryColumn() ),
|
|
qry.exec() ) )
|
|
{
|
|
while ( qry.next() )
|
|
{
|
|
ids << qry.value( 0 ).toString();
|
|
names << qry.value( 1 ).toString();
|
|
descriptions << qry.value( 2 ).toString();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
conn->disconnect();
|
|
|
|
return res;
|
|
}
|
|
|
|
QString QgsOracleProviderMetadata::getStyleById( const QString &uri, QString styleId, QString &errCause )
|
|
{
|
|
QString style;
|
|
QgsDataSourceUri dsUri( uri );
|
|
|
|
QgsOracleConn *conn = QgsOracleConn::connectDb( dsUri, false );
|
|
if ( !conn )
|
|
{
|
|
errCause = QObject::tr( "Could not connect to database" );
|
|
return style;
|
|
}
|
|
|
|
QSqlQuery qry( *conn );
|
|
|
|
if ( !qry.prepare( QStringLiteral( "SELECT styleQml FROM layer_styles WHERE id=?" ) ) ||
|
|
!(
|
|
qry.addBindValue( styleId ),
|
|
qry.exec() ) )
|
|
{
|
|
errCause = QObject::tr( "Could not load layer style table [%1]" ).arg( qry.lastError().text() );
|
|
}
|
|
else if ( !qry.next() )
|
|
{
|
|
errCause = QObject::tr( "No styles found in layer table [%1]" ).arg( qry.lastError().text() );
|
|
}
|
|
else
|
|
{
|
|
style = qry.value( 0 ).toString();
|
|
}
|
|
|
|
conn->disconnect();
|
|
|
|
return style;
|
|
}
|
|
|
|
|
|
#ifdef HAVE_GUI
|
|
|
|
//! Provider for Oracle source select
|
|
class QgsOracleSourceSelectProvider : public QgsSourceSelectProvider
|
|
{
|
|
public:
|
|
|
|
QString providerKey() const override { return QStringLiteral( "oracle" ); }
|
|
QString text() const override { return QObject::tr( "Oracle" ); }
|
|
int ordering() const override { return QgsSourceSelectProvider::OrderDatabaseProvider + 40; }
|
|
QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddOracleLayer.svg" ) ); }
|
|
QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr,
|
|
Qt::WindowFlags fl = Qt::Widget,
|
|
QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const override
|
|
{
|
|
return new QgsOracleSourceSelect( parent, fl, widgetMode );
|
|
}
|
|
};
|
|
|
|
QgsOracleProviderGuiMetadata::QgsOracleProviderGuiMetadata()
|
|
: QgsProviderGuiMetadata( ORACLE_KEY )
|
|
{
|
|
|
|
}
|
|
|
|
QList<QgsSourceSelectProvider *> QgsOracleProviderGuiMetadata::sourceSelectProviders()
|
|
{
|
|
return QList<QgsSourceSelectProvider *>() << new QgsOracleSourceSelectProvider;
|
|
}
|
|
|
|
void QgsOracleProviderGuiMetadata::registerGui( QMainWindow *mainWindow )
|
|
{
|
|
QgsOracleRootItem::sMainWindow = mainWindow;
|
|
}
|
|
|
|
#endif
|
|
|
|
QgsOracleProviderMetadata::QgsOracleProviderMetadata():
|
|
QgsProviderMetadata( ORACLE_KEY, ORACLE_DESCRIPTION )
|
|
{
|
|
}
|
|
|
|
QGISEXTERN QgsProviderMetadata *providerMetadataFactory()
|
|
{
|
|
return QSqlDatabase::isDriverAvailable( "QOCISPATIAL" ) ? new QgsOracleProviderMetadata() : nullptr;
|
|
}
|
|
|
|
#ifdef HAVE_GUI
|
|
QGISEXTERN QgsProviderGuiMetadata *providerGuiMetadataFactory()
|
|
{
|
|
return QSqlDatabase::isDriverAvailable( "QOCISPATIAL" ) ? new QgsOracleProviderGuiMetadata() : nullptr;
|
|
}
|
|
#endif
|
|
|
|
// vim: set sw=2
|