316 changes: 316 additions & 0 deletions src/providers/oracle/qgsoraclefeatureiterator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,316 @@
/***************************************************************************
qgsoraclefeatureiterator.cpp - Oracle feature iterator
---------------------
begin : December 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 "qgsoraclefeatureiterator.h"
#include "qgsoracleprovider.h"

#include "qgslogger.h"
#include "qgsmessagelog.h"
#include "qgsgeometry.h"

#include <QObject>

QgsOracleFeatureIterator::QgsOracleFeatureIterator( QgsOracleProvider *p, const QgsFeatureRequest &request )
: QgsAbstractFeatureIterator( request )
, P( p )
, mRewind( false )
{
mQry = QSqlQuery( *P->mConnection );

if( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes )
{
mAttributeList = mRequest.subsetOfAttributes();
if( mAttributeList.isEmpty() )
mAttributeList = P->attributeIndexes();
}
else
mAttributeList = P->attributeIndexes();

QString whereClause;

switch( request.filterType() )
{
case QgsFeatureRequest::FilterRect:
if( !P->mGeometryColumn.isNull() )
{
QgsRectangle rect( mRequest.filterRect() );
QString bbox = QString( "mdsys.sdo_geometry(2003,%1,NULL,"
"mdsys.sdo_elem_info_array(1,1003,3),"
"mdsys.sdo_ordinate_array(%2,%3,%4,%5)"
")" )
.arg( P->mSrid < 1 ? "NULL" : QString::number( P->mSrid ) )
.arg( rect.xMinimum(), 0, 'f', 16 )
.arg( rect.yMinimum(), 0, 'f', 16 )
.arg( rect.xMaximum(), 0, 'f', 16 )
.arg( rect.yMaximum(), 0, 'f', 16 );

if ( !P->mSpatialIndex.isNull() )
{
whereClause = QString( "sdo_filter(%1,%2)='TRUE'" ).arg( P->quotedIdentifier( P->mGeometryColumn ) ).arg( bbox );
#if 0
if ( mRequest.flags() & QgsFeatureRequest::ExactIntersect )
{
whereClause += QString( " AND sdo_relate(%1,%2,'mask=ANYINTERACT')='TRUE'" )
.arg( quotedIdentifier( P->mGeometryColumn ) )
.arg( bbox );
}
#endif
}
}
break;

case QgsFeatureRequest::FilterFid:
whereClause = P->whereClause( request.filterFid() );
break;

case QgsFeatureRequest::FilterNone:
break;
}

if ( P->mRequestedGeomType != QGis::WKBUnknown && P->mRequestedGeomType != P->mDetectedGeomType )
{
if ( !whereClause.isEmpty() )
whereClause += " AND ";

whereClause += QgsOracleConn::databaseTypeFilter( "featureRequest", P->mGeometryColumn, P->mRequestedGeomType );
}

if ( !P->mSqlWhereClause.isEmpty() )
{
if ( !whereClause.isEmpty() )
whereClause += " AND ";
whereClause += "(" + P->mSqlWhereClause + ")";
}

if ( !openQuery( whereClause ) )
return;
}

QgsOracleFeatureIterator::~QgsOracleFeatureIterator()
{
close();
}

bool QgsOracleFeatureIterator::nextFeature( QgsFeature& feature )
{
feature.setValid( false );

if( !mQry.isActive() )
return false;

for ( ;; )
{
feature.initAttributes( P->fields().count() );
feature.setGeometry( 0 );

if ( mRewind )
{
mRewind = false;
if( !mQry.first() )
return true;
}
else if( !mQry.next() )
{
return false;
}

int col = 0;

if ( (mRequest.flags() & QgsFeatureRequest::NoGeometry ) == 0 ||
( (mRequest.flags() & QgsFeatureRequest::ExactIntersect ) != 0 && !P->mConnection->hasSpatial() ) )
{
QByteArray *ba = static_cast<QByteArray*>( mQry.value( col++ ).data() );
unsigned char *copy = new unsigned char[ba->size()];
memcpy( copy, ba->constData(), ba->size() );

feature.setGeometryAndOwnership( copy, ba->size() );

if ( !P->mConnection->hasSpatial() &&
mRequest.filterType() == QgsFeatureRequest::FilterRect &&
(mRequest.flags() & QgsFeatureRequest::ExactIntersect ) != 0 &&
(!feature.geometry() || !feature.geometry()->intersects( mRequest.filterRect() ) ) )
{
// skip feature that don't intersect with our rectangle
QgsDebugMsg( "no intersect" );
continue;
}


if ( (mRequest.flags() & QgsFeatureRequest::NoGeometry ) != 0 )
{
feature.setGeometryAndOwnership( 0, 0 );
}
}

QgsFeatureId fid = 0;

switch ( P->mPrimaryKeyType )
{
case QgsOracleProvider::pktInt:
// get 64bit integer from result
fid = mQry.value( col++ ).toLongLong();
if ( mAttributeList.contains( P->mPrimaryKeyAttrs[0] ) )
feature.setAttribute( P->mPrimaryKeyAttrs[0], fid );
break;

case QgsOracleProvider::pktRowId:
case QgsOracleProvider::pktFidMap:
{
QList<QVariant> primaryKeyVals;

if ( P->mPrimaryKeyType == QgsOracleProvider::pktFidMap )
{
foreach ( int idx, P->mPrimaryKeyAttrs )
{
const QgsField &fld = P->field( idx );

QVariant v = P->convertValue( fld.type(), mQry.value( col ).toString() );
primaryKeyVals << v;

if ( mAttributeList.contains( idx ) )
feature.setAttribute( idx, v );

col++;
}
}
else
{
primaryKeyVals << mQry.value( col++ );
}

fid = P->lookupFid( QVariant( primaryKeyVals ) );
}
break;

case QgsOracleProvider::pktUnknown:
Q_ASSERT( !"FAILURE: cannot get feature with unknown primary key" );
return false;
}

feature.setFeatureId( fid );
QgsDebugMsgLevel( QString( "fid=%1" ).arg( fid ), 5 );

// iterate attributes
foreach ( int idx, mAttributeList )
{
if ( P->mPrimaryKeyAttrs.contains( idx ) )
continue;

const QgsField &fld = P->field( idx );

QVariant v = P->convertValue( fld.type(), mQry.value( col ).toString() );
feature.setAttribute( idx, v );

col++;
}

feature.setValid( true );
return true;
}
}

bool QgsOracleFeatureIterator::rewind()
{
if ( !mQry.isActive() )
return false;

// move cursor to first record
mRewind = true;
return true;
}

bool QgsOracleFeatureIterator::close()
{
if ( !mQry.isActive() )
return false;

mQry.finish();

return true;
}

bool QgsOracleFeatureIterator::openQuery( QString whereClause )
{
if ( ( mRequest.flags() & QgsFeatureRequest::NoGeometry ) == 0 && P->mGeometryColumn.isNull() )
{
return false;
}

try
{
QString query = "SELECT ", delim = "";

if ( ( mRequest.flags() & QgsFeatureRequest::NoGeometry ) == 0 )
{
query += P->quotedIdentifier( P->mGeometryColumn );
delim = ",";
}

switch ( P->mPrimaryKeyType )
{
case QgsOracleProvider::pktRowId:
query += delim + P->quotedIdentifier( "ROWID" );
delim = ",";
break;

case QgsOracleProvider::pktInt:
query += delim + P->quotedIdentifier( P->field( P->mPrimaryKeyAttrs[0] ).name() );
delim = ",";
break;

case QgsOracleProvider::pktFidMap:
foreach ( int idx, P->mPrimaryKeyAttrs )
{
query += delim + P->mConnection->fieldExpression( P->field( idx ) );
delim = ",";
}
break;

case QgsOracleProvider::pktUnknown:
QgsDebugMsg( "Cannot query without primary key." );
return false;
break;
}

foreach ( int idx, mAttributeList )
{
if ( P->mPrimaryKeyAttrs.contains( idx ) )
continue;

query += delim + P->mConnection->fieldExpression( P->field( idx ) );
}

query += QString( " FROM %1 \"featureRequest\"" ).arg( P->mQuery );

if ( !whereClause.isEmpty() )
query += QString( " WHERE %1" ).arg( whereClause );

QgsDebugMsg( QString( "Fetch features: %1" ).arg( query ) );
if ( !P->exec( mQry, query ) )
{
QgsMessageLog::logMessage( QObject::tr( "Fetching features failed.\nSQL:%1\nError: %2" )
.arg( mQry.lastQuery() )
.arg( mQry.lastError().text() ),
QObject::tr( "Oracle" ) );
return false;
}
}
catch ( QgsOracleProvider::OracleFieldNotFound )
{
return false;
}

return true;
}
56 changes: 56 additions & 0 deletions src/providers/oracle/qgsoraclefeatureiterator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/***************************************************************************
qgsoraclefeatureiterator.h - QGIS data provider for Oracle layers
-------------------
begin : December 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. *
* *
***************************************************************************/

#ifndef QGSORACLEFEATUREITERATOR_H
#define QGSORACLEFEATUREITERATOR_H

#include "qgsfeatureiterator.h"

#include <QSqlQuery>


class QgsOracleProvider;

class QgsOracleFeatureIterator : public QgsAbstractFeatureIterator
{
public:
QgsOracleFeatureIterator( QgsOracleProvider *p, const QgsFeatureRequest &request );

~QgsOracleFeatureIterator();

//! fetch next feature, return true on success
virtual bool nextFeature( QgsFeature& feature );

//! reset the iterator to the starting position
virtual bool rewind();

//! end of iterating: free the resources / lock
virtual bool close();

protected:
QgsOracleProvider *P;

bool openQuery( QString whereClause );

bool getFeature( QgsFeature &feature );

QSqlQuery mQry;
bool mRewind;
QgsAttributeList mAttributeList;
};

#endif // QGSORACLEFEATUREITERATOR_H
560 changes: 112 additions & 448 deletions src/providers/oracle/qgsoracleprovider.cpp

Large diffs are not rendered by default.

69 changes: 14 additions & 55 deletions src/providers/oracle/qgsoracleprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@
class QgsFeature;
class QgsField;
class QgsGeometry;

#include "qgsdatasourceuri.h"
#include "ocispatial/wkbptr.h"
class QgsOracleFeatureIterator;

/**
\class QgsOracleProvider
Expand All @@ -53,7 +51,7 @@ class QgsOracleProvider : public QgsVectorDataProvider
/** Import a vector layer into the database */
static QgsVectorLayerImport::ImportError createEmptyLayer(
const QString& uri,
const QgsFieldMap &fields,
const QgsFields &fields,
QGis::WkbType wkbType,
const QgsCoordinateReferenceSystem *srs,
bool overwrite,
Expand Down Expand Up @@ -85,38 +83,6 @@ class QgsOracleProvider : public QgsVectorDataProvider
*/
virtual QgsCoordinateReferenceSystem crs();

/** Select features based on a bounding rectangle. Features can be retrieved with calls to nextFeature.
* @param fetchAttributes list of attributes which should be fetched
* @param rect spatial filter
* @param fetchGeometry true if the feature geometry should be fetched
* @param useIntersect true if an accurate intersection test should be used,
* false if a test based on bounding box is sufficient
*/
virtual void select( QgsAttributeList fetchAttributes = QgsAttributeList(),
QgsRectangle rect = QgsRectangle(),
bool fetchGeometry = true,
bool useIntersect = false );

/**
* Get the next feature resulting from a select operation.
* @param feature feature which will receive data from the provider
* @return true when there was a feature to fetch, false when end was hit
*/
virtual bool nextFeature( QgsFeature& feature );

/**
* Gets the feature at the given feature ID.
* @param featureId id of the feature
* @param feature feature which will receive the data
* @param fetchGeoemtry if true, geometry will be fetched from the provider
* @param fetchAttributes a list containing the indexes of the attribute fields to copy
* @return True when feature was found, otherwise false
*/
virtual bool featureAtId( QgsFeatureId featureId,
QgsFeature& feature,
bool fetchGeometry = true,
QgsAttributeList fetchAttributes = QgsAttributeList() );

/** Get the feature type. This corresponds to
* WKBPoint,
* WKBLineString,
Expand Down Expand Up @@ -170,7 +136,7 @@ class QgsOracleProvider : public QgsVectorDataProvider
* Get the field information for the layer
* @return vector of QgsField objects
*/
const QgsFieldMap &fields() const;
const QgsFields &fields() const;

/**
* Return a short comment for the data that this provider is
Expand Down Expand Up @@ -199,8 +165,6 @@ class QgsOracleProvider : public QgsVectorDataProvider
*/
bool isValid();

QgsAttributeList attributeIndexes();

QgsAttributeList pkAttributeIndexes() { return mPrimaryKeyAttrs; }

/**Returns the default value for field specified by @c fieldName */
Expand Down Expand Up @@ -289,18 +253,14 @@ class QgsOracleProvider : public QgsVectorDataProvider
*/
QString description() const;

/**
* Query the provider for features specified in request.
*/
virtual QgsFeatureIterator getFeatures( const QgsFeatureRequest& request = QgsFeatureRequest() );

static bool exec( QSqlQuery &qry, QString sql );

private:
bool openQuery( QString alias,
const QgsAttributeList &fetchAttributes,
bool fetchGeometry,
QString whereClause );

bool getFeature( bool fetchGeometry,
QgsFeature &feature,
const QgsAttributeList &fetchAttributes );

QString whereClause( QgsFeatureId featureId ) const;
QString pkParamWhereClause() const;
QString paramValue( QString fieldvalue, const QString &defaultValue ) const;
Expand All @@ -318,8 +278,8 @@ class QgsOracleProvider : public QgsVectorDataProvider
/** convert a QgsField to work with Oracle */
static bool convertField( QgsField &field );

QgsFieldMap mAttributeFields;
QMap<int, QVariant> mDefaultValues;
QgsFields mAttributeFields; //! List of fields
QVariantList mDefaultValues; //! List of default values
QString mDataComment;

//! Data source URI struct for this layer
Expand Down Expand Up @@ -427,12 +387,11 @@ class QgsOracleProvider : public QgsVectorDataProvider
QMap<QgsFeatureId, QVariant> mFidToKey; //! map feature back to fea
QgsFeatureId mFidCounter; //! next feature id if map is used
QgsOracleConn *mConnection;
QSqlQuery mQry;
bool mFetchGeomRequested; //! geometry was requested
bool mIntersectResult; //! need to intersect the results (Oracle Locator only)
bool mHasSpatial; //! Oracle Spatial is installed

QString mSpatialIndex; //! name of spatial index of geometry column
QgsRectangle mIntersectRect; //! rectangle to intersect with (if any)
bool mHasSpatial; //! Oracle Spatial is installed

friend QgsOracleFeatureIterator;
};

#endif
2 changes: 1 addition & 1 deletion src/providers/postgres/qgspostgresprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1601,7 +1601,7 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist )

if ( i == flist.size() )
{
if ( attributevec[idx] == defVal )
if ( v == defVal )
{
if ( defVal.isNull() )
{
Expand Down
2 changes: 1 addition & 1 deletion tests/src/core/testqgsvectordataprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ void TestQgsVectorDataProvider::featureAtId()

QVERIFY( fi.nextFeature( feature ) );
QVERIFY( feature.isValid() );
qDebug( "FID: %d", feature.id() );
qDebug( "FID: %lld", feature.id() );
QVERIFY( feature.id() == 4 );

// further invocations are not valid
Expand Down